@greypan/web-ui 3.0.1 → 5.0.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/README.CN.md +495 -125
- package/README.md +504 -125
- package/dist/_virtual/{_@oxc-project_runtime@0.140.0 → _@oxc-project_runtime@0.146.0}/helpers/esm/decorate.js +1 -1
- package/dist/assets/glass.js +1 -1
- package/dist/assets/glass.js.map +1 -1
- package/dist/assets/menu-portal.js +1 -1
- package/dist/assets/menu-portal.js.map +1 -1
- package/dist/assets/overlay-motion.js +1 -1
- package/dist/assets/overlay-motion.js.map +1 -1
- package/dist/assets/toast-containers.js +1 -1
- package/dist/assets/toast-containers.js.map +1 -1
- package/dist/components/autocomplete/index.d.ts +12 -12
- package/dist/components/autocomplete/index.js +162 -111
- package/dist/components/autocomplete/index.js.map +1 -1
- package/dist/components/autocomplete/style.js +1 -1
- package/dist/components/autocomplete/style.js.map +1 -1
- package/dist/components/avatar/index.d.ts +0 -1
- package/dist/components/avatar/index.js +8 -13
- package/dist/components/avatar/index.js.map +1 -1
- package/dist/components/back-top/index.js +2 -2
- package/dist/components/back-top/index.js.map +1 -1
- package/dist/components/back-top/style.js +1 -1
- package/dist/components/back-top/style.js.map +1 -1
- package/dist/components/badge/index.d.ts +1 -0
- package/dist/components/badge/index.js +12 -14
- package/dist/components/badge/index.js.map +1 -1
- package/dist/components/button/index.d.ts +2 -0
- package/dist/components/button/index.js +52 -38
- 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/button-group/index.d.ts +1 -2
- package/dist/components/button-group/index.js +21 -23
- package/dist/components/button-group/index.js.map +1 -1
- package/dist/components/button-group/style.js +1 -1
- package/dist/components/button-group/style.js.map +1 -1
- package/dist/components/checkbox/index.d.ts +4 -7
- package/dist/components/checkbox/index.js +56 -38
- package/dist/components/checkbox/index.js.map +1 -1
- package/dist/components/checkbox/style.js +1 -1
- package/dist/components/checkbox/style.js.map +1 -1
- package/dist/components/checkbox-group/index.d.ts +4 -10
- package/dist/components/checkbox-group/index.js +74 -60
- package/dist/components/checkbox-group/index.js.map +1 -1
- package/dist/components/collapse/index.d.ts +66 -0
- package/dist/components/collapse/index.js +159 -0
- package/dist/components/collapse/index.js.map +1 -0
- package/dist/components/collapse/style.js +6 -0
- package/dist/components/collapse/style.js.map +1 -0
- package/dist/components/context-menu/index.d.ts +29 -0
- package/dist/components/context-menu/index.js +168 -53
- package/dist/components/context-menu/index.js.map +1 -1
- package/dist/components/dialog/index.d.ts +6 -0
- package/dist/components/dialog/index.js +34 -13
- package/dist/components/dialog/index.js.map +1 -1
- package/dist/components/dialog/style.js +1 -1
- package/dist/components/dialog/style.js.map +1 -1
- package/dist/components/drawer/index.d.ts +45 -0
- package/dist/components/drawer/index.js +270 -56
- 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 -0
- package/dist/components/dropdown/index.js +24 -14
- package/dist/components/dropdown/index.js.map +1 -1
- package/dist/components/dropdown-divider/index.js +1 -1
- package/dist/components/dropdown-header/index.js +1 -1
- package/dist/components/dropdown-header/style.js +1 -1
- package/dist/components/dropdown-header/style.js.map +1 -1
- package/dist/components/dropdown-item/index.js +3 -2
- package/dist/components/dropdown-item/index.js.map +1 -1
- package/dist/components/dropdown-item/style.js +1 -1
- package/dist/components/dropdown-item/style.js.map +1 -1
- package/dist/components/empty/index.d.ts +1 -0
- package/dist/components/empty/index.js +24 -22
- package/dist/components/empty/index.js.map +1 -1
- package/dist/components/empty/style.js +1 -1
- package/dist/components/empty/style.js.map +1 -1
- package/dist/components/icon/index.js +1 -1
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +21 -20
- package/dist/components/input/index.d.ts +2 -3
- package/dist/components/input/index.js +62 -52
- 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.d.ts +2 -3
- package/dist/components/input-number/index.js +58 -38
- package/dist/components/input-number/index.js.map +1 -1
- package/dist/components/input-number/style.js +1 -1
- package/dist/components/input-number/style.js.map +1 -1
- package/dist/components/layout/index.d.ts +76 -3
- package/dist/components/layout/index.js +250 -39
- package/dist/components/layout/index.js.map +1 -1
- package/dist/components/layout/style.js +1 -1
- package/dist/components/layout/style.js.map +1 -1
- package/dist/components/option/index.js +1 -1
- package/dist/components/option/style.js +1 -1
- package/dist/components/option/style.js.map +1 -1
- package/dist/components/popover/index.d.ts +2 -1
- package/dist/components/popover/index.js +14 -8
- package/dist/components/popover/index.js.map +1 -1
- package/dist/components/popover/style.js +1 -1
- package/dist/components/popover/style.js.map +1 -1
- package/dist/components/radio/index.d.ts +4 -7
- package/dist/components/radio/index.js +51 -37
- package/dist/components/radio/index.js.map +1 -1
- package/dist/components/radio/style.js +1 -1
- package/dist/components/radio/style.js.map +1 -1
- package/dist/components/radio-group/index.d.ts +4 -10
- package/dist/components/radio-group/index.js +70 -52
- package/dist/components/radio-group/index.js.map +1 -1
- package/dist/components/segmented/index.d.ts +14 -12
- package/dist/components/segmented/index.js +132 -54
- package/dist/components/segmented/index.js.map +1 -1
- package/dist/components/segmented/style.js +1 -1
- package/dist/components/segmented/style.js.map +1 -1
- package/dist/components/segmented-trigger/index.d.ts +1 -2
- package/dist/components/segmented-trigger/index.js +23 -21
- 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.d.ts +13 -8
- package/dist/components/select/index.js +114 -75
- package/dist/components/select/index.js.map +1 -1
- package/dist/components/select/style.js +1 -1
- package/dist/components/select/style.js.map +1 -1
- package/dist/components/slider/index.d.ts +6 -7
- package/dist/components/slider/index.js +99 -64
- package/dist/components/slider/index.js.map +1 -1
- package/dist/components/slider/style.js +1 -1
- package/dist/components/slider/style.js.map +1 -1
- package/dist/components/spinner/index.d.ts +0 -2
- package/dist/components/spinner/index.js +14 -13
- package/dist/components/spinner/index.js.map +1 -1
- package/dist/components/spinner/style.js +1 -1
- package/dist/components/spinner/style.js.map +1 -1
- package/dist/components/svg-draw-lines/index.js +1 -1
- package/dist/components/switch/index.d.ts +13 -6
- package/dist/components/switch/index.js +102 -41
- 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 +5 -6
- package/dist/components/textarea/index.js +74 -67
- 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.js +1 -1
- package/dist/components/theme/style.js +1 -1
- package/dist/components/theme/style.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.js +12 -12
- package/dist/components/toast/toast.js.map +1 -1
- package/dist/components/tooltip/index.js +3 -3
- package/dist/components/tooltip/index.js.map +1 -1
- package/dist/components/tooltip/style.js +1 -1
- package/dist/components/tooltip/style.js.map +1 -1
- package/dist/icons/generated/heroicons-arrow-down-16-solid.d.ts +6 -0
- package/dist/icons/generated/heroicons-arrow-up-16-solid.d.ts +6 -0
- package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.d.ts +6 -0
- package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.js +10 -0
- package/dist/icons/generated/heroicons-bars-arrow-down-16-solid.js.map +1 -0
- package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.d.ts +6 -0
- package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.js +10 -0
- package/dist/icons/generated/heroicons-bars-arrow-up-16-solid.js.map +1 -0
- package/dist/icons/generated/lucide-clapperboard.d.ts +6 -0
- package/dist/icons/generated/lucide-clapperboard.js +10 -0
- package/dist/icons/generated/lucide-clapperboard.js.map +1 -0
- package/dist/icons/generated/lucide-code.d.ts +6 -0
- package/dist/icons/generated/lucide-code.js +10 -0
- package/dist/icons/generated/lucide-code.js.map +1 -0
- package/dist/icons/generated/lucide-ellipsis-vertical.d.ts +6 -0
- package/dist/icons/generated/lucide-ellipsis-vertical.js +10 -0
- package/dist/icons/generated/lucide-ellipsis-vertical.js.map +1 -0
- package/dist/icons/generated/lucide-eye.d.ts +6 -0
- package/dist/icons/generated/lucide-eye.js +10 -0
- package/dist/icons/generated/lucide-eye.js.map +1 -0
- package/dist/icons/generated/lucide-headphones.d.ts +6 -0
- package/dist/icons/generated/lucide-headphones.js +10 -0
- package/dist/icons/generated/lucide-headphones.js.map +1 -0
- package/dist/icons/generated/lucide-link.d.ts +6 -0
- package/dist/icons/generated/lucide-link.js +10 -0
- package/dist/icons/generated/lucide-link.js.map +1 -0
- package/dist/icons/generated/lucide-list-filter.d.ts +6 -0
- package/dist/icons/generated/lucide-list-filter.js +10 -0
- package/dist/icons/generated/lucide-list-filter.js.map +1 -0
- package/dist/icons/generated/lucide-list-restart.d.ts +6 -0
- package/dist/icons/generated/lucide-list-restart.js +10 -0
- package/dist/icons/generated/lucide-list-restart.js.map +1 -0
- package/dist/icons/generated/lucide-pen-line.d.ts +6 -0
- package/dist/icons/generated/lucide-pen-line.js +10 -0
- package/dist/icons/generated/lucide-pen-line.js.map +1 -0
- package/dist/icons/generated/lucide-play.d.ts +6 -0
- package/dist/icons/generated/lucide-play.js +10 -0
- package/dist/icons/generated/lucide-play.js.map +1 -0
- package/dist/icons/generated/lucide-sliders-horizontal.d.ts +6 -0
- package/dist/icons/generated/lucide-sliders-horizontal.js +10 -0
- package/dist/icons/generated/lucide-sliders-horizontal.js.map +1 -0
- package/dist/icons/generated/lucide-tag.d.ts +6 -0
- package/dist/icons/generated/lucide-tag.js +10 -0
- package/dist/icons/generated/lucide-tag.js.map +1 -0
- package/dist/icons/generated/tabler-sort-ascending-letters.d.ts +6 -0
- package/dist/icons/generated/tabler-sort-ascending-letters.js +10 -0
- package/dist/icons/generated/tabler-sort-ascending-letters.js.map +1 -0
- package/dist/icons/index.d.ts +15 -0
- package/dist/icons/index.js +18 -3
- package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js +1 -3
- package/dist/node_modules/.pnpm/@floating-ui_core@1.8.0/node_modules/@floating-ui/core/dist/floating-ui.core.js.map +1 -1
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js +2 -1
- package/dist/node_modules/.pnpm/@floating-ui_utils@0.2.12/node_modules/@floating-ui/utils/dist/floating-ui.utils.dom.js.map +1 -1
- package/dist/shared/events/user-change.js.map +1 -1
- package/dist/shared/form-association/index.js +41 -0
- package/dist/shared/form-association/index.js.map +1 -0
- package/dist/shared/gesture/drag-gesture.js +133 -0
- package/dist/shared/gesture/drag-gesture.js.map +1 -0
- package/dist/shared/gesture/physics.js +45 -0
- package/dist/shared/gesture/physics.js.map +1 -0
- package/dist/shared/group-management/index.js +111 -0
- package/dist/shared/group-management/index.js.map +1 -0
- package/dist/shared/menu-portal/menu-portal.js +22 -10
- package/dist/shared/menu-portal/menu-portal.js.map +1 -1
- package/dist/shared/menu-portal/menu-tree.js +22 -7
- package/dist/shared/menu-portal/menu-tree.js.map +1 -1
- package/dist/shared/option-portal/index.js +65 -0
- package/dist/shared/option-portal/index.js.map +1 -0
- package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
- package/dist/shared/overlay/nested-drawer-layers.js +55 -0
- package/dist/shared/overlay/nested-drawer-layers.js.map +1 -0
- package/dist/shared/overlay/overlay.js +1 -1
- package/dist/shared/overlay/overlay.js.map +1 -1
- package/dist/shared/overlay/portal.js +47 -13
- package/dist/shared/overlay/portal.js.map +1 -1
- package/dist/shared/overlay/presence.js.map +1 -1
- package/dist/shared/visible-area/index.js +69 -0
- package/dist/shared/visible-area/index.js.map +1 -0
- package/package.json +8 -4
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/drawer/index.ts"],"sourcesContent":["import { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { oouiClose } from '@/icons'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = ['right', 'left', 'top', 'bottom'] as const\n\nexport type DrawerPlacement = (typeof ALLOWED_PLACEMENTS)[number]\n\n@customElement('web-ui-drawer')\nexport class WebUiDrawer extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n @property({ type: String, reflect: true })\n get placement(): DrawerPlacement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'right')\n this.requestUpdate('placement', old)\n }\n private _placement: DrawerPlacement = 'right'\n\n // 标题文字(未传 header slot 时显示默认 header)\n @property({ type: String }) heading = ''\n\n @property({ type: Boolean, reflect: true }) closable = false\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n private _hasHeaderSlot = false\n private _hasFooterSlot = false\n private readonly _userOpenChange = new UserChangeController()\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n\n override connectedCallback() {\n super.connectedCallback()\n this._hasHeaderSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'header')\n this._hasFooterSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'footer')\n }\n\n override firstUpdated() {\n this._checkSlotContent('footer')\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._presence.dispose()\n this._scrollLock.release()\n }\n\n private _checkSlotContent(name: string) {\n const slot = this.shadowRoot?.querySelector(`slot[name=\"${name}\"]`) as HTMLSlotElement | null\n if (!slot) return\n const has = slot.assignedNodes().length > 0\n if (name === 'footer' && has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n private handleHeaderSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasHeaderSlot) {\n this._hasHeaderSlot = has\n this.requestUpdate()\n }\n }\n\n private handleFooterSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n if (this._userOpenChange.consume()) this.emitOpenChange()\n this._presence.sync(this.open)\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n }\n\n private handleTransitionEnd(e: TransitionEvent) {\n this._presence.handleTransitionEnd(e)\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n e.preventDefault()\n this._closeFromUser()\n }\n\n // 打开抽屉(命令式)\n show() {\n if (this.open) return\n this.open = true\n }\n\n // 关闭抽屉(带动画)\n close() {\n if (!this.open) return\n this.open = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this.open) return\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleCancel(e: Event) {\n // 保留 top layer 直到 CSS 过渡结束,避免原生关闭跳过退出动画。\n e.preventDefault()\n this._closeFromUser()\n }\n\n private handleNativeClose() {\n if (!this.open) return\n\n // 原生关闭可绕过 cancel;同步受控状态,避免 show() 误判为已打开。\n this._presence.handleNativeClose()\n this._userOpenChange.mark()\n this.open = false\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n this._closeFromUser()\n }\n\n private emitOpenChange() {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n override render() {\n const showHeader = this._hasHeaderSlot || !!this.heading\n\n return html`\n <dialog\n @cancel=${this.handleCancel}\n @close=${this.handleNativeClose}\n @click=${this.handleBackdropClick}\n @keydown=${this.handleKeydown}\n @transitionend=${this.handleTransitionEnd}\n >\n <div class=\"wui-drawer-body wui-glass\">\n ${showHeader\n ? html`\n <div class=\"wui-drawer-header\">\n <slot name=\"header\" @slotchange=${this.handleHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-drawer-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n `\n : nothing}\n <div class=\"wui-drawer-content\">\n <slot></slot>\n </div>\n <div class=\"wui-drawer-footer\" ?hidden=${!this._hasFooterSlot}>\n <slot name=\"footer\" @slotchange=${this.handleFooterSlotChange}></slot>\n </div>\n </div>\n ${this.closable\n ? html`\n <web-ui-button\n class=\"wui-drawer-close\"\n @click=${this._closeFromUser}\n aria-label=\"关闭\"\n variant=\"secondary\"\n icon\n size=\"26\"\n >\n <web-ui-icon .icon=${oouiClose} size=\"16\"></web-ui-icon>\n </web-ui-button>\n `\n : nothing}\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-drawer': WebUiDrawer\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/drawer/index.ts"],"sourcesContent":["import { html, LitElement, nothing, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport '@/components/button'\nimport glass from '@/assets/glass.css?inline'\nimport { oouiClose } from '@/icons'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { attachDragGesture, type DragGestureHandle, rubberband, SPRING_PRESETS, springOffsets } from '@/shared/gesture'\nimport { normalizeLiteral } from '@/shared/normalize'\nimport { defineNativeDialogPresence } from '@/shared/overlay/native-dialog-presence'\nimport { defineNestedDrawerLayers } from '@/shared/overlay/nested-drawer-layers'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\nimport { findNearestTheme } from '@/shared/theme/theme-scope'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_PLACEMENTS = ['right', 'left', 'top', 'bottom'] as const\n\nexport type DrawerPlacement = (typeof ALLOWED_PLACEMENTS)[number]\n\n// 拖拽关闭判定:位移超过抽屉尺寸 1/3,或闭合方向甩动速度超过 500px/s 视为关闭意图。\nconst DRAG_CLOSE_RATIO = 1 / 3\nconst DRAG_FLICK_VELOCITY = 500\nconst DRAG_REQUEST_WINDOW_MS = 120\nconst SPRING_SAMPLE_MS = 16\n\n/*\n * 拖拽期间由 JS 写入的遮罩透明度变量参与 WAAPI 关键帧。未注册的自定义属性在\n * 关键帧之间是离散插值(半帧跳变),注册为 <number> 后获得线性插值。\n * inherits 必须为 true:::backdrop 只从 originating element 继承「可继承属性」,\n * 注册为不可继承会让变量到不了遮罩,跟手淡出整体失效(Chrome 151 实证)。\n * 注册表是全局的,模块重复执行(HMR)时捕获已注册错误并忽略。\n */\nif (typeof CSS !== 'undefined' && 'registerProperty' in CSS) {\n try {\n CSS.registerProperty({\n name: '--wui-internal-drag-backdrop-opacity',\n syntax: '<number>',\n inherits: true,\n initialValue: '1'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n /*\n * 公开 token 注册为 <length>:Consumer 传 unitless 0 时计算值被归一为 0px,\n * 避免 calc(100% + 0) 因 number/percentage 不兼容而使闭合位移失效。\n */\n try {\n CSS.registerProperty({\n name: '--wui-drawer-inset',\n syntax: '<length>',\n inherits: true,\n initialValue: '8px'\n })\n } catch {\n // 已注册(如 HMR 重复执行)时忽略\n }\n}\n\n@customElement('web-ui-drawer')\nexport class WebUiDrawer extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n @property({ type: Boolean, reflect: true, attribute: 'no-backdrop-close' }) noBackdropClose = false\n\n /**\n * Controlled 模式下,Escape、遮罩、关闭按钮和拖拽关闭只派发 `open-change` 请求,\n * 不会自行修改 `open`。Consumer 回写 `open` 后才执行关闭动画。\n * 程序化 API(show/close/直接赋值 open)不受此模式影响,始终直通。\n */\n @property({ type: Boolean, reflect: true }) controlled = false\n\n @property({ type: String, reflect: true })\n get placement(): DrawerPlacement {\n return this._placement\n }\n set placement(v: string) {\n const old = this._placement\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'right')\n this.requestUpdate('placement', old)\n }\n private _placement: DrawerPlacement = 'right'\n\n // 标题文字(未传 header slot 时显示默认 header)\n @property({ type: String }) heading = ''\n\n @property({ type: Boolean, reflect: true }) closable = false\n\n /**\n * Headless 模式:只保留 overlay 基础设施(backdrop、动画、scroll lock、dialog 语义),\n * 移除内置 UI(glass 样式、header、close 按钮、footer)。Consumer 自定义内容样式。\n */\n @property({ type: Boolean, reflect: true }) headless = false\n\n /**\n * 内部原生 dialog 的 accessible name。headless 模式必须由 Consumer 提供,\n * 因为该模式不会渲染可自动关联的内置 header。\n */\n @property({ type: String, attribute: 'dialog-label' }) dialogLabel = ''\n\n /**\n * 启用拖拽关闭:打开态在抽屉内缘渲染 drag bar(灰色胶囊 + 加宽热区),\n * 指针拖拽实时跟手,松手按位移与甩动速度判定关闭或弹回。\n * 关闭态基于原生 dialog 无可见渲染物,因此不支持从关闭态拖拽打开。\n */\n @property({ type: Boolean, reflect: true }) override draggable = false\n\n private get dialog() {\n return this.shadowRoot?.querySelector('dialog') ?? null\n }\n\n private _hasHeaderSlot = false\n private _hasFooterSlot = false\n private readonly _userOpenChange = new UserChangeController()\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _presence = defineNativeDialogPresence().make({\n getDialog: () => this.dialog,\n isConnected: () => this.isConnected,\n isOpen: () => this.open\n })\n // nested 层序:打开后纳入全局栈,上层打开/关闭时本层缩放平移(对齐 Base UI)。\n private readonly _nestedLayers = defineNestedDrawerLayers().make({\n getDialog: () => this.dialog,\n getPlacement: () => this._placement\n })\n\n // ===== 拖拽关闭手势状态 =====\n private _dragGesture: DragGestureHandle | null = null\n // pointerdown 时刻已存在的闭合方向位移(从弹回动画中抓取时非 0)。\n private _dragInitialOffset = 0\n private _dragOffset = 0\n private _dragAnimation: Animation | null = null\n // controlled:弹簧到闭合位后等待 Consumer 回写 open;超时未回写则弹回。\n private _dragAwaitWriteback = false\n private _dragRequestTimer: ReturnType<typeof setTimeout> | undefined\n\n // placement 的闭合轴向:right/left 沿 X 轴,top/bottom 沿 Y 轴。\n private get _dragAxis(): 'x' | 'y' {\n return this._placement === 'left' || this._placement === 'right' ? 'x' : 'y'\n }\n\n // 位移正方向 = 抽屉的闭合方向(拖出屏幕为正)。\n private get _dragCloseSign(): number {\n switch (this._placement) {\n case 'right':\n return 1\n case 'left':\n return -1\n case 'top':\n return -1\n case 'bottom':\n return 1\n }\n }\n\n private _measureDragSize(): number {\n const dialog = this.dialog\n if (!dialog) return 0\n return this._dragAxis === 'x' ? dialog.offsetWidth : dialog.offsetHeight\n }\n\n // 浮动卡片(非 headless)的四周留边;闭合位移需越过它才能完全滑出视口。\n // headless 在 :host([headless]) dialog 上显式归零(防嵌套继承),解析失败回退 0。\n private _readDrawerInset(dialog: HTMLDialogElement): number {\n const raw = getComputedStyle(dialog).getPropertyValue('--wui-internal-drawer-inset')\n const parsed = Number.parseFloat(raw)\n return Number.isFinite(parsed) ? parsed : 0\n }\n\n // 闭合方向上的完全出屏距离:抽屉尺寸 + 浮动留边(headless 下即尺寸本身)。\n // CSS 闭合态 transform、controlled 悬停终态与弹簧终点共用同一数学,避免衔接跳变。\n private _dragCloseDistance(dialog: HTMLDialogElement): number {\n return this._measureDragSize() + this._readDrawerInset(dialog)\n }\n\n private _isReducedMotion(): boolean {\n // 优先尊重所在 web-ui-theme 的 motion 设置(与 svg-draw-lines 一致);\n // 无主题范围时回退到系统 prefers-reduced-motion。jsdom 等环境无 matchMedia,视为完整动效。\n const theme = findNearestTheme(this)\n if (theme) return theme.isReducedMotion()\n try {\n return typeof window.matchMedia === 'function' && window.matchMedia('(prefers-reduced-motion: reduce)').matches\n } catch {\n return false\n }\n }\n\n private _isDragging(): boolean {\n return this._dragGesture?.isDragging() ?? false\n }\n\n private _cancelDragAwait() {\n if (this._dragRequestTimer !== undefined) {\n clearTimeout(this._dragRequestTimer)\n this._dragRequestTimer = undefined\n }\n this._dragAwaitWriteback = false\n }\n\n private _handleDragPointerDown(e: PointerEvent) {\n if (!this.open || !this.draggable || this._isDragging()) return\n // 多点触控的副指针(isPrimary 明确为 false)不参与手势。\n if (e.isPrimary === false) return\n\n const dialog = this.dialog\n if (!dialog) return\n // is-visible 由 presence 在 showModal 后一帧加上;就位前(打开极早期)忽略。\n // 就位后即使 enter 过渡仍在进行也允许抓取,起始位移从当前计算值续接。\n if (!dialog.classList.contains('is-visible')) return\n\n // 先读取动画中的当前位移再取消弹回动画,避免取消后回跳到内联样式值。\n const currentTransform = getComputedStyle(dialog).transform\n const axisValue =\n currentTransform && currentTransform !== 'none' && typeof DOMMatrixReadOnly === 'function'\n ? new DOMMatrixReadOnly(currentTransform)[this._dragAxis === 'x' ? 'm41' : 'm42']\n : 0\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\n\n this._dragInitialOffset = axisValue * this._dragCloseSign\n this._dragOffset = this._dragInitialOffset\n dialog.classList.add('is-dragging')\n\n this._dragGesture = attachDragGesture(e, {\n axis: this._dragAxis,\n onMove: info => {\n const pointerDelta = this._dragAxis === 'x' ? info.deltaX : info.deltaY\n // 闭合方向全额跟随;开启方向施加阻尼(橡皮筋),最多回弹 10% 抽屉尺寸。\n const raw = this._dragInitialOffset + pointerDelta * this._dragCloseSign\n this._dragOffset = rubberband(raw, this._measureDragSize() * 0.1, 0.15)\n\n // 达到关闭阈值时胶囊变 accent 色作视觉确认(ADR-0035)。\n const dragSize = this._measureDragSize()\n dialog.classList.toggle('is-drag-close', dragSize > 0 && this._dragOffset > dragSize * DRAG_CLOSE_RATIO)\n this._applyDragOffset(dialog, this._dragOffset)\n },\n onEnd: info => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n const size = this._measureDragSize()\n const velocity = (this._dragAxis === 'x' ? info.velocityX : info.velocityY) * this._dragCloseSign\n const shouldClose =\n this._dragOffset > size * DRAG_CLOSE_RATIO || (this._dragOffset > 8 && velocity > DRAG_FLICK_VELOCITY)\n\n if (shouldClose) this._springToClose(dialog, velocity)\n else this._springRebound(dialog, velocity, this._dragOffset)\n },\n onCancel: () => {\n this._dragGesture = null\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._springRebound(dialog, 0, this._dragOffset)\n }\n })\n }\n\n // 受控状态写入等外部原因强制终结拖拽:清手势状态与拖拽样式,不弹回,\n // 后续管线(关闭/打开)由调用方继续执行。\n private _cancelActiveDrag() {\n if (!this._isDragging()) return\n this._dragGesture?.destroy()\n this._dragGesture = null\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.remove('is-dragging', 'is-drag-close')\n this._clearDragStyles(dialog)\n }\n\n // 将闭合方向位移映射为 dialog transform,并同步遮罩透明度保持跟手反馈。\n private _applyDragOffset(dialog: HTMLDialogElement, offset: number) {\n const sign = this._dragCloseSign\n const value = offset * sign\n dialog.style.transform = this._dragAxis === 'x' ? `translateX(${value}px)` : `translateY(${value}px)`\n const size = this._measureDragSize()\n const progress = size > 0 ? Math.min(1, Math.max(0, offset / size)) : 0\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', String(1 - progress))\n }\n\n private _clearDragStyles(dialog: HTMLDialogElement) {\n dialog.style.removeProperty('transform')\n dialog.style.removeProperty('--wui-internal-drag-backdrop-opacity')\n }\n\n // 弹簧到完全闭合;controlled 下保持闭合位等待回写,其余走常规关闭管线。\n private _springToClose(dialog: HTMLDialogElement, velocity: number) {\n const from = this._dragOffset\n this._dragOffset = 0\n const sign = this._dragCloseSign\n // 弹簧终点 = 完全出屏距离(含浮动留边),与 CSS 闭合态/悬停终态一致,\n // 否则 onfinish 后会有一个留边宽度的瞬移。\n const to = this._dragCloseDistance(dialog)\n const size = this._measureDragSize()\n\n const finishClose = () => {\n if (this.controlled) {\n // 保持在闭合位(is-visible 未移除,状态仍 open),等待 Consumer 回写或超时弹回。\n const distance = to\n dialog.style.transform =\n this._dragAxis === 'x' ? `translateX(${distance * sign}px)` : `translateY(${distance * sign}px)`\n dialog.style.setProperty('--wui-internal-drag-backdrop-opacity', '0')\n } else {\n // 移除 is-visible 后基础 transform 即闭合位,清内联样式不产生跳变;\n // presence.sync(false) 检测不到 is-visible 会立即完成关闭,不重播退出动画。\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n }\n this._closeFromDrag()\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function') {\n finishClose()\n return\n }\n\n const samples = springOffsets(from, to, velocity, SPRING_PRESETS.close)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 采样帧含首值与附加终点,实际时长为帧间隔数 × 采样周期。\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 与 _springRebound 对称:堵住 finish→onfinish 清理窗口,\n // 防止该窗口内绘制的帧暴露内联位移、随清理触发跳变。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // onfinish 同步应用终态并取消动画:fill 会持续覆盖 transform,阻断后续 CSS 过渡。\n animation.onfinish = () => {\n animation.cancel()\n finishClose()\n }\n }\n\n // 弹回打开:从 from 位移弹回 0;结束后清内联样式交还 CSS transition 管辖。\n private _springRebound(dialog: HTMLDialogElement, velocity: number, from: number) {\n this._dragOffset = 0\n\n const finishRebound = () => {\n this._dragAnimation = null\n dialog.classList.remove('is-dragging')\n this._clearDragStyles(dialog)\n }\n\n if (this._isReducedMotion() || typeof dialog.animate !== 'function' || Math.abs(from) < 1) {\n finishRebound()\n return\n }\n\n const sign = this._dragCloseSign\n const size = this._measureDragSize()\n const samples = springOffsets(from, 0, velocity, SPRING_PRESETS.rebound)\n const keyframes = samples.map(o => ({\n transform: this._dragAxis === 'x' ? `translateX(${o * sign}px)` : `translateY(${o * sign}px)`,\n '--wui-internal-drag-backdrop-opacity': String(Math.max(0, 1 - o / size))\n }))\n // 弹回期间抑制 transform/backdrop 的 CSS transition,避免与弹簧动画叠加。\n dialog.classList.add('is-dragging')\n const duration = (samples.length - 1) * SPRING_SAMPLE_MS\n // fill:'both' 堵住 finish→onfinish 清理之间的竞态窗口:无 fill 时动画结束即\n // 失效,窗口内被绘制的帧会暴露内联拖拽位移,随后的清理再触发 CSS 过渡滑回\n // 打开位 —— 表现为回弹后又多弹一次。\n const animation = dialog.animate(keyframes, { duration, easing: 'linear', fill: 'both' })\n // onfinish 同步清除内联样式并取消动画:fill 会持续覆盖 transform,阻断后续 CSS 过渡。\n animation.onfinish = () => {\n animation.cancel()\n finishRebound()\n }\n this._dragAnimation = animation\n }\n\n private _closeFromDrag() {\n // 先派发关闭请求(_closeFromUser),再进入悬停等待:等待态中 _closeFromUser\n // 会被 L2 去重守卫短路,顺序颠倒会吞掉首次请求。\n this._closeFromUser()\n if (this.controlled) {\n this._dragAwaitWriteback = true\n this._dragRequestTimer = setTimeout(this._handleDragWritebackTimeout, DRAG_REQUEST_WINDOW_MS)\n }\n }\n\n // controlled 回写窗口超时:Consumer 拒绝关闭,从闭合位弹回打开位。\n private readonly _handleDragWritebackTimeout = () => {\n this._dragRequestTimer = undefined\n if (!this._dragAwaitWriteback || !this.open || this._isDragging()) {\n this._dragAwaitWriteback = false\n return\n }\n this._dragAwaitWriteback = false\n\n const dialog = this.dialog\n if (!dialog) return\n dialog.classList.add('is-visible')\n // 悬停终态位于完全出屏位(含留边),弹回也从该真实位置起步,避免首帧内跳。\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._hasHeaderSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'header')\n this._hasFooterSlot = Array.from(this.children).some(child => child.getAttribute?.('slot') === 'footer')\n }\n\n override firstUpdated() {\n this._checkSlotContent('footer')\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._nestedLayers.dispose()\n this._dragGesture?.destroy()\n this._dragGesture = null\n this._presence.dispose()\n this._scrollLock.release()\n this._dragAnimation?.cancel()\n this._dragAnimation = null\n this._cancelDragAwait()\n }\n\n private _checkSlotContent(name: string) {\n const slot = this.shadowRoot?.querySelector(`slot[name=\"${name}\"]`) as HTMLSlotElement | null\n if (!slot) return\n const has = slot.assignedNodes().length > 0\n if (name === 'footer' && has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n private handleHeaderSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasHeaderSlot) {\n this._hasHeaderSlot = has\n this.requestUpdate()\n }\n }\n\n private handleFooterSlotChange(e: Event) {\n const has = (e.target as HTMLSlotElement).assignedNodes().length > 0\n if (has !== this._hasFooterSlot) {\n this._hasFooterSlot = has\n this.requestUpdate()\n }\n }\n\n protected override updated(props: PropertyValues) {\n super.updated(props)\n if (!this.isConnected) return\n\n if (props.has('open')) {\n // Consumer 在拖拽进行中写入受控 open:受控状态优先,立即终结手势(等同\n // pointercancel 的清理但不弹回),交由下方 presence 走标准关闭/打开管线。\n if (this._isDragging() && !this.open) this._cancelActiveDrag()\n if (this._userOpenChange.consume()) this.emitOpenChange()\n // controlled 拖拽关闭的回写结果:确认关闭则清除闭合位悬停状态走正常关闭;\n // 拒绝关闭(回写 open=true)则从闭合位(含留边)弹回。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n const dialog = this.dialog\n if (dialog && !this.open) {\n dialog.classList.remove('is-visible')\n this._clearDragStyles(dialog)\n } else if (dialog && this.open) {\n this._springRebound(dialog, 0, this._dragCloseDistance(dialog))\n }\n }\n this._presence.sync(this.open)\n if (this.open) {\n // presence.sync 已同步发起 showModal:此刻 dialog.open 为真,计数出正确\n // 的层序 depth 并驱动下层缩放。直接同步 register,不等待 is-visible\n //(那要再等一帧,且打开过渡期间上层关系已应确立)。\n this._nestedLayers.register()\n } else {\n this._nestedLayers.unregister()\n }\n }\n if (props.has('open') || props.has('noScrollLock')) this._syncScrollLock()\n }\n\n private handleTransitionEnd(e: TransitionEvent) {\n this._presence.handleTransitionEnd(e)\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (e.key !== 'Escape') return\n /*\n * nested 场景防连锁:子 drawer 的 dialog 经 slot 投影在本层 shadow 内,其\n * keydown composed 冒泡路径会再次经过本层 dialog(事件路径含 slot 宿主链)。\n * 若事件传播路径在本层 dialog 之前已存在其他 <dialog>,说明该事件来自子层,本层忽略。\n */\n const path = e.composedPath()\n const ownDialogIndex = path.indexOf(e.currentTarget as Node)\n if (ownDialogIndex > 0) {\n const hasChildDialog = path.slice(0, ownDialogIndex).some(n => n instanceof HTMLDialogElement)\n if (hasChildDialog) return\n }\n // 拖拽进行中忽略 ESC,避免手势与关闭管线竞争。\n if (this._isDragging()) {\n e.preventDefault()\n return\n }\n e.preventDefault()\n this._closeFromUser()\n }\n\n show() {\n if (this.open) return\n this.open = true\n }\n\n close() {\n if (!this.open) return\n this.open = false\n }\n\n private readonly _closeFromUser = () => {\n if (!this.open) return\n // controlled 悬停等待回写期间,关闭意图已在途(open-change 已派发):\n // Escape/遮罩/关闭按钮的重复触发不再派发第二次请求,也不与超时弹回竞争。\n if (this._dragAwaitWriteback) return\n if (this.controlled) {\n this.emitOpenChange(false)\n return\n }\n\n this._userOpenChange.mark()\n this.close()\n }\n\n private handleCancel(e: Event) {\n // 保留 top layer 直到 CSS 过渡结束,避免原生关闭跳过退出动画。\n e.preventDefault()\n this._closeFromUser()\n }\n\n private handleNativeClose() {\n if (!this.open) return\n\n // 悬停等待期间的原生关闭(如表单 method=\"dialog\"):视为回写窗口内的重复\n // 关闭意图,取消等待不补发请求;已原生关闭的 dialog 不再被超时弹回拉起。\n if (this._dragAwaitWriteback) {\n this._cancelDragAwait()\n return\n }\n\n // 原生关闭可绕过 cancel;controlled 时恢复受控状态,由 Consumer 决定是否关闭。\n if (this.controlled) {\n this._presence.sync(true)\n this.emitOpenChange(false)\n return\n }\n\n this._presence.handleNativeClose()\n this._userOpenChange.mark()\n this.open = false\n }\n\n private handleBackdropClick(e: MouseEvent) {\n if (e.target !== (e.currentTarget as HTMLDialogElement)) return\n if (this.noBackdropClose) return\n // 拖拽进行中 pointer capture 使 click 落在 dialog 上,忽略以避免与手势竞争。\n if (this._isDragging()) return\n this._closeFromUser()\n }\n\n private emitOpenChange(open = this.open) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n override render() {\n const showHeader = this._hasHeaderSlot || !!this.heading\n const dialogLabel = this.dialogLabel.trim()\n const dialogLabelledBy = !dialogLabel && !this.headless && showHeader ? 'wui-drawer-heading' : nothing\n\n // 拖拽热区:仅在打开且 draggable 时渲染;胶囊 + 加宽命中条贴在抽屉内缘。\n const dragBar = this.draggable\n ? html`\n <div class=\"wui-drawer-drag-zone\" @pointerdown=${this._handleDragPointerDown}>\n <div class=\"wui-drawer-drag-bar\"></div>\n </div>\n `\n : nothing\n\n // 保持同一个 dialog 实例,避免打开期间切换 headless 时脱离 top layer。\n return html`\n <dialog\n aria-label=${dialogLabel || nothing}\n aria-labelledby=${dialogLabelledBy}\n @cancel=${this.handleCancel}\n @close=${this.handleNativeClose}\n @click=${this.handleBackdropClick}\n @keydown=${this.handleKeydown}\n @transitionend=${this.handleTransitionEnd}\n >\n ${\n this.headless\n ? html`<slot></slot>${dragBar}`\n : html`\n <div class=\"wui-drawer-body wui-glass\">\n <div class=\"wui-drawer-header\" id=\"wui-drawer-heading\" ?hidden=${!showHeader}>\n <slot name=\"header\" @slotchange=${this.handleHeaderSlotChange}>\n ${this.heading ? html`<span class=\"wui-drawer-heading\">${this.heading}</span>` : nothing}\n </slot>\n </div>\n <div class=\"wui-drawer-content\">\n <slot></slot>\n </div>\n <div class=\"wui-drawer-footer\" ?hidden=${!this._hasFooterSlot}>\n <slot name=\"footer\" @slotchange=${this.handleFooterSlotChange}></slot>\n </div>\n </div>\n ${\n this.closable\n ? html`\n <web-ui-button\n class=\"wui-drawer-close\"\n @click=${this._closeFromUser}\n aria-label=\"关闭\"\n variant=\"secondary\"\n icon\n size=\"26\"\n >\n <web-ui-icon size=\"14\" .icon=${oouiClose}></web-ui-icon>\n </web-ui-button>\n `\n : nothing\n }\n ${dragBar}\n `\n }\n </dialog>\n `\n }\n\n declare readonly $events: {\n 'open-change': CustomEvent<{ open: boolean }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-drawer': WebUiDrawer\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/drawer/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{box-sizing:border-box;width:var(--wui-drawer-width,320px);max-width:90vw;height:100dvh;max-height:100dvh;color:var(--wui-color-text,#1b1b1b);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;margin:0;padding:0;font-family:inherit;inset:0 0 0 auto;overflow:visible;transform:translate(100%)}dialog::backdrop{opacity:0;background:var(--wui-drawer-overlay-bg,#0000001f);transition:opacity var(--wui-duration-drawer-enter,.28s) ease}dialog[open].is-visible,:host([placement=left]) dialog[open].is-visible,:host([placement=top]) dialog[open].is-visible,:host([placement=bottom]) dialog[open].is-visible{transform:translate(0)}dialog.is-closing{pointer-events:none;transition:transform var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog.is-closing::backdrop{transition:opacity var(--wui-duration-drawer-exit,.24s) ease}dialog[open].is-visible::backdrop{opacity:1}:host([placement=left]) dialog{left:0;right:auto;transform:translate(-100%)}:host([placement=top]) dialog,:host([placement=bottom]) dialog{width:100vw;max-width:100vw;height:var(--wui-drawer-height,300px);max-height:90dvh}:host([placement=top]) dialog{inset:0 0 auto;transform:translateY(-100%)}:host([placement=bottom]) dialog{inset:auto 0 0;transform:translateY(100%)}.wui-drawer-body{--wui-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);background-color:var(--wui-color-surface-overlay,#
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{display:contents}dialog{box-sizing:border-box;width:var(--wui-drawer-width,320px);max-width:90vw;height:100dvh;max-height:100dvh;color:var(--wui-color-text,#1b1b1b);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));background:0 0;border:none;margin:0;padding:0;font-family:inherit;inset:0 0 0 auto;overflow:visible;transform:translate(100%)}dialog::backdrop{opacity:0;background:var(--wui-drawer-overlay-bg,var(--wui-color-backdrop,#0000001f));transition:opacity var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog[open].is-visible{transform:translate(0)}:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging,:is(:host(:not([headless])) dialog.is-dragging[open].is-visible,dialog.is-dragging)::backdrop{transition:none}:host([placement=left]) dialog[open].is-visible,:host([placement=top]) dialog[open].is-visible,:host([placement=bottom]) dialog[open].is-visible{transform:translate(0)}dialog.is-closing{pointer-events:none;transition:transform var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog.is-closing::backdrop{transition:opacity var(--wui-duration-drawer-exit,.24s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1))}dialog[open].is-visible::backdrop{opacity:var(--wui-internal-drag-backdrop-opacity,1)}.wui-drawer-drag-zone{touch-action:none;cursor:grab;z-index:4;transition:opacity var(--wui-duration-feedback,.1s) ease;justify-content:center;align-items:center;display:flex;position:absolute}dialog.is-nested-lower .wui-drawer-drag-zone{pointer-events:none;opacity:0}:host([placement=right]) .wui-drawer-drag-zone{width:24px;height:100%;top:0;left:0}:host([placement=left]) .wui-drawer-drag-zone{width:24px;height:100%;top:0;right:0}:host([placement=top]) .wui-drawer-drag-zone,:host([placement=bottom]) .wui-drawer-drag-zone{width:100%;height:24px;left:0;right:0}:host([placement=top]) .wui-drawer-drag-zone{bottom:0}:host([placement=bottom]) .wui-drawer-drag-zone{top:0}.wui-drawer-drag-bar{background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 24%, transparent);border-radius:999px;width:4px;height:48px}:host([placement=top]) .wui-drawer-drag-bar,:host([placement=bottom]) .wui-drawer-drag-bar{width:48px;height:4px}.wui-drawer-drag-zone:hover .wui-drawer-drag-bar,.wui-drawer-drag-zone:active .wui-drawer-drag-bar{background:color-mix(in srgb, var(--wui-color-text,#1b1b1b) 40%, transparent)}dialog.is-drag-close .wui-drawer-drag-zone .wui-drawer-drag-bar{background:var(--wui-color-accent,#08f)}:host([placement=left]) dialog{left:0;right:auto;transform:translate(-100%)}:host([placement=top]) dialog,:host([placement=bottom]) dialog{width:100vw;max-width:100vw;height:var(--wui-drawer-height,300px);max-height:90dvh}:host([placement=top]) dialog{inset:0 0 auto;transform:translateY(-100%)}:host([placement=bottom]) dialog{inset:auto 0 0;transform:translateY(100%)}dialog.is-dragging .wui-drawer-drag-zone{cursor:grabbing}.wui-drawer-body{--wui-internal-glass-shadow:var(--wui-shadow-overlay,2px 16px 40px #0006);--wui-glass-corner-radius:var(--wui-drawer-radius,28px);background-color:var(--wui-drawer-bg,var(--wui-color-surface-overlay,#f6f6f6d1));flex-direction:column;height:100%;display:flex}.wui-drawer-header{border-bottom:1px solid #0000000f;justify-content:space-between;align-items:center;padding:16px 20px;font-size:18px;font-weight:600;line-height:1.4;display:flex}.wui-drawer-header[hidden]{display:none}.wui-drawer-content{overscroll-behavior:contain;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);flex:1;padding:20px;line-height:1.5;overflow-y:auto}.wui-drawer-footer{border-top:1px solid #0000000f;flex-shrink:0;padding:16px 20px}.wui-drawer-footer[hidden]{display:none}.wui-drawer-close{z-index:3;position:absolute;top:16px;right:16px}:host([headless]) dialog{--wui-internal-drawer-inset:0px;width:auto;max-width:none}:host(:not([headless])) dialog{--wui-internal-drawer-inset:var(--wui-drawer-inset,8px);--wui-internal-drawer-nested-depth:0;--wui-internal-drawer-nested-scale:1;--wui-internal-drawer-nested-shift:0px;top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);transform:translateX(calc(100% + var(--wui-internal-drawer-inset)));border-radius:var(--wui-drawer-radius,28px);height:auto;left:auto}:host(:not([headless])) .wui-drawer-body{border-radius:inherit}:host(:not([headless])[placement=left]) dialog{top:var(--wui-internal-drawer-inset);right:auto;bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateX(calc(-100% - var(--wui-internal-drawer-inset)));height:auto}:host(:not([headless])[placement=top]) dialog{top:var(--wui-internal-drawer-inset);right:var(--wui-internal-drawer-inset);bottom:auto;left:var(--wui-internal-drawer-inset);transform:translateY(calc(-100% - var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])[placement=bottom]) dialog{top:auto;right:var(--wui-internal-drawer-inset);bottom:var(--wui-internal-drawer-inset);left:var(--wui-internal-drawer-inset);transform:translateY(calc(100% + var(--wui-internal-drawer-inset)));width:auto;max-width:none}:host(:not([headless])) dialog[open].is-visible{transform:translate(0);translate:calc(var(--wui-internal-drawer-nested-shift) * -1) 0;scale:var(--wui-internal-drawer-nested-scale);transition:transform var(--wui-duration-drawer-enter,.28s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1)), translate var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1), scale var(--wui-duration-drawer-nested,.45s) cubic-bezier(.22, 1, .36, 1)}:host(:not([headless])[placement=left]) dialog[open].is-visible{transform:translate(0);translate:var(--wui-internal-drawer-nested-shift) 0}:host(:not([headless])[placement=top]) dialog[open].is-visible{transform:translate(0);translate:0 var(--wui-internal-drawer-nested-shift)}:host(:not([headless])[placement=bottom]) dialog[open].is-visible{transform:translate(0);translate:0 calc(var(--wui-internal-drawer-nested-shift) * -1)}@media (prefers-reduced-motion:reduce){dialog:not(.is-dragging){transition:opacity var(--wui-duration-feedback,.1s) ease!important}dialog:not(.is-dragging)::backdrop{transition-duration:var(--wui-duration-feedback,.1s)!important}dialog[open].is-visible:not(.is-dragging){transform:translate(0)scale(1)!important}}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/drawer/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/* ===== Dialog 容器 ===== */\ndialog {\n /* 默认:右侧滑出 — 需显式 left:auto 覆盖 UA dialog:modal 的 left:0 */\n top: 0;\n right: 0;\n bottom: 0;\n left: auto;\n transform: translateX(100%);\n\n /* 阴影属于抽屉主体,不能由同尺寸的原生 dialog 裁剪。内容滚动由 .wui-drawer-content 管理。 */\n overflow: visible;\n\n box-sizing: border-box;\n width: var(--wui-drawer-width, 320px);\n max-width: 90vw;\n height: 100vh;\n height: 100dvh;\n max-height: 100vh;\n max-height: 100dvh;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n\n transition: transform var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-drawer-overlay-bg, rgb(0 0 0 / 0.12));\n transition: opacity var(--wui-duration-drawer-enter, 280ms) ease;\n }\n}\n\ndialog[open].is-visible {\n transform: translate(0, 0);\n}\n\n:host([placement='left']) dialog[open].is-visible,\n:host([placement='top']) dialog[open].is-visible,\n:host([placement='bottom']) dialog[open].is-visible {\n transform: translate(0, 0);\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition: transform var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog.is-closing::backdrop {\n transition: opacity var(--wui-duration-drawer-exit, 240ms) ease;\n}\n\ndialog[open].is-visible::backdrop {\n opacity: 1;\n}\n\n/* ===== Placement 变体 ===== */\n\n:host([placement='left']) dialog {\n right: auto;\n left: 0;\n transform: translateX(-100%);\n}\n\n:host([placement='top']) dialog,\n:host([placement='bottom']) dialog {\n width: 100vw;\n max-width: 100vw;\n height: var(--wui-drawer-height, 300px);\n max-height: 90dvh;\n}\n\n:host([placement='top']) dialog {\n top: 0;\n right: 0;\n bottom: auto;\n left: 0;\n transform: translateY(-100%);\n}\n\n:host([placement='bottom']) dialog {\n top: auto;\n right: 0;\n bottom: 0;\n left: 0;\n transform: translateY(100%);\n}\n\n/* ===== 抽屉主体 ===== */\n\n.wui-drawer-body {\n --wui-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.88));\n}\n\n/* ===== Header ===== */\n\n.wui-drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 16px 20px;\n border-bottom: 1px solid rgb(0 0 0 / 0.06);\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n/* ===== 内容区 ===== */\n\n.wui-drawer-content {\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n\n padding: 20px;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== Footer ===== */\n\n.wui-drawer-footer {\n flex-shrink: 0;\n padding: 16px 20px;\n border-top: 1px solid rgb(0 0 0 / 0.06);\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog {\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n }\n\n dialog::backdrop {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n dialog[open].is-visible {\n transform: none;\n }\n}\n\n.wui-drawer-footer[hidden] {\n display: none;\n}\n\n/* ===== 关闭按钮(独立定位,与 header 解耦) ===== */\n\n.wui-drawer-close {\n position: absolute;\n z-index: 3;\n top: 16px;\n right: 16px;\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/drawer/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/* ===== Dialog 容器 ===== */\ndialog {\n /* 默认:右侧滑出 — 需显式 left:auto 覆盖 UA dialog:modal 的 left:0 */\n top: 0;\n right: 0;\n bottom: 0;\n left: auto;\n transform: translateX(100%);\n\n /* 阴影属于抽屉主体,不能由同尺寸的原生 dialog 裁剪。内容滚动由 .wui-drawer-content 管理。 */\n overflow: visible;\n\n box-sizing: border-box;\n width: var(--wui-drawer-width, 320px);\n max-width: 90vw;\n height: 100vh;\n height: 100dvh;\n max-height: 100vh;\n max-height: 100dvh;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n\n transition: transform var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n &::backdrop {\n opacity: 0;\n background: var(--wui-drawer-overlay-bg, var(--wui-color-backdrop, rgb(0 0 0 / 0.12)));\n transition: opacity var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n }\n}\n\ndialog[open].is-visible {\n transform: translate(0, 0);\n}\n\n/*\n * 拖拽跟手期间与弹簧弹回期间,transform/backdrop 由 JS 内联样式与 WAAPI 管辖。\n * 特异性必须压过下方开放守卫块(:host(:not()) dialog[open].is-visible = (0,4,1)\n * 的 nested transition 声明),否则拖拽写入的 transform 会被平滑掉、跟手失效。\n * 复合选择器达 (0,6,1),恒高于守卫。\n */\n:host(:not([headless])) dialog.is-dragging[open].is-visible,\ndialog.is-dragging {\n transition: none;\n\n &::backdrop {\n transition: none;\n }\n}\n\n:host([placement='left']) dialog[open].is-visible,\n:host([placement='top']) dialog[open].is-visible,\n:host([placement='bottom']) dialog[open].is-visible {\n transform: translate(0, 0);\n}\n\ndialog.is-closing {\n pointer-events: none;\n transition: transform var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog.is-closing::backdrop {\n transition: opacity var(--wui-duration-drawer-exit, 240ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\ndialog[open].is-visible::backdrop {\n /* 拖拽期间由 JS 写入 --wui-internal-drag-backdrop-opacity 实现遮罩跟手淡出。 */\n opacity: var(--wui-internal-drag-backdrop-opacity, 1);\n}\n\n/* ===== Drag bar(拖拽关闭热区) ===== */\n\n/*\n * 热区贴在抽屉内缘,宽度约 24px 便于触控命中;胶囊本身是约 4×48px 的灰色短条。\n * 位置随 placement 切换:right 贴在左缘、left 贴在右缘、top 贴在底边、bottom 贴在顶边。\n */\n.wui-drawer-drag-zone {\n touch-action: none;\n cursor: grab;\n\n position: absolute;\n z-index: 4;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n}\n\ndialog.is-nested-lower .wui-drawer-drag-zone {\n pointer-events: none;\n opacity: 0;\n}\n\n:host([placement='right']) .wui-drawer-drag-zone {\n top: 0;\n left: 0;\n width: 24px;\n height: 100%;\n}\n\n:host([placement='left']) .wui-drawer-drag-zone {\n top: 0;\n right: 0;\n width: 24px;\n height: 100%;\n}\n\n:host([placement='top']) .wui-drawer-drag-zone,\n:host([placement='bottom']) .wui-drawer-drag-zone {\n right: 0;\n left: 0;\n width: 100%;\n height: 24px;\n}\n\n:host([placement='top']) .wui-drawer-drag-zone {\n bottom: 0;\n}\n\n:host([placement='bottom']) .wui-drawer-drag-zone {\n top: 0;\n}\n\n.wui-drawer-drag-bar {\n width: 4px;\n height: 48px;\n border-radius: 999px;\n background: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 24%, transparent);\n}\n\n:host([placement='top']) .wui-drawer-drag-bar,\n:host([placement='bottom']) .wui-drawer-drag-bar {\n width: 48px;\n height: 4px;\n}\n\n.wui-drawer-drag-zone:hover .wui-drawer-drag-bar,\n.wui-drawer-drag-zone:active .wui-drawer-drag-bar {\n background: color-mix(in srgb, var(--wui-color-text, #1b1b1b) 40%, transparent);\n}\n\n/*\n * 达到关闭阈值的 accent 确认需压过上方 :hover/:active 中性色(拖拽全程 capture\n * 使 hover 恒成立),这里的选择器特异性 (0,3,1) 高于 hover/active 的 (0,3,0)。\n */\ndialog.is-drag-close .wui-drawer-drag-zone .wui-drawer-drag-bar {\n background: var(--wui-color-accent, #08f);\n}\n\n/* ===== Placement 变体 ===== */\n\n:host([placement='left']) dialog {\n right: auto;\n left: 0;\n transform: translateX(-100%);\n}\n\n:host([placement='top']) dialog,\n:host([placement='bottom']) dialog {\n width: 100vw;\n max-width: 100vw;\n height: var(--wui-drawer-height, 300px);\n max-height: 90dvh;\n}\n\n:host([placement='top']) dialog {\n top: 0;\n right: 0;\n bottom: auto;\n left: 0;\n transform: translateY(-100%);\n}\n\n:host([placement='bottom']) dialog {\n top: auto;\n right: 0;\n bottom: 0;\n left: 0;\n transform: translateY(100%);\n}\n\n/*\n * 拖拽中的 grabbing 光标。dialog 复合前缀使其特异性恒高于上方 placement 变体,\n * stylelint 的顺序比较对这种结构必然告警;cascade 上此规则不依赖 placement,\n * 置于其后仅为可读性,故显式豁免。\n */\n/* stylelint-disable-next-line no-descending-specificity */\ndialog.is-dragging .wui-drawer-drag-zone {\n cursor: grabbing;\n}\n\n/* ===== 抽屉主体 ===== */\n\n.wui-drawer-body {\n --wui-internal-glass-shadow: var(--wui-shadow-overlay, 2px 16px 40px rgb(0 0 0 / 0.4));\n --wui-glass-corner-radius: var(--wui-drawer-radius, 28px);\n display: flex;\n flex-direction: column;\n height: 100%;\n background-color: var(--wui-drawer-bg, var(--wui-color-surface-overlay, rgb(246 246 246 / 0.82)));\n}\n\n/* ===== Header ===== */\n\n.wui-drawer-header {\n display: flex;\n align-items: center;\n justify-content: space-between;\n\n padding: 16px 20px;\n border-bottom: 1px solid rgb(0 0 0 / 0.06);\n\n font-size: 18px;\n font-weight: 600;\n line-height: 1.4;\n}\n\n/* ===== 隐藏态 ===== */\n\n.wui-drawer-header[hidden] {\n display: none;\n}\n\n/* ===== 内容区 ===== */\n\n.wui-drawer-content {\n overflow-y: auto;\n overscroll-behavior: contain;\n flex: 1;\n\n padding: 20px;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1.5;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n/* ===== Footer ===== */\n\n.wui-drawer-footer {\n flex-shrink: 0;\n padding: 16px 20px;\n border-top: 1px solid rgb(0 0 0 / 0.06);\n}\n\n.wui-drawer-footer[hidden] {\n display: none;\n}\n\n/* ===== 关闭按钮(独立定位,与 header 解耦) ===== */\n\n.wui-drawer-close {\n position: absolute;\n z-index: 3;\n top: 16px;\n right: 16px;\n}\n\n/* ===== Headless 模式 ===== */\n\n/* Headless 模式下,dialog 只作为 overlay 容器,不渲染内置 UI */\n:host([headless]) dialog {\n /*\n * inset 只属于浮动卡片几何;headless 嵌套在非 headless 外层内时会沿\n * flattened tree 继承到 8px,破坏 JS 读取该变量得到的贴边几何假设\n * (parseFloat('0px') 与回退 0 语义一致),故显式归零。\n */\n --wui-internal-drawer-inset: 0px;\n\n /* 保持 overlay 基础设施,移除内置样式 */\n width: auto;\n max-width: none;\n}\n\n/* ===== 浮动卡片视觉语言(ADR-0036) ===== */\n\n/*\n * 非 headless 时抽屉是四周留边的圆角卡片,与 layout sidebar 的设计语言一致;\n * 弹性拖拽使边距变化(8px → 更大)是合法状态而非渲染缺口。\n * headless 保持贴边几何,视觉由 Consumer 自绘。\n *\n * 留边经公开 token --wui-drawer-inset 暴露(默认 8px,ADR-0036 修订);\n * 置 0 即贴边几何(与 headless 相同),通常与 --wui-drawer-radius: 0 搭配。\n *\n * 闭合位移需要越过留边才能完全滑出视口,因此闭合态 transform 以\n * --wui-internal-drawer-inset 补偿;JS 在 controlled 悬停终态读取同一变量,\n * 保证两条关闭路径的行为一致。headless 将该变量显式归零(见上方 Headless 模式)。\n */\n\n/* 默认 right:四周留边、高度随上下边距拉伸,闭合位移越过右侧留边 */\n:host(:not([headless])) dialog {\n --wui-internal-drawer-inset: var(--wui-drawer-inset, 8px);\n --wui-internal-drawer-nested-depth: 0;\n --wui-internal-drawer-nested-scale: 1;\n --wui-internal-drawer-nested-shift: 0px;\n\n top: var(--wui-internal-drawer-inset);\n right: var(--wui-internal-drawer-inset);\n bottom: var(--wui-internal-drawer-inset);\n left: auto;\n transform: translateX(calc(100% + var(--wui-internal-drawer-inset)));\n\n height: auto;\n border-radius: var(--wui-drawer-radius, 28px);\n}\n\n:host(:not([headless])) .wui-drawer-body {\n border-radius: inherit;\n}\n\n:host(:not([headless])[placement='left']) dialog {\n top: var(--wui-internal-drawer-inset);\n right: auto;\n bottom: var(--wui-internal-drawer-inset);\n left: var(--wui-internal-drawer-inset);\n transform: translateX(calc(-100% - var(--wui-internal-drawer-inset)));\n\n height: auto;\n}\n\n:host(:not([headless])[placement='top']) dialog {\n top: var(--wui-internal-drawer-inset);\n right: var(--wui-internal-drawer-inset);\n bottom: auto;\n left: var(--wui-internal-drawer-inset);\n transform: translateY(calc(-100% - var(--wui-internal-drawer-inset)));\n\n width: auto;\n max-width: none;\n}\n\n:host(:not([headless])[placement='bottom']) dialog {\n top: auto;\n right: var(--wui-internal-drawer-inset);\n bottom: var(--wui-internal-drawer-inset);\n left: var(--wui-internal-drawer-inset);\n transform: translateY(calc(100% + var(--wui-internal-drawer-inset)));\n\n width: auto;\n max-width: none;\n}\n\n/*\n * 开放态守卫:上方闭合 transform 与基础 `dialog[open].is-visible` 特异性相同且\n * 在文件更后方,必须以更高特异性显式恢复打开位,否则抽屉停在闭合位。\n *\n * Nested 层叠(对齐卡片堆栈效果):非顶层抽屉按 0.95^depth 缩放并向屏幕内侧\n * 平移(shift 为正,朝屏幕中心方向),使缩小的卡片在顶层后方露出阶梯式卡片边缘。\n * 用独立 translate/scale 属性(而非并入 transform):\n * 它们是标准可过渡属性,变量变化即触发平滑过渡;且与 transform 轴上的\n * 闭合/拖拽/弹簧位移互不覆写(独立属性先于 transform 应用后合并)。\n * JS 层序管理写入 depth/scale/shift 三个内部变量,单层使用时全为中性值。\n *\n * 层叠变化的平滑过渡(Base UI nested 实测规格:450ms easeOutQuint 类曲线)。\n * transform 保持既有 enter/exit 时长;translate/scale 只在层叠数变化时更新,\n * 用更长的 nested 时长。\n */\n:host(:not([headless])) dialog[open].is-visible {\n transform: translate(0, 0);\n translate: calc(var(--wui-internal-drawer-nested-shift) * -1) 0;\n scale: var(--wui-internal-drawer-nested-scale);\n transition:\n transform var(--wui-duration-drawer-enter, 280ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n translate var(--wui-duration-drawer-nested, 450ms) cubic-bezier(0.22, 1, 0.36, 1),\n scale var(--wui-duration-drawer-nested, 450ms) cubic-bezier(0.22, 1, 0.36, 1);\n}\n\n:host(:not([headless])[placement='left']) dialog[open].is-visible {\n transform: translate(0, 0);\n translate: var(--wui-internal-drawer-nested-shift) 0;\n}\n\n:host(:not([headless])[placement='top']) dialog[open].is-visible {\n transform: translate(0, 0);\n translate: 0 var(--wui-internal-drawer-nested-shift);\n}\n\n:host(:not([headless])[placement='bottom']) dialog[open].is-visible {\n transform: translate(0, 0);\n translate: 0 calc(var(--wui-internal-drawer-nested-shift) * -1);\n}\n\n@media (prefers-reduced-motion: reduce) {\n /*\n * reduce 下:位移一律归零、transform 不参与过渡(仅 opacity 反馈)。\n * 全部 !important:压过开放守卫(嵌套层叠变量的特异性猫鼠不可维护),\n * 排除 .is-dragging —— reduce 下拖拽仍需实时跟手(ADR-0035)。\n * JS 收尾移除内联 transform 后立即为终态,无 280ms 过渡尾巴。\n */\n dialog:not(.is-dragging) {\n transition: opacity var(--wui-duration-feedback, 100ms) ease !important;\n\n &::backdrop {\n transition-duration: var(--wui-duration-feedback, 100ms) !important;\n }\n }\n\n dialog[open].is-visible:not(.is-dragging) {\n transform: none !important;\n translate: none !important;\n scale: none !important;\n }\n}\n"],"mappings":""}
|
|
@@ -26,6 +26,7 @@ export declare class WebUiDropdown extends LitElement {
|
|
|
26
26
|
private _shouldOpenInstantly;
|
|
27
27
|
get isOpen(): boolean;
|
|
28
28
|
connectedCallback(): void;
|
|
29
|
+
firstUpdated(): void;
|
|
29
30
|
disconnectedCallback(): void;
|
|
30
31
|
protected updated(changed: Map<string, unknown>): void;
|
|
31
32
|
openMenu(): void;
|
|
@@ -53,6 +54,7 @@ export declare class WebUiDropdown extends LitElement {
|
|
|
53
54
|
private _restoreSubmenuItems;
|
|
54
55
|
private _syncScrollLock;
|
|
55
56
|
private _queryTriggerAnchor;
|
|
57
|
+
private _syncTriggerAria;
|
|
56
58
|
private _getSubmenuTriggerAnchor;
|
|
57
59
|
private _onTriggerClick;
|
|
58
60
|
private _onClickOutside;
|
|
@@ -1,19 +1,19 @@
|
|
|
1
|
-
import e from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
|
|
2
2
|
import { normalizeLiteral as t, normalizeNumber as n } from "../../shared/normalize/index.js";
|
|
3
3
|
import { defineOverlay as r } from "../../shared/overlay/overlay.js";
|
|
4
4
|
import { hideOverlayPresence as i, showOverlayPresence as a } from "../../shared/overlay/presence.js";
|
|
5
5
|
import { defineScrollLockLease as o } from "../../shared/scroll-lock/scroll-lock.js";
|
|
6
|
+
import { UserChangeController as s } from "../../shared/events/user-change.js";
|
|
6
7
|
import "../dropdown-divider/index.js";
|
|
7
8
|
import "../dropdown-header/index.js";
|
|
8
9
|
import "../dropdown-item/index.js";
|
|
9
|
-
import { UserChangeController as s } from "../../shared/events/user-change.js";
|
|
10
10
|
import { createMenuPortalOverlay as c } from "../../shared/menu-portal/menu-portal.js";
|
|
11
11
|
import { findFocusedMenuItem as l, focusMenuItem as u, getEnabledMenuItems as d, getMenuChildren as f, getMenuItemFromEvent as p, hideNestedMenuChildren as m, moveMenuChildren as h } from "../../shared/menu-portal/menu-tree.js";
|
|
12
12
|
import g from "./style.js";
|
|
13
13
|
import { LitElement as _, html as v, unsafeCSS as y } from "lit";
|
|
14
14
|
import { customElement as b, property as x, state as S } from "lit/decorators.js";
|
|
15
15
|
//#region src/components/dropdown/index.ts
|
|
16
|
-
var C = "web-ui-menu-level-", w = [
|
|
16
|
+
var C = "web-ui-menu-level-", w = 0, T = [
|
|
17
17
|
"top",
|
|
18
18
|
"top-start",
|
|
19
19
|
"top-end",
|
|
@@ -26,7 +26,7 @@ var C = "web-ui-menu-level-", w = [
|
|
|
26
26
|
"left",
|
|
27
27
|
"left-start",
|
|
28
28
|
"left-end"
|
|
29
|
-
],
|
|
29
|
+
], E = class extends _ {
|
|
30
30
|
constructor(...e) {
|
|
31
31
|
super(...e), this.open = !1, this.disabled = !1, this.matchWidth = !1, this.noScrollLock = !1, this._placement = "bottom-start", this._offset = 4, this._activePath = [], this._overlays = /* @__PURE__ */ new Map(), this._closingSubmenuOverlays = /* @__PURE__ */ new Map(), this._ignoreOutsideClick = !1, this._hoverCleanupFns = [], this._scrollLock = o().make(), this._userOpenChange = new s(), this._shouldOpenInstantly = !0, this._onTriggerClick = (e) => {
|
|
32
32
|
this.disabled || (this.open ? this._closeAll(!0) : this._openMenu(e.detail === 0, !0));
|
|
@@ -89,7 +89,7 @@ var C = "web-ui-menu-level-", w = [
|
|
|
89
89
|
return this._placement;
|
|
90
90
|
}
|
|
91
91
|
set placement(e) {
|
|
92
|
-
this._placement = t(e,
|
|
92
|
+
this._placement = t(e, T, "bottom-start");
|
|
93
93
|
}
|
|
94
94
|
get offset() {
|
|
95
95
|
return this._offset;
|
|
@@ -103,12 +103,15 @@ var C = "web-ui-menu-level-", w = [
|
|
|
103
103
|
connectedCallback() {
|
|
104
104
|
super.connectedCallback(), this.addEventListener("keydown", this._onKeydown), document.addEventListener("click", this._onClickOutside);
|
|
105
105
|
}
|
|
106
|
+
firstUpdated() {
|
|
107
|
+
this.shadowRoot?.querySelector("slot[name=\"trigger\"]")?.addEventListener("slotchange", () => this.requestUpdate());
|
|
108
|
+
}
|
|
106
109
|
disconnectedCallback() {
|
|
107
110
|
super.disconnectedCallback(), this.removeEventListener("keydown", this._onKeydown), document.removeEventListener("click", this._onClickOutside), clearTimeout(this._openTimer), clearTimeout(this._ignoreOutsideClickTimer), this._scrollLock.release(), this._cleanupClosedMenu(), this._hoverCleanupFns.forEach((e) => e());
|
|
108
111
|
}
|
|
109
112
|
updated(e) {
|
|
110
|
-
e.has("open") && (this.open ? (this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : void 0, this._ignoreCurrentOutsideClick(), this._syncScrollLock(), this._hideAllSubmenuChildren(), requestAnimationFrame(() => {
|
|
111
|
-
this.open && (this._ensureOverlay(0, this._shouldOpenInstantly), this._shouldOpenInstantly = !0, this._focusMenuItem(this._getEnabledLevelItems(0)[0]));
|
|
113
|
+
this._syncTriggerAria(), e.has("open") && (this.open ? (this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : void 0, this._ignoreCurrentOutsideClick(), this._syncScrollLock(), this._hideAllSubmenuChildren(), requestAnimationFrame(() => {
|
|
114
|
+
this.open && (this._ensureOverlay(0, this._shouldOpenInstantly), this._shouldOpenInstantly = !0, this._focusMenuItem(this._getEnabledLevelItems(0)[0]), this._syncTriggerAria());
|
|
112
115
|
}), this._bindHoversAfterUpdate()) : (this._syncScrollLock(), this._closeRootAfterPresence()), this._userOpenChange.consume() && this.dispatchEvent(new CustomEvent("open-change", {
|
|
113
116
|
detail: { open: this.open },
|
|
114
117
|
bubbles: !0,
|
|
@@ -200,7 +203,7 @@ var C = "web-ui-menu-level-", w = [
|
|
|
200
203
|
}
|
|
201
204
|
_buildOverlay(e, t = !1) {
|
|
202
205
|
let { panel: n, content: i } = c("dropdown-overlay", this);
|
|
203
|
-
n.setAttribute("role", "menu"), n.dataset.level = String(e), n.addEventListener("click", this._onMenuClick), n.addEventListener("keydown", this._onKeydown);
|
|
206
|
+
n.setAttribute("role", "menu"), e === 0 && (n.id = `wui-dropdown-menu-${++w}`), n.dataset.level = String(e), n.addEventListener("click", this._onMenuClick), n.addEventListener("keydown", this._onKeydown);
|
|
204
207
|
let o = e === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(e - 1);
|
|
205
208
|
if (o) {
|
|
206
209
|
let s = r().make({
|
|
@@ -244,6 +247,13 @@ var C = "web-ui-menu-level-", w = [
|
|
|
244
247
|
let e = (this.shadowRoot?.querySelector("slot[name=\"trigger\"]"))?.assignedElements()[0];
|
|
245
248
|
return e instanceof HTMLElement ? e : null;
|
|
246
249
|
}
|
|
250
|
+
_syncTriggerAria() {
|
|
251
|
+
let e = this._queryTriggerAnchor();
|
|
252
|
+
if (!e) return;
|
|
253
|
+
e.setAttribute("aria-haspopup", "menu"), e.setAttribute("aria-expanded", String(this.open));
|
|
254
|
+
let t = this._overlays.get(0)?.overlay;
|
|
255
|
+
t?.id ? e.setAttribute("aria-controls", t.id) : e.removeAttribute("aria-controls");
|
|
256
|
+
}
|
|
247
257
|
_getSubmenuTriggerAnchor(e) {
|
|
248
258
|
let t = this._activePath[e];
|
|
249
259
|
if (t === void 0) return null;
|
|
@@ -300,22 +310,22 @@ var C = "web-ui-menu-level-", w = [
|
|
|
300
310
|
e([x({
|
|
301
311
|
type: Boolean,
|
|
302
312
|
reflect: !0
|
|
303
|
-
})],
|
|
313
|
+
})], E.prototype, "open", void 0), e([x({
|
|
304
314
|
type: Boolean,
|
|
305
315
|
reflect: !0
|
|
306
|
-
})],
|
|
316
|
+
})], E.prototype, "disabled", void 0), e([x({
|
|
307
317
|
type: Boolean,
|
|
308
318
|
reflect: !0,
|
|
309
319
|
attribute: "match-width"
|
|
310
|
-
})],
|
|
320
|
+
})], E.prototype, "matchWidth", void 0), e([x({
|
|
311
321
|
type: Boolean,
|
|
312
322
|
reflect: !0,
|
|
313
323
|
attribute: "no-scroll-lock"
|
|
314
|
-
})],
|
|
324
|
+
})], E.prototype, "noScrollLock", void 0), e([x({
|
|
315
325
|
type: String,
|
|
316
326
|
reflect: !0
|
|
317
|
-
})],
|
|
327
|
+
})], E.prototype, "placement", null), e([x({ type: Number })], E.prototype, "offset", null), e([S()], E.prototype, "_activePath", void 0), E = e([b("web-ui-dropdown")], E);
|
|
318
328
|
//#endregion
|
|
319
|
-
export {
|
|
329
|
+
export { E as WebUiDropdown };
|
|
320
330
|
|
|
321
331
|
//# sourceMappingURL=index.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { createMenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n findFocusedMenuItem,\n focusMenuItem,\n getEnabledMenuItems,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren\n} from '@/shared/menu-portal/menu-tree'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { defineOverlay } from '@/shared/overlay/overlay'\nimport type { OverlayApi } from '@/shared/overlay/overlay'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nexport type { Placement }\n\nconst SLOT_PREFIX = 'web-ui-menu-level-'\nconst ALLOWED_PLACEMENTS = [\n 'top',\n 'top-start',\n 'top-end',\n 'right',\n 'right-start',\n 'right-end',\n 'bottom',\n 'bottom-start',\n 'bottom-end',\n 'left',\n 'left-start',\n 'left-end'\n] as const\n\ninterface MenuOverlay {\n api: OverlayApi\n overlay: HTMLElement\n content: HTMLElement\n}\n\n@customElement('web-ui-dropdown')\nexport class WebUiDropdown extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'match-width' }) matchWidth = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n private _placement: Placement = 'bottom-start'\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'bottom-start')\n }\n\n private _offset = 4\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n this._offset = normalizeNumber(v, 0, 100, 4)\n }\n\n @state() private _activePath: number[] = []\n\n private readonly _overlays = new Map<number, MenuOverlay>()\n private readonly _closingSubmenuOverlays = new Map<HTMLElement, MenuOverlay>()\n private _openTimer?: ReturnType<typeof setTimeout>\n private _ignoreOutsideClick = false\n private _ignoreOutsideClickTimer?: ReturnType<typeof setTimeout>\n private _hoverCleanupFns: (() => void)[] = []\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n\n get isOpen(): boolean {\n return this.open || this._activePath.length > 0\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('keydown', this._onKeydown)\n document.addEventListener('click', this._onClickOutside)\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('keydown', this._onKeydown)\n document.removeEventListener('click', this._onClickOutside)\n clearTimeout(this._openTimer)\n clearTimeout(this._ignoreOutsideClickTimer)\n this._scrollLock.release()\n this._cleanupClosedMenu()\n this._hoverCleanupFns.forEach(fn => fn())\n }\n\n protected override updated(changed: Map<string, unknown>) {\n if (changed.has('open')) {\n if (this.open) {\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._ignoreCurrentOutsideClick()\n this._syncScrollLock()\n this._hideAllSubmenuChildren()\n requestAnimationFrame(() => {\n if (!this.open) return\n this._ensureOverlay(0, this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n this._focusMenuItem(this._getEnabledLevelItems(0)[0])\n })\n this._bindHoversAfterUpdate()\n } else {\n this._syncScrollLock()\n void this._closeRootAfterPresence()\n }\n if (this._userOpenChange.consume()) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.open },\n bubbles: true,\n composed: true\n })\n )\n }\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n if (changed.has('placement') || changed.has('offset') || changed.has('matchWidth')) {\n this._overlays.get(0)?.api.update({\n placement: this.placement,\n offset: this.offset,\n matchWidth: this.matchWidth\n })\n }\n this._bindLevelHovers()\n }\n\n // 打开菜单(命令式,不派发 `open-change`)。\n openMenu() {\n this._openMenu(true, false)\n }\n\n private _openMenu(isInstant: boolean, fromUser: boolean) {\n if (this.disabled || this.open) return\n this._ignoreCurrentOutsideClick()\n this._shouldOpenInstantly = isInstant\n if (fromUser) this._userOpenChange.mark()\n this.open = true\n }\n\n // 关闭所有层级(命令式,不派发 `open-change`)。\n closeAll() {\n this._closeAll(false)\n }\n\n private _closeAll(fromUser: boolean) {\n if (!this.open) return\n if (fromUser) this._userOpenChange.mark()\n this.open = false\n }\n\n // 清理子菜单状态(不影响 open prop)\n private _closeAllSubmenus() {\n this._closeSubmenuFrom(1, true)\n this._disposeClosingSubmenuOverlays()\n this._syncActiveAttrs()\n }\n\n private _cleanupClosedMenu() {\n this._closeAllSubmenus()\n this._returnLevel0Items()\n this._disposeAll()\n }\n\n private async _closeRootAfterPresence() {\n const overlay = this._overlays.get(0)?.overlay\n if (overlay && !(await hideOverlayPresence(overlay))) return\n if (this.open || !this.isConnected) return\n\n this._cleanupClosedMenu()\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideAllSubmenuChildren() {\n hideNestedMenuChildren(this, `${SLOT_PREFIX}-hidden`)\n }\n\n private _toggleSubmenu(level: number, itemIndex: number, isInstant = false) {\n if (this._activePath[level] === itemIndex) {\n return\n }\n\n this._closeSubmenuFrom(level + 1)\n\n this._activePath = [...this._activePath.slice(0, level), itemIndex]\n this._syncActiveAttrs()\n\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.hasAttribute('submenu')) {\n requestAnimationFrame(() => {\n if (!this.open || this._activePath[level] !== itemIndex) return\n const restored = this._ensureOverlay(level + 1, isInstant, item)\n if (!restored) this._populateOverlay(level + 1, item)\n })\n }\n\n this._bindHoversAfterUpdate()\n }\n\n private _closeSubmenuFrom(level: number, isInstant = false) {\n for (let lv = this._activePath.length; lv >= level; lv--) {\n const overlay = this._overlays.get(lv)\n const parentItem = this._getLevelItems(lv - 1)[this._activePath[lv - 1]]\n if (!overlay) continue\n\n if (!parentItem || isInstant) {\n this._depopulateOverlay(lv, parentItem)\n this._disposeOverlay(lv)\n } else {\n this._overlays.delete(lv)\n this._closingSubmenuOverlays.set(parentItem, overlay)\n void this._closeSubmenuAfterPresence(overlay, parentItem)\n }\n }\n const nextActivePath = this._activePath.slice(0, level - 1)\n if (nextActivePath.length !== this._activePath.length) {\n this._activePath = nextActivePath\n }\n this._syncActiveAttrs()\n }\n\n private _syncActiveAttrs() {\n this.querySelectorAll('web-ui-dropdown-item').forEach(item => {\n item.removeAttribute('active')\n })\n this._overlays.forEach((_, level) => {\n this._getLevelItems(level).forEach(item => item.removeAttribute('active'))\n })\n this._activePath.forEach((itemIndex, level) => {\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.matches('web-ui-dropdown-item')) {\n item.setAttribute('active', '')\n }\n })\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const content = this._overlays.get(level)?.content\n return content ? getMenuChildren(content) : []\n }\n\n private _populateLevel0() {\n const content = this._overlays.get(0)?.content\n if (content) moveMenuChildren(this, content)\n }\n\n private _returnLevel0Items() {\n const content = this._overlays.get(0)?.content\n if (content) moveMenuChildren(content, this)\n }\n\n private _populateOverlay(level: number, submenuItem: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (content) moveMenuChildren(submenuItem, content)\n }\n\n private _depopulateOverlay(level: number, parentItem?: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (!content) return\n const targetLevel = level - 1\n const submenuItem = parentItem ?? this._getLevelItems(targetLevel)[this._activePath[targetLevel]]\n if (submenuItem) moveMenuChildren(content, submenuItem)\n }\n\n private _ensureOverlay(level: number, isInstant = false, submenuItem?: HTMLElement): boolean {\n const closingOverlay = submenuItem ? this._closingSubmenuOverlays.get(submenuItem) : undefined\n if (closingOverlay) {\n this._closingSubmenuOverlays.delete(submenuItem!)\n this._overlays.set(level, closingOverlay)\n closingOverlay.api.open()\n showOverlayPresence(closingOverlay.overlay, { isInstant })\n return true\n }\n const existing = this._overlays.get(level)\n if (existing) {\n existing.api.open()\n showOverlayPresence(existing.overlay, { isInstant })\n return false\n }\n this._buildOverlay(level, isInstant)\n return false\n }\n\n private _buildOverlay(level: number, isInstant = false) {\n const { panel: overlay, content } = createMenuPortalOverlay('dropdown-overlay', this)\n overlay.setAttribute('role', 'menu')\n overlay.dataset.level = String(level)\n overlay.addEventListener('click', this._onMenuClick)\n overlay.addEventListener('keydown', this._onKeydown)\n\n const anchor = level === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(level - 1)\n\n if (anchor) {\n const ctrl = defineOverlay().make({\n anchor,\n overlay,\n placement: level === 0 ? this.placement : 'right-start',\n offset: level === 0 ? this.offset : 0,\n // 菜单至少与 trigger/父项同宽,内容可更宽(floor 来自 --wui-overlay-min-width)\n minAnchorWidth: true,\n matchWidth: level === 0 ? this.matchWidth : false,\n strategy: 'fixed'\n })\n this._overlays.set(level, { api: ctrl, overlay, content })\n if (level === 0) this._populateLevel0()\n ctrl.open()\n showOverlayPresence(overlay, { isInstant })\n }\n }\n\n private _disposeOverlay(level: number) {\n const overlay = this._overlays.get(level)?.overlay\n overlay?.removeEventListener('click', this._onMenuClick)\n overlay?.removeEventListener('keydown', this._onKeydown)\n overlay?.remove()\n this._overlays.get(level)?.api.dispose()\n this._overlays.delete(level)\n }\n\n private _disposeAll() {\n for (const level of this._overlays.keys()) {\n this._disposeOverlay(level)\n }\n }\n\n private async _closeSubmenuAfterPresence(overlay: MenuOverlay, parentItem: HTMLElement) {\n if (!(await hideOverlayPresence(overlay.overlay))) return\n if (this._closingSubmenuOverlays.get(parentItem) !== overlay) return\n\n this._closingSubmenuOverlays.delete(parentItem)\n this._restoreSubmenuItems(overlay, parentItem)\n overlay.api.dispose()\n overlay.overlay.remove()\n }\n\n private _disposeClosingSubmenuOverlays() {\n this._closingSubmenuOverlays.forEach((overlay, parentItem) => {\n this._restoreSubmenuItems(overlay, parentItem)\n overlay.api.dispose()\n overlay.overlay.remove()\n })\n this._closingSubmenuOverlays.clear()\n }\n\n private _restoreSubmenuItems(overlay: MenuOverlay, parentItem: HTMLElement) {\n moveMenuChildren(overlay.content, parentItem)\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _queryTriggerAnchor(): HTMLElement | null {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n const el = slot?.assignedElements()[0]\n return el instanceof HTMLElement ? el : null\n }\n\n private _getSubmenuTriggerAnchor(level: number): HTMLElement | null {\n const idx = this._activePath[level]\n if (idx === undefined) return null\n const items = this._getLevelItems(level)\n const item = items[idx]\n if (!item) return null\n return item.shadowRoot?.querySelector('.item-inner') ?? item\n }\n\n private _onTriggerClick = (event: MouseEvent) => {\n if (this.disabled) return\n if (this.open) {\n this._closeAll(true)\n } else {\n this._openMenu(event.detail === 0, true)\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this.isOpen || this._ignoreOutsideClick) return\n if (this._isInsideShadowRoot(e)) return\n this._closeAll(true)\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled') || !item.hasAttribute('submenu')) return\n\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, e.detail === 0)\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled || !this.open) return\n\n // overlay 上键盘事件 target 是菜单项而非组件自身,不能限定 e.target === this,\n // 否则鼠标打开、焦点在菜单项时 Escape 无法关闭\n if (e.key === 'Escape') {\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n e.preventDefault()\n return\n }\n\n const focused = this._getFocusedItem(e)\n const level = this._getFocusedLevel(focused)\n if (level === undefined) return\n const items = this._getEnabledLevelItems(level)\n const currentIndex = focused ? items.indexOf(focused) : -1\n\n switch (e.key) {\n case 'ArrowDown':\n this._focusMenuItem(items[(currentIndex + 1 + items.length) % items.length])\n break\n case 'ArrowUp':\n this._focusMenuItem(items[(currentIndex - 1 + items.length) % items.length])\n break\n case 'Home':\n this._focusMenuItem(items[0])\n break\n case 'End':\n this._focusMenuItem(items.at(-1))\n break\n case 'ArrowRight':\n if (focused?.hasAttribute('submenu')) {\n const itemIndex = this._getLevelItems(level).indexOf(focused)\n this._toggleSubmenu(level, itemIndex, true)\n requestAnimationFrame(() =>\n requestAnimationFrame(() => this._focusMenuItem(this._getEnabledLevelItems(level + 1)[0]))\n )\n }\n break\n case 'ArrowLeft':\n if (level > 0) {\n this._closeSubmenuFrom(level)\n this._focusMenuItem(this._getLevelItems(level - 1)[this._activePath[level - 1]])\n }\n break\n case 'Enter':\n case ' ':\n focused?.click()\n break\n case 'Escape':\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n break\n default:\n return\n }\n e.preventDefault()\n e.stopPropagation()\n }\n\n private _getEnabledLevelItems(level: number) {\n const content = this._overlays.get(level)?.content\n return content ? getEnabledMenuItems(content) : []\n }\n\n private _getFocusedItem(event?: KeyboardEvent): HTMLElement | undefined {\n const eventItem = event\n ?.composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.matches('web-ui-dropdown-item'))\n if (eventItem) return eventItem\n\n return findFocusedMenuItem([...this._overlays.values()].map(({ overlay }) => overlay))\n }\n\n private _getFocusedLevel(item = this._getFocusedItem()): number | undefined {\n if (!item) return undefined\n const overlay = [...this._overlays.entries()].find(([, value]) => value.overlay.contains(item))\n return overlay?.[0]\n }\n\n private _focusMenuItem(item: HTMLElement | undefined) {\n focusMenuItem(item)\n }\n\n private _isInsideShadowRoot(e: MouseEvent): boolean {\n for (const node of e.composedPath()) {\n if (node === this || node === this.shadowRoot) return true\n if (node instanceof Node && node.getRootNode() === this.shadowRoot) return true\n if (node instanceof Node && [...this._overlays.values()].some(({ overlay }) => overlay.contains(node)))\n return true\n }\n return false\n }\n\n private _ignoreCurrentOutsideClick() {\n this._ignoreOutsideClick = true\n clearTimeout(this._ignoreOutsideClickTimer)\n this._ignoreOutsideClickTimer = setTimeout(() => {\n this._ignoreOutsideClick = false\n })\n }\n\n private _bindLevelHovers() {\n this._hoverCleanupFns.forEach(fn => fn())\n this._hoverCleanupFns.length = 0\n\n const maxLevel = this._activePath.length + 1\n for (let lv = 0; lv <= maxLevel; lv++) {\n const items = this._getLevelItems(lv)\n if (!items.length) continue\n items.forEach((item, idx) => {\n if (!item.matches('web-ui-dropdown-item') || item.hasAttribute('disabled')) return\n const handler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return\n clearTimeout(this._openTimer)\n if (item.hasAttribute('submenu')) {\n if (this._activePath[lv] !== idx) {\n this._openTimer = setTimeout(() => this._toggleSubmenu(lv, idx), 200)\n }\n } else if (this._activePath.length > lv) {\n this._closeSubmenuFrom(lv + 1)\n }\n }\n item.addEventListener('pointerenter', handler, { passive: true })\n this._hoverCleanupFns.push(() => item.removeEventListener('pointerenter', handler))\n })\n }\n }\n\n private _bindHoversAfterUpdate() {\n requestAnimationFrame(() => this._bindLevelHovers())\n }\n\n override render() {\n return html`\n <div class=\"dropdown-trigger\" @click=${this._onTriggerClick}>\n <slot name=\"trigger\"></slot>\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-dropdown': WebUiDropdown\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown/index.ts"],"sourcesContent":["import type { Placement } from '@floating-ui/dom'\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport '@/components/dropdown-divider'\nimport '@/components/dropdown-header'\nimport '@/components/dropdown-item'\nimport { UserChangeController } from '@/shared/events/user-change'\nimport { createMenuPortalOverlay } from '@/shared/menu-portal/menu-portal'\nimport {\n findFocusedMenuItem,\n focusMenuItem,\n getEnabledMenuItems,\n getMenuChildren,\n getMenuItemFromEvent,\n hideNestedMenuChildren,\n moveMenuChildren\n} from '@/shared/menu-portal/menu-tree'\nimport { normalizeLiteral, normalizeNumber } from '@/shared/normalize'\nimport { defineOverlay } from '@/shared/overlay/overlay'\nimport type { OverlayApi } from '@/shared/overlay/overlay'\nimport { hideOverlayPresence, showOverlayPresence } from '@/shared/overlay/presence'\nimport { defineScrollLockLease } from '@/shared/scroll-lock/scroll-lock'\n\nimport style from './style.css?inline'\n\nexport type { Placement }\n\nconst SLOT_PREFIX = 'web-ui-menu-level-'\n\nlet dropdownIdCounter = 0\n\nconst ALLOWED_PLACEMENTS = [\n 'top',\n 'top-start',\n 'top-end',\n 'right',\n 'right-start',\n 'right-end',\n 'bottom',\n 'bottom-start',\n 'bottom-end',\n 'left',\n 'left-start',\n 'left-end'\n] as const\n\ninterface MenuOverlay {\n api: OverlayApi\n overlay: HTMLElement\n content: HTMLElement\n}\n\n@customElement('web-ui-dropdown')\nexport class WebUiDropdown extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) open = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true, attribute: 'match-width' }) matchWidth = false\n @property({ type: Boolean, reflect: true, attribute: 'no-scroll-lock' }) noScrollLock = false\n\n private _placement: Placement = 'bottom-start'\n\n @property({ type: String, reflect: true })\n get placement(): Placement {\n return this._placement\n }\n set placement(v: string) {\n this._placement = normalizeLiteral(v, ALLOWED_PLACEMENTS, 'bottom-start')\n }\n\n private _offset = 4\n\n @property({ type: Number })\n get offset(): number {\n return this._offset\n }\n set offset(v: number) {\n this._offset = normalizeNumber(v, 0, 100, 4)\n }\n\n @state() private _activePath: number[] = []\n\n private readonly _overlays = new Map<number, MenuOverlay>()\n private readonly _closingSubmenuOverlays = new Map<HTMLElement, MenuOverlay>()\n private _openTimer?: ReturnType<typeof setTimeout>\n private _ignoreOutsideClick = false\n private _ignoreOutsideClickTimer?: ReturnType<typeof setTimeout>\n private _hoverCleanupFns: (() => void)[] = []\n private readonly _scrollLock = defineScrollLockLease().make()\n private readonly _userOpenChange = new UserChangeController()\n private _restoreFocusTarget?: HTMLElement\n private _shouldOpenInstantly = true\n\n get isOpen(): boolean {\n return this.open || this._activePath.length > 0\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this.addEventListener('keydown', this._onKeydown)\n document.addEventListener('click', this._onClickOutside)\n }\n\n override firstUpdated() {\n // trigger slot 内容增删不触发宿主响应式更新:显式请求一轮渲染,\n // 由 updated() 的 ARIA 回写覆盖晚到的 trigger 元素。\n this.shadowRoot\n ?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n ?.addEventListener('slotchange', () => this.requestUpdate())\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.removeEventListener('keydown', this._onKeydown)\n document.removeEventListener('click', this._onClickOutside)\n clearTimeout(this._openTimer)\n clearTimeout(this._ignoreOutsideClickTimer)\n this._scrollLock.release()\n this._cleanupClosedMenu()\n this._hoverCleanupFns.forEach(fn => fn())\n }\n\n protected override updated(changed: Map<string, unknown>) {\n // ARIA 回写不依赖 open 分支,任何渲染后都保持与宿主状态同步。\n this._syncTriggerAria()\n\n if (changed.has('open')) {\n if (this.open) {\n this._restoreFocusTarget ??= document.activeElement instanceof HTMLElement ? document.activeElement : undefined\n this._ignoreCurrentOutsideClick()\n this._syncScrollLock()\n this._hideAllSubmenuChildren()\n requestAnimationFrame(() => {\n if (!this.open) return\n this._ensureOverlay(0, this._shouldOpenInstantly)\n this._shouldOpenInstantly = true\n this._focusMenuItem(this._getEnabledLevelItems(0)[0])\n // 面板 id 在 overlay 构建后才有:回写 aria-controls 指向。\n this._syncTriggerAria()\n })\n this._bindHoversAfterUpdate()\n } else {\n this._syncScrollLock()\n void this._closeRootAfterPresence()\n }\n if (this._userOpenChange.consume()) {\n this.dispatchEvent(\n new CustomEvent('open-change', {\n detail: { open: this.open },\n bubbles: true,\n composed: true\n })\n )\n }\n }\n if (changed.has('noScrollLock')) this._syncScrollLock()\n if (changed.has('placement') || changed.has('offset') || changed.has('matchWidth')) {\n this._overlays.get(0)?.api.update({\n placement: this.placement,\n offset: this.offset,\n matchWidth: this.matchWidth\n })\n }\n this._bindLevelHovers()\n }\n\n // 命令式打开,不派发 open-change。\n openMenu() {\n this._openMenu(true, false)\n }\n\n private _openMenu(isInstant: boolean, fromUser: boolean) {\n if (this.disabled || this.open) return\n this._ignoreCurrentOutsideClick()\n this._shouldOpenInstantly = isInstant\n if (fromUser) this._userOpenChange.mark()\n this.open = true\n }\n\n // 命令式关闭全部层级,不派发 open-change。\n closeAll() {\n this._closeAll(false)\n }\n\n private _closeAll(fromUser: boolean) {\n if (!this.open) return\n if (fromUser) this._userOpenChange.mark()\n this.open = false\n }\n\n // 不影响 open prop。\n private _closeAllSubmenus() {\n this._closeSubmenuFrom(1, true)\n this._disposeClosingSubmenuOverlays()\n this._syncActiveAttrs()\n }\n\n private _cleanupClosedMenu() {\n this._closeAllSubmenus()\n this._returnLevel0Items()\n this._disposeAll()\n }\n\n private async _closeRootAfterPresence() {\n const overlay = this._overlays.get(0)?.overlay\n if (overlay && !(await hideOverlayPresence(overlay))) return\n if (this.open || !this.isConnected) return\n\n this._cleanupClosedMenu()\n this._restoreFocusTarget?.focus()\n this._restoreFocusTarget = undefined\n }\n\n private _hideAllSubmenuChildren() {\n hideNestedMenuChildren(this, `${SLOT_PREFIX}-hidden`)\n }\n\n private _toggleSubmenu(level: number, itemIndex: number, isInstant = false) {\n if (this._activePath[level] === itemIndex) {\n return\n }\n\n this._closeSubmenuFrom(level + 1)\n\n this._activePath = [...this._activePath.slice(0, level), itemIndex]\n this._syncActiveAttrs()\n\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.hasAttribute('submenu')) {\n requestAnimationFrame(() => {\n if (!this.open || this._activePath[level] !== itemIndex) return\n const restored = this._ensureOverlay(level + 1, isInstant, item)\n if (!restored) this._populateOverlay(level + 1, item)\n })\n }\n\n this._bindHoversAfterUpdate()\n }\n\n private _closeSubmenuFrom(level: number, isInstant = false) {\n for (let lv = this._activePath.length; lv >= level; lv--) {\n const overlay = this._overlays.get(lv)\n const parentItem = this._getLevelItems(lv - 1)[this._activePath[lv - 1]]\n if (!overlay) continue\n\n if (!parentItem || isInstant) {\n this._depopulateOverlay(lv, parentItem)\n this._disposeOverlay(lv)\n } else {\n this._overlays.delete(lv)\n this._closingSubmenuOverlays.set(parentItem, overlay)\n void this._closeSubmenuAfterPresence(overlay, parentItem)\n }\n }\n const nextActivePath = this._activePath.slice(0, level - 1)\n if (nextActivePath.length !== this._activePath.length) {\n this._activePath = nextActivePath\n }\n this._syncActiveAttrs()\n }\n\n private _syncActiveAttrs() {\n this.querySelectorAll('web-ui-dropdown-item').forEach(item => {\n item.removeAttribute('active')\n })\n this._overlays.forEach((_, level) => {\n this._getLevelItems(level).forEach(item => item.removeAttribute('active'))\n })\n this._activePath.forEach((itemIndex, level) => {\n const item = this._getLevelItems(level)[itemIndex]\n if (item?.matches('web-ui-dropdown-item')) {\n item.setAttribute('active', '')\n }\n })\n }\n\n private _getLevelItems(level: number): HTMLElement[] {\n const content = this._overlays.get(level)?.content\n return content ? getMenuChildren(content) : []\n }\n\n private _populateLevel0() {\n const content = this._overlays.get(0)?.content\n if (content) moveMenuChildren(this, content)\n }\n\n private _returnLevel0Items() {\n const content = this._overlays.get(0)?.content\n if (content) moveMenuChildren(content, this)\n }\n\n private _populateOverlay(level: number, submenuItem: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (content) moveMenuChildren(submenuItem, content)\n }\n\n private _depopulateOverlay(level: number, parentItem?: HTMLElement) {\n const content = this._overlays.get(level)?.content\n if (!content) return\n const targetLevel = level - 1\n const submenuItem = parentItem ?? this._getLevelItems(targetLevel)[this._activePath[targetLevel]]\n if (submenuItem) moveMenuChildren(content, submenuItem)\n }\n\n private _ensureOverlay(level: number, isInstant = false, submenuItem?: HTMLElement): boolean {\n const closingOverlay = submenuItem ? this._closingSubmenuOverlays.get(submenuItem) : undefined\n if (closingOverlay) {\n this._closingSubmenuOverlays.delete(submenuItem!)\n this._overlays.set(level, closingOverlay)\n closingOverlay.api.open()\n showOverlayPresence(closingOverlay.overlay, { isInstant })\n return true\n }\n const existing = this._overlays.get(level)\n if (existing) {\n existing.api.open()\n showOverlayPresence(existing.overlay, { isInstant })\n return false\n }\n this._buildOverlay(level, isInstant)\n return false\n }\n\n private _buildOverlay(level: number, isInstant = false) {\n const { panel: overlay, content } = createMenuPortalOverlay('dropdown-overlay', this)\n overlay.setAttribute('role', 'menu')\n // 根层级面板带 id:trigger 的 aria-controls 指向它(Q9a)。\n if (level === 0) overlay.id = `wui-dropdown-menu-${++dropdownIdCounter}`\n overlay.dataset.level = String(level)\n overlay.addEventListener('click', this._onMenuClick)\n overlay.addEventListener('keydown', this._onKeydown)\n\n const anchor = level === 0 ? this._queryTriggerAnchor() : this._getSubmenuTriggerAnchor(level - 1)\n\n if (anchor) {\n const ctrl = defineOverlay().make({\n anchor,\n overlay,\n placement: level === 0 ? this.placement : 'right-start',\n offset: level === 0 ? this.offset : 0,\n // 菜单至少与 trigger/父项同宽,内容可更宽(floor 来自 --wui-overlay-min-width)\n minAnchorWidth: true,\n matchWidth: level === 0 ? this.matchWidth : false,\n strategy: 'fixed'\n })\n this._overlays.set(level, { api: ctrl, overlay, content })\n if (level === 0) this._populateLevel0()\n ctrl.open()\n showOverlayPresence(overlay, { isInstant })\n }\n }\n\n private _disposeOverlay(level: number) {\n const overlay = this._overlays.get(level)?.overlay\n overlay?.removeEventListener('click', this._onMenuClick)\n overlay?.removeEventListener('keydown', this._onKeydown)\n overlay?.remove()\n this._overlays.get(level)?.api.dispose()\n this._overlays.delete(level)\n }\n\n private _disposeAll() {\n for (const level of this._overlays.keys()) {\n this._disposeOverlay(level)\n }\n }\n\n private async _closeSubmenuAfterPresence(overlay: MenuOverlay, parentItem: HTMLElement) {\n if (!(await hideOverlayPresence(overlay.overlay))) return\n if (this._closingSubmenuOverlays.get(parentItem) !== overlay) return\n\n this._closingSubmenuOverlays.delete(parentItem)\n this._restoreSubmenuItems(overlay, parentItem)\n overlay.api.dispose()\n overlay.overlay.remove()\n }\n\n private _disposeClosingSubmenuOverlays() {\n this._closingSubmenuOverlays.forEach((overlay, parentItem) => {\n this._restoreSubmenuItems(overlay, parentItem)\n overlay.api.dispose()\n overlay.overlay.remove()\n })\n this._closingSubmenuOverlays.clear()\n }\n\n private _restoreSubmenuItems(overlay: MenuOverlay, parentItem: HTMLElement) {\n moveMenuChildren(overlay.content, parentItem)\n }\n\n private _syncScrollLock(isOpen = this.open) {\n this._scrollLock.sync(isOpen && !this.noScrollLock)\n }\n\n private _queryTriggerAnchor(): HTMLElement | null {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"trigger\"]')\n const el = slot?.assignedElements()[0]\n return el instanceof HTMLElement ? el : null\n }\n\n /*\n * trigger 语义回写(Q9a):slot 进来的元素是真正的可聚焦交互元素,\n * aria-haspopup / aria-expanded / aria-controls 必须设在它身上;\n * 组件没有 trigger 包装 div,此前完全没有 ARIA 接线。\n */\n private _syncTriggerAria() {\n const trigger = this._queryTriggerAnchor()\n if (!trigger) return\n trigger.setAttribute('aria-haspopup', 'menu')\n trigger.setAttribute('aria-expanded', String(this.open))\n const rootOverlay = this._overlays.get(0)?.overlay\n if (rootOverlay?.id) trigger.setAttribute('aria-controls', rootOverlay.id)\n else trigger.removeAttribute('aria-controls')\n }\n\n private _getSubmenuTriggerAnchor(level: number): HTMLElement | null {\n const idx = this._activePath[level]\n if (idx === undefined) return null\n const items = this._getLevelItems(level)\n const item = items[idx]\n if (!item) return null\n return item.shadowRoot?.querySelector('.item-inner') ?? item\n }\n\n private _onTriggerClick = (event: MouseEvent) => {\n if (this.disabled) return\n if (this.open) {\n this._closeAll(true)\n } else {\n this._openMenu(event.detail === 0, true)\n }\n }\n\n private _onClickOutside = (e: MouseEvent) => {\n if (!this.isOpen || this._ignoreOutsideClick) return\n if (this._isInsideShadowRoot(e)) return\n this._closeAll(true)\n }\n\n private _onMenuClick = (e: MouseEvent) => {\n const item = getMenuItemFromEvent(e)\n if (!item || item.hasAttribute('disabled') || !item.hasAttribute('submenu')) return\n\n const level = this._getFocusedLevel(item)\n if (level === undefined) return\n const itemIndex = this._getLevelItems(level).indexOf(item)\n if (itemIndex >= 0) this._toggleSubmenu(level, itemIndex, e.detail === 0)\n }\n\n private _onKeydown = (e: KeyboardEvent) => {\n if (this.disabled || !this.open) return\n\n // overlay 上键盘事件 target 是菜单项而非组件自身,不能限定 e.target === this,\n // 否则鼠标打开、焦点在菜单项时 Escape 无法关闭\n if (e.key === 'Escape') {\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n e.preventDefault()\n return\n }\n\n const focused = this._getFocusedItem(e)\n const level = this._getFocusedLevel(focused)\n if (level === undefined) return\n const items = this._getEnabledLevelItems(level)\n const currentIndex = focused ? items.indexOf(focused) : -1\n\n switch (e.key) {\n case 'ArrowDown':\n this._focusMenuItem(items[(currentIndex + 1 + items.length) % items.length])\n break\n case 'ArrowUp':\n this._focusMenuItem(items[(currentIndex - 1 + items.length) % items.length])\n break\n case 'Home':\n this._focusMenuItem(items[0])\n break\n case 'End':\n this._focusMenuItem(items.at(-1))\n break\n case 'ArrowRight':\n if (focused?.hasAttribute('submenu')) {\n const itemIndex = this._getLevelItems(level).indexOf(focused)\n this._toggleSubmenu(level, itemIndex, true)\n requestAnimationFrame(() =>\n requestAnimationFrame(() => this._focusMenuItem(this._getEnabledLevelItems(level + 1)[0]))\n )\n }\n break\n case 'ArrowLeft':\n if (level > 0) {\n this._closeSubmenuFrom(level)\n this._focusMenuItem(this._getLevelItems(level - 1)[this._activePath[level - 1]])\n }\n break\n case 'Enter':\n case ' ':\n focused?.click()\n break\n case 'Escape':\n if (this._activePath.length > 0) this._closeSubmenuFrom(this._activePath.length)\n else this._closeAll(true)\n break\n default:\n return\n }\n e.preventDefault()\n e.stopPropagation()\n }\n\n private _getEnabledLevelItems(level: number) {\n const content = this._overlays.get(level)?.content\n return content ? getEnabledMenuItems(content) : []\n }\n\n private _getFocusedItem(event?: KeyboardEvent): HTMLElement | undefined {\n const eventItem = event\n ?.composedPath()\n .find((node): node is HTMLElement => node instanceof HTMLElement && node.matches('web-ui-dropdown-item'))\n if (eventItem) return eventItem\n\n return findFocusedMenuItem([...this._overlays.values()].map(({ overlay }) => overlay))\n }\n\n private _getFocusedLevel(item = this._getFocusedItem()): number | undefined {\n if (!item) return undefined\n const overlay = [...this._overlays.entries()].find(([, value]) => value.overlay.contains(item))\n return overlay?.[0]\n }\n\n private _focusMenuItem(item: HTMLElement | undefined) {\n focusMenuItem(item)\n }\n\n private _isInsideShadowRoot(e: MouseEvent): boolean {\n for (const node of e.composedPath()) {\n if (node === this || node === this.shadowRoot) return true\n if (node instanceof Node && node.getRootNode() === this.shadowRoot) return true\n if (node instanceof Node && [...this._overlays.values()].some(({ overlay }) => overlay.contains(node)))\n return true\n }\n return false\n }\n\n private _ignoreCurrentOutsideClick() {\n this._ignoreOutsideClick = true\n clearTimeout(this._ignoreOutsideClickTimer)\n this._ignoreOutsideClickTimer = setTimeout(() => {\n this._ignoreOutsideClick = false\n })\n }\n\n private _bindLevelHovers() {\n this._hoverCleanupFns.forEach(fn => fn())\n this._hoverCleanupFns.length = 0\n\n const maxLevel = this._activePath.length + 1\n for (let lv = 0; lv <= maxLevel; lv++) {\n const items = this._getLevelItems(lv)\n if (!items.length) continue\n items.forEach((item, idx) => {\n if (!item.matches('web-ui-dropdown-item') || item.hasAttribute('disabled')) return\n const handler = (event: PointerEvent) => {\n if (event.pointerType === 'touch') return\n clearTimeout(this._openTimer)\n if (item.hasAttribute('submenu')) {\n if (this._activePath[lv] !== idx) {\n this._openTimer = setTimeout(() => this._toggleSubmenu(lv, idx), 200)\n }\n } else if (this._activePath.length > lv) {\n this._closeSubmenuFrom(lv + 1)\n }\n }\n item.addEventListener('pointerenter', handler, { passive: true })\n this._hoverCleanupFns.push(() => item.removeEventListener('pointerenter', handler))\n })\n }\n }\n\n private _bindHoversAfterUpdate() {\n requestAnimationFrame(() => this._bindLevelHovers())\n }\n\n override render() {\n return html`\n <div class=\"dropdown-trigger\" @click=${this._onTriggerClick}>\n <slot name=\"trigger\"></slot>\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-dropdown': WebUiDropdown\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
|
|
2
2
|
import t from "./style.js";
|
|
3
3
|
import { LitElement as n, html as r, unsafeCSS as i } from "lit";
|
|
4
4
|
import { customElement as a } from "lit/decorators.js";
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
|
|
2
2
|
import t from "./style.js";
|
|
3
3
|
import { LitElement as n, html as r, unsafeCSS as i } from "lit";
|
|
4
4
|
import { customElement as a } from "lit/decorators.js";
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/dropdown-header/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}.header{-webkit-user-select:none;user-select:none;color
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}.header{-webkit-user-select:none;user-select:none;color:var(--wui-color-text-tertiary,#1b1b1b59);padding:8px 8px 4px;font-size:12px;font-weight:600}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-header/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n.header {\n user-select: none;\n\n padding: 8px
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-header/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n.header {\n user-select: none;\n\n padding: 8px 8px 4px;\n\n font-size: 12px;\n font-weight: 600;\n color: var(--wui-color-text-tertiary, rgb(27 27 27 / 0.35));\n}\n"],"mappings":""}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import e from "../../_virtual/_@oxc-project_runtime@0.
|
|
1
|
+
import e from "../../_virtual/_@oxc-project_runtime@0.146.0/helpers/esm/decorate.js";
|
|
2
2
|
import "../icon/index.js";
|
|
3
3
|
import t from "../../icons/generated/lucide-chevron-right.js";
|
|
4
4
|
import n from "./style.js";
|
|
@@ -16,8 +16,9 @@ var c = class extends r {
|
|
|
16
16
|
this.shadowRoot?.querySelector(".item-inner")?.focus();
|
|
17
17
|
}
|
|
18
18
|
render() {
|
|
19
|
+
let e = this.pl ? `padding-left: ${this.pl}` : "";
|
|
19
20
|
return i`
|
|
20
|
-
<div class="item-inner" style=${
|
|
21
|
+
<div class="item-inner" style=${e} role="menuitem" tabindex=${this.disabled ? "-1" : "0"}>
|
|
21
22
|
<slot name="prefix"></slot>
|
|
22
23
|
<span class="item-label"><slot></slot></span>
|
|
23
24
|
${this.submenu ? i`<web-ui-icon .icon=${t}></web-ui-icon>` : i`<span class="item-suffix"><slot name="suffix"></slot></span>`}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown-item/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport { lucideChevronRight } from '@/icons'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dropdown-item')\nexport class WebUiDropdownItem extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: String, reflect: true }) pl = ''\n @property({ type: String, reflect: true }) value = ''\n @property({ type: Boolean, reflect: true }) submenu = false\n\n // 通过可见元素聚焦,避免 portal 菜单跨 Shadow DOM 时丢失焦点。\n focusItem() {\n this.shadowRoot?.querySelector<HTMLElement>('.item-inner')?.focus()\n }\n\n override render() {\n const plStyle = this.pl ? `padding-left: ${this.pl}` : ''\n return html`\n <div class=\"item-inner\" style=${plStyle} role=\"menuitem\" tabindex=${this.disabled ? '-1' : '0'}>\n <slot name=\"prefix\"></slot>\n <span class=\"item-label\"><slot></slot></span>\n ${this.submenu\n
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/dropdown-item/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\n\nimport '@/components/icon'\nimport { lucideChevronRight } from '@/icons'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-dropdown-item')\nexport class WebUiDropdownItem extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: String, reflect: true }) pl = ''\n @property({ type: String, reflect: true }) value = ''\n @property({ type: Boolean, reflect: true }) submenu = false\n\n // 通过可见元素聚焦,避免 portal 菜单跨 Shadow DOM 时丢失焦点。\n focusItem() {\n this.shadowRoot?.querySelector<HTMLElement>('.item-inner')?.focus()\n }\n\n override render() {\n const plStyle = this.pl ? `padding-left: ${this.pl}` : ''\n return html`\n <div class=\"item-inner\" style=${plStyle} role=\"menuitem\" tabindex=${this.disabled ? '-1' : '0'}>\n <slot name=\"prefix\"></slot>\n <span class=\"item-label\"><slot></slot></span>\n ${\n this.submenu\n ? html`<web-ui-icon .icon=${lucideChevronRight}></web-ui-icon>`\n : html`<span class=\"item-suffix\"><slot name=\"suffix\"></slot></span>`\n }\n </div>\n `\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-dropdown-item': WebUiDropdownItem\n }\n}\n"],"mappings":";;;;;;;AASO,IAAM,IAAN,cAAgC,EAAW;;EAMM,aAHC,KAAA,WAAA,IACP,KAAA,KAAA,IACG,KAAA,QAAA,IACG,KAAA,UAAA;;;EAL7B,KAAA,SAAA,EAAU,CAAK;;CAQxC,YAAY;EACV,KAAK,YAAY,cAA2B,aAAa,CAAC,EAAE,MAAM;CACpE;CAEA,SAAkB;EAChB,IAAM,IAAU,KAAK,KAAK,iBAAiB,KAAK,OAAO;EACvD,OAAO,CAAI;sCACuB,EAAQ,4BAA4B,KAAK,WAAW,OAAO,IAAI;;;UAI3F,KAAK,UACD,CAAI,sBAAsB,EAAmB,mBAC7C,CAAI,+DACT;;;CAGP;AACF;AAxBG,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,MAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAQ,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,KAAA,CAAA,GACxC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GAP3C,IAAA,EAAA,CAAA,EAAc,sBAAsB,CAAA,GAAA,CAAA"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/dropdown-item/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}:host([disabled]){cursor:not-allowed;opacity:.4}.item-inner{-webkit-user-select:none;user-select:none;height:32px;font-size:var(--wui-font-size,14px);color:inherit;white-space:nowrap;
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{display:block}:host([disabled]){cursor:not-allowed;opacity:.4}.item-inner{-webkit-user-select:none;user-select:none;height:32px;font-size:var(--wui-font-size,14px);color:inherit;white-space:nowrap;border-radius:2147483647px;outline:none;align-items:center;gap:8px;padding:0 8px;display:flex}.item-inner:focus-visible{color:var(--wui-color-on-accent,#fff);background:var(--wui-color-accent,#08f)}:host([active]) .item-inner{color:inherit;background:var(--wui-color-border,#0000001a)}@media (hover:hover) and (pointer:fine){:host(:hover) .item-inner{color:var(--wui-color-on-accent,#fff);background:var(--wui-color-accent,#08f)}}.item-label{min-width:0;color:inherit;text-overflow:ellipsis;flex:1;overflow:hidden}.item-suffix{opacity:.4;flex:none}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-item/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.item-inner {\n user-select: none;\n\n display: flex;\n gap: 8px;\n align-items: center;\n\n height: 32px;\n padding: 0 8px;\n border-radius: calc(infinity * 1px);\n\n font-size: var(--wui-font-size, 14px);\n color: inherit;\n white-space: nowrap;\n\n outline: none;\n
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/dropdown-item/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.item-inner {\n user-select: none;\n\n display: flex;\n gap: 8px;\n align-items: center;\n\n height: 32px;\n padding: 0 8px;\n border-radius: calc(infinity * 1px);\n\n font-size: var(--wui-font-size, 14px);\n color: inherit;\n white-space: nowrap;\n\n outline: none;\n}\n\n.item-inner:focus-visible {\n color: var(--wui-color-on-accent, #fff);\n background: var(--wui-color-accent, #08f);\n}\n\n:host([active]) .item-inner {\n color: inherit;\n background: var(--wui-color-border, rgb(0 0 0 / 0.1));\n}\n\n@media (hover: hover) and (pointer: fine) {\n :host(:hover) .item-inner {\n color: var(--wui-color-on-accent, #fff);\n background: var(--wui-color-accent, #08f);\n }\n}\n\n.item-label {\n overflow: hidden;\n flex: 1;\n\n min-width: 0;\n\n color: inherit;\n text-overflow: ellipsis;\n}\n\n.item-suffix {\n flex: none;\n opacity: 0.4;\n}\n"],"mappings":""}
|