fix: Switch to preview mode performance issue (#18457)
* In progress fixes for auto height perf * Revert collapse logic * Revert changes * Remove console logs, and fix tests * Fix scenario where container widgets don't collapse * Bring back hidden widgets * fix: Overlapping of widgets while reflowing other widgets (#18460) * fix: api url z-index to show it above response panel (#18200) * chore: Switched to sequential checks instead of a parallel one for RTS check (#18440) fix: Switched to sequential checks instead of a parallel one * chore: Added size limit check for the message before sending the data to segment (#18453) * fix: Allowing multi form to json switching and eliminating json to form sw… (#18192) * Allowing multi form to json switching and eliminating json to form switching unless form data is cleared * Fix failing jest test case Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> * add missed width and height limits instead of incrementing depth by 1 Co-authored-by: Aman Agarwal <aman@appsmith.com> Co-authored-by: Nidhi <nidhi@appsmith.com> Co-authored-by: Anagh Hegde <anagh@appsmith.com> Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> * fix: auto height with limits deselect widget (#18455) * fix: api url z-index to show it above response panel (#18200) * chore: Switched to sequential checks instead of a parallel one for RTS check (#18440) fix: Switched to sequential checks instead of a parallel one * chore: Added size limit check for the message before sending the data to segment (#18453) * fix: Allowing multi form to json switching and eliminating json to form sw… (#18192) * Allowing multi form to json switching and eliminating json to form switching unless form data is cleared * Fix failing jest test case Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> * refactor overlay and handles state into ui reducer and added a check for is limits changing in the widgets editor as well * added helpful comments at relevant places Co-authored-by: Aman Agarwal <aman@appsmith.com> Co-authored-by: Nidhi <nidhi@appsmith.com> Co-authored-by: Anagh Hegde <anagh@appsmith.com> Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> * changes the label on limit handles collision to height * fix: issues related to tabs widget in auto height (#18468) * Fix issues related to tabs widget in auto height * Fix issue where preview mode canvas did not scroll * Fix scroll issues with fixed containers * Fix issue where tabs widget computed the canvas height incorrectly * Re-compute in case of tabs widget * fix: widgets increase spacing (#18462) * Change how the spacing works when widgets push or pull widgets below * Fix type issues in test file * Fix tests to reflect changes * Add comment to describe why we're generating distanceToNearestAbove Co-authored-by: rahulramesha <71900764+rahulramesha@users.noreply.github.com> Co-authored-by: Aman Agarwal <aman@appsmith.com> Co-authored-by: Nidhi <nidhi@appsmith.com> Co-authored-by: Anagh Hegde <anagh@appsmith.com> Co-authored-by: Ayangade Adeoluwa <37867493+Irongade@users.noreply.github.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> Co-authored-by: ankurrsinghal <ankur@appsmith.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com>
This commit is contained in:
parent
3144c0a452
commit
b2070083a6
|
|
@ -18,7 +18,7 @@ describe("Preview mode functionality", function() {
|
|||
it("checks if widgets can be selected or not", function() {
|
||||
// in preview mode, entity explorer and property pane are not visible
|
||||
// Also, draggable and resizable components are not available.
|
||||
const selector = `.t--widget-buttonwidget`;
|
||||
const selector = `.t--draggable-buttonwidget`;
|
||||
cy.wait(500);
|
||||
cy.get(selector)
|
||||
.first()
|
||||
|
|
|
|||
|
|
@ -22,9 +22,7 @@ function AutoHeightContainerWrapper(props: AutoHeightWrapperProps) {
|
|||
if (isCanvas) return <>{children}</>;
|
||||
|
||||
const onHeightUpdate = (height: number) => {
|
||||
requestAnimationFrame(() => {
|
||||
props.onUpdateDynamicHeight(height);
|
||||
});
|
||||
props.onUpdateDynamicHeight(height);
|
||||
};
|
||||
|
||||
const maxDynamicHeight = getWidgetMaxAutoHeight(widgetProps);
|
||||
|
|
|
|||
|
|
@ -151,9 +151,9 @@ const AutoHeightLimitHandleGroup: React.FC<AutoHeightLimitHandleGroupProps> = ({
|
|||
cypressDataID="t--auto-height-overlay-handles-max"
|
||||
height={maxY}
|
||||
isActive={isMaxDotActive}
|
||||
isColliding={isColliding}
|
||||
isColliding={false}
|
||||
isDragging={isMaxDotDragging}
|
||||
label="Max-Height"
|
||||
label={isColliding ? "Height" : "Max-Height"}
|
||||
onDragCallbacks={onMaxLimitDragCallbacks}
|
||||
onMouseHoverFunctions={onMaxLimitMouseHoverCallbacks}
|
||||
/>
|
||||
|
|
|
|||
|
|
@ -1,6 +1,11 @@
|
|||
import { focusWidget } from "actions/widgetActions";
|
||||
import React, { CSSProperties, memo, useEffect, useMemo } from "react";
|
||||
import { useState } from "react";
|
||||
import React, {
|
||||
CSSProperties,
|
||||
memo,
|
||||
useEffect,
|
||||
useMemo,
|
||||
useReducer,
|
||||
} from "react";
|
||||
import { useSelector } from "react-redux";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import styled from "styled-components";
|
||||
|
|
@ -18,6 +23,11 @@ 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 { previewModeSelector } from "selectors/editorSelectors";
|
||||
|
||||
interface StyledAutoHeightOverlayProps {
|
||||
layerIndex: number;
|
||||
|
|
@ -72,41 +82,93 @@ const AutoHeightOverlay: React.FC<AutoHeightOverlayProps> = memo(
|
|||
getParentToOpenSelector(props.widgetId),
|
||||
);
|
||||
const showTableFilterPane = useShowTableFilterPane();
|
||||
const { setIsAutoHeightWithLimitsChanging } = useAutoHeightUIState();
|
||||
const isAutoHeightWithLimitsChanging = useSelector(
|
||||
(state: AppState) => state.ui.autoHeightUI.isAutoHeightWithLimitsChanging,
|
||||
const {
|
||||
isAutoHeightWithLimitsChanging,
|
||||
setIsAutoHeightWithLimitsChanging,
|
||||
} = useAutoHeightUIState();
|
||||
|
||||
const [autoHeightUIState, autoHeightUIStateDispatch] = useReducer(
|
||||
AutoHeightOverlayUIStateReducer,
|
||||
createInitialAutoHeightUIState({ maxDynamicHeight, minDynamicHeight }),
|
||||
);
|
||||
|
||||
const [isMinDotDragging, setIsMinDotDragging] = useState(false);
|
||||
const [isMaxDotDragging, setIsMaxDotDragging] = useState(false);
|
||||
const {
|
||||
isMaxDotDragging,
|
||||
isMinDotDragging,
|
||||
maxdY,
|
||||
maxY,
|
||||
mindY,
|
||||
minY,
|
||||
} = autoHeightUIState;
|
||||
|
||||
const [maxY, setMaxY] = useState(
|
||||
maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
);
|
||||
const [maxdY, setMaxdY] = useState(0);
|
||||
function setIsMaxDotDragging(isMaxDotDragging: boolean) {
|
||||
autoHeightUIStateDispatch({
|
||||
type: "SET_IS_MAX_DOT_DRAGGING",
|
||||
payload: {
|
||||
isMaxDotDragging,
|
||||
},
|
||||
});
|
||||
}
|
||||
|
||||
const [minY, setMinY] = useState(
|
||||
minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
);
|
||||
const [mindY, setMindY] = useState(0);
|
||||
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;
|
||||
|
||||
// to be included when min and max fields are
|
||||
// added back to the property pane
|
||||
// const {
|
||||
// isPropertyPaneMaxFieldFocused,
|
||||
// isPropertyPaneMinFieldFocused,
|
||||
// } = useMaxMinPropertyPaneFieldsFocused();
|
||||
|
||||
useEffect(() => {
|
||||
setMaxY(maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT);
|
||||
}, [maxDynamicHeight]);
|
||||
|
||||
function onAnyDotStop() {
|
||||
setIsAutoHeightWithLimitsChanging &&
|
||||
setIsAutoHeightWithLimitsChanging(false);
|
||||
// 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);
|
||||
|
||||
|
|
@ -317,11 +379,13 @@ const AutoHeightOverlayContainer: React.FC<AutoHeightOverlayContainerProps> = me
|
|||
selectedWidgets,
|
||||
} = useSelector((state: AppState) => state.ui.widgetDragResize);
|
||||
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
|
||||
const isWidgetSelected = selectedWidget === widgetId;
|
||||
const multipleWidgetsSelected = selectedWidgets.length > 1;
|
||||
const isHidden = multipleWidgetsSelected || isDragging || isResizing;
|
||||
|
||||
if (isWidgetSelected) {
|
||||
if (isWidgetSelected && !isPreviewMode) {
|
||||
return <AutoHeightOverlay isHidden={isHidden} {...props} />;
|
||||
}
|
||||
|
||||
|
|
|
|||
100
app/client/src/components/autoHeightOverlay/store.ts
Normal file
100
app/client/src/components/autoHeightOverlay/store.ts
Normal file
|
|
@ -0,0 +1,100 @@
|
|||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
|
||||
interface AutoHeightLimitsUIState {
|
||||
isMaxDotDragging: boolean;
|
||||
isMinDotDragging: boolean;
|
||||
maxY: number; // the actual value
|
||||
maxdY: number; // the difference during dragging
|
||||
minY: number; // the actual value
|
||||
mindY: number; // the difference during dragging
|
||||
}
|
||||
|
||||
type SET_MAX_Y = { type: "SET_MAX_Y"; payload: { maxY: number } };
|
||||
type SET_MIN_Y = { type: "SET_MIN_Y"; payload: { minY: number } };
|
||||
type SET_MAX_D_Y = { type: "SET_MAX_D_Y"; payload: { maxdY: number } };
|
||||
type SET_MIN_D_Y = { type: "SET_MIN_D_Y"; payload: { mindY: number } };
|
||||
type SET_IS_MIN_DOT_DRAGGING = {
|
||||
type: "SET_IS_MIN_DOT_DRAGGING";
|
||||
payload: { isMinDotDragging: boolean };
|
||||
};
|
||||
|
||||
type SET_IS_MAX_DOT_DRAGGING = {
|
||||
type: "SET_IS_MAX_DOT_DRAGGING";
|
||||
payload: { isMaxDotDragging: boolean };
|
||||
};
|
||||
|
||||
type AutoHeightLimitsUIAction =
|
||||
| SET_MAX_Y
|
||||
| SET_MIN_Y
|
||||
| SET_MAX_D_Y
|
||||
| SET_MIN_D_Y
|
||||
| SET_IS_MIN_DOT_DRAGGING
|
||||
| SET_IS_MAX_DOT_DRAGGING;
|
||||
|
||||
export function AutoHeightOverlayUIStateReducer(
|
||||
state: AutoHeightLimitsUIState,
|
||||
action: AutoHeightLimitsUIAction,
|
||||
) {
|
||||
if (action.type === "SET_IS_MAX_DOT_DRAGGING") {
|
||||
return {
|
||||
...state,
|
||||
isMaxDotDragging: action.payload.isMaxDotDragging,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === "SET_IS_MIN_DOT_DRAGGING") {
|
||||
return {
|
||||
...state,
|
||||
isMinDotDragging: action.payload.isMinDotDragging,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === "SET_MAX_Y") {
|
||||
return {
|
||||
...state,
|
||||
maxY: action.payload.maxY,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === "SET_MIN_Y") {
|
||||
return {
|
||||
...state,
|
||||
minY: action.payload.minY,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === "SET_MAX_D_Y") {
|
||||
return {
|
||||
...state,
|
||||
maxdY: action.payload.maxdY,
|
||||
};
|
||||
}
|
||||
|
||||
if (action.type === "SET_MIN_D_Y") {
|
||||
return {
|
||||
...state,
|
||||
mindY: action.payload.mindY,
|
||||
};
|
||||
}
|
||||
|
||||
return state;
|
||||
}
|
||||
|
||||
interface CreateInitialAutoHeightUIStateProps {
|
||||
maxDynamicHeight: number;
|
||||
minDynamicHeight: number;
|
||||
}
|
||||
|
||||
export function createInitialAutoHeightUIState({
|
||||
maxDynamicHeight,
|
||||
minDynamicHeight,
|
||||
}: CreateInitialAutoHeightUIStateProps) {
|
||||
return {
|
||||
isMinDotDragging: false,
|
||||
isMaxDotDragging: false,
|
||||
maxY: maxDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, // the actual value
|
||||
maxdY: 0, // the difference during dragging
|
||||
minY: minDynamicHeight * GridDefaults.DEFAULT_GRID_ROW_HEIGHT, // the actual value
|
||||
mindY: 0, // the difference during dragging
|
||||
};
|
||||
}
|
||||
|
|
@ -2,9 +2,17 @@ import { canDrag } from "./DraggableComponent";
|
|||
|
||||
describe("DraggableComponent", () => {
|
||||
it("it tests draggable canDrag helper function", () => {
|
||||
expect(canDrag(false, false, { dragDisabled: false }, false)).toBe(true);
|
||||
expect(canDrag(true, false, { dragDisabled: false }, false)).toBe(false);
|
||||
expect(canDrag(false, true, { dragDisabled: false }, false)).toBe(false);
|
||||
expect(canDrag(false, false, { dragDisabled: true }, false)).toBe(false);
|
||||
expect(canDrag(false, false, { dragDisabled: false }, false, false)).toBe(
|
||||
true,
|
||||
);
|
||||
expect(canDrag(true, false, { dragDisabled: false }, false, false)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(canDrag(false, true, { dragDisabled: false }, false, false)).toBe(
|
||||
false,
|
||||
);
|
||||
expect(canDrag(false, false, { dragDisabled: true }, false, false)).toBe(
|
||||
false,
|
||||
);
|
||||
});
|
||||
});
|
||||
|
|
|
|||
|
|
@ -9,7 +9,10 @@ import {
|
|||
useShowTableFilterPane,
|
||||
useWidgetDragResize,
|
||||
} from "utils/hooks/dragResizeHooks";
|
||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import {
|
||||
previewModeSelector,
|
||||
snipingModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { useWidgetSelection } from "utils/hooks/useWidgetSelection";
|
||||
import {
|
||||
isCurrentWidgetFocused,
|
||||
|
|
@ -56,12 +59,14 @@ export const canDrag = (
|
|||
isDraggingDisabled: boolean,
|
||||
props: any,
|
||||
isSnipingMode: boolean,
|
||||
isPreviewMode: boolean,
|
||||
) => {
|
||||
return (
|
||||
!isResizingOrDragging &&
|
||||
!isDraggingDisabled &&
|
||||
!props?.dragDisabled &&
|
||||
!isSnipingMode
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode
|
||||
);
|
||||
};
|
||||
|
||||
|
|
@ -69,6 +74,7 @@ function DraggableComponent(props: DraggableComponentProps) {
|
|||
// Dispatch hook handy to set a widget as focused/selected
|
||||
const { focusWidget, selectWidget } = useWidgetSelection();
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
// Dispatch hook handy to set any `DraggableComponent` as dragging/ not dragging
|
||||
// The value is boolean
|
||||
const { setDraggingCanvas, setDraggingState } = useWidgetDragResize();
|
||||
|
|
@ -136,6 +142,7 @@ function DraggableComponent(props: DraggableComponentProps) {
|
|||
isDraggingDisabled,
|
||||
props,
|
||||
isSnipingMode,
|
||||
isPreviewMode,
|
||||
);
|
||||
const className = `${classNameForTesting}`;
|
||||
const draggableRef = useRef<HTMLDivElement>(null);
|
||||
|
|
|
|||
|
|
@ -24,9 +24,13 @@ import {
|
|||
useShowPropertyPane,
|
||||
useCanvasSnapRowsUpdateHook,
|
||||
} from "utils/hooks/dragResizeHooks";
|
||||
import { getOccupiedSpacesSelectorForContainer } from "selectors/editorSelectors";
|
||||
import {
|
||||
getOccupiedSpacesSelectorForContainer,
|
||||
previewModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { useWidgetSelection } from "utils/hooks/useWidgetSelection";
|
||||
import { getDragDetails } from "sagas/selectors";
|
||||
import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks";
|
||||
|
||||
type DropTargetComponentProps = WidgetProps & {
|
||||
children?: ReactNode;
|
||||
|
|
@ -64,19 +68,52 @@ export const DropTargetContext: Context<{
|
|||
) => number | false;
|
||||
}> = createContext({});
|
||||
|
||||
export function DropTargetComponent(props: DropTargetComponentProps) {
|
||||
const canDropTargetExtend = props.canExtend;
|
||||
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
||||
/**
|
||||
* Gets the dropTarget height
|
||||
* @param canDropTargetExtend boolean: Can we put widgets below the scrollview in this canvas?
|
||||
* @param isPreviewMode boolean: Are we in the preview mode
|
||||
* @param currentHeight number: Current height in the ref and what we have set in the dropTarget
|
||||
* @param snapRowSpace number: This is a static value actually, GridDefaults.DEFAULT_GRID_ROW_HEIGHT
|
||||
* @param minHeight number: The minHeight we've set to the widget in the reducer
|
||||
* @returns number: A new height style to set in the dropTarget.
|
||||
*/
|
||||
function getDropTargetHeight(
|
||||
canDropTargetExtend: boolean,
|
||||
isPreviewMode: boolean,
|
||||
currentHeight: number,
|
||||
snapRowSpace: number,
|
||||
minHeight: number,
|
||||
) {
|
||||
let height = canDropTargetExtend
|
||||
? `${Math.max(currentHeight * snapRowSpace, minHeight)}px`
|
||||
: "100%";
|
||||
if (isPreviewMode && canDropTargetExtend)
|
||||
height = `${currentHeight * snapRowSpace}px`;
|
||||
return height;
|
||||
}
|
||||
|
||||
export function DropTargetComponent(props: DropTargetComponentProps) {
|
||||
// Get if this is in preview mode.
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
// Pretty much the shouldScrollContents from the parent container like widget
|
||||
const canDropTargetExtend = props.canExtend;
|
||||
// If in preview mode, we don't need that extra row
|
||||
// This gives us the number of rows
|
||||
const snapRows = getCanvasSnapRows(
|
||||
props.bottomRow,
|
||||
props.canExtend && !isPreviewMode,
|
||||
);
|
||||
|
||||
// Are we currently resizing?
|
||||
const isResizing = useSelector(
|
||||
(state: AppState) => state.ui.widgetDragResize.isResizing,
|
||||
);
|
||||
// Are we currently dragging?
|
||||
const isDragging = useSelector(
|
||||
(state: AppState) => state.ui.widgetDragResize.isDragging,
|
||||
);
|
||||
const isAutoHeightWithLimitsChanging = useSelector(
|
||||
(state: AppState) => state.ui.autoHeightUI.isAutoHeightWithLimitsChanging,
|
||||
);
|
||||
// Are we changing the auto height limits by dragging the signifiers?
|
||||
const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState();
|
||||
|
||||
// dragDetails contains of info needed for a container jump:
|
||||
// which parent the dragging widget belongs,
|
||||
|
|
@ -87,37 +124,57 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
|
||||
const { draggedOn } = dragDetails;
|
||||
|
||||
// All the widgets in this canvas
|
||||
const childWidgets: string[] | undefined = useSelector(
|
||||
(state: AppState) => state.entities.canvasWidgets[props.widgetId]?.children,
|
||||
);
|
||||
|
||||
// The occupied spaces in this canvas. It is a data structure which has the rect values of each child.
|
||||
const selectOccupiedSpaces = useCallback(
|
||||
getOccupiedSpacesSelectorForContainer(props.widgetId),
|
||||
[props.widgetId],
|
||||
);
|
||||
|
||||
// Call the selector above.
|
||||
const occupiedSpacesByChildren = useSelector(selectOccupiedSpaces, equal);
|
||||
|
||||
// Put the existing snap rows in a ref.
|
||||
const rowRef = useRef(snapRows);
|
||||
|
||||
// This shows the property pane
|
||||
const showPropertyPane = useShowPropertyPane();
|
||||
const { deselectAll, focusWidget } = useWidgetSelection();
|
||||
const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook();
|
||||
const showDragLayer =
|
||||
(isDragging && draggedOn === props.widgetId) ||
|
||||
isResizing ||
|
||||
isAutoHeightWithLimitsChanging;
|
||||
|
||||
const { deselectAll, focusWidget } = useWidgetSelection();
|
||||
|
||||
// This updates the bottomRow of this canvas, as simple as that
|
||||
// This also doesn't cause an eval as it uses the action which is
|
||||
// not registered to cause an eval
|
||||
const updateCanvasSnapRows = useCanvasSnapRowsUpdateHook();
|
||||
|
||||
// Everytime we get a new bottomRow, or we toggle shouldScrollContents
|
||||
// we call this effect
|
||||
useEffect(() => {
|
||||
const snapRows = getCanvasSnapRows(props.bottomRow, props.canExtend);
|
||||
const snapRows = getCanvasSnapRows(
|
||||
props.bottomRow,
|
||||
props.canExtend && !isPreviewMode,
|
||||
);
|
||||
// If the current ref is not set to the new snaprows we've received (based on bottomRow)
|
||||
if (rowRef.current !== snapRows) {
|
||||
rowRef.current = snapRows;
|
||||
// This sets the "height" property of the dropTarget div
|
||||
// This makes the div change heights if new heights are different
|
||||
updateHeight();
|
||||
if (canDropTargetExtend) {
|
||||
// This sets the new rows in the reducer
|
||||
// Not sure why, as we've just received the values from the props.
|
||||
// seems like a potential way to cause recursive renders
|
||||
// See this: https://github.com/appsmithorg/appsmith/pull/18457#issuecomment-1327615572
|
||||
if (canDropTargetExtend && !isPreviewMode) {
|
||||
updateCanvasSnapRows(props.widgetId, snapRows);
|
||||
}
|
||||
}
|
||||
}, [props.bottomRow, props.canExtend]);
|
||||
}, [props.bottomRow, props.canExtend, isPreviewMode]);
|
||||
|
||||
// If we've stopped dragging, resizing or changing auto height limits
|
||||
useEffect(() => {
|
||||
if (!isDragging || !isResizing || !isAutoHeightWithLimitsChanging) {
|
||||
// bottom row of canvas can increase by any number as user moves/resizes any widget towards the bottom of the canvas
|
||||
|
|
@ -129,14 +186,38 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
}
|
||||
}, [isDragging, isResizing, isAutoHeightWithLimitsChanging]);
|
||||
|
||||
// Update the drop target height style directly.
|
||||
const updateHeight = () => {
|
||||
if (dropTargetRef.current) {
|
||||
const height = canDropTargetExtend
|
||||
? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px`
|
||||
: "100%";
|
||||
const height = getDropTargetHeight(
|
||||
canDropTargetExtend,
|
||||
isPreviewMode,
|
||||
rowRef.current,
|
||||
props.snapRowSpace,
|
||||
props.minHeight,
|
||||
);
|
||||
|
||||
dropTargetRef.current.style.height = height;
|
||||
}
|
||||
};
|
||||
|
||||
const handleFocus = (e: any) => {
|
||||
// Making sure that we don't deselect the widget
|
||||
// after we are done dragging the limits in auto height with limits
|
||||
if (!isResizing && !isDragging && !isAutoHeightWithLimitsChanging) {
|
||||
if (!props.parentId) {
|
||||
deselectAll();
|
||||
focusWidget && focusWidget(props.widgetId);
|
||||
showPropertyPane && showPropertyPane();
|
||||
}
|
||||
}
|
||||
e.preventDefault();
|
||||
};
|
||||
|
||||
/** PREPARE CONTEXT */
|
||||
|
||||
// Function which computes and updates the height of the dropTarget
|
||||
// This is used in a context and hence in one of the children of this dropTarget
|
||||
const updateDropTargetRows = (
|
||||
widgetIdsToExclude: string[],
|
||||
widgetBottomRow: number,
|
||||
|
|
@ -158,23 +239,23 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
}
|
||||
return false;
|
||||
};
|
||||
// memoizing context values
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
updateDropTargetRows,
|
||||
};
|
||||
}, [updateDropTargetRows, occupiedSpacesByChildren]);
|
||||
|
||||
const handleFocus = (e: any) => {
|
||||
if (!isResizing && !isDragging && !isAutoHeightWithLimitsChanging) {
|
||||
if (!props.parentId) {
|
||||
deselectAll();
|
||||
focusWidget && focusWidget(props.widgetId);
|
||||
showPropertyPane && showPropertyPane();
|
||||
}
|
||||
}
|
||||
// commenting this out to allow propagation of click events
|
||||
// e.stopPropagation();
|
||||
e.preventDefault();
|
||||
};
|
||||
/** EO PREPARE CONTEXT */
|
||||
|
||||
const height = getDropTargetHeight(
|
||||
canDropTargetExtend,
|
||||
isPreviewMode,
|
||||
rowRef.current,
|
||||
props.snapRowSpace,
|
||||
props.minHeight,
|
||||
);
|
||||
|
||||
const height = canDropTargetExtend
|
||||
? `${Math.max(rowRef.current * props.snapRowSpace, props.minHeight)}px`
|
||||
: "100%";
|
||||
const boxShadow =
|
||||
(isResizing || isDragging || isAutoHeightWithLimitsChanging) &&
|
||||
props.widgetId === MAIN_CONTAINER_WIDGET_ID
|
||||
|
|
@ -185,25 +266,19 @@ export function DropTargetComponent(props: DropTargetComponentProps) {
|
|||
height,
|
||||
boxShadow,
|
||||
};
|
||||
const dropTargetRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
// memoizing context values
|
||||
const contextValue = useMemo(() => {
|
||||
return {
|
||||
updateDropTargetRows,
|
||||
};
|
||||
}, [updateDropTargetRows, occupiedSpacesByChildren]);
|
||||
|
||||
const shouldOnboard =
|
||||
!(childWidgets && childWidgets.length) && !isDragging && !props.parentId;
|
||||
|
||||
if (props.widgetId !== MAIN_CONTAINER_WIDGET_ID) {
|
||||
// console.log(
|
||||
// "Dynamic height: Drop Target Height:",
|
||||
// { height },
|
||||
// { snapRows },
|
||||
// );
|
||||
}
|
||||
// The drag layer is the one with the grid dots.
|
||||
// They need to show in certain scenarios
|
||||
const showDragLayer =
|
||||
((isDragging && draggedOn === props.widgetId) ||
|
||||
isResizing ||
|
||||
isAutoHeightWithLimitsChanging) &&
|
||||
!isPreviewMode;
|
||||
|
||||
const dropTargetRef = useRef<HTMLDivElement>(null);
|
||||
|
||||
return (
|
||||
<DropTargetContext.Provider value={contextValue}>
|
||||
|
|
|
|||
|
|
@ -32,7 +32,10 @@ import {
|
|||
BottomRightHandleStyles,
|
||||
} from "./ResizeStyledComponents";
|
||||
import AnalyticsUtil from "utils/AnalyticsUtil";
|
||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import {
|
||||
snipingModeSelector,
|
||||
previewModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { useWidgetSelection } from "utils/hooks/useWidgetSelection";
|
||||
import { focusWidget } from "actions/widgetActions";
|
||||
import { GridDefaults } from "constants/WidgetConstants";
|
||||
|
|
@ -58,6 +61,7 @@ export const ResizableComponent = memo(function ResizableComponent(
|
|||
const { updateWidget } = useContext(EditorContext);
|
||||
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
|
||||
const showPropertyPane = useShowPropertyPane();
|
||||
const showTableFilterPane = useShowTableFilterPane();
|
||||
|
|
@ -243,7 +247,11 @@ export const ResizableComponent = memo(function ResizableComponent(
|
|||
}, [props]);
|
||||
|
||||
const isEnabled =
|
||||
!isDragging && isWidgetFocused && !props.resizeDisabled && !isSnipingMode;
|
||||
!isDragging &&
|
||||
isWidgetFocused &&
|
||||
!props.resizeDisabled &&
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode;
|
||||
const { updateDropTargetRows } = useContext(DropTargetContext);
|
||||
|
||||
const gridProps = {
|
||||
|
|
|
|||
|
|
@ -14,7 +14,10 @@ import { useWidgetSelection } from "utils/hooks/useWidgetSelection";
|
|||
import WidgetFactory from "utils/WidgetFactory";
|
||||
|
||||
const WidgetTypes = WidgetFactory.widgetTypes;
|
||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import {
|
||||
previewModeSelector,
|
||||
snipingModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { bindDataToWidget } from "actions/propertyPaneActions";
|
||||
import { hideErrors } from "selectors/debuggerSelectors";
|
||||
import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors";
|
||||
|
|
@ -54,6 +57,7 @@ type WidgetNameComponentProps = {
|
|||
export function WidgetNameComponent(props: WidgetNameComponentProps) {
|
||||
const dispatch = useDispatch();
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
const showTableFilterPane = useShowTableFilterPane();
|
||||
// Dispatch hook handy to set a widget as focused/selected
|
||||
const { selectWidget } = useWidgetSelection();
|
||||
|
|
@ -126,6 +130,7 @@ export function WidgetNameComponent(props: WidgetNameComponentProps) {
|
|||
selectedWidgets.includes(props.widgetId);
|
||||
const shouldShowWidgetName = () => {
|
||||
return (
|
||||
!isPreviewMode &&
|
||||
!isMultiSelectedWidget &&
|
||||
(isSnipingMode
|
||||
? focusedWidget === props.widgetId
|
||||
|
|
|
|||
|
|
@ -34,15 +34,13 @@ export type RenderMode =
|
|||
| "COMPONENT_PANE"
|
||||
| "CANVAS"
|
||||
| "PAGE"
|
||||
| "CANVAS_SELECTED"
|
||||
| "PREVIEW";
|
||||
| "CANVAS_SELECTED";
|
||||
|
||||
export const RenderModes: { [id: string]: RenderMode } = {
|
||||
COMPONENT_PANE: "COMPONENT_PANE",
|
||||
CANVAS: "CANVAS",
|
||||
PAGE: "PAGE",
|
||||
CANVAS_SELECTED: "CANVAS_SELECTED",
|
||||
PREVIEW: "PREVIEW",
|
||||
};
|
||||
|
||||
export const CSSUnits: { [id: string]: CSSUnit } = {
|
||||
|
|
|
|||
|
|
@ -94,11 +94,9 @@ const Canvas = memo((props: CanvasProps) => {
|
|||
* background for canvas
|
||||
*/
|
||||
let backgroundForCanvas;
|
||||
let renderMode = RenderModes.CANVAS;
|
||||
|
||||
if (isPreviewMode) {
|
||||
backgroundForCanvas = "initial";
|
||||
renderMode = RenderModes.PREVIEW;
|
||||
} else {
|
||||
backgroundForCanvas = selectedTheme.properties.colors.backgroundColor;
|
||||
}
|
||||
|
|
@ -125,7 +123,10 @@ const Canvas = memo((props: CanvasProps) => {
|
|||
}}
|
||||
>
|
||||
{props.widgetsStructure.widgetId &&
|
||||
WidgetFactory.createWidget(props.widgetsStructure, renderMode)}
|
||||
WidgetFactory.createWidget(
|
||||
props.widgetsStructure,
|
||||
RenderModes.CANVAS,
|
||||
)}
|
||||
{isMultiplayerEnabledForUser && (
|
||||
<CanvasMultiPointerArena pageId={pageId} />
|
||||
)}
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ import EditorContextProvider from "components/editorComponents/EditorContextProv
|
|||
import Guide from "../GuidedTour/Guide";
|
||||
import PropertyPaneContainer from "./PropertyPaneContainer";
|
||||
import CanvasTopSection from "./EmptyCanvasSection";
|
||||
import { useAutoHeightUIState } from "utils/hooks/autoHeightUIHooks";
|
||||
|
||||
/* eslint-disable react/display-name */
|
||||
function WidgetsEditor() {
|
||||
|
|
@ -71,16 +72,24 @@ function WidgetsEditor() {
|
|||
}, [isFetchingPage, selectWidget, guidedTourEnabled]);
|
||||
|
||||
const allowDragToSelect = useAllowEditorDragToSelect();
|
||||
const { isAutoHeightWithLimitsChanging } = useAutoHeightUIState();
|
||||
|
||||
const handleWrapperClick = useCallback(() => {
|
||||
if (allowDragToSelect) {
|
||||
// Making sure that we don't deselect the widget
|
||||
// after we are done dragging the limits in auto height with limits
|
||||
if (allowDragToSelect && !isAutoHeightWithLimitsChanging) {
|
||||
focusWidget && focusWidget();
|
||||
deselectAll && deselectAll();
|
||||
dispatch(closePropertyPane());
|
||||
dispatch(closeTableFilterPane());
|
||||
dispatch(setCanvasSelectionFromEditor(false));
|
||||
}
|
||||
}, [allowDragToSelect, focusWidget, deselectAll]);
|
||||
}, [
|
||||
allowDragToSelect,
|
||||
focusWidget,
|
||||
deselectAll,
|
||||
isAutoHeightWithLimitsChanging,
|
||||
]);
|
||||
|
||||
/**
|
||||
* drag event handler for selection drawing
|
||||
|
|
|
|||
|
|
@ -736,7 +736,8 @@ function getMovementMapHelper(
|
|||
collisionTree[accessors.parallelMax] -
|
||||
collisionTree[accessors.parallelMin],
|
||||
occupiedLength:
|
||||
(movementMap[collisionTree.id].horizontalOccupiedLength || 0) + 1,
|
||||
(movementMap[collisionTree.id].horizontalOccupiedLength || 0) +
|
||||
HORIZONTAL_RESIZE_LIMIT,
|
||||
currentEmptySpaces:
|
||||
(movementMap[collisionTree.id].horizontalEmptySpaces as number) ||
|
||||
0,
|
||||
|
|
@ -747,7 +748,10 @@ function getMovementMapHelper(
|
|||
collisionTree[accessors.parallelMax] -
|
||||
collisionTree[accessors.parallelMin],
|
||||
occupiedLength:
|
||||
(movementMap[collisionTree.id].verticalOccupiedLength || 0) + 1,
|
||||
(movementMap[collisionTree.id].verticalOccupiedLength || 0) +
|
||||
(collisionTree.fixedHeight && accessors.directionIndicator < 0
|
||||
? collisionTree.fixedHeight
|
||||
: VERTICAL_RESIZE_LIMIT),
|
||||
currentEmptySpaces:
|
||||
(movementMap[collisionTree.id].verticalEmptySpaces as number) || 0,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -33,7 +33,6 @@ export function* batchCallsToUpdateWidgetAutoHeightSaga(
|
|||
const isLayoutUpdating: boolean = yield select(getIsDraggingOrResizing);
|
||||
const { height, widgetId } = action.payload;
|
||||
log.debug("Dynamic height: batching update:", { widgetId, height });
|
||||
|
||||
addWidgetToAutoHeightUpdateQueue(widgetId, height);
|
||||
if (isLayoutUpdating) return;
|
||||
yield put({
|
||||
|
|
|
|||
|
|
@ -28,7 +28,7 @@ export function* dynamicallyUpdateContainersSaga() {
|
|||
const isCanvasWidget = widget.type === "CANVAS_WIDGET";
|
||||
const parent = widget.parentId ? stateWidgets[widget.parentId] : undefined;
|
||||
if (parent?.type === "LIST_WIDGET") return false;
|
||||
if (!parent) return false;
|
||||
if (parent === undefined) return false;
|
||||
return isCanvasWidget;
|
||||
});
|
||||
|
||||
|
|
@ -112,7 +112,15 @@ export function* dynamicallyUpdateContainersSaga() {
|
|||
let maxBottomRow = minDynamicHeightInRows;
|
||||
|
||||
// For the child Canvas, use the value in pixels.
|
||||
let canvasBottomRow = maxBottomRow;
|
||||
let canvasBottomRow = maxBottomRow + 0;
|
||||
|
||||
// For widgets like Tabs Widget, some of the height is occupied by the
|
||||
// tabs themselves, the child canvas as a result has less number of rows available
|
||||
// To accommodate for this, we need to increase the new height by the offset amount.
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
parentContainerWidget.type,
|
||||
parentContainerWidget,
|
||||
);
|
||||
|
||||
// If this canvas has children
|
||||
// we need to consider the bottom most child for the height
|
||||
|
|
@ -130,19 +138,14 @@ export function* dynamicallyUpdateContainersSaga() {
|
|||
maxBottomRowBasedOnChildren += GridDefaults.CANVAS_EXTENSION_OFFSET;
|
||||
// Set the canvas bottom row as a new variable with a new reference
|
||||
canvasBottomRow = maxBottomRowBasedOnChildren + 0;
|
||||
// For widgets like Tabs Widget, some of the height is occupied by the
|
||||
// tabs themselves, the child canvas as a result has less number of rows available
|
||||
// To accommodate for this, we need to increase the new height by the offset amount.
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
parentContainerWidget.type,
|
||||
parentContainerWidget,
|
||||
);
|
||||
|
||||
// Add the offset to the total height of the parent widget
|
||||
maxBottomRowBasedOnChildren += canvasHeightOffset;
|
||||
|
||||
// Get the larger value between the minDynamicHeightInRows and bottomMostRowForChild
|
||||
maxBottomRow = Math.max(maxBottomRowBasedOnChildren, maxBottomRow);
|
||||
} else {
|
||||
canvasBottomRow = maxBottomRow - canvasHeightOffset;
|
||||
}
|
||||
|
||||
// The following makes sure we stay within bounds
|
||||
|
|
@ -156,7 +159,7 @@ export function* dynamicallyUpdateContainersSaga() {
|
|||
}
|
||||
|
||||
canvasBottomRow =
|
||||
Math.max(maxBottomRow, canvasBottomRow) *
|
||||
Math.max(maxBottomRow - canvasHeightOffset, canvasBottomRow) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
// If we have a new height to set and
|
||||
|
|
@ -189,7 +192,7 @@ export function* dynamicallyUpdateContainersSaga() {
|
|||
}
|
||||
}
|
||||
log.debug(
|
||||
"Dynamic height: Container computations took:",
|
||||
"Dynamic height: Container computations time taken:",
|
||||
performance.now() - start,
|
||||
"ms",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -7,8 +7,12 @@ import {
|
|||
} from "reducers/entityReducers/canvasWidgetsReducer";
|
||||
import { select } from "redux-saga/effects";
|
||||
import { getWidgetMetaProps, getWidgets } from "sagas/selectors";
|
||||
import { previewModeSelector } from "selectors/editorSelectors";
|
||||
import {
|
||||
getCanvasHeightOffset,
|
||||
previewModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { getAppMode } from "selectors/entitiesSelector";
|
||||
import { TreeNode } from "utils/autoHeight/constants";
|
||||
|
||||
export function* shouldWidgetsCollapse() {
|
||||
const isPreviewMode: boolean = yield select(previewModeSelector);
|
||||
|
|
@ -50,6 +54,22 @@ export function* getChildOfContainerLikeWidget(
|
|||
}
|
||||
}
|
||||
|
||||
export function getParentCurrentHeightInRows(
|
||||
tree: Record<string, TreeNode>,
|
||||
parentId: string,
|
||||
changesSoFar: Record<string, { bottomRow: number; topRow: number }>,
|
||||
) {
|
||||
// Get the parentHeight in rows
|
||||
let parentHeightInRows = tree[parentId].bottomRow - tree[parentId].topRow;
|
||||
|
||||
// If the parent has changed so far.
|
||||
if (changesSoFar.hasOwnProperty(parentId)) {
|
||||
parentHeightInRows =
|
||||
changesSoFar[parentId].bottomRow - changesSoFar[parentId].topRow;
|
||||
}
|
||||
return parentHeightInRows;
|
||||
}
|
||||
|
||||
export function* getMinHeightBasedOnChildren(
|
||||
widgetId: string,
|
||||
changesSoFar: Record<string, { bottomRow: number; topRow: number }>,
|
||||
|
|
@ -67,17 +87,22 @@ export function* getMinHeightBasedOnChildren(
|
|||
const { children = [], parentId } = stateWidgets[widgetId];
|
||||
// If we need to consider the parent height
|
||||
if (parentId && !ignoreParent) {
|
||||
// Get the parentHeight in rows
|
||||
let parentHeightInRows = tree[parentId].bottomRow - tree[parentId].topRow;
|
||||
|
||||
// If the parent has changed so far.
|
||||
if (changesSoFar.hasOwnProperty(parentId)) {
|
||||
parentHeightInRows =
|
||||
changesSoFar[parentId].bottomRow - changesSoFar[parentId].topRow;
|
||||
}
|
||||
|
||||
const parent = stateWidgets[parentId];
|
||||
const parentHeightInRows = getParentCurrentHeightInRows(
|
||||
tree,
|
||||
parentId,
|
||||
changesSoFar,
|
||||
);
|
||||
// The canvas will be an extension smaller than the parent?
|
||||
minHeightInRows = parentHeightInRows - GridDefaults.CANVAS_EXTENSION_OFFSET;
|
||||
|
||||
// We will also remove any extra offsets the parent has
|
||||
// As we're dealing with the child canvas widget here.
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
parent.type,
|
||||
parent,
|
||||
);
|
||||
minHeightInRows = minHeightInRows - canvasHeightOffset;
|
||||
// If the canvas is empty return the parent's height in rows, without
|
||||
// the canvas extension offset
|
||||
if (!children.length) {
|
||||
|
|
|
|||
|
|
@ -8,7 +8,10 @@ import { updateWidgetAutoHeightSaga } from "./widgets";
|
|||
export default function* autoHeightSagas() {
|
||||
yield all([
|
||||
takeLatest(
|
||||
ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
[
|
||||
ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
ReduxActionTypes.SET_PREVIEW_MODE,
|
||||
],
|
||||
dynamicallyUpdateContainersSaga,
|
||||
),
|
||||
takeEvery(
|
||||
|
|
@ -16,7 +19,7 @@ export default function* autoHeightSagas() {
|
|||
batchCallsToUpdateWidgetAutoHeightSaga,
|
||||
),
|
||||
debounce(
|
||||
100,
|
||||
50,
|
||||
ReduxActionTypes.PROCESS_AUTO_HEIGHT_UPDATES,
|
||||
updateWidgetAutoHeightSaga,
|
||||
),
|
||||
|
|
|
|||
|
|
@ -39,7 +39,7 @@ export function* getLayoutTree(layoutUpdated: boolean) {
|
|||
}
|
||||
}
|
||||
log.debug(
|
||||
"Dynamic Height: Tree generation took:",
|
||||
"Dynamic Height: Tree generation time taken:",
|
||||
performance.now() - start,
|
||||
"ms",
|
||||
);
|
||||
|
|
|
|||
|
|
@ -25,6 +25,7 @@ import {
|
|||
import {
|
||||
getChildOfContainerLikeWidget,
|
||||
getMinHeightBasedOnChildren,
|
||||
getParentCurrentHeightInRows,
|
||||
shouldWidgetsCollapse,
|
||||
} from "./helpers";
|
||||
import { updateMultipleWidgetPropertiesAction } from "actions/controlActions";
|
||||
|
|
@ -63,6 +64,7 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
const updates = getAutoHeightUpdateQueue();
|
||||
log.debug("Dynamic Height: updates to process", { updates });
|
||||
const start = performance.now();
|
||||
let shouldRecomputeContainers = false;
|
||||
|
||||
const shouldCollapse: boolean = yield shouldWidgetsCollapse();
|
||||
|
||||
|
|
@ -96,6 +98,8 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
let minDynamicHeightInPixels =
|
||||
getWidgetMinAutoHeight(widget) * GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
if (widget.type === "TABS_WIDGET") shouldRecomputeContainers = true;
|
||||
|
||||
// In case of a widget going invisible in view mode
|
||||
if (updates[widgetId] === 0) {
|
||||
if (shouldCollapse && isAutoHeightEnabledForWidget(widget)) {
|
||||
|
|
@ -138,7 +142,6 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
newHeightInPixels / GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
||||
),
|
||||
parentId: widget.parentId,
|
||||
hasScroll: widget.isCanvas ? true : false,
|
||||
});
|
||||
} else if (widget) {
|
||||
// For widgets like Modal Widget. (Rather this assumes that it is only the modal widget which needs a change)
|
||||
|
|
@ -297,26 +300,14 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
|
||||
// Add extra rows, this is to accommodate for padding and margins in the parent
|
||||
minCanvasHeightInRows += GridDefaults.CANVAS_EXTENSION_OFFSET;
|
||||
// Setting this in a variable, as this will be the total scroll height in the canvas.
|
||||
const minCanvasHeightInPixels =
|
||||
minCanvasHeightInRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
// We need to make sure that the canvas widget doesn't have
|
||||
// any extra scroll, to this end, we need to add the `minHeight` update
|
||||
// for the canvas widgets. Canvas Widgets are never updated in other flows
|
||||
// As they simply take up whatever space the parent has, but this doesn't effect
|
||||
// the `minHeight`, which leads to scroll if the `minHeight` is a larger value.
|
||||
// Also, for canvas widgets, the values are in pure pixels instead of rows.
|
||||
widgetsToUpdate[parentCanvasWidgetId] = [
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
{
|
||||
propertyPath: "minHeight",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
];
|
||||
// For widgets like Tabs Widget, some of the height is occupied by the
|
||||
// tabs themselves, the child canvas as a result has less number of rows available
|
||||
// To accommodate for this, we need to increase the new height by the offset amount.
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
parentContainerLikeWidget.type,
|
||||
parentContainerLikeWidget,
|
||||
);
|
||||
|
||||
// Widgets need to consider changing heights, only if they have dynamic height
|
||||
// enabled.
|
||||
|
|
@ -329,17 +320,30 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
|
||||
minHeightInRows = Math.max(
|
||||
minHeightInRows,
|
||||
minCanvasHeightInRows,
|
||||
minCanvasHeightInRows + canvasHeightOffset,
|
||||
);
|
||||
|
||||
// For widgets like Tabs Widget, some of the height is occupied by the
|
||||
// tabs themselves, the child canvas as a result has less number of rows available
|
||||
// To accommodate for this, we need to increase the new height by the offset amount.
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
parentContainerLikeWidget.type,
|
||||
parentContainerLikeWidget,
|
||||
);
|
||||
minHeightInRows += canvasHeightOffset;
|
||||
// Setting this in a variable, as this will be the total scroll height in the canvas.
|
||||
const minCanvasHeightInPixels =
|
||||
(minHeightInRows - canvasHeightOffset) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
// We need to make sure that the canvas widget doesn't have
|
||||
// any extra scroll, to this end, we need to add the `minHeight` update
|
||||
// for the canvas widgets. Canvas Widgets are never updated in other flows
|
||||
// As they simply take up whatever space the parent has, but this doesn't effect
|
||||
// the `minHeight`, which leads to scroll if the `minHeight` is a larger value.
|
||||
// Also, for canvas widgets, the values are in pure pixels instead of rows.
|
||||
widgetsToUpdate[parentCanvasWidgetId] = [
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
{
|
||||
propertyPath: "minHeight",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
];
|
||||
|
||||
// Make sure we're not overflowing the max height bounds
|
||||
const maxDynamicHeight = getWidgetMaxAutoHeight(
|
||||
|
|
@ -436,6 +440,36 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
]);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let parentContainerHeightInRows = getParentCurrentHeightInRows(
|
||||
dynamicHeightLayoutTree,
|
||||
parentContainerLikeWidget.widgetId,
|
||||
changesSoFar,
|
||||
);
|
||||
|
||||
parentContainerHeightInRows -= canvasHeightOffset;
|
||||
|
||||
// Setting this in a variable, as this will be the total scroll height in the canvas.
|
||||
const minCanvasHeightInPixels =
|
||||
Math.max(minCanvasHeightInRows, parentContainerHeightInRows) *
|
||||
GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
||||
|
||||
// We need to make sure that the canvas widget doesn't have
|
||||
// any extra scroll, to this end, we need to add the `minHeight` update
|
||||
// for the canvas widgets. Canvas Widgets are never updated in other flows
|
||||
// As they simply take up whatever space the parent has, but this doesn't effect
|
||||
// the `minHeight`, which leads to scroll if the `minHeight` is a larger value.
|
||||
// Also, for canvas widgets, the values are in pure pixels instead of rows.
|
||||
widgetsToUpdate[parentCanvasWidgetId] = [
|
||||
{
|
||||
propertyPath: "bottomRow",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
{
|
||||
propertyPath: "minHeight",
|
||||
propertyValue: minCanvasHeightInPixels,
|
||||
},
|
||||
];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
@ -471,10 +505,8 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
|
||||
// Convert the changesSoFar (this are the computed changes)
|
||||
// To the widgetsToUpdate data structure for final reducer update.
|
||||
|
||||
for (const changedWidgetId in changesSoFar) {
|
||||
const hasScroll = Object.values(expectedUpdates).find(
|
||||
(entry) => entry.widgetId === changedWidgetId,
|
||||
)?.hasScroll;
|
||||
const { originalBottomRow, originalTopRow } = dynamicHeightLayoutTree[
|
||||
changedWidgetId
|
||||
];
|
||||
|
|
@ -497,19 +529,22 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
propertyValue: originalBottomRow,
|
||||
},
|
||||
];
|
||||
if (hasScroll) {
|
||||
const containerLikeWidget = stateWidgets[changedWidgetId];
|
||||
const containerLikeWidget = stateWidgets[changedWidgetId];
|
||||
|
||||
if (
|
||||
Array.isArray(containerLikeWidget.children) &&
|
||||
containerLikeWidget.children.length > 0
|
||||
) {
|
||||
const childWidgetId:
|
||||
| string
|
||||
| undefined = yield getChildOfContainerLikeWidget(
|
||||
containerLikeWidget,
|
||||
);
|
||||
if (childWidgetId) {
|
||||
if (
|
||||
Array.isArray(containerLikeWidget.children) &&
|
||||
containerLikeWidget.children.length > 0
|
||||
) {
|
||||
const childWidgetId:
|
||||
| string
|
||||
| undefined = yield getChildOfContainerLikeWidget(
|
||||
containerLikeWidget,
|
||||
);
|
||||
|
||||
if (childWidgetId) {
|
||||
const childCanvasWidget = stateWidgets[childWidgetId];
|
||||
const isCanvasWidget = childCanvasWidget?.type === "CANVAS_WIDGET";
|
||||
if (isCanvasWidget) {
|
||||
let canvasHeight: number = yield getMinHeightBasedOnChildren(
|
||||
childWidgetId,
|
||||
changesSoFar,
|
||||
|
|
@ -517,11 +552,7 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
dynamicHeightLayoutTree,
|
||||
);
|
||||
canvasHeight += GridDefaults.CANVAS_EXTENSION_OFFSET;
|
||||
const canvasHeightOffset: number = getCanvasHeightOffset(
|
||||
containerLikeWidget.type,
|
||||
containerLikeWidget,
|
||||
);
|
||||
canvasHeight -= canvasHeightOffset;
|
||||
|
||||
const propertyUpdates = [
|
||||
{
|
||||
propertyPath: "minHeight",
|
||||
|
|
@ -547,13 +578,16 @@ export function* updateWidgetAutoHeightSaga() {
|
|||
}
|
||||
|
||||
log.debug("Dynamic height: Widgets to update:", { widgetsToUpdate });
|
||||
|
||||
if (Object.keys(widgetsToUpdate).length > 0) {
|
||||
// Push all updates to the CanvasWidgetsReducer.
|
||||
// Note that we're not calling `UPDATE_LAYOUT`
|
||||
// as we don't need to trigger an eval
|
||||
yield put(updateMultipleWidgetPropertiesAction(widgetsToUpdate));
|
||||
resetAutoHeightUpdateQueue();
|
||||
yield put(generateAutoHeightLayoutTreeAction(false, false));
|
||||
yield put(
|
||||
generateAutoHeightLayoutTreeAction(shouldRecomputeContainers, false),
|
||||
);
|
||||
}
|
||||
|
||||
log.debug(
|
||||
|
|
|
|||
|
|
@ -178,9 +178,9 @@ export const selectURLSlugs = createSelector(
|
|||
);
|
||||
|
||||
export const getRenderMode = (state: AppState) => {
|
||||
if (state.ui.editor.isPreviewMode) return RenderModes.PREVIEW;
|
||||
else if (state.entities.app.mode === APP_MODE.EDIT) return RenderModes.CANVAS;
|
||||
else return RenderModes.PAGE;
|
||||
return state.entities.app.mode === APP_MODE.EDIT
|
||||
? RenderModes.CANVAS
|
||||
: RenderModes.PAGE;
|
||||
};
|
||||
|
||||
export const getViewModePageList = createSelector(
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@ export type TreeNode = {
|
|||
bottomRow: number;
|
||||
originalTopRow: number;
|
||||
originalBottomRow: number;
|
||||
distanceToNearestAbove: number;
|
||||
};
|
||||
|
||||
export type NodeSpace = {
|
||||
|
|
|
|||
|
|
@ -17,6 +17,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: [],
|
||||
|
|
@ -25,6 +26,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -36,7 +38,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
it("Does conflict when part of the boxes overlap horizontally", () => {
|
||||
const input: NodeSpace[] = [
|
||||
{ left: 0, right: 100, top: 0, bottom: 30, id: "1" },
|
||||
{ left: 80, top: 30, bottom: 40, right: 120, id: "2" },
|
||||
{ left: 80, top: 40, bottom: 80, right: 120, id: "2" },
|
||||
];
|
||||
const previousTree: Record<string, TreeNode> = {};
|
||||
const layoutUpdated = false;
|
||||
|
|
@ -48,14 +50,16 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
belows: [],
|
||||
topRow: 30,
|
||||
bottomRow: 40,
|
||||
originalBottomRow: 40,
|
||||
originalTopRow: 30,
|
||||
topRow: 40,
|
||||
bottomRow: 80,
|
||||
originalBottomRow: 80,
|
||||
originalTopRow: 40,
|
||||
distanceToNearestAbove: 10,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -77,6 +81,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 20,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -85,6 +90,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 40,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 20,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
};
|
||||
const layoutUpdated = false;
|
||||
|
|
@ -96,6 +102,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 20,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -104,6 +111,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 40,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 20,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -125,6 +133,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 20,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -133,6 +142,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 40,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 20,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
};
|
||||
const layoutUpdated = true;
|
||||
|
|
@ -144,6 +154,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 30,
|
||||
originalBottomRow: 30,
|
||||
originalTopRow: 0,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -152,6 +163,7 @@ describe("Generate Auto Height Layout tree", () => {
|
|||
bottomRow: 40,
|
||||
originalBottomRow: 40,
|
||||
originalTopRow: 30,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
import { areIntersecting } from "utils/boxHelpers";
|
||||
import { pushToArray } from "utils/helpers";
|
||||
import { MAX_BOX_SIZE, NodeSpace, TreeNode } from "./constants";
|
||||
import { getNearestAbove } from "./helpers";
|
||||
|
||||
// This function uses the spaces occupied by sibling boxes and provides us with
|
||||
// a data structure which defines the relative vertical positioning of the boxes
|
||||
|
|
@ -76,6 +77,7 @@ export function generateTree(
|
|||
if (originalBottomRow === undefined || layoutUpdated) {
|
||||
originalBottomRow = currentSpace.bottom - MAX_BOX_SIZE;
|
||||
}
|
||||
|
||||
tree[currentSpace.id] = {
|
||||
aboves: aboveMap[currentSpace.id] || [],
|
||||
belows: belowMap[currentSpace.id] || [],
|
||||
|
|
@ -83,9 +85,21 @@ export function generateTree(
|
|||
bottomRow: currentSpace.bottom - MAX_BOX_SIZE,
|
||||
originalTopRow,
|
||||
originalBottomRow,
|
||||
distanceToNearestAbove: 0,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
for (const boxId in tree) {
|
||||
// For each box, get the nearest above node
|
||||
// Then get the distance between this node and the nearest above
|
||||
// We'll try to maintain this distance when reflowing due to auto height
|
||||
const nearestAbove = getNearestAbove(tree, boxId, {});
|
||||
if (nearestAbove.length > 0) {
|
||||
tree[boxId].distanceToNearestAbove =
|
||||
tree[boxId].topRow - tree[nearestAbove[0]].bottomRow;
|
||||
}
|
||||
}
|
||||
|
||||
return tree;
|
||||
}
|
||||
|
|
|
|||
58
app/client/src/utils/autoHeight/helpers.ts
Normal file
58
app/client/src/utils/autoHeight/helpers.ts
Normal file
|
|
@ -0,0 +1,58 @@
|
|||
import { TreeNode } from "./constants";
|
||||
|
||||
/**
|
||||
* Gets the nearest above box for the current box. Including the aboves which have changes so far.
|
||||
*
|
||||
* @param tree: Auto Height Layout Tree
|
||||
* @param effectedBoxId: Current box in consideration
|
||||
* @param repositionedBoxes: Boxes repositioned so far
|
||||
* @returns An array of boxIds which are above and nearest the effectedBoxId
|
||||
*/
|
||||
export function getNearestAbove(
|
||||
tree: Record<string, TreeNode>,
|
||||
effectedBoxId: string,
|
||||
repositionedBoxes: Record<string, { topRow: number; bottomRow: number }>,
|
||||
) {
|
||||
// Get all the above boxes
|
||||
const aboves = tree[effectedBoxId].aboves;
|
||||
// We're trying to find the nearest boxes above this box
|
||||
|
||||
return aboves.reduce((prev: string[], next: string) => {
|
||||
if (!prev[0]) return [next];
|
||||
// Get the bottomRow of the above box
|
||||
let nextBottomRow = tree[next].bottomRow;
|
||||
let prevBottomRow = tree[prev[0]].bottomRow;
|
||||
// If we've already repositioned this, use the new bottomRow of the box
|
||||
if (repositionedBoxes[next]) {
|
||||
nextBottomRow = repositionedBoxes[next].bottomRow;
|
||||
}
|
||||
if (repositionedBoxes[prev[0]]) {
|
||||
prevBottomRow = repositionedBoxes[prev[0]].bottomRow;
|
||||
}
|
||||
|
||||
// If the current box's (next) bottomRow is larger than the previous
|
||||
// This (next) box is the bottom most above so far
|
||||
if (nextBottomRow > prevBottomRow) return [next];
|
||||
// If this (next) box's bottom row is the same as the previous
|
||||
// We have two bottom most boxes
|
||||
else if (nextBottomRow === prevBottomRow) {
|
||||
if (
|
||||
repositionedBoxes[prev[0]] &&
|
||||
repositionedBoxes[prev[0]].bottomRow ===
|
||||
repositionedBoxes[prev[0]].topRow
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
if (
|
||||
repositionedBoxes[next] &&
|
||||
repositionedBoxes[next].bottomRow === repositionedBoxes[next].topRow
|
||||
) {
|
||||
return [next];
|
||||
}
|
||||
return [...prev, next];
|
||||
}
|
||||
// This (next) box's bottom row is lower than the boxes selected so far
|
||||
// so, we ignore it.
|
||||
else return prev;
|
||||
}, []);
|
||||
}
|
||||
|
|
@ -18,6 +18,7 @@ describe("reflow", () => {
|
|||
bottomRow: box1BottomRow,
|
||||
originalTopRow: box1TopRow,
|
||||
originalBottomRow: box1BottomRow,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -26,6 +27,7 @@ describe("reflow", () => {
|
|||
bottomRow: box2BottomRow,
|
||||
originalTopRow: box2TopRow,
|
||||
originalBottomRow: box2BottomRow,
|
||||
distanceToNearestAbove: 10,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -70,6 +72,7 @@ describe("reflow", () => {
|
|||
bottomRow: box1BottomRow,
|
||||
originalTopRow: box1OriginalTopRow,
|
||||
originalBottomRow: box1OriginalBottomRow,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -78,6 +81,7 @@ describe("reflow", () => {
|
|||
bottomRow: box2BottomRow,
|
||||
originalTopRow: box2OriginalTopRow,
|
||||
originalBottomRow: box2OriginalBottomRow,
|
||||
distanceToNearestAbove: 10,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -122,6 +126,7 @@ describe("reflow", () => {
|
|||
bottomRow: box1BottomRow,
|
||||
originalTopRow: box1OriginalTopRow,
|
||||
originalBottomRow: box1OriginalBottomRow,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1"],
|
||||
|
|
@ -130,6 +135,7 @@ describe("reflow", () => {
|
|||
bottomRow: box2BottomRow,
|
||||
originalTopRow: box2OriginalTopRow,
|
||||
originalBottomRow: box2OriginalBottomRow,
|
||||
distanceToNearestAbove: 40,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -173,6 +179,7 @@ describe("reflow", () => {
|
|||
bottomRow: 120,
|
||||
originalBottomRow: 20,
|
||||
originalTopRow: 10,
|
||||
distanceToNearestAbove: 0,
|
||||
};
|
||||
|
||||
const tree: Record<string, TreeNode> = {
|
||||
|
|
@ -183,6 +190,7 @@ describe("reflow", () => {
|
|||
bottomRow: box1BottomRow,
|
||||
originalTopRow: box1OriginalTopRow,
|
||||
originalBottomRow: box1OriginalBottomRow,
|
||||
distanceToNearestAbove: 0,
|
||||
},
|
||||
"2": {
|
||||
aboves: ["1", "3"],
|
||||
|
|
@ -191,6 +199,7 @@ describe("reflow", () => {
|
|||
bottomRow: box2BottomRow,
|
||||
originalTopRow: box2OriginalTopRow,
|
||||
originalBottomRow: box2OriginalBottomRow,
|
||||
distanceToNearestAbove: 20,
|
||||
},
|
||||
"3": box3,
|
||||
};
|
||||
|
|
@ -208,8 +217,8 @@ describe("reflow", () => {
|
|||
bottomRow: box1BottomRow + box1DeltaHeight,
|
||||
},
|
||||
"2": {
|
||||
topRow: 130,
|
||||
bottomRow: 170,
|
||||
topRow: 140,
|
||||
bottomRow: 180,
|
||||
},
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -1,92 +1,5 @@
|
|||
import { TreeNode } from "./constants";
|
||||
|
||||
/**
|
||||
*
|
||||
* @param tree : Auto Height Layout Tree
|
||||
* @param effectedBoxId : Current box in consideration
|
||||
* @param aboveId : Above box which may or maynot have changed
|
||||
* @param offsetSoFar : Offset of the above box, or changes to be applied so far
|
||||
* @returns : The offset expected to be applied to the effectedBoxId. This is how much this box should move
|
||||
*/
|
||||
export function getNegativeOffset(
|
||||
tree: Record<string, TreeNode>,
|
||||
effectedBoxId: string,
|
||||
aboveId: string,
|
||||
offsetSoFar = 0,
|
||||
): number {
|
||||
if (offsetSoFar <= 0) {
|
||||
// Let's take in to account the old spacing between the effected box and bottom most above box
|
||||
// when the layout was last updated.
|
||||
const oldSpacing =
|
||||
tree[effectedBoxId].originalTopRow - tree[aboveId].originalBottomRow;
|
||||
// Let's compute the spacing between the effected box and bottom most above box
|
||||
const currentSpacing = tree[effectedBoxId].topRow - tree[aboveId].bottomRow;
|
||||
// If the old spacing is less than current spacing and the offset of the bottom most above,
|
||||
// we need to make sure that we're sticking to the original spacing between the bottom most above
|
||||
// and the current effected box.
|
||||
// Note: This applies only if the offset is negative, which is to say that the box is to move up
|
||||
if (oldSpacing < currentSpacing + offsetSoFar) {
|
||||
return oldSpacing + offsetSoFar - currentSpacing;
|
||||
}
|
||||
}
|
||||
return offsetSoFar;
|
||||
}
|
||||
/**
|
||||
* Gets the nearest above box for the current box. Including the aboves which have changes so far.
|
||||
*
|
||||
* @param tree: Auto Height Layout Tree
|
||||
* @param effectedBoxId: Current box in consideration
|
||||
* @param repositionedBoxes: Boxes repositioned so far
|
||||
* @returns An array of boxIds which are above and nearest the effectedBoxId
|
||||
*/
|
||||
export function getNearestAbove(
|
||||
tree: Record<string, TreeNode>,
|
||||
effectedBoxId: string,
|
||||
repositionedBoxes: Record<string, { topRow: number; bottomRow: number }>,
|
||||
) {
|
||||
// Get all the above boxes
|
||||
const aboves = tree[effectedBoxId].aboves;
|
||||
// We're trying to find the nearest boxes above this box
|
||||
|
||||
return aboves.reduce((prev: string[], next: string) => {
|
||||
if (!prev[0]) return [next];
|
||||
// Get the bottomRow of the above box
|
||||
let nextBottomRow = tree[next].bottomRow;
|
||||
let prevBottomRow = tree[prev[0]].bottomRow;
|
||||
// If we've already repositioned this, use the new bottomRow of the box
|
||||
if (repositionedBoxes[next]) {
|
||||
nextBottomRow = repositionedBoxes[next].bottomRow;
|
||||
}
|
||||
if (repositionedBoxes[prev[0]]) {
|
||||
prevBottomRow = repositionedBoxes[prev[0]].bottomRow;
|
||||
}
|
||||
|
||||
// If the current box's (next) bottomRow is larger than the previous
|
||||
// This (next) box is the bottom most above so far
|
||||
if (nextBottomRow > prevBottomRow) return [next];
|
||||
// If this (next) box's bottom row is the same as the previous
|
||||
// We have two bottom most boxes
|
||||
else if (nextBottomRow === prevBottomRow) {
|
||||
if (
|
||||
repositionedBoxes[prev[0]] &&
|
||||
repositionedBoxes[prev[0]].bottomRow ===
|
||||
repositionedBoxes[prev[0]].topRow
|
||||
) {
|
||||
return prev;
|
||||
}
|
||||
if (
|
||||
repositionedBoxes[next] &&
|
||||
repositionedBoxes[next].bottomRow === repositionedBoxes[next].topRow
|
||||
) {
|
||||
return [next];
|
||||
}
|
||||
return [...prev, next];
|
||||
}
|
||||
// This (next) box's bottom row is lower than the boxes selected so far
|
||||
// so, we ignore it.
|
||||
else return prev;
|
||||
}, []);
|
||||
}
|
||||
import { getNearestAbove } from "./helpers";
|
||||
|
||||
function getAllEffectedBoxes(
|
||||
effectorBoxId: string,
|
||||
|
|
@ -159,39 +72,23 @@ export function computeChangeInPositionBasedOnDelta(
|
|||
// If the above box has been effected by another box change height
|
||||
// Or, if this above box itself has changed height
|
||||
if (effectedBoxes.includes(aboveId) || delta[aboveId]) {
|
||||
// In case the above box has changed heights
|
||||
const _aboveOffset = repositionedBoxes[aboveId]
|
||||
? repositionedBoxes[aboveId].bottomRow - tree[aboveId].bottomRow
|
||||
: 0;
|
||||
|
||||
// If so far, we haven't got any _offset updates
|
||||
// This can happen if this is the first aboveId we're checking
|
||||
if (_offset === undefined) _offset = _aboveOffset;
|
||||
|
||||
const negativeOffset = getNegativeOffset(
|
||||
tree,
|
||||
effectedBoxId,
|
||||
aboveId,
|
||||
_aboveOffset,
|
||||
);
|
||||
|
||||
// If the bottom most above (_aboveOffset), has moved down (either by increasing height and/or due to its above)
|
||||
// Let's take the effected boxs' change to be the max of _offset and _aboveOffset
|
||||
// The _offset so far will be due to other bottomMostAbove effecting this effected box.
|
||||
if (_aboveOffset > 0) _offset = Math.max(_aboveOffset, _offset);
|
||||
// If the bottom most above (_aboveOffset) has moved up (either by decreasing height and/or due to its above)
|
||||
// Let's take the Min (negative values, so max offset in the upward direction) of the _aboveOffset, _offset, negativeOffset.
|
||||
else if (_aboveOffset < 0) {
|
||||
_offset = Math.min(_aboveOffset, _offset, negativeOffset);
|
||||
// If we have the above repositioned
|
||||
if (repositionedBoxes[aboveId]) {
|
||||
// Get the new expected top row of this effectedBox
|
||||
const newTopRow =
|
||||
repositionedBoxes[aboveId].bottomRow +
|
||||
tree[effectedBoxId].distanceToNearestAbove;
|
||||
// Get the offset this effectedBox needs to consider moving
|
||||
_offset = newTopRow - tree[effectedBoxId].topRow;
|
||||
} else {
|
||||
// Since the above hasn't changed, don't change this.
|
||||
_offset = 0;
|
||||
}
|
||||
} else {
|
||||
// Stick to the widget above if the bottomMost above box hasn't changed
|
||||
// TODO(abhinav): Here we may want to use the same logic as negativeOffset using originals as done previously.
|
||||
// Test this.
|
||||
// Let's take in to account the old spacing between the effected box and bottom most above box
|
||||
// when the layout was last updated.
|
||||
const negativeOffset = getNegativeOffset(tree, effectedBoxId, aboveId);
|
||||
_offset = negativeOffset;
|
||||
// Maintain distance from the bottom most above.
|
||||
const newTopRow =
|
||||
tree[aboveId].bottomRow + tree[effectedBoxId].distanceToNearestAbove;
|
||||
_offset = newTopRow - tree[effectedBoxId].topRow;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,10 +1,14 @@
|
|||
import { ReduxActionTypes } from "ce/constants/ReduxActionConstants";
|
||||
import { AppState } from "ce/reducers";
|
||||
import { useCallback } from "react";
|
||||
import { useDispatch } from "react-redux";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
||||
export const useAutoHeightUIState = () => {
|
||||
const dispatch = useDispatch();
|
||||
return {
|
||||
isAutoHeightWithLimitsChanging: useSelector(
|
||||
(state: AppState) => state.ui.autoHeightUI.isAutoHeightWithLimitsChanging,
|
||||
),
|
||||
setIsAutoHeightWithLimitsChanging: useCallback(
|
||||
(isAutoHeightWithLimitsChanging: boolean) => {
|
||||
dispatch({
|
||||
|
|
|
|||
|
|
@ -1,5 +1,8 @@
|
|||
import { AppState } from "@appsmith/reducers";
|
||||
import { snipingModeSelector } from "selectors/editorSelectors";
|
||||
import {
|
||||
previewModeSelector,
|
||||
snipingModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { useSelector } from "store";
|
||||
|
||||
export const useAllowEditorDragToSelect = () => {
|
||||
|
|
@ -28,6 +31,12 @@ export const useAllowEditorDragToSelect = () => {
|
|||
// True when any widget is dragging or resizing, including this one
|
||||
const isResizingOrDragging = !!isResizing || !!isDragging || !!isSelecting;
|
||||
const isSnipingMode = useSelector(snipingModeSelector);
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
|
||||
return !isResizingOrDragging && !isDraggingDisabled && !isSnipingMode;
|
||||
return (
|
||||
!isResizingOrDragging &&
|
||||
!isDraggingDisabled &&
|
||||
!isSnipingMode &&
|
||||
!isPreviewMode
|
||||
);
|
||||
};
|
||||
|
|
|
|||
|
|
@ -420,7 +420,7 @@ abstract class BaseWidget<
|
|||
|
||||
if (
|
||||
isAutoHeightEnabledForWidget(this.props) &&
|
||||
!this.props.isAutoGeneratedWidget //To skip list widget's auto generated widgets
|
||||
!this.props.isAutoGeneratedWidget // To skip list widget's auto generated widgets
|
||||
) {
|
||||
return (
|
||||
<AutoHeightContainerWrapper
|
||||
|
|
@ -455,18 +455,12 @@ abstract class BaseWidget<
|
|||
|
||||
// return this.getCanvasView();
|
||||
case RenderModes.PAGE:
|
||||
case RenderModes.PREVIEW:
|
||||
content = this.getWidgetComponent();
|
||||
if (this.props.isVisible) {
|
||||
if (!this.props.detachFromLayout) {
|
||||
content = this.makePositioned(content);
|
||||
}
|
||||
return content;
|
||||
} else {
|
||||
// When widgets are invisible in view mode, they should not take up space.
|
||||
// We're sending an update that sets the widget to have zero height,
|
||||
// this should make sure that widgets below this invisible widget move up
|
||||
// this.updateAutoHeight(0);
|
||||
}
|
||||
return null;
|
||||
default:
|
||||
|
|
|
|||
|
|
@ -42,21 +42,6 @@ const TAB_CONTAINER_HEIGHT = "44px";
|
|||
const CHILDREN_WRAPPER_HEIGHT_WITH_TABS = `calc(100% - ${TAB_CONTAINER_HEIGHT})`;
|
||||
const CHILDREN_WRAPPER_HEIGHT_WITHOUT_TABS = "100%";
|
||||
|
||||
// const scrollNavControlContainerBaseStyle = css`
|
||||
// display: flex;
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// bottom: 0;
|
||||
// z-index: 2;
|
||||
// background: white;
|
||||
|
||||
// button {
|
||||
// z-index: 1;
|
||||
// border-radius: 0px;
|
||||
// border-bottom: ${(props) => `1px solid ${props.theme.colors.bodyBG}`};
|
||||
// }
|
||||
// `;
|
||||
|
||||
const scrollContents = css`
|
||||
overflow-y: auto;
|
||||
position: absolute;
|
||||
|
|
@ -111,39 +96,6 @@ export interface TabsContainerProps {
|
|||
isScrollable: boolean;
|
||||
}
|
||||
|
||||
// const TabsContainer = styled.div<TabsContainerProps>`
|
||||
// position: absolute;
|
||||
// top: 0;
|
||||
// overflow-x: auto;
|
||||
// overflow-y: hidden;
|
||||
// display: flex;
|
||||
// height: ${TAB_CONTAINER_HEIGHT};
|
||||
// background: ${(props) => props.theme.colors.builderBodyBG};
|
||||
// overflow: hidden;
|
||||
// border-bottom: ${(props) => `1px solid ${props.theme.colors.bodyBG}`};
|
||||
|
||||
// overflow-x: scroll;
|
||||
// &::-webkit-scrollbar {
|
||||
// display: none;
|
||||
// }
|
||||
// /* Hide scrollbar for IE, Edge and Firefox */
|
||||
// -ms-overflow-style: none; /* IE and Edge */
|
||||
// scrollbar-width: none; /* Firefox */
|
||||
|
||||
// && {
|
||||
// width: 100%;
|
||||
// display: flex;
|
||||
// justify-content: flex-start;
|
||||
// align-items: flex-end;
|
||||
// }
|
||||
// `;
|
||||
|
||||
// type TabProps = {
|
||||
// selected?: boolean;
|
||||
// onClick: (e: React.MouseEvent<HTMLDivElement>) => void;
|
||||
// primaryColor: string;
|
||||
// };
|
||||
|
||||
const Container = styled.div`
|
||||
width: 100%;
|
||||
align-items: flex-end;
|
||||
|
|
@ -194,19 +146,6 @@ export interface ScrollNavControlProps {
|
|||
className?: string;
|
||||
}
|
||||
|
||||
// function ScrollNavControl(props: ScrollNavControlProps) {
|
||||
// const { className, disabled, icon, onClick } = props;
|
||||
// return (
|
||||
// <Button
|
||||
// className={className}
|
||||
// disabled={disabled}
|
||||
// icon={icon}
|
||||
// minimal
|
||||
// onClick={onClick}
|
||||
// />
|
||||
// );
|
||||
// }
|
||||
|
||||
function TabsComponent(props: TabsComponentProps) {
|
||||
// eslint-disable-next-line @typescript-eslint/no-unused-vars
|
||||
const { onTabChange, tabs, width, ...remainingProps } = props;
|
||||
|
|
@ -251,14 +190,6 @@ function TabsComponent(props: TabsComponentProps) {
|
|||
},
|
||||
[tabsRef.current],
|
||||
);
|
||||
// eslint-disable-next-line
|
||||
// const [_intervalRef, _rafRef, requestAF] = useThrottledRAF(scroll, 10);
|
||||
|
||||
// useEffect(() => {
|
||||
// if (!props.shouldScrollContents) {
|
||||
// tabContainerRef.current?.scrollTo({ top: 0, behavior: "smooth" });
|
||||
// }
|
||||
// }, [props.shouldScrollContents]);
|
||||
|
||||
return (
|
||||
<TabsContainerWrapper
|
||||
|
|
|
|||
|
|
@ -25,7 +25,7 @@ export const CONFIG = {
|
|||
},
|
||||
},
|
||||
defaults: {
|
||||
rows: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS,
|
||||
rows: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS + 5,
|
||||
columns: 24,
|
||||
shouldScrollContents: false,
|
||||
widgetName: "Tabs",
|
||||
|
|
@ -33,6 +33,7 @@ export const CONFIG = {
|
|||
borderWidth: 1,
|
||||
borderColor: Colors.GREY_5,
|
||||
backgroundColor: Colors.WHITE,
|
||||
minDynamicHeight: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS + 5,
|
||||
tabsObj: {
|
||||
tab1: {
|
||||
label: "Tab 1",
|
||||
|
|
@ -66,6 +67,7 @@ export const CONFIG = {
|
|||
tabName: "Tab 1",
|
||||
children: [],
|
||||
version: 1,
|
||||
bottomRow: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS,
|
||||
},
|
||||
},
|
||||
{
|
||||
|
|
@ -81,6 +83,7 @@ export const CONFIG = {
|
|||
tabName: "Tab 2",
|
||||
children: [],
|
||||
version: 1,
|
||||
bottomRow: WidgetHeightLimits.MIN_CANVAS_HEIGHT_IN_ROWS,
|
||||
},
|
||||
},
|
||||
],
|
||||
|
|
|
|||
|
|
@ -172,9 +172,7 @@ class TabsWidget extends BaseWidget<
|
|||
controlType: "SWITCH",
|
||||
isBindProperty: false,
|
||||
isTriggerProperty: false,
|
||||
postUpdateActions: [
|
||||
ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
],
|
||||
postUpdateAction: ReduxActionTypes.CHECK_CONTAINERS_FOR_AUTO_HEIGHT,
|
||||
},
|
||||
],
|
||||
},
|
||||
|
|
|
|||
|
|
@ -581,7 +581,7 @@ describe("Auto Height Utils", () => {
|
|||
expect(result).toBeUndefined();
|
||||
});
|
||||
|
||||
it.skip("should return 4 if widget has AUTO_HEIGHT", () => {
|
||||
it("should return 20 if widget has AUTO_HEIGHT and props has 20", () => {
|
||||
const props = {
|
||||
...DUMMY_WIDGET,
|
||||
dynamicHeight: "AUTO_HEIGHT",
|
||||
|
|
@ -589,7 +589,7 @@ describe("Auto Height Utils", () => {
|
|||
};
|
||||
|
||||
const result = getWidgetMinAutoHeight(props);
|
||||
expect(result).toBe(WidgetHeightLimits.MIN_HEIGHT_IN_ROWS);
|
||||
expect(result).toBe(20);
|
||||
});
|
||||
it("should return 20 if widget has AUTO_HEIGHT_WITH_LIMITS", () => {
|
||||
const props = {
|
||||
|
|
@ -612,7 +612,7 @@ describe("Auto Height Utils", () => {
|
|||
expect(result).toBe(WidgetHeightLimits.MIN_HEIGHT_IN_ROWS);
|
||||
});
|
||||
|
||||
it.skip("should return undefined if widget is FIXED ", () => {
|
||||
it("should return undefined if widget is FIXED ", () => {
|
||||
const props = {
|
||||
...DUMMY_WIDGET,
|
||||
dynamicHeight: "FIXED",
|
||||
|
|
|
|||
|
|
@ -772,7 +772,8 @@ export function getWidgetMaxAutoHeight(props: WidgetProps) {
|
|||
* @returns: The min possible height of the widget (in rows)
|
||||
*/
|
||||
export function getWidgetMinAutoHeight(props: WidgetProps) {
|
||||
return props.minDynamicHeight || WidgetHeightLimits.MIN_HEIGHT_IN_ROWS;
|
||||
if (props.dynamicHeight !== DynamicHeight.FIXED)
|
||||
return props.minDynamicHeight || WidgetHeightLimits.MIN_HEIGHT_IN_ROWS;
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -15,6 +15,7 @@ import {
|
|||
computeMainContainerWidget,
|
||||
getChildWidgets,
|
||||
getRenderMode,
|
||||
previewModeSelector,
|
||||
} from "selectors/editorSelectors";
|
||||
import { AppState } from "@appsmith/reducers";
|
||||
import { useDispatch, useSelector } from "react-redux";
|
||||
|
|
@ -33,6 +34,7 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
|||
props: WidgetProps & { skipWidgetPropsHydration?: boolean },
|
||||
) {
|
||||
const { children, skipWidgetPropsHydration, type, widgetId } = props;
|
||||
const isPreviewMode = useSelector(previewModeSelector);
|
||||
|
||||
const canvasWidget = useSelector((state: AppState) =>
|
||||
getWidget(state, widgetId),
|
||||
|
|
@ -122,25 +124,27 @@ function withWidgetProps(WrappedWidget: typeof BaseWidget) {
|
|||
|
||||
const shouldCollapseWidgetInViewOrPreviewMode =
|
||||
!widgetProps.isVisible &&
|
||||
(renderMode === RenderModes.PAGE || renderMode === RenderModes.PREVIEW) &&
|
||||
widgetProps.bottomRow !== widgetProps.topRow;
|
||||
(renderMode === RenderModes.PAGE || isPreviewMode);
|
||||
|
||||
const shouldResetCollapsedContainerHeightInViewOrPreviewMode =
|
||||
widgetProps.isVisible && widgetProps.topRow === widgetProps.bottomRow;
|
||||
|
||||
const shouldResetCollapsedContainerHeightInCanvasMode =
|
||||
widgetProps.topRow === widgetProps.bottomRow &&
|
||||
renderMode === RenderModes.CANVAS;
|
||||
renderMode === RenderModes.CANVAS &&
|
||||
!isPreviewMode;
|
||||
|
||||
// We don't render invisible widgets in view mode
|
||||
if (shouldCollapseWidgetInViewOrPreviewMode) {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.UPDATE_WIDGET_AUTO_HEIGHT,
|
||||
payload: {
|
||||
widgetId: props.widgetId,
|
||||
height: 0,
|
||||
},
|
||||
});
|
||||
if (widgetProps.bottomRow !== widgetProps.topRow) {
|
||||
dispatch({
|
||||
type: ReduxActionTypes.UPDATE_WIDGET_AUTO_HEIGHT,
|
||||
payload: {
|
||||
widgetId: props.widgetId,
|
||||
height: 0,
|
||||
},
|
||||
});
|
||||
}
|
||||
return null;
|
||||
} else if (
|
||||
shouldResetCollapsedContainerHeightInViewOrPreviewMode ||
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user