* Updated test * updated assertions * Resizing image to take full width of table cell * updated assertion * Stop updating dynamicBindingPathList directly from widget * Fix selectedRow and selectedRows computations * Fix primaryColumns computations * Updated test for derived column * Added tests for computed value * Added check clear data * Reordering of test * updated common method * Made image size as 100% of table cell size * add templating logic * Updated flow and dsl * Clear old primary columns * Updated testname * updated assertion * use evaluated values for children * Fix primary columns update on component mount and component update * add isArray check * remove property pane enhancement reducer * add property pane enhancement reducer * disable items other than template + fix running property enchancment on drop of list widget * disbled drag, resize, settingsControl, drag for items other than template * add grid options * uncomment the widget operation for add child for grid children * handle delete scenario for child widget in list widget * WIP: Use the new delete and update property features * add listdsl.json for testcases * add test cases for correct no. of items being rendered * add test cases currentItem binding in list widget * change dragEnabled to dragDisabled * change resizeEnabled to resizeDisabled * change settingsControlEnabled to settingsControlDisabled * change dropEnabled to dropDisabled * update settingsControlDisabled default value * Use deleteProperties in propertyControls * Fix unsetting of array indices when deleting widget properties * remove old TableWidget.tsx file * Fix derived column property update on primary column property update * Handle undefined primary columns * Fix filepicker immutable prop issue * Fix object.freeze issue when adding ids to the property pane configuration * fix widget issue in grid * Fix column actions dynamicBindingPathList inclusion issue * remove consoles + fix typo around batch update * Remove redundant tests * js binding test for date picker * hydate enhancement map on copy list widget * check for dynamicleaf * fixes * improve check * fix getNextWidgetName * update template in list widget when copying * updating template copy logic when copying widget * update dynamicBindingPathList in copied widget * Add path parameter to hidden functions in property pane configs * fix copy bug when copying list widget * add computed list property control * Remove time column type Fix editor prompt for currentRow Fix undefined derivedColumns scenario Remove validations for primaryColums and derivedColumns Fix section toggle for video, image and button column types * Fix table widget actions and custom column migrations * Add logs for cyclical dependency map ♻️ * Process array differences * add property control for list widget * Fix onClick migrations * Property pane config parity * binding and trigger paths from the property pane config (#2920) * try react virtualized library * Fix unit test * Fix unit test ✅ * Fix minor issues in table widget * Add default meta props to binding paths to ensure eval and validation * Dummy commit 🎉 * Remove unnecessary datepicker test Fix chart data as string issue * Achieve table column sorting and resizing parity with release * handle scenario where last column isn't available to access * Fix for panel config path not existing in the widget * Fix bindings in currentRow (default) Add dummy property pane config for canvas widget * Update canvas widgets with dynamicPathLists on delete of property paths * Add all diffs to change paths and trim later * Add back default properties 🚶🏻♂️ * Use object based paths instead of arrays for primaryColumns and derivedColumns * Fix issue in reordered columns * Fix inccorect update order * add virtualized list * Fix failing property pane tests * minor change * minor list widget change * Remove .vscode from git * Rename ads to alloy Fix isVisible in list widget * move grid component to widget folder * fix import in widget registry * add sticky row in virtualized list * add sticky container * Fix Height of grid widget items container * fix dragging of items in children other than template children * update list widget * update list widget * Fix padding in list widget * hide scrollbar in list widget list * fix copy bug in list widget * regenrate enhancement map on undo delete widget * Use enhancementmap for autocomplete in list widget Basic styles for list widget scrollbar * add custom control in widget config * minor commit * update scrollbar styles * remove unused variable * fix typo in custom control * comment out test cases * remove unused imports * remove unused imports * add JSON stringify in interweave * add noPad styling in dragLayer for noPad prop * implement grid gap * add list item background color prop * add white color in color picker control * fix gap in last list item * remove onBeforeParse in textcomponent * remove virtualization in grid widget * allow overflow-y * add onListItemClick action * add beta label * add pagination * fix actions in pagination in list widget * add list widget icon * add list background color default value * remove extra div * fix pagination issue * fix list widget crashing on perpage change * extract child operation function to widgetblueprint saga * refactor enhancements * add enhancement hook * refactor propertyUpdate hook enhancment * remove enhacement map * revert renaming ads to alloy * add autopagination * Cleanup unused vars Re-write loop using map Fix binding with external input widget * update default background color * remove unnessary scrol + fix pagination per page * remove console.log * use grid gap in pixel instead of snap * fix list widget tests for binding * add tests for on click action and pagination * remove unnecessary imports * remove overflow hidden in list component * Add feature to enable template actions * update property pane help text for list widget * disable pagination in editor view * update property pane options * add test case for action * uncomment tests * fix grid gap validation * update test cases * fix property pane opening issue for list tempalte * Disable form widgets in list widget * fix template issue for actions * add validation tests for list data * update starting template * add selectedRow + enable pagination in edit mode * remove extra padding in list widget + popper fix on settingDisabled * add stop propagation for button click * fix click event in edit mode * disallow filepicker widget for list widget * add test for list widget entity definition for selectItem * remove unused imports * fix test * remove evaluated value for list child widgets * add comment * remove log * fix copying bug in list widget * add check for not allowing template to copy * fix test * add test for property pane actions * remove unused import * add draglayercomponent test * add test for draggable component * add test for evaluatedvalue popup * add test for messages.ts * add test for widgeticons * add test for property pane selector * add test for widget config response * start testing widget configresponse * add test for enhancements in widget config * add test for codeeditor * add test for base widget + list widget * add test for executeWidgetBlueprintChildOperations * remove unused import * add test for widget operation utils * remove unused import * add test for handleSpecificCasesWhilePasting * remove unused function * remove unused import * add empty list styling * resolve all review comments * fix message test * add test for widget operation utils * fix merge conflicts * move validations in property config Co-authored-by: Abhinav Jha <abhinav@appsmith.com> Co-authored-by: nandan.anantharamu <nandan.anantharamu@thoughtspot.com> Co-authored-by: vicky-primathon.in <vicky.bansal@primathon.in> Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro.local> Co-authored-by: Piyush <piyush@codeitout.com> Co-authored-by: hetunandu <hetu@appsmith.com> Co-authored-by: Hetu Nandu <hetunandu@gmail.com> Co-authored-by: root <root@DESKTOP-9GENCK0.localdomain>
305 lines
9.5 KiB
TypeScript
305 lines
9.5 KiB
TypeScript
import React, {
|
|
useState,
|
|
useContext,
|
|
ReactNode,
|
|
Context,
|
|
createContext,
|
|
useEffect,
|
|
memo,
|
|
} from "react";
|
|
import styled from "styled-components";
|
|
import { useDrop, XYCoord, DropTargetMonitor } from "react-dnd";
|
|
import { WidgetProps } from "widgets/BaseWidget";
|
|
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
|
|
import {
|
|
widgetOperationParams,
|
|
noCollision,
|
|
getCanvasSnapRows,
|
|
} from "utils/WidgetPropsUtils";
|
|
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
|
import {
|
|
MAIN_CONTAINER_WIDGET_ID,
|
|
GridDefaults,
|
|
WidgetTypes,
|
|
} from "constants/WidgetConstants";
|
|
import { calculateDropTargetRows } from "./DropTargetUtils";
|
|
import DragLayerComponent from "./DragLayerComponent";
|
|
import { AppState } from "reducers";
|
|
import { useSelector } from "react-redux";
|
|
import {
|
|
useShowPropertyPane,
|
|
useWidgetSelection,
|
|
useCanvasSnapRowsUpdateHook,
|
|
} from "utils/hooks/dragResizeHooks";
|
|
import { getOccupiedSpaces } from "selectors/editorSelectors";
|
|
|
|
type DropTargetComponentProps = WidgetProps & {
|
|
children?: ReactNode;
|
|
snapColumnSpace: number;
|
|
snapRowSpace: number;
|
|
minHeight: number;
|
|
noPad?: boolean;
|
|
};
|
|
|
|
const StyledDropTarget = styled.div`
|
|
transition: height 100ms ease-in;
|
|
width: 100%;
|
|
position: relative;
|
|
background: none;
|
|
user-select: none;
|
|
`;
|
|
|
|
const Onboarding = () => {
|
|
return (
|
|
<div style={{ position: "fixed", left: "50%", top: "50vh" }}>
|
|
<h2 style={{ color: "#ccc" }}>Drag and drop a widget here</h2>
|
|
</div>
|
|
);
|
|
};
|
|
|
|
/*
|
|
This context will provide the function which will help the draglayer and resizablecomponents trigger
|
|
an update of the main container's rows
|
|
*/
|
|
export const DropTargetContext: Context<{
|
|
updateDropTargetRows?: (widgetId: string, row: number) => boolean;
|
|
persistDropTargetRows?: (widgetId: string, row: number) => void;
|
|
}> = createContext({});
|
|
|
|
export const DropTargetComponent = (props: DropTargetComponentProps) => {
|
|
const canDropTargetExtend = props.canExtend;
|
|
|
|
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
|
|
|
const { updateWidget } = useContext(EditorContext);
|
|
const occupiedSpaces = useSelector(getOccupiedSpaces);
|
|
const selectedWidget = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
|
|
);
|
|
const isResizing = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.isResizing,
|
|
);
|
|
const isDragging = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.isDragging,
|
|
);
|
|
|
|
const spacesOccupiedBySiblingWidgets =
|
|
occupiedSpaces && occupiedSpaces[props.widgetId]
|
|
? occupiedSpaces[props.widgetId]
|
|
: undefined;
|
|
|
|
const childWidgets = useSelector(
|
|
(state: AppState) => state.entities.canvasWidgets[props.widgetId].children,
|
|
);
|
|
|
|
const occupiedSpacesByChildren =
|
|
occupiedSpaces && occupiedSpaces[props.widgetId];
|
|
|
|
const [dropTargetOffset, setDropTargetOffset] = useState({ x: 0, y: 0 });
|
|
const [rows, setRows] = useState(snapRows);
|
|
|
|
const showPropertyPane = useShowPropertyPane();
|
|
const { selectWidget, focusWidget } = useWidgetSelection();
|
|
const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook();
|
|
|
|
useEffect(() => {
|
|
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
|
setRows(snapRows);
|
|
}, [props.bottomRow, props.canExtend]);
|
|
|
|
const persistDropTargetRows = (widgetId: string, widgetBottomRow: number) => {
|
|
const newRows = calculateDropTargetRows(
|
|
widgetId,
|
|
widgetBottomRow,
|
|
props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
|
occupiedSpacesByChildren,
|
|
);
|
|
const rowsToPersist = Math.max(
|
|
props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
|
newRows,
|
|
);
|
|
setRows(rowsToPersist);
|
|
if (canDropTargetExtend) {
|
|
updateCanvasSnapRows(props.widgetId, rowsToPersist);
|
|
}
|
|
};
|
|
|
|
/* Update the rows of the main container based on the current widget's (dragging/resizing) bottom row */
|
|
const updateDropTargetRows = (widgetId: string, widgetBottomRow: number) => {
|
|
if (canDropTargetExtend) {
|
|
const newRows = calculateDropTargetRows(
|
|
widgetId,
|
|
widgetBottomRow,
|
|
props.minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
|
|
occupiedSpacesByChildren,
|
|
);
|
|
if (rows < newRows) {
|
|
setRows(newRows);
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
const isChildFocused =
|
|
!!childWidgets &&
|
|
!!selectedWidget &&
|
|
childWidgets.length > 0 &&
|
|
childWidgets.indexOf(selectedWidget) > -1;
|
|
|
|
const isChildResizing = !!isResizing && isChildFocused;
|
|
// Make this component a drop target
|
|
const [{ isExactlyOver, isOver }, drop] = useDrop({
|
|
accept: Object.values(WidgetTypes),
|
|
options: {
|
|
arePropsEqual: () => {
|
|
return true;
|
|
},
|
|
},
|
|
drop(widget: WidgetProps & Partial<WidgetConfigProps>, monitor) {
|
|
// Make sure we're dropping in this container.
|
|
if (isExactlyOver) {
|
|
const updateWidgetParams = widgetOperationParams(
|
|
widget,
|
|
monitor.getSourceClientOffset() as XYCoord,
|
|
dropTargetOffset,
|
|
props.snapColumnSpace,
|
|
props.snapRowSpace,
|
|
widget.detachFromLayout ? MAIN_CONTAINER_WIDGET_ID : props.widgetId,
|
|
);
|
|
|
|
// const widgetBottomRow = getWidgetBottomRow(widget, updateWidgetParams);
|
|
const widgetBottomRow =
|
|
updateWidgetParams.payload.topRow +
|
|
(updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow);
|
|
|
|
// Only show propertypane if this is a new widget.
|
|
// If it is not a new widget, then let the DraggableComponent handle it.
|
|
// Give evaluations a second to complete.
|
|
setTimeout(() => {
|
|
if (showPropertyPane && updateWidgetParams.payload.newWidgetId) {
|
|
showPropertyPane(updateWidgetParams.payload.newWidgetId);
|
|
// toggleEditWidgetName(updateWidgetParams.payload.newWidgetId, true);
|
|
}
|
|
}, 100);
|
|
|
|
// Select the widget if it is a new widget
|
|
selectWidget && selectWidget(widget.widgetId);
|
|
persistDropTargetRows(widget.widgetId, widgetBottomRow);
|
|
|
|
/* Finally update the widget */
|
|
updateWidget &&
|
|
updateWidget(
|
|
updateWidgetParams.operation,
|
|
updateWidgetParams.widgetId,
|
|
updateWidgetParams.payload,
|
|
);
|
|
}
|
|
return undefined;
|
|
},
|
|
// Collect isOver for ui transforms when hovering over this component
|
|
collect: (monitor: DropTargetMonitor) => ({
|
|
isExactlyOver: monitor.isOver({ shallow: true }),
|
|
isOver: monitor.isOver(),
|
|
}),
|
|
// Only allow drop if the drag object is directly over this component
|
|
// As opposed to the drag object being over a child component, or outside the component bounds
|
|
// Also only if the dropzone does not overlap any existing children
|
|
canDrop: (widget, monitor) => {
|
|
// Check if the draggable is the same as the dropTarget
|
|
if (isExactlyOver) {
|
|
const hasCollision = !noCollision(
|
|
monitor.getSourceClientOffset() as XYCoord,
|
|
props.snapColumnSpace,
|
|
props.snapRowSpace,
|
|
widget,
|
|
dropTargetOffset,
|
|
spacesOccupiedBySiblingWidgets,
|
|
rows,
|
|
GridDefaults.DEFAULT_GRID_COLUMNS,
|
|
);
|
|
return !hasCollision;
|
|
}
|
|
return false;
|
|
},
|
|
});
|
|
|
|
const handleBoundsUpdate = (rect: DOMRect) => {
|
|
if (rect.x !== dropTargetOffset.x || rect.y !== dropTargetOffset.y) {
|
|
setDropTargetOffset({
|
|
x: rect.x,
|
|
y: rect.y,
|
|
});
|
|
}
|
|
};
|
|
|
|
const handleFocus = (e: any) => {
|
|
if (!isResizing && !isDragging) {
|
|
if (!props.parentId) {
|
|
selectWidget && selectWidget(props.widgetId);
|
|
focusWidget && focusWidget(props.widgetId);
|
|
showPropertyPane && showPropertyPane();
|
|
} else {
|
|
selectWidget && selectWidget(props.parentId);
|
|
focusWidget && focusWidget(props.parentId);
|
|
}
|
|
}
|
|
// commenting this out to allow propagation of click events
|
|
// e.stopPropagation();
|
|
e.preventDefault();
|
|
};
|
|
const height = canDropTargetExtend
|
|
? `${Math.max(rows * props.snapRowSpace, props.minHeight)}px`
|
|
: "100%";
|
|
|
|
const border =
|
|
(isResizing || isDragging) &&
|
|
isExactlyOver &&
|
|
props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
|
? "1px solid #DDDDDD"
|
|
: "1px solid transparent";
|
|
|
|
const dropRef = !props.dropDisabled ? drop : undefined;
|
|
|
|
return (
|
|
<DropTargetContext.Provider
|
|
value={{ updateDropTargetRows, persistDropTargetRows }}
|
|
>
|
|
<StyledDropTarget
|
|
onClick={handleFocus}
|
|
ref={dropRef}
|
|
style={{
|
|
height,
|
|
border,
|
|
}}
|
|
className={"t--drop-target"}
|
|
>
|
|
{props.children}
|
|
{!(childWidgets && childWidgets.length) &&
|
|
!isDragging &&
|
|
!props.parentId && <Onboarding />}
|
|
<DragLayerComponent
|
|
parentWidgetId={props.widgetId}
|
|
canDropTargetExtend={canDropTargetExtend}
|
|
parentRowHeight={props.snapRowSpace}
|
|
parentColumnWidth={props.snapColumnSpace}
|
|
visible={isExactlyOver || isChildResizing}
|
|
isOver={isExactlyOver}
|
|
occupiedSpaces={spacesOccupiedBySiblingWidgets}
|
|
onBoundsUpdate={handleBoundsUpdate}
|
|
parentRows={rows}
|
|
parentCols={props.snapColumns}
|
|
isResizing={isChildResizing}
|
|
noPad={props.noPad || false}
|
|
force={isDragging && !isOver && !props.parentId}
|
|
/>
|
|
</StyledDropTarget>
|
|
</DropTargetContext.Provider>
|
|
);
|
|
};
|
|
|
|
const MemoizedDropTargetComponent = memo(DropTargetComponent);
|
|
|
|
export default MemoizedDropTargetComponent;
|