## Description This PR primarily adds the modal widget to WDS. The following changes were made: 1. WidgetNameCanvas now listens to modal body scrolls to position widget name components correctly 2. Modal Widget is rendered as a detached widget that is outside of the layout flow of the main canvas 3. Main container resizer now has a higher z-index to show even if the modal is open in the preview mode 4. Widget selection flow in Anvil layout system has been modified to be handled in a central location (`AnvilMainCanvas`) 5. Modal widget's type in modal sagas are selected via a selector that checks for the feature flag. 6. Modal widget has its own preset that (at present) is similar to the Main container's layout preset #### PR fixes following issue(s) Fixes #28588 Fixes #28328 Fixes #27459 #### Media https://github.com/appsmithorg/appsmith/assets/103687/bf350be4-2202-49f3-a860-3e38681ab32e #### Type of change - New feature (non-breaking change which adds functionality) - This change requires a documentation update ## Testing > #### How Has This Been Tested? - [x] Manual - [ ] JUnit - [ ] Jest - [ ] Cypress ## 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/Guidelines-for-test-plans#speedbreakers-) have been covered - [ ] Test plan covers all impacted features and [areas of interest](https://github.com/appsmithorg/TestSmith/wiki/Guidelines-for-test-plans#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 <!-- This is an auto-generated comment: release notes by coderabbit.ai --> ## Summary by CodeRabbit - **New Features** - Enhanced Modal components with additional styling and customization options. - Introduced a new event type for modal submission actions. - Added a `--on-canvas-ui-z-index` CSS variable for improved layering control. - Implemented a new method for widget focus management in the editor. - **Improvements** - Modal components now use context hooks for close actions. - Improved the handling of detached widgets in various layout systems. - Simplified the drag-and-drop state management for widgets. - Upgraded the visual presentation of the widget drop area. - Enhanced widget selection with new custom event dispatching. - Updated the modal widget configuration with default settings and property pane structure. - **Bug Fixes** - Fixed an issue with modal scrolling to behave consistently with the main container. - Addressed a problem where the `id` was not found during layout element position updates. - **Style** - Adjusted modal overlay positioning and content width with new CSS standards. - Updated zIndex references to use CSS variables for consistent styling. - **Refactor** - Reorganized the `Widgets` array into categorized groups for better clarity. - Simplified the `modalPreset` function's parameters and layout declaration. - **Documentation** - Added default values for feature flags in the documentation. - **Chores** - Altered feature flags to enable new functionalities by default. - **Tests** - No visible changes to end-users in this category. - **Revert** - No visible changes to end-users in this category. <!-- end of auto-generated comment: release notes by coderabbit.ai --> --------- Co-authored-by: Preet Sidhu <preetsidhu.bits@gmail.com> Co-authored-by: Valera Melnikov <valera@appsmith.com>
578 lines
16 KiB
TypeScript
578 lines
16 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 type { BatchPropertyUpdatePayload } from "actions/controlActions";
|
|
import type { EditorContextType } from "components/editorComponents/EditorContextProvider";
|
|
import { EditorContext } from "components/editorComponents/EditorContextProvider";
|
|
import type { ExecuteTriggerPayload } from "constants/AppsmithActionConstants/ActionConstants";
|
|
import type { PropertyPaneConfig } from "constants/PropertyControlConstants";
|
|
import type {
|
|
CSSUnit,
|
|
PositionType,
|
|
RenderMode,
|
|
WidgetTags,
|
|
WidgetType,
|
|
} from "constants/WidgetConstants";
|
|
import { RenderModes } from "constants/WidgetConstants";
|
|
import { ENTITY_TYPE } from "entities/AppsmithConsole";
|
|
import type { SetterConfig, Stylesheet } from "entities/AppTheming";
|
|
import type { Context, ReactNode, RefObject } from "react";
|
|
import { Component } from "react";
|
|
import type {
|
|
ModifyMetaWidgetPayload,
|
|
UpdateMetaWidgetPropertyPayload,
|
|
} from "reducers/entityReducers/metaWidgetsReducer";
|
|
import type { SelectionRequestType } from "sagas/WidgetSelectUtils";
|
|
import shallowequal from "shallowequal";
|
|
import AppsmithConsole from "utils/AppsmithConsole";
|
|
import type {
|
|
DataTreeEvaluationProps,
|
|
WidgetDynamicPathListProps,
|
|
} from "utils/DynamicBindingUtils";
|
|
import type { DerivedPropertiesMap } from "WidgetProvider/factory";
|
|
import type {
|
|
AnvilConfig,
|
|
AutoLayoutConfig,
|
|
CanvasWidgetStructure,
|
|
FlattenedWidgetProps,
|
|
WidgetBaseConfiguration,
|
|
WidgetDefaultProps,
|
|
WidgetMethods,
|
|
} from "../WidgetProvider/constants";
|
|
import type { WidgetEntity } from "@appsmith/entities/DataTree/types";
|
|
import type { AutocompletionDefinitions } from "../WidgetProvider/constants";
|
|
import type {
|
|
FlexVerticalAlignment,
|
|
LayoutDirection,
|
|
ResponsiveBehavior,
|
|
} from "layoutSystems/common/utils/constants";
|
|
import type { FeatureFlag } from "@appsmith/entities/FeatureFlag";
|
|
import store from "store";
|
|
import { selectFeatureFlags } from "@appsmith/selectors/featureFlagsSelectors";
|
|
import type { WidgetFeatures } from "utils/WidgetFeatures";
|
|
import { LayoutSystemTypes } from "layoutSystems/types";
|
|
import type { CanvasWidgetsReduxState } from "reducers/entityReducers/canvasWidgetsReducer";
|
|
import type {
|
|
CopiedWidgetData,
|
|
PasteDestinationInfo,
|
|
PastePayload,
|
|
} from "layoutSystems/anvil/utils/paste/types";
|
|
import { type CallEffect, call } from "redux-saga/effects";
|
|
|
|
/***
|
|
* 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
|
|
*
|
|
*/
|
|
|
|
const REFERENCE_KEY = "$$refs$$";
|
|
|
|
abstract class BaseWidget<
|
|
T extends WidgetProps,
|
|
K extends WidgetState,
|
|
TCache = unknown,
|
|
> extends Component<T, K> {
|
|
static contextType = EditorContext;
|
|
|
|
context!: React.ContextType<Context<EditorContextType<TCache>>>;
|
|
|
|
static type = "BASE_WIDGET";
|
|
|
|
static getDefaults(): WidgetDefaultProps {
|
|
return {} as WidgetDefaultProps;
|
|
}
|
|
|
|
static getConfig(): WidgetBaseConfiguration {
|
|
return {
|
|
name: "baseWidget",
|
|
};
|
|
}
|
|
|
|
static getFeatures(): WidgetFeatures | null {
|
|
return null;
|
|
}
|
|
|
|
static getMethods(): WidgetMethods {
|
|
return {};
|
|
}
|
|
|
|
static getAutoLayoutConfig(): AutoLayoutConfig | null {
|
|
return null;
|
|
}
|
|
|
|
static getAnvilConfig(): AnvilConfig | null {
|
|
return null;
|
|
}
|
|
|
|
static getSetterConfig(): SetterConfig | null {
|
|
return null;
|
|
}
|
|
|
|
static getPropertyPaneConfig(): PropertyPaneConfig[] {
|
|
return [];
|
|
}
|
|
|
|
static getPropertyPaneContentConfig(): PropertyPaneConfig[] {
|
|
return [];
|
|
}
|
|
|
|
static getPropertyPaneStyleConfig(): PropertyPaneConfig[] {
|
|
return [];
|
|
}
|
|
|
|
static getDerivedPropertiesMap(): DerivedPropertiesMap {
|
|
return {};
|
|
}
|
|
|
|
static getDefaultPropertiesMap(): Record<string, any> {
|
|
return {};
|
|
}
|
|
|
|
static getDependencyMap(): Record<string, string[]> {
|
|
return {};
|
|
}
|
|
|
|
// TODO Find a way to enforce this, (dont let it be set)
|
|
static getMetaPropertiesMap(): Record<string, any> {
|
|
return {};
|
|
}
|
|
|
|
static getStylesheetConfig(): Stylesheet {
|
|
return {};
|
|
}
|
|
|
|
static getAutocompleteDefinitions(): AutocompletionDefinitions {
|
|
return {};
|
|
}
|
|
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
static pasteOperationChecks(
|
|
allWidgets: CanvasWidgetsReduxState, // All widgets
|
|
oldWidget: FlattenedWidgetProps, // Original copied widget
|
|
newWidget: FlattenedWidgetProps, // Newly generated widget
|
|
widgetIdMap: Record<string, string>, // Map of oldWidgetId -> newWidgetId
|
|
): FlattenedWidgetProps | null {
|
|
return null;
|
|
}
|
|
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
static *performPasteOperation(
|
|
allWidgets: CanvasWidgetsReduxState, // All widgets
|
|
copiedWidgets: CopiedWidgetData[], // Original copied widgets
|
|
destinationInfo: PasteDestinationInfo, // Destination info of copied widgets
|
|
widgetIdMap: Record<string, string>, // Map of oldWidgetId -> newWidgetId
|
|
reverseWidgetIdMap: Record<string, string>, // Map of newWidgetId -> oldWidgetId
|
|
): Generator<CallEffect<PastePayload>, PastePayload, any> {
|
|
const res: PastePayload = yield call(function* () {
|
|
return { widgets: allWidgets, widgetIdMap, reverseWidgetIdMap };
|
|
});
|
|
return res;
|
|
}
|
|
|
|
/**
|
|
* getLoadingProperties returns a list of regexp's used to specify bindingPaths,
|
|
* which can set the isLoading prop of the widget.
|
|
* When:
|
|
* 1. the path is bound to an action (API/Query)
|
|
* 2. the action is currently in-progress
|
|
*
|
|
* if undefined, all paths can set the isLoading state
|
|
* if empty array, no paths can set the isLoading state
|
|
*/
|
|
static getLoadingProperties(): Array<RegExp> | undefined {
|
|
return;
|
|
}
|
|
|
|
/**
|
|
* Widgets can execute actions using this `executeAction` method.
|
|
* Triggers may be specific to the widget
|
|
*/
|
|
executeAction(actionPayload: ExecuteTriggerPayload): void {
|
|
const { executeAction } = this.context;
|
|
executeAction &&
|
|
executeAction({
|
|
...actionPayload,
|
|
source: {
|
|
id: this.props.widgetId,
|
|
name: this.props.widgetName,
|
|
},
|
|
});
|
|
|
|
actionPayload.triggerPropertyName &&
|
|
AppsmithConsole.info({
|
|
text: `${actionPayload.triggerPropertyName} triggered`,
|
|
source: {
|
|
type: ENTITY_TYPE.WIDGET,
|
|
id: this.props.widgetId,
|
|
name: this.props.widgetName,
|
|
},
|
|
});
|
|
}
|
|
|
|
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,
|
|
shouldReplay = true,
|
|
): void {
|
|
const { batchUpdateWidgetProperty } = this.context;
|
|
const { widgetId } = this.props;
|
|
if (batchUpdateWidgetProperty && widgetId) {
|
|
batchUpdateWidgetProperty(widgetId, updates, shouldReplay);
|
|
}
|
|
}
|
|
|
|
updateWidgetProperty(propertyName: string, propertyValue: any): void {
|
|
this.batchUpdateWidgetProperty({
|
|
modify: { [propertyName]: propertyValue },
|
|
});
|
|
}
|
|
|
|
resetChildrenMetaProperty(widgetId: string) {
|
|
const { resetChildrenMetaProperty } = this.context;
|
|
if (resetChildrenMetaProperty) resetChildrenMetaProperty(widgetId);
|
|
}
|
|
|
|
selectWidgetRequest = (
|
|
selectionRequestType: SelectionRequestType,
|
|
payload?: string[],
|
|
) => {
|
|
const { selectWidgetRequest } = this.context;
|
|
if (selectWidgetRequest) {
|
|
selectWidgetRequest(selectionRequestType, payload);
|
|
}
|
|
};
|
|
|
|
unfocusWidget = () => {
|
|
const { unfocusWidget } = this.context;
|
|
if (unfocusWidget) {
|
|
unfocusWidget();
|
|
}
|
|
};
|
|
|
|
/* eslint-disable @typescript-eslint/no-empty-function */
|
|
|
|
/* eslint-disable @typescript-eslint/no-unused-vars */
|
|
componentDidUpdate(prevProps: T, prevState?: K) {
|
|
if (
|
|
!this.props.deferRender &&
|
|
this.props.deferRender !== prevProps.deferRender
|
|
) {
|
|
this.deferredComponentDidRender();
|
|
}
|
|
}
|
|
|
|
componentDidMount(): void {}
|
|
|
|
/*
|
|
* With lazy rendering, skeleton loaders are rendered for below fold widgets.
|
|
* This Appsmith widget life cycle method that gets called when the actual widget
|
|
* component renders instead of the skeleton loader.
|
|
*/
|
|
deferredComponentDidRender(): void {}
|
|
|
|
/* eslint-enable @typescript-eslint/no-empty-function */
|
|
|
|
modifyMetaWidgets = (modifications: ModifyMetaWidgetPayload) => {
|
|
this.context.modifyMetaWidgets?.({
|
|
...modifications,
|
|
creatorId: this.props.widgetId,
|
|
});
|
|
};
|
|
|
|
deleteMetaWidgets = () => {
|
|
this.context?.deleteMetaWidgets?.({
|
|
creatorIds: [this.props.widgetId],
|
|
});
|
|
};
|
|
|
|
setWidgetCache = (data: TCache) => {
|
|
const key = this.getWidgetCacheKey();
|
|
|
|
if (key) {
|
|
this.context?.setWidgetCache?.(key, data);
|
|
}
|
|
};
|
|
|
|
updateMetaWidgetProperty = (payload: UpdateMetaWidgetPropertyPayload) => {
|
|
const { widgetId } = this.props;
|
|
|
|
this.context.updateMetaWidgetProperty?.({
|
|
...payload,
|
|
creatorId: widgetId,
|
|
});
|
|
};
|
|
|
|
getWidgetCache = () => {
|
|
const key = this.getWidgetCacheKey();
|
|
|
|
if (key) {
|
|
return this.context?.getWidgetCache?.(key);
|
|
}
|
|
};
|
|
|
|
getWidgetCacheKey = () => {
|
|
return this.props.metaWidgetId || this.props.widgetId;
|
|
};
|
|
|
|
setWidgetReferenceCache = <TRefCache,>(data: TRefCache) => {
|
|
const key = this.getWidgetCacheReferenceKey();
|
|
|
|
this.context?.setWidgetCache?.(`${key}.${REFERENCE_KEY}`, data);
|
|
};
|
|
|
|
getWidgetReferenceCache = <TRefCache,>() => {
|
|
const key = this.getWidgetCacheReferenceKey();
|
|
|
|
return this.context?.getWidgetCache?.<TRefCache>(`${key}.${REFERENCE_KEY}`);
|
|
};
|
|
|
|
getWidgetCacheReferenceKey = () => {
|
|
return this.props.referencedWidgetId || this.props.widgetId;
|
|
};
|
|
|
|
render() {
|
|
return this.getWidgetView();
|
|
}
|
|
|
|
get isAutoLayoutMode() {
|
|
return this.props.layoutSystemType === LayoutSystemTypes.AUTO;
|
|
}
|
|
|
|
updateOneClickBindingOptionsVisibility(visibility: boolean) {
|
|
const { updateOneClickBindingOptionsVisibility } = this.context;
|
|
|
|
updateOneClickBindingOptionsVisibility?.(visibility);
|
|
}
|
|
|
|
abstract getWidgetView(): ReactNode;
|
|
|
|
// 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)
|
|
);
|
|
}
|
|
|
|
// 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,
|
|
isFlexChild: false,
|
|
isMobile: false,
|
|
};
|
|
|
|
/*
|
|
* Function to get a specific feature flag
|
|
* TODO(Keyur): To move the below function to the EditorContextProvider
|
|
*/
|
|
static getFeatureFlag(featureFlag: FeatureFlag) {
|
|
const state = store.getState();
|
|
const featureFlags = selectFeatureFlags(state);
|
|
|
|
return featureFlags[featureFlag];
|
|
}
|
|
}
|
|
|
|
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 CanvasWidgetStructure,
|
|
S extends WidgetState,
|
|
> {
|
|
buildWidget(widgetProps: T): JSX.Element;
|
|
}
|
|
|
|
export interface WidgetBaseProps {
|
|
widgetId: string;
|
|
metaWidgetId?: string;
|
|
type: WidgetType;
|
|
widgetName: string;
|
|
parentId?: string;
|
|
renderMode: RenderMode;
|
|
version: number;
|
|
childWidgets?: WidgetEntity[];
|
|
flattenedChildCanvasWidgets?: Record<string, FlattenedWidgetProps>;
|
|
metaWidgetChildrenStructure?: CanvasWidgetStructure[];
|
|
referencedWidgetId?: string;
|
|
requiresFlatWidgetChildren?: boolean;
|
|
hasMetaWidgets?: boolean;
|
|
creatorId?: string;
|
|
isMetaWidget?: boolean;
|
|
suppressAutoComplete?: boolean;
|
|
suppressDebuggerError?: boolean;
|
|
disallowCopy?: boolean;
|
|
/**
|
|
* The keys of the props mentioned here would always be picked from the canvas widget
|
|
* rather than the evaluated values in withWidgetProps HOC.
|
|
* */
|
|
additionalStaticProps?: string[];
|
|
mainCanvasWidth?: number;
|
|
isMobile?: boolean;
|
|
hasAutoHeight?: boolean;
|
|
hasAutoWidth?: boolean;
|
|
widgetSize?: { [key: string]: Record<string, string> };
|
|
}
|
|
|
|
export interface WidgetRowCols {
|
|
leftColumn: number;
|
|
rightColumn: number;
|
|
topRow: number;
|
|
bottomRow: number;
|
|
minHeight?: number; // Required to reduce the size of CanvasWidgets.
|
|
mobileLeftColumn?: number;
|
|
mobileRightColumn?: number;
|
|
mobileTopRow?: number;
|
|
mobileBottomRow?: number;
|
|
height?: number;
|
|
}
|
|
|
|
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
|
|
isFlexChild?: boolean;
|
|
direction?: LayoutDirection;
|
|
responsiveBehavior?: ResponsiveBehavior;
|
|
minWidth?: number; // Required to avoid squishing of widgets on mobile viewport.
|
|
isMobile?: boolean;
|
|
flexVerticalAlignment?: FlexVerticalAlignment;
|
|
layoutSystemType?: LayoutSystemTypes;
|
|
widthInPercentage?: number; // Stores the widget's width set by the user
|
|
mobileWidthInPercentage?: number;
|
|
width?: number;
|
|
}
|
|
|
|
export interface WidgetCanvasProps {
|
|
isWidgetSelected?: boolean;
|
|
}
|
|
|
|
export const WIDGET_DISPLAY_PROPS = {
|
|
isVisible: true,
|
|
isLoading: true,
|
|
isDisabled: true,
|
|
backgroundColor: true,
|
|
};
|
|
export interface WidgetError extends Error {
|
|
type: "property" | "configuration" | "other";
|
|
path?: string;
|
|
}
|
|
export interface WidgetErrorProps {
|
|
errors?: WidgetError[];
|
|
}
|
|
|
|
export interface WidgetDisplayProps {
|
|
//TODO(abhinav): Some of these props are mandatory
|
|
isVisible?: boolean;
|
|
isLoading: boolean;
|
|
isDisabled?: boolean;
|
|
backgroundColor?: string;
|
|
animateLoading?: boolean;
|
|
deferRender?: boolean;
|
|
wrapperRef?: RefObject<HTMLDivElement>;
|
|
selectedWidgetAncestry?: string[];
|
|
classList?: string[];
|
|
}
|
|
|
|
export interface WidgetDataProps
|
|
extends WidgetBaseProps,
|
|
WidgetErrorProps,
|
|
WidgetPositionProps,
|
|
WidgetDisplayProps,
|
|
WidgetCanvasProps {}
|
|
|
|
export interface WidgetProps
|
|
extends WidgetDataProps,
|
|
WidgetDynamicPathListProps,
|
|
DataTreeEvaluationProps {
|
|
key?: string;
|
|
isDefaultClickDisabled?: boolean;
|
|
|
|
[key: string]: any;
|
|
}
|
|
|
|
export interface WidgetCardProps {
|
|
rows: number;
|
|
columns: number;
|
|
type: WidgetType;
|
|
key?: string;
|
|
displayName: string;
|
|
icon: string;
|
|
isBeta?: boolean;
|
|
tags?: WidgetTags[];
|
|
isSearchWildcard?: 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;
|