diff --git a/app/client/src/components/autoHeightOverlay/AutoHeightOverlay.tsx b/app/client/src/components/autoHeightOverlay/AutoHeightOverlay.tsx new file mode 100644 index 0000000000..73538decdf --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/AutoHeightOverlay.tsx @@ -0,0 +1,175 @@ +import React, { memo, useCallback, useEffect } from "react"; +import styled from "styled-components"; +import { GridDefaults } from "constants/WidgetConstants"; +import AutoHeightLimitHandleGroup from "./AutoHeightLimitHandleGroup"; +import AutoHeightLimitOverlayDisplay from "./ui/AutoHeightLimitOverlayDisplay"; +import { + useAutoHeightOverlayUIStateActions, + useDragCallbacksForHandles, + useHoverState, + usePositionedStyles, +} from "./hooks"; +import { LayersContext } from "constants/Layers"; +import { useAutoHeightLimitsState } from "./store"; +import { AutoHeightOverlayContainerProps } from "."; + +interface StyledAutoHeightOverlayProps { + layerIndex: number; + isHidden: boolean; +} + +const StyledAutoHeightOverlay = styled.div` + width: 100%; + height: 100%; + position: absolute; + z-index: ${(props) => props.layerIndex}; + pointer-events: none; + display: ${(props) => (props.isHidden ? "none" : "block")}; +`; + +export interface AutoHeightOverlayProps + extends AutoHeightOverlayContainerProps { + isHidden: boolean; +} + +const AutoHeightOverlay: React.FC = memo( + ({ + batchUpdate, + isHidden, + maxDynamicHeight, + minDynamicHeight, + onMaxHeightSet, + onMinHeightSet, + style, + ...props + }) => { + const updateMaxHeight = useCallback((height: number) => { + onMaxHeightSet(height); + }, []); + + const updateMinHeight = useCallback((height: number) => { + onMinHeightSet(height); + }, []); + + const { + onMaxDotStart, + onMaxStop, + onMaxUpdate, + onMinDotStart, + onMinStop, + onMinUpdate, + } = useDragCallbacksForHandles({ + widgetId: props.widgetId, + parentColumnSpace: props.parentColumnSpace, + parentRowSpace: props.parentRowSpace, + updateMaxHeight, + updateMinHeight, + batchUpdate, + }); + + const { + isMaxDotDragging, + isMinDotDragging, + maxdY, + maxY, + mindY, + minY, + } = useAutoHeightLimitsState(); + + const { + setMaxdY, + setMaxY, + setMindY, + setMinY, + } = useAutoHeightOverlayUIStateActions(); + + const finalMaxY = maxY + maxdY; + const finalMinY = minY + mindY; + + useEffect(() => { + // reset the diff on backend update + setMindY(0); + setMaxdY(0); + setMaxY(maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); + }, [maxDynamicHeight]); + + useEffect(() => { + // reset the diff on backend update + setMindY(0); + setMaxdY(0); + setMinY(minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); + }, [minDynamicHeight]); + + const [isMinDotActive, minHoverFns] = useHoverState(); + const [isMaxDotActive, maxHoverFns] = useHoverState(); + + const { + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, + } = props; + + const styles = usePositionedStyles({ + bottomRow, + leftColumn, + noContainerOffset, + parentColumnSpace, + parentRowSpace, + rightColumn, + topRow, + }); + + const { autoHeightWithLimitsOverlay } = React.useContext(LayersContext); + + return ( + { + // avoid DropTarget handleFocus + e.stopPropagation(); + }} + style={style ?? styles} + > + + + + ); + }, +); + +export default AutoHeightOverlay; diff --git a/app/client/src/components/autoHeightOverlay/AutoHeightOverlayWithStateContext.tsx b/app/client/src/components/autoHeightOverlay/AutoHeightOverlayWithStateContext.tsx new file mode 100644 index 0000000000..fe4871564d --- /dev/null +++ b/app/client/src/components/autoHeightOverlay/AutoHeightOverlayWithStateContext.tsx @@ -0,0 +1,16 @@ +import React from "react"; +import AutoHeightOverlay, { AutoHeightOverlayProps } from "./AutoHeightOverlay"; +import { AutoHeightLimitsStateContextProvider } from "./store"; + +function AutoHeightOverlayWithStateContext(props: AutoHeightOverlayProps) { + return ( + + + + ); +} + +export default AutoHeightOverlayWithStateContext; diff --git a/app/client/src/components/autoHeightOverlay/hooks.ts b/app/client/src/components/autoHeightOverlay/hooks.ts index 6243f6d811..4f28915fd0 100644 --- a/app/client/src/components/autoHeightOverlay/hooks.ts +++ b/app/client/src/components/autoHeightOverlay/hooks.ts @@ -1,8 +1,30 @@ -import { CONTAINER_GRID_PADDING } from "constants/WidgetConstants"; -import { CSSProperties, useEffect, useMemo, useState } from "react"; +import { focusWidget } from "actions/widgetActions"; +import { + CONTAINER_GRID_PADDING, + GridDefaults, + WidgetHeightLimits, +} from "constants/WidgetConstants"; +import { + CSSProperties, + useCallback, + useEffect, + useMemo, + useState, +} from "react"; import { CallbackHandlerEventType } from "utils/CallbackHandler/CallbackHandlerEventType"; import DynamicHeightCallbackHandler from "utils/CallbackHandler/DynamicHeightCallbackHandler"; +import { useAutoHeightLimitsDispatch, useAutoHeightLimitsState } from "./store"; import { onMouseHoverCallbacksProps } from "./types"; +import { getSnappedValues } from "./utils"; +import { AppState } from "@appsmith/reducers"; +import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; +import { useSelector } from "react-redux"; +import { + useShowPropertyPane, + useShowTableFilterPane, +} from "utils/hooks/dragResizeHooks"; +import { getParentToOpenSelector } from "selectors/widgetSelectors"; +import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; type UseHoverStateReturnType = [boolean, onMouseHoverCallbacksProps]; @@ -142,3 +164,284 @@ export const useMaxMinPropertyPaneFieldsFocused = () => { isPropertyPaneMinFieldFocused, }; }; + +export function useAutoHeightOverlayUIStateActions() { + const autoHeightUIDispatch = useAutoHeightLimitsDispatch(); + + const setIsMaxDotDragging = useCallback((isMaxDotDragging: boolean) => { + autoHeightUIDispatch({ + type: "SET_IS_MAX_DOT_DRAGGING", + payload: { + isMaxDotDragging, + }, + }); + }, []); + + const setIsMinDotDragging = useCallback((isMinDotDragging: boolean) => { + autoHeightUIDispatch({ + type: "SET_IS_MIN_DOT_DRAGGING", + payload: { + isMinDotDragging, + }, + }); + }, []); + + const setMaxY = useCallback((maxY: number) => { + autoHeightUIDispatch({ + type: "SET_MAX_Y", + payload: { + maxY, + }, + }); + }, []); + + const setMinY = useCallback((minY: number) => { + autoHeightUIDispatch({ + type: "SET_MIN_Y", + payload: { + minY, + }, + }); + }, []); + + const setMaxdY = useCallback((maxdY: number) => { + autoHeightUIDispatch({ + type: "SET_MAX_D_Y", + payload: { + maxdY, + }, + }); + }, []); + + const setMindY = useCallback((mindY: number) => { + autoHeightUIDispatch({ + type: "SET_MIN_D_Y", + payload: { + mindY, + }, + }); + }, []); + + return { + setIsMaxDotDragging, + setMaxY, + setMaxdY, + setIsMinDotDragging, + setMinY, + setMindY, + }; +} + +export function useDragCallbacksForHandles({ + batchUpdate, + parentColumnSpace, + parentRowSpace, + updateMaxHeight, + updateMinHeight, + widgetId, +}: { + widgetId: string; + parentColumnSpace: number; + parentRowSpace: number; + updateMaxHeight: (height: number) => void; + updateMinHeight: (height: number) => void; + batchUpdate: (options: { maxHeight?: number; minHeight?: number }) => void; +}) { + const { selectWidget } = useWidgetSelection(); + const selectedWidget = useSelector( + (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, + ); + const showPropertyPane = useShowPropertyPane(); + + const parentWidgetToSelect = useSelector(getParentToOpenSelector(widgetId)); + + const showTableFilterPane = useShowTableFilterPane(); + const { + isAutoHeightWithLimitsChanging, + setIsAutoHeightWithLimitsChanging, + } = useAutoHeightUIState(); + + const { maxdY, maxY, mindY, minY } = useAutoHeightLimitsState(); + + const { + setIsMaxDotDragging, + setIsMinDotDragging, + setMaxdY, + setMindY, + } = useAutoHeightOverlayUIStateActions(); + + const snapGrid = useMemo( + () => ({ + x: parentColumnSpace, + y: parentRowSpace, + }), + [parentColumnSpace, parentRowSpace], + ); + + const onAnyDotStart = useCallback(() => { + setIsAutoHeightWithLimitsChanging && + !isAutoHeightWithLimitsChanging && + setIsAutoHeightWithLimitsChanging(true); + selectWidget && selectedWidget !== widgetId && selectWidget(widgetId); + // Make sure that this tableFilterPane should close + showTableFilterPane && showTableFilterPane(); + }, [widgetId]); + + const onAnyDotStop = useCallback(() => { + // Tell the Canvas that we've stopped resizing + // Put it later in the stack so that other updates like click, are not propagated to the parent container + setTimeout(() => { + setIsAutoHeightWithLimitsChanging && + setIsAutoHeightWithLimitsChanging(false); + }, 0); + + selectWidget && selectWidget(widgetId); + + if (parentWidgetToSelect) { + selectWidget && + selectedWidget !== parentWidgetToSelect.widgetId && + selectWidget(parentWidgetToSelect.widgetId); + focusWidget(parentWidgetToSelect.widgetId); + } else { + selectWidget && selectedWidget !== widgetId && selectWidget(widgetId); + } + // Property pane closes after a resize/drag + showPropertyPane && showPropertyPane(); + }, [widgetId]); + + const onMaxUpdate = useCallback( + (dx: number, dy: number) => { + const snapped = getSnappedValues(dx, dy, snapGrid); + + if (snapped.y === maxdY) { + return; + } + + if ( + maxY + dy <= + (WidgetHeightLimits.MIN_HEIGHT_IN_ROWS + 1) * // now max will always be minimum GridDefaults.DEFAULT_GRID_ROW_HEIGHT + 1 rows + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + ) { + return; + } + + if (maxY + snapped.y <= minY + mindY) { + setMindY( + snapped.y + (maxY - minY) - GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + ); + } else { + // Moving together after min has been decreased + // while moving with the max + if ( + snapped.y - maxdY > 0 && // to check whether max is increasing now + mindY < 0 && // to check whether we want to increase the min because it may have decreased with the max + maxY + maxdY - GridDefaults.DEFAULT_GRID_ROW_HEIGHT >= minY + mindY // to check whether we still have one row difference + ) { + setMindY(Math.min(mindY + (snapped.y - maxdY), 0)); + } + } + + setMaxdY(snapped.y); + }, + [maxdY, maxY, minY, mindY], + ); + + const onMinUpdate = useCallback( + (dx: number, dy: number) => { + const snapped = getSnappedValues(dx, dy, snapGrid); + + if (snapped.y === mindY) { + return; + } + + if ( + minY + dy <= + WidgetHeightLimits.MIN_HEIGHT_IN_ROWS * + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + ) { + return; + } + + // Moving together when increasing the min + if (minY + snapped.y >= maxY + maxdY) { + setMaxdY( + GridDefaults.DEFAULT_GRID_ROW_HEIGHT + snapped.y - (maxY - minY), + ); + } else { + // Moving together after max has been increased + // while moving with the min + if ( + snapped.y - mindY < 0 && // to check whether min is decreasing now + maxdY > 0 && // to check whether we want to decrease the max because it may have increased with the min + minY + mindY + GridDefaults.DEFAULT_GRID_ROW_HEIGHT <= maxY + maxdY // to check whether we still have one row difference + ) { + setMaxdY(Math.max(maxdY + (snapped.y - mindY), 0)); + } + } + + setMindY(snapped.y); + }, + [snapGrid, minY, maxY, maxdY, mindY], + ); + + const onMinDotStart = useCallback(() => { + setIsMinDotDragging(true); + onAnyDotStart(); + }, []); + + const onMaxDotStart = useCallback(() => { + setIsMaxDotDragging(true); + onAnyDotStart(); + }, []); + + function tryBatchUpdateIfPossible(): boolean { + // if there are changes to both + // use batch + if (mindY !== 0 && maxdY !== 0) { + batchUpdate({ + maxHeight: maxY + maxdY, + minHeight: minY + mindY, + }); + return true; + } + + return false; + } + + function onMaxStop() { + setIsMaxDotDragging(false); + + if (!tryBatchUpdateIfPossible()) { + // since no batch update + // update only max + if (maxdY !== 0) { + updateMaxHeight(maxY + maxdY); + } + } + + onAnyDotStop(); + } + + function onMinStop() { + setIsMinDotDragging(false); + + if (!tryBatchUpdateIfPossible()) { + // since no batch update + // update only max + if (mindY !== 0) { + updateMinHeight(minY + mindY); + } + } + + onAnyDotStop(); + } + + return { + onMaxDotStart, + onMinDotStart, + onMaxUpdate, + onMinUpdate, + onMinStop, + onMaxStop, + }; +} diff --git a/app/client/src/components/autoHeightOverlay/index.tsx b/app/client/src/components/autoHeightOverlay/index.tsx index 795a9fea63..d0aada86d6 100644 --- a/app/client/src/components/autoHeightOverlay/index.tsx +++ b/app/client/src/components/autoHeightOverlay/index.tsx @@ -1,376 +1,24 @@ -import { focusWidget } from "actions/widgetActions"; -import React, { - CSSProperties, - memo, - useEffect, - useMemo, - useReducer, -} from "react"; -import { useSelector } from "react-redux"; import { AppState } from "@appsmith/reducers"; -import styled from "styled-components"; -import { - useShowPropertyPane, - useShowTableFilterPane, -} from "utils/hooks/dragResizeHooks"; -import { useWidgetSelection } from "utils/hooks/useWidgetSelection"; -import { WidgetProps } from "widgets/BaseWidget"; -import { GridDefaults, WidgetHeightLimits } from "constants/WidgetConstants"; -import { getParentToOpenSelector } from "selectors/widgetSelectors"; -import AutoHeightLimitHandleGroup from "./AutoHeightLimitHandleGroup"; -import AutoHeightLimitOverlayDisplay from "./ui/AutoHeightLimitOverlayDisplay"; -import { useHoverState, usePositionedStyles } from "./hooks"; -import { getSnappedValues } from "./utils"; -import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks"; -import { LayersContext } from "constants/Layers"; -import { - AutoHeightOverlayUIStateReducer, - createInitialAutoHeightUIState, -} from "./store"; +import React, { CSSProperties, memo } from "react"; +import { useSelector } from "react-redux"; import { previewModeSelector } from "selectors/editorSelectors"; +import { WidgetProps } from "widgets/BaseWidget"; +import AutoHeightOverlayWithStateContext from "./AutoHeightOverlayWithStateContext"; -interface StyledAutoHeightOverlayProps { - layerIndex: number; - isHidden: boolean; -} - -const StyledAutoHeightOverlay = styled.div` - width: 100%; - height: 100%; - position: absolute; - z-index: ${(props) => props.layerIndex}; - pointer-events: none; - display: ${(props) => (props.isHidden ? "none" : "block")}; -`; - -interface MinMaxHeightProps { +export interface MinMaxHeightProps { maxDynamicHeight: number; minDynamicHeight: number; } -interface AutoHeightOverlayContainerProps +export interface AutoHeightOverlayContainerProps extends MinMaxHeightProps, WidgetProps { - batchUpdate: (height: number) => void; + batchUpdate: (options: { minHeight?: number; maxHeight?: number }) => void; onMaxHeightSet: (height: number) => void; onMinHeightSet: (height: number) => void; style?: CSSProperties; } -interface AutoHeightOverlayProps extends AutoHeightOverlayContainerProps { - isHidden: boolean; -} - -const AutoHeightOverlay: React.FC = memo( - ({ - batchUpdate, - isHidden, - maxDynamicHeight, - minDynamicHeight, - onMaxHeightSet, - onMinHeightSet, - style, - ...props - }) => { - const showPropertyPane = useShowPropertyPane(); - const { selectWidget } = useWidgetSelection(); - const selectedWidget = useSelector( - (state: AppState) => state.ui.widgetDragResize.lastSelectedWidget, - ); - - const parentWidgetToSelect = useSelector( - getParentToOpenSelector(props.widgetId), - ); - const showTableFilterPane = useShowTableFilterPane(); - const { - isAutoHeightWithLimitsChanging, - setIsAutoHeightWithLimitsChanging, - } = useAutoHeightUIState(); - - const [autoHeightUIState, autoHeightUIStateDispatch] = useReducer( - AutoHeightOverlayUIStateReducer, - createInitialAutoHeightUIState({ maxDynamicHeight, minDynamicHeight }), - ); - - const { - isMaxDotDragging, - isMinDotDragging, - maxdY, - maxY, - mindY, - minY, - } = autoHeightUIState; - - function setIsMaxDotDragging(isMaxDotDragging: boolean) { - autoHeightUIStateDispatch({ - type: "SET_IS_MAX_DOT_DRAGGING", - payload: { - isMaxDotDragging, - }, - }); - } - - function setIsMinDotDragging(isMinDotDragging: boolean) { - autoHeightUIStateDispatch({ - type: "SET_IS_MIN_DOT_DRAGGING", - payload: { - isMinDotDragging, - }, - }); - } - - function setMaxY(maxY: number) { - autoHeightUIStateDispatch({ - type: "SET_MAX_Y", - payload: { - maxY, - }, - }); - } - - function setMinY(minY: number) { - autoHeightUIStateDispatch({ - type: "SET_MIN_Y", - payload: { - minY, - }, - }); - } - - function setMaxdY(maxdY: number) { - autoHeightUIStateDispatch({ - type: "SET_MAX_D_Y", - payload: { - maxdY, - }, - }); - } - - function setMindY(mindY: number) { - autoHeightUIStateDispatch({ - type: "SET_MIN_D_Y", - payload: { - mindY, - }, - }); - } - - const finalMaxY = maxY + maxdY; - const finalMinY = minY + mindY; - - useEffect(() => { - // reset the diff on backend update - setMindY(0); - setMaxdY(0); - setMaxY(maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); - }, [maxDynamicHeight]); - - function onAnyDotStop() { - // Tell the Canvas that we've stopped resizing - // Put it later in the stack so that other updates like click, are not propagated to the parent container - setTimeout(() => { - setIsAutoHeightWithLimitsChanging && - setIsAutoHeightWithLimitsChanging(false); - }, 0); - - selectWidget && selectWidget(props.widgetId); - - if (parentWidgetToSelect) { - selectWidget && - selectedWidget !== parentWidgetToSelect.widgetId && - selectWidget(parentWidgetToSelect.widgetId); - focusWidget(parentWidgetToSelect.widgetId); - } else { - selectWidget && - selectedWidget !== props.widgetId && - selectWidget(props.widgetId); - } - // Property pane closes after a resize/drag - showPropertyPane && showPropertyPane(); - } - - function onMaxUpdate(dx: number, dy: number) { - if ( - maxY + dy <= - WidgetHeightLimits.MIN_HEIGHT_IN_ROWS * - GridDefaults.DEFAULT_GRID_ROW_HEIGHT - ) { - return; - } - - const snapped = getSnappedValues(dx, dy, snapGrid); - - if (maxY + snapped.y <= minY) { - setMindY(snapped.y + (maxY - minY)); - } - - setMaxdY(snapped.y); - } - - function updateMaxHeight(height: number) { - setMaxY(height); - onMaxHeightSet(height); - } - - function updateMinHeight(height: number) { - setMinY(height); - onMinHeightSet(height); - } - - function onMaxStop() { - setIsMaxDotDragging(false); - const heightToSet = maxY + maxdY; - - if (heightToSet === minY + mindY) { - batchUpdate(heightToSet); - } else { - updateMaxHeight(heightToSet); - setMaxdY(0); - } - - onAnyDotStop(); - } - - useEffect(() => { - // reset the diff on backend update - setMindY(0); - setMaxdY(0); - setMinY(minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT); - }, [minDynamicHeight]); - - function onMinUpdate(dx: number, dy: number) { - if ( - minY + dy <= - WidgetHeightLimits.MIN_HEIGHT_IN_ROWS * - GridDefaults.DEFAULT_GRID_ROW_HEIGHT - ) { - return; - } - - const snapped = getSnappedValues(dx, dy, snapGrid); - - if (minY + snapped.y >= maxY) { - setMaxdY(snapped.y - (maxY - minY)); - } - - setMindY(snapped.y); - } - - function onMinStop() { - setIsMinDotDragging(false); - const heightToSet = minY + mindY; - - if (heightToSet === maxY + maxdY) { - batchUpdate(heightToSet); - } else { - updateMinHeight(heightToSet); - setMindY(0); - } - - onAnyDotStop(); - } - - function onMinDotStart() { - setIsMinDotDragging(true); - onAnyDotStart(); - } - - function onAnyDotStart() { - setIsAutoHeightWithLimitsChanging && - !isAutoHeightWithLimitsChanging && - setIsAutoHeightWithLimitsChanging(true); - selectWidget && - selectedWidget !== props.widgetId && - selectWidget(props.widgetId); - // Make sure that this tableFilterPane should close - showTableFilterPane && showTableFilterPane(); - } - - function onMaxDotStart() { - setIsMaxDotDragging(true); - onAnyDotStart(); - } - - const [isMinDotActive, minHoverFns] = useHoverState(); - const [isMaxDotActive, maxHoverFns] = useHoverState(); - - const snapGrid = useMemo( - () => ({ - x: props.parentColumnSpace, - y: props.parentRowSpace, - }), - [props.parentColumnSpace, props.parentRowSpace], - ); - - const { - bottomRow, - leftColumn, - noContainerOffset, - parentColumnSpace, - parentRowSpace, - rightColumn, - topRow, - } = props; - - const styles = usePositionedStyles({ - bottomRow, - leftColumn, - noContainerOffset, - parentColumnSpace, - parentRowSpace, - rightColumn, - topRow, - }); - - const { autoHeightWithLimitsOverlay } = React.useContext(LayersContext); - - return ( - { - // avoid DropTarget handleFocus - e.stopPropagation(); - }} - style={style ?? styles} - > - - - - - ); - }, -); - const AutoHeightOverlayContainer: React.FC = memo( (props) => { const widgetId = props.widgetId; @@ -388,7 +36,9 @@ const AutoHeightOverlayContainer: React.FC = me const isHidden = multipleWidgetsSelected || isDragging || isResizing; if (isWidgetSelected && !isPreviewMode) { - return ; + return ( + + ); } return null; diff --git a/app/client/src/components/autoHeightOverlay/store.ts b/app/client/src/components/autoHeightOverlay/store.tsx similarity index 69% rename from app/client/src/components/autoHeightOverlay/store.ts rename to app/client/src/components/autoHeightOverlay/store.tsx index d1174da885..a20e9053bf 100644 --- a/app/client/src/components/autoHeightOverlay/store.ts +++ b/app/client/src/components/autoHeightOverlay/store.tsx @@ -1,4 +1,5 @@ import { GridDefaults } from "constants/WidgetConstants"; +import React, { useContext, useReducer } from "react"; interface AutoHeightLimitsUIState { isMaxDotDragging: boolean; @@ -98,3 +99,39 @@ export function createInitialAutoHeightUIState({ mindY: 0, // the difference during dragging }; } + +interface StateContextType { + state: AutoHeightLimitsUIState; + dispatch: React.Dispatch; +} + +export const AutoHeightLimitsStateContext = React.createContext< + StateContextType +>({} as StateContextType); + +export const AutoHeightLimitsStateContextProvider: React.FC<{ + children: React.ReactNode; + maxDynamicHeight: number; + minDynamicHeight: number; +}> = ({ children, maxDynamicHeight, minDynamicHeight }) => { + const [state, dispatch] = useReducer( + AutoHeightOverlayUIStateReducer, + createInitialAutoHeightUIState({ maxDynamicHeight, minDynamicHeight }), + ); + + return ( + + {children} + + ); +}; + +export const useAutoHeightLimitsDispatch = () => { + const { dispatch } = useContext(AutoHeightLimitsStateContext); + return dispatch; +}; + +export const useAutoHeightLimitsState = () => { + const { state } = useContext(AutoHeightLimitsStateContext); + return state; +}; diff --git a/app/client/src/widgets/BaseWidget.tsx b/app/client/src/widgets/BaseWidget.tsx index 97346e047a..a876c986da 100644 --- a/app/client/src/widgets/BaseWidget.tsx +++ b/app/client/src/widgets/BaseWidget.tsx @@ -364,16 +364,30 @@ abstract class BaseWidget< } addAutoHeightOverlay(content: ReactNode, style?: CSSProperties) { - const onBatchUpdate = (height: number, propertiesToUpdate?: string[]) => { - if (propertiesToUpdate === undefined) { - propertiesToUpdate = ["minDynamicHeight", "maxDynamicHeight"]; - } + // required when the limits have to be updated + // simultaneosuly when they move together + // to maintain the undo/redo stack + const onBatchUpdate = ({ + maxHeight, + minHeight, + }: { + maxHeight?: number; + minHeight?: number; + }) => { const modifyObj: Record = {}; - propertiesToUpdate.forEach((propertyName) => { - modifyObj[propertyName] = Math.floor( - height / GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + + if (maxHeight !== undefined) { + modifyObj["maxDynamicHeight"] = Math.floor( + maxHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT, ); - }); + } + + if (minHeight !== undefined) { + modifyObj["minDynamicHeight"] = Math.floor( + minHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT, + ); + } + this.batchUpdateWidgetProperty({ modify: modifyObj, postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT, @@ -381,11 +395,9 @@ abstract class BaseWidget< AnalyticsUtil.logEvent("AUTO_HEIGHT_OVERLAY_HANDLES_UPDATE", modifyObj); }; - const onMaxHeightSet = (height: number) => - onBatchUpdate(height, ["maxDynamicHeight"]); + const onMaxHeightSet = (maxHeight: number) => onBatchUpdate({ maxHeight }); - const onMinHeightSet = (height: number) => - onBatchUpdate(height, ["minDynamicHeight"]); + const onMinHeightSet = (minHeight: number) => onBatchUpdate({ minHeight }); return ( <>