@lobehub/ui 5.7.0 → 5.8.0
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/EmojiPicker/EmojiPicker.mjs +24 -2
- package/es/EmojiPicker/EmojiPicker.mjs.map +1 -1
- package/es/EmojiPicker/type.d.mts +1 -0
- package/es/awesome/GridBackground/components/Grid.mjs +15 -33
- package/es/awesome/GridBackground/components/Grid.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/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
|
@@ -18,10 +18,11 @@ import { SmileIcon, TrashIcon, UploadIcon } from "lucide-react";
|
|
|
18
18
|
import chroma from "chroma-js";
|
|
19
19
|
import data from "@emoji-mart/data";
|
|
20
20
|
import Picker from "@emoji-mart/react";
|
|
21
|
+
import { getLobeIconCDN, toc } from "@lobehub/icons";
|
|
21
22
|
import useSWR from "swr";
|
|
22
23
|
//#region src/EmojiPicker/EmojiPicker.tsx
|
|
23
24
|
const DEFAULT_AVATAR = "🤖";
|
|
24
|
-
const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, locale = "en-US", allowUpload, allowDelete, texts, onDelete, compressSize = 256, customEmojis, className, loading, onUpload, customTabs = [], popupClassName, popupStyle, customRender, open, defaultOpen = false, onOpenChange, popupProps, shape, contentProps, ...rest }) => {
|
|
25
|
+
const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, locale = "en-US", allowUpload, allowDelete, allowModelAvatar, texts, onDelete, compressSize = 256, customEmojis, className, loading, onUpload, customTabs = [], popupClassName, popupStyle, customRender, open, defaultOpen = false, onOpenChange, popupProps, shape, contentProps, ...rest }) => {
|
|
25
26
|
const ref = useRef(null);
|
|
26
27
|
const { t } = useTranslation(emojiPicker_default);
|
|
27
28
|
const [visible, setVisible] = useMergeState(defaultOpen, {
|
|
@@ -48,6 +49,27 @@ const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, loc
|
|
|
48
49
|
setAva(emoji);
|
|
49
50
|
setVisible(false);
|
|
50
51
|
};
|
|
52
|
+
const mergedCustomEmojis = useMemo(() => {
|
|
53
|
+
if (!allowModelAvatar) return customEmojis;
|
|
54
|
+
const modelEmojisCategory = {
|
|
55
|
+
id: "models",
|
|
56
|
+
name: "Models / Providers",
|
|
57
|
+
emojis: toc.filter((item) => item.group !== "application").map((item) => ({
|
|
58
|
+
id: item.id.toLowerCase(),
|
|
59
|
+
name: item.title,
|
|
60
|
+
keywords: [
|
|
61
|
+
item.title,
|
|
62
|
+
item.id,
|
|
63
|
+
item.fullTitle
|
|
64
|
+
],
|
|
65
|
+
skins: [{ src: getLobeIconCDN(item.id, {
|
|
66
|
+
format: "avatar",
|
|
67
|
+
cdn: "aliyun"
|
|
68
|
+
}) }]
|
|
69
|
+
}))
|
|
70
|
+
};
|
|
71
|
+
return customEmojis ? [modelEmojisCategory, ...customEmojis] : [modelEmojisCategory];
|
|
72
|
+
}, [allowModelAvatar, customEmojis]);
|
|
51
73
|
const emojiText = texts?.emoji ?? t("emojiPicker.emoji");
|
|
52
74
|
const uploadText = texts?.upload ?? t("emojiPicker.upload");
|
|
53
75
|
const deleteText = texts?.delete ?? t("emojiPicker.delete");
|
|
@@ -121,7 +143,7 @@ const EmojiPicker = memo(({ value, defaultAvatar = DEFAULT_AVATAR, onChange, loc
|
|
|
121
143
|
})]
|
|
122
144
|
}),
|
|
123
145
|
tab === "emoji" && /* @__PURE__ */ jsx(Picker, {
|
|
124
|
-
custom:
|
|
146
|
+
custom: mergedCustomEmojis,
|
|
125
147
|
data,
|
|
126
148
|
i18n,
|
|
127
149
|
icons: "outline",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"EmojiPicker.mjs","names":["emojiPickerMessages","Flexbox"],"sources":["../../src/EmojiPicker/EmojiPicker.tsx"],"sourcesContent":["'use client';\n\nimport data from '@emoji-mart/data';\nimport Picker from '@emoji-mart/react';\nimport { cx, useTheme } from 'antd-style';\nimport chroma from 'chroma-js';\nimport { SmileIcon, TrashIcon, UploadIcon } from 'lucide-react';\nimport { memo, useMemo, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport useMergeState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport Avatar from '@/Avatar';\nimport { Flexbox } from '@/Flex';\nimport emojiPickerMessages from '@/i18n/resources/en/emojiPicker';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport Popover from '@/Popover';\nimport Tabs, { type TabsProps } from '@/Tabs';\nimport Tooltip from '@/Tooltip';\n\nimport AvatarUploader from './AvatarUploader';\nimport { styles } from './style';\nimport { type EmojiPickerProps } from './type';\n\nconst DEFAULT_AVATAR = '🤖';\n\nconst EmojiPicker = memo<EmojiPickerProps>(\n ({\n value,\n defaultAvatar = DEFAULT_AVATAR,\n onChange,\n locale = 'en-US',\n allowUpload,\n allowDelete,\n texts,\n onDelete,\n compressSize = 256,\n customEmojis,\n className,\n loading,\n onUpload,\n customTabs = [],\n popupClassName,\n popupStyle,\n customRender,\n open,\n defaultOpen = false,\n onOpenChange,\n popupProps,\n shape,\n contentProps,\n ...rest\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const { t } = useTranslation(emojiPickerMessages);\n const [visible, setVisible] = useMergeState(defaultOpen, {\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n value: open,\n });\n const [tab, setTab] = useState<'emoji' | 'upload'>('emoji');\n\n const theme = useTheme();\n const pickerCssVariables = useMemo<Record<string, string>>(\n () => ({\n '--emoji-picker-rgb-accent': chroma(theme.colorPrimary).rgb().join(', '),\n '--emoji-picker-rgb-background': chroma(theme.colorBgElevated).rgb().join(', '),\n }),\n [theme.colorPrimary, theme.colorBgElevated],\n );\n\n const { data: i18n } = useSWR(\n locale,\n async () => await import(`@emoji-mart/data/i18n/${locale.split('-')[0]}.json`),\n { revalidateOnFocus: false, revalidateOnMount: false },\n );\n\n const [ava, setAva] = useMergeState(defaultAvatar, {\n defaultValue: defaultAvatar,\n onChange,\n value,\n });\n\n const handleAvatarChange = (emoji: string) => {\n setAva(emoji);\n setVisible(false);\n };\n\n const emojiText = texts?.emoji ?? t('emojiPicker.emoji');\n const uploadText = texts?.upload ?? t('emojiPicker.upload');\n const deleteText = texts?.delete ?? t('emojiPicker.delete');\n\n const hideEmojiTab = typeof allowUpload === 'object' && allowUpload?.enableEmoji === false;\n\n const items: TabsProps['items'] = [\n !hideEmojiTab && {\n key: 'emoji',\n label: (\n <Tooltip title={emojiText}>\n <Icon icon={SmileIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n allowUpload && {\n key: 'upload',\n label: (\n <Tooltip title={uploadText}>\n <Icon icon={UploadIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n ...customTabs.map((tab) => ({ key: tab.value, label: tab.label })),\n ].filter(Boolean) as TabsProps['items'];\n\n const showTabs = allowDelete || (items && items.length > 1);\n\n const content = (\n <Flexbox\n className={cx(styles.picker, popupClassName)}\n ref={ref}\n style={{\n minWidth: 310,\n paddingTop: showTabs ? 4 : 0,\n ...pickerCssVariables,\n ...popupStyle,\n }}\n {...contentProps}\n >\n {showTabs && (\n <Flexbox\n horizontal\n align={'center'}\n className={styles.tabs}\n justify={'space-between'}\n paddingInline={10}\n >\n <Tabs\n compact\n activeKey={tab}\n items={items}\n size={'small'}\n onChange={(key) => setTab(key as any)}\n />\n {allowDelete && (\n <ActionIcon\n icon={TrashIcon}\n title={deleteText}\n size={{\n blockSize: 32,\n size: 18,\n }}\n onClick={() => {\n handleAvatarChange(defaultAvatar);\n onDelete?.();\n }}\n />\n )}\n </Flexbox>\n )}\n {tab === 'emoji' && (\n <Picker\n custom={customEmojis}\n data={data}\n i18n={i18n}\n icons={'outline'}\n locale={locale.split('-')[0]}\n navPosition={showTabs ? 'bottom' : 'top'}\n previewPosition={'none'}\n skinTonePosition={'none'}\n theme={theme.isDarkMode ? 'dark' : 'light'}\n onEmojiSelect={(e: any) => handleAvatarChange(e.src || e.native)}\n />\n )}\n {tab === 'upload' && (\n <AvatarUploader\n compressSize={compressSize}\n shape={shape}\n texts={texts}\n onChange={handleAvatarChange}\n onUpload={onUpload}\n />\n )}\n {customTabs.map(\n (item) =>\n tab === item.value && (\n <Flexbox key={item.value} padding={10}>\n {item.render(handleAvatarChange)}\n </Flexbox>\n ),\n )}\n </Flexbox>\n );\n\n return (\n <Popover\n className={cx(styles.popover)}\n content={content}\n defaultOpen={defaultOpen}\n open={visible}\n placement={'bottom'}\n trigger={'click'}\n classNames={{\n content: styles.popover,\n root: styles.positioner,\n }}\n onOpenChange={(v) => {\n if (loading) return;\n setVisible(v);\n }}\n {...popupProps}\n >\n {customRender ? (\n customRender(ava)\n ) : (\n <Avatar\n avatar={ava}\n className={cx(styles.root, className)}\n loading={loading}\n shape={shape}\n {...rest}\n />\n )}\n </Popover>\n );\n },\n);\n\nEmojiPicker.displayName = 'EmojiPicker';\n\nexport default EmojiPicker;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"EmojiPicker.mjs","names":["emojiPickerMessages","Flexbox"],"sources":["../../src/EmojiPicker/EmojiPicker.tsx"],"sourcesContent":["'use client';\n\nimport data from '@emoji-mart/data';\nimport Picker from '@emoji-mart/react';\nimport { getLobeIconCDN, toc } from '@lobehub/icons';\nimport { cx, useTheme } from 'antd-style';\nimport chroma from 'chroma-js';\nimport { SmileIcon, TrashIcon, UploadIcon } from 'lucide-react';\nimport { memo, useMemo, useRef, useState } from 'react';\nimport useSWR from 'swr';\nimport useMergeState from 'use-merge-value';\n\nimport ActionIcon from '@/ActionIcon';\nimport Avatar from '@/Avatar';\nimport { Flexbox } from '@/Flex';\nimport emojiPickerMessages from '@/i18n/resources/en/emojiPicker';\nimport { useTranslation } from '@/i18n/useTranslation';\nimport Icon from '@/Icon';\nimport Popover from '@/Popover';\nimport Tabs, { type TabsProps } from '@/Tabs';\nimport Tooltip from '@/Tooltip';\n\nimport AvatarUploader from './AvatarUploader';\nimport { styles } from './style';\nimport { type EmojiPickerProps } from './type';\n\nconst DEFAULT_AVATAR = '🤖';\n\nconst EmojiPicker = memo<EmojiPickerProps>(\n ({\n value,\n defaultAvatar = DEFAULT_AVATAR,\n onChange,\n locale = 'en-US',\n allowUpload,\n allowDelete,\n allowModelAvatar,\n texts,\n onDelete,\n compressSize = 256,\n customEmojis,\n className,\n loading,\n onUpload,\n customTabs = [],\n popupClassName,\n popupStyle,\n customRender,\n open,\n defaultOpen = false,\n onOpenChange,\n popupProps,\n shape,\n contentProps,\n ...rest\n }) => {\n const ref = useRef<HTMLDivElement>(null);\n const { t } = useTranslation(emojiPickerMessages);\n const [visible, setVisible] = useMergeState(defaultOpen, {\n defaultValue: defaultOpen,\n onChange: onOpenChange,\n value: open,\n });\n const [tab, setTab] = useState<'emoji' | 'upload'>('emoji');\n\n const theme = useTheme();\n const pickerCssVariables = useMemo<Record<string, string>>(\n () => ({\n '--emoji-picker-rgb-accent': chroma(theme.colorPrimary).rgb().join(', '),\n '--emoji-picker-rgb-background': chroma(theme.colorBgElevated).rgb().join(', '),\n }),\n [theme.colorPrimary, theme.colorBgElevated],\n );\n\n const { data: i18n } = useSWR(\n locale,\n async () => await import(`@emoji-mart/data/i18n/${locale.split('-')[0]}.json`),\n { revalidateOnFocus: false, revalidateOnMount: false },\n );\n\n const [ava, setAva] = useMergeState(defaultAvatar, {\n defaultValue: defaultAvatar,\n onChange,\n value,\n });\n\n const handleAvatarChange = (emoji: string) => {\n setAva(emoji);\n setVisible(false);\n };\n\n // Generate model avatars from @lobehub/icons when allowModelAvatar is enabled\n const mergedCustomEmojis = useMemo(() => {\n if (!allowModelAvatar) return customEmojis;\n\n const modelIcons = toc.filter((item) => item.group !== 'application');\n\n const modelEmojisCategory = {\n id: 'models',\n name: 'Models / Providers',\n emojis: modelIcons.map((item) => ({\n id: item.id.toLowerCase(),\n name: item.title,\n keywords: [item.title, item.id, item.fullTitle],\n skins: [{ src: getLobeIconCDN(item.id, { format: 'avatar', cdn: 'aliyun' }) }],\n })),\n };\n\n return customEmojis ? [modelEmojisCategory, ...customEmojis] : [modelEmojisCategory];\n }, [allowModelAvatar, customEmojis]);\n\n const emojiText = texts?.emoji ?? t('emojiPicker.emoji');\n const uploadText = texts?.upload ?? t('emojiPicker.upload');\n const deleteText = texts?.delete ?? t('emojiPicker.delete');\n\n const hideEmojiTab = typeof allowUpload === 'object' && allowUpload?.enableEmoji === false;\n\n const items: TabsProps['items'] = [\n !hideEmojiTab && {\n key: 'emoji',\n label: (\n <Tooltip title={emojiText}>\n <Icon icon={SmileIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n allowUpload && {\n key: 'upload',\n label: (\n <Tooltip title={uploadText}>\n <Icon icon={UploadIcon} size={{ size: 20, strokeWidth: 2.5 }} />\n </Tooltip>\n ),\n },\n ...customTabs.map((tab) => ({ key: tab.value, label: tab.label })),\n ].filter(Boolean) as TabsProps['items'];\n\n const showTabs = allowDelete || (items && items.length > 1);\n\n const content = (\n <Flexbox\n className={cx(styles.picker, popupClassName)}\n ref={ref}\n style={{\n minWidth: 310,\n paddingTop: showTabs ? 4 : 0,\n ...pickerCssVariables,\n ...popupStyle,\n }}\n {...contentProps}\n >\n {showTabs && (\n <Flexbox\n horizontal\n align={'center'}\n className={styles.tabs}\n justify={'space-between'}\n paddingInline={10}\n >\n <Tabs\n compact\n activeKey={tab}\n items={items}\n size={'small'}\n onChange={(key) => setTab(key as any)}\n />\n {allowDelete && (\n <ActionIcon\n icon={TrashIcon}\n title={deleteText}\n size={{\n blockSize: 32,\n size: 18,\n }}\n onClick={() => {\n handleAvatarChange(defaultAvatar);\n onDelete?.();\n }}\n />\n )}\n </Flexbox>\n )}\n {tab === 'emoji' && (\n <Picker\n custom={mergedCustomEmojis}\n data={data}\n i18n={i18n}\n icons={'outline'}\n locale={locale.split('-')[0]}\n navPosition={showTabs ? 'bottom' : 'top'}\n previewPosition={'none'}\n skinTonePosition={'none'}\n theme={theme.isDarkMode ? 'dark' : 'light'}\n onEmojiSelect={(e: any) => handleAvatarChange(e.src || e.native)}\n />\n )}\n {tab === 'upload' && (\n <AvatarUploader\n compressSize={compressSize}\n shape={shape}\n texts={texts}\n onChange={handleAvatarChange}\n onUpload={onUpload}\n />\n )}\n {customTabs.map(\n (item) =>\n tab === item.value && (\n <Flexbox key={item.value} padding={10}>\n {item.render(handleAvatarChange)}\n </Flexbox>\n ),\n )}\n </Flexbox>\n );\n\n return (\n <Popover\n className={cx(styles.popover)}\n content={content}\n defaultOpen={defaultOpen}\n open={visible}\n placement={'bottom'}\n trigger={'click'}\n classNames={{\n content: styles.popover,\n root: styles.positioner,\n }}\n onOpenChange={(v) => {\n if (loading) return;\n setVisible(v);\n }}\n {...popupProps}\n >\n {customRender ? (\n customRender(ava)\n ) : (\n <Avatar\n avatar={ava}\n className={cx(styles.root, className)}\n loading={loading}\n shape={shape}\n {...rest}\n />\n )}\n </Popover>\n );\n },\n);\n\nEmojiPicker.displayName = 'EmojiPicker';\n\nexport default EmojiPicker;\n"],"mappings":"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"}
|
|
@@ -2,43 +2,25 @@ import { memo, useCallback } from "react";
|
|
|
2
2
|
import { Fragment as Fragment$1, jsx, jsxs } from "react/jsx-runtime";
|
|
3
3
|
import { isUndefined } from "es-toolkit/compat";
|
|
4
4
|
//#region src/awesome/GridBackground/components/Grid.tsx
|
|
5
|
-
var Line = /* @__PURE__ */ function(Line) {
|
|
6
|
-
Line[Line["l7"] = 0] = "l7";
|
|
7
|
-
Line[Line["l6"] = 1] = "l6";
|
|
8
|
-
Line[Line["l5"] = 2] = "l5";
|
|
9
|
-
Line[Line["l4"] = 3] = "l4";
|
|
10
|
-
Line[Line["l3"] = 4] = "l3";
|
|
11
|
-
Line[Line["l2"] = 5] = "l2";
|
|
12
|
-
Line[Line["l1"] = 6] = "l1";
|
|
13
|
-
Line[Line["center"] = 7] = "center";
|
|
14
|
-
Line[Line["r1"] = 8] = "r1";
|
|
15
|
-
Line[Line["r2"] = 9] = "r2";
|
|
16
|
-
Line[Line["r3"] = 10] = "r3";
|
|
17
|
-
Line[Line["r4"] = 11] = "r4";
|
|
18
|
-
Line[Line["r5"] = 12] = "r5";
|
|
19
|
-
Line[Line["r6"] = 13] = "r6";
|
|
20
|
-
Line[Line["r7"] = 14] = "r7";
|
|
21
|
-
return Line;
|
|
22
|
-
}(Line || {});
|
|
23
5
|
const Grid = memo(({ color = "#fff", strokeWidth = 3, linePick, ...rest }) => {
|
|
24
6
|
const isUnpick = isUndefined(linePick);
|
|
25
7
|
const showLine = useCallback((l) => isUnpick || linePick === l, [linePick]);
|
|
26
8
|
const vLine = /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
27
|
-
showLine(
|
|
28
|
-
showLine(
|
|
29
|
-
showLine(
|
|
30
|
-
showLine(
|
|
31
|
-
showLine(
|
|
32
|
-
showLine(
|
|
33
|
-
showLine(
|
|
34
|
-
showLine(
|
|
35
|
-
showLine(
|
|
36
|
-
showLine(
|
|
37
|
-
showLine(
|
|
38
|
-
showLine(
|
|
39
|
-
showLine(
|
|
40
|
-
showLine(
|
|
41
|
-
showLine(
|
|
9
|
+
showLine(0) && /* @__PURE__ */ jsx("path", { d: "M2 420v-60.343c0-21.82 14.15-41.12 34.959-47.684L1026 0h0" }),
|
|
10
|
+
showLine(1) && /* @__PURE__ */ jsx("path", { d: "M268 420v-62.077c0-20.977 13.094-39.724 32.789-46.944L1149 0h0" }),
|
|
11
|
+
showLine(2) && /* @__PURE__ */ jsx("path", { d: "M534 420v-64.358a50 50 0 0129.884-45.775L1269 0h0" }),
|
|
12
|
+
showLine(3) && /* @__PURE__ */ jsx("path", { d: "M800 420v-67.395a50 50 0 0125.958-43.84L1389 0h0" }),
|
|
13
|
+
showLine(4) && /* @__PURE__ */ jsx("path", { d: "M1066 420v-71.645a50 50 0 0120.456-40.337L1507 0h0" }),
|
|
14
|
+
showLine(5) && /* @__PURE__ */ jsx("path", { d: "M1332 420v-77.506a50 50 0 0113.194-33.843L1629 0h0" }),
|
|
15
|
+
showLine(6) && /* @__PURE__ */ jsx("path", { d: "M1598 420v-86.225a50 50 0 014.438-20.594L1744 0h0" }),
|
|
16
|
+
showLine(7) && /* @__PURE__ */ jsx("path", { d: "M1864 420V0h0" }),
|
|
17
|
+
showLine(8) && /* @__PURE__ */ jsx("path", { d: "M2130 420v-86.225a50 50 0 00-4.438-20.594L1984 0h0" }),
|
|
18
|
+
showLine(9) && /* @__PURE__ */ jsx("path", { d: "M2396 420v-77.506a50 50 0 00-13.194-33.843L2099 0h0" }),
|
|
19
|
+
showLine(10) && /* @__PURE__ */ jsx("path", { d: "M2662 420v-71.645a50 50 0 00-20.456-40.337L2221 0h0" }),
|
|
20
|
+
showLine(11) && /* @__PURE__ */ jsx("path", { d: "M2928 420v-67.395a50 50 0 00-25.958-43.84L2339 0h0" }),
|
|
21
|
+
showLine(12) && /* @__PURE__ */ jsx("path", { d: "M3194 420v-64.358a50 50 0 00-29.884-45.775L2459 0h0" }),
|
|
22
|
+
showLine(13) && /* @__PURE__ */ jsx("path", { d: "M3460 420v-62.077c0-20.977-13.094-39.724-32.789-46.944L2579 0h0" }),
|
|
23
|
+
showLine(14) && /* @__PURE__ */ jsx("path", { d: "M3726 420v-60.343c0-21.82-14.15-41.12-34.959-47.684L2702 0h0" })
|
|
42
24
|
] });
|
|
43
25
|
const hLine = isUnpick && /* @__PURE__ */ jsxs(Fragment$1, { children: [
|
|
44
26
|
/* @__PURE__ */ jsx("path", { d: "M2835 42H892" }),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Grid.mjs","names":[],"sources":["../../../../src/awesome/GridBackground/components/Grid.tsx"],"sourcesContent":["import { isUndefined } from 'es-toolkit/compat';\nimport { memo, useCallback } from 'react';\n\nimport type { SvgProps } from '@/types';\n\nenum Line {\n l7,\n l6,\n l5,\n l4,\n l3,\n l2,\n l1,\n center,\n r1,\n r2,\n r3,\n r4,\n r5,\n r6,\n r7,\n}\n\nexport interface GridProps extends SvgProps {\n color?: string;\n linePick?: Line;\n strokeWidth?: number;\n}\n\nconst Grid = memo<GridProps>(({ color = '#fff', strokeWidth = 3, linePick, ...rest }) => {\n const isUnpick = isUndefined(linePick);\n\n const showLine = useCallback((l: Line) => isUnpick || linePick === l, [linePick]);\n\n const vLine = (\n <>\n {showLine(Line.l7) && <path d=\"M2 420v-60.343c0-21.82 14.15-41.12 34.959-47.684L1026 0h0\" />}\n {showLine(Line.l6) && (\n <path d=\"M268 420v-62.077c0-20.977 13.094-39.724 32.789-46.944L1149 0h0\" />\n )}\n {showLine(Line.l5) && <path d=\"M534 420v-64.358a50 50 0 0129.884-45.775L1269 0h0\" />}\n {showLine(Line.l4) && <path d=\"M800 420v-67.395a50 50 0 0125.958-43.84L1389 0h0\" />}\n {showLine(Line.l3) && <path d=\"M1066 420v-71.645a50 50 0 0120.456-40.337L1507 0h0\" />}\n {showLine(Line.l2) && <path d=\"M1332 420v-77.506a50 50 0 0113.194-33.843L1629 0h0\" />}\n {showLine(Line.l1) && <path d=\"M1598 420v-86.225a50 50 0 014.438-20.594L1744 0h0\" />}\n {showLine(Line.center) && <path d=\"M1864 420V0h0\" />}\n {showLine(Line.r1) && <path d=\"M2130 420v-86.225a50 50 0 00-4.438-20.594L1984 0h0\" />}\n {showLine(Line.r2) && <path d=\"M2396 420v-77.506a50 50 0 00-13.194-33.843L2099 0h0\" />}\n {showLine(Line.r3) && <path d=\"M2662 420v-71.645a50 50 0 00-20.456-40.337L2221 0h0\" />}\n {showLine(Line.r4) && <path d=\"M2928 420v-67.395a50 50 0 00-25.958-43.84L2339 0h0\" />}\n {showLine(Line.r5) && <path d=\"M3194 420v-64.358a50 50 0 00-29.884-45.775L2459 0h0\" />}\n {showLine(Line.r6) && (\n <path d=\"M3460 420v-62.077c0-20.977-13.094-39.724-32.789-46.944L2579 0h0\" />\n )}\n {showLine(Line.r7) && (\n <path d=\"M3726 420v-60.343c0-21.82-14.15-41.12-34.959-47.684L2702 0h0\" />\n )}\n </>\n );\n\n const hLine = isUnpick && (\n <>\n <path d=\"M2835 42H892\" />\n <path d=\"M595 136h2538\" />\n <path d=\"M237 249h3254\" />\n </>\n );\n\n return (\n <svg\n style={{ width: '100%' }}\n viewBox=\"0 0 3728 422\"\n xmlns=\"http://www.w3.org/2000/svg\"\n {...rest}\n >\n <g fill=\"none\" fillRule=\"evenodd\" stroke={color} strokeWidth={strokeWidth}>\n {vLine}\n {hLine}\n </g>\n </svg>\n );\n});\n\nGrid.displayName = 'Grid';\n\nexport default Grid;\n"],"mappings":";;;;
|
|
1
|
+
{"version":3,"file":"Grid.mjs","names":[],"sources":["../../../../src/awesome/GridBackground/components/Grid.tsx"],"sourcesContent":["import { isUndefined } from 'es-toolkit/compat';\nimport { memo, useCallback } from 'react';\n\nimport type { SvgProps } from '@/types';\n\nenum Line {\n l7,\n l6,\n l5,\n l4,\n l3,\n l2,\n l1,\n center,\n r1,\n r2,\n r3,\n r4,\n r5,\n r6,\n r7,\n}\n\nexport interface GridProps extends SvgProps {\n color?: string;\n linePick?: Line;\n strokeWidth?: number;\n}\n\nconst Grid = memo<GridProps>(({ color = '#fff', strokeWidth = 3, linePick, ...rest }) => {\n const isUnpick = isUndefined(linePick);\n\n const showLine = useCallback((l: Line) => isUnpick || linePick === l, [linePick]);\n\n const vLine = (\n <>\n {showLine(Line.l7) && <path d=\"M2 420v-60.343c0-21.82 14.15-41.12 34.959-47.684L1026 0h0\" />}\n {showLine(Line.l6) && (\n <path d=\"M268 420v-62.077c0-20.977 13.094-39.724 32.789-46.944L1149 0h0\" />\n )}\n {showLine(Line.l5) && <path d=\"M534 420v-64.358a50 50 0 0129.884-45.775L1269 0h0\" />}\n {showLine(Line.l4) && <path d=\"M800 420v-67.395a50 50 0 0125.958-43.84L1389 0h0\" />}\n {showLine(Line.l3) && <path d=\"M1066 420v-71.645a50 50 0 0120.456-40.337L1507 0h0\" />}\n {showLine(Line.l2) && <path d=\"M1332 420v-77.506a50 50 0 0113.194-33.843L1629 0h0\" />}\n {showLine(Line.l1) && <path d=\"M1598 420v-86.225a50 50 0 014.438-20.594L1744 0h0\" />}\n {showLine(Line.center) && <path d=\"M1864 420V0h0\" />}\n {showLine(Line.r1) && <path d=\"M2130 420v-86.225a50 50 0 00-4.438-20.594L1984 0h0\" />}\n {showLine(Line.r2) && <path d=\"M2396 420v-77.506a50 50 0 00-13.194-33.843L2099 0h0\" />}\n {showLine(Line.r3) && <path d=\"M2662 420v-71.645a50 50 0 00-20.456-40.337L2221 0h0\" />}\n {showLine(Line.r4) && <path d=\"M2928 420v-67.395a50 50 0 00-25.958-43.84L2339 0h0\" />}\n {showLine(Line.r5) && <path d=\"M3194 420v-64.358a50 50 0 00-29.884-45.775L2459 0h0\" />}\n {showLine(Line.r6) && (\n <path d=\"M3460 420v-62.077c0-20.977-13.094-39.724-32.789-46.944L2579 0h0\" />\n )}\n {showLine(Line.r7) && (\n <path d=\"M3726 420v-60.343c0-21.82-14.15-41.12-34.959-47.684L2702 0h0\" />\n )}\n </>\n );\n\n const hLine = isUnpick && (\n <>\n <path d=\"M2835 42H892\" />\n <path d=\"M595 136h2538\" />\n <path d=\"M237 249h3254\" />\n </>\n );\n\n return (\n <svg\n style={{ width: '100%' }}\n viewBox=\"0 0 3728 422\"\n xmlns=\"http://www.w3.org/2000/svg\"\n {...rest}\n >\n <g fill=\"none\" fillRule=\"evenodd\" stroke={color} strokeWidth={strokeWidth}>\n {vLine}\n {hLine}\n </g>\n </svg>\n );\n});\n\nGrid.displayName = 'Grid';\n\nexport default Grid;\n"],"mappings":";;;;AA6BA,MAAM,OAAO,MAAiB,EAAE,QAAQ,QAAQ,cAAc,GAAG,UAAU,GAAG,WAAW;CACvF,MAAM,WAAW,YAAY,SAAS;CAEtC,MAAM,WAAW,aAAa,MAAY,YAAY,aAAa,GAAG,CAAC,SAAS,CAAC;CAEjF,MAAM,QACJ,qBAAA,YAAA,EAAA,UAAA;EACG,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,6DAA8D,CAAA;EAC3F,SAAA,EAAiB,IAChB,oBAAC,QAAD,EAAM,GAAE,kEAAmE,CAAA;EAE5E,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,qDAAsD,CAAA;EACnF,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,oDAAqD,CAAA;EAClF,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,sDAAuD,CAAA;EACpF,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,sDAAuD,CAAA;EACpF,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,qDAAsD,CAAA;EACnF,SAAA,EAAqB,IAAI,oBAAC,QAAD,EAAM,GAAE,iBAAkB,CAAA;EACnD,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,sDAAuD,CAAA;EACpF,SAAA,EAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,uDAAwD,CAAA;EACrF,SAAA,GAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,uDAAwD,CAAA;EACrF,SAAA,GAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,sDAAuD,CAAA;EACpF,SAAA,GAAiB,IAAI,oBAAC,QAAD,EAAM,GAAE,uDAAwD,CAAA;EACrF,SAAA,GAAiB,IAChB,oBAAC,QAAD,EAAM,GAAE,mEAAoE,CAAA;EAE7E,SAAA,GAAiB,IAChB,oBAAC,QAAD,EAAM,GAAE,gEAAiE,CAAA;EAE1E,EAAA,CAAA;CAGL,MAAM,QAAQ,YACZ,qBAAA,YAAA,EAAA,UAAA;EACE,oBAAC,QAAD,EAAM,GAAE,gBAAiB,CAAA;EACzB,oBAAC,QAAD,EAAM,GAAE,iBAAkB,CAAA;EAC1B,oBAAC,QAAD,EAAM,GAAE,iBAAkB,CAAA;EACzB,EAAA,CAAA;AAGL,QACE,oBAAC,OAAD;EACE,OAAO,EAAE,OAAO,QAAQ;EACxB,SAAQ;EACR,OAAM;EACN,GAAI;YAEJ,qBAAC,KAAD;GAAG,MAAK;GAAO,UAAS;GAAU,QAAQ;GAAoB;aAA9D,CACG,OACA,MACC;;EACA,CAAA;EAER;AAEF,KAAK,cAAc"}
|
|
@@ -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"}
|
|
@@ -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
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"useStreamHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useStreamHighlight.ts"],"sourcesContent":["'use client';\n\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { type BuiltinTheme, type ThemedToken } from 'shiki';\nimport { ShikiStreamTokenizer } from 'shiki-stream';\n\nimport { getCodeLanguageByInput } from '@/Highlighter/const';\nimport lobeTheme from '@/Highlighter/theme/lobe-theme';\n\nimport { shikiModulePromise, type StreamingHighlightResult } from './useHighlight';\n\ntype StreamingOptions = {\n customThemes?: Record<string, any>;\n enabled?: boolean;\n language: string;\n theme: string;\n};\n\n// Optimized version: reduce array allocations and object spreading\nconst tokensToLineTokens = (tokens: ThemedToken[]): ThemedToken[][] => {\n if (!tokens.length) return [[]];\n\n const lines: ThemedToken[][] = [];\n let currentLine: ThemedToken[] = [];\n\n for (const token of tokens) {\n const content = token.content ?? '';\n\n if (content === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n continue;\n }\n\n const newlineIndex = content.indexOf('\\n');\n if (newlineIndex === -1) {\n // No newline, add token directly\n currentLine.push(token);\n } else {\n // Split on newlines\n const segments = content.split('\\n');\n for (const [j, segment] of segments.entries()) {\n if (segment) {\n // Only create new object if we need to modify content\n currentLine.push(j === 0 && segment === content ? token : { ...token, content: segment });\n }\n if (j < segments.length - 1) {\n lines.push(currentLine);\n currentLine = [];\n }\n }\n }\n }\n\n // Don't forget the last line\n if (currentLine.length > 0 || lines.length === 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n};\n\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\n if (!bg && !fg) return undefined;\n return {\n backgroundColor: bg,\n color: fg,\n };\n};\n\nconst useStreamingHighlighter = (\n text: string,\n options: StreamingOptions,\n): StreamingHighlightResult | undefined => {\n const { customThemes, enabled, language, theme } = options;\n const [result, setResult] = useState<StreamingHighlightResult>();\n const tokenizerRef = useRef<ShikiStreamTokenizer | null>(null);\n const previousTextRef = useRef('');\n const safeText = text ?? '';\n const latestTextRef = useRef(safeText);\n const preStyleRef = useRef<CSSProperties | undefined>(undefined);\n const linesRef = useRef<ThemedToken[][]>([[]]);\n\n useEffect(() => {\n latestTextRef.current = safeText;\n }, [safeText]);\n\n // Use ref to store callback to avoid recreating it\n const setStreamingResultRef = useRef((rawLines: ThemedToken[][]) => {\n const previousLines = linesRef.current;\n const newLinesLength = rawLines.length;\n const prevLinesLength = previousLines.length;\n\n // Fast path: if lengths differ or it's a complete reset, use new lines directly\n if (newLinesLength !== prevLinesLength || newLinesLength === 0) {\n linesRef.current = rawLines;\n setResult({\n lines: rawLines,\n preStyle: preStyleRef.current,\n });\n return;\n }\n\n // Optimized comparison: only check changed lines\n let hasChanges = false;\n const mergedLines: ThemedToken[][] = [];\n\n for (let i = 0; i < newLinesLength; i++) {\n const newLine = rawLines[i];\n const prevLine = previousLines[i];\n\n // Quick reference equality check first\n if (prevLine === newLine) {\n mergedLines[i] = prevLine;\n continue;\n }\n\n // Length check\n if (!prevLine || prevLine.length !== newLine.length) {\n mergedLines[i] = newLine;\n hasChanges = true;\n continue;\n }\n\n // Deep comparison only for lines that might have changed\n let lineChanged = false;\n for (const [j, newToken] of newLine.entries()) {\n if (prevLine[j] !== newToken) {\n lineChanged = true;\n break;\n }\n }\n\n if (lineChanged) {\n mergedLines[i] = newLine;\n hasChanges = true;\n } else {\n mergedLines[i] = prevLine;\n }\n }\n\n // Only update state if there are actual changes\n if (hasChanges) {\n linesRef.current = mergedLines;\n setResult({\n lines: mergedLines,\n preStyle: preStyleRef.current,\n });\n }\n });\n\n const updateTokens = useCallback(async (nextText: string, forceReset = false) => {\n const tokenizer = tokenizerRef.current;\n if (!tokenizer) return;\n\n if (forceReset) {\n tokenizer.clear();\n previousTextRef.current = '';\n }\n\n const previousText = previousTextRef.current;\n let chunk = nextText;\n const canAppend = !forceReset && nextText.startsWith(previousText);\n\n if (canAppend) {\n chunk = nextText.slice(previousText.length);\n } else if (!forceReset) {\n tokenizer.clear();\n }\n\n previousTextRef.current = nextText;\n\n if (!chunk) {\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const totalLength = stableTokens.length + unstableTokens.length;\n\n if (totalLength === 0) {\n setStreamingResultRef.current([[]]);\n return;\n }\n\n // Only create merged array if we have both stable and unstable tokens\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n return;\n }\n\n try {\n await tokenizer.enqueue(chunk);\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n } catch (error) {\n console.error('Streaming highlighting failed:', error);\n }\n }, []);\n\n // Cache highlighter key to avoid unnecessary recreations\n const highlighterKeyRef = useRef<string>('');\n\n useEffect(() => {\n if (!enabled) {\n tokenizerRef.current?.clear();\n tokenizerRef.current = null;\n previousTextRef.current = '';\n preStyleRef.current = undefined;\n linesRef.current = [[]];\n setResult(undefined);\n highlighterKeyRef.current = '';\n return;\n }\n\n // Skip if language/theme combination hasn't changed\n const currentKey = `${language}-${theme}`;\n if (highlighterKeyRef.current === currentKey && tokenizerRef.current) {\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n const mod = await shikiModulePromise;\n if (!mod || cancelled) return;\n\n try {\n // Load custom theme if using slack-dark or slack-ochin\n let themesToLoad: any[] = [theme];\n if (customThemes && theme === 'lobe-theme') {\n const customTheme = customThemes[theme];\n if (customTheme) {\n themesToLoad = [customTheme as any];\n }\n }\n\n // Only load the specific language and theme needed\n // getSingletonHighlighter will cache the instance internally\n const highlighter = await mod.getSingletonHighlighter({\n langs: language ? [language] : ['plaintext'],\n themes: themesToLoad,\n });\n\n if (!highlighter || cancelled) return;\n\n // Only create new tokenizer if key changed\n if (highlighterKeyRef.current !== currentKey) {\n // Clear old tokenizer\n tokenizerRef.current?.clear();\n\n const tokenizer = new ShikiStreamTokenizer({\n highlighter,\n lang: language,\n theme,\n });\n\n tokenizerRef.current = tokenizer;\n highlighterKeyRef.current = currentKey;\n previousTextRef.current = '';\n linesRef.current = [[]];\n\n const themeInfo = highlighter.getTheme(theme);\n preStyleRef.current = createPreStyle(themeInfo?.bg, themeInfo?.fg);\n }\n\n const currentText = latestTextRef.current;\n if (currentText) {\n await updateTokens(currentText, true);\n } else {\n setStreamingResultRef.current([[]]);\n }\n } catch (error) {\n console.error('Streaming highlighter initialization failed:', error);\n // Reset on error\n highlighterKeyRef.current = '';\n }\n })();\n\n return () => {\n cancelled = true;\n // Cleanup only if this effect was cancelled before completion\n // The next effect will handle cleanup if key changed\n };\n }, [enabled, language, theme, updateTokens, customThemes]);\n\n // Separate effect for text updates to avoid unnecessary tokenizer recreation\n useEffect(() => {\n if (!enabled) return;\n if (!tokenizerRef.current) return;\n // Use ref to get latest text to avoid stale closures\n const currentText = latestTextRef.current;\n updateTokens(currentText);\n }, [enabled, safeText, updateTokens]);\n\n return result;\n};\n\nexport const useStreamHighlight = (\n text: string,\n {\n language,\n theme: builtinTheme,\n streaming,\n }: { enableTransformer?: boolean; language: string; streaming?: boolean; theme?: BuiltinTheme },\n) => {\n // Safely handle language and text with boundary checks\n const safeText = text ?? '';\n const lang = (language ?? 'plaintext').toLowerCase();\n\n // Match supported languages\n const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);\n\n const effectiveTheme = builtinTheme || 'lobe-theme';\n\n return useStreamingHighlighter(safeText, {\n customThemes: {\n 'lobe-theme': lobeTheme,\n },\n enabled: streaming,\n language: matchedLanguage,\n theme: effectiveTheme,\n });\n};\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"useStreamHighlight.mjs","names":["lobeTheme"],"sources":["../../src/hooks/useStreamHighlight.ts"],"sourcesContent":["'use client';\n\nimport { type CSSProperties, useCallback, useEffect, useMemo, useRef, useState } from 'react';\nimport { type BuiltinTheme, type ThemedToken } from 'shiki';\nimport { ShikiStreamTokenizer } from 'shiki-stream';\n\nimport { getCodeLanguageByInput } from '@/Highlighter/const';\nimport lobeTheme from '@/Highlighter/theme/lobe-theme';\n\nimport { shikiModulePromise, type StreamingHighlightResult } from './useHighlight';\n\ntype StreamingOptions = {\n customThemes?: Record<string, any>;\n enabled?: boolean;\n language: string;\n theme: string;\n};\n\n// Optimized version: reduce array allocations and object spreading\nconst tokensToLineTokens = (tokens: ThemedToken[]): ThemedToken[][] => {\n if (!tokens.length) return [[]];\n\n const lines: ThemedToken[][] = [];\n let currentLine: ThemedToken[] = [];\n\n for (const token of tokens) {\n const content = token.content ?? '';\n\n if (content === '\\n') {\n lines.push(currentLine);\n currentLine = [];\n continue;\n }\n\n const newlineIndex = content.indexOf('\\n');\n if (newlineIndex === -1) {\n // No newline, add token directly\n currentLine.push(token);\n } else {\n // Split on newlines\n const segments = content.split('\\n');\n for (const [j, segment] of segments.entries()) {\n if (segment) {\n // Only create new object if we need to modify content\n currentLine.push(j === 0 && segment === content ? token : { ...token, content: segment });\n }\n if (j < segments.length - 1) {\n lines.push(currentLine);\n currentLine = [];\n }\n }\n }\n }\n\n // Don't forget the last line\n if (currentLine.length > 0 || lines.length === 0) {\n lines.push(currentLine);\n }\n\n return lines.length > 0 ? lines : [[]];\n};\n\nconst createPreStyle = (bg?: string, fg?: string): CSSProperties | undefined => {\n if (!bg && !fg) return undefined;\n return {\n backgroundColor: bg,\n color: fg,\n };\n};\n\nconst useStreamingHighlighter = (\n text: string,\n options: StreamingOptions,\n): StreamingHighlightResult | undefined => {\n const { customThemes, enabled, language, theme } = options;\n const [result, setResult] = useState<StreamingHighlightResult>();\n const tokenizerRef = useRef<ShikiStreamTokenizer | null>(null);\n const previousTextRef = useRef('');\n const safeText = text ?? '';\n const latestTextRef = useRef(safeText);\n const preStyleRef = useRef<CSSProperties | undefined>(undefined);\n const linesRef = useRef<ThemedToken[][]>([[]]);\n\n useEffect(() => {\n latestTextRef.current = safeText;\n }, [safeText]);\n\n // Use ref to store callback to avoid recreating it\n const setStreamingResultRef = useRef((rawLines: ThemedToken[][]) => {\n const previousLines = linesRef.current;\n const newLinesLength = rawLines.length;\n const prevLinesLength = previousLines.length;\n\n // Fast path: if lengths differ or it's a complete reset, use new lines directly\n if (newLinesLength !== prevLinesLength || newLinesLength === 0) {\n linesRef.current = rawLines;\n setResult({\n lines: rawLines,\n preStyle: preStyleRef.current,\n });\n return;\n }\n\n // Optimized comparison: only check changed lines\n let hasChanges = false;\n const mergedLines: ThemedToken[][] = [];\n\n for (let i = 0; i < newLinesLength; i++) {\n const newLine = rawLines[i];\n const prevLine = previousLines[i];\n\n // Quick reference equality check first\n if (prevLine === newLine) {\n mergedLines[i] = prevLine;\n continue;\n }\n\n // Length check\n if (!prevLine || prevLine.length !== newLine.length) {\n mergedLines[i] = newLine;\n hasChanges = true;\n continue;\n }\n\n // Deep comparison only for lines that might have changed\n let lineChanged = false;\n for (const [j, newToken] of newLine.entries()) {\n if (prevLine[j] !== newToken) {\n lineChanged = true;\n break;\n }\n }\n\n if (lineChanged) {\n mergedLines[i] = newLine;\n hasChanges = true;\n } else {\n mergedLines[i] = prevLine;\n }\n }\n\n // Only update state if there are actual changes\n if (hasChanges) {\n linesRef.current = mergedLines;\n setResult({\n lines: mergedLines,\n preStyle: preStyleRef.current,\n });\n }\n });\n\n const updateTokens = useCallback(async (nextText: string, forceReset = false) => {\n const tokenizer = tokenizerRef.current;\n if (!tokenizer) return;\n\n if (forceReset) {\n tokenizer.clear();\n previousTextRef.current = '';\n }\n\n const previousText = previousTextRef.current;\n let chunk = nextText;\n const canAppend = !forceReset && nextText.startsWith(previousText);\n\n if (canAppend) {\n chunk = nextText.slice(previousText.length);\n } else if (!forceReset) {\n tokenizer.clear();\n }\n\n previousTextRef.current = nextText;\n\n if (!chunk) {\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const totalLength = stableTokens.length + unstableTokens.length;\n\n if (totalLength === 0) {\n setStreamingResultRef.current([[]]);\n return;\n }\n\n // Only create merged array if we have both stable and unstable tokens\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n return;\n }\n\n try {\n await tokenizer.enqueue(chunk);\n // Optimize: avoid array spread if possible\n const stableTokens = tokenizer.tokensStable;\n const unstableTokens = tokenizer.tokensUnstable;\n const mergedTokens =\n stableTokens.length === 0\n ? unstableTokens\n : unstableTokens.length === 0\n ? stableTokens\n : [...stableTokens, ...unstableTokens];\n setStreamingResultRef.current(tokensToLineTokens(mergedTokens));\n } catch (error) {\n console.error('Streaming highlighting failed:', error);\n }\n }, []);\n\n // Cache highlighter key to avoid unnecessary recreations\n const highlighterKeyRef = useRef<string>('');\n\n useEffect(() => {\n if (!enabled) {\n tokenizerRef.current?.clear();\n tokenizerRef.current = null;\n previousTextRef.current = '';\n preStyleRef.current = undefined;\n linesRef.current = [[]];\n setResult(undefined);\n highlighterKeyRef.current = '';\n return;\n }\n\n // Skip if language/theme combination hasn't changed\n const currentKey = `${language}-${theme}`;\n if (highlighterKeyRef.current === currentKey && tokenizerRef.current) {\n return;\n }\n\n let cancelled = false;\n\n (async () => {\n const mod = await shikiModulePromise;\n if (!mod || cancelled) return;\n\n try {\n // Load custom theme if using slack-dark or slack-ochin\n let themesToLoad: any[] = [theme];\n if (customThemes && theme === 'lobe-theme') {\n const customTheme = customThemes[theme];\n if (customTheme) {\n themesToLoad = [customTheme as any];\n }\n }\n\n // Only load the specific language and theme needed\n // getSingletonHighlighter will cache the instance internally\n const highlighter = await mod.getSingletonHighlighter({\n langs: language ? [language] : ['plaintext'],\n themes: themesToLoad,\n });\n\n if (!highlighter || cancelled) return;\n\n // Only create new tokenizer if key changed\n if (highlighterKeyRef.current !== currentKey) {\n // Clear old tokenizer\n tokenizerRef.current?.clear();\n\n const tokenizer = new ShikiStreamTokenizer({\n highlighter,\n lang: language,\n theme,\n });\n\n tokenizerRef.current = tokenizer;\n highlighterKeyRef.current = currentKey;\n previousTextRef.current = '';\n linesRef.current = [[]];\n\n const themeInfo = highlighter.getTheme(theme);\n preStyleRef.current = createPreStyle(themeInfo?.bg, themeInfo?.fg);\n }\n\n const currentText = latestTextRef.current;\n if (currentText) {\n await updateTokens(currentText, true);\n } else {\n setStreamingResultRef.current([[]]);\n }\n } catch (error) {\n console.error('Streaming highlighter initialization failed:', error);\n // Reset on error\n highlighterKeyRef.current = '';\n }\n })();\n\n return () => {\n cancelled = true;\n // Cleanup only if this effect was cancelled before completion\n // The next effect will handle cleanup if key changed\n };\n }, [enabled, language, theme, updateTokens, customThemes]);\n\n // Separate effect for text updates to avoid unnecessary tokenizer recreation\n useEffect(() => {\n if (!enabled) return;\n if (!tokenizerRef.current) return;\n // Use ref to get latest text to avoid stale closures\n const currentText = latestTextRef.current;\n updateTokens(currentText);\n }, [enabled, safeText, updateTokens]);\n\n return result;\n};\n\nexport const useStreamHighlight = (\n text: string,\n {\n language,\n theme: builtinTheme,\n streaming,\n }: { enableTransformer?: boolean; language: string; streaming?: boolean; theme?: BuiltinTheme },\n) => {\n // Safely handle language and text with boundary checks\n const safeText = text ?? '';\n const lang = (language ?? 'plaintext').toLowerCase();\n\n // Match supported languages\n const matchedLanguage = useMemo(() => getCodeLanguageByInput(lang), [lang]);\n\n const effectiveTheme = builtinTheme || 'lobe-theme';\n\n return useStreamingHighlighter(safeText, {\n customThemes: {\n 'lobe-theme': lobeTheme,\n },\n enabled: streaming,\n language: matchedLanguage,\n theme: effectiveTheme,\n });\n};\n"],"mappings":"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"}
|
package/es/utils/blobToPng.mjs
CHANGED
|
@@ -32,8 +32,7 @@ const blobToPng = (blob) => new Promise((resolve, reject) => {
|
|
|
32
32
|
const getClipboardBlob = async (blob) => {
|
|
33
33
|
const type = (blob.type || "").toLowerCase();
|
|
34
34
|
if (type === "image/png" || type === "image/svg+xml") return { [type]: blob };
|
|
35
|
-
|
|
36
|
-
return { "image/png": pngBlob };
|
|
35
|
+
return { "image/png": await blobToPng(blob) };
|
|
37
36
|
};
|
|
38
37
|
//#endregion
|
|
39
38
|
export { getClipboardBlob };
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"blobToPng.mjs","names":[],"sources":["../../src/utils/blobToPng.ts"],"sourcesContent":["/**\n * Convert image blob to PNG format.\n * Clipboard API only supports image/png and image/svg+xml.\n * WebP, JPEG and other formats need to be converted for clipboard copy.\n */\nexport const blobToPng = (blob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n const canvas = document.createElement('canvas');\n canvas.width = img.naturalWidth;\n canvas.height = img.naturalHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Canvas context not available'));\n return;\n }\n ctx.drawImage(img, 0, 0);\n canvas.toBlob(\n (pngBlob) => {\n if (pngBlob) {\n resolve(pngBlob);\n } else {\n reject(new Error('Failed to convert to PNG'));\n }\n },\n 'image/png',\n 1,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load image'));\n };\n\n img.src = url;\n });\n\nconst CLIPBOARD_SUPPORTED_TYPES = ['image/png', 'image/svg+xml'] as const;\n\nexport const getClipboardBlob = async (\n blob: Blob,\n): Promise<{ 'image/png': Blob } | { 'image/svg+xml': Blob }> => {\n const type = (blob.type || '').toLowerCase();\n\n if (type === 'image/png' || type === 'image/svg+xml') {\n return { [type]: blob } as { 'image/png': Blob } | { 'image/svg+xml': Blob };\n }\n\n const pngBlob = await blobToPng(blob);\n return { 'image/png': pngBlob };\n};\n"],"mappings":";;;;;;AAKA,MAAa,aAAa,SACxB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,MAAM,IAAI,gBAAgB,KAAK;AAErC,KAAI,eAAe;AACjB,MAAI,gBAAgB,IAAI;EACxB,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ,IAAI;AACnB,SAAO,SAAS,IAAI;EACpB,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,MAAI,CAAC,KAAK;AACR,0BAAO,IAAI,MAAM,+BAA+B,CAAC;AACjD;;AAEF,MAAI,UAAU,KAAK,GAAG,EAAE;AACxB,SAAO,QACJ,YAAY;AACX,OAAI,QACF,SAAQ,QAAQ;OAEhB,wBAAO,IAAI,MAAM,2BAA2B,CAAC;KAGjD,aACA,EACD;;AAGH,KAAI,gBAAgB;AAClB,MAAI,gBAAgB,IAAI;AACxB,yBAAO,IAAI,MAAM,uBAAuB,CAAC;;AAG3C,KAAI,MAAM;EACV;AAIJ,MAAa,mBAAmB,OAC9B,SAC+D;CAC/D,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;AAE5C,KAAI,SAAS,eAAe,SAAS,gBACnC,QAAO,GAAG,OAAO,MAAM;
|
|
1
|
+
{"version":3,"file":"blobToPng.mjs","names":[],"sources":["../../src/utils/blobToPng.ts"],"sourcesContent":["/**\n * Convert image blob to PNG format.\n * Clipboard API only supports image/png and image/svg+xml.\n * WebP, JPEG and other formats need to be converted for clipboard copy.\n */\nexport const blobToPng = (blob: Blob): Promise<Blob> =>\n new Promise((resolve, reject) => {\n const img = new Image();\n const url = URL.createObjectURL(blob);\n\n img.onload = () => {\n URL.revokeObjectURL(url);\n const canvas = document.createElement('canvas');\n canvas.width = img.naturalWidth;\n canvas.height = img.naturalHeight;\n const ctx = canvas.getContext('2d');\n if (!ctx) {\n reject(new Error('Canvas context not available'));\n return;\n }\n ctx.drawImage(img, 0, 0);\n canvas.toBlob(\n (pngBlob) => {\n if (pngBlob) {\n resolve(pngBlob);\n } else {\n reject(new Error('Failed to convert to PNG'));\n }\n },\n 'image/png',\n 1,\n );\n };\n\n img.onerror = () => {\n URL.revokeObjectURL(url);\n reject(new Error('Failed to load image'));\n };\n\n img.src = url;\n });\n\nconst CLIPBOARD_SUPPORTED_TYPES = ['image/png', 'image/svg+xml'] as const;\n\nexport const getClipboardBlob = async (\n blob: Blob,\n): Promise<{ 'image/png': Blob } | { 'image/svg+xml': Blob }> => {\n const type = (blob.type || '').toLowerCase();\n\n if (type === 'image/png' || type === 'image/svg+xml') {\n return { [type]: blob } as { 'image/png': Blob } | { 'image/svg+xml': Blob };\n }\n\n const pngBlob = await blobToPng(blob);\n return { 'image/png': pngBlob };\n};\n"],"mappings":";;;;;;AAKA,MAAa,aAAa,SACxB,IAAI,SAAS,SAAS,WAAW;CAC/B,MAAM,MAAM,IAAI,OAAO;CACvB,MAAM,MAAM,IAAI,gBAAgB,KAAK;AAErC,KAAI,eAAe;AACjB,MAAI,gBAAgB,IAAI;EACxB,MAAM,SAAS,SAAS,cAAc,SAAS;AAC/C,SAAO,QAAQ,IAAI;AACnB,SAAO,SAAS,IAAI;EACpB,MAAM,MAAM,OAAO,WAAW,KAAK;AACnC,MAAI,CAAC,KAAK;AACR,0BAAO,IAAI,MAAM,+BAA+B,CAAC;AACjD;;AAEF,MAAI,UAAU,KAAK,GAAG,EAAE;AACxB,SAAO,QACJ,YAAY;AACX,OAAI,QACF,SAAQ,QAAQ;OAEhB,wBAAO,IAAI,MAAM,2BAA2B,CAAC;KAGjD,aACA,EACD;;AAGH,KAAI,gBAAgB;AAClB,MAAI,gBAAgB,IAAI;AACxB,yBAAO,IAAI,MAAM,uBAAuB,CAAC;;AAG3C,KAAI,MAAM;EACV;AAIJ,MAAa,mBAAmB,OAC9B,SAC+D;CAC/D,MAAM,QAAQ,KAAK,QAAQ,IAAI,aAAa;AAE5C,KAAI,SAAS,eAAe,SAAS,gBACnC,QAAO,GAAG,OAAO,MAAM;AAIzB,QAAO,EAAE,aADO,MAAM,UAAU,KAAK,EACN"}
|