PromucFlow_constructor/app/client/src/utils/WidgetPropsUtils.tsx
Pawan Kumar 8395f5e18f
feat: property pane docking (#7361)
* add tailwindcss

* docked property pane

* uncomment a line

* make entity explorer as drawer on unpin

* remove unused imports

* add pin state in  reducer

* add menu icon in header

* fix widget sidebar

* fix widgets sidebar

* style property pane

* update property pane css

* update icons in property pane

* update property pane header styles

* update spacing

* fix few ui issues

* wip: preview mode

* wip:preview mode

* remove unused import

* comments sidebar in app and edit mode

* fix order of import

* use selected state for property pane

* update scrollbar style

* add classes to sidebar and property pane

* make widgets editor fluid

* make widgets editor fluid and refactor logic

* resize the widgets editor if explorer is pinned

* add shortcut for preview mode

* fix link for tabs in edit mode

* zoom in/zoom out for 0.75

* fix chart widget + table widget crashing

* allow zooming of canvas

* fix weird canvas draw issue + update container for handling zoom

* add actions for is panning

* allow panning with grab cursor

* reset panning + zooming when entering preview mode

* add grabbing cursor when grabbing

* only prevent default when space key is pressed

* dont allow zoom in preview mode

* remove unused imports

* fix dont allow zoom in preview mode

* fix ux of panning on space hit

* make fluid as the default app layout

* chart spec

* fix dropdown_on change spec

* fix add widget table and bind spec

* remove draggable property pane spec

* fix container spec

* fix form widget spec

* fix jest test

* fix the function typo

* remove clicking of close button for property pane in cypress tests

* remove property pane actions test

* fix drag and drop test failing

* add cypress selector id to back button in property pane

* fix toggle js spec

* fix merge conflicts from new design system

* editor header

* fix product updates styles + widget card

* remove all unused imports

* fix dynamic layout spec

* fix entity explorer tab rename test failing

* fix table spec

* fix bind tabletextpagination spec

* fix js object spec

* fix entity explorer rename issue

* fix cypress test

* fix cypress command wrong commit

* fix tab spec

* fix property pane copy tests

* add zoom header

* zoom levels

* make property pane sidebar resizable

* add multi select property pane

* fix widget search bug

* update property pane width in state on drag end

* fix viewer header

* fix editor header

* update editor header + remove zooming

* update small style

* dont allow closing of explorer when resizing

* fix jest test

* fix dropdown widget jest test

* preview test case wip

* add entity explorer pinning tests + preview mode tests

* add tooltip in layout control + add padding bottom in property pane view

* incorporate aakash feedbacks

* fix preview mode margin issue

* remove panning code

* fix cypress failing test

* uncomment jest test

* remove redundant code

* fix maincontainer test

* incorporate review feedbacks

* incorporate aakash feedbacks

* review feedbacks

* incorporate review feedbacks

* incorporate qa feedbacks

* fix dynamic layout spec

* updated test based on latest change

* dsl updated

* Updated dsl

* Updated dsl

* resize deselects widget issue.

* fix canvas height issue

* fix typo

* incorporate qa feedbacks

* incorporate qa feedbacks

* incorporate qa feedbacks

* update color for setting control for widget name

* fix onboarding styles conflicts

* Updated tests

* fix application overflow issue

* updated test method

Co-authored-by: root <root@DESKTOP-9GENCK0.localdomain>
Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro.local>
Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com>
Co-authored-by: Apple <nandan@thinkify.io>
2021-11-23 13:31:46 +05:30

291 lines
7.0 KiB
TypeScript

import { FetchPageResponse } from "api/PageApi";
import { XYCord } from "utils/hooks/useCanvasDragging";
import { WidgetConfigProps } from "reducers/entityReducers/widgetConfigReducer";
import {
WidgetOperation,
WidgetOperations,
WidgetProps,
} from "widgets/BaseWidget";
import { GridDefaults, RenderMode } 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";
export type WidgetOperationParams = {
operation: WidgetOperation;
widgetId: string;
payload: any;
};
type Rect = {
top: number;
left: number;
right: number;
bottom: number;
};
const defaultDSL = defaultTemplate;
export const extractCurrentDSL = (
fetchPageResponse?: FetchPageResponse,
): DSLWidget => {
const newPage = !fetchPageResponse;
const currentDSL = fetchPageResponse?.data.layouts[0].dsl || {
...defaultDSL,
};
return transformDSL(currentDSL, newPage);
};
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 areIntersecting = (r1: Rect, r2: Rect) => {
return !(
r2.left >= r1.right ||
r2.right <= r1.left ||
r2.top >= r1.bottom ||
r2.bottom <= r1.top
);
};
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
): 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,
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 updateWidgetPosition = (
widget: WidgetProps,
leftColumn: number,
topRow: number,
) => {
const newPositions = {
leftColumn,
topRow,
rightColumn: leftColumn + (widget.rightColumn - widget.leftColumn),
bottomRow: topRow + (widget.bottomRow - widget.topRow),
};
return {
...newPositions,
};
};
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 ");
}
};