PromucFlow_constructor/app/client/src/actions/pageActions.tsx
Ashok Kumar M 0149085bf8
feat: Reflow and Resize while Dragging and Resizing widgets. (#9054)
* resize n reflow rough cut

* removing warnings

* relatively stable changes

* minor bug fix

* reflow relative collision

* working dp cut

* fix for reflow of widgets closer next to each other

* disabling scroll

* Drag with reflow

* reflow fix

* overlap and retracing fix

* On Drop updates.

* bug when no displacement but resize update.

* temp fix for new widget addition.

* reflow bug fixes

* new widget addition bug.

* stop reflow on leave.

* fix corner case overlap

* update bottom row when reflowed widgets go beyond bottom boundary.

* capture mouse positions on enter

* enable container jumps with faster mouse movements.

* reflow only for snap changes.

* restructured reflow Algorithm

* collision check and bug fixes

* undo redo fix for new widget drop

* resizable fix snapRows fix

* directional stability

* self collision fix

* first round of perf fixes

* update bottom row while resizing and resize-reflowing

* performance fix and overlapping fix

* Remove eslint warning

* remove eslint warning

* eslint warning

* can reflowed Drop Indication Stability

* container jumps and force direction on entering canvas

* fixing scroll on resize jitters.

* reflow when jumping into container.

* reflow ux fixes while leaving container

* resizing fixes.

* fixes for edge move.

* restrict container jumps into reflowed containers.

* container jump direction reflow

* checkbox dimensions fix.

* Excess bottom rows not lost post dragging or resizing widgets.

* fixing the after drop css glitch.

* double first move trigger bug fix.

* stop reflow only if reflowing

* stabilize container exit directions

* using acceleration and speed instead of movement covered to restrict reflow.

* fixing modal drops.

* remove warnings.

* reflow resize styles

* moving acceleration and movement logic to a monitoring effect.

* adding beta flag for reflow.

* fixing jest tests

* Adding analytics to beta flag toggle.

* Adding placeholder for reflow beta screens.

* fixing initial load's screen

* few more crashes.

* force close onboarding for the session.

* fixing bugs in reset canvas.

* Beta flag bug fixes.

* fixing bugs.

* restrict reflow screens during onboarding.

* disabling reflow screens in tests.

* code review comments.

* fixing store based specs.

* fixing cypress failures.

* fixing specs.

* code cleanup

* reverting yarn lock changes

* removing onboarding screens.

* more cleanup and function descriptors

* keeping reflow under the hood.

Co-authored-by: rahulramesha <rahul@appsmith.com>
Co-authored-by: Arpit Mohan <arpit@appsmith.com>
2022-01-13 18:51:57 +05:30

474 lines
9.5 KiB
TypeScript

import { WidgetType } from "constants/WidgetConstants";
import {
EvaluationReduxAction,
ReduxAction,
ReduxActionTypes,
ReduxActionWithoutPayload,
UpdateCanvasPayload,
ReduxActionErrorTypes,
} from "constants/ReduxActionConstants";
import AnalyticsUtil from "utils/AnalyticsUtil";
import { WidgetOperation } from "widgets/BaseWidget";
import { FetchPageRequest, PageLayout, SavePageResponse } from "api/PageApi";
import { UrlDataState } from "reducers/entityReducers/appReducer";
import { APP_MODE } from "entities/App";
import { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
import { GenerateTemplatePageRequest } from "../api/PageApi";
import {
WidgetReduxActionTypes,
ReplayReduxActionTypes,
} from "../constants/ReduxActionConstants";
import { ENTITY_TYPE } from "entities/AppsmithConsole";
import { Replayable } from "entities/Replay/ReplayEntity/ReplayEditor";
export interface FetchPageListPayload {
applicationId: string;
mode: APP_MODE;
}
export interface ClonePageActionPayload {
id: string;
blockNavigation?: boolean;
}
export interface CreatePageActionPayload {
applicationId: string;
name: string;
layouts: Partial<PageLayout>[];
blockNavigation?: boolean;
}
export const fetchPageList = (
{
applicationId,
}: {
applicationId: string;
},
mode: APP_MODE,
): ReduxAction<FetchPageListPayload> => {
return {
type: ReduxActionTypes.FETCH_PAGE_LIST_INIT,
payload: {
applicationId,
mode,
},
};
};
export const fetchPage = (
pageId: string,
isFirstLoad = false,
): ReduxAction<FetchPageRequest> => {
return {
type: ReduxActionTypes.FETCH_PAGE_INIT,
payload: {
id: pageId,
isFirstLoad,
},
};
};
export const fetchPublishedPage = (pageId: string, bustCache = false) => ({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_INIT,
payload: {
pageId,
bustCache,
},
});
export const fetchPageSuccess = (
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<undefined> => {
return {
type: ReduxActionTypes.FETCH_PAGE_SUCCESS,
postEvalActions,
payload: undefined,
};
};
export const fetchPublishedPageSuccess = (
postEvalActions: Array<ReduxAction<unknown> | ReduxActionWithoutPayload>,
): EvaluationReduxAction<undefined> => ({
type: ReduxActionTypes.FETCH_PUBLISHED_PAGE_SUCCESS,
postEvalActions,
payload: undefined,
});
export const updateCurrentPage = (id: string) => ({
type: ReduxActionTypes.SWITCH_CURRENT_PAGE_ID,
payload: { id },
});
export const initCanvasLayout = (
payload: UpdateCanvasPayload,
): ReduxAction<UpdateCanvasPayload> => {
return {
type: ReduxActionTypes.INIT_CANVAS_LAYOUT,
payload,
};
};
export const setLastUpdatedTime = (payload: number): ReduxAction<number> => ({
type: ReduxActionTypes.SET_LAST_UPDATED_TIME,
payload,
});
export const savePageSuccess = (payload: SavePageResponse) => {
return {
type: ReduxActionTypes.SAVE_PAGE_SUCCESS,
payload,
};
};
export const updateWidgetNameSuccess = () => {
return {
type: ReduxActionTypes.UPDATE_WIDGET_NAME_SUCCESS,
};
};
export const deletePageSuccess = () => {
return {
type: ReduxActionTypes.DELETE_PAGE_SUCCESS,
};
};
export const updateAndSaveLayout = (
widgets: CanvasWidgetsReduxState,
isRetry?: boolean,
shouldReplay?: boolean,
) => {
return {
type: ReduxActionTypes.UPDATE_LAYOUT,
payload: { widgets, isRetry, shouldReplay },
};
};
export const saveLayout = (isRetry?: boolean) => {
return {
type: ReduxActionTypes.SAVE_PAGE_INIT,
payload: { isRetry },
};
};
export const createPage = (
applicationId: string,
pageName: string,
layouts: Partial<PageLayout>[],
blockNavigation?: boolean,
) => {
AnalyticsUtil.logEvent("CREATE_PAGE", {
pageName,
});
return {
type: ReduxActionTypes.CREATE_PAGE_INIT,
payload: {
applicationId,
name: pageName,
layouts,
blockNavigation,
},
};
};
/**
* action to clone page
*
* @param pageId
* @param blockNavigation
* @returns
*/
export const clonePageInit = (pageId: string, blockNavigation?: boolean) => {
return {
type: ReduxActionTypes.CLONE_PAGE_INIT,
payload: {
id: pageId,
blockNavigation,
},
};
};
export const clonePageSuccess = (
pageId: string,
pageName: string,
layoutId: string,
) => {
return {
type: ReduxActionTypes.CLONE_PAGE_SUCCESS,
payload: {
pageId,
pageName,
layoutId,
},
};
};
export const updatePage = (id: string, name: string, isHidden: boolean) => {
return {
type: ReduxActionTypes.UPDATE_PAGE_INIT,
payload: {
id,
name,
isHidden,
},
};
};
export type WidgetAddChild = {
widgetId: string;
widgetName?: string;
type: WidgetType;
leftColumn: number;
topRow: number;
columns: number;
rows: number;
parentRowSpace: number;
parentColumnSpace: number;
newWidgetId: string;
tabId: string;
props?: Record<string, any>;
};
export type WidgetRemoveChild = {
widgetId: string;
childWidgetId: string;
};
export type WidgetDelete = {
widgetId?: string;
parentId?: string;
disallowUndo?: boolean;
isShortcut?: boolean;
};
export type MultipleWidgetDeletePayload = {
widgetIds: string[];
disallowUndo?: boolean;
isShortcut?: boolean;
};
export type WidgetResize = {
widgetId: string;
parentId: string;
leftColumn: number;
rightColumn: number;
topRow: number;
bottomRow: number;
snapColumnSpace: number;
snapRowSpace: number;
};
export type ModalWidgetResize = {
height: number;
width: number;
widgetId: string;
canvasWidgetId: string;
};
export type WidgetAddChildren = {
widgetId: string;
children: Array<{
type: WidgetType;
widgetId: string;
parentId: string;
parentRowSpace: number;
parentColumnSpace: number;
leftColumn: number;
rightColumn: number;
topRow: number;
bottomRow: number;
isLoading: boolean;
}>;
};
export type WidgetUpdateProperty = {
widgetId: string;
propertyPath: string;
propertyValue: any;
};
export const updateWidget = (
operation: WidgetOperation,
widgetId: string,
payload: any,
): ReduxAction<
| WidgetAddChild
| WidgetResize
| WidgetDelete
| WidgetAddChildren
| WidgetUpdateProperty
> => {
return {
type: WidgetReduxActionTypes["WIDGET_" + operation],
payload: { widgetId, ...payload },
};
};
export const setUrlData = (
payload: UrlDataState,
): ReduxAction<UrlDataState> => {
return {
type: ReduxActionTypes.SET_URL_DATA,
payload,
};
};
export const setAppMode = (payload: APP_MODE): ReduxAction<APP_MODE> => {
return {
type: ReduxActionTypes.SET_APP_MODE,
payload,
};
};
export const updateAppTransientStore = (
payload: Record<string, unknown>,
): ReduxAction<Record<string, unknown>> => ({
type: ReduxActionTypes.UPDATE_APP_TRANSIENT_STORE,
payload,
});
export const updateAppPersistentStore = (
payload: Record<string, unknown>,
): ReduxAction<Record<string, unknown>> => {
return {
type: ReduxActionTypes.UPDATE_APP_PERSISTENT_STORE,
payload,
};
};
export interface ReduxActionWithExtraParams<T> extends ReduxAction<T> {
extraParams: Record<any, any>;
}
export type GenerateCRUDSuccess = {
page: {
layouts: Array<any>;
id: string;
name: string;
isDefault?: boolean;
};
isNewPage: boolean;
};
export const generateTemplateSuccess = (payload: GenerateCRUDSuccess) => {
return {
type: ReduxActionTypes.GENERATE_TEMPLATE_PAGE_SUCCESS,
payload,
};
};
export const generateTemplateError = () => {
return {
type: ReduxActionErrorTypes.GENERATE_TEMPLATE_PAGE_ERROR,
};
};
export const generateTemplateToUpdatePage = ({
applicationId,
columns,
datasourceId,
mode,
pageId,
pluginSpecificParams,
searchColumn,
tableName,
}: GenerateTemplatePageRequest): ReduxActionWithExtraParams<GenerateTemplatePageRequest> => {
return {
type: ReduxActionTypes.GENERATE_TEMPLATE_PAGE_INIT,
payload: {
pageId,
tableName,
datasourceId,
applicationId,
columns,
searchColumn,
pluginSpecificParams,
},
extraParams: {
mode,
},
};
};
export function updateReplayEntity(
entityId: string,
entity: Replayable,
entityType: ENTITY_TYPE,
) {
return {
type: ReduxActionTypes.UPDATE_REPLAY_ENTITY,
payload: { entityId, entity, entityType },
};
}
export function undoAction() {
return {
type: ReduxActionTypes.UNDO_REDO_OPERATION,
payload: {
operation: ReplayReduxActionTypes.UNDO,
},
};
}
export function redoAction() {
return {
type: ReduxActionTypes.UNDO_REDO_OPERATION,
payload: {
operation: ReplayReduxActionTypes.REDO,
},
};
}
/**
* action for delete page
*
* @param pageId
* @param pageName
* @returns
*/
export const deletePage = (pageId: string) => {
return {
type: ReduxActionTypes.DELETE_PAGE_INIT,
payload: {
id: pageId,
},
};
};
/**
* action for set page as default
*
* @param pageId
* @param applicationId
* @returns
*/
export const setPageAsDefault = (pageId: string, applicationId?: string) => {
return {
type: ReduxActionTypes.SET_DEFAULT_APPLICATION_PAGE_INIT,
payload: {
id: pageId,
applicationId,
},
};
};
/**
* action for updating order of a page
*
* @param pageId
* @param applicationId
* @returns
*/
export const setPageOrder = (
applicationId: string,
pageId: string,
order: number,
) => {
return {
type: ReduxActionTypes.SET_PAGE_ORDER_INIT,
payload: {
pageId: pageId,
order: order,
applicationId,
},
};
};