* Updated test * updated assertions * Resizing image to take full width of table cell * updated assertion * Stop updating dynamicBindingPathList directly from widget * Fix selectedRow and selectedRows computations * Fix primaryColumns computations * Updated test for derived column * Added tests for computed value * Added check clear data * Reordering of test * updated common method * Made image size as 100% of table cell size * add templating logic * Updated flow and dsl * Clear old primary columns * Updated testname * updated assertion * use evaluated values for children * Fix primary columns update on component mount and component update * add isArray check * remove property pane enhancement reducer * add property pane enhancement reducer * disable items other than template + fix running property enchancment on drop of list widget * disbled drag, resize, settingsControl, drag for items other than template * add grid options * uncomment the widget operation for add child for grid children * handle delete scenario for child widget in list widget * WIP: Use the new delete and update property features * add listdsl.json for testcases * add test cases for correct no. of items being rendered * add test cases currentItem binding in list widget * change dragEnabled to dragDisabled * change resizeEnabled to resizeDisabled * change settingsControlEnabled to settingsControlDisabled * change dropEnabled to dropDisabled * update settingsControlDisabled default value * Use deleteProperties in propertyControls * Fix unsetting of array indices when deleting widget properties * remove old TableWidget.tsx file * Fix derived column property update on primary column property update * Handle undefined primary columns * Fix filepicker immutable prop issue * Fix object.freeze issue when adding ids to the property pane configuration * fix widget issue in grid * Fix column actions dynamicBindingPathList inclusion issue * remove consoles + fix typo around batch update * Remove redundant tests * js binding test for date picker * hydate enhancement map on copy list widget * check for dynamicleaf * fixes * improve check * fix getNextWidgetName * update template in list widget when copying * updating template copy logic when copying widget * update dynamicBindingPathList in copied widget * Add path parameter to hidden functions in property pane configs * fix copy bug when copying list widget * add computed list property control * Remove time column type Fix editor prompt for currentRow Fix undefined derivedColumns scenario Remove validations for primaryColums and derivedColumns Fix section toggle for video, image and button column types * Fix table widget actions and custom column migrations * Add logs for cyclical dependency map ♻️ * Process array differences * add property control for list widget * Fix onClick migrations * Property pane config parity * binding and trigger paths from the property pane config (#2920) * try react virtualized library * Fix unit test * Fix unit test ✅ * Fix minor issues in table widget * Add default meta props to binding paths to ensure eval and validation * Dummy commit 🎉 * Remove unnecessary datepicker test Fix chart data as string issue * Achieve table column sorting and resizing parity with release * handle scenario where last column isn't available to access * Fix for panel config path not existing in the widget * Fix bindings in currentRow (default) Add dummy property pane config for canvas widget * Update canvas widgets with dynamicPathLists on delete of property paths * Add all diffs to change paths and trim later * Add back default properties 🚶🏻♂️ * Use object based paths instead of arrays for primaryColumns and derivedColumns * Fix issue in reordered columns * Fix inccorect update order * add virtualized list * Fix failing property pane tests * minor change * minor list widget change * Remove .vscode from git * Rename ads to alloy Fix isVisible in list widget * move grid component to widget folder * fix import in widget registry * add sticky row in virtualized list * add sticky container * Fix Height of grid widget items container * fix dragging of items in children other than template children * update list widget * update list widget * Fix padding in list widget * hide scrollbar in list widget list * fix copy bug in list widget * regenrate enhancement map on undo delete widget * Use enhancementmap for autocomplete in list widget Basic styles for list widget scrollbar * add custom control in widget config * minor commit * update scrollbar styles * remove unused variable * fix typo in custom control * comment out test cases * remove unused imports * remove unused imports * add JSON stringify in interweave * add noPad styling in dragLayer for noPad prop * implement grid gap * add list item background color prop * add white color in color picker control * fix gap in last list item * remove onBeforeParse in textcomponent * remove virtualization in grid widget * allow overflow-y * add onListItemClick action * add beta label * add pagination * fix actions in pagination in list widget * add list widget icon * add list background color default value * remove extra div * fix pagination issue * fix list widget crashing on perpage change * extract child operation function to widgetblueprint saga * refactor enhancements * add enhancement hook * refactor propertyUpdate hook enhancment * remove enhacement map * revert renaming ads to alloy * add autopagination * Cleanup unused vars Re-write loop using map Fix binding with external input widget * update default background color * remove unnessary scrol + fix pagination per page * remove console.log * use grid gap in pixel instead of snap * fix list widget tests for binding * add tests for on click action and pagination * remove unnecessary imports * remove overflow hidden in list component * Add feature to enable template actions * update property pane help text for list widget * disable pagination in editor view * update property pane options * add test case for action * uncomment tests * fix grid gap validation * update test cases * fix property pane opening issue for list tempalte * Disable form widgets in list widget * fix template issue for actions * add validation tests for list data * update starting template * add selectedRow + enable pagination in edit mode * remove extra padding in list widget + popper fix on settingDisabled * add stop propagation for button click * fix click event in edit mode * disallow filepicker widget for list widget * add test for list widget entity definition for selectItem * remove unused imports * fix test * remove evaluated value for list child widgets * add comment * remove log * fix copying bug in list widget * add check for not allowing template to copy * fix test * add test for property pane actions * remove unused import * add draglayercomponent test * add test for draggable component * add test for evaluatedvalue popup * add test for messages.ts * add test for widgeticons * add test for property pane selector * add test for widget config response * start testing widget configresponse * add test for enhancements in widget config * add test for codeeditor * add test for base widget + list widget * add test for executeWidgetBlueprintChildOperations * remove unused import * add test for widget operation utils * remove unused import * add test for handleSpecificCasesWhilePasting * remove unused function * remove unused import * add empty list styling * resolve all review comments * fix message test * add test for widget operation utils * fix merge conflicts * move validations in property config Co-authored-by: Abhinav Jha <abhinav@appsmith.com> Co-authored-by: nandan.anantharamu <nandan.anantharamu@thoughtspot.com> Co-authored-by: vicky-primathon.in <vicky.bansal@primathon.in> Co-authored-by: Pawan Kumar <pawankumar@Pawans-MacBook-Pro.local> Co-authored-by: Piyush <piyush@codeitout.com> Co-authored-by: hetunandu <hetu@appsmith.com> Co-authored-by: Hetu Nandu <hetunandu@gmail.com> Co-authored-by: root <root@DESKTOP-9GENCK0.localdomain>
426 lines
12 KiB
TypeScript
426 lines
12 KiB
TypeScript
/**
|
|
* Widget are responsible for accepting the abstraction layer inputs, interpretting them into rederable props and
|
|
* spawing components based on those props
|
|
* Widgets are also responsible for dispatching actions and updating the state tree
|
|
*/
|
|
import {
|
|
WidgetType,
|
|
RenderMode,
|
|
RenderModes,
|
|
CSSUnits,
|
|
} from "constants/WidgetConstants";
|
|
import React, { Component, ReactNode } from "react";
|
|
import {
|
|
PositionType,
|
|
CSSUnit,
|
|
CONTAINER_GRID_PADDING,
|
|
} from "constants/WidgetConstants";
|
|
import DraggableComponent from "components/editorComponents/DraggableComponent";
|
|
import ResizableComponent from "components/editorComponents/ResizableComponent";
|
|
import { ExecuteActionPayload } from "constants/AppsmithActionConstants/ActionConstants";
|
|
import PositionedContainer from "components/designSystems/appsmith/PositionedContainer";
|
|
import WidgetNameComponent from "components/editorComponents/WidgetNameComponent";
|
|
import shallowequal from "shallowequal";
|
|
import { PositionTypes } from "constants/WidgetConstants";
|
|
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
|
import ErrorBoundary from "components/editorComponents/ErrorBoundry";
|
|
import { DerivedPropertiesMap } from "utils/WidgetFactory";
|
|
import {
|
|
WidgetDynamicPathListProps,
|
|
WidgetEvaluatedProps,
|
|
} from "../utils/DynamicBindingUtils";
|
|
import { PropertyPaneConfig } from "constants/PropertyControlConstants";
|
|
import { BatchPropertyUpdatePayload } from "actions/controlActions";
|
|
|
|
/***
|
|
* BaseWidget
|
|
*
|
|
* The abstract class which is extended/implemented by all widgets.
|
|
* Widgets must adhere to the abstractions provided by BaseWidget.
|
|
*
|
|
* Do not:
|
|
* 1) Use the context directly in the widgets
|
|
* 2) Update or access the dsl in the widgets
|
|
* 3) Call actions in widgets or connect the widgets to the entity reducers
|
|
*
|
|
*/
|
|
abstract class BaseWidget<
|
|
T extends WidgetProps,
|
|
K extends WidgetState
|
|
> extends Component<T, K> {
|
|
static contextType = EditorContext;
|
|
|
|
static getPropertyPaneConfig(): PropertyPaneConfig[] {
|
|
return [];
|
|
}
|
|
|
|
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 {};
|
|
}
|
|
|
|
/**
|
|
* Widget abstraction to register the widget type
|
|
* ```javascript
|
|
* getWidgetType() {
|
|
* return "MY_AWESOME_WIDGET",
|
|
* }
|
|
* ```
|
|
*/
|
|
abstract getWidgetType(): WidgetType;
|
|
|
|
/**
|
|
* Widgets can execute actions using this `executeAction` method.
|
|
* Triggers may be specific to the widget
|
|
*/
|
|
executeAction(actionPayload: ExecuteActionPayload): void {
|
|
const { executeAction } = this.context;
|
|
executeAction && executeAction(actionPayload);
|
|
}
|
|
|
|
disableDrag(disable: boolean) {
|
|
const { disableDrag } = this.context;
|
|
disableDrag && disable !== undefined && disableDrag(disable);
|
|
}
|
|
|
|
updateWidget(
|
|
operationName: string,
|
|
widgetId: string,
|
|
widgetProperties: any,
|
|
): void {
|
|
const { updateWidget } = this.context;
|
|
updateWidget && updateWidget(operationName, widgetId, widgetProperties);
|
|
}
|
|
|
|
deleteWidgetProperty(propertyPaths: string[]): void {
|
|
const { deleteWidgetProperty } = this.context;
|
|
const { widgetId } = this.props;
|
|
if (deleteWidgetProperty && widgetId) {
|
|
deleteWidgetProperty(widgetId, propertyPaths);
|
|
}
|
|
}
|
|
|
|
batchUpdateWidgetProperty(updates: BatchPropertyUpdatePayload): void {
|
|
const { batchUpdateWidgetProperty } = this.context;
|
|
const { widgetId } = this.props;
|
|
if (batchUpdateWidgetProperty && widgetId) {
|
|
batchUpdateWidgetProperty(widgetId, updates);
|
|
}
|
|
}
|
|
|
|
updateWidgetProperty(propertyName: string, propertyValue: any): void {
|
|
this.batchUpdateWidgetProperty({
|
|
modify: { [propertyName]: propertyValue },
|
|
});
|
|
}
|
|
|
|
resetChildrenMetaProperty(widgetId: string) {
|
|
const { resetChildrenMetaProperty } = this.context;
|
|
resetChildrenMetaProperty(widgetId);
|
|
}
|
|
|
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
componentDidUpdate(prevProps: T) {}
|
|
|
|
componentDidMount(): void {}
|
|
/* eslint-enable @typescript-eslint/no-empty-function */
|
|
|
|
getComponentDimensions = () => {
|
|
return this.calculateWidgetBounds(
|
|
this.props.rightColumn,
|
|
this.props.leftColumn,
|
|
this.props.topRow,
|
|
this.props.bottomRow,
|
|
this.props.parentColumnSpace,
|
|
this.props.parentRowSpace,
|
|
);
|
|
};
|
|
|
|
calculateWidgetBounds(
|
|
rightColumn: number,
|
|
leftColumn: number,
|
|
topRow: number,
|
|
bottomRow: number,
|
|
parentColumnSpace: number,
|
|
parentRowSpace: number,
|
|
): {
|
|
componentWidth: number;
|
|
componentHeight: number;
|
|
} {
|
|
return {
|
|
componentWidth: (rightColumn - leftColumn) * parentColumnSpace,
|
|
componentHeight: (bottomRow - topRow) * parentRowSpace,
|
|
};
|
|
}
|
|
|
|
render() {
|
|
return this.getWidgetView();
|
|
}
|
|
|
|
/**
|
|
* this function is responsive for making the widget resizable.
|
|
* A widget can be made by non-resizable by passing resizeDisabled prop.
|
|
*
|
|
* @param content
|
|
*/
|
|
makeResizable(content: ReactNode) {
|
|
return (
|
|
<ResizableComponent
|
|
{...this.props}
|
|
paddingOffset={PositionedContainer.padding}
|
|
>
|
|
{content}
|
|
</ResizableComponent>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* this functions wraps the widget in a component that shows a setting control at the top right
|
|
* which gets shown on hover. A widget can enable/disable this by setting `disablePropertyPane` prop
|
|
*
|
|
* @param content
|
|
* @param showControls
|
|
*/
|
|
showWidgetName(content: ReactNode, showControls = false) {
|
|
return (
|
|
<>
|
|
{!this.props.disablePropertyPane && (
|
|
<WidgetNameComponent
|
|
widgetName={this.props.widgetName}
|
|
widgetId={this.props.widgetId}
|
|
parentId={this.props.parentId}
|
|
type={this.props.type}
|
|
showControls={showControls}
|
|
/>
|
|
)}
|
|
{content}
|
|
</>
|
|
);
|
|
}
|
|
|
|
/**
|
|
* wraps the widget in a draggable component.
|
|
* Note: widget drag can be disabled by setting `dragDisabled` prop to true
|
|
*
|
|
* @param content
|
|
*/
|
|
makeDraggable(content: ReactNode) {
|
|
return <DraggableComponent {...this.props}>{content}</DraggableComponent>;
|
|
}
|
|
|
|
makePositioned(content: ReactNode) {
|
|
const style = this.getPositionStyle();
|
|
return (
|
|
<PositionedContainer
|
|
widgetId={this.props.widgetId}
|
|
widgetType={this.props.type}
|
|
style={style}
|
|
>
|
|
{content}
|
|
</PositionedContainer>
|
|
);
|
|
}
|
|
|
|
addErrorBoundary(content: ReactNode) {
|
|
return <ErrorBoundary>{content}</ErrorBoundary>;
|
|
}
|
|
|
|
private getWidgetView(): ReactNode {
|
|
let content: ReactNode;
|
|
|
|
switch (this.props.renderMode) {
|
|
case RenderModes.CANVAS:
|
|
content = this.getCanvasView();
|
|
if (!this.props.detachFromLayout) {
|
|
if (!this.props.resizeDisabled) content = this.makeResizable(content);
|
|
content = this.showWidgetName(content);
|
|
content = this.makeDraggable(content);
|
|
content = this.makePositioned(content);
|
|
}
|
|
return content;
|
|
|
|
// return this.getCanvasView();
|
|
case RenderModes.PAGE:
|
|
content = this.getPageView();
|
|
if (this.props.isVisible) {
|
|
content = this.addErrorBoundary(content);
|
|
if (!this.props.detachFromLayout) {
|
|
content = this.makePositioned(content);
|
|
}
|
|
return content;
|
|
}
|
|
return <React.Fragment />;
|
|
default:
|
|
throw Error("RenderMode not defined");
|
|
}
|
|
}
|
|
|
|
abstract getPageView(): ReactNode;
|
|
|
|
getCanvasView(): ReactNode {
|
|
const content = this.getPageView();
|
|
return this.addErrorBoundary(content);
|
|
}
|
|
|
|
// TODO(abhinav): Maybe make this a pure component to bailout from updating altogether.
|
|
// This would involve making all widgets which have "states" to not have states,
|
|
// as they're extending this one.
|
|
shouldComponentUpdate(nextProps: WidgetProps, nextState: WidgetState) {
|
|
return (
|
|
!shallowequal(nextProps, this.props) ||
|
|
!shallowequal(nextState, this.state)
|
|
);
|
|
}
|
|
|
|
/**
|
|
* generates styles that positions the widget
|
|
*/
|
|
private getPositionStyle(): BaseStyle {
|
|
const { componentHeight, componentWidth } = this.getComponentDimensions();
|
|
|
|
return {
|
|
positionType: PositionTypes.ABSOLUTE,
|
|
componentHeight,
|
|
componentWidth,
|
|
yPosition:
|
|
this.props.topRow * this.props.parentRowSpace +
|
|
(this.props.noContainerOffset ? 0 : CONTAINER_GRID_PADDING),
|
|
xPosition:
|
|
this.props.leftColumn * this.props.parentColumnSpace +
|
|
(this.props.noContainerOffset ? 0 : CONTAINER_GRID_PADDING),
|
|
xPositionUnit: CSSUnits.PIXEL,
|
|
yPositionUnit: CSSUnits.PIXEL,
|
|
};
|
|
}
|
|
|
|
// TODO(abhinav): These defaultProps seem unneccessary. Check it out.
|
|
static defaultProps: Partial<WidgetProps> | undefined = {
|
|
parentRowSpace: 1,
|
|
parentColumnSpace: 1,
|
|
topRow: 0,
|
|
leftColumn: 0,
|
|
isLoading: false,
|
|
renderMode: RenderModes.CANVAS,
|
|
dragDisabled: false,
|
|
dropDisabled: false,
|
|
isDeletable: true,
|
|
resizeDisabled: false,
|
|
disablePropertyPane: false,
|
|
};
|
|
}
|
|
|
|
export interface BaseStyle {
|
|
componentHeight: number;
|
|
componentWidth: number;
|
|
positionType: PositionType;
|
|
xPosition: number;
|
|
yPosition: number;
|
|
xPositionUnit: CSSUnit;
|
|
yPositionUnit: CSSUnit;
|
|
heightUnit?: CSSUnit;
|
|
widthUnit?: CSSUnit;
|
|
}
|
|
|
|
export type WidgetState = Record<string, unknown>;
|
|
|
|
export interface WidgetBuilder<T extends WidgetProps, S extends WidgetState> {
|
|
buildWidget(widgetProps: T): JSX.Element;
|
|
}
|
|
|
|
export interface WidgetBaseProps {
|
|
widgetId: string;
|
|
type: WidgetType;
|
|
widgetName: string;
|
|
parentId?: string;
|
|
renderMode: RenderMode;
|
|
version: number;
|
|
}
|
|
|
|
export type WidgetRowCols = {
|
|
leftColumn: number;
|
|
rightColumn: number;
|
|
topRow: number;
|
|
bottomRow: number;
|
|
minHeight?: number; // Required to reduce the size of CanvasWidgets.
|
|
};
|
|
|
|
export interface WidgetPositionProps extends WidgetRowCols {
|
|
parentColumnSpace: number;
|
|
parentRowSpace: number;
|
|
// The detachFromLayout flag tells use about the following properties when enabled
|
|
// 1) Widget does not drag/resize
|
|
// 2) Widget CAN (but not neccessarily) be a dropTarget
|
|
// Examples: MainContainer is detached from layout,
|
|
// MODAL_WIDGET is also detached from layout.
|
|
detachFromLayout?: boolean;
|
|
noContainerOffset?: boolean; // This won't offset the child in parent
|
|
}
|
|
|
|
export const WIDGET_STATIC_PROPS = {
|
|
leftColumn: true,
|
|
rightColumn: true,
|
|
topRow: true,
|
|
bottomRow: true,
|
|
minHeight: true,
|
|
parentColumnSpace: true,
|
|
parentRowSpace: true,
|
|
children: true,
|
|
type: true,
|
|
widgetId: true,
|
|
widgetName: true,
|
|
parentId: true,
|
|
renderMode: true,
|
|
detachFromLayout: true,
|
|
noContainerOffset: false,
|
|
};
|
|
|
|
export interface WidgetDisplayProps {
|
|
//TODO(abhinav): Some of these props are mandatory
|
|
isVisible?: boolean;
|
|
isLoading: boolean;
|
|
isDisabled?: boolean;
|
|
backgroundColor?: string;
|
|
}
|
|
|
|
export interface WidgetDataProps
|
|
extends WidgetBaseProps,
|
|
WidgetPositionProps,
|
|
WidgetDisplayProps {}
|
|
|
|
export interface WidgetProps
|
|
extends WidgetDataProps,
|
|
WidgetDynamicPathListProps,
|
|
WidgetEvaluatedProps {
|
|
key?: string;
|
|
isDefaultClickDisabled?: boolean;
|
|
[key: string]: any;
|
|
}
|
|
|
|
export interface WidgetCardProps {
|
|
type: WidgetType;
|
|
key?: string;
|
|
widgetCardName: string;
|
|
isBeta?: boolean;
|
|
}
|
|
|
|
export const WidgetOperations = {
|
|
MOVE: "MOVE",
|
|
RESIZE: "RESIZE",
|
|
ADD_CHILD: "ADD_CHILD",
|
|
UPDATE_PROPERTY: "UPDATE_PROPERTY",
|
|
DELETE: "DELETE",
|
|
ADD_CHILDREN: "ADD_CHILDREN",
|
|
};
|
|
|
|
export type WidgetOperation = typeof WidgetOperations[keyof typeof WidgetOperations];
|
|
|
|
export default BaseWidget;
|