diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..39649ae48 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,7 @@ +{ + "workbench.colorCustomizations": { + "activityBar.background": "#2A3012", + "titleBar.activeBackground": "#3B431A", + "titleBar.activeForeground": "#F9FAF2" + } +} \ No newline at end of file diff --git a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx index dd2a6b1dc..8002436e1 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookComp.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookComp.tsx @@ -29,6 +29,7 @@ import { useInterval, useTitle, useWindowSize } from "react-use"; import { useCurrentUser } from "util/currentUser"; import { LocalStorageComp } from "./localStorageComp"; import { MessageComp } from "./messageComp"; +import { ToastComp } from "./toastComp"; import { ThemeComp } from "./themeComp"; import UrlParamsHookComp from "./UrlParamsHookComp"; import { UtilsComp } from "./utilsComp"; @@ -94,6 +95,7 @@ const HookMap: HookCompMapRawType = { momentJsLib: DayJsLib, // old components use this hook utils: UtilsComp, message: MessageComp, + toast: ToastComp, localStorage: LocalStorageComp, modal: ModalComp, meeting: VideoMeetingControllerComp, diff --git a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx index a985a8e72..617f2f6c1 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookCompTypes.tsx @@ -12,6 +12,7 @@ const AllHookComp = [ "momentJsLib", "utils", "message", + "toast", "localStorage", "currentUser", "screenInfo", @@ -58,6 +59,7 @@ const HookCompConfig: Record< }, utils: { category: "hide" }, message: { category: "hide" }, + toast: { category: "hide" }, }; // Get hook component category diff --git a/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx b/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx index 3f28e2d1a..7b267ec19 100644 --- a/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx +++ b/client/packages/lowcoder/src/comps/hooks/hookListComp.tsx @@ -19,6 +19,7 @@ const defaultHookListValue = [ { compType: "lodashJsLib", name: "_" }, { compType: "utils", name: "utils" }, { compType: "message", name: "message" }, + { compType: "toast", name: "toast" }, { compType: "localStorage", name: "localStorage" }, { compType: "currentUser", name: "currentUser" }, { compType: "screenInfo", name: "screenInfo" }, diff --git a/client/packages/lowcoder/src/comps/hooks/messageComp.ts b/client/packages/lowcoder/src/comps/hooks/messageComp.ts index e0e6451cb..028c37691 100644 --- a/client/packages/lowcoder/src/comps/hooks/messageComp.ts +++ b/client/packages/lowcoder/src/comps/hooks/messageComp.ts @@ -11,7 +11,7 @@ const params: ParamsConfig = [ { name: "options", type: "JSON" }, ]; -const showMessage = (params: EvalParamType[], level: "info" | "success" | "warning" | "error") => { +const showMessage = (params: EvalParamType[], level: "info" | "success" | "loading" | "warning" | "error") => { const text = params?.[0]; const options = params?.[1] as JSONObject; const duration = options?.["duration"] ?? 3; @@ -35,6 +35,12 @@ MessageComp = withMethodExposing(MessageComp, [ showMessage(params, "success"); }, }, + { + method: { name: "loading", description: trans("messageComp.loading"), params: params }, + execute: (comp, params) => { + showMessage(params, "loading"); + }, + }, { method: { name: "warn", description: trans("messageComp.warn"), params: params }, execute: (comp, params) => { diff --git a/client/packages/lowcoder/src/comps/hooks/toastComp.ts b/client/packages/lowcoder/src/comps/hooks/toastComp.ts new file mode 100644 index 000000000..8b80e89b9 --- /dev/null +++ b/client/packages/lowcoder/src/comps/hooks/toastComp.ts @@ -0,0 +1,95 @@ +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/i18n/locales/de.ts b/client/packages/lowcoder/src/i18n/locales/de.ts index a6bcf9f00..feda98419 100644 --- a/client/packages/lowcoder/src/i18n/locales/de.ts +++ b/client/packages/lowcoder/src/i18n/locales/de.ts @@ -1615,10 +1615,18 @@ export const de = { }, "messageComp": { "info": "Eine Benachrichtigung senden", + "loading": "Ladebestätigung senden", "success": "Erfolgsbenachrichtigung senden", "warn": "Eine Warnmeldung senden", "error": "Eine Fehlerbenachrichtigung senden" }, + "tostComp": { + "info": "Eine Benachrichtigung senden", + "loading": "Ladebestätigung senden", + "success": "Erfolgsbenachrichtigung senden", + "warn": "Eine Warnmeldung senden", + "error": "Eine Fehlerbenachrichtigung senden" +}, "themeComp": { "switchTo": "Thema wechseln" }, diff --git a/client/packages/lowcoder/src/i18n/locales/en.ts b/client/packages/lowcoder/src/i18n/locales/en.ts index 7e97eaad9..2ac3be091 100644 --- a/client/packages/lowcoder/src/i18n/locales/en.ts +++ b/client/packages/lowcoder/src/i18n/locales/en.ts @@ -1778,6 +1778,15 @@ export const en = { }, "messageComp": { "info": "Send a Notification", + "loading": "Send a Loading Notification", + "success": "Send a Success Notification", + "warn": "Send a Warning Notification", + "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" diff --git a/client/packages/lowcoder/src/i18n/locales/zh.ts b/client/packages/lowcoder/src/i18n/locales/zh.ts index e50500fb4..921a09e14 100644 --- a/client/packages/lowcoder/src/i18n/locales/zh.ts +++ b/client/packages/lowcoder/src/i18n/locales/zh.ts @@ -1686,6 +1686,14 @@ utilsComp: { }, messageComp: { info: "发送通知", + loading: "发送加载通知", + success: "发送成功通知", + warn: "发送警告通知", + error: "发送错误通知", +}, +toastComp: { + info: "发送通知", + loading: "发送加载通知", success: "发送成功通知", warn: "发送警告通知", error: "发送错误通知", diff --git a/docs/.gitbook/assets/builtin-js-toasts.png b/docs/.gitbook/assets/builtin-js-toasts.png new file mode 100644 index 000000000..cfaf58332 Binary files /dev/null and b/docs/.gitbook/assets/builtin-js-toasts.png differ diff --git a/docs/build-apps/write-javascript/built-in-javascript-functions.md b/docs/build-apps/write-javascript/built-in-javascript-functions.md index 757c30511..0bbbb6db0 100644 --- a/docs/build-apps/write-javascript/built-in-javascript-functions.md +++ b/docs/build-apps/write-javascript/built-in-javascript-functions.md @@ -109,18 +109,45 @@ utils.copyToClipboard( input1.value ) Use `message` methods to send a global alert notification, which displays at the top of the screen and lasts for 3 seconds by default. Each of the following four methods supports a unique display style. ```javascript -// messageInstance.info( text: string, options?: {duration: number = 3 } ) -messageInstance.info("Please confirm your information", { duration: 10 }) -// messageInstance.success( text: string, options?: {duration: number = 3 } ) -messageInstance.success("Query runs successfully", { duration: 10 }) -// messageInstance.warning( text: string, options?: {duration: number = 3 } ) -messageInstance.warning("Warning", { duration: 10 }) -// messageInstance.error( text: string, options?: {duration: number = 3 } ) -messageInstance.error("Query runs with error", { duration: 10 }) +// message.info( text: string, options?: {duration: number = 3 } ) +message.info("Please confirm your information", { duration: 10 }) +// message.loading( text: string, options?: {duration: number = 3 } ) +message.loading("Query is running", { duration: 5 }) +// message.success( text: string, options?: {duration: number = 3 } ) +message.success("Query runs successfully", { duration: 10 }) +// message.warning( text: string, options?: {duration: number = 3 } ) +message.warning("Warning", { duration: 10 }) +// message.error( text: string, options?: {duration: number = 3 } ) +message.error("Query runs with error", { duration: 10 }) ```
+## toast - dismissible stack-able notifications + +Use `toast` methods to send a notification, which displays at the top of the screen and lasts for 3 seconds by default. Each of the following five methods supports a unique display style. After 3 toasts they will be stacked. + +The id field can be used to update previous toasts. Or used to destroy the previous toast. + +Destroy function used without an id will remove all toast. + +```javascript +// toast.open( title: string, options?: { message?: string, duration?: number = 3, id?: string, placement?: "top" | "topLeft" | "topRight" | "bottom" | "bottomRight", "bottomRight" = "bottomRight", dismissible?: boolean = true } ) +toast.open("This Is a Notification", {message: "I do not go away automatically.", duration: 0}) +// toast.info( title: string, options?: { message?: string, duration?: number = 3, id?: string, placement?: "top" | "topLeft" | "topRight" | "bottom" | "bottomRight", "bottomRight" = "bottomRight", dismissible?: boolean = true } ) +toast.info("Order #1519", {message: "Shipped out on Tuesday, Jan 3rd.", duration: 5}) +// toast.success( title: string, options?: { message?: string, duration?: number = 3, id?: string, placement?: "top" | "topLeft" | "topRight" | "bottom" | "bottomRight", "bottomRight" = "bottomRight", dismissible?: boolean = true } ) +toast.success("Query runs successfully", { duration: 10 }) +// toast.warn( title: string, options?: { message?: string, duration?: number = 3, id?: string, placement?: "top" | "topLeft" | "topRight" | "bottom" | "bottomRight", "bottomRight" = "bottomRight", dismissible?: boolean = true } ) +toast.warn("Duplicate Action", {message: "The email was previously sent on Jan 3rd. Click the button again to send.", duration: 5}) +// toast.error( title: string, options?: { message?: string, duration?: number = 3, id?: string, placement?: "top" | "topLeft" | "topRight" | "bottom" | "bottomRight", "bottomRight" = "bottomRight", dismissible?: boolean = true } ) +toast.error("Your credentials were invalid", {message: "You have 5 tries left", duration: 5}) +//toast.destroy(id?: string) +toast.destroy() +``` + +
+ ## localStorage Use `localStorage` methods to store and manage key-value pair data locally, which is not reset when the app refreshes, and can be accessed in any app within the workspace using `localStorage.values`.