diff --git a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx index 01587643d..70ae4d527 100644 --- a/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx +++ b/client/packages/lowcoder/src/comps/controls/styleControlConstants.tsx @@ -1598,6 +1598,51 @@ export const ModalStyle = [ BACKGROUND_IMAGE_ORIGIN, ] as const; + +export const NotificationStyle = [ + getBackground("primarySurface"), + { + name: "color", + label: trans("color"), + depName: "background", + depType: DEP_TYPE.CONTRAST_TEXT, + transformer: contrastText, + }, + { + name: "closeIconColor", + label: trans("toastComp.closeIconColor"), + depName: "background", + depType: DEP_TYPE.CONTRAST_TEXT, + transformer: contrastText, + }, + { + name: "infoIconColor", + label: trans("toastComp.infoIconColor"), + color: "#1890ff", + }, + { + name: "successIconColor", + label: trans("toastComp.successIconColor"), + color: "#52c41a", + }, + { + name: "warningIconColor", + label: trans("toastComp.warningIconColor"), + color: "#faad14", + }, + { + name: "errorIconColor", + label: trans("toastComp.errorIconColor"), + color: "#ff4d4f", + }, + getStaticBorder("transparent"), + RADIUS, + BORDER_WIDTH, + BORDER_STYLE, + MARGIN, + PADDING, +] as const; + export const CascaderStyle = [ ...getStaticBgBorderRadiusByBg(SURFACE_COLOR, "pc"), TEXT, @@ -2488,6 +2533,7 @@ export type ChildrenMultiSelectStyleType = StyleConfigType< export type TabContainerStyleType = StyleConfigType; export type TabBodyStyleType = StyleConfigType; export type ModalStyleType = StyleConfigType; +export type NotificationStyleType = StyleConfigType; export type CascaderStyleType = StyleConfigType; export type CheckboxStyleType = StyleConfigType; export type RadioStyleType = StyleConfigType; diff --git a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx index a310ff6e3..66442f648 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx @@ -60,7 +60,10 @@ const HookCompConfig: Record< }, utils: { category: "hide" }, message: { category: "hide" }, - toast: { category: "hide" }, + toast: { + category: "ui", + singleton: false, + }, }; // Get hook component category diff --git a/client/packages/lowcoder/src/comps/hooks/toastComp.ts b/client/packages/lowcoder/src/comps/hooks/toastComp.ts deleted file mode 100644 index fdcee872f..000000000 --- a/client/packages/lowcoder/src/comps/hooks/toastComp.ts +++ /dev/null @@ -1,95 +0,0 @@ -import { withMethodExposing } from "../generators/withMethodExposing"; -import { simpleMultiComp } from "../generators"; -import { withExposingConfigs } from "../generators/withExposing"; -import { EvalParamType, ParamsConfig } from "../controls/actionSelector/executeCompTypes"; -import { JSONObject } from "../../util/jsonTypes"; -import { trans } from "i18n"; -import { notificationInstance } from "lowcoder-design"; -import type { ArgsProps, NotificationPlacement } from "antd/es/notification/interface"; - -const params: ParamsConfig = [ - { name: "text", type: "string" }, - { name: "options", type: "JSON" }, -]; - -const showNotification = ( - params: EvalParamType[], - level: "open" | "info" | "success" | "warning" | "error" -) => { - const text = params?.[0] as string; - const options = (params?.[1] as JSONObject) || {}; - - const { message , duration, id, placement, dismissible } = options; - - const closeIcon: boolean | undefined = dismissible === true ? undefined : (dismissible === false ? false : undefined); - - const durationNumberOrNull: number | null = typeof duration === 'number' ? duration : null; - - const notificationArgs: ArgsProps = { - message: text, - description: message as React.ReactNode, - duration: durationNumberOrNull ?? 3, - key: id as React.Key, - placement: placement as NotificationPlacement ?? "bottomRight", - closeIcon: closeIcon as boolean, - }; - - // Use notificationArgs to trigger the notification - - text && notificationInstance[level](notificationArgs); -}; - -const destroy = ( - params: EvalParamType[] -) => { - // Extract the id from the params - const id = params[0] as React.Key; - - // Call notificationInstance.destroy with the provided id - notificationInstance.destroy(id); -}; - -//what we would like to expose: title, text, duration, id, btn-obj, onClose, placement - -const ToastCompBase = simpleMultiComp({}); - -export let ToastComp = withExposingConfigs(ToastCompBase, []); - -ToastComp = withMethodExposing(ToastComp, [ - { - method: { name: "destroy", description: trans("toastComp.destroy"), params: params }, - execute: (comp, params) => destroy(params), - }, - { - method: { name: "open", description: trans("toastComp.info"), params: params }, - execute: (comp, params) => { - showNotification(params, "open"); - }, - }, - { - method: { name: "info", description: trans("toastComp.info"), params: params }, - execute: (comp, params) => { - showNotification(params, "info"); - }, - }, - { - method: { name: "success", description: trans("toastComp.success"), params: params }, - execute: (comp, params) => { - showNotification(params, "success"); - }, - }, - { - method: { name: "warn", description: trans("toastComp.warn"), params: params }, - execute: (comp, params) => { - showNotification(params, "warning"); - }, - }, - { - method: { name: "error", description: trans("toastComp.error"), params: params }, - execute: (comp, params) => { - showNotification(params, "error"); - }, - }, -]); - - diff --git a/client/packages/lowcoder/src/comps/hooks/toastComp.tsx b/client/packages/lowcoder/src/comps/hooks/toastComp.tsx new file mode 100644 index 000000000..1540e071f --- /dev/null +++ b/client/packages/lowcoder/src/comps/hooks/toastComp.tsx @@ -0,0 +1,475 @@ +import { BoolControl } from "comps/controls/boolControl"; +import { NumberControl, StringControl } from "comps/controls/codeControl"; +import { dropdownControl } from "comps/controls/dropdownControl"; +import { eventHandlerControl } from "comps/controls/eventHandlerControl"; +import { styleControl } from "comps/controls/styleControl"; +import { NotificationStyle, NotificationStyleType } from "comps/controls/styleControlConstants"; +import { withDefault, simpleMultiComp, withPropertyViewFn, withViewFn } from "comps/generators"; +import { withMethodExposing } from "comps/generators/withMethodExposing"; +import { NameConfig, withExposingConfigs } from "comps/generators/withExposing"; +import { Section, sectionNames } from "lowcoder-design"; +import { trans } from "i18n"; +import { notificationInstance } from "lowcoder-design"; +import type { ArgsProps, NotificationPlacement } from "antd/es/notification/interface"; +import { EvalParamType, ParamsConfig } from "comps/controls/actionSelector/executeCompTypes"; +import { JSONObject } from "util/jsonTypes"; +import React, { useEffect, useId } from "react"; +import { stateComp } from "comps/generators/simpleGenerators"; +import { isEqual } from "lodash"; +import { createGlobalStyle } from "styled-components"; + + +const ToastGlobalStyle = createGlobalStyle<{ + $instanceId: string; + $background?: string; + $textColor?: string; + $closeIconColor?: string; + $infoIconColor?: string; + $successIconColor?: string; + $warningIconColor?: string; + $errorIconColor?: string; + $border?: string; + $borderWidth?: string; + $borderStyle?: string; + $radius?: string; + $margin?: string; + $padding?: string; + $width?: string; +}>` + .ant-notification .ant-notification-notice-wrapper:has(.lowcoder-toast-${props => props.$instanceId}) { + background: ${props => props.$background || 'inherit'}; + border-color: ${props => props.$border || 'transparent'}; + border-width: ${props => props.$borderWidth || '0'}; + border-style: ${props => props.$borderStyle || 'solid'}; + border-radius: ${props => props.$radius || '8px'}; + ${props => props.$margin ? `margin: ${props.$margin};` : ''} + ${props => props.$padding ? `padding: ${props.$padding};` : ''} + + .ant-notification-notice { + background: transparent; + ${props => props.$width ? `width: ${props.$width};` : ''} + } + + .ant-notification-notice-message, + .ant-notification-notice-description { + color: ${props => props.$textColor || 'inherit'}; + } + + .ant-notification-notice-close { + color: ${props => props.$closeIconColor || 'inherit'}; + } + + .ant-notification-notice-icon-info.anticon { + color: ${props => props.$infoIconColor || '#1890ff'}; + } + + .ant-notification-notice-icon-success.anticon { + color: ${props => props.$successIconColor || '#52c41a'}; + } + + .ant-notification-notice-icon-warning.anticon { + color: ${props => props.$warningIconColor || '#faad14'}; + } + + .ant-notification-notice-icon-error.anticon { + color: ${props => props.$errorIconColor || '#ff4d4f'}; + } + } +`; + +// Toast type options +const toastTypeOptions = [ + { label: trans("toastComp.typeInfo"), value: "info" }, + { label: trans("toastComp.typeSuccess"), value: "success" }, + { label: trans("toastComp.typeWarning"), value: "warning" }, + { label: trans("toastComp.typeError"), value: "error" }, +] as const; + +const placementOptions = [ + { label: trans("toastComp.placementTopLeft"), value: "topLeft" }, + { label: trans("toastComp.placementTopRight"), value: "topRight" }, + { label: trans("toastComp.placementBottomLeft"), value: "bottomLeft" }, + { label: trans("toastComp.placementBottomRight"), value: "bottomRight" }, +] as const; + +const ToastEventOptions = [ + { label: trans("toastComp.click"), value: "click", description: trans("toastComp.clickDesc") }, + { label: trans("toastComp.close"), value: "close", description: trans("toastComp.closeDesc") }, +] as const; + +const showParams: ParamsConfig = [ + { name: "text", type: "string" }, + { name: "options", type: "JSON" }, +]; + +const closeParams: ParamsConfig = [ + { name: "key", type: "string" }, +]; + +// Children map for toast component configuration +const childrenMap = { + // Basic configuration + title: withDefault(StringControl, ""), + description: withDefault(StringControl, ""), + type: dropdownControl(toastTypeOptions, "info"), + + // Timing + duration: withDefault(NumberControl, 4.5), + + // Position & Appearance + placement: dropdownControl(placementOptions, "bottomRight"), + dismissible: withDefault(BoolControl, true), + showProgress: withDefault(BoolControl, false), + pauseOnHover: withDefault(BoolControl, true), + + // Layout + width: withDefault(StringControl, ""), + + // Event handlers + onEvent: eventHandlerControl(ToastEventOptions), + + // Style + style: styleControl(NotificationStyle), + resolvedStyle: stateComp({ + background: "", + color: "", + closeIconColor: "", + infoIconColor: "", + successIconColor: "", + warningIconColor: "", + errorIconColor: "", + border: "", + radius: "", + borderWidth: "", + borderStyle: "", + margin: "", + padding: "", + }), + + // Internal state for tracking visibility + visible: stateComp(false), + + // Unique instance ID for scoped styling (set by ToastRuntimeView) + instanceId: stateComp(""), +}; + +type ToastType = "info" | "success" | "warning" | "error"; + +// Helper function to show notification with event callbacks +const showNotificationWithEvents = ( + config: { + title: string; + description: string; + type: ToastType; + duration: number; + placement: NotificationPlacement; + dismissible: boolean; + showProgress: boolean; + pauseOnHover: boolean; + key?: string; + styleConfig?: NotificationStyleType; + style?: React.CSSProperties; + instanceId: string; + }, + onEvent: (eventName: "click" | "close") => Promise, + setVisible: (visible: boolean) => void +) => { + const notificationKey = config.key || `toast-${Date.now()}`; + + const notificationArgs: ArgsProps = { + message: config.title, + description: config.description || undefined, + duration: config.duration === 0 ? null : config.duration, + key: notificationKey, + placement: config.placement, + closeIcon: config.dismissible ? undefined : false, + showProgress: config.showProgress, + pauseOnHover: config.pauseOnHover, + className: `lowcoder-toast-${config.instanceId}`, + style: config.style, + onClick: () => { + onEvent("click"); + }, + onClose: () => { + setVisible(false); + onEvent("close"); + }, + }; + + // Show notification based on type + if (config.title || config.description) { + setVisible(true); + notificationInstance[config.type](notificationArgs); + } + + return notificationKey; +}; + +// Helper for programmatic API (backwards compatible) +const showNotificationProgrammatic = ( + params: EvalParamType[], + level: ToastType, + comp: any +) => { + const text = params?.[0] as string; + const options = (params?.[1] as JSONObject) || {}; + + const { + description, + duration, + key, + placement, + dismissible, + showProgress, + pauseOnHover, + style, + } = options; + + // Use component config as defaults, override with params + const config = { + title: text || comp.children.title.getView(), + description: (description as string) ?? comp.children.description.getView(), + type: level, + duration: typeof duration === "number" ? duration : comp.children.duration.getView(), + placement: (placement as NotificationPlacement) ?? comp.children.placement.getView(), + dismissible: typeof dismissible === "boolean" ? dismissible : comp.children.dismissible.getView(), + showProgress: typeof showProgress === "boolean" ? showProgress : comp.children.showProgress.getView(), + pauseOnHover: typeof pauseOnHover === "boolean" ? pauseOnHover : comp.children.pauseOnHover.getView(), + key: key as string | undefined, + styleConfig: comp.children.resolvedStyle.getView() as NotificationStyleType, + style: style as React.CSSProperties | undefined, + instanceId: comp.children.instanceId.getView() as string, + }; + + const onEvent = comp.children.onEvent.getView(); + const setVisible = (visible: boolean) => { + comp.children.visible.dispatchChangeValueAction(visible); + }; + + return showNotificationWithEvents(config, onEvent, setVisible); +}; + +// Property view component +const ToastPropertyView = React.memo((props: { comp: any }) => { + const { comp } = props; + + return ( + <> +
+ {comp.children.title.propertyView({ + label: trans("toastComp.title"), + placeholder: trans("toastComp.titlePlaceholder"), + })} + {comp.children.description.propertyView({ + label: trans("toastComp.description"), + placeholder: trans("toastComp.descriptionPlaceholder"), + })} + {comp.children.type.propertyView({ + label: trans("toastComp.type"), + })} +
+ +
+ {comp.children.duration.propertyView({ + label: trans("toastComp.duration"), + tooltip: trans("toastComp.durationTooltip"), + placeholder: "4.5", + })} + {comp.children.placement.propertyView({ + label: trans("toastComp.placement"), + })} + {comp.children.dismissible.propertyView({ + label: trans("toastComp.dismissible"), + })} + {comp.children.showProgress.propertyView({ + label: trans("toastComp.showProgress"), + tooltip: trans("toastComp.showProgressTooltip"), + })} + {comp.children.pauseOnHover.propertyView({ + label: trans("toastComp.pauseOnHover"), + })} +
+ +
+ {comp.children.width.propertyView({ + label: trans("toastComp.width"), + tooltip: trans("toastComp.widthTooltip"), + placeholder: "384px or 100vw", + })} +
+ +
+ {comp.children.onEvent.getPropertyView()} +
+ +
+ {comp.children.style.getPropertyView()} +
+ + ); +}); + +ToastPropertyView.displayName = "ToastPropertyView"; + +/** + * Toast runtime view + */ +const ToastRuntimeView = React.memo((props: { comp: any }) => { + const { comp } = props; + const style = comp.children.style.getView() as NotificationStyleType; + const width = comp.children.width.getView() as string; + const instanceId = useId().replace(/:/g, '-'); + + // Store instance ID and resolved styles + useEffect(() => { + comp.children.instanceId.dispatchChangeValueAction(instanceId); + }, [comp, instanceId]); + + useEffect(() => { + const current = comp.children.resolvedStyle.getView() as NotificationStyleType; + if (!isEqual(style, current)) { + comp.children.resolvedStyle.dispatchChangeValueAction(style); + } + }, [comp, style]); + + return ( + + ); +}); + +ToastRuntimeView.displayName = "ToastRuntimeView"; + +// Build the component +let ToastCompBase = simpleMultiComp(childrenMap); + +ToastCompBase = withViewFn(ToastCompBase, (comp) => ); + +ToastCompBase = withPropertyViewFn(ToastCompBase, (comp) => ( + +)); + +// Add exposing configs +let ToastCompWithExposing = withExposingConfigs(ToastCompBase, [ + new NameConfig("visible", trans("toastComp.visibleDesc")), + new NameConfig("title", trans("toastComp.titleDesc")), + new NameConfig("description", trans("toastComp.descriptionDesc")), + new NameConfig("type", trans("toastComp.typeDesc")), + new NameConfig("duration", trans("toastComp.durationDesc")), + new NameConfig("placement", trans("toastComp.placementDesc")), + new NameConfig("width", trans("toastComp.widthDesc")), +]); + +// Add method exposing +export let ToastComp = withMethodExposing(ToastCompWithExposing, [ + { + method: { + name: "show", + description: trans("toastComp.showMethod"), + params: [], + }, + execute: (comp) => { + const config = { + title: comp.children.title.getView(), + description: comp.children.description.getView(), + type: comp.children.type.getView() as ToastType, + duration: comp.children.duration.getView(), + placement: comp.children.placement.getView() as NotificationPlacement, + dismissible: comp.children.dismissible.getView(), + showProgress: comp.children.showProgress.getView(), + pauseOnHover: comp.children.pauseOnHover.getView(), + styleConfig: comp.children.resolvedStyle.getView() as NotificationStyleType, + instanceId: comp.children.instanceId.getView() as string, + }; + + const onEvent = comp.children.onEvent.getView(); + const setVisible = (visible: boolean) => { + comp.children.visible.dispatchChangeValueAction(visible); + }; + + showNotificationWithEvents(config, onEvent, setVisible); + }, + }, + { + method: { + name: "info", + description: trans("toastComp.info"), + params: showParams, + }, + execute: (comp, params) => showNotificationProgrammatic(params, "info", comp), + }, + { + method: { + name: "success", + description: trans("toastComp.success"), + params: showParams, + }, + execute: (comp, params) => showNotificationProgrammatic(params, "success", comp), + }, + { + method: { + name: "warn", + description: trans("toastComp.warn"), + params: showParams, + }, + execute: (comp, params) => showNotificationProgrammatic(params, "warning", comp), + }, + { + method: { + name: "error", + description: trans("toastComp.error"), + params: showParams, + }, + execute: (comp, params) => showNotificationProgrammatic(params, "error", comp), + }, + { + method: { + name: "close", + description: trans("toastComp.closeMethod"), + params: closeParams, + }, + execute: (comp, params) => { + const key = params?.[0] as string; + if (key) { + notificationInstance.destroy(key); + } + comp.children.visible.dispatchChangeValueAction(false); + comp.children.onEvent.getView()("close"); + }, + }, + // Legacy method for backwards compatibility + { + method: { + name: "destroy", + description: trans("toastComp.destroy"), + params: closeParams, + }, + execute: (comp, params) => { + const key = params?.[0] as string; + notificationInstance.destroy(key); + }, + }, + { + method: { + name: "open", + description: trans("toastComp.openMethod"), + params: showParams, + }, + execute: (comp, params) => showNotificationProgrammatic(params, "info", comp), + }, +]); diff --git a/client/packages/lowcoder/src/comps/index.tsx b/client/packages/lowcoder/src/comps/index.tsx index 0bbf0b731..2f07d21f6 100644 --- a/client/packages/lowcoder/src/comps/index.tsx +++ b/client/packages/lowcoder/src/comps/index.tsx @@ -193,6 +193,7 @@ import { TreeComp } from "./comps/treeComp/treeComp"; import { TreeSelectComp } from "./comps/treeComp/treeSelectComp"; import { DrawerComp } from "./hooks/drawerComp"; import { ModalComp } from "./hooks/modalComp"; +import { ToastComp } from "./hooks/toastComp"; import { defaultCollapsibleContainerData } from "./comps/containerComp/collapsibleContainerComp"; import { ContainerComp as FloatTextContainerComp } from "./comps/containerComp/textContainerComp"; import { MultiTagsComp } from "./comps/tagsComp/tagsCompView"; @@ -761,6 +762,16 @@ export var uiCompMap: Registry = { comp: DrawerComp, withoutLoading: true, }, + toast: { + name: trans("uiComp.toastCompName"), + enName: "Toast", + description: trans("uiComp.toastCompDesc"), + categories: ["layout"], + icon: CommentCompIcon, + keywords: trans("uiComp.toastCompKeywords"), + comp: ToastComp, + withoutLoading: true, + }, divider: { name: trans("uiComp.dividerCompName"), enName: "Divider", diff --git a/client/packages/lowcoder/src/comps/uiCompRegistry.ts b/client/packages/lowcoder/src/comps/uiCompRegistry.ts index f8e09763c..ef37a4179 100644 --- a/client/packages/lowcoder/src/comps/uiCompRegistry.ts +++ b/client/packages/lowcoder/src/comps/uiCompRegistry.ts @@ -110,6 +110,7 @@ export type UICompType = | "multiTags" // Added by Kamal Qureshi | "tabbedContainer" | "modal" + | "toast" | "listView" | "grid" | "navigation" diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 8cbc26404..09cc320b7 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -1258,6 +1258,10 @@ export const en = { "drawerCompDesc": "A sliding panel component that can be used for additional navigation or content display, typically emerging from the edge of the screen.", "drawerCompKeywords": "drawer, sliding, panel, navigation", + "toastCompName": "Toast", + "toastCompDesc": "A notification component for displaying brief messages, alerts, or feedback to users. Supports click and close event handlers.", + "toastCompKeywords": "toast, notification, alert, message, snackbar", + "chartCompName": "Chart (deprecated)", "chartCompDesc": "A versatile component for visualizing data through various types of charts and graphs.", "chartCompKeywords": "chart, graph, data, visualization", @@ -3271,12 +3275,64 @@ export const en = { "error": "Send an Error Notification" }, "toastComp": { - "destroy": "close a Notification", - "info": "Send a Notification", - "loading": "Send a Loading Notification", - "success": "Send a Success Notification", - "warn": "Send a Warning Notification", - "error": "Send an Error Notification" + // Method descriptions + "destroy": "Close a notification by key", + "info": "Show an info notification", + "success": "Show a success notification", + "warn": "Show a warning notification", + "error": "Show an error notification", + "showMethod": "Show notification with configured settings", + "closeMethod": "Close the notification", + "openMethod": "Show an info notification (alias for info)", + + // Property labels + "title": "Title", + "titlePlaceholder": "Notification title", + "description": "Description", + "descriptionPlaceholder": "Notification description", + "type": "Type", + "duration": "Duration (seconds)", + "durationTooltip": "Time in seconds before auto-close. Set to 0 to disable auto-close.", + "placement": "Placement", + "dismissible": "Show Close Button", + "showProgress": "Show Progress Bar", + "showProgressTooltip": "Display a progress bar indicating time until auto-close", + "pauseOnHover": "Pause on Hover", + "behavior": "Behavior", + "width": "Width", + "widthTooltip": "Width of the notification in pixels, percentages, or other CSS units. if you want to adjust it according to the screen size, you can use viewport units. Example: 100vw", + "closeIconColor": "Close Icon Color", + "infoIconColor": "Info Icon Color", + "successIconColor": "Success Icon Color", + "warningIconColor": "Warning Icon Color", + "errorIconColor": "Error Icon Color", + + // Type options + "typeInfo": "Info", + "typeSuccess": "Success", + "typeWarning": "Warning", + "typeError": "Error", + + // Placement options + "placementTopLeft": "Top Left", + "placementTopRight": "Top Right", + "placementBottomLeft": "Bottom Left", + "placementBottomRight": "Bottom Right", + + // Event labels + "click": "Click", + "clickDesc": "Triggered when the notification is clicked", + "close": "Close", + "closeDesc": "Triggered when the notification is closed or dismissed", + + // Exposed state descriptions + "visibleDesc": "Whether the notification is currently visible", + "titleDesc": "The configured title of the notification", + "descriptionDesc": "The configured description of the notification", + "typeDesc": "The configured type (info, success, warning, error)", + "durationDesc": "The configured duration in seconds", + "placementDesc": "The configured placement position", + "widthDesc": "The configured width of the notification" }, "themeComp": { "switchTo": "Switch Theme" diff --git a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx index 54cd5faaf..ebdc14503 100644 --- a/client/packages/lowcoder/src/pages/editor/editorConstants.tsx +++ b/client/packages/lowcoder/src/pages/editor/editorConstants.tsx @@ -222,6 +222,7 @@ export const CompStateIcon: { mention: , mermaid: , modal: , + toast: , module: , moduleContainer: , navigation: ,