PromucFlow_constructor/app/client/src/sagas/CanvasSagas/DraggingCanvasSagas.ts
Tanvi Bhakta ac0c872843
chore: migrate toast (#17208)
* Refactor toast to be passed the dispatch hook externally

* Add comments explaining dilemma

* use store.dispatch instead of a hook

* use alpha version

* Change imports

* Refactor DebugButton out

* update release

* fix issue with incorrectly merged package.lock

* fix syntax of alpha version

* bump ds vesion

* copy lock from release

* update lock to have alpha

* make changes

* delete Toast

* DS package version updated

* import change from release

* use new alpha version

* update ds version

* update ds version

* chore: migrate editable text and friends (#17285)

* Delete empty components

* use alpha for ds

* Deleted EditableTextSubComponent, import changes

* Delete EditableText, import changes

* use ds alpha 10

* Delete EditableTextWrapper.tsx

* update ds to use next minor version

* use new alpha

* fix issue with merge

Co-authored-by: Albin <albin@appsmith.com>

* chore: migrate file picker v2 (#17308)

* use alpha ds

* Delete FilePickerV2, import changes

* Delete FilePicker, change imports

* update alpha version

* chore: move copy url form into setting components (#17322)

* move CopyUrlForm to src/pages/settings/formgroup

* update ds version to use next minor release

* feat: Migrate table component to design system (#17329)

* feat: Migrate table component to design system

* removed commented code in ads index file

* fix: table no data hover effect removed

Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com>

* feat: Banner message component migrated to design system (#17327)

* feat: Banner image component migrated to design system

* Version update for design system package

* design system version updated

Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com>

* feat: Tabs component migrated to design system (#17321)

* feat: Tabs component migrated to design system

* design system package version updated

* Update app/client/src/components/editorComponents/form/FormDialogComponent.tsx

* Update app/client/src/pages/Editor/PropertyPane/PropertyPaneTab.tsx

* Tab component expand issue fix

Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com>

Co-authored-by: Albin <albin@appsmith.com>
Co-authored-by: albinAppsmith <87797149+albinAppsmith@users.noreply.github.com>
2022-10-14 01:43:44 +05:30

396 lines
12 KiB
TypeScript

import { Toaster } from "design-system";
import {
ReduxAction,
ReduxActionErrorTypes,
ReduxActionTypes,
} from "@appsmith/constants/ReduxActionConstants";
import {
CanvasWidgetsReduxState,
FlattenedWidgetProps,
} from "reducers/entityReducers/canvasWidgetsReducer";
import { all, call, put, select, takeLatest } from "redux-saga/effects";
import log from "loglevel";
import { cloneDeep } from "lodash";
import { updateAndSaveLayout, WidgetAddChild } from "actions/pageActions";
import { calculateDropTargetRows } from "components/editorComponents/DropTargetUtils";
import {
GridDefaults,
MAIN_CONTAINER_WIDGET_ID,
} from "constants/WidgetConstants";
import { WidgetProps } from "widgets/BaseWidget";
import {
getMainCanvasProps,
getOccupiedSpacesSelectorForContainer,
} from "selectors/editorSelectors";
import { OccupiedSpace } from "constants/CanvasEditorConstants";
import { collisionCheckPostReflow } from "utils/reflowHookUtils";
import { WidgetDraggingUpdateParams } from "pages/common/CanvasArenas/hooks/useBlocksToBeDraggedOnCanvas";
import { getWidget, getWidgets } from "sagas/selectors";
import { getUpdateDslAfterCreatingChild } from "sagas/WidgetAdditionSagas";
import { MainCanvasReduxState } from "reducers/uiReducers/mainCanvasReducer";
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
import AnalyticsUtil from "utils/AnalyticsUtil";
export type WidgetMoveParams = {
widgetId: string;
leftColumn: number;
topRow: number;
bottomRow: number;
rightColumn: number;
parentId: string;
/*
If newParentId is different from what we have in redux store,
then we have to delete this,
as it has been dropped in another container somewhere.
*/
newParentId: string;
allWidgets: CanvasWidgetsReduxState;
};
export function* getCanvasSizeAfterWidgetMove(
canvasWidgetId: string,
movedWidgetIds: string[],
movedWidgetsBottomRow: number,
) {
const canvasWidget: WidgetProps = yield select(getWidget, canvasWidgetId);
//get mainCanvas's minHeight if the canvasWidget is mianCanvas
let mainCanvasMinHeight;
if (canvasWidgetId === MAIN_CONTAINER_WIDGET_ID) {
const mainCanvasProps: MainCanvasReduxState = yield select(
getMainCanvasProps,
);
mainCanvasMinHeight = mainCanvasProps?.height;
}
if (canvasWidget) {
const occupiedSpacesByChildren: OccupiedSpace[] | undefined = yield select(
getOccupiedSpacesSelectorForContainer(canvasWidgetId),
);
const canvasMinHeight =
mainCanvasMinHeight ||
canvasWidget.minHeight ||
CANVAS_DEFAULT_MIN_HEIGHT_PX;
const newRows = calculateDropTargetRows(
movedWidgetIds,
movedWidgetsBottomRow,
canvasMinHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
occupiedSpacesByChildren,
canvasWidgetId,
);
const rowsToPersist = Math.max(
canvasMinHeight / GridDefaults.DEFAULT_GRID_ROW_HEIGHT - 1,
newRows,
);
const originalSnapRows = canvasWidget.bottomRow - canvasWidget.topRow;
const newBottomRow = Math.round(
rowsToPersist * GridDefaults.DEFAULT_GRID_ROW_HEIGHT,
);
/* Update the canvas's rows, ONLY if it has changed since the last render */
if (originalSnapRows !== newBottomRow) {
// TODO(abhinav): This considers that the topRow will always be zero
// Check this out when non canvas widgets are updating snapRows
// erstwhile: Math.round((rows * props.snapRowSpace) / props.parentRowSpace),
return newBottomRow;
}
return canvasWidget.bottomRow;
}
}
const getBottomMostRowAfterMove = (
draggedBlocksToUpdate: WidgetDraggingUpdateParams[],
allWidgets: CanvasWidgetsReduxState,
) => {
const bottomMostBlock =
draggedBlocksToUpdate[draggedBlocksToUpdate.length - 1];
const widget = allWidgets[bottomMostBlock.widgetId];
const { updateWidgetParams } = bottomMostBlock;
const widgetBottomRow =
updateWidgetParams.payload.topRow +
(updateWidgetParams.payload.rows || widget.bottomRow - widget.topRow);
return widgetBottomRow;
};
function* addWidgetAndMoveWidgetsSaga(
actionPayload: ReduxAction<{
newWidget: WidgetAddChild;
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
canvasId: string;
}>,
) {
const start = performance.now();
const { canvasId, draggedBlocksToUpdate, newWidget } = actionPayload.payload;
try {
const updatedWidgetsOnAddAndMove: CanvasWidgetsReduxState = yield call(
addWidgetAndMoveWidgets,
newWidget,
draggedBlocksToUpdate,
canvasId,
);
if (
!collisionCheckPostReflow(
updatedWidgetsOnAddAndMove,
draggedBlocksToUpdate.map((block) => block.widgetId),
canvasId,
)
) {
throw Error;
}
yield put(updateAndSaveLayout(updatedWidgetsOnAddAndMove));
yield put({
type: ReduxActionTypes.RECORD_RECENTLY_ADDED_WIDGET,
payload: [newWidget.newWidgetId],
});
log.debug("move computations took", performance.now() - start, "ms");
} catch (error) {
yield put({
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGETS_ADD_CHILD_AND_MOVE,
error,
},
});
}
}
function* addWidgetAndMoveWidgets(
newWidget: WidgetAddChild,
draggedBlocksToUpdate: WidgetDraggingUpdateParams[],
canvasId: string,
) {
const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
const updatedWidgetsOnAddition: CanvasWidgetsReduxState = yield call(
getUpdateDslAfterCreatingChild,
{ ...newWidget, widgetId: canvasId },
);
const bottomMostRowOnAddition = updatedWidgetsOnAddition[canvasId]
? updatedWidgetsOnAddition[canvasId].bottomRow
: 0;
const allWidgetsAfterAddition = {
...allWidgets,
...updatedWidgetsOnAddition,
};
const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call(
moveAndUpdateWidgets,
allWidgetsAfterAddition,
draggedBlocksToUpdate,
canvasId,
);
const bottomMostRowOnMove = updatedWidgetsOnMove[canvasId]
? updatedWidgetsOnMove[canvasId].bottomRow
: 0;
const bottomMostRow =
bottomMostRowOnAddition > bottomMostRowOnMove
? bottomMostRowOnAddition
: bottomMostRowOnMove;
const updatedWidgets = {
...updatedWidgetsOnMove,
};
updatedWidgets[canvasId].bottomRow = bottomMostRow;
return updatedWidgets;
}
function* moveAndUpdateWidgets(
allWidgets: CanvasWidgetsReduxState,
draggedBlocksToUpdate: WidgetDraggingUpdateParams[],
canvasId: string,
) {
const widgets = cloneDeep(allWidgets);
const bottomMostRowAfterMove = getBottomMostRowAfterMove(
draggedBlocksToUpdate,
allWidgets,
);
// draggedBlocksToUpdate is already sorted based on bottomRow
const updatedWidgets = draggedBlocksToUpdate.reduce((widgetsObj, each) => {
return moveWidget({
...each.updateWidgetParams.payload,
widgetId: each.widgetId,
allWidgets: widgetsObj,
});
}, widgets);
const movedWidgetIds = draggedBlocksToUpdate.map((a) => a.widgetId);
const updatedCanvasBottomRow: number = yield call(
getCanvasSizeAfterWidgetMove,
canvasId,
movedWidgetIds,
bottomMostRowAfterMove,
);
if (updatedCanvasBottomRow) {
const canvasWidget = updatedWidgets[canvasId];
updatedWidgets[canvasId] = {
...canvasWidget,
bottomRow: updatedCanvasBottomRow,
};
}
return updatedWidgets;
}
function getParentWidgetType(
allWidgets: CanvasWidgetsReduxState,
widgetId: string,
) {
const widget = allWidgets[widgetId];
if (!widget.parentId) return "MAIN_CONTAINER";
const containerWidget = allWidgets[widget.parentId];
/**
* container widget can be of type FORM_WIDGET, STATBOX_WIDGET
*/
if (containerWidget.type !== "CONTAINER_WIDGET") {
return containerWidget.type;
}
/**
* Handling the case for list widget where we have
* canvas2 -> container -> canvas1 -> listWidget
*/
if (containerWidget.parentId) {
// Take the first parent that is canvas1
const containerParent = allWidgets[containerWidget.parentId];
// Now take the parent of canvas1 that is listWidget
if (containerParent.parentId) {
const mainParent = allWidgets[containerParent.parentId];
return mainParent.type;
}
}
return containerWidget.type;
}
function* moveWidgetsSaga(
actionPayload: ReduxAction<{
draggedBlocksToUpdate: WidgetDraggingUpdateParams[];
canvasId: string;
}>,
) {
const start = performance.now();
const { canvasId, draggedBlocksToUpdate } = actionPayload.payload;
try {
const allWidgets: CanvasWidgetsReduxState = yield select(getWidgets);
const updatedWidgetsOnMove: CanvasWidgetsReduxState = yield call(
moveAndUpdateWidgets,
allWidgets,
draggedBlocksToUpdate,
canvasId,
);
if (
!collisionCheckPostReflow(
updatedWidgetsOnMove,
draggedBlocksToUpdate.map((block) => block.widgetId),
canvasId,
)
) {
throw Error;
}
yield put(updateAndSaveLayout(updatedWidgetsOnMove));
const block = draggedBlocksToUpdate[0];
const oldParentId = block.updateWidgetParams.payload.parentId;
const newParentId = block.updateWidgetParams.payload.newParentId;
const oldParentWidgetType = getParentWidgetType(allWidgets, oldParentId);
const newParentWidgetType = getParentWidgetType(allWidgets, newParentId);
AnalyticsUtil.logEvent("WIDGET_DRAG", {
widgets: draggedBlocksToUpdate.map((block) => {
const widget = allWidgets[block.widgetId];
return {
widgetType: widget.type,
widgetName: widget.widgetName,
};
}),
multiple: draggedBlocksToUpdate.length > 1,
movedToNewWidget: oldParentId !== newParentId,
source: oldParentWidgetType,
destination: newParentWidgetType,
});
log.debug("move computations took", performance.now() - start, "ms");
} catch (error) {
yield put({
type: ReduxActionErrorTypes.WIDGET_OPERATION_ERROR,
payload: {
action: ReduxActionTypes.WIDGETS_MOVE,
error,
},
});
}
}
function moveWidget(widgetMoveParams: WidgetMoveParams) {
Toaster.clear();
const {
allWidgets,
bottomRow,
leftColumn,
newParentId,
parentId,
rightColumn,
topRow,
widgetId,
} = widgetMoveParams;
const stateWidget: FlattenedWidgetProps = allWidgets[widgetId];
let widget = Object.assign({}, stateWidget);
// Get all widgets from DSL/Redux Store
const widgets = Object.assign({}, allWidgets);
// Get parent from DSL/Redux Store
const stateParent: FlattenedWidgetProps = allWidgets[parentId];
const parent = {
...stateParent,
children: [...(stateParent.children || [])],
};
// Update position of widget
const updatedPosition = {
topRow,
bottomRow,
leftColumn,
rightColumn,
};
widget = { ...widget, ...updatedPosition };
// Replace widget with update widget props
widgets[widgetId] = widget;
// If the parent has changed i.e parentWidgetId is not parent.widgetId
if (parent.widgetId !== newParentId && widgetId !== newParentId) {
// Remove from the previous parent
if (parent.children && Array.isArray(parent.children)) {
const indexOfChild = parent.children.indexOf(widgetId);
if (indexOfChild > -1) delete parent.children[indexOfChild];
parent.children = parent.children.filter(Boolean);
}
// Add to new parent
widgets[parent.widgetId] = parent;
const newParent = {
...widgets[newParentId],
children: widgets[newParentId].children
? [...(widgets[newParentId].children || []), widgetId]
: [widgetId],
};
widgets[widgetId].parentId = newParentId;
widgets[newParentId] = newParent;
}
return widgets;
}
export default function* draggingCanvasSagas() {
yield all([
takeLatest(ReduxActionTypes.WIDGETS_MOVE, moveWidgetsSaga),
takeLatest(
ReduxActionTypes.WIDGETS_ADD_CHILD_AND_MOVE,
addWidgetAndMoveWidgetsSaga,
),
]);
}