## Description This change is a refactor of widget selection logic. It consolidates all the business logic to make it easy to maintain. It also improves the performance a bit. It touched a lot of features as we heavily rely on this ``` Select one Select multiple with drag Select multiple with shift Select multiple with cmd/ctrl Selections should be on the same level of hierarchy Unselect all by clicking on the canvas Unselect all by pressing esc Select all with cmd + a Paste in main container Paste in another container Undo Redo Modal Selection Modal child selection Context switching cmd click snipping mode new widget suggestion onboarding ``` > Refactor widget selection logic Fixes #19570 ## Type of change - Refactor ## How Has This Been Tested? All existing tests should pass ### Test Plan > Add Testsmith test cases links that relate to this PR ### Issues raised during DP testing https://github.com/appsmithorg/appsmith/pull/19643#issuecomment-1383570810 https://github.com/appsmithorg/appsmith/pull/19643#issuecomment-1383607820 https://github.com/appsmithorg/appsmith/pull/19643#issuecomment-1385095478 [Bug bash issues](https://www.notion.so/appsmith/610aa302f3e146a7b090b7dc6bc63ef9?v=0d277a9b07bf4aac9d717bcaf138c33a) ## Checklist: ### Dev activity - [ ] My code follows the style guidelines of this project - [ ] I have performed a self-review of my own code - [ ] I have commented my code, particularly in hard-to-understand areas - [ ] I have made corresponding changes to the documentation - [ ] My changes generate no new warnings - [ ] I have added tests that prove my fix is effective or that my feature works - [ ] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag ### QA activity: - [ ] Test plan has been approved by relevant developers - [ ] Test plan has been peer reviewed by QA - [ ] Cypress test cases have been added and approved by either SDET or manual QA - [ ] Organized project review call with relevant stakeholders after Round 1/2 of QA - [ ] Added Test Plan Approved label after reveiwing all Cypress test
104 lines
3.1 KiB
TypeScript
104 lines
3.1 KiB
TypeScript
import { AppState } from "@appsmith/reducers";
|
|
import equal from "fast-deep-equal/es6";
|
|
import React, { ReactNode, useCallback } from "react";
|
|
import { useSelector } from "react-redux";
|
|
import { getIsPropertyPaneVisible } from "selectors/propertyPaneSelectors";
|
|
import {
|
|
getFocusedParentToOpen,
|
|
isWidgetSelected,
|
|
shouldWidgetIgnoreClicksSelector,
|
|
} from "selectors/widgetSelectors";
|
|
import { stopEventPropagation } from "utils/AppsmithUtils";
|
|
import { useWidgetSelection } from "./useWidgetSelection";
|
|
import { SelectionRequestType } from "sagas/WidgetSelectUtils";
|
|
|
|
export function ClickContentToOpenPropPane({
|
|
children,
|
|
widgetId,
|
|
}: {
|
|
widgetId: string;
|
|
children?: ReactNode;
|
|
}) {
|
|
const { focusWidget } = useWidgetSelection();
|
|
|
|
const clickToSelectWidget = useClickToSelectWidget(widgetId);
|
|
|
|
const focusedWidget = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.focusedWidget,
|
|
);
|
|
|
|
const isResizing = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.isResizing,
|
|
);
|
|
const isDragging = useSelector(
|
|
(state: AppState) => state.ui.widgetDragResize.isDragging,
|
|
);
|
|
const isResizingOrDragging = !!isResizing || !!isDragging;
|
|
const handleMouseOver = (e: any) => {
|
|
focusWidget &&
|
|
!isResizingOrDragging &&
|
|
focusedWidget !== widgetId &&
|
|
focusWidget(widgetId);
|
|
e.stopPropagation();
|
|
};
|
|
|
|
const styles = {
|
|
width: "100%",
|
|
height: "100%",
|
|
};
|
|
|
|
return (
|
|
<div
|
|
onClick={stopEventPropagation}
|
|
onMouseDownCapture={clickToSelectWidget}
|
|
onMouseOver={handleMouseOver}
|
|
style={styles}
|
|
>
|
|
{children}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
export const useClickToSelectWidget = (widgetId: string) => {
|
|
const { focusWidget, selectWidget } = useWidgetSelection();
|
|
const isPropPaneVisible = useSelector(getIsPropertyPaneVisible);
|
|
const isSelected = useSelector(isWidgetSelected(widgetId));
|
|
const parentWidgetToOpen = useSelector(getFocusedParentToOpen, equal);
|
|
const shouldIgnoreClicks = useSelector(
|
|
shouldWidgetIgnoreClicksSelector(widgetId),
|
|
);
|
|
|
|
const clickToSelectWidget = useCallback(
|
|
(e: any) => {
|
|
// Ignore click captures
|
|
// 1. If the component is resizing or dragging because it is handled internally in draggable component.
|
|
// 2. If table filter property pane is open.
|
|
if (shouldIgnoreClicks) return;
|
|
if ((!isPropPaneVisible && isSelected) || !isSelected) {
|
|
let type: SelectionRequestType = SelectionRequestType.One;
|
|
if (e.metaKey || e.ctrlKey) {
|
|
type = SelectionRequestType.PushPop;
|
|
} else if (e.shiftKey) {
|
|
type = SelectionRequestType.ShiftSelect;
|
|
}
|
|
|
|
if (parentWidgetToOpen) {
|
|
selectWidget(type, [parentWidgetToOpen.widgetId]);
|
|
} else {
|
|
selectWidget(type, [widgetId]);
|
|
focusWidget(widgetId);
|
|
}
|
|
|
|
if (
|
|
type === SelectionRequestType.PushPop ||
|
|
type === SelectionRequestType.ShiftSelect
|
|
) {
|
|
e.stopPropagation();
|
|
}
|
|
}
|
|
},
|
|
[shouldIgnoreClicks, isPropPaneVisible, isSelected, parentWidgetToOpen],
|
|
);
|
|
return clickToSelectWidget;
|
|
};
|