@lobehub/ui 5.6.5 → 5.7.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/es/DraggablePanel/DraggablePanel.mjs +309 -150
- package/es/DraggablePanel/DraggablePanel.mjs.map +1 -1
- package/es/DraggablePanel/type.d.mts +5 -0
- package/es/Modal/Modal.mjs +1 -1
- package/es/Modal/Modal.mjs.map +1 -1
- package/es/Modal/ModalStackItem.mjs +1 -1
- package/es/Modal/ModalStackItem.mjs.map +1 -1
- package/es/Modal/RawModalStackItem.mjs +1 -1
- package/es/Modal/RawModalStackItem.mjs.map +1 -1
- package/es/Modal/imperative.mjs +2 -2
- package/es/Modal/imperative.mjs.map +1 -1
- package/es/base-ui/Modal/Modal.mjs +17 -4
- package/es/base-ui/Modal/Modal.mjs.map +1 -1
- package/es/base-ui/Modal/imperative.mjs +24 -5
- package/es/base-ui/Modal/imperative.mjs.map +1 -1
- package/es/base-ui/Modal/zIndexManager.mjs +10 -0
- package/es/base-ui/Modal/zIndexManager.mjs.map +1 -0
- package/es/hooks/useMermaid.mjs +6 -5
- package/es/hooks/useMermaid.mjs.map +1 -1
- package/es/hooks/useStreamHighlight.mjs +1 -2
- package/es/hooks/useStreamHighlight.mjs.map +1 -1
- package/es/utils/blobToPng.mjs +1 -2
- package/es/utils/blobToPng.mjs.map +1 -1
- package/package.json +1 -1
|
@@ -38,6 +38,11 @@ interface DraggablePanelProps extends DivProps {
|
|
|
38
38
|
showHandleWhenCollapsed?: boolean;
|
|
39
39
|
showHandleWideArea?: boolean;
|
|
40
40
|
size?: Partial<Size>;
|
|
41
|
+
/**
|
|
42
|
+
* Use two-layer container layout to keep content layout stable when collapsed/expanded.
|
|
43
|
+
* @default false
|
|
44
|
+
*/
|
|
45
|
+
stableLayout?: boolean;
|
|
41
46
|
styles?: {
|
|
42
47
|
content?: CSSProperties;
|
|
43
48
|
handle?: CSSProperties;
|
package/es/Modal/Modal.mjs
CHANGED
|
@@ -65,7 +65,6 @@ const Modal$1 = memo(({ panelRef, allowFullscreen, children, title = " ", classN
|
|
|
65
65
|
theme: { token: { colorBgElevated: cssVar.colorBgContainer } },
|
|
66
66
|
children: /* @__PURE__ */ jsx(Modal, {
|
|
67
67
|
closable: true,
|
|
68
|
-
maskClosable: true,
|
|
69
68
|
cancelText,
|
|
70
69
|
className: cx(styles.content, className),
|
|
71
70
|
closeIcon: /* @__PURE__ */ jsx(Icon, {
|
|
@@ -75,6 +74,7 @@ const Modal$1 = memo(({ panelRef, allowFullscreen, children, title = " ", classN
|
|
|
75
74
|
confirmLoading,
|
|
76
75
|
destroyOnHidden,
|
|
77
76
|
footer: hideFooter ? null : footer,
|
|
77
|
+
mask: { closable: true },
|
|
78
78
|
okButtonProps,
|
|
79
79
|
okText,
|
|
80
80
|
open,
|
package/es/Modal/Modal.mjs.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.mjs","names":["Modal","AntModal"],"sources":["../../src/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { Button, ConfigProvider, Drawer, Modal as AntModal } from 'antd';\nimport { cssVar, cx, useResponsive } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { memo, type ReactNode, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport { type ModalProps } from './type';\n\nconst Modal = memo<ModalProps>(\n ({\n panelRef,\n allowFullscreen,\n children,\n title = ' ',\n className,\n classNames,\n width = 700,\n onCancel,\n open,\n destroyOnHidden,\n paddings,\n height = '75dvh',\n enableResponsive = true,\n footer,\n styles: customStyles,\n okText,\n onOk,\n cancelText,\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n ...rest\n }) => {\n const [fullscreen, setFullscreen] = useState(false);\n const { mobile } = useResponsive();\n const hideFooter = footer === false || footer === null;\n if (enableResponsive && mobile)\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <Drawer\n className={cx(styles.drawerContent, className)}\n closeIcon={<ActionIcon icon={X} />}\n destroyOnHidden={destroyOnHidden}\n height={fullscreen ? 'calc(100% - env(safe-area-inset-top))' : height}\n open={open}\n panelRef={panelRef}\n placement={'bottom'}\n title={title}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n extra={\n allowFullscreen && (\n <ActionIcon\n icon={fullscreen ? Minimize2 : Maximize2}\n onClick={() => setFullscreen(!fullscreen)}\n />\n )\n }\n footer={\n hideFooter\n ? null\n : (footer as ReactNode) || (\n <>\n <Button\n color={'default'}\n variant={'filled'}\n onClick={onCancel as any}\n {...cancelButtonProps}\n >\n {cancelText || 'Cancel'}\n </Button>\n <Button\n loading={confirmLoading}\n type=\"primary\"\n onClick={onOk as any}\n {...okButtonProps}\n style={{\n marginInlineStart: 8,\n ...okButtonProps?.style,\n }}\n >\n {okText || 'OK'}\n </Button>\n </>\n )\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n paddingBlock: `16px ${footer ? 0 : '16px'}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onClose={onCancel as any}\n {...rest}\n >\n {children}\n </Drawer>\n </ConfigProvider>\n );\n\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <AntModal\n closable\n
|
|
1
|
+
{"version":3,"file":"Modal.mjs","names":["Modal","AntModal"],"sources":["../../src/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { Button, ConfigProvider, Drawer, Modal as AntModal } from 'antd';\nimport { cssVar, cx, useResponsive } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { memo, type ReactNode, useState } from 'react';\n\nimport ActionIcon from '@/ActionIcon';\nimport Icon from '@/Icon';\n\nimport { styles } from './style';\nimport { type ModalProps } from './type';\n\nconst Modal = memo<ModalProps>(\n ({\n panelRef,\n allowFullscreen,\n children,\n title = ' ',\n className,\n classNames,\n width = 700,\n onCancel,\n open,\n destroyOnHidden,\n paddings,\n height = '75dvh',\n enableResponsive = true,\n footer,\n styles: customStyles,\n okText,\n onOk,\n cancelText,\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n ...rest\n }) => {\n const [fullscreen, setFullscreen] = useState(false);\n const { mobile } = useResponsive();\n const hideFooter = footer === false || footer === null;\n if (enableResponsive && mobile)\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <Drawer\n className={cx(styles.drawerContent, className)}\n closeIcon={<ActionIcon icon={X} />}\n destroyOnHidden={destroyOnHidden}\n height={fullscreen ? 'calc(100% - env(safe-area-inset-top))' : height}\n open={open}\n panelRef={panelRef}\n placement={'bottom'}\n title={title}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n extra={\n allowFullscreen && (\n <ActionIcon\n icon={fullscreen ? Minimize2 : Maximize2}\n onClick={() => setFullscreen(!fullscreen)}\n />\n )\n }\n footer={\n hideFooter\n ? null\n : (footer as ReactNode) || (\n <>\n <Button\n color={'default'}\n variant={'filled'}\n onClick={onCancel as any}\n {...cancelButtonProps}\n >\n {cancelText || 'Cancel'}\n </Button>\n <Button\n loading={confirmLoading}\n type=\"primary\"\n onClick={onOk as any}\n {...okButtonProps}\n style={{\n marginInlineStart: 8,\n ...okButtonProps?.style,\n }}\n >\n {okText || 'OK'}\n </Button>\n </>\n )\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n paddingBlock: `16px ${footer ? 0 : '16px'}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onClose={onCancel as any}\n {...rest}\n >\n {children}\n </Drawer>\n </ConfigProvider>\n );\n\n return (\n <ConfigProvider\n theme={{\n token: {\n colorBgElevated: cssVar.colorBgContainer,\n },\n }}\n >\n <AntModal\n closable\n cancelText={cancelText}\n className={cx(styles.content, className)}\n closeIcon={<Icon icon={X} size={20} />}\n confirmLoading={confirmLoading}\n destroyOnHidden={destroyOnHidden}\n footer={hideFooter ? null : footer}\n mask={{ closable: true }}\n okButtonProps={okButtonProps}\n okText={okText}\n open={open}\n panelRef={panelRef}\n title={title}\n width={width}\n cancelButtonProps={{\n color: 'default',\n variant: 'filled',\n ...cancelButtonProps,\n }}\n classNames={\n typeof classNames === 'function'\n ? classNames\n : {\n ...classNames,\n wrapper: cx(styles.wrap, classNames?.wrapper),\n }\n }\n styles={\n typeof customStyles === 'function'\n ? customStyles\n : {\n ...customStyles,\n body: {\n maxHeight: height,\n overflow: 'hidden auto',\n paddingBlock: `0 ${footer === null ? '16px' : 0}`,\n paddingInline: paddings?.desktop ?? 16,\n ...customStyles?.body,\n },\n }\n }\n onCancel={onCancel}\n onOk={onOk}\n {...rest}\n >\n {children}\n </AntModal>\n </ConfigProvider>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"mappings":";;;;;;;;;;AAaA,MAAMA,UAAQ,MACX,EACC,UACA,iBACA,UACA,QAAQ,KACR,WACA,YACA,QAAQ,KACR,UACA,MACA,iBACA,UACA,SAAS,SACT,mBAAmB,MACnB,QACA,QAAQ,cACR,QACA,MACA,YACA,eACA,mBACA,gBACA,GAAG,WACC;CACJ,MAAM,CAAC,YAAY,iBAAiB,SAAS,MAAM;CACnD,MAAM,EAAE,WAAW,eAAe;CAClC,MAAM,aAAa,WAAW,SAAS,WAAW;AAClD,KAAI,oBAAoB,OACtB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAAC,QAAD;GACE,WAAW,GAAG,OAAO,eAAe,UAAU;GAC9C,WAAW,oBAAC,YAAD,EAAY,MAAM,GAAK,CAAA;GACjB;GACjB,QAAQ,aAAa,0CAA0C;GACzD;GACI;GACV,WAAW;GACJ;GACP,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,OACE,mBACE,oBAAC,YAAD;IACE,MAAM,aAAa,YAAY;IAC/B,eAAe,cAAc,CAAC,WAAW;IACzC,CAAA;GAGN,QACE,aACI,OACC,UACC,qBAAA,YAAA,EAAA,UAAA,CACE,oBAAC,QAAD;IACE,OAAO;IACP,SAAS;IACT,SAAS;IACT,GAAI;cAEH,cAAc;IACR,CAAA,EACT,oBAAC,QAAD;IACE,SAAS;IACT,MAAK;IACL,SAAS;IACT,GAAI;IACJ,OAAO;KACL,mBAAmB;KACnB,GAAG,eAAe;KACnB;cAEA,UAAU;IACJ,CAAA,CACR,EAAA,CAAA;GAGX,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,cAAc,QAAQ,SAAS,IAAI;KACnC,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEP,SAAS;GACT,GAAI;GAEH;GACM,CAAA;EACM,CAAA;AAGrB,QACE,oBAAC,gBAAD;EACE,OAAO,EACL,OAAO,EACL,iBAAiB,OAAO,kBACzB,EACF;YAED,oBAACC,OAAD;GACE,UAAA;GACY;GACZ,WAAW,GAAG,OAAO,SAAS,UAAU;GACxC,WAAW,oBAAC,MAAD;IAAM,MAAM;IAAG,MAAM;IAAM,CAAA;GACtB;GACC;GACjB,QAAQ,aAAa,OAAO;GAC5B,MAAM,EAAE,UAAU,MAAM;GACT;GACP;GACF;GACI;GACH;GACA;GACP,mBAAmB;IACjB,OAAO;IACP,SAAS;IACT,GAAG;IACJ;GACD,YACE,OAAO,eAAe,aAClB,aACA;IACE,GAAG;IACH,SAAS,GAAG,OAAO,MAAM,YAAY,QAAQ;IAC9C;GAEP,QACE,OAAO,iBAAiB,aACpB,eACA;IACE,GAAG;IACH,MAAM;KACJ,WAAW;KACX,UAAU;KACV,cAAc,KAAK,WAAW,OAAO,SAAS;KAC9C,eAAe,UAAU,WAAW;KACpC,GAAG,cAAc;KAClB;IACF;GAEG;GACJ;GACN,GAAI;GAEH;GACQ,CAAA;EACI,CAAA;EAGtB;AAED,QAAM,cAAc"}
|
|
@@ -12,7 +12,7 @@ const ModalStackItem = memo(({ id, props, onClose, onUpdate, onDestroy }) => {
|
|
|
12
12
|
const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);
|
|
13
13
|
const stableOnCancel = useEventCallback(onCancel ?? noop);
|
|
14
14
|
const close = useEventCallback(() => onClose(id));
|
|
15
|
-
const setCanDismissByClickOutside = useEventCallback((value) => onUpdate(id, {
|
|
15
|
+
const setCanDismissByClickOutside = useEventCallback((value) => onUpdate(id, { mask: { closable: value } }));
|
|
16
16
|
const stableContextValue = useMemo(() => ({
|
|
17
17
|
close,
|
|
18
18
|
setCanDismissByClickOutside
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"ModalStackItem.mjs","names":[],"sources":["../../src/Modal/ModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport Modal from './Modal';\nimport { ModalProvider } from './ModalProvider';\nimport type { ImperativeModalProps } from './type';\n\nexport type ModalStackItemProps = {\n id: string;\n onClose: (id: string) => void;\n onDestroy: (id: string) => void;\n onUpdate: (id: string, nextProps: Partial<ImperativeModalProps>) => void;\n props: ImperativeModalProps;\n};\n\nconst noop = () => {};\nexport const ModalStackItem = memo(\n ({ id, props, onClose, onUpdate, onDestroy }: ModalStackItemProps) => {\n const { afterClose, afterOpenChange, children, onCancel, open, ...rest } = props;\n const stableAfterClose = useEventCallback(afterClose ?? noop);\n const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);\n const stableOnCancel = useEventCallback(onCancel ?? noop);\n const close = useEventCallback(() => onClose(id));\n const setCanDismissByClickOutside = useEventCallback((value: boolean) =>\n onUpdate(id, {
|
|
1
|
+
{"version":3,"file":"ModalStackItem.mjs","names":[],"sources":["../../src/Modal/ModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport { memo, useCallback, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport Modal from './Modal';\nimport { ModalProvider } from './ModalProvider';\nimport type { ImperativeModalProps } from './type';\n\nexport type ModalStackItemProps = {\n id: string;\n onClose: (id: string) => void;\n onDestroy: (id: string) => void;\n onUpdate: (id: string, nextProps: Partial<ImperativeModalProps>) => void;\n props: ImperativeModalProps;\n};\n\nconst noop = () => {};\nexport const ModalStackItem = memo(\n ({ id, props, onClose, onUpdate, onDestroy }: ModalStackItemProps) => {\n const { afterClose, afterOpenChange, children, onCancel, open, ...rest } = props;\n const stableAfterClose = useEventCallback(afterClose ?? noop);\n const stableAfterOpenChange = useEventCallback(afterOpenChange ?? noop);\n const stableOnCancel = useEventCallback(onCancel ?? noop);\n const close = useEventCallback(() => onClose(id));\n const setCanDismissByClickOutside = useEventCallback((value: boolean) =>\n onUpdate(id, { mask: { closable: value } }),\n );\n const stableContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n\n return (\n <Modal\n {...rest}\n open={open ?? true}\n afterClose={useCallback(() => {\n stableAfterClose?.();\n onDestroy(id);\n }, [stableAfterClose, onDestroy, id])}\n afterOpenChange={useCallback(\n (nextOpen: boolean) => {\n stableAfterOpenChange?.(nextOpen);\n if (!nextOpen) onDestroy(id);\n },\n [stableAfterOpenChange, onDestroy, id],\n )}\n onCancel={useCallback(\n (e: React.MouseEvent<HTMLButtonElement> | React.KeyboardEvent<HTMLElement>) => {\n stableOnCancel?.(e);\n close();\n },\n [stableOnCancel, close],\n )}\n >\n <ModalProvider value={stableContextValue}>{children}</ModalProvider>\n </Modal>\n );\n },\n);\n\nModalStackItem.displayName = 'ModalStackItem';\n"],"mappings":";;;;;;;AAkBA,MAAM,aAAa;AACnB,MAAa,iBAAiB,MAC3B,EAAE,IAAI,OAAO,SAAS,UAAU,gBAAqC;CACpE,MAAM,EAAE,YAAY,iBAAiB,UAAU,UAAU,MAAM,GAAG,SAAS;CAC3E,MAAM,mBAAmB,iBAAiB,cAAc,KAAK;CAC7D,MAAM,wBAAwB,iBAAiB,mBAAmB,KAAK;CACvE,MAAM,iBAAiB,iBAAiB,YAAY,KAAK;CACzD,MAAM,QAAQ,uBAAuB,QAAQ,GAAG,CAAC;CACjD,MAAM,8BAA8B,kBAAkB,UACpD,SAAS,IAAI,EAAE,MAAM,EAAE,UAAU,OAAO,EAAE,CAAC,CAC5C;CACD,MAAM,qBAAqB,eAClB;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;AAED,QACE,oBAAC,OAAD;EACE,GAAI;EACJ,MAAM,QAAQ;EACd,YAAY,kBAAkB;AAC5B,uBAAoB;AACpB,aAAU,GAAG;KACZ;GAAC;GAAkB;GAAW;GAAG,CAAC;EACrC,iBAAiB,aACd,aAAsB;AACrB,2BAAwB,SAAS;AACjC,OAAI,CAAC,SAAU,WAAU,GAAG;KAE9B;GAAC;GAAuB;GAAW;GAAG,CACvC;EACD,UAAU,aACP,MAA8E;AAC7E,oBAAiB,EAAE;AACnB,UAAO;KAET,CAAC,gBAAgB,MAAM,CACxB;YAED,oBAAC,eAAD;GAAe,OAAO;GAAqB;GAAyB,CAAA;EAC9D,CAAA;EAGb;AAED,eAAe,cAAc"}
|
|
@@ -8,7 +8,7 @@ const RawModalStackItem = memo(({ component: Component, id, onClose, onUpdate, o
|
|
|
8
8
|
const stableOnClose = useEventCallback(onClose);
|
|
9
9
|
const close = useEventCallback(() => stableOnClose(id));
|
|
10
10
|
const setCanDismissByClickOutside = useEventCallback((value) => {
|
|
11
|
-
onUpdate(id, {
|
|
11
|
+
onUpdate(id, { mask: { closable: value } });
|
|
12
12
|
});
|
|
13
13
|
const contextValue = useMemo(() => ({
|
|
14
14
|
close,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RawModalStackItem.mjs","names":[],"sources":["../../src/Modal/RawModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport { memo, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport { ModalProvider } from './ModalProvider';\nimport type { ModalContextValue, RawModalOptions } from './type';\n\nexport type RawModalStackItemProps = {\n component: ComponentType<any>;\n id: string;\n onClose: (id: string) => void;\n onUpdate: (id: string, nextProps: Record<string, unknown>) => void;\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\nexport const RawModalStackItem = memo(\n ({\n component: Component,\n id,\n onClose,\n onUpdate,\n open,\n options,\n props,\n }: RawModalStackItemProps) => {\n const stableOnClose = useEventCallback(onClose);\n const close = useEventCallback(() => stableOnClose(id));\n\n const setCanDismissByClickOutside = useEventCallback((value: boolean) => {\n onUpdate(id, {
|
|
1
|
+
{"version":3,"file":"RawModalStackItem.mjs","names":[],"sources":["../../src/Modal/RawModalStackItem.tsx"],"sourcesContent":["'use client';\n\nimport type { ComponentType } from 'react';\nimport { memo, useMemo } from 'react';\n\nimport { useEventCallback } from '@/hooks/useEventCallback';\n\nimport { ModalProvider } from './ModalProvider';\nimport type { ModalContextValue, RawModalOptions } from './type';\n\nexport type RawModalStackItemProps = {\n component: ComponentType<any>;\n id: string;\n onClose: (id: string) => void;\n onUpdate: (id: string, nextProps: Record<string, unknown>) => void;\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\nexport const RawModalStackItem = memo(\n ({\n component: Component,\n id,\n onClose,\n onUpdate,\n open,\n options,\n props,\n }: RawModalStackItemProps) => {\n const stableOnClose = useEventCallback(onClose);\n const close = useEventCallback(() => stableOnClose(id));\n\n const setCanDismissByClickOutside = useEventCallback((value: boolean) => {\n onUpdate(id, { mask: { closable: value } });\n });\n const contextValue: ModalContextValue = useMemo(\n () => ({ close, setCanDismissByClickOutside }),\n [close, setCanDismissByClickOutside],\n );\n const openKey = options?.openKey ?? 'open';\n const onCloseKey = options?.onCloseKey ?? 'onClose';\n const injectedProps = {\n ...props,\n [onCloseKey]: close,\n [openKey]: open,\n };\n\n return (\n <ModalProvider value={contextValue}>\n <Component {...injectedProps} />\n </ModalProvider>\n );\n },\n);\n\nRawModalStackItem.displayName = 'RawModalStackItem';\n"],"mappings":";;;;;;AAoBA,MAAa,oBAAoB,MAC9B,EACC,WAAW,WACX,IACA,SACA,UACA,MACA,SACA,YAC4B;CAC5B,MAAM,gBAAgB,iBAAiB,QAAQ;CAC/C,MAAM,QAAQ,uBAAuB,cAAc,GAAG,CAAC;CAEvD,MAAM,8BAA8B,kBAAkB,UAAmB;AACvE,WAAS,IAAI,EAAE,MAAM,EAAE,UAAU,OAAO,EAAE,CAAC;GAC3C;CACF,MAAM,eAAkC,eAC/B;EAAE;EAAO;EAA6B,GAC7C,CAAC,OAAO,4BAA4B,CACrC;CACD,MAAM,UAAU,SAAS,WAAW;CACpC,MAAM,aAAa,SAAS,cAAc;AAO1C,QACE,oBAAC,eAAD;EAAe,OAAO;YACpB,oBAAC,WAAD;GAPF,GAAG;IACF,aAAa;IACb,UAAU;GAKuB,CAAA;EAClB,CAAA;EAGrB;AAED,kBAAkB,cAAc"}
|
package/es/Modal/imperative.mjs
CHANGED
|
@@ -158,7 +158,7 @@ const createModal = (props) => {
|
|
|
158
158
|
return {
|
|
159
159
|
close: () => closeModal(id),
|
|
160
160
|
destroy: () => destroyModal(id),
|
|
161
|
-
setCanDismissByClickOutside: (value) => updateModal(id, {
|
|
161
|
+
setCanDismissByClickOutside: (value) => updateModal(id, { mask: { closable: value } }),
|
|
162
162
|
update: (nextProps) => updateModal(id, nextProps)
|
|
163
163
|
};
|
|
164
164
|
};
|
|
@@ -176,7 +176,7 @@ function createRawModal(component, props, options) {
|
|
|
176
176
|
return {
|
|
177
177
|
close: () => closeModal(id),
|
|
178
178
|
destroy: () => destroyModal(id),
|
|
179
|
-
setCanDismissByClickOutside: (value) => updateRawProps(id, {
|
|
179
|
+
setCanDismissByClickOutside: (value) => updateRawProps(id, { mask: { closable: value } }),
|
|
180
180
|
update: (nextProps) => updateRawProps(id, nextProps)
|
|
181
181
|
};
|
|
182
182
|
}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imperative.mjs","names":[],"sources":["../../src/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport { ModalStackItem } from './ModalStackItem';\nimport { RawModalStackItem } from './RawModalStackItem';\nimport type {\n ImperativeModalProps,\n ModalInstance,\n RawModalComponent,\n RawModalComponentProps,\n RawModalInstance,\n RawModalKeyOptions,\n RawModalOptions,\n} from './type';\n\ntype ModalStackItemBase = {\n id: string;\n};\n\ntype ModalStackItemModal = ModalStackItemBase & {\n kind: 'modal';\n props: ImperativeModalProps;\n};\n\ntype ModalStackItemRaw = ModalStackItemBase & {\n component: RawModalComponent;\n kind: 'raw';\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\ntype TModalStackItem = ModalStackItemModal | ModalStackItemRaw;\n\ntype ModalStackProps = {\n stack: TModalStackItem[];\n};\n\nexport type ModalHostProps = {\n root?: HTMLElement | ShadowRoot | null;\n};\n\nlet modalStack: TModalStackItem[] = [];\nlet modalSeed = 0;\nconst listeners = new Set<() => void>();\nconst rawDestroyTimers = new Map<string, number>();\n\nconst notify = () => {\n listeners.forEach((listener) => listener());\n};\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n};\n\nconst EMPTY_STACK: TModalStackItem[] = [];\nconst getSnapshot = () => modalStack;\nconst getServerSnapshot = () => EMPTY_STACK;\n\nconst ModalPortal = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n\n return createPortal(children, container);\n};\n\nconst updateModal = (id: string, nextProps: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'modal') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst updateRawProps = (id: string, nextProps: Record<string, unknown>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst setRawOpen = (id: string, open: boolean) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n if (item.open === open) return item;\n changed = true;\n return { ...item, open };\n });\n\n if (open) {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n }\n\n if (changed) notify();\n};\n\nconst closeModal = (id: string) => {\n const target = modalStack.find((item) => item.id === id);\n if (!target) return;\n\n if (target.kind === 'modal') {\n updateModal(id, { open: false });\n return;\n }\n\n setRawOpen(id, false);\n\n const shouldDestroy = target.options?.destroyOnClose ?? true;\n if (!shouldDestroy) return;\n\n const delay = target.options?.destroyDelay ?? 200;\n const existing = rawDestroyTimers.get(id);\n if (existing) clearTimeout(existing);\n const timer = window.setTimeout(() => {\n rawDestroyTimers.delete(id);\n\n destroyModal(id);\n }, delay);\n rawDestroyTimers.set(id, timer);\n};\n\nconst destroyModal = (id: string) => {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n const nextStack = modalStack.filter((item) => item.id !== id);\n if (nextStack.length === modalStack.length) return;\n modalStack = nextStack;\n notify();\n};\n\nconst ModalStack = memo(({ stack }: ModalStackProps) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((item) => {\n if (item.kind === 'modal') {\n return (\n <ModalStackItem\n id={item.id}\n key={item.id}\n props={item.props}\n onClose={closeModal}\n onDestroy={destroyModal}\n onUpdate={updateModal}\n />\n );\n }\n\n return (\n <RawModalStackItem\n component={item.component}\n id={item.id}\n key={item.id}\n open={item.open}\n options={item.options}\n props={item.props}\n onClose={closeModal}\n onUpdate={updateRawProps}\n />\n );\n });\n});\n\nModalStack.displayName = 'ModalStack';\n\nexport const ModalHost = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n // Enforce singleton per portal root (dev-only).\n const scope = root ?? document.body;\n return registerDevSingleton('ModalHost', scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortal root={root}>\n <ModalStack stack={stack} />\n </ModalPortal>\n );\n};\n\nexport const createModal = (props: ImperativeModalProps): ModalInstance => {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n { id, kind: 'modal', props: { ...props, open: props.open ?? true } },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { maskClosable: value }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n};\n\nexport function createRawModal<P extends RawModalComponentProps>(\n component: RawModalComponent<P>,\n props: Omit<P, 'open' | 'onClose'>,\n options?: RawModalOptions,\n): RawModalInstance<P>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options: RawModalKeyOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options?: RawModalOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey> {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n {\n component,\n id,\n kind: 'raw',\n open: true,\n options,\n props: props as Record<string, unknown>,\n },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateRawProps(id, { maskClosable: value }),\n update: (nextProps) => updateRawProps(id, nextProps as Record<string, unknown>),\n };\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"imperative.mjs","names":[],"sources":["../../src/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport type { ReactNode } from 'react';\nimport { memo, useEffect, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport { ModalStackItem } from './ModalStackItem';\nimport { RawModalStackItem } from './RawModalStackItem';\nimport type {\n ImperativeModalProps,\n ModalInstance,\n RawModalComponent,\n RawModalComponentProps,\n RawModalInstance,\n RawModalKeyOptions,\n RawModalOptions,\n} from './type';\n\ntype ModalStackItemBase = {\n id: string;\n};\n\ntype ModalStackItemModal = ModalStackItemBase & {\n kind: 'modal';\n props: ImperativeModalProps;\n};\n\ntype ModalStackItemRaw = ModalStackItemBase & {\n component: RawModalComponent;\n kind: 'raw';\n open: boolean;\n options?: RawModalOptions<PropertyKey, PropertyKey>;\n props: Record<string, unknown>;\n};\n\ntype TModalStackItem = ModalStackItemModal | ModalStackItemRaw;\n\ntype ModalStackProps = {\n stack: TModalStackItem[];\n};\n\nexport type ModalHostProps = {\n root?: HTMLElement | ShadowRoot | null;\n};\n\nlet modalStack: TModalStackItem[] = [];\nlet modalSeed = 0;\nconst listeners = new Set<() => void>();\nconst rawDestroyTimers = new Map<string, number>();\n\nconst notify = () => {\n listeners.forEach((listener) => listener());\n};\n\nconst subscribe = (listener: () => void) => {\n listeners.add(listener);\n return () => listeners.delete(listener);\n};\n\nconst EMPTY_STACK: TModalStackItem[] = [];\nconst getSnapshot = () => modalStack;\nconst getServerSnapshot = () => EMPTY_STACK;\n\nconst ModalPortal = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n\n return createPortal(children, container);\n};\n\nconst updateModal = (id: string, nextProps: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'modal') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst updateRawProps = (id: string, nextProps: Record<string, unknown>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n changed = true;\n return {\n ...item,\n props: { ...item.props, ...nextProps },\n };\n });\n\n if (changed) notify();\n};\n\nconst setRawOpen = (id: string, open: boolean) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n if (item.kind !== 'raw') return item;\n if (item.open === open) return item;\n changed = true;\n return { ...item, open };\n });\n\n if (open) {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n }\n\n if (changed) notify();\n};\n\nconst closeModal = (id: string) => {\n const target = modalStack.find((item) => item.id === id);\n if (!target) return;\n\n if (target.kind === 'modal') {\n updateModal(id, { open: false });\n return;\n }\n\n setRawOpen(id, false);\n\n const shouldDestroy = target.options?.destroyOnClose ?? true;\n if (!shouldDestroy) return;\n\n const delay = target.options?.destroyDelay ?? 200;\n const existing = rawDestroyTimers.get(id);\n if (existing) clearTimeout(existing);\n const timer = window.setTimeout(() => {\n rawDestroyTimers.delete(id);\n\n destroyModal(id);\n }, delay);\n rawDestroyTimers.set(id, timer);\n};\n\nconst destroyModal = (id: string) => {\n const timer = rawDestroyTimers.get(id);\n if (timer) {\n clearTimeout(timer);\n rawDestroyTimers.delete(id);\n }\n const nextStack = modalStack.filter((item) => item.id !== id);\n if (nextStack.length === modalStack.length) return;\n modalStack = nextStack;\n notify();\n};\n\nconst ModalStack = memo(({ stack }: ModalStackProps) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((item) => {\n if (item.kind === 'modal') {\n return (\n <ModalStackItem\n id={item.id}\n key={item.id}\n props={item.props}\n onClose={closeModal}\n onDestroy={destroyModal}\n onUpdate={updateModal}\n />\n );\n }\n\n return (\n <RawModalStackItem\n component={item.component}\n id={item.id}\n key={item.id}\n open={item.open}\n options={item.options}\n props={item.props}\n onClose={closeModal}\n onUpdate={updateRawProps}\n />\n );\n });\n});\n\nModalStack.displayName = 'ModalStack';\n\nexport const ModalHost = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n // Enforce singleton per portal root (dev-only).\n const scope = root ?? document.body;\n return registerDevSingleton('ModalHost', scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortal root={root}>\n <ModalStack stack={stack} />\n </ModalPortal>\n );\n};\n\nexport const createModal = (props: ImperativeModalProps): ModalInstance => {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n { id, kind: 'modal', props: { ...props, open: props.open ?? true } },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { mask: { closable: value } }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n};\n\nexport function createRawModal<P extends RawModalComponentProps>(\n component: RawModalComponent<P>,\n props: Omit<P, 'open' | 'onClose'>,\n options?: RawModalOptions,\n): RawModalInstance<P>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options: RawModalKeyOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey>;\n\nexport function createRawModal<P, OpenKey extends keyof P, CloseKey extends keyof P>(\n component: RawModalComponent<P>,\n props: Omit<P, OpenKey | CloseKey>,\n options?: RawModalOptions<OpenKey, CloseKey>,\n): RawModalInstance<P, OpenKey, CloseKey> {\n const id = `modal-${Date.now()}-${modalSeed++}`;\n modalStack = [\n ...modalStack,\n {\n component,\n id,\n kind: 'raw',\n open: true,\n options,\n props: props as Record<string, unknown>,\n },\n ];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateRawProps(id, { mask: { closable: value } }),\n update: (nextProps) => updateRawProps(id, nextProps as Record<string, unknown>),\n };\n}\n"],"mappings":"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"}
|
|
@@ -1,20 +1,28 @@
|
|
|
1
1
|
"use client";
|
|
2
|
+
import { safeReadableColor } from "../../utils/safeReadableColor.mjs";
|
|
2
3
|
import { stopPropagation } from "../../utils/dom.mjs";
|
|
3
4
|
import { styles } from "./style.mjs";
|
|
4
5
|
import { ModalBackdrop, ModalContent, ModalFooter, ModalHeader, ModalPopup, ModalPortal, ModalRoot, ModalTitle } from "./atoms.mjs";
|
|
6
|
+
import { acquireModalZIndex } from "./zIndexManager.mjs";
|
|
5
7
|
import { memo, useCallback, useEffect, useMemo, useRef, useState } from "react";
|
|
6
8
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
-
import { cx } from "antd-style";
|
|
9
|
+
import { cx, useTheme } from "antd-style";
|
|
8
10
|
import { useDragControls } from "motion/react";
|
|
9
11
|
import { Maximize2, Minimize2, X } from "lucide-react";
|
|
10
12
|
//#region src/base-ui/Modal/Modal.tsx
|
|
11
13
|
const OkBtn = ({ confirmLoading, okButtonProps, okText, onOk }) => {
|
|
12
|
-
const
|
|
14
|
+
const theme = useTheme();
|
|
15
|
+
const { className: userCls, danger, disabled: userDisabled, onClick: userOnClick, style: userStyle, ...restOk } = okButtonProps ?? {};
|
|
16
|
+
const textColor = safeReadableColor(danger ? theme.colorError : theme.colorPrimary);
|
|
13
17
|
return /* @__PURE__ */ jsxs("button", {
|
|
14
18
|
type: "button",
|
|
15
19
|
...restOk,
|
|
16
20
|
className: cx(styles.buttonBase, danger ? styles.dangerOkButton : styles.okButton, userCls),
|
|
17
21
|
disabled: confirmLoading || userDisabled,
|
|
22
|
+
style: {
|
|
23
|
+
color: textColor,
|
|
24
|
+
...userStyle
|
|
25
|
+
},
|
|
18
26
|
onClick: (e) => {
|
|
19
27
|
onOk(e);
|
|
20
28
|
userOnClick?.(e);
|
|
@@ -124,8 +132,13 @@ const Modal = memo(({ open, title, children, onOk, onCancel, okText = "OK", canc
|
|
|
124
132
|
handleOk
|
|
125
133
|
]);
|
|
126
134
|
const container = getContainer === false ? void 0 : getContainer ?? void 0;
|
|
127
|
-
const
|
|
128
|
-
const
|
|
135
|
+
const prevOpenRef = useRef(false);
|
|
136
|
+
const acquiredZRef = useRef(void 0);
|
|
137
|
+
if (open && !prevOpenRef.current) acquiredZRef.current = acquireModalZIndex();
|
|
138
|
+
prevOpenRef.current = !!open;
|
|
139
|
+
const effectiveZIndex = zIndex ?? acquiredZRef.current;
|
|
140
|
+
const backdropZIndex = effectiveZIndex ? { zIndex: effectiveZIndex } : void 0;
|
|
141
|
+
const popupZIndex = effectiveZIndex ? { zIndex: effectiveZIndex + 1 } : void 0;
|
|
129
142
|
const shouldDrag = draggable && !isFullscreen;
|
|
130
143
|
const dragProps = shouldDrag ? {
|
|
131
144
|
drag: true,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Modal.mjs","names":[],"sources":["../../../src/base-ui/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { useDragControls } from 'motion/react';\nimport type { MouseEvent, PointerEvent } from 'react';\nimport type React from 'react';\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { stopPropagation } from '@/utils/dom';\n\nimport {\n ModalBackdrop,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalPopup,\n ModalPortal,\n ModalRoot,\n ModalTitle,\n} from './atoms';\nimport { styles } from './style';\nimport type { ModalComponentProps } from './type';\n\ninterface OkBtnProps {\n confirmLoading?: boolean;\n okButtonProps?: ModalComponentProps['okButtonProps'];\n okText?: React.ReactNode;\n onOk: (e: MouseEvent<HTMLButtonElement>) => void;\n}\n\nconst OkBtn: React.FC<OkBtnProps> = ({ confirmLoading, okButtonProps, okText, onOk }) => {\n const {\n className: userCls,\n danger,\n disabled: userDisabled,\n onClick: userOnClick,\n ...restOk\n } = okButtonProps ?? {};\n return (\n <button\n type=\"button\"\n {...restOk}\n className={cx(styles.buttonBase, danger ? styles.dangerOkButton : styles.okButton, userCls)}\n disabled={confirmLoading || userDisabled}\n onClick={(e) => {\n onOk(e);\n userOnClick?.(e);\n }}\n >\n {confirmLoading && <span className={styles.loadingSpinner} />}\n {okText}\n </button>\n );\n};\ninterface CancelBtnProps {\n cancelButtonProps?: ModalComponentProps['cancelButtonProps'];\n cancelText?: React.ReactNode;\n onCancel: (e: MouseEvent<HTMLButtonElement>) => void;\n}\n\nconst CancelBtn: React.FC<CancelBtnProps> = ({ cancelButtonProps, cancelText, onCancel }) => {\n const { className: userCls, onClick: userOnClick, ...restCancel } = cancelButtonProps ?? {};\n return (\n <button\n type=\"button\"\n {...restCancel}\n className={cx(styles.buttonBase, styles.cancelButton, userCls)}\n onClick={(e) => {\n onCancel(e);\n userOnClick?.(e);\n }}\n >\n {cancelText}\n </button>\n );\n};\n\nconst Modal = memo<ModalComponentProps>(\n ({\n open,\n title,\n children,\n onOk,\n onCancel,\n okText = 'OK',\n cancelText = 'Cancel',\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n footer,\n width,\n height,\n maskClosable = true,\n closable = true,\n closeIcon,\n className,\n style,\n classNames,\n styles: semanticStyles,\n zIndex,\n afterClose,\n afterOpenChange,\n loading,\n getContainer,\n mask = true,\n keyboard,\n draggable = true,\n allowFullscreen = false,\n }) => {\n const dragControls = useDragControls();\n const constraintsRef = useRef<HTMLDivElement>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isDenying, setIsDenying] = useState(false);\n const denyTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => () => clearTimeout(denyTimerRef.current), []);\n\n const triggerDeny = useCallback(() => {\n clearTimeout(denyTimerRef.current);\n setIsDenying(true);\n denyTimerRef.current = setTimeout(() => setIsDenying(false), 400);\n }, []);\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails: { reason: string }) => {\n if (!open) return;\n if (!nextOpen && keyboard === false && eventDetails.reason === 'escape-key') return;\n if (!nextOpen && !maskClosable && eventDetails.reason === 'outside-press') {\n triggerDeny();\n return;\n }\n if (!nextOpen) {\n onCancel?.(new MouseEvent('click') as unknown as MouseEvent<HTMLButtonElement>);\n }\n },\n [onCancel, keyboard, maskClosable, open, triggerDeny],\n );\n\n const handleExitComplete = useCallback(() => {\n setIsFullscreen(false);\n afterClose?.();\n afterOpenChange?.(false);\n }, [afterClose, afterOpenChange]);\n\n const handleAnimationComplete = useCallback(() => {\n if (open) afterOpenChange?.(true);\n }, [open, afterOpenChange]);\n\n const handleDragStart = useCallback(\n (e: PointerEvent) => {\n if (draggable && !isFullscreen) {\n dragControls.start(e);\n setIsDragging(true);\n }\n },\n [draggable, dragControls, isFullscreen],\n );\n\n const handleDragEnd = useCallback(() => {\n setIsDragging(false);\n }, []);\n\n const handleOk = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onOk?.(e);\n },\n [onOk],\n );\n\n const handleCancel = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onCancel?.(e);\n },\n [onCancel],\n );\n\n const footerNode = useMemo(() => {\n if (footer === false || footer === null) return null;\n const cancelBtnNode = (\n <CancelBtn\n cancelButtonProps={cancelButtonProps}\n cancelText={cancelText}\n onCancel={handleCancel}\n />\n );\n const okBtnNode = (\n <OkBtn\n confirmLoading={confirmLoading}\n okButtonProps={okButtonProps}\n okText={okText}\n onOk={handleOk}\n />\n );\n const defaultFooter = (\n <>\n {cancelBtnNode}\n {okBtnNode}\n </>\n );\n\n if (typeof footer === 'function') {\n const BoundCancelBtn: React.FC = () => cancelBtnNode;\n const BoundOkBtn: React.FC = () => okBtnNode;\n return footer(defaultFooter, { CancelBtn: BoundCancelBtn, OkBtn: BoundOkBtn });\n }\n\n return footer ?? defaultFooter;\n }, [\n footer,\n cancelButtonProps,\n cancelText,\n handleCancel,\n confirmLoading,\n okButtonProps,\n okText,\n handleOk,\n ]);\n\n const container = getContainer === false ? undefined : (getContainer ?? undefined);\n const backdropZIndex = zIndex ? { zIndex } : undefined;\n const popupZIndex = zIndex ? { zIndex: (zIndex || 1000) + 1 } : undefined;\n\n const shouldDrag = draggable && !isFullscreen;\n const dragProps = shouldDrag\n ? {\n drag: true as const,\n dragConstraints: constraintsRef,\n dragControls,\n dragElastic: 0,\n dragListener: false,\n dragMomentum: false,\n whileDrag: { cursor: 'grabbing' as const },\n }\n : {};\n\n const showTitle = title !== undefined && title !== false && title !== null;\n const showHeader = showTitle || closable || allowFullscreen;\n\n const hasHeight = height !== undefined;\n const panelStyle: React.CSSProperties = {\n ...(hasHeight && !isFullscreen ? { height } : {}),\n ...style,\n };\n\n return (\n <ModalRoot\n open={open ?? false}\n onExitComplete={handleExitComplete}\n onOpenChange={handleOpenChange}\n >\n <ModalPortal container={container}>\n {mask && (\n <ModalBackdrop\n className={classNames?.mask}\n style={{ ...backdropZIndex, ...semanticStyles?.mask }}\n />\n )}\n <ModalPopup\n className={classNames?.wrapper}\n popupStyle={{ ...popupZIndex, ...semanticStyles?.wrapper }}\n ref={constraintsRef}\n style={panelStyle}\n width={isFullscreen ? undefined : width}\n motionProps={{\n ...dragProps,\n onAnimationComplete: handleAnimationComplete,\n }}\n panelClassName={cx(\n className,\n isFullscreen && styles.fullscreenPopupInner,\n isDenying && styles.denyAnimation,\n )}\n >\n {showHeader && (\n <ModalHeader\n className={cx(classNames?.header, shouldDrag && styles.headerDraggable)}\n style={{\n ...(isDragging ? { cursor: 'grabbing' } : {}),\n ...semanticStyles?.header,\n }}\n onPointerCancel={handleDragEnd}\n onPointerDown={handleDragStart}\n onPointerUp={handleDragEnd}\n >\n {showTitle ? (\n <ModalTitle className={classNames?.title} style={semanticStyles?.title}>\n {title}\n </ModalTitle>\n ) : (\n <span />\n )}\n <div className={styles.headerActions} onPointerDown={stopPropagation}>\n {allowFullscreen && (\n <button\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n className={styles.fullscreenToggle}\n type=\"button\"\n onClick={() => setIsFullscreen((prev) => !prev)}\n >\n {isFullscreen ? <Minimize2 size={14} /> : <Maximize2 size={14} />}\n </button>\n )}\n {closable && (\n <button\n aria-label=\"Close\"\n className={styles.closeInline}\n type=\"button\"\n onClick={handleCancel}\n >\n {closeIcon ?? <X size={18} />}\n </button>\n )}\n </div>\n </ModalHeader>\n )}\n <ModalContent\n className={classNames?.body}\n style={{\n ...(hasHeight || isFullscreen ? { flex: 1 } : {}),\n ...semanticStyles?.body,\n }}\n >\n {loading ? (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n padding: '32px 0',\n }}\n >\n <span className={styles.loadingSpinner} style={{ height: 24, width: 24 }} />\n </div>\n ) : (\n children\n )}\n </ModalContent>\n {footerNode !== null && (\n <ModalFooter className={classNames?.footer} style={semanticStyles?.footer}>\n {footerNode}\n </ModalFooter>\n )}\n </ModalPopup>\n </ModalPortal>\n </ModalRoot>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"Modal.mjs","names":[],"sources":["../../../src/base-ui/Modal/Modal.tsx"],"sourcesContent":["'use client';\n\nimport { cx, useTheme } from 'antd-style';\nimport { Maximize2, Minimize2, X } from 'lucide-react';\nimport { useDragControls } from 'motion/react';\nimport type { MouseEvent, PointerEvent } from 'react';\nimport type React from 'react';\nimport { memo, useCallback, useEffect, useMemo, useRef, useState } from 'react';\n\nimport { stopPropagation } from '@/utils/dom';\nimport { safeReadableColor } from '@/utils/safeReadableColor';\n\nimport {\n ModalBackdrop,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalPopup,\n ModalPortal,\n ModalRoot,\n ModalTitle,\n} from './atoms';\nimport { styles } from './style';\nimport type { ModalComponentProps } from './type';\nimport { acquireModalZIndex } from './zIndexManager';\n\ninterface OkBtnProps {\n confirmLoading?: boolean;\n okButtonProps?: ModalComponentProps['okButtonProps'];\n okText?: React.ReactNode;\n onOk: (e: MouseEvent<HTMLButtonElement>) => void;\n}\n\nconst OkBtn: React.FC<OkBtnProps> = ({ confirmLoading, okButtonProps, okText, onOk }) => {\n const theme = useTheme();\n const {\n className: userCls,\n danger,\n disabled: userDisabled,\n onClick: userOnClick,\n style: userStyle,\n ...restOk\n } = okButtonProps ?? {};\n const bgColor = danger ? theme.colorError : theme.colorPrimary;\n const textColor = safeReadableColor(bgColor);\n return (\n <button\n type=\"button\"\n {...restOk}\n className={cx(styles.buttonBase, danger ? styles.dangerOkButton : styles.okButton, userCls)}\n disabled={confirmLoading || userDisabled}\n style={{ color: textColor, ...userStyle }}\n onClick={(e) => {\n onOk(e);\n userOnClick?.(e);\n }}\n >\n {confirmLoading && <span className={styles.loadingSpinner} />}\n {okText}\n </button>\n );\n};\ninterface CancelBtnProps {\n cancelButtonProps?: ModalComponentProps['cancelButtonProps'];\n cancelText?: React.ReactNode;\n onCancel: (e: MouseEvent<HTMLButtonElement>) => void;\n}\n\nconst CancelBtn: React.FC<CancelBtnProps> = ({ cancelButtonProps, cancelText, onCancel }) => {\n const { className: userCls, onClick: userOnClick, ...restCancel } = cancelButtonProps ?? {};\n return (\n <button\n type=\"button\"\n {...restCancel}\n className={cx(styles.buttonBase, styles.cancelButton, userCls)}\n onClick={(e) => {\n onCancel(e);\n userOnClick?.(e);\n }}\n >\n {cancelText}\n </button>\n );\n};\n\nconst Modal = memo<ModalComponentProps>(\n ({\n open,\n title,\n children,\n onOk,\n onCancel,\n okText = 'OK',\n cancelText = 'Cancel',\n okButtonProps,\n cancelButtonProps,\n confirmLoading,\n footer,\n width,\n height,\n maskClosable = true,\n closable = true,\n closeIcon,\n className,\n style,\n classNames,\n styles: semanticStyles,\n zIndex,\n afterClose,\n afterOpenChange,\n loading,\n getContainer,\n mask = true,\n keyboard,\n draggable = true,\n allowFullscreen = false,\n }) => {\n const dragControls = useDragControls();\n const constraintsRef = useRef<HTMLDivElement>(null);\n const [isFullscreen, setIsFullscreen] = useState(false);\n const [isDragging, setIsDragging] = useState(false);\n const [isDenying, setIsDenying] = useState(false);\n const denyTimerRef = useRef<ReturnType<typeof setTimeout> | undefined>(undefined);\n\n useEffect(() => () => clearTimeout(denyTimerRef.current), []);\n\n const triggerDeny = useCallback(() => {\n clearTimeout(denyTimerRef.current);\n setIsDenying(true);\n denyTimerRef.current = setTimeout(() => setIsDenying(false), 400);\n }, []);\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails: { reason: string }) => {\n if (!open) return;\n if (!nextOpen && keyboard === false && eventDetails.reason === 'escape-key') return;\n if (!nextOpen && !maskClosable && eventDetails.reason === 'outside-press') {\n triggerDeny();\n return;\n }\n if (!nextOpen) {\n onCancel?.(new MouseEvent('click') as unknown as MouseEvent<HTMLButtonElement>);\n }\n },\n [onCancel, keyboard, maskClosable, open, triggerDeny],\n );\n\n const handleExitComplete = useCallback(() => {\n setIsFullscreen(false);\n afterClose?.();\n afterOpenChange?.(false);\n }, [afterClose, afterOpenChange]);\n\n const handleAnimationComplete = useCallback(() => {\n if (open) afterOpenChange?.(true);\n }, [open, afterOpenChange]);\n\n const handleDragStart = useCallback(\n (e: PointerEvent) => {\n if (draggable && !isFullscreen) {\n dragControls.start(e);\n setIsDragging(true);\n }\n },\n [draggable, dragControls, isFullscreen],\n );\n\n const handleDragEnd = useCallback(() => {\n setIsDragging(false);\n }, []);\n\n const handleOk = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onOk?.(e);\n },\n [onOk],\n );\n\n const handleCancel = useCallback(\n (e: MouseEvent<HTMLButtonElement>) => {\n onCancel?.(e);\n },\n [onCancel],\n );\n\n const footerNode = useMemo(() => {\n if (footer === false || footer === null) return null;\n const cancelBtnNode = (\n <CancelBtn\n cancelButtonProps={cancelButtonProps}\n cancelText={cancelText}\n onCancel={handleCancel}\n />\n );\n const okBtnNode = (\n <OkBtn\n confirmLoading={confirmLoading}\n okButtonProps={okButtonProps}\n okText={okText}\n onOk={handleOk}\n />\n );\n const defaultFooter = (\n <>\n {cancelBtnNode}\n {okBtnNode}\n </>\n );\n\n if (typeof footer === 'function') {\n const BoundCancelBtn: React.FC = () => cancelBtnNode;\n const BoundOkBtn: React.FC = () => okBtnNode;\n return footer(defaultFooter, { CancelBtn: BoundCancelBtn, OkBtn: BoundOkBtn });\n }\n\n return footer ?? defaultFooter;\n }, [\n footer,\n cancelButtonProps,\n cancelText,\n handleCancel,\n confirmLoading,\n okButtonProps,\n okText,\n handleOk,\n ]);\n\n const container = getContainer === false ? undefined : (getContainer ?? undefined);\n\n const prevOpenRef = useRef(false);\n const acquiredZRef = useRef<number | undefined>(undefined);\n if (open && !prevOpenRef.current) {\n acquiredZRef.current = acquireModalZIndex();\n }\n prevOpenRef.current = !!open;\n const effectiveZIndex = zIndex ?? acquiredZRef.current;\n const backdropZIndex = effectiveZIndex ? { zIndex: effectiveZIndex } : undefined;\n const popupZIndex = effectiveZIndex ? { zIndex: effectiveZIndex + 1 } : undefined;\n\n const shouldDrag = draggable && !isFullscreen;\n const dragProps = shouldDrag\n ? {\n drag: true as const,\n dragConstraints: constraintsRef,\n dragControls,\n dragElastic: 0,\n dragListener: false,\n dragMomentum: false,\n whileDrag: { cursor: 'grabbing' as const },\n }\n : {};\n\n const showTitle = title !== undefined && title !== false && title !== null;\n const showHeader = showTitle || closable || allowFullscreen;\n\n const hasHeight = height !== undefined;\n const panelStyle: React.CSSProperties = {\n ...(hasHeight && !isFullscreen ? { height } : {}),\n ...style,\n };\n\n return (\n <ModalRoot\n open={open ?? false}\n onExitComplete={handleExitComplete}\n onOpenChange={handleOpenChange}\n >\n <ModalPortal container={container}>\n {mask && (\n <ModalBackdrop\n className={classNames?.mask}\n style={{ ...backdropZIndex, ...semanticStyles?.mask }}\n />\n )}\n <ModalPopup\n className={classNames?.wrapper}\n popupStyle={{ ...popupZIndex, ...semanticStyles?.wrapper }}\n ref={constraintsRef}\n style={panelStyle}\n width={isFullscreen ? undefined : width}\n motionProps={{\n ...dragProps,\n onAnimationComplete: handleAnimationComplete,\n }}\n panelClassName={cx(\n className,\n isFullscreen && styles.fullscreenPopupInner,\n isDenying && styles.denyAnimation,\n )}\n >\n {showHeader && (\n <ModalHeader\n className={cx(classNames?.header, shouldDrag && styles.headerDraggable)}\n style={{\n ...(isDragging ? { cursor: 'grabbing' } : {}),\n ...semanticStyles?.header,\n }}\n onPointerCancel={handleDragEnd}\n onPointerDown={handleDragStart}\n onPointerUp={handleDragEnd}\n >\n {showTitle ? (\n <ModalTitle className={classNames?.title} style={semanticStyles?.title}>\n {title}\n </ModalTitle>\n ) : (\n <span />\n )}\n <div className={styles.headerActions} onPointerDown={stopPropagation}>\n {allowFullscreen && (\n <button\n aria-label={isFullscreen ? 'Exit fullscreen' : 'Fullscreen'}\n className={styles.fullscreenToggle}\n type=\"button\"\n onClick={() => setIsFullscreen((prev) => !prev)}\n >\n {isFullscreen ? <Minimize2 size={14} /> : <Maximize2 size={14} />}\n </button>\n )}\n {closable && (\n <button\n aria-label=\"Close\"\n className={styles.closeInline}\n type=\"button\"\n onClick={handleCancel}\n >\n {closeIcon ?? <X size={18} />}\n </button>\n )}\n </div>\n </ModalHeader>\n )}\n <ModalContent\n className={classNames?.body}\n style={{\n ...(hasHeight || isFullscreen ? { flex: 1 } : {}),\n ...semanticStyles?.body,\n }}\n >\n {loading ? (\n <div\n style={{\n display: 'flex',\n justifyContent: 'center',\n padding: '32px 0',\n }}\n >\n <span className={styles.loadingSpinner} style={{ height: 24, width: 24 }} />\n </div>\n ) : (\n children\n )}\n </ModalContent>\n {footerNode !== null && (\n <ModalFooter className={classNames?.footer} style={semanticStyles?.footer}>\n {footerNode}\n </ModalFooter>\n )}\n </ModalPopup>\n </ModalPortal>\n </ModalRoot>\n );\n },\n);\n\nModal.displayName = 'Modal';\n\nexport default Modal;\n"],"mappings":"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"}
|
|
@@ -1,13 +1,15 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
import { useIsClient } from "../../hooks/useIsClient.mjs";
|
|
3
3
|
import { useAppElement } from "../../ThemeProvider/AppElementContext.mjs";
|
|
4
|
+
import { safeReadableColor } from "../../utils/safeReadableColor.mjs";
|
|
4
5
|
import { registerDevSingleton } from "../../utils/devSingleton.mjs";
|
|
5
6
|
import { styles } from "./style.mjs";
|
|
6
7
|
import { ModalBackdrop, ModalClose, ModalContent, ModalFooter, ModalHeader, ModalPopup, ModalPortal, ModalRoot, ModalTitle } from "./atoms.mjs";
|
|
7
8
|
import { ModalContext, useModalContext } from "./context.mjs";
|
|
8
|
-
import {
|
|
9
|
+
import { acquireModalZIndex } from "./zIndexManager.mjs";
|
|
10
|
+
import { memo, useCallback, useEffect, useRef, useState, useSyncExternalStore } from "react";
|
|
9
11
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
10
|
-
import { cx } from "antd-style";
|
|
12
|
+
import { cx, useTheme } from "antd-style";
|
|
11
13
|
import { createPortal } from "react-dom";
|
|
12
14
|
//#region src/base-ui/Modal/imperative.tsx
|
|
13
15
|
const ModalPortalWrapper = ({ children, root }) => {
|
|
@@ -16,9 +18,11 @@ const ModalPortalWrapper = ({ children, root }) => {
|
|
|
16
18
|
};
|
|
17
19
|
const ConfirmBody = ({ config }) => {
|
|
18
20
|
const { close } = useModalContext();
|
|
21
|
+
const theme = useTheme();
|
|
19
22
|
const [loading, setLoading] = useState(false);
|
|
20
23
|
const { cancelText = "Cancel", content, okButtonProps, okText = "OK", onCancel, onOk } = config;
|
|
21
|
-
const { danger, className: okUserCls, ...restOkProps } = okButtonProps ?? {};
|
|
24
|
+
const { danger, className: okUserCls, style: okUserStyle, ...restOkProps } = okButtonProps ?? {};
|
|
25
|
+
const okTextColor = safeReadableColor(danger ? theme.colorError : theme.colorPrimary);
|
|
22
26
|
const handleCancel = useCallback(() => {
|
|
23
27
|
close();
|
|
24
28
|
onCancel?.();
|
|
@@ -48,6 +52,10 @@ const ConfirmBody = ({ config }) => {
|
|
|
48
52
|
}), /* @__PURE__ */ jsxs("button", {
|
|
49
53
|
...restOkProps,
|
|
50
54
|
disabled: loading,
|
|
55
|
+
style: {
|
|
56
|
+
color: okTextColor,
|
|
57
|
+
...okUserStyle
|
|
58
|
+
},
|
|
51
59
|
type: "button",
|
|
52
60
|
className: cx(styles.buttonBase, danger ? styles.dangerOkButton : styles.okButton, okUserCls),
|
|
53
61
|
onClick: handleOk,
|
|
@@ -98,6 +106,11 @@ function createModalSystem() {
|
|
|
98
106
|
const { id, props } = entry;
|
|
99
107
|
const { children, classNames, content, footer, maskClosable, onOpenChange, onOpenChangeComplete, open, styles: semanticStyles, title, width } = props;
|
|
100
108
|
const isOpen = open ?? true;
|
|
109
|
+
const zIndexRef = useRef(void 0);
|
|
110
|
+
const prevOpenRef = useRef(false);
|
|
111
|
+
if (isOpen && !prevOpenRef.current) zIndexRef.current = acquireModalZIndex();
|
|
112
|
+
prevOpenRef.current = isOpen;
|
|
113
|
+
const zIndex = zIndexRef.current ?? 1e3;
|
|
101
114
|
const handleOpenChange = useCallback((nextOpen, eventDetails) => {
|
|
102
115
|
if (!nextOpen && maskClosable === false && eventDetails?.reason === "outside-press") return;
|
|
103
116
|
if (!nextOpen) closeModal(id);
|
|
@@ -125,10 +138,16 @@ function createModalSystem() {
|
|
|
125
138
|
onOpenChange: handleOpenChange,
|
|
126
139
|
children: /* @__PURE__ */ jsxs(ModalPortal, { children: [/* @__PURE__ */ jsx(ModalBackdrop, {
|
|
127
140
|
className: classNames?.backdrop,
|
|
128
|
-
style:
|
|
141
|
+
style: {
|
|
142
|
+
zIndex,
|
|
143
|
+
...semanticStyles?.backdrop
|
|
144
|
+
}
|
|
129
145
|
}), /* @__PURE__ */ jsxs(ModalPopup, {
|
|
130
146
|
className: classNames?.popup,
|
|
131
|
-
popupStyle:
|
|
147
|
+
popupStyle: {
|
|
148
|
+
zIndex: zIndex + 1,
|
|
149
|
+
...semanticStyles?.popup
|
|
150
|
+
},
|
|
132
151
|
width,
|
|
133
152
|
children: [
|
|
134
153
|
showTitle && /* @__PURE__ */ jsxs(ModalHeader, {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"imperative.mjs","names":[],"sources":["../../../src/base-ui/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport { cx } from 'antd-style';\nimport type { ReactNode } from 'react';\nimport { memo, useCallback, useEffect, useState, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\n\nimport {\n ModalBackdrop,\n ModalClose,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalPopup,\n ModalPortal,\n ModalRoot,\n ModalTitle,\n} from './atoms';\nimport { ModalContext, useModalContext } from './context';\nimport { styles } from './style';\nimport type { ImperativeModalProps, ModalConfirmConfig, ModalInstance } from './type';\n\n// --- Shared types ---\n\ntype ModalStackEntry = {\n id: string;\n props: ImperativeModalProps;\n};\n\n// --- Shared components (stack-independent) ---\n\nconst ModalPortalWrapper = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n return createPortal(children, container);\n};\n\nconst ConfirmBody = ({ config }: { config: ModalConfirmConfig }) => {\n const { close } = useModalContext();\n const [loading, setLoading] = useState(false);\n\n const { cancelText = 'Cancel', content, okButtonProps, okText = 'OK', onCancel, onOk } = config;\n\n const { danger, className: okUserCls, ...restOkProps } = okButtonProps ?? {};\n\n const handleCancel = useCallback(() => {\n close();\n onCancel?.();\n }, [close, onCancel]);\n\n const handleOk = useCallback(async () => {\n if (onOk) {\n try {\n const result = onOk();\n if (result && typeof (result as any).then === 'function') {\n setLoading(true);\n await result;\n setLoading(false);\n }\n } catch {\n setLoading(false);\n return;\n }\n }\n close();\n }, [close, onOk]);\n\n return (\n <>\n {content && <div style={{ padding: '16px 24px' }}>{content}</div>}\n <ModalFooter>\n <button\n className={cx(styles.buttonBase, styles.cancelButton)}\n type=\"button\"\n onClick={handleCancel}\n >\n {cancelText}\n </button>\n <button\n {...restOkProps}\n disabled={loading}\n type=\"button\"\n className={cx(\n styles.buttonBase,\n danger ? styles.dangerOkButton : styles.okButton,\n okUserCls,\n )}\n onClick={handleOk}\n >\n {loading && <span className={styles.loadingSpinner} />}\n {okText}\n </button>\n </ModalFooter>\n </>\n );\n};\nConfirmBody.displayName = 'ConfirmBody';\n\n// --- Factory ---\n\nexport interface ModalHostProps {\n root?: HTMLElement | ShadowRoot | null;\n}\n\nexport interface ModalSystem {\n confirmModal: (config: ModalConfirmConfig) => { close: () => void; destroy: () => void };\n createModal: (props: ImperativeModalProps) => ModalInstance;\n ModalHost: React.FC<ModalHostProps>;\n}\n\nlet systemSeed = 0;\n\nexport function createModalSystem(): ModalSystem {\n const systemId = systemSeed++;\n const singletonName = systemId === 0 ? 'BaseModalHost' : `BaseModalHost-${systemId}`;\n\n // --- Stack state (isolated per system) ---\n let modalStack: ModalStackEntry[] = [];\n let modalSeed = 0;\n const listeners = new Set<() => void>();\n\n const notify = () => listeners.forEach((l) => l());\n const subscribe = (l: () => void) => {\n listeners.add(l);\n return () => listeners.delete(l);\n };\n const EMPTY: ModalStackEntry[] = [];\n const getSnapshot = () => modalStack;\n const getServerSnapshot = () => EMPTY;\n\n // --- Stack operations ---\n\n const updateModal = (id: string, next: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n changed = true;\n return { ...item, props: { ...item.props, ...next } };\n });\n if (changed) notify();\n };\n\n const closeModal = (id: string) => {\n updateModal(id, { open: false });\n };\n\n const destroyModal = (id: string) => {\n const next = modalStack.filter((item) => item.id !== id);\n if (next.length === modalStack.length) return;\n modalStack = next;\n notify();\n };\n\n // --- Stack Item (captures operations via closure) ---\n\n const StackItem = memo(({ entry }: { entry: ModalStackEntry }) => {\n const { id, props } = entry;\n const {\n children,\n classNames,\n content,\n footer,\n maskClosable,\n onOpenChange,\n onOpenChangeComplete,\n open,\n styles: semanticStyles,\n title,\n width,\n } = props;\n\n const isOpen = open ?? true;\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails?: { reason: string }) => {\n if (!nextOpen && maskClosable === false && eventDetails?.reason === 'outside-press') return;\n if (!nextOpen) closeModal(id);\n onOpenChange?.(nextOpen);\n },\n [id, maskClosable, onOpenChange],\n );\n\n const handleExitComplete = useCallback(() => {\n onOpenChangeComplete?.(false);\n destroyModal(id);\n }, [id, onOpenChangeComplete]);\n\n const close = useCallback(() => closeModal(id), [id]);\n const setCanDismissByClickOutside = useCallback(\n (value: boolean) => updateModal(id, { maskClosable: value }),\n [id],\n );\n\n const showTitle = title !== undefined && title !== false && title !== null;\n\n return (\n <ModalContext value={{ close, setCanDismissByClickOutside }}>\n <ModalRoot\n open={isOpen}\n onExitComplete={handleExitComplete}\n onOpenChange={handleOpenChange}\n >\n <ModalPortal>\n <ModalBackdrop className={classNames?.backdrop} style={semanticStyles?.backdrop} />\n <ModalPopup\n className={classNames?.popup}\n popupStyle={semanticStyles?.popup}\n width={width}\n >\n {showTitle && (\n <ModalHeader className={classNames?.header} style={semanticStyles?.header}>\n <ModalTitle className={classNames?.title} style={semanticStyles?.title}>\n {title}\n </ModalTitle>\n <ModalClose className={classNames?.close} style={semanticStyles?.close} />\n </ModalHeader>\n )}\n <ModalContent className={classNames?.content} style={semanticStyles?.content}>\n {content ?? children}\n </ModalContent>\n {footer}\n </ModalPopup>\n </ModalPortal>\n </ModalRoot>\n </ModalContext>\n );\n });\n StackItem.displayName = 'ModalStackItem';\n\n const StackRenderer = memo(({ stack }: { stack: ModalStackEntry[] }) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((entry) => <StackItem entry={entry} key={entry.id} />);\n });\n StackRenderer.displayName = 'ModalStackRenderer';\n\n // --- ModalHost ---\n\n const Host = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n const scope = root ?? document.body;\n return registerDevSingleton(singletonName, scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortalWrapper root={root}>\n <StackRenderer stack={stack} />\n </ModalPortalWrapper>\n );\n };\n\n // --- createModal ---\n\n const create = (props: ImperativeModalProps): ModalInstance => {\n const id = `base-modal-${Date.now()}-${modalSeed++}`;\n modalStack = [...modalStack, { id, props: { ...props, open: props.open ?? true } }];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { maskClosable: value }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n };\n\n // --- confirmModal ---\n\n const confirm = (config: ModalConfirmConfig) => {\n const instance = create({\n content: <ConfirmBody config={config} />,\n styles: { content: { padding: 0 } },\n title: config.title,\n width: 420,\n });\n\n return {\n close: instance.close,\n destroy: instance.destroy,\n };\n };\n\n return { ModalHost: Host, confirmModal: confirm, createModal: create };\n}\n\n// --- Default global instance (backward compatible) ---\n\nconst defaultSystem = createModalSystem();\nexport const ModalHost = defaultSystem.ModalHost;\nexport const createModal = defaultSystem.createModal;\nexport const confirmModal = defaultSystem.confirmModal;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"imperative.mjs","names":[],"sources":["../../../src/base-ui/Modal/imperative.tsx"],"sourcesContent":["'use client';\n\nimport { cx, useTheme } from 'antd-style';\nimport type { ReactNode } from 'react';\nimport { memo, useCallback, useEffect, useRef, useState, useSyncExternalStore } from 'react';\nimport { createPortal } from 'react-dom';\n\nimport { useIsClient } from '@/hooks/useIsClient';\nimport { useAppElement } from '@/ThemeProvider';\nimport { registerDevSingleton } from '@/utils/devSingleton';\nimport { safeReadableColor } from '@/utils/safeReadableColor';\n\nimport {\n ModalBackdrop,\n ModalClose,\n ModalContent,\n ModalFooter,\n ModalHeader,\n ModalPopup,\n ModalPortal,\n ModalRoot,\n ModalTitle,\n} from './atoms';\nimport { ModalContext, useModalContext } from './context';\nimport { styles } from './style';\nimport type { ImperativeModalProps, ModalConfirmConfig, ModalInstance } from './type';\nimport { acquireModalZIndex } from './zIndexManager';\n\n// --- Shared types ---\n\ntype ModalStackEntry = {\n id: string;\n props: ImperativeModalProps;\n};\n\n// --- Shared components (stack-independent) ---\n\nconst ModalPortalWrapper = ({\n children,\n root,\n}: {\n children: ReactNode;\n root?: HTMLElement | ShadowRoot | null;\n}) => {\n const appElement = useAppElement();\n const container = root ?? appElement ?? document.body;\n return createPortal(children, container);\n};\n\nconst ConfirmBody = ({ config }: { config: ModalConfirmConfig }) => {\n const { close } = useModalContext();\n const theme = useTheme();\n const [loading, setLoading] = useState(false);\n\n const { cancelText = 'Cancel', content, okButtonProps, okText = 'OK', onCancel, onOk } = config;\n\n const { danger, className: okUserCls, style: okUserStyle, ...restOkProps } = okButtonProps ?? {};\n const okBgColor = danger ? theme.colorError : theme.colorPrimary;\n const okTextColor = safeReadableColor(okBgColor);\n\n const handleCancel = useCallback(() => {\n close();\n onCancel?.();\n }, [close, onCancel]);\n\n const handleOk = useCallback(async () => {\n if (onOk) {\n try {\n const result = onOk();\n if (result && typeof (result as any).then === 'function') {\n setLoading(true);\n await result;\n setLoading(false);\n }\n } catch {\n setLoading(false);\n return;\n }\n }\n close();\n }, [close, onOk]);\n\n return (\n <>\n {content && <div style={{ padding: '16px 24px' }}>{content}</div>}\n <ModalFooter>\n <button\n className={cx(styles.buttonBase, styles.cancelButton)}\n type=\"button\"\n onClick={handleCancel}\n >\n {cancelText}\n </button>\n <button\n {...restOkProps}\n disabled={loading}\n style={{ color: okTextColor, ...okUserStyle }}\n type=\"button\"\n className={cx(\n styles.buttonBase,\n danger ? styles.dangerOkButton : styles.okButton,\n okUserCls,\n )}\n onClick={handleOk}\n >\n {loading && <span className={styles.loadingSpinner} />}\n {okText}\n </button>\n </ModalFooter>\n </>\n );\n};\nConfirmBody.displayName = 'ConfirmBody';\n\n// --- Factory ---\n\nexport interface ModalHostProps {\n root?: HTMLElement | ShadowRoot | null;\n}\n\nexport interface ModalSystem {\n confirmModal: (config: ModalConfirmConfig) => { close: () => void; destroy: () => void };\n createModal: (props: ImperativeModalProps) => ModalInstance;\n ModalHost: React.FC<ModalHostProps>;\n}\n\nlet systemSeed = 0;\n\nexport function createModalSystem(): ModalSystem {\n const systemId = systemSeed++;\n const singletonName = systemId === 0 ? 'BaseModalHost' : `BaseModalHost-${systemId}`;\n\n // --- Stack state (isolated per system) ---\n let modalStack: ModalStackEntry[] = [];\n let modalSeed = 0;\n const listeners = new Set<() => void>();\n\n const notify = () => listeners.forEach((l) => l());\n const subscribe = (l: () => void) => {\n listeners.add(l);\n return () => listeners.delete(l);\n };\n const EMPTY: ModalStackEntry[] = [];\n const getSnapshot = () => modalStack;\n const getServerSnapshot = () => EMPTY;\n\n // --- Stack operations ---\n\n const updateModal = (id: string, next: Partial<ImperativeModalProps>) => {\n let changed = false;\n modalStack = modalStack.map((item) => {\n if (item.id !== id) return item;\n changed = true;\n return { ...item, props: { ...item.props, ...next } };\n });\n if (changed) notify();\n };\n\n const closeModal = (id: string) => {\n updateModal(id, { open: false });\n };\n\n const destroyModal = (id: string) => {\n const next = modalStack.filter((item) => item.id !== id);\n if (next.length === modalStack.length) return;\n modalStack = next;\n notify();\n };\n\n // --- Stack Item (captures operations via closure) ---\n\n const StackItem = memo(({ entry }: { entry: ModalStackEntry }) => {\n const { id, props } = entry;\n const {\n children,\n classNames,\n content,\n footer,\n maskClosable,\n onOpenChange,\n onOpenChangeComplete,\n open,\n styles: semanticStyles,\n title,\n width,\n } = props;\n\n const isOpen = open ?? true;\n\n const zIndexRef = useRef<number | undefined>(undefined);\n const prevOpenRef = useRef(false);\n if (isOpen && !prevOpenRef.current) {\n zIndexRef.current = acquireModalZIndex();\n }\n prevOpenRef.current = isOpen;\n const zIndex = zIndexRef.current ?? 1000;\n\n const handleOpenChange = useCallback(\n (nextOpen: boolean, eventDetails?: { reason: string }) => {\n if (!nextOpen && maskClosable === false && eventDetails?.reason === 'outside-press') return;\n if (!nextOpen) closeModal(id);\n onOpenChange?.(nextOpen);\n },\n [id, maskClosable, onOpenChange],\n );\n\n const handleExitComplete = useCallback(() => {\n onOpenChangeComplete?.(false);\n destroyModal(id);\n }, [id, onOpenChangeComplete]);\n\n const close = useCallback(() => closeModal(id), [id]);\n const setCanDismissByClickOutside = useCallback(\n (value: boolean) => updateModal(id, { maskClosable: value }),\n [id],\n );\n\n const showTitle = title !== undefined && title !== false && title !== null;\n\n return (\n <ModalContext value={{ close, setCanDismissByClickOutside }}>\n <ModalRoot\n open={isOpen}\n onExitComplete={handleExitComplete}\n onOpenChange={handleOpenChange}\n >\n <ModalPortal>\n <ModalBackdrop\n className={classNames?.backdrop}\n style={{ zIndex, ...semanticStyles?.backdrop }}\n />\n <ModalPopup\n className={classNames?.popup}\n popupStyle={{ zIndex: zIndex + 1, ...semanticStyles?.popup }}\n width={width}\n >\n {showTitle && (\n <ModalHeader className={classNames?.header} style={semanticStyles?.header}>\n <ModalTitle className={classNames?.title} style={semanticStyles?.title}>\n {title}\n </ModalTitle>\n <ModalClose className={classNames?.close} style={semanticStyles?.close} />\n </ModalHeader>\n )}\n <ModalContent className={classNames?.content} style={semanticStyles?.content}>\n {content ?? children}\n </ModalContent>\n {footer}\n </ModalPopup>\n </ModalPortal>\n </ModalRoot>\n </ModalContext>\n );\n });\n StackItem.displayName = 'ModalStackItem';\n\n const StackRenderer = memo(({ stack }: { stack: ModalStackEntry[] }) => {\n const isClient = useIsClient();\n if (!isClient) return null;\n return stack.map((entry) => <StackItem entry={entry} key={entry.id} />);\n });\n StackRenderer.displayName = 'ModalStackRenderer';\n\n // --- ModalHost ---\n\n const Host = ({ root }: ModalHostProps) => {\n const stack = useSyncExternalStore(subscribe, getSnapshot, getServerSnapshot);\n const isClient = useIsClient();\n\n useEffect(() => {\n if (!isClient) return;\n const scope = root ?? document.body;\n return registerDevSingleton(singletonName, scope);\n }, [isClient, root]);\n\n if (!isClient) return null;\n if (stack.length === 0) return null;\n\n return (\n <ModalPortalWrapper root={root}>\n <StackRenderer stack={stack} />\n </ModalPortalWrapper>\n );\n };\n\n // --- createModal ---\n\n const create = (props: ImperativeModalProps): ModalInstance => {\n const id = `base-modal-${Date.now()}-${modalSeed++}`;\n modalStack = [...modalStack, { id, props: { ...props, open: props.open ?? true } }];\n notify();\n\n return {\n close: () => closeModal(id),\n destroy: () => destroyModal(id),\n setCanDismissByClickOutside: (value) => updateModal(id, { maskClosable: value }),\n update: (nextProps) => updateModal(id, nextProps),\n };\n };\n\n // --- confirmModal ---\n\n const confirm = (config: ModalConfirmConfig) => {\n const instance = create({\n content: <ConfirmBody config={config} />,\n styles: { content: { padding: 0 } },\n title: config.title,\n width: 420,\n });\n\n return {\n close: instance.close,\n destroy: instance.destroy,\n };\n };\n\n return { ModalHost: Host, confirmModal: confirm, createModal: create };\n}\n\n// --- Default global instance (backward compatible) ---\n\nconst defaultSystem = createModalSystem();\nexport const ModalHost = defaultSystem.ModalHost;\nexport const createModal = defaultSystem.createModal;\nexport const confirmModal = defaultSystem.confirmModal;\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"zIndexManager.mjs","names":[],"sources":["../../../src/base-ui/Modal/zIndexManager.ts"],"sourcesContent":["const BASE_MODAL_Z_INDEX = 1000;\n\nlet top = BASE_MODAL_Z_INDEX;\n\nexport const acquireModalZIndex = () => {\n top += 10;\n return top;\n};\n"],"mappings":";AAEA,IAAI,MAFuB;AAI3B,MAAa,2BAA2B;AACtC,QAAO;AACP,QAAO"}
|
package/es/hooks/useMermaid.mjs
CHANGED
|
@@ -17,10 +17,10 @@ const loadMermaid = () => {
|
|
|
17
17
|
if (!mermaidPromise) mermaidPromise = import("mermaid").then((mod) => mod.default);
|
|
18
18
|
return mermaidPromise;
|
|
19
19
|
};
|
|
20
|
-
const createMermaidConfig = (theme, customTheme) => ({
|
|
20
|
+
const createMermaidConfig = (theme, customTheme, securityLevel = "strict") => ({
|
|
21
21
|
fontFamily: theme.fontFamilyCode,
|
|
22
22
|
gantt: { useWidth: 1920 },
|
|
23
|
-
securityLevel
|
|
23
|
+
securityLevel,
|
|
24
24
|
startOnLoad: false,
|
|
25
25
|
theme: customTheme || (theme.isDarkMode ? "dark" : "neutral"),
|
|
26
26
|
themeVariables: customTheme ? void 0 : {
|
|
@@ -50,10 +50,10 @@ const createMermaidConfig = (theme, customTheme) => ({
|
|
|
50
50
|
/**
|
|
51
51
|
* 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)
|
|
52
52
|
*/
|
|
53
|
-
const useMermaid = (content, { id, theme: customTheme }) => {
|
|
53
|
+
const useMermaid = (content, { id, theme: customTheme, securityLevel }) => {
|
|
54
54
|
const theme = useTheme();
|
|
55
55
|
const [data, setData] = useState("");
|
|
56
|
-
const mermaidConfig = useMemo(() => createMermaidConfig(theme, customTheme), [
|
|
56
|
+
const mermaidConfig = useMemo(() => createMermaidConfig(theme, customTheme, securityLevel), [
|
|
57
57
|
theme.fontFamilyCode,
|
|
58
58
|
theme.isDarkMode,
|
|
59
59
|
theme.colorTextDescription,
|
|
@@ -72,7 +72,8 @@ const useMermaid = (content, { id, theme: customTheme }) => {
|
|
|
72
72
|
theme.colorSuccessBg,
|
|
73
73
|
theme.colorSuccessText,
|
|
74
74
|
theme.colorText,
|
|
75
|
-
customTheme
|
|
75
|
+
customTheme,
|
|
76
|
+
securityLevel
|
|
76
77
|
]);
|
|
77
78
|
const cacheKey = useMemo(() => {
|
|
78
79
|
const hash = content.length < 1e4 ? content : Md5.hashStr(content);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useMermaid.mjs","names":[],"sources":["../../src/hooks/useMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Md5 } from 'ts-md5';\n\n// 缓存已验证的图表内容\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\n// Application-level cache for rendered Mermaid SVG\n// Key: cacheKey string, Value: Promise<string>\nconst mermaidCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 500;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (mermaidCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(mermaidCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n mermaidCache.delete(key);\n }\n }\n};\n\n// 懒加载 mermaid 实例\nlet mermaidPromise: Promise<typeof import('mermaid').default | null> | null = null;\n\nexport const loadMermaid = (): Promise<typeof import('mermaid').default | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => mod.default);\n }\n\n return mermaidPromise;\n};\n\n// Helper to create mermaid config\nexport const createMermaidConfig = (\n theme: ReturnType<typeof useTheme>,\n customTheme?: MermaidConfig['theme'],\n): MermaidConfig => ({\n fontFamily: theme.fontFamilyCode,\n gantt: {\n useWidth: 1920,\n },\n securityLevel
|
|
1
|
+
{"version":3,"file":"useMermaid.mjs","names":[],"sources":["../../src/hooks/useMermaid.ts"],"sourcesContent":["'use client';\n\nimport { useTheme } from 'antd-style';\nimport type { MermaidConfig } from 'mermaid';\nimport { useEffect, useMemo, useState } from 'react';\nimport { Md5 } from 'ts-md5';\n\n// 缓存已验证的图表内容\nexport const MD5_LENGTH_THRESHOLD = 10_000;\n\n// Application-level cache for rendered Mermaid SVG\n// Key: cacheKey string, Value: Promise<string>\nconst mermaidCache = new Map<string, Promise<string>>();\n\n// Maximum cache size to prevent memory leaks\nconst MAX_CACHE_SIZE = 500;\n\n// Clean up old cache entries when limit is reached\nconst cleanupCache = () => {\n if (mermaidCache.size > MAX_CACHE_SIZE) {\n // Remove oldest 20% of entries\n const entriesToRemove = Math.floor(MAX_CACHE_SIZE * 0.2);\n const keysToRemove = Array.from(mermaidCache.keys()).slice(0, entriesToRemove);\n for (const key of keysToRemove) {\n mermaidCache.delete(key);\n }\n }\n};\n\n// 懒加载 mermaid 实例\nlet mermaidPromise: Promise<typeof import('mermaid').default | null> | null = null;\n\nexport const loadMermaid = (): Promise<typeof import('mermaid').default | null> => {\n if (typeof window === 'undefined') return Promise.resolve(null);\n\n if (!mermaidPromise) {\n mermaidPromise = import('mermaid').then((mod) => mod.default);\n }\n\n return mermaidPromise;\n};\n\n// Helper to create mermaid config\nexport const createMermaidConfig = (\n theme: ReturnType<typeof useTheme>,\n customTheme?: MermaidConfig['theme'],\n // SECURITY: Keep 'strict' as the default. Using 'loose' causes Mermaid to render\n // node labels via innerHTML, enabling XSS when diagram content is user-controlled.\n // Only pass 'loose' if your use case explicitly requires HTML labels and you control\n // the diagram source entirely.\n securityLevel: MermaidConfig['securityLevel'] = 'strict',\n): MermaidConfig => ({\n fontFamily: theme.fontFamilyCode,\n gantt: {\n useWidth: 1920,\n },\n securityLevel,\n startOnLoad: false,\n theme: customTheme || (theme.isDarkMode ? 'dark' : 'neutral'),\n themeVariables: customTheme\n ? undefined\n : {\n errorBkgColor: theme.colorTextDescription,\n errorTextColor: theme.colorTextDescription,\n fontFamily: theme.fontFamily,\n lineColor: theme.colorTextSecondary,\n mainBkg: theme.colorBgContainer,\n noteBkgColor: theme.colorInfoBg,\n noteTextColor: theme.colorInfoText,\n pie1: theme.geekblue,\n pie2: theme.colorWarning,\n pie3: theme.colorSuccess,\n pie4: theme.colorError,\n primaryBorderColor: theme.colorBorder,\n primaryColor: theme.colorBgContainer,\n primaryTextColor: theme.colorText,\n secondaryBorderColor: theme.colorInfoBorder,\n secondaryColor: theme.colorInfoBg,\n secondaryTextColor: theme.colorInfoText,\n tertiaryBorderColor: theme.colorSuccessBorder,\n tertiaryColor: theme.colorSuccessBg,\n tertiaryTextColor: theme.colorSuccessText,\n textColor: theme.colorText,\n },\n});\n\n/**\n * 验证并处理 Mermaid 图表内容 - 优化版本(移除 SWR)\n */\nexport const useMermaid = (\n content: string,\n {\n id,\n theme: customTheme,\n securityLevel,\n }: {\n id: string;\n // SECURITY: Defaults to 'strict'. Set to 'loose' only when you fully control\n // the diagram source and intentionally need HTML rendering in node labels.\n securityLevel?: MermaidConfig['securityLevel'];\n theme?: MermaidConfig['theme'];\n },\n): string => {\n const theme = useTheme();\n const [data, setData] = useState<string>('');\n\n // 提取主题相关配置到 useMemo 中 - 只依赖实际使用的 theme 属性\n const mermaidConfig = useMemo(\n () => createMermaidConfig(theme, customTheme, securityLevel),\n [\n theme.fontFamilyCode,\n theme.isDarkMode,\n theme.colorTextDescription,\n theme.fontFamily,\n theme.colorTextSecondary,\n theme.colorBgContainer,\n theme.colorInfoBg,\n theme.colorInfoText,\n theme.geekblue,\n theme.colorWarning,\n theme.colorSuccess,\n theme.colorError,\n theme.colorBorder,\n theme.colorInfoBorder,\n theme.colorSuccessBorder,\n theme.colorSuccessBg,\n theme.colorSuccessText,\n theme.colorText,\n customTheme,\n securityLevel,\n ],\n );\n\n // 为长内容生成哈希键\n const cacheKey = useMemo((): string => {\n const hash = content.length < MD5_LENGTH_THRESHOLD ? content : Md5.hashStr(content);\n return [id, customTheme || (theme.isDarkMode ? 'd' : 'l'), hash].filter(Boolean).join('-');\n }, [content, id, theme.isDarkMode, customTheme]);\n\n useEffect(() => {\n // Check cache first\n const cachedPromise = mermaidCache.get(cacheKey);\n if (cachedPromise) {\n cachedPromise\n .then((svg) => {\n setData(svg);\n })\n .catch(() => {\n // Silently handle errors, fallback will be handled in the promise\n });\n return;\n }\n\n // Create new promise for rendering\n const renderPromise = (async (): Promise<string> => {\n try {\n const mermaidInstance = await loadMermaid();\n if (!mermaidInstance) return '';\n\n // 验证语法\n const isValid = await mermaidInstance.parse(content);\n\n if (isValid) {\n // 初始化并渲染\n mermaidInstance.initialize(mermaidConfig);\n const { svg } = await mermaidInstance.render(id, content);\n return svg;\n } else {\n throw new Error('Mermaid 语法无效');\n }\n } catch (error_) {\n console.error('Mermaid 解析错误:', error_);\n return '';\n }\n })();\n\n // Cache the promise\n mermaidCache.set(cacheKey, renderPromise);\n cleanupCache();\n\n // Handle promise result\n renderPromise\n .then((svg) => {\n // Only update if this is still the current cache key\n if (mermaidCache.get(cacheKey) === renderPromise) {\n setData(svg);\n }\n })\n .catch(() => {\n // Remove failed promise from cache\n if (mermaidCache.get(cacheKey) === renderPromise) {\n mermaidCache.delete(cacheKey);\n }\n });\n }, [cacheKey, content, id, mermaidConfig]);\n\n return data;\n};\n"],"mappings":";;;;AAYA,MAAM,+BAAe,IAAI,KAA8B;AAGvD,MAAM,iBAAiB;AAGvB,MAAM,qBAAqB;AACzB,KAAI,aAAa,OAAO,gBAAgB;EAEtC,MAAM,kBAAkB,KAAK,MAAM,iBAAiB,GAAI;EACxD,MAAM,eAAe,MAAM,KAAK,aAAa,MAAM,CAAC,CAAC,MAAM,GAAG,gBAAgB;AAC9E,OAAK,MAAM,OAAO,aAChB,cAAa,OAAO,IAAI;;;AAM9B,IAAI,iBAA0E;AAE9E,MAAa,oBAAsE;AACjF,KAAI,OAAO,WAAW,YAAa,QAAO,QAAQ,QAAQ,KAAK;AAE/D,KAAI,CAAC,eACH,kBAAiB,OAAO,WAAW,MAAM,QAAQ,IAAI,QAAQ;AAG/D,QAAO;;AAIT,MAAa,uBACX,OACA,aAKA,gBAAgD,cAC7B;CACnB,YAAY,MAAM;CAClB,OAAO,EACL,UAAU,MACX;CACD;CACA,aAAa;CACb,OAAO,gBAAgB,MAAM,aAAa,SAAS;CACnD,gBAAgB,cACZ,KAAA,IACA;EACE,eAAe,MAAM;EACrB,gBAAgB,MAAM;EACtB,YAAY,MAAM;EAClB,WAAW,MAAM;EACjB,SAAS,MAAM;EACf,cAAc,MAAM;EACpB,eAAe,MAAM;EACrB,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,MAAM,MAAM;EACZ,oBAAoB,MAAM;EAC1B,cAAc,MAAM;EACpB,kBAAkB,MAAM;EACxB,sBAAsB,MAAM;EAC5B,gBAAgB,MAAM;EACtB,oBAAoB,MAAM;EAC1B,qBAAqB,MAAM;EAC3B,eAAe,MAAM;EACrB,mBAAmB,MAAM;EACzB,WAAW,MAAM;EAClB;CACN;;;;AAKD,MAAa,cACX,SACA,EACE,IACA,OAAO,aACP,oBAQS;CACX,MAAM,QAAQ,UAAU;CACxB,MAAM,CAAC,MAAM,WAAW,SAAiB,GAAG;CAG5C,MAAM,gBAAgB,cACd,oBAAoB,OAAO,aAAa,cAAc,EAC5D;EACE,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN,MAAM;EACN;EACA;EACD,CACF;CAGD,MAAM,WAAW,cAAsB;EACrC,MAAM,OAAO,QAAQ,SAAA,MAAgC,UAAU,IAAI,QAAQ,QAAQ;AACnF,SAAO;GAAC;GAAI,gBAAgB,MAAM,aAAa,MAAM;GAAM;GAAK,CAAC,OAAO,QAAQ,CAAC,KAAK,IAAI;IACzF;EAAC;EAAS;EAAI,MAAM;EAAY;EAAY,CAAC;AAEhD,iBAAgB;EAEd,MAAM,gBAAgB,aAAa,IAAI,SAAS;AAChD,MAAI,eAAe;AACjB,iBACG,MAAM,QAAQ;AACb,YAAQ,IAAI;KACZ,CACD,YAAY,GAEX;AACJ;;EAIF,MAAM,iBAAiB,YAA6B;AAClD,OAAI;IACF,MAAM,kBAAkB,MAAM,aAAa;AAC3C,QAAI,CAAC,gBAAiB,QAAO;AAK7B,QAFgB,MAAM,gBAAgB,MAAM,QAAQ,EAEvC;AAEX,qBAAgB,WAAW,cAAc;KACzC,MAAM,EAAE,QAAQ,MAAM,gBAAgB,OAAO,IAAI,QAAQ;AACzD,YAAO;UAEP,OAAM,IAAI,MAAM,eAAe;YAE1B,QAAQ;AACf,YAAQ,MAAM,iBAAiB,OAAO;AACtC,WAAO;;MAEP;AAGJ,eAAa,IAAI,UAAU,cAAc;AACzC,gBAAc;AAGd,gBACG,MAAM,QAAQ;AAEb,OAAI,aAAa,IAAI,SAAS,KAAK,cACjC,SAAQ,IAAI;IAEd,CACD,YAAY;AAEX,OAAI,aAAa,IAAI,SAAS,KAAK,cACjC,cAAa,OAAO,SAAS;IAE/B;IACH;EAAC;EAAU;EAAS;EAAI;EAAc,CAAC;AAE1C,QAAO"}
|
|
@@ -205,12 +205,11 @@ const useStreamHighlight = (text, { language, theme: builtinTheme, streaming })
|
|
|
205
205
|
const safeText = text ?? "";
|
|
206
206
|
const lang = (language ?? "plaintext").toLowerCase();
|
|
207
207
|
const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);
|
|
208
|
-
const effectiveTheme = builtinTheme || "lobe-theme";
|
|
209
208
|
return useStreamingHighlighter(safeText, {
|
|
210
209
|
customThemes: { "lobe-theme": lobe_theme_default },
|
|
211
210
|
enabled: streaming,
|
|
212
211
|
language: matchedLanguage,
|
|
213
|
-
theme:
|
|
212
|
+
theme: builtinTheme || "lobe-theme"
|
|
214
213
|
});
|
|
215
214
|
};
|
|
216
215
|
//#endregion
|