PromucFlow_constructor/app/client/src/resizable/common.tsx

269 lines
7.2 KiB
TypeScript
Raw Normal View History

import type { OccupiedSpace } from "constants/CanvasEditorConstants";
import { Colors } from "constants/Colors";
import type { DefaultDimensionMap } from "constants/WidgetConstants";
import React from "react";
import type { CSSProperties, ReactNode } from "react";
import { animated } from "react-spring";
import { useDrag } from "react-use-gesture";
import type { GridProps, ReflowDirection } from "reflow/reflowTypes";
import type { StyledComponent } from "styled-components";
import styled from "styled-components";
import type {
LayoutDirection,
ResponsiveBehavior,
} from "utils/autoLayout/constants";
import { getNearestParentCanvas } from "utils/generators";
import memoize from "micro-memoize";
const resizeBorderPadding = 1;
const resizeBorder = 1;
const resizeBoxShadow = 1;
const resizeOutline = 1;
export const RESIZE_BORDER_BUFFER =
resizeBorderPadding + resizeBorder + resizeBoxShadow + resizeOutline;
export const ResizeWrapper = styled(animated.div)``;
export const getWrapperStyle = memoize(
(
inverted: boolean,
showBoundaries: boolean,
allowResize: boolean,
isHovered: boolean,
): CSSProperties => {
return {
borderRadius: allowResize
? inverted
? "4px 4px 0px 4px"
: "4px 0px 4px 4px"
: "4px",
border: `${resizeBorder}px solid`,
padding: `${resizeBorderPadding}px`,
borderColor: `${showBoundaries ? Colors.GREY_1 : "transparent"}`,
boxShadow: `${
showBoundaries
? "0px 0px 0px " +
resizeBoxShadow +
"px " +
(isHovered ? Colors.WATUSI : "#f86a2b")
: "none"
}`,
};
},
);
// export const ResizeWrapper = (props: {
// $prevents: boolean;
// isHovered: boolean;
// showBoundaries: boolean;
// inverted?: boolean;
// children: ReactNode;
// className?: string;
// id?: string;
// ref?: any;
// style?: any;
// }) => {
// const wrapperStyle: CSSProperties = useMemo(getWrapperStyle, [
// props.inverted,
// props.showBoundaries,
// props.isHovered,
// props.style,
// ]);
// return (
// <ResizeWrapperBox $prevents={props.$prevents} style={wrapperStyle}>
// {props.children}
// </ResizeWrapperBox>
// );
// };
// export const ResizeWrapper = styled(animated.div)<{
// $prevents: boolean;
// isHovered: boolean;
// showBoundaries: boolean;
// inverted?: boolean;
// }>`
// display: block;
// & {
// * {
// pointer-events: ${(props) => !props.$prevents && "none"};
// }
// }
// border-radius: 4px 0px 4px 4px;
// ${(props) => {
// if (props.inverted) {
// return `border-radius: 4px 4px 4px 0px;`;
// } else {
// return `border-radius: 0px 4px 4px 4px;`;
// }
// }}
// border: ${resizeBorder}px solid;
// padding: ${resizeBorderPadding}px;
// outline: ${resizeOutline}px solid !important;
// outline-offset: 1px;
// ${(props) => {
// if (props.showBoundaries) {
// return `
// box-shadow: 0px 0px 0px ${resizeBoxShadow}px ${
// props.isHovered ? Colors.WATUSI : "#f86a2b"
// };
// outline-color: ${Colors.GREY_1} !important;
// border-color: ${Colors.GREY_1};
// `;
// } else {
// return `
// outline-color: transparent !important;
// border-color: transparent;
// `;
// }
// }}}
// `;
const getSnappedValues = (
x: number,
y: number,
snapGrid: { x: number; y: number },
) => {
return {
x: Math.round(x / snapGrid.x) * snapGrid.x,
y: Math.round(y / snapGrid.y) * snapGrid.y,
};
};
export type DimensionUpdateProps = {
width: number;
height: number;
x: number;
y: number;
reset?: boolean;
direction: ReflowDirection;
X?: number;
Y?: number;
reflectPosition: boolean;
reflectDimension: boolean;
};
type ResizableHandleProps = {
allowResize: boolean;
scrollParent: HTMLDivElement | null;
disableDot: boolean;
isHovered: boolean;
checkForCollision: (widgetNewSize: {
left: number;
top: number;
bottom: number;
right: number;
}) => boolean;
dragCallback: (x: number, y: number) => void;
component: StyledComponent<"div", Record<string, unknown>>;
onStart: () => void;
onStop: () => void;
snapGrid: {
x: number;
y: number;
};
direction?: ReflowDirection;
};
export function ResizableHandle(props: ResizableHandleProps) {
const bind = useDrag((state) => {
const {
first,
last,
dragging,
memo,
movement: [mx, my],
} = state;
if (!props.allowResize || props.disableDot) {
return;
}
const scrollParent = getNearestParentCanvas(props.scrollParent);
const initialScrollTop = memo ? memo.scrollTop : 0;
const currentScrollTop = scrollParent?.scrollTop || 0;
const deltaScrolledHeight = currentScrollTop - initialScrollTop;
const deltaY = my + deltaScrolledHeight;
const snapped = getSnappedValues(mx, deltaY, props.snapGrid);
if (first) {
props.onStart();
return { scrollTop: currentScrollTop, snapped };
}
const { snapped: snappedMemo } = memo;
if (
dragging &&
snappedMemo &&
(snapped.x !== snappedMemo.x || snapped.y !== snappedMemo.y)
) {
props.dragCallback(snapped.x, snapped.y);
}
if (last) {
props.onStop();
}
return { ...memo, snapped };
});
const propsToPass = {
...bind(),
showAsBorder: !props.allowResize,
disableDot: props.disableDot,
isHovered: props.isHovered,
};
return (
<props.component
feat: [epic] appsmith design system version 2 deduplication (#22030) ## Description ### Fixes - [x] https://github.com/appsmithorg/appsmith/issues/19383 - [x] https://github.com/appsmithorg/appsmith/issues/19384 - [x] https://github.com/appsmithorg/appsmith/issues/19385 - [x] https://github.com/appsmithorg/appsmith/issues/19386 - [x] https://github.com/appsmithorg/appsmith/issues/19387 - [x] https://github.com/appsmithorg/appsmith/issues/19388 - [x] https://github.com/appsmithorg/appsmith/issues/19389 - [x] https://github.com/appsmithorg/appsmith/issues/19390 - [x] https://github.com/appsmithorg/appsmith/issues/19391 - [x] https://github.com/appsmithorg/appsmith/issues/19392 - [x] https://github.com/appsmithorg/appsmith/issues/19393 - [x] https://github.com/appsmithorg/appsmith/issues/19394 - [x] https://github.com/appsmithorg/appsmith/issues/19395 - [x] https://github.com/appsmithorg/appsmith/issues/19396 - [x] https://github.com/appsmithorg/appsmith/issues/19397 - [x] https://github.com/appsmithorg/appsmith/issues/19398 - [x] https://github.com/appsmithorg/appsmith/issues/19399 - [x] https://github.com/appsmithorg/appsmith/issues/19400 - [x] https://github.com/appsmithorg/appsmith/issues/19401 - [x] https://github.com/appsmithorg/appsmith/issues/19402 - [x] https://github.com/appsmithorg/appsmith/issues/19403 - [x] https://github.com/appsmithorg/appsmith/issues/19404 - [x] https://github.com/appsmithorg/appsmith/issues/19405 - [x] https://github.com/appsmithorg/appsmith/issues/19406 - [x] https://github.com/appsmithorg/appsmith/issues/19407 - [x] https://github.com/appsmithorg/appsmith/issues/19408 - [x] https://github.com/appsmithorg/appsmith/issues/19409 Fixes # (issue) > if no issue exists, please create an issue and ask the maintainers about this first Media > A video or a GIF is preferred. when using Loom, don’t embed because it looks like it’s a GIF. instead, just link to the video ## Type of change > Please delete options that are not relevant. - Bug fix (non-breaking change which fixes an issue) - New feature (non-breaking change which adds functionality) - Breaking change (fix or feature that would cause existing functionality to not work as expected) - Chore (housekeeping or task changes that don't impact user perception) - This change requires a documentation update ## How Has This Been Tested? > Please describe the tests that you ran to verify your changes. Provide instructions, so we can reproduce. > Please also list any relevant details for your test configuration. > Delete anything that is not important - Manual - Jest - Cypress ### Test Plan > Add Testsmith test cases links that relate to this PR ### Issues raised during DP testing > Link issues raised during DP testing for better visiblity and tracking (copy link from comments dropped on this PR) ## 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 --------- Co-authored-by: Ankita Kinger <ankita@appsmith.com> Co-authored-by: akash-codemonk <67054171+akash-codemonk@users.noreply.github.com> Co-authored-by: Tanvi Bhakta <tanvi@appsmith.com> Co-authored-by: Arsalan <arsalanyaldram0211@outlook.com> Co-authored-by: Aman Agarwal <aman@appsmith.com> Co-authored-by: Rohit Agarwal <rohit_agarwal@live.in> Co-authored-by: Nilesh Sarupriya <nilesh@appsmith.com> Co-authored-by: Nilesh Sarupriya <20905988+nsarupr@users.noreply.github.com> Co-authored-by: Tanvi Bhakta <tanvibhakta@gmail.com> Co-authored-by: Aishwarya UR <aishwarya@appsmith.com> Co-authored-by: Parthvi Goswami <parthvigoswami@Parthvis-MacBook-Pro.local> Co-authored-by: Vijetha-Kaja <vijetha@appsmith.com> Co-authored-by: Parthvi <80334441+Parthvi12@users.noreply.github.com> Co-authored-by: Apple <nandan@thinkify.io> Co-authored-by: Saroj <43822041+sarojsarab@users.noreply.github.com> Co-authored-by: Sangeeth Sivan <74818788+berzerkeer@users.noreply.github.com> Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com> Co-authored-by: Aishwarya-U-R <91450662+Aishwarya-U-R@users.noreply.github.com> Co-authored-by: rahulramesha <rahul@appsmith.com> Co-authored-by: Aswath K <aswath.sana@gmail.com> Co-authored-by: Preet Sidhu <preetsidhu.bits@gmail.com> Co-authored-by: Vijetha-Kaja <119562824+Vijetha-Kaja@users.noreply.github.com> Co-authored-by: Shrikant Sharat Kandula <shrikant@appsmith.com>
2023-05-19 18:37:06 +00:00
data-testid={`t--resizable-handle-${props.direction}`}
{...propsToPass}
/>
);
}
export type ResizableProps = {
allowResize: boolean;
handles: {
left?: StyledComponent<"div", Record<string, unknown>>;
top?: StyledComponent<"div", Record<string, unknown>>;
bottom?: StyledComponent<"div", Record<string, unknown>>;
right?: StyledComponent<"div", Record<string, unknown>>;
bottomRight?: StyledComponent<"div", Record<string, unknown>>;
topLeft?: StyledComponent<"div", Record<string, unknown>>;
topRight?: StyledComponent<"div", Record<string, unknown>>;
bottomLeft?: StyledComponent<"div", Record<string, unknown>>;
};
componentWidth: number;
componentHeight: number;
children: ReactNode;
updateBottomRow: (bottomRow: number) => void;
getResizedPositions: (resizedPositions: OccupiedSpace) => {
canResizeHorizontally: boolean;
canResizeVertically: boolean;
};
fix: resizable max height (#23143) ## Description #### Reported issue In a container widget, when we switch to auto height with limits (in the property pane), then switch back to FIXED (in the property pane), we are no longer able to vertically resize the container widget to be larger. #### Results of RCA This was happening because, we set a `maxDynamicHeight` when we switch to `auto height with limits`. When we switched back to `fixed`, the same limits were being respected by the resizing library. This resulted in the height being capped at what was set in the `maxDynamicHeight`. #### Code design issue: There was also an issue where the resizing library was "aware" of the "dynamic height". Dynamic height is a platform construct and the resizing library being aware of this construct is a breach in the interface. #### Solution: We now pass the `maxHeightInPx` and `autoHeight` to the resizing library. `maxHeightInPx` is the maximum height -- the resizing library can -- allow in vertical resize. `autoHeight` ignores the resizing library's ability to set the height (in this scenario we handle the height using the dynamic height feature in the platform) This allows us to keep the resizing library agnostic of the platform dynamic height feature and lets the platform code decide the max height possible. #### PR fixes following issue(s) Fixes #22439 #### Media #### Type of change - Bug fix (non-breaking change which fixes an issue) ## Testing #### How Has This Been Tested? - [x] Manual - [ ] Jest - [ ] Cypress - Limitations in the testing infra makes it difficult to add a test for this #### Test Plan #### Issues raised during DP testing ## 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: - [ ] [Speedbreak features](https://github.com/appsmithorg/TestSmith/wiki/Test-plan-implementation#speedbreaker-features-to-consider-for-every-change) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans/_edit#areas-of-interest) - [ ] Test plan has been peer reviewed by project stakeholders and other QA members - [ ] Manually tested functionality on DP - [ ] We had an implementation alignment call with stakeholders post QA Round 2 - [ ] Cypress test cases have been added and approved by SDET/manual QA - [ ] Added `Test Plan Approved` label after Cypress tests were reviewed - [ ] Added `Test Plan Approved` label after JUnit tests were reviewed
2023-05-22 07:59:29 +00:00
maxHeightInPx: number; // Maximum height in pixels, the child can have.
autoHeight: boolean; // true if we don't want a pixel height specified for the child
originalPositions: OccupiedSpace;
onStart: (affectsWidth?: boolean) => void;
onStop: (
size: { width: number; height: number },
position: { x: number; y: number },
dimensionMap?: typeof DefaultDimensionMap,
) => void;
snapGrid: { x: number; y: number };
enableVerticalResize: boolean;
enableHorizontalResize: boolean;
className?: string;
parentId?: string;
widgetId: string;
gridProps: GridProps;
zWidgetType?: string;
zWidgetId?: string;
isFlexChild?: boolean;
isHovered: boolean;
responsiveBehavior?: ResponsiveBehavior;
direction?: LayoutDirection;
paddingOffset: number;
isMobile: boolean;
showResizeBoundary: boolean;
topRow: number;
};