## Description Expand auto layout implementation to handle auto height use cases. Use cases handled in this PR: 1. Change canvas and container-like widget height on adding / removing widgets. 2. Container height update on content change of individual props, e.g. text, checkbox groups. 3. Tabs widget use cases - change height on tab change, shouldShowProps update. 4. Correct modal widget height. 5. List widget updates - disable auto height, enable manual resizing of item container. 6. Fix resize loop. Fixes https://github.com/appsmithorg/appsmith/issues/21977 Fixes https://github.com/appsmithorg/appsmith/issues/22093 Fixes https://github.com/appsmithorg/appsmith/issues/21837 Fixes https://github.com/appsmithorg/appsmith/issues/22183 Fixes https://github.com/appsmithorg/appsmith/issues/21758 Fixes https://github.com/appsmithorg/appsmith/issues/21870 Fixes https://github.com/appsmithorg/appsmith/issues/22086 Fixes https://github.com/appsmithorg/appsmith/issues/22539 Fixes https://github.com/appsmithorg/appsmith/issues/22329 Fixes #22588 ## 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) ## How Has This Been Tested? - Manual - Jest - Cypress ## Checklist: ### Dev activity - [x] My code follows the style guidelines of this project - [x] I have performed a self-review of my own code - [x] 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 - [x] New and existing unit tests pass locally with my changes - [ ] PR is being merged under a feature flag --------- Co-authored-by: Aswath K <aswath@appsmith.com> Co-authored-by: rahulramesha <rahul@appsmith.com> Co-authored-by: Aswath K <aswath.sana@gmail.com> Co-authored-by: Ashok Kumar M <35134347+marks0351@users.noreply.github.com>
236 lines
7.5 KiB
TypeScript
236 lines
7.5 KiB
TypeScript
import {
|
|
LayoutDirection,
|
|
Positioning,
|
|
ResponsiveBehavior,
|
|
} from "utils/autoLayout/constants";
|
|
import FlexBoxComponent from "components/designSystems/appsmith/autoLayout/FlexBoxComponent";
|
|
import DropTargetComponent from "components/editorComponents/DropTargetComponent";
|
|
import { CANVAS_DEFAULT_MIN_HEIGHT_PX } from "constants/AppConstants";
|
|
import { FILL_WIDGET_MIN_WIDTH } from "constants/minWidthConstants";
|
|
import { GridDefaults, RenderModes } from "constants/WidgetConstants";
|
|
import { CanvasDraggingArena } from "pages/common/CanvasArenas/CanvasDraggingArena";
|
|
import { CanvasSelectionArena } from "pages/common/CanvasArenas/CanvasSelectionArena";
|
|
import WidgetsMultiSelectBox from "pages/Editor/WidgetsMultiSelectBox";
|
|
import type { CSSProperties } from "react";
|
|
import React from "react";
|
|
import { getCanvasClassName } from "utils/generators";
|
|
import type { DerivedPropertiesMap } from "utils/WidgetFactory";
|
|
import WidgetFactory from "utils/WidgetFactory";
|
|
import { getCanvasSnapRows } from "utils/WidgetPropsUtils";
|
|
import type { WidgetProps } from "widgets/BaseWidget";
|
|
import type { ContainerWidgetProps } from "widgets/ContainerWidget/widget";
|
|
import ContainerWidget from "widgets/ContainerWidget/widget";
|
|
import type { CanvasWidgetStructure, DSLWidget } from "./constants";
|
|
import ContainerComponent from "./ContainerWidget/component";
|
|
import { AppPositioningTypes } from "reducers/entityReducers/pageListReducer";
|
|
|
|
class CanvasWidget extends ContainerWidget {
|
|
static getPropertyPaneConfig() {
|
|
return [];
|
|
}
|
|
static getWidgetType() {
|
|
return "CANVAS_WIDGET";
|
|
}
|
|
|
|
getCanvasProps(): DSLWidget & { minHeight: number } {
|
|
return {
|
|
...this.props,
|
|
parentRowSpace: 1,
|
|
parentColumnSpace: 1,
|
|
topRow: 0,
|
|
leftColumn: 0,
|
|
containerStyle: "none",
|
|
detachFromLayout: true,
|
|
minHeight: this.props.minHeight || CANVAS_DEFAULT_MIN_HEIGHT_PX,
|
|
shouldScrollContents: false,
|
|
};
|
|
}
|
|
|
|
renderAsDropTarget() {
|
|
const canvasProps = this.getCanvasProps();
|
|
const { snapColumnSpace } = this.getSnapSpaces();
|
|
return (
|
|
<DropTargetComponent
|
|
bottomRow={this.props.bottomRow}
|
|
isMobile={this.props.isMobile}
|
|
minHeight={this.props.minHeight || CANVAS_DEFAULT_MIN_HEIGHT_PX}
|
|
mobileBottomRow={this.props.mobileBottomRow}
|
|
noPad={this.props.noPad}
|
|
parentId={this.props.parentId}
|
|
snapColumnSpace={snapColumnSpace}
|
|
useAutoLayout={this.props.useAutoLayout}
|
|
widgetId={this.props.widgetId}
|
|
>
|
|
{this.renderAsContainerComponent(canvasProps)}
|
|
</DropTargetComponent>
|
|
);
|
|
}
|
|
|
|
renderChildWidget(childWidgetData: CanvasWidgetStructure): React.ReactNode {
|
|
if (!childWidgetData) return null;
|
|
|
|
const childWidget = { ...childWidgetData };
|
|
|
|
const snapSpaces = this.getSnapSpaces();
|
|
childWidget.parentColumnSpace = snapSpaces.snapColumnSpace;
|
|
childWidget.parentRowSpace = snapSpaces.snapRowSpace;
|
|
if (this.props.noPad) childWidget.noContainerOffset = true;
|
|
childWidget.parentId = this.props.widgetId;
|
|
// Pass layout controls to children
|
|
childWidget.positioning =
|
|
childWidget?.positioning || this.props.positioning;
|
|
childWidget.isFlexChild = this.props.useAutoLayout;
|
|
childWidget.direction = this.getDirection();
|
|
|
|
return WidgetFactory.createWidget(childWidget, this.props.renderMode);
|
|
}
|
|
|
|
renderAsContainerComponent(
|
|
props: ContainerWidgetProps<WidgetProps>,
|
|
): JSX.Element {
|
|
const direction = this.getDirection();
|
|
const snapRows = getCanvasSnapRows(
|
|
this.props.bottomRow,
|
|
this.props.mobileBottomRow,
|
|
this.props.isMobile,
|
|
this.props.appPositioningType === AppPositioningTypes.AUTO,
|
|
);
|
|
return (
|
|
<ContainerComponent {...props}>
|
|
{props.renderMode === RenderModes.CANVAS && (
|
|
<>
|
|
<CanvasDraggingArena
|
|
{...this.getSnapSpaces()}
|
|
alignItems={props.alignItems}
|
|
canExtend={props.canExtend}
|
|
direction={direction}
|
|
dropDisabled={!!props.dropDisabled}
|
|
noPad={this.props.noPad}
|
|
parentId={props.parentId}
|
|
snapRows={snapRows}
|
|
useAutoLayout={this.props.useAutoLayout}
|
|
widgetId={props.widgetId}
|
|
widgetName={props.widgetName}
|
|
/>
|
|
<CanvasSelectionArena
|
|
{...this.getSnapSpaces()}
|
|
canExtend={props.canExtend}
|
|
dropDisabled={!!props.dropDisabled}
|
|
parentId={props.parentId}
|
|
snapRows={snapRows}
|
|
widgetId={props.widgetId}
|
|
/>
|
|
</>
|
|
)}
|
|
{this.props.useAutoLayout
|
|
? this.renderFlexCanvas(direction)
|
|
: this.renderFixedCanvas(props)}
|
|
</ContainerComponent>
|
|
);
|
|
}
|
|
|
|
renderFlexCanvas(direction: LayoutDirection) {
|
|
const stretchFlexBox = !this.props.children || !this.props.children?.length;
|
|
return (
|
|
<FlexBoxComponent
|
|
direction={direction}
|
|
flexLayers={this.props.flexLayers || []}
|
|
isMobile={this.props.isMobile || false}
|
|
stretchHeight={stretchFlexBox}
|
|
useAutoLayout={this.props.useAutoLayout || false}
|
|
widgetId={this.props.widgetId}
|
|
>
|
|
{this.renderChildren()}
|
|
</FlexBoxComponent>
|
|
);
|
|
}
|
|
|
|
renderFixedCanvas(props: ContainerWidgetProps<WidgetProps>) {
|
|
return (
|
|
<>
|
|
<WidgetsMultiSelectBox
|
|
{...this.getSnapSpaces()}
|
|
noContainerOffset={!!props.noContainerOffset}
|
|
widgetId={this.props.widgetId}
|
|
widgetType={this.props.type}
|
|
/>
|
|
{this.renderChildren()}
|
|
</>
|
|
);
|
|
}
|
|
|
|
getDirection(): LayoutDirection {
|
|
return this.props.positioning === Positioning.Vertical
|
|
? LayoutDirection.Vertical
|
|
: LayoutDirection.Horizontal;
|
|
}
|
|
|
|
getPageView() {
|
|
let height = 0;
|
|
const snapRows = getCanvasSnapRows(
|
|
this.props.bottomRow,
|
|
this.props.mobileBottomRow,
|
|
this.props.isMobile,
|
|
this.props.appPositioningType === AppPositioningTypes.AUTO,
|
|
);
|
|
height = snapRows * GridDefaults.DEFAULT_GRID_ROW_HEIGHT;
|
|
const style: CSSProperties = {
|
|
width: "100%",
|
|
height: this.props.isListWidgetCanvas ? "auto" : `${height}px`,
|
|
background: "none",
|
|
position: "relative",
|
|
};
|
|
// This div is the DropTargetComponent alternative for the page view
|
|
// DropTargetComponent and this div are responsible for the canvas height
|
|
return (
|
|
<div className={getCanvasClassName()} style={style}>
|
|
{this.renderAsContainerComponent(this.getCanvasProps())}
|
|
</div>
|
|
);
|
|
}
|
|
|
|
getCanvasView() {
|
|
if (!this.props.dropDisabled) {
|
|
return this.renderAsDropTarget();
|
|
}
|
|
return this.getPageView();
|
|
}
|
|
|
|
static getDerivedPropertiesMap(): DerivedPropertiesMap {
|
|
return {};
|
|
}
|
|
|
|
static getDefaultPropertiesMap(): Record<string, string> {
|
|
return {};
|
|
}
|
|
// TODO Find a way to enforce this, (dont let it be set)
|
|
static getMetaPropertiesMap(): Record<string, any> {
|
|
return {};
|
|
}
|
|
}
|
|
|
|
export const CONFIG = {
|
|
type: CanvasWidget.getWidgetType(),
|
|
name: "Canvas",
|
|
hideCard: true,
|
|
eagerRender: true,
|
|
defaults: {
|
|
rows: 0,
|
|
columns: 0,
|
|
widgetName: "Canvas",
|
|
version: 1,
|
|
detachFromLayout: true,
|
|
flexLayers: [],
|
|
responsiveBehavior: ResponsiveBehavior.Fill,
|
|
minWidth: FILL_WIDGET_MIN_WIDTH,
|
|
},
|
|
properties: {
|
|
derived: CanvasWidget.getDerivedPropertiesMap(),
|
|
default: CanvasWidget.getDefaultPropertiesMap(),
|
|
meta: CanvasWidget.getMetaPropertiesMap(),
|
|
config: CanvasWidget.getPropertyPaneConfig(),
|
|
},
|
|
};
|
|
|
|
export default CanvasWidget;
|