@greypan/web-ui 2.1.7 → 2.1.8
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/README.CN.md +51 -35
- package/README.md +54 -35
- package/dist/assets/glass.js.map +1 -1
- package/dist/components/avatar/index.js +1 -1
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/back-top/index.js +1 -1
- package/dist/components/back-top/index.js.map +1 -1
- package/dist/components/button/index.d.ts +6 -1
- package/dist/components/button/index.js +35 -14
- package/dist/components/button/index.js.map +1 -1
- package/dist/components/button/style.js +1 -1
- package/dist/components/button/style.js.map +1 -1
- package/dist/components/checkbox/index.d.ts +0 -3
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/context-menu/index.d.ts +4 -3
- package/dist/components/context-menu/index.js +47 -50
- package/dist/components/context-menu/index.js.map +1 -1
- package/dist/components/dialog/index.d.ts +1 -1
- package/dist/components/dialog/index.js +20 -19
- package/dist/components/dialog/index.js.map +1 -1
- package/dist/components/drawer/index.d.ts +2 -3
- package/dist/components/drawer/index.js +41 -31
- package/dist/components/drawer/index.js.map +1 -1
- package/dist/components/drawer/style.js +1 -1
- package/dist/components/drawer/style.js.map +1 -1
- package/dist/components/dropdown/index.d.ts +2 -9
- package/dist/components/dropdown/index.js +55 -51
- package/dist/components/dropdown/index.js.map +1 -1
- package/dist/components/dropdown-item/index.d.ts +0 -1
- package/dist/components/dropdown-item/index.js.map +1 -1
- package/dist/components/empty/index.js.map +1 -1
- package/dist/components/input/index.d.ts +1 -0
- package/dist/components/input/index.js +27 -14
- package/dist/components/input/index.js.map +1 -1
- package/dist/components/input/style.js +1 -1
- package/dist/components/input/style.js.map +1 -1
- package/dist/components/input-number/index.js.map +1 -1
- package/dist/components/option/index.js +1 -1
- package/dist/components/option/index.js.map +1 -1
- package/dist/components/popover/index.d.ts +1 -5
- package/dist/components/popover/index.js +41 -36
- package/dist/components/popover/index.js.map +1 -1
- package/dist/components/radio/index.d.ts +0 -3
- package/dist/components/radio/index.js.map +1 -1
- package/dist/components/segmented/index.js +6 -2
- package/dist/components/segmented/index.js.map +1 -1
- package/dist/components/segmented-trigger/index.js +1 -1
- package/dist/components/segmented-trigger/index.js.map +1 -1
- package/dist/components/segmented-trigger/style.js +1 -1
- package/dist/components/segmented-trigger/style.js.map +1 -1
- package/dist/components/select/index.js +5 -3
- package/dist/components/select/index.js.map +1 -1
- package/dist/components/slider/index.d.ts +0 -2
- package/dist/components/slider/index.js +1 -1
- package/dist/components/slider/index.js.map +1 -1
- package/dist/components/spinner/index.d.ts +0 -2
- package/dist/components/spinner/index.js.map +1 -1
- package/dist/components/svg-draw-lines/index.d.ts +0 -2
- package/dist/components/svg-draw-lines/index.js.map +1 -1
- package/dist/components/switch/index.d.ts +0 -1
- package/dist/components/switch/index.js.map +1 -1
- package/dist/components/switch/style.js +1 -1
- package/dist/components/switch/style.js.map +1 -1
- package/dist/components/textarea/index.d.ts +1 -0
- package/dist/components/textarea/index.js +47 -33
- package/dist/components/textarea/index.js.map +1 -1
- package/dist/components/textarea/style.js +1 -1
- package/dist/components/textarea/style.js.map +1 -1
- package/dist/components/theme/index.d.ts +0 -2
- package/dist/components/theme/index.js.map +1 -1
- package/dist/components/toast/manager.js.map +1 -1
- package/dist/components/toast/style.js +1 -1
- package/dist/components/toast/style.js.map +1 -1
- package/dist/components/toast/toast.d.ts +0 -4
- package/dist/components/toast/toast.js +14 -6
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/toast/types.d.ts +0 -3
- package/dist/components/tooltip/index.d.ts +1 -0
- package/dist/components/tooltip/index.js +31 -30
- package/dist/components/tooltip/index.js.map +1 -1
- package/dist/shared/events/user-change.js +17 -0
- package/dist/shared/events/user-change.js.map +1 -0
- package/dist/shared/normalize/index.js.map +1 -1
- package/dist/shared/overlay/native-dialog-presence.js +8 -3
- package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
- package/dist/shared/overlay/overlay.js.map +1 -1
- package/dist/shared/overlay/portal.js +3 -1
- package/dist/shared/overlay/portal.js.map +1 -1
- package/dist/shared/overlay/presence.js.map +1 -1
- package/package.json +9 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"manager.js","names":[],"sources":["../../../src/components/toast/manager.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/theme/overlay-root'\nimport { findNearestTheme, findRootTheme } from '@/shared/theme/theme-scope'\n\nimport type { WebUiToast } from './toast'\nimport type {\n ToastCloseReason,\n ToastInstanceOptions,\n ToastMessageUpdateOptions,\n ToastOptions,\n ToastPosition\n} from './types'\n\nlet toastIdCounter = 0\n\nconst toastContainers = new Set<HTMLElement>()\nconst visibleToasts = new Map<string, WebUiToast>()\n\nlet pendingBatch: Array<{ id: string; options: ToastInstanceOptions; container: HTMLElement }> | undefined\nlet isBatchScheduled = false\n\ntype ToastShortcutOptions = Omit<ToastOptions, 'message' | 'type'> & Pick<ToastInstanceOptions, 'position'>\n\nfunction generateId(): string {\n return `toast-${++toastIdCounter}`\n}\n\nfunction ensureContainer(position: ToastPosition, root: HTMLElement): HTMLElement {\n const existing = Array.from(root.children).find(\n (child): child is HTMLElement => child instanceof HTMLElement && child.dataset.wuiToastPosition === position\n )\n if (existing) return existing\n\n const container = document.createElement('div')\n container.className = `wui-toast-container wui-toast-${position}`\n container.dataset.wuiToastPosition = position\n container.setAttribute('role', 'log')\n container.setAttribute('aria-live', 'polite')\n container.setAttribute('aria-relevant', 'additions')\n root.appendChild(container)\n toastContainers.add(container)\n\n container.addEventListener('pointerenter', () => (container.dataset.hovered = 'true'))\n container.addEventListener('pointerleave', () => {\n delete container.dataset.hovered\n scrollToBottom(container)\n })\n return container\n}\n\nfunction scrollToBottom(container: HTMLElement) {\n if (container.dataset.hovered === 'true') return\n requestAnimationFrame(() => {\n container.scrollTop = container.scrollHeight\n })\n}\n\nfunction scheduleBatchFlush() {\n if (isBatchScheduled) return\n isBatchScheduled = true\n void Promise.resolve().then(flushBatch)\n}\n\nfunction flushBatch() {\n isBatchScheduled = false\n const batch = pendingBatch\n pendingBatch = undefined\n if (!batch) return\n\n const containersToScroll = new Set<HTMLElement>()\n for (const item of batch) {\n mountToast(item.id, item.options, item.container)\n containersToScroll.add(item.container)\n }\n for (const container of containersToScroll) scrollToBottom(container)\n}\n\nfunction createToast(options: ToastInstanceOptions): string {\n const id = options.id || generateId()\n if (visibleToasts.has(id)) return id\n\n const position = options.position || 'top-right'\n const targetTheme = options.target ? findNearestTheme(options.target) : findRootTheme()\n const root = options.container ?? targetTheme?.getOverlayRoot() ?? getFallbackOverlayRoot()\n const container = ensureContainer(position, root)\n\n if (!pendingBatch) pendingBatch = []\n pendingBatch.push({ id, options, container })\n scheduleBatchFlush()\n return id\n}\n\nfunction mountToast(id: string, options: ToastInstanceOptions, container: HTMLElement) {\n const el = document.createElement('web-ui-toast')\n el.toastId = id\n el.type = options.type || 'info'\n el.position = options.position || 'top-right'\n el.heading = options.heading || ''\n el.message = options.message\n el.duration = options.duration ?? 3000\n el.noCloseButton = options.closable === false\n\n el.addEventListener('toast-close', (e: Event) => {\n const detail = (e as CustomEvent<{ id: string; reason: ToastCloseReason }>).detail\n removeToast(detail.id)\n })\n\n container.appendChild(el)\n visibleToasts.set(id, el)\n requestAnimationFrame(() => el.show())\n}\n\nfunction removeToast(id: string) {\n const el = visibleToasts.get(id)\n if (!el) return\n el.remove()\n visibleToasts.delete(id)\n}\n\nfunction close(id: string) {\n visibleToasts.get(id)?.dismiss('programmatic')\n}\n\nfunction clear() {\n for (const el of visibleToasts.values()) el.dismiss('clear')\n}\n\n/**\n * 更新指定 Toast 的消息与可选标题,不影响其自动关闭倒计时。\n * 尚未完成批量挂载的 Toast 也会更新其初始内容。\n */\nfunction updateMessage(id: string, options: ToastMessageUpdateOptions) {\n const visibleToast = visibleToasts.get(id)\n if (visibleToast) {\n visibleToast.message = options.message\n if (options.heading !== undefined) visibleToast.heading = options.heading\n return\n }\n\n const pendingToast = pendingBatch?.find(item => item.id === id)\n if (!pendingToast) return\n pendingToast.options.message = options.message\n if (options.heading !== undefined) pendingToast.options.heading = options.heading\n}\n\nfunction toast(options: ToastInstanceOptions): string {\n return createToast(options)\n}\n\ntoast.success = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'success' })\ntoast.info = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'info' })\ntoast.warning = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'warning' })\ntoast.error = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'error', duration: options?.duration ?? 5000 })\ntoast.close = close\ntoast.clear = clear\ntoast.updateMessage = updateMessage\n\n
|
|
1
|
+
{"version":3,"file":"manager.js","names":[],"sources":["../../../src/components/toast/manager.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/theme/overlay-root'\nimport { findNearestTheme, findRootTheme } from '@/shared/theme/theme-scope'\n\nimport type { WebUiToast } from './toast'\nimport type {\n ToastCloseReason,\n ToastInstanceOptions,\n ToastMessageUpdateOptions,\n ToastOptions,\n ToastPosition\n} from './types'\n\nlet toastIdCounter = 0\n\nconst toastContainers = new Set<HTMLElement>()\nconst visibleToasts = new Map<string, WebUiToast>()\n\nlet pendingBatch: Array<{ id: string; options: ToastInstanceOptions; container: HTMLElement }> | undefined\nlet isBatchScheduled = false\n\ntype ToastShortcutOptions = Omit<ToastOptions, 'message' | 'type'> & Pick<ToastInstanceOptions, 'position'>\n\nfunction generateId(): string {\n return `toast-${++toastIdCounter}`\n}\n\nfunction ensureContainer(position: ToastPosition, root: HTMLElement): HTMLElement {\n const existing = Array.from(root.children).find(\n (child): child is HTMLElement => child instanceof HTMLElement && child.dataset.wuiToastPosition === position\n )\n if (existing) return existing\n\n const container = document.createElement('div')\n container.className = `wui-toast-container wui-toast-${position}`\n container.dataset.wuiToastPosition = position\n container.setAttribute('role', 'log')\n container.setAttribute('aria-live', 'polite')\n container.setAttribute('aria-relevant', 'additions')\n root.appendChild(container)\n toastContainers.add(container)\n\n container.addEventListener('pointerenter', () => (container.dataset.hovered = 'true'))\n container.addEventListener('pointerleave', () => {\n delete container.dataset.hovered\n scrollToBottom(container)\n })\n return container\n}\n\nfunction scrollToBottom(container: HTMLElement) {\n if (container.dataset.hovered === 'true') return\n requestAnimationFrame(() => {\n container.scrollTop = container.scrollHeight\n })\n}\n\nfunction scheduleBatchFlush() {\n if (isBatchScheduled) return\n isBatchScheduled = true\n void Promise.resolve().then(flushBatch)\n}\n\nfunction flushBatch() {\n isBatchScheduled = false\n const batch = pendingBatch\n pendingBatch = undefined\n if (!batch) return\n\n const containersToScroll = new Set<HTMLElement>()\n for (const item of batch) {\n mountToast(item.id, item.options, item.container)\n containersToScroll.add(item.container)\n }\n for (const container of containersToScroll) scrollToBottom(container)\n}\n\nfunction createToast(options: ToastInstanceOptions): string {\n const id = options.id || generateId()\n if (visibleToasts.has(id)) return id\n\n const position = options.position || 'top-right'\n const targetTheme = options.target ? findNearestTheme(options.target) : findRootTheme()\n const root = options.container ?? targetTheme?.getOverlayRoot() ?? getFallbackOverlayRoot()\n const container = ensureContainer(position, root)\n\n if (!pendingBatch) pendingBatch = []\n pendingBatch.push({ id, options, container })\n scheduleBatchFlush()\n return id\n}\n\nfunction mountToast(id: string, options: ToastInstanceOptions, container: HTMLElement) {\n const el = document.createElement('web-ui-toast')\n el.toastId = id\n el.type = options.type || 'info'\n el.position = options.position || 'top-right'\n el.heading = options.heading || ''\n el.message = options.message\n el.duration = options.duration ?? 3000\n el.noCloseButton = options.closable === false\n\n el.addEventListener('toast-close', (e: Event) => {\n const detail = (e as CustomEvent<{ id: string; reason: ToastCloseReason }>).detail\n removeToast(detail.id)\n })\n\n container.appendChild(el)\n visibleToasts.set(id, el)\n requestAnimationFrame(() => el.show())\n}\n\nfunction removeToast(id: string) {\n const el = visibleToasts.get(id)\n if (!el) return\n el.remove()\n visibleToasts.delete(id)\n}\n\nfunction close(id: string) {\n visibleToasts.get(id)?.dismiss('programmatic')\n}\n\nfunction clear() {\n for (const el of visibleToasts.values()) el.dismiss('clear')\n}\n\n/**\n * 更新指定 Toast 的消息与可选标题,不影响其自动关闭倒计时。\n * 尚未完成批量挂载的 Toast 也会更新其初始内容。\n */\nfunction updateMessage(id: string, options: ToastMessageUpdateOptions) {\n const visibleToast = visibleToasts.get(id)\n if (visibleToast) {\n visibleToast.message = options.message\n if (options.heading !== undefined) visibleToast.heading = options.heading\n return\n }\n\n const pendingToast = pendingBatch?.find(item => item.id === id)\n if (!pendingToast) return\n pendingToast.options.message = options.message\n if (options.heading !== undefined) pendingToast.options.heading = options.heading\n}\n\nfunction toast(options: ToastInstanceOptions): string {\n return createToast(options)\n}\n\ntoast.success = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'success' })\ntoast.info = (message: string, options?: ToastShortcutOptions) => createToast({ ...options, message, type: 'info' })\ntoast.warning = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'warning' })\ntoast.error = (message: string, options?: ToastShortcutOptions) =>\n createToast({ ...options, message, type: 'error', duration: options?.duration ?? 5000 })\ntoast.close = close\ntoast.clear = clear\ntoast.updateMessage = updateMessage\n\n// 获取当前可见 toast 数量(测试用)\ntoast._visibleCount = () => visibleToasts.size\n\n// 重置全部状态(测试用)\ntoast._reset = () => {\n pendingBatch = undefined\n isBatchScheduled = false\n for (const el of visibleToasts.values()) el.remove()\n visibleToasts.clear()\n for (const container of toastContainers) container.remove()\n toastContainers.clear()\n}\n\nexport { toast }\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/toast/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}:host([hidden]){display:none}.toast{--wui-glass-shadow:var(--wui-shadow-pop,0 3px 9px #00000045);transform:translate(var(--toast-slide-x,0), var(--toast-slide-y,-8px)) scale(.97);width:
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}:host([hidden]){display:none}.toast{--wui-glass-shadow:var(--wui-shadow-pop,0 3px 9px #00000045);transform:translate(var(--toast-slide-x,0), var(--toast-slide-y,-8px)) scale(.97);width:320px;min-height:36px;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);opacity:0;background-color:var(--wui-color-surface-overlay,#f6f6f6e0);transition:opacity var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), transform var(--wui-duration-overlay-enter,.18s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));border-radius:20px;gap:8px;padding:8px;line-height:1.5;display:flex;position:relative}:host([visible]) .toast{opacity:1;transform:translate(0)scale(1)}:host(:not([visible])) .toast{transition-duration:var(--wui-duration-overlay-exit,.14s)}web-ui-button.toast-close-btn{pointer-events:none;cursor:pointer;opacity:0;transition:opacity var(--wui-duration-trigger,.16s) ease;position:absolute;top:-8px;right:-8px}web-ui-button.toast-close-btn:focus-within{pointer-events:auto;opacity:1}@media (hover:hover) and (pointer:fine){web-ui-button.toast-close-btn:hover,.toast:hover web-ui-button.toast-close-btn{pointer-events:auto;opacity:1}}.toast web-ui-button[icon]{--wui-button-color:var(--wui-color-text-muted,#6a6a6a)}.toast-icon{border-radius:2147483647px;flex-shrink:0;justify-content:center;align-items:center;width:20px;height:20px;margin-bottom:auto;display:flex}.toast.success .toast-icon{color:var(--wui-color-success,#16a34a);background-color:color-mix(in srgb, var(--wui-color-success,#16a34a) 10%, transparent)}.toast.info .toast-icon{color:var(--wui-color-info,#2563eb);background-color:color-mix(in srgb, var(--wui-color-info,#2563eb) 10%, transparent)}.toast.warning .toast-icon{color:var(--wui-color-warning,#d97706);background-color:color-mix(in srgb, var(--wui-color-warning,#d97706) 10%, transparent)}.toast.error .toast-icon{color:var(--wui-color-danger,#dc2626);background-color:color-mix(in srgb, var(--wui-color-danger,#dc2626) 10%, transparent)}.toast-body{flex:1;min-width:0}.toast-heading{font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);text-overflow:ellipsis;white-space:nowrap;font-weight:600;line-height:1.4;overflow:hidden}.toast-message{-webkit-line-clamp:3;font-size:calc(var(--wui-font-size,14px) - 1px);color:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 55%, transparent);word-break:break-all;-webkit-box-orient:vertical;line-height:1.4;display:-webkit-box;overflow:hidden}.toast-time{color:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 30%, transparent);white-space:nowrap;font-size:12px}@media (prefers-reduced-motion:reduce){.toast,:host([visible]) .toast{transform:none}}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/toast/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ===== 入场 / 退场动画 ===== */\n\n.toast {\n --wui-glass-shadow: var(--wui-shadow-pop, 0 3px 9px rgb(0 0 0 / 0.27));\n position: relative;\n transform: translate(var(--toast-slide-x, 0), var(--toast-slide-y, -8px)) scale(0.97);\n\n display: flex;\n gap:
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/toast/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n:host([hidden]) {\n display: none;\n}\n\n/* ===== 入场 / 退场动画 ===== */\n\n.toast {\n --wui-glass-shadow: var(--wui-shadow-pop, 0 3px 9px rgb(0 0 0 / 0.27));\n position: relative;\n transform: translate(var(--toast-slide-x, 0), var(--toast-slide-y, -8px)) scale(0.97);\n\n display: flex;\n gap: 8px;\n\n width: 320px;\n min-height: 36px;\n padding: 8px;\n border-radius: 20px;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n\n opacity: 0;\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88));\n\n transition:\n opacity var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n transform var(--wui-duration-overlay-enter, 180ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n:host([visible]) .toast {\n transform: translate(0, 0) scale(1);\n opacity: 1;\n}\n\n:host(:not([visible])) .toast {\n transition-duration: var(--wui-duration-overlay-exit, 140ms);\n}\n\n/* ===== 关闭按钮(放在 hover 规则之前,避免 specificity 降序) ===== */\n\nweb-ui-button.toast-close-btn {\n pointer-events: none;\n cursor: pointer;\n\n position: absolute;\n top: -8px;\n right: -8px;\n\n opacity: 0;\n\n transition: opacity var(--wui-duration-trigger, 160ms) ease;\n\n /* 内层原生按钮获得键盘焦点时宿主同样可见 */\n &:focus-within {\n pointer-events: auto;\n opacity: 1;\n }\n}\n\n@media (hover: hover) and (pointer: fine) {\n web-ui-button.toast-close-btn:hover,\n .toast:hover web-ui-button.toast-close-btn {\n pointer-events: auto;\n opacity: 1;\n }\n}\n\n/* secondary 变体默认用文本色,恢复原关闭按钮的 muted 观感 */\n.toast web-ui-button[icon] {\n --wui-button-color: var(--wui-color-text-muted, #6a6a6a);\n}\n\n/* ===== 图标 ===== */\n\n.toast-icon {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 20px;\n height: 20px;\n margin-bottom: auto;\n border-radius: calc(infinity * 1px);\n}\n\n.toast.success .toast-icon {\n color: var(--wui-color-success, #16a34a);\n background-color: color-mix(in srgb, var(--wui-color-success, #16a34a) 10%, transparent);\n}\n\n.toast.info .toast-icon {\n color: var(--wui-color-info, #2563eb);\n background-color: color-mix(in srgb, var(--wui-color-info, #2563eb) 10%, transparent);\n}\n\n.toast.warning .toast-icon {\n color: var(--wui-color-warning, #d97706);\n background-color: color-mix(in srgb, var(--wui-color-warning, #d97706) 10%, transparent);\n}\n\n.toast.error .toast-icon {\n color: var(--wui-color-danger, #dc2626);\n background-color: color-mix(in srgb, var(--wui-color-danger, #dc2626) 10%, transparent);\n}\n\n/* ===== 内容区 ===== */\n\n.toast-body {\n flex: 1;\n min-width: 0;\n}\n\n.toast-heading {\n overflow: hidden;\n\n font-size: var(--wui-font-size, 14px);\n font-weight: 600;\n line-height: 1.4;\n color: var(--wui-color-text, #1b1b1b);\n text-overflow: ellipsis;\n white-space: nowrap;\n}\n\n.toast-message {\n overflow: hidden;\n display: -webkit-box;\n -webkit-box-orient: vertical;\n -webkit-line-clamp: 3;\n\n font-size: calc(var(--wui-font-size, 14px) - 1px);\n line-height: 1.4;\n color: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 55%, transparent);\n word-break: break-all;\n}\n\n/* ===== 时间戳 ===== */\n\n.toast-time {\n font-size: 12px;\n color: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 30%, transparent);\n white-space: nowrap;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .toast,\n :host([visible]) .toast {\n transform: none;\n }\n}\n"],"mappings":""}
|
|
@@ -14,13 +14,9 @@ export declare class WebUiToast extends LitElement {
|
|
|
14
14
|
connectedCallback(): void;
|
|
15
15
|
disconnectedCallback(): void;
|
|
16
16
|
protected updated(changed: PropertyValues): void;
|
|
17
|
-
/** 根据 position 设置入场/退场滑动方向 */
|
|
18
17
|
private _applySlideDirection;
|
|
19
|
-
/** 启动自动关闭计时器 */
|
|
20
18
|
startAutoClose(): void;
|
|
21
|
-
/** 由 manager 调用:播放入场动画后自动开始计时 */
|
|
22
19
|
show(): void;
|
|
23
|
-
/** 关闭(含退场动画) */
|
|
24
20
|
dismiss(reason?: ToastCloseReason): void;
|
|
25
21
|
private _clearTimer;
|
|
26
22
|
private _onPointerEnter;
|
|
@@ -6,6 +6,7 @@ import r from "../../icons/generated/lucide-info.js";
|
|
|
6
6
|
import i from "../../icons/generated/lucide-triangle-alert.js";
|
|
7
7
|
import a from "../../icons/generated/lucide-circle-alert.js";
|
|
8
8
|
import o from "../../icons/generated/heroicons-x-mark-16-solid.js";
|
|
9
|
+
import "../button/index.js";
|
|
9
10
|
import { getTransitionDuration as s } from "../../shared/overlay/presence.js";
|
|
10
11
|
import c from "./style.js";
|
|
11
12
|
import { LitElement as l, html as u, nothing as d, unsafeCSS as f } from "lit";
|
|
@@ -79,11 +80,11 @@ var _ = class extends l {
|
|
|
79
80
|
}));
|
|
80
81
|
}
|
|
81
82
|
render() {
|
|
82
|
-
let e = h[this.type], t = this.type === "error" ? "assertive" : "polite";
|
|
83
|
+
let e = h[this.type], t = this.type === "error" ? "assertive" : "polite", n = this.type === "error" ? "alert" : d;
|
|
83
84
|
return u`
|
|
84
|
-
<div class="toast wui-glass ${this.type}" role
|
|
85
|
+
<div class="toast wui-glass ${this.type}" role=${n} aria-live=${t} aria-atomic="true">
|
|
85
86
|
<span class="toast-icon" aria-hidden="true">
|
|
86
|
-
<web-ui-icon .icon=${e}
|
|
87
|
+
<web-ui-icon .icon=${e} size="14"></web-ui-icon>
|
|
87
88
|
</span>
|
|
88
89
|
<div class="toast-body">
|
|
89
90
|
${this.heading ? u`<div class="toast-heading">${this.heading}</div>` : d}
|
|
@@ -91,9 +92,16 @@ var _ = class extends l {
|
|
|
91
92
|
</div>
|
|
92
93
|
<span class="toast-time">${g()}</span>
|
|
93
94
|
${this.noCloseButton ? d : u`
|
|
94
|
-
<
|
|
95
|
-
|
|
96
|
-
|
|
95
|
+
<web-ui-button
|
|
96
|
+
class="toast-close-btn"
|
|
97
|
+
icon
|
|
98
|
+
variant="secondary"
|
|
99
|
+
size="24"
|
|
100
|
+
aria-label="关闭"
|
|
101
|
+
@click=${this._onCloseClick}
|
|
102
|
+
>
|
|
103
|
+
<web-ui-icon .icon=${o} size="16"></web-ui-icon>
|
|
104
|
+
</web-ui-button>
|
|
97
105
|
`}
|
|
98
106
|
</div>
|
|
99
107
|
`;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"toast.js","names":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["import type { IconifyIcon } from '@iconify/types'\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\n\nimport '@/components/icon'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { lucideCheck, lucideInfo, lucideTriangleAlert, lucideCircleAlert, heroiconsXMark16Solid } from '@/icons'\nimport { getTransitionDuration } from '@/shared/overlay/presence'\n\nimport style from './style.css?inline'\nimport type { ToastCloseReason, ToastPosition, ToastType } from './types'\n\nconst TYPE_ICONS: Record<ToastType, IconifyIcon> = {\n success: lucideCheck,\n info: lucideInfo,\n warning: lucideTriangleAlert,\n error: lucideCircleAlert\n}\n\nfunction _formatTime(): string {\n const now = new Date()\n const h = now.getHours().toString().padStart(2, '0')\n const m = now.getMinutes().toString().padStart(2, '0')\n return `${h}:${m}`\n}\n\n@customElement('web-ui-toast')\nexport class WebUiToast extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, attribute: false }) toastId = ''\n @property({ type: String, reflect: true }) type: ToastType = 'info'\n @property({ type: String, reflect: true }) position: ToastPosition = 'top-right'\n @property({ type: String }) heading = ''\n @property({ type: String }) message = ''\n @property({ type: Number }) duration = 3000\n @property({ type: Boolean, reflect: true, attribute: 'no-close-button' }) noCloseButton = false\n @property({ type: Boolean, reflect: true }) visible = false\n\n private _closeTimer?: ReturnType<typeof setTimeout>\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this._clearTimer()\n }\n\n protected override updated(changed: PropertyValues) {\n if (changed.has('position')) {\n this._applySlideDirection()\n }\n }\n\n
|
|
1
|
+
{"version":3,"file":"toast.js","names":[],"sources":["../../../src/components/toast/toast.ts"],"sourcesContent":["import type { IconifyIcon } from '@iconify/types'\nimport { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\n\nimport '@/components/icon'\n// web-ui-button 复用 icon 变体的焦点环与统一样式(secondary 变体,浅灰底贴合 toast 角落)\nimport '@/components/button'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { lucideCheck, lucideInfo, lucideTriangleAlert, lucideCircleAlert, heroiconsXMark16Solid } from '@/icons'\nimport { getTransitionDuration } from '@/shared/overlay/presence'\n\nimport style from './style.css?inline'\nimport type { ToastCloseReason, ToastPosition, ToastType } from './types'\n\nconst TYPE_ICONS: Record<ToastType, IconifyIcon> = {\n success: lucideCheck,\n info: lucideInfo,\n warning: lucideTriangleAlert,\n error: lucideCircleAlert\n}\n\nfunction _formatTime(): string {\n const now = new Date()\n const h = now.getHours().toString().padStart(2, '0')\n const m = now.getMinutes().toString().padStart(2, '0')\n return `${h}:${m}`\n}\n\n@customElement('web-ui-toast')\nexport class WebUiToast extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, attribute: false }) toastId = ''\n @property({ type: String, reflect: true }) type: ToastType = 'info'\n @property({ type: String, reflect: true }) position: ToastPosition = 'top-right'\n @property({ type: String }) heading = ''\n @property({ type: String }) message = ''\n @property({ type: Number }) duration = 3000\n @property({ type: Boolean, reflect: true, attribute: 'no-close-button' }) noCloseButton = false\n @property({ type: Boolean, reflect: true }) visible = false\n\n private _closeTimer?: ReturnType<typeof setTimeout>\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this._clearTimer()\n }\n\n protected override updated(changed: PropertyValues) {\n if (changed.has('position')) {\n this._applySlideDirection()\n }\n }\n\n // 根据 position 设置入场/退场滑动方向\n private _applySlideDirection() {\n const SLIDE_OFFSET = 20\n let x = 0\n let y = 0\n\n if (this.position.includes('left')) x = -SLIDE_OFFSET\n else if (this.position.includes('right')) x = SLIDE_OFFSET\n\n if (this.position.startsWith('top')) y = -SLIDE_OFFSET\n else if (this.position.startsWith('bottom')) y = SLIDE_OFFSET\n\n this.style.setProperty('--toast-slide-x', `${x}px`)\n this.style.setProperty('--toast-slide-y', `${y}px`)\n }\n\n // 启动自动关闭计时器\n startAutoClose() {\n this._clearTimer()\n if (this.duration > 0) {\n this._closeTimer = setTimeout(() => {\n this.dismiss('auto')\n }, this.duration)\n }\n }\n\n // 由 manager 调用:播放入场动画后自动开始计时\n show() {\n this.visible = true\n this.startAutoClose()\n }\n\n // 关闭(含退场动画)\n dismiss(reason: ToastCloseReason = 'programmatic') {\n if (!this.visible) return\n this.visible = false\n this._clearTimer()\n const onEnd = () => {\n this.removeEventListener('transitionend', onEnd)\n this._dispatchClose(reason)\n }\n this.addEventListener('transitionend', onEnd)\n // fallback: transitionend 不触发时(display:none 等)\n const toast = this.shadowRoot?.querySelector<HTMLElement>('.toast')\n setTimeout(onEnd, getTransitionDuration(toast ?? this) + 80)\n }\n\n private _clearTimer() {\n if (this._closeTimer !== undefined) {\n clearTimeout(this._closeTimer)\n this._closeTimer = undefined\n }\n }\n\n private _onPointerEnter = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n this._clearTimer()\n }\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n this.startAutoClose()\n }\n\n private _onCloseClick = () => {\n this.dismiss('manual')\n }\n\n private _dispatchClose(reason: ToastCloseReason) {\n this.dispatchEvent(\n new CustomEvent('toast-close', {\n detail: { id: this.toastId, reason },\n bubbles: true,\n composed: true\n })\n )\n }\n\n override render() {\n const icon = TYPE_ICONS[this.type]\n const ariaLive = this.type === 'error' ? 'assertive' : 'polite'\n // role=\"alert\" 会强制隐式 aria-live=\"assertive\",覆盖掉上方 aria-live 设置,\n // 因此仅 error toast 使用 alert(本就应 assertive);其余类型交给容器 role=\"log\" 的 polite 播报。\n const role = this.type === 'error' ? 'alert' : nothing\n\n return html`\n <div class=\"toast wui-glass ${this.type}\" role=${role} aria-live=${ariaLive} aria-atomic=\"true\">\n <span class=\"toast-icon\" aria-hidden=\"true\">\n <web-ui-icon .icon=${icon} size=\"14\"></web-ui-icon>\n </span>\n <div class=\"toast-body\">\n ${this.heading ? html`<div class=\"toast-heading\">${this.heading}</div>` : nothing}\n <div class=\"toast-message\">${this.message}</div>\n </div>\n <span class=\"toast-time\">${_formatTime()}</span>\n ${!this.noCloseButton\n ? html`\n <web-ui-button\n class=\"toast-close-btn\"\n icon\n variant=\"secondary\"\n size=\"24\"\n aria-label=\"关闭\"\n @click=${this._onCloseClick}\n >\n <web-ui-icon .icon=${heroiconsXMark16Solid} size=\"16\"></web-ui-icon>\n </web-ui-button>\n `\n : nothing}\n </div>\n `\n }\n\n declare readonly $events: {\n 'toast-close': CustomEvent<{ id: string; reason: ToastCloseReason }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-toast': WebUiToast\n }\n}\n"],"mappings":"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"}
|
|
@@ -8,15 +8,12 @@ export interface ToastOptions {
|
|
|
8
8
|
closable?: boolean;
|
|
9
9
|
id?: string;
|
|
10
10
|
heading?: string;
|
|
11
|
-
/** 用于解析最近 web-ui-theme 的触发元素。 */
|
|
12
11
|
target?: Element;
|
|
13
|
-
/** 显式挂载容器,优先级高于 target 和主题作用域。 */
|
|
14
12
|
container?: HTMLElement;
|
|
15
13
|
}
|
|
16
14
|
export interface ToastInstanceOptions extends ToastOptions {
|
|
17
15
|
position?: ToastPosition;
|
|
18
16
|
}
|
|
19
|
-
/** 更新已创建 Toast 的文本内容。未传入 heading 时保留原值。 */
|
|
20
17
|
export interface ToastMessageUpdateOptions {
|
|
21
18
|
message: string;
|
|
22
19
|
heading?: string;
|
|
@@ -1,14 +1,15 @@
|
|
|
1
1
|
import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
|
|
2
2
|
import t from "../../assets/glass.js";
|
|
3
3
|
import { normalizeLiteral as n, normalizeNumber as r } from "../../shared/normalize/index.js";
|
|
4
|
-
import i from "../../
|
|
5
|
-
import
|
|
6
|
-
import {
|
|
7
|
-
import s from "
|
|
8
|
-
import
|
|
9
|
-
import {
|
|
4
|
+
import { UserChangeController as i } from "../../shared/events/user-change.js";
|
|
5
|
+
import a from "../../assets/overlay-motion.js";
|
|
6
|
+
import { defineAnchoredPanel as o } from "../../shared/overlay/anchored-panel.js";
|
|
7
|
+
import { createOverlayPortal as s } from "../../shared/overlay/portal.js";
|
|
8
|
+
import c from "./style.js";
|
|
9
|
+
import { LitElement as l, html as u, unsafeCSS as d } from "lit";
|
|
10
|
+
import { customElement as f, property as p } from "lit/decorators.js";
|
|
10
11
|
//#region src/components/tooltip/index.ts
|
|
11
|
-
var
|
|
12
|
+
var m = [
|
|
12
13
|
"top",
|
|
13
14
|
"top-start",
|
|
14
15
|
"top-end",
|
|
@@ -21,9 +22,9 @@ var p = [
|
|
|
21
22
|
"right",
|
|
22
23
|
"right-start",
|
|
23
24
|
"right-end"
|
|
24
|
-
],
|
|
25
|
+
], h = 0, g = class extends l {
|
|
25
26
|
constructor(...e) {
|
|
26
|
-
super(...e), this._placement = "top", this._showDelay = 200, this._hideDelay = 100, this.disabled = !1, this._offset = 6, this.content = "", this.open = !1, this.portal = !1, this._isCountedVisible = !1, this._shouldOpenInstantly = !0, this._panel =
|
|
27
|
+
super(...e), this._placement = "top", this._showDelay = 200, this._hideDelay = 100, this.disabled = !1, this._offset = 6, this.content = "", this.open = !1, this.portal = !1, this._isCountedVisible = !1, this._userOpenChange = new i(), this._shouldOpenInstantly = !0, this._panel = o().make({
|
|
27
28
|
getAnchor: () => this.shadowRoot?.querySelector(".tooltip-trigger") ?? null,
|
|
28
29
|
getLocalPanel: () => this.shadowRoot?.querySelector(".tooltip-panel") ?? null,
|
|
29
30
|
getPositioning: () => ({
|
|
@@ -34,7 +35,7 @@ var p = [
|
|
|
34
35
|
isPortal: () => this.portal,
|
|
35
36
|
createPortal: () => this._createPortal()
|
|
36
37
|
}), this._onPointerEnter = (e) => {
|
|
37
|
-
e.pointerType !== "touch" && (this.disabled || (clearTimeout(this._hideTimer), this._showTimer = setTimeout(() => this._show(),
|
|
38
|
+
e.pointerType !== "touch" && (this.disabled || (clearTimeout(this._hideTimer), this._showTimer = setTimeout(() => this._show(), h > 0 ? 0 : this.showDelay)));
|
|
38
39
|
}, this._onPointerLeave = (e) => {
|
|
39
40
|
e.pointerType !== "touch" && (this.disabled || (clearTimeout(this._showTimer), this._hideTimer = setTimeout(() => this._hide(), this.hideDelay)));
|
|
40
41
|
}, this._onFocusIn = () => {
|
|
@@ -45,9 +46,9 @@ var p = [
|
|
|
45
46
|
}
|
|
46
47
|
static {
|
|
47
48
|
this.styles = [
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
49
|
+
d(t),
|
|
50
|
+
d(a),
|
|
51
|
+
d(c)
|
|
51
52
|
];
|
|
52
53
|
}
|
|
53
54
|
get placement() {
|
|
@@ -55,7 +56,7 @@ var p = [
|
|
|
55
56
|
}
|
|
56
57
|
set placement(e) {
|
|
57
58
|
let t = this._placement;
|
|
58
|
-
this._placement = n(e,
|
|
59
|
+
this._placement = n(e, m, "top"), this.requestUpdate("placement", t);
|
|
59
60
|
}
|
|
60
61
|
get showDelay() {
|
|
61
62
|
return this._showDelay;
|
|
@@ -91,22 +92,22 @@ var p = [
|
|
|
91
92
|
this.open && (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0);
|
|
92
93
|
}
|
|
93
94
|
updated(e) {
|
|
94
|
-
e.has("portal") || e.has("overlayContainer") ? requestAnimationFrame(() => this._reconfigureOverlay()) : (e.has("placement") || e.has("offset")) && requestAnimationFrame(() => this._panel.updatePosition()), e.has("open") && (this._syncVisibleTooltipCount(this.open), this.open ? (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0) : this._closeOverlay(), this._dispatchChange(this.open)), e.has("content") && this._syncPortalContent();
|
|
95
|
+
e.has("portal") || e.has("overlayContainer") ? requestAnimationFrame(() => this._reconfigureOverlay()) : (e.has("placement") || e.has("offset")) && requestAnimationFrame(() => this._panel.updatePosition()), e.has("open") && (this._syncVisibleTooltipCount(this.open), this.open ? (this._openOverlay(this._shouldOpenInstantly), this._shouldOpenInstantly = !0) : this._closeOverlay(), this._userOpenChange.consume() && this._dispatchChange(this.open)), e.has("content") && this._syncPortalContent();
|
|
95
96
|
}
|
|
96
97
|
_show(e = !1) {
|
|
97
|
-
this.open ||= (this._shouldOpenInstantly = e, !0);
|
|
98
|
+
this.open ||= (this._shouldOpenInstantly = e, this._userOpenChange.mark(), !0);
|
|
98
99
|
}
|
|
99
100
|
_hide() {
|
|
100
|
-
this.open &&= !1;
|
|
101
|
+
this.open &&= (this._userOpenChange.mark(), !1);
|
|
101
102
|
}
|
|
102
103
|
_openOverlay(e = !1) {
|
|
103
104
|
this._panel.open(e);
|
|
104
105
|
}
|
|
105
106
|
_createPortal() {
|
|
106
|
-
let e =
|
|
107
|
+
let e = s({
|
|
107
108
|
container: this.overlayContainer,
|
|
108
109
|
target: this,
|
|
109
|
-
style: `${t}\n${
|
|
110
|
+
style: `${t}\n${a}\n${c}`,
|
|
110
111
|
className: "tooltip-panel portal wui-glass wui-floating-panel"
|
|
111
112
|
});
|
|
112
113
|
if (this.content) {
|
|
@@ -143,39 +144,39 @@ var p = [
|
|
|
143
144
|
}));
|
|
144
145
|
}
|
|
145
146
|
_syncVisibleTooltipCount(e) {
|
|
146
|
-
e && !this._isCountedVisible ? (
|
|
147
|
+
e && !this._isCountedVisible ? (h += 1, this._isCountedVisible = !0) : !e && this._isCountedVisible && (h = Math.max(0, h - 1), this._isCountedVisible = !1);
|
|
147
148
|
}
|
|
148
149
|
render() {
|
|
149
|
-
return
|
|
150
|
+
return u`
|
|
150
151
|
<div class="tooltip-anchor">
|
|
151
152
|
<div class="tooltip-trigger"><slot></slot></div>
|
|
152
153
|
<div class="tooltip-panel wui-glass wui-floating-panel" hidden role="tooltip">
|
|
153
|
-
${this.content ?
|
|
154
|
+
${this.content ? u`<span class="tooltip-text">${this.content}</span>` : u`<slot name="content"></slot>`}
|
|
154
155
|
</div>
|
|
155
156
|
</div>
|
|
156
157
|
`;
|
|
157
158
|
}
|
|
158
159
|
};
|
|
159
|
-
e([
|
|
160
|
+
e([p({
|
|
160
161
|
type: String,
|
|
161
162
|
reflect: !0
|
|
162
|
-
})],
|
|
163
|
+
})], g.prototype, "placement", null), e([p({
|
|
163
164
|
type: Number,
|
|
164
165
|
attribute: "show-delay"
|
|
165
|
-
})],
|
|
166
|
+
})], g.prototype, "showDelay", null), e([p({
|
|
166
167
|
type: Number,
|
|
167
168
|
attribute: "hide-delay"
|
|
168
|
-
})],
|
|
169
|
+
})], g.prototype, "hideDelay", null), e([p({
|
|
169
170
|
type: Boolean,
|
|
170
171
|
reflect: !0
|
|
171
|
-
})],
|
|
172
|
+
})], g.prototype, "disabled", void 0), e([p({ type: Number })], g.prototype, "offset", null), e([p({ type: String })], g.prototype, "content", void 0), e([p({
|
|
172
173
|
type: Boolean,
|
|
173
174
|
reflect: !0
|
|
174
|
-
})],
|
|
175
|
+
})], g.prototype, "open", void 0), e([p({
|
|
175
176
|
type: Boolean,
|
|
176
177
|
reflect: !0
|
|
177
|
-
})],
|
|
178
|
+
})], g.prototype, "portal", void 0), e([p({ attribute: !1 })], g.prototype, "overlayContainer", void 0), g = e([f("web-ui-tooltip")], g);
|
|
178
179
|
//#endregion
|
|
179
|
-
export {
|
|
180
|
+
export { g as WebUiTooltip };
|
|
180
181
|
|
|
181
182
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/tooltip/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { createOverlayPortal } from '@/shared/overlay/portal'\nimport type { OverlayContainer, OverlayPortal } from '@/shared/overlay/portal'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = [\n 'top',\n 'top-start',\n 'top-end',\n 'bottom',\n 'bottom-start',\n 'bottom-end',\n 'left',\n 'left-start',\n 'left-end',\n 'right',\n 'right-start',\n 'right-end'\n] as const\n\nlet visibleTooltipCount = 0\n\n@customElement('web-ui-tooltip')\nexport class WebUiTooltip extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'top')\n this.requestUpdate('placement', old)\n }\n private _placement: Placement = 'top'\n\n @property({ type: Number, attribute: 'show-delay' })\n get showDelay(): number {\n return this._showDelay\n }\n set showDelay(v: number) {\n const old = this._showDelay\n this._showDelay = normalizeNumber(v, 0, 5000, 200)\n this.requestUpdate('showDelay', old)\n }\n private _showDelay = 200\n\n @property({ type: Number, attribute: 'hide-delay' })\n get hideDelay(): number {\n return this._hideDelay\n }\n set hideDelay(v: number) {\n const old = this._hideDelay\n this._hideDelay = normalizeNumber(v, 0, 5000, 100)\n this.requestUpdate('hideDelay', old)\n }\n private _hideDelay = 100\n\n @property({ type: Boolean, reflect: true }) disabled = false\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n const old = this._offset\n this._offset = normalizeNumber(v, 0, 100, 6)\n this.requestUpdate('offset', old)\n }\n private _offset = 6\n\n @property({ type: String }) content = ''\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n\n private _showTimer?: ReturnType<typeof setTimeout>\n private _hideTimer?: ReturnType<typeof setTimeout>\n private _isCountedVisible = false\n private _shouldOpenInstantly = true\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-panel') ?? null,\n getPositioning: () => ({\n placement: this.placement,\n offset: this.offset,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal()\n })\n\n get isOpen(): boolean {\n return this.open\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n this.addEventListener('focusin', this._onFocusIn)\n this.addEventListener('focusout', this._onFocusOut)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this.removeEventListener('focusin', this._onFocusIn)\n this.removeEventListener('focusout', this._onFocusOut)\n clearTimeout(this._showTimer)\n clearTimeout(this._hideTimer)\n this._syncVisibleTooltipCount(false)\n this._panel.dispose()\n }\n\n override firstUpdated() {\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n }\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('portal') || changed.has('overlayContainer')) {\n requestAnimationFrame(() => this._reconfigureOverlay())\n } else if (changed.has('placement') || changed.has('offset'))\n requestAnimationFrame(() => this._panel.updatePosition())\n\n if (changed.has('open')) {\n this._syncVisibleTooltipCount(this.open)\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n } else {\n void this._closeOverlay()\n }\n this._dispatchChange(this.open)\n }\n if (changed.has('content')) this._syncPortalContent()\n }\n\n private _onPointerEnter = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n this._showTimer = setTimeout(() => this._show(), visibleTooltipCount > 0 ? 0 : this.showDelay)\n }\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hideTimer = setTimeout(() => this._hide(), this.hideDelay)\n }\n\n private _onFocusIn = () => {\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n this._show(true)\n }\n\n private _onFocusOut = () => {\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hide()\n }\n\n private _show(isInstant = false) {\n if (this.open) return\n this._shouldOpenInstantly = isInstant\n this.open = true\n }\n\n private _hide() {\n if (!this.open) return\n this.open = false\n }\n\n private _openOverlay(isInstant = false) {\n this._panel.open(isInstant)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = createOverlayPortal({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'tooltip-panel portal wui-glass wui-floating-panel'\n })\n if (this.content) {\n const text = document.createElement('span')\n text.className = 'tooltip-text'\n text.textContent = this.content\n portal.panel.append(text)\n } else portal.moveContent(Array.from(this.querySelectorAll('[slot=\"content\"]')))\n portal.panel.setAttribute('role', 'tooltip')\n return portal\n }\n\n private async _closeOverlay() {\n await this._panel.close(() => this.open)\n }\n\n private _reconfigureOverlay() {\n this._panel.reconfigure(this.open)\n }\n\n private _syncPortalContent() {\n if (!this.portal || !this.open) return\n const panel = this._panel.getPanel()\n if (!panel) return\n const text = panel.querySelector<HTMLElement>('.tooltip-text')\n if (text) {\n text.textContent = this.content\n return\n }\n if (!this.content) return\n panel.replaceChildren()\n const nextText = document.createElement('span')\n nextText.className = 'tooltip-text'\n nextText.textContent = this.content\n panel.append(nextText)\n }\n\n private _dispatchChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _syncVisibleTooltipCount(isVisible: boolean) {\n if (isVisible && !this._isCountedVisible) {\n visibleTooltipCount += 1\n this._isCountedVisible = true\n } else if (!isVisible && this._isCountedVisible) {\n visibleTooltipCount = Math.max(0, visibleTooltipCount - 1)\n this._isCountedVisible = false\n }\n }\n\n override render() {\n return html`\n <div class=\"tooltip-anchor\">\n <div class=\"tooltip-trigger\"><slot></slot></div>\n <div class=\"tooltip-panel wui-glass wui-floating-panel\" hidden role=\"tooltip\">\n ${this.content ? html`<span class=\"tooltip-text\">${this.content}</span>` : html`<slot name=\"content\"></slot>`}\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-tooltip': WebUiTooltip\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/tooltip/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport overlayMotion from '@/assets/overlay-motion.css?inline'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { defineAnchoredPanel } from '@/shared/overlay/anchored-panel'\nimport { createOverlayPortal } from '@/shared/overlay/portal'\nimport type { OverlayContainer, OverlayPortal } from '@/shared/overlay/portal'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = [\n 'top',\n 'top-start',\n 'top-end',\n 'bottom',\n 'bottom-start',\n 'bottom-end',\n 'left',\n 'left-start',\n 'left-end',\n 'right',\n 'right-start',\n 'right-end'\n] as const\n\nlet visibleTooltipCount = 0\n\n@customElement('web-ui-tooltip')\nexport class WebUiTooltip extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(overlayMotion), unsafeCSS(style)]\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'top')\n this.requestUpdate('placement', old)\n }\n private _placement: Placement = 'top'\n\n @property({ type: Number, attribute: 'show-delay' })\n get showDelay(): number {\n return this._showDelay\n }\n set showDelay(v: number) {\n const old = this._showDelay\n this._showDelay = normalizeNumber(v, 0, 5000, 200)\n this.requestUpdate('showDelay', old)\n }\n private _showDelay = 200\n\n @property({ type: Number, attribute: 'hide-delay' })\n get hideDelay(): number {\n return this._hideDelay\n }\n set hideDelay(v: number) {\n const old = this._hideDelay\n this._hideDelay = normalizeNumber(v, 0, 5000, 100)\n this.requestUpdate('hideDelay', old)\n }\n private _hideDelay = 100\n\n @property({ type: Boolean, reflect: true }) disabled = false\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n const old = this._offset\n this._offset = normalizeNumber(v, 0, 100, 6)\n this.requestUpdate('offset', old)\n }\n private _offset = 6\n\n @property({ type: String }) content = ''\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) portal = false\n @property({ attribute: false }) overlayContainer?: OverlayContainer\n\n private _showTimer?: ReturnType<typeof setTimeout>\n private _hideTimer?: ReturnType<typeof setTimeout>\n private _isCountedVisible = false\n private readonly _userOpenChange = new UserChangeController()\n private _shouldOpenInstantly = true\n private readonly _panel = defineAnchoredPanel().make({\n getAnchor: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-trigger') ?? null,\n getLocalPanel: () => this.shadowRoot?.querySelector<HTMLElement>('.tooltip-panel') ?? null,\n getPositioning: () => ({\n placement: this.placement,\n offset: this.offset,\n strategy: this.portal ? 'fixed' : 'absolute'\n }),\n isPortal: () => this.portal,\n createPortal: () => this._createPortal()\n })\n\n get isOpen(): boolean {\n return this.open\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('pointerenter', this._onPointerEnter)\n this.addEventListener('pointerleave', this._onPointerLeave)\n this.addEventListener('focusin', this._onFocusIn)\n this.addEventListener('focusout', this._onFocusOut)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('pointerenter', this._onPointerEnter)\n this.removeEventListener('pointerleave', this._onPointerLeave)\n this.removeEventListener('focusin', this._onFocusIn)\n this.removeEventListener('focusout', this._onFocusOut)\n clearTimeout(this._showTimer)\n clearTimeout(this._hideTimer)\n this._syncVisibleTooltipCount(false)\n this._panel.dispose()\n }\n\n override firstUpdated() {\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n }\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('portal') || changed.has('overlayContainer')) {\n requestAnimationFrame(() => this._reconfigureOverlay())\n } else if (changed.has('placement') || changed.has('offset'))\n requestAnimationFrame(() => this._panel.updatePosition())\n\n if (changed.has('open')) {\n this._syncVisibleTooltipCount(this.open)\n if (this.open) {\n this._openOverlay(this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n } else {\n void this._closeOverlay()\n }\n if (this._userOpenChange.consume()) this._dispatchChange(this.open)\n }\n if (changed.has('content')) this._syncPortalContent()\n }\n\n private _onPointerEnter = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n this._showTimer = setTimeout(() => this._show(), visibleTooltipCount > 0 ? 0 : this.showDelay)\n }\n\n private _onPointerLeave = (e: PointerEvent) => {\n if (e.pointerType === 'touch') return\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hideTimer = setTimeout(() => this._hide(), this.hideDelay)\n }\n\n private _onFocusIn = () => {\n if (this.disabled) return\n clearTimeout(this._hideTimer)\n this._show(true)\n }\n\n private _onFocusOut = () => {\n if (this.disabled) return\n clearTimeout(this._showTimer)\n this._hide()\n }\n\n private _show(isInstant = false) {\n if (this.open) return\n this._shouldOpenInstantly = isInstant\n this._userOpenChange.mark()\n this.open = true\n }\n\n private _hide() {\n if (!this.open) return\n this._userOpenChange.mark()\n this.open = false\n }\n\n private _openOverlay(isInstant = false) {\n this._panel.open(isInstant)\n }\n\n private _createPortal(): OverlayPortal {\n const portal = createOverlayPortal({\n container: this.overlayContainer,\n target: this,\n style: `${glass}\\n${overlayMotion}\\n${style}`,\n className: 'tooltip-panel portal wui-glass wui-floating-panel'\n })\n if (this.content) {\n const text = document.createElement('span')\n text.className = 'tooltip-text'\n text.textContent = this.content\n portal.panel.append(text)\n } else portal.moveContent(Array.from(this.querySelectorAll('[slot=\"content\"]')))\n portal.panel.setAttribute('role', 'tooltip')\n return portal\n }\n\n private async _closeOverlay() {\n await this._panel.close(() => this.open)\n }\n\n private _reconfigureOverlay() {\n this._panel.reconfigure(this.open)\n }\n\n private _syncPortalContent() {\n if (!this.portal || !this.open) return\n const panel = this._panel.getPanel()\n if (!panel) return\n const text = panel.querySelector<HTMLElement>('.tooltip-text')\n if (text) {\n text.textContent = this.content\n return\n }\n if (!this.content) return\n panel.replaceChildren()\n const nextText = document.createElement('span')\n nextText.className = 'tooltip-text'\n nextText.textContent = this.content\n panel.append(nextText)\n }\n\n private _dispatchChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _syncVisibleTooltipCount(isVisible: boolean) {\n if (isVisible && !this._isCountedVisible) {\n visibleTooltipCount += 1\n this._isCountedVisible = true\n } else if (!isVisible && this._isCountedVisible) {\n visibleTooltipCount = Math.max(0, visibleTooltipCount - 1)\n this._isCountedVisible = false\n }\n }\n\n override render() {\n return html`\n <div class=\"tooltip-anchor\">\n <div class=\"tooltip-trigger\"><slot></slot></div>\n <div class=\"tooltip-panel wui-glass wui-floating-panel\" hidden role=\"tooltip\">\n ${this.content ? html`<span class=\"tooltip-text\">${this.content}</span>` : html`<slot name=\"content\"></slot>`}\n </div>\n </div>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-tooltip': WebUiTooltip\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
//#region src/shared/events/user-change.ts
|
|
2
|
+
var e = class {
|
|
3
|
+
constructor() {
|
|
4
|
+
this.pending = !1;
|
|
5
|
+
}
|
|
6
|
+
mark() {
|
|
7
|
+
this.pending = !0;
|
|
8
|
+
}
|
|
9
|
+
consume() {
|
|
10
|
+
let e = this.pending;
|
|
11
|
+
return this.pending = !1, e;
|
|
12
|
+
}
|
|
13
|
+
};
|
|
14
|
+
//#endregion
|
|
15
|
+
export { e as UserChangeController };
|
|
16
|
+
|
|
17
|
+
//# sourceMappingURL=user-change.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"user-change.js","names":[],"sources":["../../../src/shared/events/user-change.ts"],"sourcesContent":["/**\n * Tracks whether the next reactive state update was caused by user interaction.\n *\n * Lit updates are asynchronous, so a boolean that is reset immediately after\n * assigning a property would be lost before `updated()` runs. Components use\n * this small controller to keep `*-change` events user-originated only.\n */\nexport class UserChangeController {\n private pending = false\n\n mark() {\n this.pending = true\n }\n\n consume(): boolean {\n const pending = this.pending\n this.pending = false\n return pending\n }\n}\n"],"mappings":";AAOA,IAAa,IAAb,MAAkC;;EACd,KAAA,UAAA;;CAElB,OAAO;EACL,KAAK,UAAU;CACjB;CAEA,UAAmB;EACjB,IAAM,IAAU,KAAK;EAErB,OADA,KAAK,UAAU,IACR;CACT;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/shared/normalize/index.ts"],"sourcesContent":["/**\n * 运行时参数规范化工具。\n *\n * 组件的属性 setter 在赋值前调用这些函数,确保非法输入回退到文档化默认值。\n * 这与 TypeScript 类型系统互补——类型保证编译期安全,运行时规范化保证 JS 调用方的健壮性。\n *\n * 使用方式:\n * ```\n * import { normalizeLiteral } from '@/shared/normalize'\n *\n * @property({ type: String, reflect: true })\n * get variant(): 'primary' | 'secondary' | 'ghost' { return this._variant }\n * set variant(v: string) {\n * this._variant = normalizeLiteral(v, ALLOWED_VARIANTS, 'primary')\n * this.requestUpdate('variant', old)\n * }\n * ```\n */\n\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/shared/normalize/index.ts"],"sourcesContent":["/**\n * 运行时参数规范化工具。\n *\n * 组件的属性 setter 在赋值前调用这些函数,确保非法输入回退到文档化默认值。\n * 这与 TypeScript 类型系统互补——类型保证编译期安全,运行时规范化保证 JS 调用方的健壮性。\n *\n * 使用方式:\n * ```\n * import { normalizeLiteral } from '@/shared/normalize'\n *\n * @property({ type: String, reflect: true })\n * get variant(): 'primary' | 'secondary' | 'ghost' { return this._variant }\n * set variant(v: string) {\n * this._variant = normalizeLiteral(v, ALLOWED_VARIANTS, 'primary')\n * this.requestUpdate('variant', old)\n * }\n * ```\n */\n\n// 字面量联合类型属性可接受的值\nexport type LiteralValues<T extends string> = readonly T[]\n\n/**\n * 将输入值规范化为字面量联合类型之一。\n * 不在允许集合中的值回退到默认值。\n */\nexport function normalizeLiteral<T extends string>(value: unknown, allowed: LiteralValues<T>, defaultValue: T): T {\n if (typeof value === 'string' && (allowed as readonly string[]).includes(value)) {\n return value as T\n }\n return defaultValue\n}\n\n/**\n * 将输入值规范化为指定范围内的数值。\n * NaN、Infinity 或非数值回退到默认值。\n */\nexport function normalizeNumber(value: unknown, min: number, max: number, defaultValue: number): number {\n if (typeof value === 'number' && Number.isFinite(value)) {\n return Math.min(max, Math.max(min, value))\n }\n return defaultValue\n}\n"],"mappings":";AA0BA,SAAgB,EAAmC,GAAgB,GAA2B,GAAoB;CAIhH,OAHI,OAAO,KAAU,YAAa,EAA8B,SAAS,CAAK,IACrE,IAEF;AACT;AAMA,SAAgB,EAAgB,GAAgB,GAAa,GAAa,GAA8B;CAItG,OAHI,OAAO,KAAU,YAAY,OAAO,SAAS,CAAK,IAC7C,KAAK,IAAI,GAAK,KAAK,IAAI,GAAK,CAAK,CAAC,IAEpC;AACT"}
|
|
@@ -11,9 +11,14 @@ var n = () => t((t) => {
|
|
|
11
11
|
!e || !i || t.isOpen() || (a(), i = !1, e.classList.remove("is-closing", "is-visible"), e.open && e.close?.());
|
|
12
12
|
}, c = () => {
|
|
13
13
|
let e = t.getDialog();
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
14
|
+
if (e) {
|
|
15
|
+
if (i = !1, a(), e.classList.remove("is-closing"), !e.open) try {
|
|
16
|
+
e.showModal?.();
|
|
17
|
+
} catch {}
|
|
18
|
+
o(), r = requestAnimationFrame(() => {
|
|
19
|
+
r = void 0, !(!t.isConnected() || !t.isOpen() || !e.open) && e.classList.add("is-visible");
|
|
20
|
+
});
|
|
21
|
+
}
|
|
17
22
|
}, l = () => {
|
|
18
23
|
let r = t.getDialog();
|
|
19
24
|
if (r?.open) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"native-dialog-presence.js","names":[],"sources":["../../../src/shared/overlay/native-dialog-presence.ts"],"sourcesContent":["import { definePlugin } from '@greypan/js-kit'\n\nimport { getTransitionDuration } from './presence'\n\nexport interface NativeDialogPresenceOptions {\n getDialog(): HTMLDialogElement | null\n isConnected(): boolean\n isOpen(): boolean\n}\n\nexport interface NativeDialogPresenceApi {\n sync(open: boolean): void\n handleTransitionEnd(event: TransitionEvent): void\n handleNativeClose(): void\n dispose(): void\n}\n\n
|
|
1
|
+
{"version":3,"file":"native-dialog-presence.js","names":[],"sources":["../../../src/shared/overlay/native-dialog-presence.ts"],"sourcesContent":["import { definePlugin } from '@greypan/js-kit'\n\nimport { getTransitionDuration } from './presence'\n\nexport interface NativeDialogPresenceOptions {\n getDialog(): HTMLDialogElement | null\n isConnected(): boolean\n isOpen(): boolean\n}\n\nexport interface NativeDialogPresenceApi {\n sync(open: boolean): void\n handleTransitionEnd(event: TransitionEvent): void\n handleNativeClose(): void\n dispose(): void\n}\n\n// 管理原生 dialog 保持在 top layer 直至退出动画完成的生命周期。\nexport const defineNativeDialogPresence = () =>\n definePlugin<NativeDialogPresenceApi, NativeDialogPresenceOptions>(ctx => {\n let closeFallbackTimer: ReturnType<typeof setTimeout> | undefined\n let openFrame: number | undefined\n let isClosing = false\n\n const clearCloseFallback = () => {\n if (closeFallbackTimer === undefined) return\n clearTimeout(closeFallbackTimer)\n closeFallbackTimer = undefined\n }\n\n const cancelOpenFrame = () => {\n if (openFrame === undefined) return\n cancelAnimationFrame(openFrame)\n openFrame = undefined\n }\n\n const finishClosing = () => {\n const dialog = ctx.getDialog()\n if (!dialog || !isClosing || ctx.isOpen()) return\n clearCloseFallback()\n isClosing = false\n dialog.classList.remove('is-closing', 'is-visible')\n if (dialog.open) dialog.close?.()\n }\n\n const startOpening = () => {\n const dialog = ctx.getDialog()\n if (!dialog) return\n isClosing = false\n clearCloseFallback()\n dialog.classList.remove('is-closing')\n if (!dialog.open) {\n try {\n dialog.showModal?.()\n } catch {\n // 顶层已有其他 modal dialog 时 showModal 抛 InvalidStateError;\n // 记录未打开状态,由 rAF 回调中的 dialog.open 判断中止动画\n }\n }\n\n cancelOpenFrame()\n openFrame = requestAnimationFrame(() => {\n openFrame = undefined\n if (!ctx.isConnected() || !ctx.isOpen() || !dialog.open) return\n dialog.classList.add('is-visible')\n })\n }\n\n const startClosing = () => {\n const dialog = ctx.getDialog()\n if (!dialog?.open) return\n cancelOpenFrame()\n isClosing = true\n if (!dialog.classList.contains('is-visible')) {\n finishClosing()\n return\n }\n\n dialog.classList.add('is-closing')\n dialog.classList.remove('is-visible')\n clearCloseFallback()\n closeFallbackTimer = setTimeout(() => finishClosing(), getTransitionDuration(dialog) + 80)\n }\n\n return {\n sync(open) {\n if (open) startOpening()\n else startClosing()\n },\n\n handleTransitionEnd(event) {\n if (event.target !== ctx.getDialog() || event.propertyName !== 'transform') return\n finishClosing()\n },\n\n handleNativeClose() {\n cancelOpenFrame()\n clearCloseFallback()\n isClosing = false\n ctx.getDialog()?.classList.remove('is-closing', 'is-visible')\n },\n\n dispose() {\n cancelOpenFrame()\n clearCloseFallback()\n isClosing = false\n }\n }\n })\n"],"mappings":";;;AAkBA,IAAa,UACX,GAAmE,MAAO;CACxE,IAAI,GACA,GACA,IAAY,IAEV,UAA2B;EAC3B,MAAuB,KAAA,MAC3B,aAAa,CAAkB,GAC/B,IAAqB,KAAA;CACvB,GAEM,UAAwB;EACxB,MAAc,KAAA,MAClB,qBAAqB,CAAS,GAC9B,IAAY,KAAA;CACd,GAEM,UAAsB;EAC1B,IAAM,IAAS,EAAI,UAAU;EACzB,CAAC,KAAU,CAAC,KAAa,EAAI,OAAO,MACxC,EAAmB,GACnB,IAAY,IACZ,EAAO,UAAU,OAAO,cAAc,YAAY,GAC9C,EAAO,QAAM,EAAO,QAAQ;CAClC,GAEM,UAAqB;EACzB,IAAM,IAAS,EAAI,UAAU;EACxB,OAIL;OAHA,IAAY,IACZ,EAAmB,GACnB,EAAO,UAAU,OAAO,YAAY,GAChC,CAAC,EAAO,MACV,IAAI;IACF,EAAO,YAAY;GACrB,QAAQ,CAGR;GAIF,AADA,EAAgB,GAChB,IAAY,4BAA4B;IACtC,IAAY,KAAA,GACR,GAAC,EAAI,YAAY,KAAK,CAAC,EAAI,OAAO,KAAK,CAAC,EAAO,SACnD,EAAO,UAAU,IAAI,YAAY;GACnC,CAAC;EARC;CASJ,GAEM,UAAqB;EACzB,IAAM,IAAS,EAAI,UAAU;EACxB,OAAQ,MAGb;OAFA,EAAgB,GAChB,IAAY,IACR,CAAC,EAAO,UAAU,SAAS,YAAY,GAAG;IAC5C,EAAc;IACd;GACF;GAKA,AAHA,EAAO,UAAU,IAAI,YAAY,GACjC,EAAO,UAAU,OAAO,YAAY,GACpC,EAAmB,GACnB,IAAqB,iBAAiB,EAAc,GAAG,EAAsB,CAAM,IAAI,EAAE;EALzF;CAMF;CAEA,OAAO;EACL,KAAK,GAAM;GACT,AAAI,IAAM,EAAa,IAClB,EAAa;EACpB;EAEA,oBAAoB,GAAO;GACrB,EAAM,WAAW,EAAI,UAAU,KAAK,EAAM,iBAAiB,eAC/D,EAAc;EAChB;EAEA,oBAAoB;GAIlB,AAHA,EAAgB,GAChB,EAAmB,GACnB,IAAY,IACZ,EAAI,UAAU,CAAC,EAAE,UAAU,OAAO,cAAc,YAAY;EAC9D;EAEA,UAAU;GAGR,AAFA,EAAgB,GAChB,EAAmB,GACnB,IAAY;EACd;CACF;AACF,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"overlay.js","names":[],"sources":["../../../src/shared/overlay/overlay.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n offset,\n shift,\n size,\n type Middleware,\n type Placement,\n type Strategy\n} from '@floating-ui/dom'\nimport { definePlugin } from '@greypan/js-kit'\n\nexport interface OverlayOptions {\n placement?: Placement\n offset?: number\n flip?: boolean\n shift?: boolean\n
|
|
1
|
+
{"version":3,"file":"overlay.js","names":[],"sources":["../../../src/shared/overlay/overlay.ts"],"sourcesContent":["import {\n autoUpdate,\n computePosition,\n flip,\n offset,\n shift,\n size,\n type Middleware,\n type Placement,\n type Strategy\n} from '@floating-ui/dom'\nimport { definePlugin } from '@greypan/js-kit'\n\nexport interface OverlayOptions {\n placement?: Placement\n offset?: number\n flip?: boolean\n shift?: boolean\n // Sync overlay width to anchor width\n matchWidth?: boolean\n // Set overlay min-width to anchor width (content can expand wider)\n minAnchorWidth?: boolean\n strategy?: Strategy\n}\n\nexport interface OverlayApi {\n isOpen(): boolean\n readonly options: Required<OverlayOptions>\n open(): void\n close(): void\n toggle(): void\n update(options: Partial<OverlayOptions>): void\n updateAnchor(anchor: HTMLElement): void\n dispose(): void\n}\n\nconst DEFAULT_OPTIONS: Required<OverlayOptions> = {\n placement: 'bottom-start',\n offset: 4,\n flip: true,\n shift: true,\n matchWidth: false,\n minAnchorWidth: false,\n strategy: 'absolute'\n}\n\nfunction getTransformOrigin(placement: Placement): string {\n const [side, alignment] = placement.split('-')\n const horizontalOrigin = alignment === 'start' ? 'left' : alignment === 'end' ? 'right' : 'center'\n const verticalOrigin = alignment === 'start' ? 'top' : alignment === 'end' ? 'bottom' : 'center'\n\n if (side === 'top') return `bottom ${horizontalOrigin}`\n if (side === 'bottom') return `top ${horizontalOrigin}`\n if (side === 'left') return `right ${verticalOrigin}`\n return `left ${verticalOrigin}`\n}\n\nexport const defineOverlay = () =>\n definePlugin<OverlayApi, { anchor: HTMLElement; overlay: HTMLElement } & OverlayOptions>(ctx => {\n const options: Required<OverlayOptions> = {\n placement: ctx.placement ?? DEFAULT_OPTIONS.placement,\n offset: ctx.offset ?? DEFAULT_OPTIONS.offset,\n flip: ctx.flip ?? DEFAULT_OPTIONS.flip,\n shift: ctx.shift ?? DEFAULT_OPTIONS.shift,\n matchWidth: ctx.matchWidth ?? DEFAULT_OPTIONS.matchWidth,\n minAnchorWidth: ctx.minAnchorWidth ?? DEFAULT_OPTIONS.minAnchorWidth,\n strategy: ctx.strategy ?? DEFAULT_OPTIONS.strategy\n }\n\n const overlay = ctx.overlay\n let isOpen = false\n let cleanup: (() => void) | null = null\n let anchor = ctx.anchor\n\n function updatePosition() {\n const middleware: Middleware[] = [offset(options.offset)]\n if (options.flip) middleware.push(flip())\n if (options.shift) middleware.push(shift({ padding: 8 }))\n if (options.matchWidth) {\n middleware.push(\n size({\n apply({ rects }) {\n overlay.style.width = `${rects.reference.width}px`\n }\n })\n )\n } else if (options.minAnchorWidth) {\n middleware.push(\n size({\n apply({ rects }) {\n overlay.style.minWidth = `${Math.max(rects.reference.width, 120)}px`\n }\n })\n )\n }\n\n cleanup?.()\n cleanup = autoUpdate(anchor, overlay, () => {\n void computePosition(anchor, overlay, {\n placement: options.placement,\n strategy: options.strategy,\n middleware\n }).then(({ x, y, placement }) => {\n overlay.style.left = `${x}px`\n overlay.style.top = `${y}px`\n overlay.style.setProperty('--wui-overlay-transform-origin', getTransformOrigin(placement))\n })\n })\n }\n\n return {\n isOpen() {\n return isOpen\n },\n options,\n\n open() {\n if (isOpen) return\n isOpen = true\n overlay.style.display = ''\n updatePosition()\n },\n\n close() {\n if (!isOpen) return\n isOpen = false\n cleanup?.()\n cleanup = null\n },\n\n toggle() {\n if (isOpen) this.close()\n else this.open()\n },\n\n update(nextOptions) {\n Object.assign(options, nextOptions)\n if (isOpen) updatePosition()\n },\n\n updateAnchor(newAnchor: HTMLElement) {\n anchor = newAnchor\n if (isOpen) updatePosition()\n },\n\n dispose() {\n this.close()\n }\n }\n })\n"],"mappings":";;;AAoCA,IAAM,IAA4C;CAChD,WAAW;CACX,QAAQ;CACR,MAAM;CACN,OAAO;CACP,YAAY;CACZ,gBAAgB;CAChB,UAAU;AACZ;AAEA,SAAS,EAAmB,GAA8B;CACxD,IAAM,CAAC,GAAM,KAAa,EAAU,MAAM,GAAG,GACvC,IAAmB,MAAc,UAAU,SAAS,MAAc,QAAQ,UAAU,UACpF,IAAiB,MAAc,UAAU,QAAQ,MAAc,QAAQ,WAAW;CAKxF,OAHI,MAAS,QAAc,UAAU,MACjC,MAAS,WAAiB,OAAO,MACjC,MAAS,SAAe,SAAS,MAC9B,QAAQ;AACjB;AAEA,IAAa,UACX,GAAyF,MAAO;CAC9F,IAAM,IAAoC;EACxC,WAAW,EAAI,aAAa,EAAgB;EAC5C,QAAQ,EAAI,UAAU,EAAgB;EACtC,MAAM,EAAI,QAAQ,EAAgB;EAClC,OAAO,EAAI,SAAS,EAAgB;EACpC,YAAY,EAAI,cAAc,EAAgB;EAC9C,gBAAgB,EAAI,kBAAkB,EAAgB;EACtD,UAAU,EAAI,YAAY,EAAgB;CAC5C,GAEM,IAAU,EAAI,SAChB,IAAS,IACT,IAA+B,MAC/B,IAAS,EAAI;CAEjB,SAAS,IAAiB;EACxB,IAAM,IAA2B,CAAC,EAAO,EAAQ,MAAM,CAAC;EAsBxD,AArBI,EAAQ,QAAM,EAAW,KAAK,EAAK,CAAC,GACpC,EAAQ,SAAO,EAAW,KAAK,EAAM,EAAE,SAAS,EAAE,CAAC,CAAC,GACpD,EAAQ,aACV,EAAW,KACT,EAAK,EACH,MAAM,EAAE,YAAS;GACf,EAAQ,MAAM,QAAQ,GAAG,EAAM,UAAU,MAAM;EACjD,EACF,CAAC,CACH,IACS,EAAQ,kBACjB,EAAW,KACT,EAAK,EACH,MAAM,EAAE,YAAS;GACf,EAAQ,MAAM,WAAW,GAAG,KAAK,IAAI,EAAM,UAAU,OAAO,GAAG,EAAE;EACnE,EACF,CAAC,CACH,GAGF,IAAU,GACV,IAAU,EAAW,GAAQ,SAAe;GAC1C,EAAqB,GAAQ,GAAS;IACpC,WAAW,EAAQ;IACnB,UAAU,EAAQ;IAClB;GACF,CAAC,CAAC,CAAC,MAAM,EAAE,MAAG,MAAG,mBAAgB;IAG/B,AAFA,EAAQ,MAAM,OAAO,GAAG,EAAE,KAC1B,EAAQ,MAAM,MAAM,GAAG,EAAE,KACzB,EAAQ,MAAM,YAAY,kCAAkC,EAAmB,CAAS,CAAC;GAC3F,CAAC;EACH,CAAC;CACH;CAEA,OAAO;EACL,SAAS;GACP,OAAO;EACT;EACA;EAEA,OAAO;GACD,MACJ,IAAS,IACT,EAAQ,MAAM,UAAU,IACxB,EAAe;EACjB;EAEA,QAAQ;GACD,MACL,IAAS,IACT,IAAU,GACV,IAAU;EACZ;EAEA,SAAS;GACP,AAAI,IAAQ,KAAK,MAAM,IAClB,KAAK,KAAK;EACjB;EAEA,OAAO,GAAa;GAElB,AADA,OAAO,OAAO,GAAS,CAAW,GAC9B,KAAQ,EAAe;EAC7B;EAEA,aAAa,GAAwB;GAEnC,AADA,IAAS,GACL,KAAQ,EAAe;EAC7B;EAEA,UAAU;GACR,KAAK,MAAM;EACb;CACF;AACF,CAAC"}
|
|
@@ -2,7 +2,9 @@ import { getFallbackOverlayRoot as e } from "../theme/overlay-root.js";
|
|
|
2
2
|
import { findNearestTheme as t } from "../theme/theme-scope.js";
|
|
3
3
|
//#region src/shared/overlay/portal.ts
|
|
4
4
|
function n(e) {
|
|
5
|
-
let t = document.createElement("div")
|
|
5
|
+
let t = document.createElement("div");
|
|
6
|
+
t.style.display = "contents";
|
|
7
|
+
let n = t.attachShadow({ mode: "open" }), i = document.createElement("style");
|
|
6
8
|
i.textContent = e.style;
|
|
7
9
|
let a = document.createElement("div");
|
|
8
10
|
a.className = e.className, a.dataset.wuiPresence = "entering", n.append(i, a), r(e.container, e.target).appendChild(t);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"portal.js","names":[],"sources":["../../../src/shared/overlay/portal.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/theme/overlay-root'\nimport { findNearestTheme } from '@/shared/theme/theme-scope'\n\nexport type OverlayContainer = HTMLElement | (() => HTMLElement | undefined)\n\ninterface OverlayPortalOptions {\n container?: OverlayContainer\n target: Element\n style: string\n className: string\n}\n\nexport interface OverlayPortal {\n readonly panel: HTMLElement\n restoreContent(): void\n moveContent(nodes: Node[], target?: HTMLElement): void\n remove(): void\n}\n\nexport function createOverlayPortal(options: OverlayPortalOptions): OverlayPortal {\n const host = document.createElement('div')\n const root = host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = options.style\n const panel = document.createElement('div')\n panel.className = options.className\n panel.dataset.wuiPresence = 'entering'\n root.append(style, panel)\n resolveOverlayContainer(options.container, options.target).appendChild(host)\n\n const nodes: Node[] = []\n return {\n panel,\n moveContent(content, target = panel) {\n nodes.splice(0, nodes.length, ...content)\n target.append(...nodes)\n },\n restoreContent() {\n options.target.append(...nodes)\n nodes.length = 0\n },\n remove() {\n host.remove()\n }\n }\n}\n\nexport function resolveOverlayContainer(container: OverlayContainer | undefined, target: Element): HTMLElement {\n const explicit = typeof container === 'function' ? container() : container\n return explicit ?? findNearestTheme(target)?.getOverlayRoot() ?? getFallbackOverlayRoot()\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAoB,GAA8C;CAChF,IAAM,IAAO,SAAS,cAAc,KAAK,
|
|
1
|
+
{"version":3,"file":"portal.js","names":[],"sources":["../../../src/shared/overlay/portal.ts"],"sourcesContent":["import { getFallbackOverlayRoot } from '@/shared/theme/overlay-root'\nimport { findNearestTheme } from '@/shared/theme/theme-scope'\n\nexport type OverlayContainer = HTMLElement | (() => HTMLElement | undefined)\n\ninterface OverlayPortalOptions {\n container?: OverlayContainer\n target: Element\n style: string\n className: string\n}\n\nexport interface OverlayPortal {\n readonly panel: HTMLElement\n restoreContent(): void\n moveContent(nodes: Node[], target?: HTMLElement): void\n remove(): void\n}\n\nexport function createOverlayPortal(options: OverlayPortalOptions): OverlayPortal {\n const host = document.createElement('div')\n // 宿主只是固定定位面板的挂载点:注入的组件样式中的 :host 规则(如 display:inline-block)\n // 会匹配到宿主,在块级 overlay 容器里产生匿名行盒,把布局撑开一行。display: contents\n // 让宿主不生成盒;面板本身 position: fixed,定位不受影响。\n host.style.display = 'contents'\n const root = host.attachShadow({ mode: 'open' })\n const style = document.createElement('style')\n style.textContent = options.style\n const panel = document.createElement('div')\n panel.className = options.className\n panel.dataset.wuiPresence = 'entering'\n root.append(style, panel)\n resolveOverlayContainer(options.container, options.target).appendChild(host)\n\n const nodes: Node[] = []\n return {\n panel,\n moveContent(content, target = panel) {\n nodes.splice(0, nodes.length, ...content)\n target.append(...nodes)\n },\n restoreContent() {\n options.target.append(...nodes)\n nodes.length = 0\n },\n remove() {\n host.remove()\n }\n }\n}\n\nexport function resolveOverlayContainer(container: OverlayContainer | undefined, target: Element): HTMLElement {\n const explicit = typeof container === 'function' ? container() : container\n return explicit ?? findNearestTheme(target)?.getOverlayRoot() ?? getFallbackOverlayRoot()\n}\n"],"mappings":";;;AAmBA,SAAgB,EAAoB,GAA8C;CAChF,IAAM,IAAO,SAAS,cAAc,KAAK;CAIzC,EAAK,MAAM,UAAU;CACrB,IAAM,IAAO,EAAK,aAAa,EAAE,MAAM,OAAO,CAAC,GACzC,IAAQ,SAAS,cAAc,OAAO;CAC5C,EAAM,cAAc,EAAQ;CAC5B,IAAM,IAAQ,SAAS,cAAc,KAAK;CAI1C,AAHA,EAAM,YAAY,EAAQ,WAC1B,EAAM,QAAQ,cAAc,YAC5B,EAAK,OAAO,GAAO,CAAK,GACxB,EAAwB,EAAQ,WAAW,EAAQ,MAAM,CAAC,CAAC,YAAY,CAAI;CAE3E,IAAM,IAAgB,CAAC;CACvB,OAAO;EACL;EACA,YAAY,GAAS,IAAS,GAAO;GAEnC,AADA,EAAM,OAAO,GAAG,EAAM,QAAQ,GAAG,CAAO,GACxC,EAAO,OAAO,GAAG,CAAK;EACxB;EACA,iBAAiB;GAEf,AADA,EAAQ,OAAO,OAAO,GAAG,CAAK,GAC9B,EAAM,SAAS;EACjB;EACA,SAAS;GACP,EAAK,OAAO;EACd;CACF;AACF;AAEA,SAAgB,EAAwB,GAAyC,GAA8B;CAE7G,QADiB,OAAO,KAAc,aAAa,EAAU,IAAI,MAC9C,EAAiB,CAAM,CAAC,EAAE,eAAe,KAAK,EAAuB;AAC1F"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"presence.js","names":[],"sources":["../../../src/shared/overlay/presence.ts"],"sourcesContent":["const DEFAULT_EXIT_DURATION = 160\nconst EXIT_FALLBACK_BUFFER = 80\n\nconst pendingExits = new WeakMap<HTMLElement, () => void>()\n\ninterface OverlayPresenceOptions {\n isInstant?: boolean\n}\n\nfunction parseDuration(value: string): number {\n const trimmed = value.trim()\n if (trimmed.endsWith('ms')) return Number.parseFloat(trimmed)\n if (trimmed.endsWith('s')) return Number.parseFloat(trimmed) * 1000\n return 0\n}\n\n
|
|
1
|
+
{"version":3,"file":"presence.js","names":[],"sources":["../../../src/shared/overlay/presence.ts"],"sourcesContent":["const DEFAULT_EXIT_DURATION = 160\nconst EXIT_FALLBACK_BUFFER = 80\n\nconst pendingExits = new WeakMap<HTMLElement, () => void>()\n\ninterface OverlayPresenceOptions {\n isInstant?: boolean\n}\n\nfunction parseDuration(value: string): number {\n const trimmed = value.trim()\n if (trimmed.endsWith('ms')) return Number.parseFloat(trimmed)\n if (trimmed.endsWith('s')) return Number.parseFloat(trimmed) * 1000\n return 0\n}\n\n// 读取元素实际 CSS transition 的最长时长,供可覆盖主题 token 的 JS 生命周期使用。\nexport function getTransitionDuration(panel: HTMLElement, fallback = DEFAULT_EXIT_DURATION): number {\n const style = getComputedStyle(panel)\n const durations = style.transitionDuration.split(',').map(parseDuration)\n const delays = style.transitionDelay.split(',').map(parseDuration)\n const longest = durations.reduce(\n (max, duration, index) => Math.max(max, duration + (delays[index] ?? delays[0] ?? 0)),\n 0\n )\n\n return longest || fallback\n}\n\n// 以可中断的方式显示浮层,避免快速切换时重启动画。\nexport function showOverlayPresence(panel: HTMLElement, options: OverlayPresenceOptions = {}) {\n pendingExits.get(panel)?.()\n panel.hidden = false\n if (options.isInstant) {\n panel.dataset.wuiPresence = 'open'\n return\n }\n panel.dataset.wuiPresence = 'entering'\n\n // Commit the initial state so a newly mounted panel has a transition source.\n void panel.offsetWidth\n\n requestAnimationFrame(() => {\n if (panel.dataset.wuiPresence === 'entering') panel.dataset.wuiPresence = 'open'\n })\n}\n\n// 播放退出过渡;若在结束前重新显示则返回 false。\nexport function hideOverlayPresence(panel: HTMLElement): Promise<boolean> {\n pendingExits.get(panel)?.()\n panel.dataset.wuiPresence = 'closing'\n\n // jsdom 等没有计算 CSS transition 的环境无需伪造退场等待。\n if (getTransitionDuration(panel, 0) === 0) {\n panel.hidden = true\n return Promise.resolve(true)\n }\n\n return new Promise(resolve => {\n let settled = false\n const finish = (didClose: boolean) => {\n if (settled) return\n settled = true\n panel.removeEventListener('transitionend', onTransitionEnd)\n clearTimeout(timeout)\n pendingExits.delete(panel)\n if (didClose && panel.dataset.wuiPresence === 'closing') panel.hidden = true\n resolve(didClose)\n }\n\n const onTransitionEnd = (event: TransitionEvent) => {\n if (event.target === panel && (event.propertyName === 'opacity' || event.propertyName === 'transform'))\n finish(true)\n }\n\n pendingExits.set(panel, () => finish(false))\n panel.addEventListener('transitionend', onTransitionEnd)\n const timeout = setTimeout(() => finish(true), getTransitionDuration(panel) + EXIT_FALLBACK_BUFFER)\n })\n}\n"],"mappings":";AAAA,IAGM,oBAAe,IAAI,QAAiC;AAM1D,SAAS,EAAc,GAAuB;CAC5C,IAAM,IAAU,EAAM,KAAK;CAG3B,OAFI,EAAQ,SAAS,IAAI,IAAU,OAAO,WAAW,CAAO,IACxD,EAAQ,SAAS,GAAG,IAAU,OAAO,WAAW,CAAO,IAAI,MACxD;AACT;AAGA,SAAgB,EAAsB,GAAoB,IAAW,KAA+B;CAClG,IAAM,IAAQ,iBAAiB,CAAK,GAC9B,IAAY,EAAM,mBAAmB,MAAM,GAAG,CAAC,CAAC,IAAI,CAAa,GACjE,IAAS,EAAM,gBAAgB,MAAM,GAAG,CAAC,CAAC,IAAI,CAAa;CAMjE,OALgB,EAAU,QACvB,GAAK,GAAU,MAAU,KAAK,IAAI,GAAK,KAAY,EAAO,MAAU,EAAO,MAAM,EAAE,GACpF,CAGK,KAAW;AACpB;AAGA,SAAgB,EAAoB,GAAoB,IAAkC,CAAC,GAAG;CAG5F,IAFA,EAAa,IAAI,CAAK,CAAC,GAAG,GAC1B,EAAM,SAAS,IACX,EAAQ,WAAW;EACrB,EAAM,QAAQ,cAAc;EAC5B;CACF;CAMA,AALA,EAAM,QAAQ,cAAc,YAG5B,EAAW,aAEX,4BAA4B;EAC1B,AAAI,EAAM,QAAQ,gBAAgB,eAAY,EAAM,QAAQ,cAAc;CAC5E,CAAC;AACH;AAGA,SAAgB,EAAoB,GAAsC;CAUxE,OATA,EAAa,IAAI,CAAK,CAAC,GAAG,GAC1B,EAAM,QAAQ,cAAc,WAGxB,EAAsB,GAAO,CAAC,MAAM,KACtC,EAAM,SAAS,IACR,QAAQ,QAAQ,EAAI,KAGtB,IAAI,SAAQ,MAAW;EAC5B,IAAI,IAAU,IACR,KAAU,MAAsB;GAChC,MACJ,IAAU,IACV,EAAM,oBAAoB,iBAAiB,CAAe,GAC1D,aAAa,CAAO,GACpB,EAAa,OAAO,CAAK,GACrB,KAAY,EAAM,QAAQ,gBAAgB,cAAW,EAAM,SAAS,KACxE,EAAQ,CAAQ;EAClB,GAEM,KAAmB,MAA2B;GAClD,AAAI,EAAM,WAAW,MAAU,EAAM,iBAAiB,aAAa,EAAM,iBAAiB,gBACxF,EAAO,EAAI;EACf;EAGA,AADA,EAAa,IAAI,SAAa,EAAO,EAAK,CAAC,GAC3C,EAAM,iBAAiB,iBAAiB,CAAe;EACvD,IAAM,IAAU,iBAAiB,EAAO,EAAI,GAAG,EAAsB,CAAK,IAAI,EAAoB;CACpG,CAAC;AACH"}
|