PromucFlow_constructor/app/client/src/components/editorComponents/ResizableComponent.tsx

282 lines
8.9 KiB
TypeScript
Raw Normal View History

import React, { useContext, useRef, memo } from "react";
import { XYCoord } from "react-dnd";
2020-02-18 19:56:58 +00:00
import {
WidgetOperations,
WidgetRowCols,
WidgetProps,
} from "widgets/BaseWidget";
import { EditorContext } from "components/editorComponents/EditorContextProvider";
import { generateClassName } from "utils/generators";
2020-01-16 11:46:21 +00:00
import { DropTargetContext } from "./DropTargetComponent";
import {
UIElementSize,
2020-02-18 19:56:58 +00:00
computeFinalRowCols,
computeRowCols,
} from "./ResizableUtils";
2020-01-20 09:00:37 +00:00
import {
useShowPropertyPane,
useWidgetSelection,
useWidgetDragResize,
} from "utils/hooks/dragResizeHooks";
2020-01-21 07:14:47 +00:00
import { useSelector } from "react-redux";
import { AppState } from "reducers";
2020-02-18 19:56:58 +00:00
import Resizable from "resizable";
import { isDropZoneOccupied, getSnapColumns } from "utils/WidgetPropsUtils";
import {
VisibilityContainer,
LeftHandleStyles,
RightHandleStyles,
TopHandleStyles,
BottomHandleStyles,
TopLeftHandleStyles,
TopRightHandleStyles,
BottomLeftHandleStyles,
BottomRightHandleStyles,
} from "./ResizeStyledComponents";
2020-03-03 07:02:53 +00:00
import AnalyticsUtil from "utils/AnalyticsUtil";
import { scrollElementIntoParentCanvasView } from "utils/helpers";
import { getNearestParentCanvas } from "utils/generators";
import { getOccupiedSpaces } from "selectors/editorSelectors";
2020-02-18 19:56:58 +00:00
export type ResizableComponentProps = WidgetProps & {
2020-02-18 19:56:58 +00:00
paddingOffset: number;
};
/* eslint-disable react/display-name */
export const ResizableComponent = memo((props: ResizableComponentProps) => {
const resizableRef = useRef<HTMLDivElement>(null);
// Fetch information from the context
const { updateWidget } = useContext(EditorContext);
const occupiedSpaces = useSelector(getOccupiedSpaces);
2020-01-16 11:46:21 +00:00
const { updateDropTargetRows, persistDropTargetRows } = useContext(
DropTargetContext,
);
2020-01-17 12:34:58 +00:00
2020-01-20 09:00:37 +00:00
const showPropertyPane = useShowPropertyPane();
const { selectWidget } = useWidgetSelection();
const { setIsResizing } = useWidgetDragResize();
const selectedWidget = useSelector(
(state: AppState) => state.ui.widgetDragResize.selectedWidget,
2020-01-20 09:00:37 +00:00
);
const focusedWidget = useSelector(
(state: AppState) => state.ui.widgetDragResize.focusedWidget,
2020-01-20 09:00:37 +00:00
);
const isDragging = useSelector(
(state: AppState) => state.ui.widgetDragResize.isDragging,
);
2020-02-18 19:56:58 +00:00
const isResizing = useSelector(
(state: AppState) => state.ui.widgetDragResize.isResizing,
);
const occupiedSpacesBySiblingWidgets =
occupiedSpaces && props.parentId && occupiedSpaces[props.parentId]
? occupiedSpaces[props.parentId]
: undefined;
2020-01-17 12:34:58 +00:00
// isFocused (string | boolean) -> isWidgetFocused (boolean)
2020-01-06 11:02:22 +00:00
const isWidgetFocused =
focusedWidget === props.widgetId || selectedWidget === props.widgetId;
2019-10-08 06:19:10 +00:00
// Calculate the dimensions of the widget,
// The ResizableContainer's size prop is controlled
const dimensions: UIElementSize = {
width:
(props.rightColumn - props.leftColumn) * props.parentColumnSpace -
2 * props.paddingOffset,
height:
(props.bottomRow - props.topRow) * props.parentRowSpace -
2 * props.paddingOffset,
};
// Resize bound's className - defaults to body
// ResizableContainer accepts the className of the element,
// whose clientRect will act as the bounds for resizing.
// Note, if there are many containers with the same className
// the bounding container becomes the nearest parent with the className
2020-02-18 19:56:58 +00:00
const boundingElementClassName = generateClassName(props.parentId);
const possibleBoundingElements = document.getElementsByClassName(
boundingElementClassName,
);
const boundingElement =
possibleBoundingElements.length > 0
? possibleBoundingElements[0]
: undefined;
const boundingElementClientRect = boundingElement
? boundingElement.getBoundingClientRect()
: undefined;
2019-10-08 06:19:10 +00:00
// onResize handler
// Checks if the current resize position has any collisions
// If yes, set isColliding flag to true.
// If no, set isColliding flag to false.
2020-02-18 19:56:58 +00:00
const isColliding = (newDimensions: UIElementSize, position: XYCoord) => {
2020-01-16 11:46:21 +00:00
const bottom =
2020-02-18 19:56:58 +00:00
props.topRow +
position.y / props.parentRowSpace +
newDimensions.height / props.parentRowSpace;
2020-01-16 11:46:21 +00:00
// Make sure to calculate collision IF we don't update the main container's rows
let updated = false;
if (updateDropTargetRows) {
updated = updateDropTargetRows(props.widgetId, bottom);
const el = resizableRef.current;
const scrollParent = getNearestParentCanvas(resizableRef.current);
scrollElementIntoParentCanvasView(el, scrollParent);
2020-02-18 19:56:58 +00:00
}
const delta: UIElementSize = {
height: newDimensions.height - dimensions.height,
width: newDimensions.width - dimensions.width,
};
const newRowCols: WidgetRowCols | false = computeRowCols(
delta,
position,
props,
);
if (newRowCols.rightColumn > getSnapColumns()) {
return true;
}
if (
newRowCols.rightColumn - newRowCols.leftColumn < 1 ||
newRowCols.bottomRow - newRowCols.topRow < 1
) {
return true;
}
2020-02-18 19:56:58 +00:00
if (
boundingElementClientRect &&
newRowCols.rightColumn * props.parentColumnSpace >
boundingElementClientRect.width
) {
return true;
}
if (newRowCols && newRowCols.leftColumn < 0) {
return true;
}
2020-01-16 11:46:21 +00:00
if (!updated) {
2020-02-18 19:56:58 +00:00
if (
boundingElementClientRect &&
newRowCols.bottomRow * props.parentRowSpace >
boundingElementClientRect.height
) {
return true;
}
if (newRowCols && newRowCols.topRow < 0) {
return true;
2020-01-16 11:46:21 +00:00
}
2019-10-08 06:19:10 +00:00
}
2020-02-18 19:56:58 +00:00
// Check if new row cols are occupied by sibling widgets
return isDropZoneOccupied(
{
left: newRowCols.leftColumn,
top: newRowCols.topRow,
bottom: newRowCols.bottomRow,
right: newRowCols.rightColumn,
},
props.widgetId,
occupiedSpacesBySiblingWidgets,
);
2019-10-08 06:19:10 +00:00
};
// onResizeStop handler
// when done resizing, check if;
// 1) There is no collision
// 2) There is a change in widget size
// Update widget, if both of the above are true.
2020-02-18 19:56:58 +00:00
const updateSize = (newDimensions: UIElementSize, position: XYCoord) => {
// Get the difference in size of the widget, before and after resizing.
const delta: UIElementSize = {
2020-02-18 19:56:58 +00:00
height: newDimensions.height - dimensions.height,
width: newDimensions.width - dimensions.width,
};
// Get the updated Widget rows and columns props
// False, if there is collision
// False, if none of the rows and cols have changed.
2020-02-18 19:56:58 +00:00
const newRowCols: WidgetRowCols | false = computeFinalRowCols(
delta,
position,
props,
);
if (newRowCols) {
2020-04-03 09:32:13 +00:00
persistDropTargetRows &&
persistDropTargetRows(props.widgetId, newRowCols.bottomRow);
updateWidget &&
updateWidget(WidgetOperations.RESIZE, props.widgetId, newRowCols);
}
// Tell the Canvas that we've stopped resizing
2020-03-04 08:10:40 +00:00
// Put it later in the stack so that other updates like click, are not propagated to the parent container
setTimeout(() => {
setIsResizing && setIsResizing(false);
2020-01-17 12:34:58 +00:00
}, 0);
// Tell the Canvas to put the focus back to this widget
// By setting the focus, we enable the control buttons on the widget
2020-03-04 08:10:40 +00:00
selectWidget &&
selectedWidget !== props.widgetId &&
selectWidget(props.widgetId);
// Let the propertypane show.
// The propertypane decides whether to show itself, based on
// whether it was showing when the widget resize started.
showPropertyPane && showPropertyPane(props.widgetId, undefined, true);
2020-03-03 07:02:53 +00:00
AnalyticsUtil.logEvent("WIDGET_RESIZE_END", {
widgetName: props.widgetName,
widgetType: props.type,
startHeight: dimensions.height,
startWidth: dimensions.width,
endHeight: newDimensions.height,
endWidth: newDimensions.width,
});
2020-02-18 19:56:58 +00:00
};
const handleResizeStart = () => {
setIsResizing && !isResizing && setIsResizing(true);
selectWidget &&
selectedWidget !== props.widgetId &&
selectWidget(props.widgetId);
2020-03-03 07:02:53 +00:00
AnalyticsUtil.logEvent("WIDGET_RESIZE_START", {
widgetName: props.widgetName,
widgetType: props.type,
});
};
2020-02-18 19:56:58 +00:00
return (
2020-02-18 19:56:58 +00:00
<Resizable
ref={resizableRef}
2020-02-18 19:56:58 +00:00
handles={{
left: LeftHandleStyles,
top: TopHandleStyles,
bottom: BottomHandleStyles,
right: RightHandleStyles,
bottomRight: BottomRightHandleStyles,
topLeft: TopLeftHandleStyles,
topRight: TopRightHandleStyles,
bottomLeft: BottomLeftHandleStyles,
}}
2020-02-18 19:56:58 +00:00
componentHeight={dimensions.height}
componentWidth={dimensions.width}
onStart={handleResizeStart}
onStop={updateSize}
snapGrid={{ x: props.parentColumnSpace, y: props.parentRowSpace }}
[Feature] Grid Widget (#2389) * 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 :recycle: * 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 :white_check_mark: * Fix minor issues in table widget * Add default meta props to binding paths to ensure eval and validation * Dummy commit :tada: * 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>
2021-04-23 05:43:13 +00:00
enable={!isDragging && isWidgetFocused && !props.resizeDisabled}
2020-02-18 19:56:58 +00:00
isColliding={isColliding}
>
<VisibilityContainer
visible={!!props.isVisible}
padding={props.paddingOffset}
>
{props.children}
</VisibilityContainer>
2020-02-18 19:56:58 +00:00
</Resizable>
);
});
export default ResizableComponent;