* Add auto height reflow functions libary * Add comments in hard to understand parts * feat: auto height reflow lib (#17978) added 2 tests for boxHelper and 1 simple test for reflow computeChangeInPositionBasedOnDelta Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> * Reduce one loop. Fix typo * Add helper functions and use them in lib * Use helper function * Add types * Fix issue where widgets don't get close to the bottom most above widget if that widget hasn't changed * fix: auto height reflow lib merge release (#18193) * feat: show number of tabs on the header (#18071) * number of tabs displayed alongside label * styling for span removed * feature added and cypress test cases written * code refactoring after review * Update top contributors * feat: Auto-height add reducers and actions (#17953) * Add reducers for auto height feature (Internal Change, No changes reflected to users) Co-authored-by: ankurrsinghal <ankur@appsmith.com> * [Bug] Incorrect count of users in workspace when adding multiple users (#17728) fix: filtering unique users by userId Co-authored-by: Anubhav <anubhav@appsmith.com> * fix: Instrumentation for execution errors (#18093) * fix: Instrumentation for execution errors * added widget editor error event * fix: Sidebar heading fontSize & checkbox alignment (#18104) sidebar heading & checkbox alignment to heading * chore: added type for feature flag (#18152) add: new type for env variable * Update top contributors * feat: [Context Switching]: Change focus target and fix cursor position (#17794) Co-authored-by: rahulramesha <rahul@appsmith.com> * fix: JS Objects save failures due to AST changes (#18018) * fix: update local_testing.sh to build image for external contributor PRs (#18024) * chore: use `typography` and `getTypographyFromKey` from the design-system (#18050) Change typography imports, change function call * dummy Co-authored-by: Rishabh Kashyap <rishabh.kashyap@appsmith.com> Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Abhinav Jha <abhinav@appsmith.com> Co-authored-by: Ankit Srivastava <67647761+ankitsrivas14@users.noreply.github.com> Co-authored-by: Anubhav <anubhav@appsmith.com> Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> Co-authored-by: Rohit Agarwal <rohit_agarwal@live.in> Co-authored-by: Ayush Pahwa <ayush@appsmith.com> Co-authored-by: Hetu Nandu <hetu@appsmith.com> Co-authored-by: subratadeypappu <subrata@appsmith.com> Co-authored-by: Sumit Kumar <sumit@appsmith.com> Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> Co-authored-by: ankurrsinghal <ankur@appsmith.com> Co-authored-by: Ankur Singhal <ankurrsinghal@gmail.com> Co-authored-by: Rishabh Kashyap <rishabh.kashyap@appsmith.com> Co-authored-by: Appsmith Bot <74705725+appsmith-bot@users.noreply.github.com> Co-authored-by: Ankit Srivastava <67647761+ankitsrivas14@users.noreply.github.com> Co-authored-by: Anubhav <anubhav@appsmith.com> Co-authored-by: ChandanBalajiBP <104058110+ChandanBalajiBP@users.noreply.github.com> Co-authored-by: Rohit Agarwal <rohit_agarwal@live.in> Co-authored-by: Ayush Pahwa <ayush@appsmith.com> Co-authored-by: Hetu Nandu <hetu@appsmith.com> Co-authored-by: subratadeypappu <subrata@appsmith.com> Co-authored-by: Sumit Kumar <sumit@appsmith.com> Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com>
338 lines
8.5 KiB
TypeScript
338 lines
8.5 KiB
TypeScript
import { FetchPageResponse } from "api/PageApi";
|
|
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
|
|
import {
|
|
WidgetOperation,
|
|
WidgetOperations,
|
|
WidgetProps,
|
|
} from "widgets/BaseWidget";
|
|
import {
|
|
CONTAINER_GRID_PADDING,
|
|
GridDefaults,
|
|
RenderMode,
|
|
WIDGET_PADDING,
|
|
} from "constants/WidgetConstants";
|
|
import { snapToGrid } from "./helpers";
|
|
import { OccupiedSpace } from "constants/CanvasEditorConstants";
|
|
import defaultTemplate from "templates/default";
|
|
import { FlattenedWidgetProps } from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import { transformDSL } from "./DSLMigrations";
|
|
import { WidgetType } from "./WidgetFactory";
|
|
import { DSLWidget } from "widgets/constants";
|
|
import { WidgetDraggingBlock } from "pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas";
|
|
import { XYCord } from "pages/common/CanvasArenas/hooks/useCanvasDragging";
|
|
import { ContainerWidgetProps } from "widgets/ContainerWidget/widget";
|
|
import { GridProps } from "reflow/reflowTypes";
|
|
import { areIntersecting, Rect } from "./boxHelpers";
|
|
|
|
export type WidgetOperationParams = {
|
|
operation: WidgetOperation;
|
|
widgetId: string;
|
|
payload: any;
|
|
};
|
|
|
|
const defaultDSL = defaultTemplate;
|
|
|
|
export const extractCurrentDSL = (
|
|
fetchPageResponse?: FetchPageResponse,
|
|
): DSLWidget => {
|
|
const newPage = !fetchPageResponse;
|
|
const currentDSL = fetchPageResponse?.data.layouts[0].dsl || {
|
|
...defaultDSL,
|
|
};
|
|
return transformDSL(currentDSL as ContainerWidgetProps<WidgetProps>, newPage);
|
|
};
|
|
|
|
/**
|
|
* To get updated positions of the dragging blocks
|
|
*
|
|
* @param draggingBlocks
|
|
* @param snapColumnSpace
|
|
* @param snapRowSpace
|
|
* @returns An array of updated positions of the dragging blocks
|
|
*/
|
|
export function getDraggingSpacesFromBlocks(
|
|
draggingBlocks: WidgetDraggingBlock[],
|
|
snapColumnSpace: number,
|
|
snapRowSpace: number,
|
|
): OccupiedSpace[] {
|
|
const draggingSpaces = [];
|
|
for (const draggingBlock of draggingBlocks) {
|
|
//gets top and left position of the block
|
|
const [leftColumn, topRow] = getDropZoneOffsets(
|
|
snapColumnSpace,
|
|
snapRowSpace,
|
|
{
|
|
x: draggingBlock.left,
|
|
y: draggingBlock.top,
|
|
},
|
|
{
|
|
x: 0,
|
|
y: 0,
|
|
},
|
|
);
|
|
draggingSpaces.push({
|
|
left: leftColumn,
|
|
top: topRow,
|
|
right: leftColumn + draggingBlock.width / snapColumnSpace,
|
|
bottom: topRow + draggingBlock.height / snapRowSpace,
|
|
id: draggingBlock.widgetId,
|
|
});
|
|
}
|
|
return draggingSpaces;
|
|
}
|
|
|
|
export const getDropZoneOffsets = (
|
|
colWidth: number,
|
|
rowHeight: number,
|
|
dragOffset: XYCord,
|
|
parentOffset: XYCord,
|
|
) => {
|
|
// Calculate actual drop position by snapping based on x, y and grid cell size
|
|
return snapToGrid(
|
|
colWidth,
|
|
rowHeight,
|
|
dragOffset.x - parentOffset.x,
|
|
dragOffset.y - parentOffset.y,
|
|
);
|
|
};
|
|
|
|
export const getMousePositionsOnCanvas = (
|
|
e: MouseEvent,
|
|
gridProps: GridProps,
|
|
) => {
|
|
const mouseTop = Math.floor(
|
|
(e.offsetY - CONTAINER_GRID_PADDING - WIDGET_PADDING) /
|
|
gridProps.parentRowSpace,
|
|
);
|
|
const mouseLeft = Math.floor(
|
|
(e.offsetX - CONTAINER_GRID_PADDING - WIDGET_PADDING) /
|
|
gridProps.parentColumnSpace,
|
|
);
|
|
|
|
return {
|
|
id: "mouse",
|
|
top: mouseTop,
|
|
left: mouseLeft,
|
|
bottom: mouseTop + 1,
|
|
right: mouseLeft + 1,
|
|
};
|
|
};
|
|
|
|
export const isDropZoneOccupied = (
|
|
offset: Rect,
|
|
widgetId: string,
|
|
occupied?: OccupiedSpace[],
|
|
) => {
|
|
if (occupied) {
|
|
occupied = occupied.filter((widgetDetails) => {
|
|
return (
|
|
widgetDetails.id !== widgetId && widgetDetails.parentId !== widgetId
|
|
);
|
|
});
|
|
for (let i = 0; i < occupied.length; i++) {
|
|
if (areIntersecting(occupied[i], offset)) {
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
return false;
|
|
};
|
|
|
|
export const isWidgetOverflowingParentBounds = (
|
|
parentRowCols: { rows?: number; cols?: number },
|
|
offset: Rect,
|
|
): boolean => {
|
|
return (
|
|
offset.right < 0 ||
|
|
offset.top < 0 ||
|
|
(parentRowCols.cols || GridDefaults.DEFAULT_GRID_COLUMNS) < offset.right ||
|
|
(parentRowCols.rows || 0) < offset.bottom
|
|
);
|
|
};
|
|
|
|
export const noCollision = (
|
|
clientOffset: XYCord,
|
|
colWidth: number,
|
|
rowHeight: number,
|
|
dropTargetOffset: XYCord,
|
|
widgetWidth: number,
|
|
widgetHeight: number,
|
|
widgetId: string,
|
|
occupiedSpaces?: OccupiedSpace[],
|
|
rows?: number,
|
|
cols?: number,
|
|
detachFromLayout = false,
|
|
): boolean => {
|
|
if (detachFromLayout) {
|
|
return true;
|
|
}
|
|
if (clientOffset && dropTargetOffset) {
|
|
const [left, top] = getDropZoneOffsets(
|
|
colWidth,
|
|
rowHeight,
|
|
clientOffset as XYCord,
|
|
dropTargetOffset,
|
|
);
|
|
if (left < 0 || top < 0) {
|
|
return false;
|
|
}
|
|
const currentOffset = {
|
|
left,
|
|
right: left + widgetWidth,
|
|
top,
|
|
bottom: top + widgetHeight,
|
|
};
|
|
return (
|
|
!isDropZoneOccupied(currentOffset, widgetId, occupiedSpaces) &&
|
|
!isWidgetOverflowingParentBounds({ rows, cols }, currentOffset)
|
|
);
|
|
}
|
|
return false;
|
|
};
|
|
|
|
export const currentDropRow = (
|
|
dropTargetRowSpace: number,
|
|
dropTargetVerticalOffset: number,
|
|
draggableItemVerticalOffset: number,
|
|
widget: WidgetProps & Partial<WidgetConfigProps>,
|
|
) => {
|
|
const widgetHeight = widget.rows
|
|
? widget.rows
|
|
: widget.bottomRow - widget.topRow;
|
|
const top = Math.round(
|
|
(draggableItemVerticalOffset - dropTargetVerticalOffset) /
|
|
dropTargetRowSpace,
|
|
);
|
|
const currentBottomOffset = top + widgetHeight;
|
|
return currentBottomOffset;
|
|
};
|
|
|
|
export const widgetOperationParams = (
|
|
widget: WidgetProps & Partial<WidgetConfigProps>,
|
|
widgetOffset: XYCord,
|
|
parentOffset: XYCord,
|
|
parentColumnSpace: number,
|
|
parentRowSpace: number,
|
|
parentWidgetId: string, // parentWidget
|
|
widgetSizeUpdates: {
|
|
width: number;
|
|
height: number;
|
|
},
|
|
): WidgetOperationParams => {
|
|
const [leftColumn, topRow] = getDropZoneOffsets(
|
|
parentColumnSpace,
|
|
parentRowSpace,
|
|
widgetOffset,
|
|
parentOffset,
|
|
);
|
|
// If this is an existing widget, we'll have the widgetId
|
|
// Therefore, this is a move operation on drop of the widget
|
|
if (widget.widgetName) {
|
|
return {
|
|
operation: WidgetOperations.MOVE,
|
|
widgetId: widget.widgetId,
|
|
payload: {
|
|
leftColumn,
|
|
topRow,
|
|
bottomRow: Math.round(
|
|
topRow + widgetSizeUpdates.height / parentRowSpace,
|
|
),
|
|
rightColumn: Math.round(
|
|
leftColumn + widgetSizeUpdates.width / parentColumnSpace,
|
|
),
|
|
parentId: widget.parentId,
|
|
newParentId: parentWidgetId,
|
|
},
|
|
};
|
|
// If this is not an existing widget, we'll not have the widgetId
|
|
// Therefore, this is an operation to add child to this container
|
|
}
|
|
const widgetDimensions = {
|
|
columns: widget.columns,
|
|
rows: widget.rows,
|
|
};
|
|
|
|
return {
|
|
operation: WidgetOperations.ADD_CHILD,
|
|
widgetId: parentWidgetId,
|
|
payload: {
|
|
type: widget.type,
|
|
leftColumn,
|
|
topRow,
|
|
...widgetDimensions,
|
|
parentRowSpace,
|
|
parentColumnSpace,
|
|
newWidgetId: widget.widgetId,
|
|
},
|
|
};
|
|
};
|
|
|
|
export const getCanvasSnapRows = (
|
|
bottomRow: number,
|
|
canExtend: boolean,
|
|
): number => {
|
|
const totalRows = Math.floor(
|
|
bottomRow / GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
|
|
);
|
|
|
|
// Canvas Widgets do not need to accommodate for widget and container padding.
|
|
// Only when they're extensible
|
|
if (canExtend) {
|
|
return totalRows;
|
|
}
|
|
// When Canvas widgets are not extensible
|
|
return totalRows - 1;
|
|
};
|
|
|
|
export const getSnapColumns = (): number => {
|
|
return GridDefaults.DEFAULT_GRID_COLUMNS;
|
|
};
|
|
|
|
export const generateWidgetProps = (
|
|
parent: FlattenedWidgetProps,
|
|
type: WidgetType,
|
|
leftColumn: number,
|
|
topRow: number,
|
|
parentRowSpace: number,
|
|
parentColumnSpace: number,
|
|
widgetName: string,
|
|
widgetConfig: {
|
|
widgetId: string;
|
|
renderMode: RenderMode;
|
|
} & Partial<WidgetProps>,
|
|
version: number,
|
|
): DSLWidget => {
|
|
if (parent) {
|
|
const sizes = {
|
|
leftColumn,
|
|
rightColumn: leftColumn + widgetConfig.columns,
|
|
topRow,
|
|
bottomRow: topRow + widgetConfig.rows,
|
|
};
|
|
|
|
const others = {};
|
|
const props: DSLWidget = {
|
|
// Todo(abhinav): abstraction leak
|
|
isVisible: "MODAL_WIDGET" === type ? undefined : true,
|
|
...widgetConfig,
|
|
type,
|
|
widgetName,
|
|
isLoading: false,
|
|
parentColumnSpace,
|
|
parentRowSpace,
|
|
...sizes,
|
|
...others,
|
|
parentId: parent.widgetId,
|
|
version,
|
|
};
|
|
delete props.rows;
|
|
delete props.columns;
|
|
return props;
|
|
} else {
|
|
if (parent) {
|
|
throw Error("Failed to create widget: Parent's size cannot be calculate");
|
|
} else throw Error("Failed to create widget: Parent was not provided ");
|
|
}
|
|
};
|