import React from "react"; import * as Sentry from "@sentry/react"; import { map, sortBy, compact } from "lodash"; import { GridDefaults, CONTAINER_GRID_PADDING, WIDGET_PADDING, } from "constants/WidgetConstants"; import WidgetFactory from "utils/WidgetFactory"; import ContainerComponent, { ContainerStyle, } from "components/designSystems/appsmith/ContainerComponent"; import { WidgetType, WidgetTypes } from "constants/WidgetConstants"; import BaseWidget, { WidgetProps, WidgetState } from "./BaseWidget"; import { VALIDATION_TYPES } from "constants/WidgetValidation"; class ContainerWidget extends BaseWidget< ContainerWidgetProps, WidgetState > { constructor(props: ContainerWidgetProps) { super(props); this.renderChildWidget = this.renderChildWidget.bind(this); } static getPropertyPaneConfig() { return [ { sectionName: "General", children: [ { helpText: "Use a html color name, HEX, RGB or RGBA value", placeholderText: "#FFFFFF / Gray / rgb(255, 99, 71)", propertyName: "backgroundColor", label: "Background Color", controlType: "INPUT_TEXT", isBindProperty: true, isTriggerProperty: false, validation: VALIDATION_TYPES.TEXT, }, { helpText: "Controls the visibility of the widget", propertyName: "isVisible", label: "Visible", controlType: "SWITCH", isJSConvertible: true, isBindProperty: true, isTriggerProperty: false, validation: VALIDATION_TYPES.BOOLEAN, }, { propertyName: "shouldScrollContents", label: "Scroll Contents", controlType: "SWITCH", isBindProperty: false, isTriggerProperty: false, }, ], }, ]; } getSnapSpaces = () => { const { componentWidth } = this.getComponentDimensions(); const padding = (CONTAINER_GRID_PADDING + WIDGET_PADDING) * 2; let width = componentWidth; if (!this.props.noPad) width -= padding; else width -= WIDGET_PADDING * 2; return { snapRowSpace: GridDefaults.DEFAULT_GRID_ROW_HEIGHT, snapColumnSpace: componentWidth ? width / GridDefaults.DEFAULT_GRID_COLUMNS : 0, }; }; renderChildWidget(childWidgetData: WidgetProps): React.ReactNode { // For now, isVisible prop defines whether to render a detached widget if (childWidgetData.detachFromLayout && !childWidgetData.isVisible) { return null; } const { componentWidth, componentHeight } = this.getComponentDimensions(); childWidgetData.rightColumn = componentWidth; childWidgetData.bottomRow = this.props.shouldScrollContents ? childWidgetData.bottomRow : componentHeight; childWidgetData.minHeight = componentHeight; childWidgetData.isVisible = this.props.isVisible; childWidgetData.shouldScrollContents = false; childWidgetData.canExtend = this.props.shouldScrollContents; childWidgetData.parentId = this.props.widgetId; return WidgetFactory.createWidget(childWidgetData, this.props.renderMode); } renderChildren = () => { return map( // sort by row so stacking context is correct // TODO(abhinav): This is hacky. The stacking context should increase for widgets rendered top to bottom, always. // Figure out a way in which the stacking context is consistent. sortBy(compact(this.props.children), (child) => child.topRow), this.renderChildWidget, ); }; renderAsContainerComponent(props: ContainerWidgetProps) { return ( {this.renderChildren()} ); } getPageView() { return this.renderAsContainerComponent(this.props); } getWidgetType(): WidgetType { return WidgetTypes.CONTAINER_WIDGET; } } export interface ContainerWidgetProps extends WidgetProps { children?: T[]; containerStyle?: ContainerStyle; shouldScrollContents?: boolean; noPad?: boolean; } export default ContainerWidget; export const ProfiledContainerWidget = Sentry.withProfiler(ContainerWidget);