@greypan/web-ui 6.1.0 → 6.2.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 +209 -80
- package/README.md +189 -60
- 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/components/autocomplete/index.js +54 -53
- 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/button/index.js +36 -32
- 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/collapse/index.d.ts +29 -1
- package/dist/components/collapse/index.js +92 -30
- package/dist/components/collapse/index.js.map +1 -1
- package/dist/components/collapse/style.js +1 -1
- package/dist/components/collapse/style.js.map +1 -1
- package/dist/components/context-menu/index.d.ts +1 -0
- package/dist/components/context-menu/index.js +80 -76
- package/dist/components/context-menu/index.js.map +1 -1
- package/dist/components/dialog/index.js +6 -2
- 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.js +24 -14
- 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 +1 -0
- package/dist/components/dropdown/index.js +65 -60
- package/dist/components/dropdown/index.js.map +1 -1
- package/dist/components/image-preview/index.d.ts +58 -0
- package/dist/components/image-preview/index.js +481 -0
- package/dist/components/image-preview/index.js.map +1 -0
- package/dist/components/image-preview/style.js +6 -0
- package/dist/components/image-preview/style.js.map +1 -0
- package/dist/components/index.d.ts +1 -0
- package/dist/components/index.js +20 -19
- package/dist/components/input/style.js +1 -1
- package/dist/components/input/style.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 +1 -1
- package/dist/components/layout/index.js +5 -3
- 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/popover/index.d.ts +1 -0
- package/dist/components/popover/index.js +53 -39
- package/dist/components/popover/index.js.map +1 -1
- package/dist/components/popover/style.js.map +1 -1
- package/dist/components/radio/index.js +18 -16
- 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/segmented/index.js +24 -22
- 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.js +17 -15
- package/dist/components/segmented-trigger/index.js.map +1 -1
- package/dist/components/segmented-trigger/style.js +1 -1
- package/dist/components/segmented-trigger/style.js.map +1 -1
- package/dist/components/select/index.js +43 -38
- 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.js +32 -30
- 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/svg-draw-lines/index.js +1 -1
- package/dist/components/svg-draw-lines/index.js.map +1 -1
- package/dist/components/switch/index.js +25 -23
- 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/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/index.js.map +1 -1
- package/dist/components/theme/style.js +1 -1
- package/dist/components/theme/style.js.map +1 -1
- package/dist/components/toast/manager.js +2 -2
- package/dist/components/toast/manager.js.map +1 -1
- package/dist/components/toast/style.js +1 -1
- package/dist/components/toast/style.js.map +1 -1
- package/dist/components/tooltip/index.d.ts +1 -0
- package/dist/components/tooltip/index.js +37 -33
- package/dist/components/tooltip/index.js.map +1 -1
- package/dist/components/tooltip/style.js.map +1 -1
- package/dist/icons/generated/radix-icons-reset.d.ts +6 -0
- package/dist/icons/generated/radix-icons-reset.js +10 -0
- package/dist/icons/generated/radix-icons-reset.js.map +1 -0
- package/dist/icons/index.d.ts +1 -0
- package/dist/icons/index.js +2 -1
- package/dist/shared/focus/pointer-focus.js +37 -0
- package/dist/shared/focus/pointer-focus.js.map +1 -0
- package/dist/shared/gesture/drag-gesture.js +53 -30
- package/dist/shared/gesture/drag-gesture.js.map +1 -1
- package/dist/shared/gesture/pinch-gesture.js +84 -0
- package/dist/shared/gesture/pinch-gesture.js.map +1 -0
- package/dist/shared/group-management/index.js.map +1 -1
- package/dist/shared/menu-portal/menu-portal.js.map +1 -1
- package/dist/shared/option-portal/combobox-shell.js +8 -6
- package/dist/shared/option-portal/combobox-shell.js.map +1 -1
- package/dist/shared/overlay/anchored-panel.js +33 -30
- package/dist/shared/overlay/anchored-panel.js.map +1 -1
- package/dist/shared/overlay/composition.js +57 -0
- package/dist/shared/overlay/composition.js.map +1 -0
- package/dist/shared/overlay/lifecycle.js +41 -0
- package/dist/shared/overlay/lifecycle.js.map +1 -0
- package/dist/shared/overlay/native-dialog-presence.js +15 -15
- package/dist/shared/overlay/native-dialog-presence.js.map +1 -1
- package/dist/shared/{theme → overlay}/overlay-root.js +1 -1
- package/dist/shared/overlay/overlay-root.js.map +1 -0
- package/dist/shared/overlay/overlay.js +43 -41
- package/dist/shared/overlay/overlay.js.map +1 -1
- package/dist/shared/overlay/portal.js +2 -2
- package/dist/shared/overlay/portal.js.map +1 -1
- package/dist/shared/overlay/positioning-generation.js +20 -0
- package/dist/shared/overlay/positioning-generation.js.map +1 -0
- package/dist/shared/{theme/theme-scope.js → overlay/theme-overlay-scope.js} +2 -2
- package/dist/shared/overlay/theme-overlay-scope.js.map +1 -0
- package/package.json +5 -5
- package/dist/shared/theme/overlay-root.js.map +0 -1
- package/dist/shared/theme/theme-scope.js.map +0 -1
package/dist/components/index.js
CHANGED
|
@@ -17,23 +17,24 @@ import { WebUiDialog as h } from "./dialog/index.js";
|
|
|
17
17
|
import { WebUiDrawer as g } from "./drawer/index.js";
|
|
18
18
|
import { WebUiDropdown as _ } from "./dropdown/index.js";
|
|
19
19
|
import { WebUiEmpty as v } from "./empty/index.js";
|
|
20
|
-
import {
|
|
21
|
-
import {
|
|
22
|
-
import {
|
|
23
|
-
import {
|
|
24
|
-
import {
|
|
25
|
-
import {
|
|
26
|
-
import {
|
|
27
|
-
import {
|
|
28
|
-
import {
|
|
29
|
-
import {
|
|
30
|
-
import {
|
|
31
|
-
import {
|
|
32
|
-
import {
|
|
33
|
-
import {
|
|
34
|
-
import {
|
|
20
|
+
import { imagePreview as y } from "./image-preview/index.js";
|
|
21
|
+
import { WebUiInput as b } from "./input/index.js";
|
|
22
|
+
import { WebUiInputNumber as x } from "./input-number/index.js";
|
|
23
|
+
import { WebUiLayout as S } from "./layout/index.js";
|
|
24
|
+
import { WebUiPopover as C } from "./popover/index.js";
|
|
25
|
+
import { WebUiRadio as w } from "./radio/index.js";
|
|
26
|
+
import { WebUiRadioGroup as T } from "./radio-group/index.js";
|
|
27
|
+
import { WebUiSegmented as E } from "./segmented/index.js";
|
|
28
|
+
import { WebUiSegmentedTrigger as D } from "./segmented-trigger/index.js";
|
|
29
|
+
import { WebUiSelect as O } from "./select/index.js";
|
|
30
|
+
import { WebUiSlider as k } from "./slider/index.js";
|
|
31
|
+
import { WebUiSpinner as A } from "./spinner/index.js";
|
|
32
|
+
import { WebUiTextarea as j } from "./textarea/index.js";
|
|
33
|
+
import { WebUiTheme as M } from "./theme/index.js";
|
|
34
|
+
import { toast as N } from "./toast/manager.js";
|
|
35
|
+
import { WebUiToast as P } from "./toast/toast.js";
|
|
35
36
|
import "./toast/index.js";
|
|
36
|
-
import { WebUiSvgDrawLines as
|
|
37
|
-
import { WebUiSwitch as
|
|
38
|
-
import { WebUiTooltip as
|
|
39
|
-
export { n as WebUiAutocomplete, r as WebUiAvatar, a as WebUiBackTop, o as WebUiBadge, i as WebUiButton, s as WebUiButtonGroup, c as WebUiCheckbox, l as WebUiCheckboxGroup, u as WebUiCollapse, m as WebUiContextMenu, h as WebUiDialog, g as WebUiDrawer, _ as WebUiDropdown, d as WebUiDropdownDivider, f as WebUiDropdownHeader, p as WebUiDropdownItem, v as WebUiEmpty, e as WebUiIcon,
|
|
37
|
+
import { WebUiSvgDrawLines as F } from "./svg-draw-lines/index.js";
|
|
38
|
+
import { WebUiSwitch as I } from "./switch/index.js";
|
|
39
|
+
import { WebUiTooltip as L } from "./tooltip/index.js";
|
|
40
|
+
export { n as WebUiAutocomplete, r as WebUiAvatar, a as WebUiBackTop, o as WebUiBadge, i as WebUiButton, s as WebUiButtonGroup, c as WebUiCheckbox, l as WebUiCheckboxGroup, u as WebUiCollapse, m as WebUiContextMenu, h as WebUiDialog, g as WebUiDrawer, _ as WebUiDropdown, d as WebUiDropdownDivider, f as WebUiDropdownHeader, p as WebUiDropdownItem, v as WebUiEmpty, e as WebUiIcon, b as WebUiInput, x as WebUiInputNumber, S as WebUiLayout, t as WebUiOption, C as WebUiPopover, w as WebUiRadio, T as WebUiRadioGroup, E as WebUiSegmented, D as WebUiSegmentedTrigger, O as WebUiSelect, k as WebUiSlider, A as WebUiSpinner, F as WebUiSvgDrawLines, I as WebUiSwitch, j as WebUiTextarea, M as WebUiTheme, P as WebUiToast, L as WebUiTooltip, y as imagePreview, N as toast };
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/input/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{--wui-input-clear-color:var(--wui-color-text-tertiary,color-mix(in srgb, var(--wui-color-text) 35%, transparent));display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-input-inner{--wui-glass-corner-radius:calc(var(--wui-control-size,36px) / 2);box-sizing:border-box;width:var(--wui-input-width,200px);min-width:var(--wui-control-size,36px);height:var(--wui-control-size,36px);border-radius:var(--wui-radius-control,calc(infinity * 1px));align-items:center;gap:8px;padding:0 12px;display:flex;position:relative}:host([full]){display:block}:host([full]) .wui-input-inner{width:100%}:host([borderless]) .wui-input-inner{-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;background:0 0;border:none}:host([borderless]) .wui-input-inner:before{content:none}.wui-input-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-input-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#08f), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,#08f6)}input{min-width:0;height:100%;font:inherit;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);background:0 0;border:none;outline:none;flex:1;margin:0;padding:0}input::placeholder{color:var(--wui-color-text-secondary,#6a6a6a)}input:disabled{cursor:not-allowed;opacity:.4}slot{font-size:var(--wui-font-size,14px);color:var(--wui-color-text-secondary,#6a6a6a);flex-shrink:0;align-items:center;display:flex}slot.empty{display:none}.wui-input-inner web-ui-button[variant=ghost]{--wui-button-color:var(--wui-input-clear-color);margin-left:-8px;margin-right:-4px}";
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{--wui-input-clear-color:var(--wui-color-text-tertiary,color-mix(in srgb, var(--wui-color-text) 35%, transparent));display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-input-inner{--wui-glass-corner-radius:calc(var(--wui-control-size,36px) / 2);box-sizing:border-box;width:var(--wui-input-width,200px);min-width:var(--wui-control-size,36px);height:var(--wui-control-size,36px);border-radius:var(--wui-radius-control,calc(infinity * 1px));align-items:center;gap:8px;padding:0 12px;display:flex;position:relative}:host([full]){display:block}:host([full]) .wui-input-inner{width:100%}:host([borderless]) .wui-input-inner{-webkit-backdrop-filter:none;backdrop-filter:none;box-shadow:none;background:0 0;border:none}:host([borderless]) .wui-input-inner:before{content:none}.wui-input-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-input-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#08f), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,#08f6)}input{min-width:0;height:100%;font:inherit;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);background:0 0;border:none;outline:none;flex:1;margin:0;padding:0}input::placeholder{color:var(--wui-color-text-secondary,#6a6a6a)}input:disabled{cursor:not-allowed;opacity:.4}@media (pointer:coarse){input{font-size:max(16px, var(--wui-font-size,14px))}}slot{font-size:var(--wui-font-size,14px);color:var(--wui-color-text-secondary,#6a6a6a);flex-shrink:0;align-items:center;display:flex}slot.empty{display:none}.wui-input-inner web-ui-button[variant=ghost]{--wui-button-color:var(--wui-input-clear-color);margin-left:-8px;margin-right:-4px}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/input/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-input-clear-color: var(--wui-color-text-tertiary, color-mix(in srgb, var(--wui-color-text) 35%, transparent));\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-input-inner {\n --wui-glass-corner-radius: calc(var(--wui-control-size, 36px) / 2);\n position: relative;\n\n display: flex;\n gap: 8px;\n align-items: center;\n\n box-sizing: border-box;\n width: var(--wui-input-width, 200px);\n min-width: var(--wui-control-size, 36px);\n height: var(--wui-control-size, 36px);\n padding: 0 12px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\n:host([full]) {\n display: block;\n}\n\n:host([full]) .wui-input-inner {\n width: 100%;\n}\n\n/* ===== Borderless ===== */\n\n:host([borderless]) .wui-input-inner {\n border: none;\n\n background: transparent;\n\n /* stylelint-disable-next-line property-no-vendor-prefix */\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n box-shadow: none;\n}\n\n/* borderless 必须移除 glass 描边环(.wui-glass::before 底环 + 对角光泽),\n 否则无框后仍残留 1px 胶囊描边 */\n:host([borderless]) .wui-input-inner::before {\n content: none;\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-input-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-input-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #08f),\n 0 0 0 var(--wui-focus-ring-width, 3px) var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n}\n\n/* ===== Input ===== */\n\ninput {\n flex: 1;\n\n min-width: 0;\n height: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n outline: none;\n\n &::placeholder {\n color: var(--wui-color-text-secondary, #6a6a6a);\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n/* ===== Prefix / Suffix ===== */\n\nslot {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text-secondary, #6a6a6a);\n}\n\n/* slot 无 slotted 内容时折叠 */\nslot.empty {\n display: none;\n}\n\n/* ===== Clear button ===== */\n\n/* 复用 web-ui-button icon ghost:仅覆盖颜色为 faint,保持弱意图清除图标观感;\n 负 margin-left 抵消容器 flex gap,让清除按钮紧贴输入文本\n 负 margin-right 抵消 button padding,让清除按钮右侧 spacing 视觉上协调 */\n.wui-input-inner web-ui-button[variant='ghost'] {\n --wui-button-color: var(--wui-input-clear-color);\n margin-right: -4px;\n margin-left: -8px;\n}\n\n/* ===== Disabled ===== */\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/input/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-input-clear-color: var(--wui-color-text-tertiary, color-mix(in srgb, var(--wui-color-text) 35%, transparent));\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-input-inner {\n --wui-glass-corner-radius: calc(var(--wui-control-size, 36px) / 2);\n position: relative;\n\n display: flex;\n gap: 8px;\n align-items: center;\n\n box-sizing: border-box;\n width: var(--wui-input-width, 200px);\n min-width: var(--wui-control-size, 36px);\n height: var(--wui-control-size, 36px);\n padding: 0 12px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\n:host([full]) {\n display: block;\n}\n\n:host([full]) .wui-input-inner {\n width: 100%;\n}\n\n/* ===== Borderless ===== */\n\n:host([borderless]) .wui-input-inner {\n border: none;\n\n background: transparent;\n\n /* stylelint-disable-next-line property-no-vendor-prefix */\n -webkit-backdrop-filter: none;\n backdrop-filter: none;\n box-shadow: none;\n}\n\n/* borderless 必须移除 glass 描边环(.wui-glass::before 底环 + 对角光泽),\n 否则无框后仍残留 1px 胶囊描边 */\n:host([borderless]) .wui-input-inner::before {\n content: none;\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-input-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-input-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #08f),\n 0 0 0 var(--wui-focus-ring-width, 3px) var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n}\n\n/* ===== Input ===== */\n\ninput {\n flex: 1;\n\n min-width: 0;\n height: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text, #1b1b1b);\n\n background: transparent;\n outline: none;\n\n &::placeholder {\n color: var(--wui-color-text-secondary, #6a6a6a);\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n/* Coarse pointers include iOS Safari, where a sub-16px font triggers focus zoom. */\n@media (pointer: coarse) {\n input {\n font-size: max(16px, var(--wui-font-size, 14px));\n }\n}\n\n/* ===== Prefix / Suffix ===== */\n\nslot {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text-secondary, #6a6a6a);\n}\n\n/* slot 无 slotted 内容时折叠 */\nslot.empty {\n display: none;\n}\n\n/* ===== Clear button ===== */\n\n/* 复用 web-ui-button icon ghost:仅覆盖颜色为 faint,保持弱意图清除图标观感;\n 负 margin-left 抵消容器 flex gap,让清除按钮紧贴输入文本\n 负 margin-right 抵消 button padding,让清除按钮右侧 spacing 视觉上协调 */\n.wui-input-inner web-ui-button[variant='ghost'] {\n --wui-button-color: var(--wui-input-clear-color);\n margin-right: -4px;\n margin-left: -8px;\n}\n\n/* ===== Disabled ===== */\n"],"mappings":""}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/input-number/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-input-inner{--wui-glass-corner-radius:calc(var(--wui-control-size,36px) / 2);box-sizing:border-box;width:var(--wui-input-width,200px);min-width:var(--wui-control-size,36px);height:var(--wui-control-size,36px);border-radius:var(--wui-radius-control,calc(infinity * 1px));align-items:center;gap:4px;padding:0 4px;display:flex;position:relative}input{min-width:48px;height:100%;font:inherit;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);text-align:center;background:0 0;border:none;outline:none;flex:1;margin:0;padding:0}input::-webkit-inner-spin-button{appearance:none;margin:0}input::-webkit-outer-spin-button{appearance:none;margin:0}input[type=number]{appearance:textfield}input:disabled{cursor:not-allowed;opacity:.4}.wui-input-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-input-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#08f), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,#08f6)}.num-btn{cursor:default;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:30px;height:30px;color:var(--wui-color-text-secondary,#6a6a6a);background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.num-btn:disabled{cursor:not-allowed;opacity:.4}.num-btn:focus-visible{outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,#08f6);outline-offset:2px}@media (hover:hover) and (pointer:fine){.num-btn:hover:not(:disabled){background:var(--wui-color-state-layer-hover,color-mix(in srgb, var(--wui-color-text,#1b1b1b) 6%, transparent))}}.num-btn:active:not(:disabled){background:var(--wui-color-state-layer-active,color-mix(in srgb, var(--wui-color-text,#1b1b1b) 15%, transparent))}";
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-input-inner{--wui-glass-corner-radius:calc(var(--wui-control-size,36px) / 2);box-sizing:border-box;width:var(--wui-input-width,200px);min-width:var(--wui-control-size,36px);height:var(--wui-control-size,36px);border-radius:var(--wui-radius-control,calc(infinity * 1px));align-items:center;gap:4px;padding:0 4px;display:flex;position:relative}input{min-width:48px;height:100%;font:inherit;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);text-align:center;background:0 0;border:none;outline:none;flex:1;margin:0;padding:0}input::-webkit-inner-spin-button{appearance:none;margin:0}input::-webkit-outer-spin-button{appearance:none;margin:0}input[type=number]{appearance:textfield}input:disabled{cursor:not-allowed;opacity:.4}@media (pointer:coarse){input{font-size:max(16px, var(--wui-font-size,14px))}}.wui-input-inner:after{pointer-events:none;content:\"\";border-radius:inherit;box-shadow:inset 0 0 0 1px transparent, 0 0 0 var(--wui-focus-ring-width,3px) transparent;transition:box-shadow var(--wui-duration-focus,.2s);position:absolute;inset:0}:host([focused]) .wui-input-inner:after{box-shadow:inset 0 0 0 1px var(--wui-color-accent,#08f), 0 0 0 var(--wui-focus-ring-width,3px) var(--wui-color-focus-ring,#08f6)}.num-btn{cursor:default;border-radius:var(--wui-radius-control,calc(infinity * 1px));width:30px;height:30px;color:var(--wui-color-text-secondary,#6a6a6a);background:0 0;border:none;flex-shrink:0;justify-content:center;align-items:center;padding:0;display:flex}.num-btn:disabled{cursor:not-allowed;opacity:.4}.num-btn:focus-visible{outline:var(--wui-focus-ring-width,3px) solid var(--wui-color-focus-ring,#08f6);outline-offset:2px}@media (hover:hover) and (pointer:fine){.num-btn:hover:not(:disabled){background:var(--wui-color-state-layer-hover,color-mix(in srgb, var(--wui-color-text,#1b1b1b) 6%, transparent))}}.num-btn:active:not(:disabled){background:var(--wui-color-state-layer-active,color-mix(in srgb, var(--wui-color-text,#1b1b1b) 15%, transparent))}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/input-number/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-input-inner {\n --wui-glass-corner-radius: calc(var(--wui-control-size, 36px) / 2);\n position: relative;\n\n display: flex;\n gap: 4px;\n align-items: center;\n\n box-sizing: border-box;\n width: var(--wui-input-width, 200px);\n min-width: var(--wui-control-size, 36px);\n height: var(--wui-control-size, 36px);\n padding: 0 4px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\ninput {\n flex: 1;\n\n min-width: 48px;\n height: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text, #1b1b1b);\n text-align: center;\n\n background: transparent;\n outline: none;\n\n &::-webkit-inner-spin-button,\n &::-webkit-outer-spin-button {\n margin: 0;\n appearance: none;\n }\n\n &[type='number'] {\n appearance: textfield;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-input-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-input-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #08f),\n 0 0 0 var(--wui-focus-ring-width, 3px) var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n}\n\n.num-btn {\n cursor: default;\n\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 30px;\n height: 30px;\n padding: 0;\n border: none;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n color: var(--wui-color-text-secondary, #6a6a6a);\n\n background: transparent;\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n &:focus-visible {\n outline: var(--wui-focus-ring-width, 3px) solid var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n@media (hover: hover) and (pointer: fine) {\n .num-btn:hover:not(:disabled) {\n background: var(--wui-color-state-layer-hover, color-mix(in srgb, var(--wui-color-text, #1b1b1b) 6%, transparent));\n }\n}\n\n.num-btn:active:not(:disabled) {\n background: var(--wui-color-state-layer-active, color-mix(in srgb, var(--wui-color-text, #1b1b1b) 15%, transparent));\n}\n"],"mappings":""}
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/input-number/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-input-inner {\n --wui-glass-corner-radius: calc(var(--wui-control-size, 36px) / 2);\n position: relative;\n\n display: flex;\n gap: 4px;\n align-items: center;\n\n box-sizing: border-box;\n width: var(--wui-input-width, 200px);\n min-width: var(--wui-control-size, 36px);\n height: var(--wui-control-size, 36px);\n padding: 0 4px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\ninput {\n flex: 1;\n\n min-width: 48px;\n height: 100%;\n margin: 0;\n padding: 0;\n border: none;\n\n /* 表单控件不继承字体;显式继承以对齐宿主排版,避免 UA 默认字体差异 */\n font: inherit;\n font-size: var(--wui-font-size, 14px);\n color: var(--wui-color-text, #1b1b1b);\n text-align: center;\n\n background: transparent;\n outline: none;\n\n &::-webkit-inner-spin-button,\n &::-webkit-outer-spin-button {\n margin: 0;\n appearance: none;\n }\n\n &[type='number'] {\n appearance: textfield;\n }\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n}\n\n/* Coarse pointers include iOS Safari, where a sub-16px font triggers focus zoom. */\n@media (pointer: coarse) {\n input {\n font-size: max(16px, var(--wui-font-size, 14px));\n }\n}\n\n/* Focus ring 独立成 ::after 层:base/focused 都是显式 box-shadow,保证所有变体都能过渡 */\n.wui-input-inner::after {\n pointer-events: none;\n content: '';\n\n position: absolute;\n inset: 0;\n\n border-radius: inherit;\n\n box-shadow:\n inset 0 0 0 1px transparent,\n 0 0 0 var(--wui-focus-ring-width, 3px) transparent;\n\n transition: box-shadow var(--wui-duration-focus, 200ms);\n}\n\n:host([focused]) .wui-input-inner::after {\n box-shadow:\n inset 0 0 0 1px var(--wui-color-accent, #08f),\n 0 0 0 var(--wui-focus-ring-width, 3px) var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n}\n\n.num-btn {\n cursor: default;\n\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 30px;\n height: 30px;\n padding: 0;\n border: none;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n color: var(--wui-color-text-secondary, #6a6a6a);\n\n background: transparent;\n\n &:disabled {\n cursor: not-allowed;\n opacity: 0.4;\n }\n\n &:focus-visible {\n outline: var(--wui-focus-ring-width, 3px) solid var(--wui-color-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n@media (hover: hover) and (pointer: fine) {\n .num-btn:hover:not(:disabled) {\n background: var(--wui-color-state-layer-hover, color-mix(in srgb, var(--wui-color-text, #1b1b1b) 6%, transparent));\n }\n}\n\n.num-btn:active:not(:disabled) {\n background: var(--wui-color-state-layer-active, color-mix(in srgb, var(--wui-color-text, #1b1b1b) 15%, transparent));\n}\n"],"mappings":""}
|
|
@@ -32,7 +32,7 @@ export declare class WebUiLayout extends LitElement {
|
|
|
32
32
|
/** 拖拽调宽的上限(px,其他单位不解析);始终受内置 50vw 硬上限钳制。 */
|
|
33
33
|
sidebarMaxWidth: string;
|
|
34
34
|
/**
|
|
35
|
-
* 移动端
|
|
35
|
+
* 移动端 web-ui-drawer 的瞬时可见性。
|
|
36
36
|
*
|
|
37
37
|
* Drawer Toggle、Escape 和遮罩关闭都通过 `sidebar-open-change` 请求更新它;它不会改变桌面端的折叠偏好。
|
|
38
38
|
* 因而不与 `sidebarCollapsed` 合并,避免响应式切换时将一次移动端交互误写为桌面端布局选择。
|
|
@@ -221,10 +221,12 @@ var _ = class extends c {
|
|
|
221
221
|
draggable
|
|
222
222
|
dialog-label="主导航"
|
|
223
223
|
@open-change="${this._onDrawerChange}"
|
|
224
|
-
style="${g({
|
|
225
|
-
|
|
224
|
+
style="${g({
|
|
225
|
+
"--wui-drawer-width": this.sidebarWidth,
|
|
226
|
+
"--wui-drawer-radius": "var(--wui-layout-sidebar-radius, var(--wui-radius-overlay, 28px))"
|
|
227
|
+
})}"
|
|
226
228
|
>
|
|
227
|
-
|
|
229
|
+
${a}
|
|
228
230
|
</web-ui-drawer>
|
|
229
231
|
`;
|
|
230
232
|
return l`
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import { html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport '@/components/icon'\nimport '@/components/button'\nimport '@/components/drawer'\nimport { radixIconsPanelLeftMinimized } from '@/icons'\nimport { attachDragGesture, clamp, type DragGestureHandle } from '@/shared/gesture'\nimport { defineVisibleAreaTracker, VisibleAreaController } from '@/shared/visible-area'\n\nimport style from './style.css?inline'\n\n/**\n * Layout 组件 - 提供页面布局框架,支持可折叠侧边栏\n *\n * @fires sidebar-collapsed-change - 桌面端侧边栏折叠状态变更请求时触发\n * @fires sidebar-open-change - 移动端侧边栏 Drawer 开关请求时触发\n * @fires sidebar-width-change - 桌面端侧边栏拖拽调宽结束时触发,携带最终宽度(受控请求)\n * @slot banner - 顶部 Banner 区域(全宽,随页面滚动,可选)\n * @slot header - 内容区顶部 header(sticky)\n * @slot sidebar - 侧边栏内容;Consumer 决定其内部固定区域与滚动容器\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\n */\n@customElement('web-ui-layout')\nexport class WebUiLayout extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n /**\n * 桌面端 Sidebar 的持久布局偏好。\n *\n * `true` 只会将桌面 Sidebar 收窄到 `collapsedWidth`,不会影响移动端 Drawer。\n * 它不与 `sidebarOpen` 合并:前者是跨视口保留的密度选择,后者是移动端瞬时的 modal 可见性。\n */\n @property({ type: Boolean, attribute: 'sidebar-collapsed', reflect: true })\n sidebarCollapsed = false\n\n @property({ type: String, attribute: 'sidebar-width', reflect: true })\n sidebarWidth = '240px'\n\n @property({ type: String, attribute: 'collapsed-width', reflect: true })\n collapsedWidth = '68px'\n\n /** 启用桌面端 Sidebar 拖拽调宽(折叠态隐藏 handle)。 */\n @property({ type: Boolean, attribute: 'sidebar-resizable', reflect: true })\n sidebarResizable = false\n\n /**\n * 拖拽调宽的下限(px,其他单位不解析);未设置时回退为 `collapsedWidth`,\n * 防止拖到不可用宽度。上限 `sidebar-max-width` 受内置 50vw 硬上限钳制。\n */\n @property({ type: String, attribute: 'sidebar-min-width', reflect: true })\n sidebarMinWidth = ''\n\n /** 拖拽调宽的上限(px,其他单位不解析);始终受内置 50vw 硬上限钳制。 */\n @property({ type: String, attribute: 'sidebar-max-width', reflect: true })\n sidebarMaxWidth = ''\n\n /**\n * 移动端 headless Drawer 的瞬时可见性。\n *\n * Drawer Toggle、Escape 和遮罩关闭都通过 `sidebar-open-change` 请求更新它;它不会改变桌面端的折叠偏好。\n * 因而不与 `sidebarCollapsed` 合并,避免响应式切换时将一次移动端交互误写为桌面端布局选择。\n */\n @property({ type: Boolean, attribute: 'sidebar-open', reflect: true })\n sidebarOpen = false\n\n /**\n * 启用 header 晕染效果\n */\n @property({ type: Boolean, attribute: 'header-glow', reflect: true })\n headerGlow = false\n\n @state() private _isMobile = false\n\n private readonly _visibleBanner = defineVisibleAreaTracker({\n onVisibleAreaChange: area => {\n this.style.setProperty('--wui-layout-visible-banner-height', `${area.height}px`)\n }\n }).make()\n\n private readonly _visibleBannerController = new VisibleAreaController(this, this._visibleBanner)\n\n private _toggleSidebar() {\n if (this._isMobile) {\n this._emitSidebarOpenChange(!this.sidebarOpen)\n return\n }\n\n this._emitSidebarCollapsedChange(!this.sidebarCollapsed)\n }\n\n private _emitSidebarCollapsedChange(collapsed: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-collapsed-change', {\n detail: { collapsed },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _emitSidebarOpenChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _onDrawerChange(e: CustomEvent<{ open: boolean }>) {\n e.stopPropagation()\n this._emitSidebarOpenChange(e.detail.open)\n }\n\n // ===== 桌面端 Sidebar 拖拽调宽 =====\n\n private _resizeGesture: DragGestureHandle | null = null\n private _resizeStartWidth = 0\n // 拖拽中的临时宽度;由 render 的 styleMap 统一写入,避免与 Lit 样式管理竞争。\n @state() private _resizeWidth: string | null = null\n\n private _parsePx(value: string, fallback: number): number {\n const parsed = Number.parseFloat(value)\n return Number.isFinite(parsed) ? parsed : fallback\n }\n\n private _resolveSidebarMinWidth(): number {\n // 未设置时回退为折叠宽度,防止拖到不可用宽度。\n return this.sidebarMinWidth\n ? this._parsePx(this.sidebarMinWidth, this._parsePx(this.collapsedWidth, 68))\n : this._parsePx(this.collapsedWidth, 68)\n }\n\n private _resolveSidebarMaxWidth(): number {\n // 组件内置硬上限:Sidebar 最多占视口一半(无需调用方设置)。\n // 调用方 sidebar-max-width 在该上限内生效,防止拖拽占据整个视口。\n const halfViewport = Math.floor(window.innerWidth / 2)\n const configured = this.sidebarMaxWidth ? this._parsePx(this.sidebarMaxWidth, halfViewport) : halfViewport\n const upper = Math.min(configured, halfViewport)\n return Math.max(upper, this._resolveSidebarMinWidth())\n }\n\n private _isResizing(): boolean {\n return this._resizeGesture?.isDragging() ?? false\n }\n\n private _handleResizePointerDown(e: PointerEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._isMobile || this._isResizing()) return\n // jsdom 等环境的 PointerEvent 可能缺失 isPrimary,仅在明确为 false(多点触控副指针)时拒绝。\n if (e.isPrimary === false) return\n\n const aside = this.renderRoot.querySelector('aside')\n if (!aside) return\n\n // 拖拽起点以 computed width 为准,并丢弃键盘未提交的调整(Enter 才提交、\n // Escape 撤回;抓取隐式放弃),保证零位移松手不会派发陈旧宽度。\n this._resizeStartWidth = this._parsePx(window.getComputedStyle(aside).width, this._parsePx(this.sidebarWidth, 240))\n this._resizeWidth = null\n aside.classList.add('is-resizing')\n\n this._resizeGesture = attachDragGesture(e, {\n axis: 'x',\n onMove: info => {\n // 向右拖增宽、向左拖收窄(handle 在右缘);钳制在 [min, max] 与视口内。\n const next = this._resizeStartWidth + info.deltaX\n const clamped = clamp(next, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth())\n this._resizeWidth = `${Math.round(clamped)}px`\n },\n onEnd: () => {\n // 零位移松手(点击而非拖拽)不视为调宽动作:按 cancel 语义静默收尾。\n if (this._resizeWidth === null) {\n this._resetActiveResize()\n return\n }\n const width = this._resizeWidth\n this._resetActiveResize()\n // 受控契约:松手派发请求并交还 prop 管辖,由 Consumer 回写 sidebar-width。\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', {\n detail: { width },\n bubbles: true,\n composed: true\n })\n )\n },\n onCancel: () => {\n this._resetActiveResize()\n }\n })\n }\n\n // 终结进行中的拖拽:清手势状态、移除跟手 class,宽度过渡回 prop 管辖,不派发事件。\n // pointer cancel 与视口跨越移动端断点(handle 随桌面 layout 卸载)共用此收尾。\n private _resetActiveResize() {\n this._resizeGesture?.destroy()\n this._resizeGesture = null\n this.renderRoot.querySelector('aside')?.classList.remove('is-resizing')\n this._resizeWidth = null\n }\n\n // 键盘调宽(WAI-ARIA splitter 模式):方向键步进 16px(Shift 加速 64px)调整\n // 临时宽度,Enter 以受控请求派发 `sidebar-width-change`(与指针松手同语义),\n // Escape 撤回临时宽度交还 prop 管辖,Home/End 直接到 min/max。\n private _handleResizeKeydown(e: KeyboardEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._isMobile || this._isResizing()) return\n\n const min = this._resolveSidebarMinWidth()\n const max = this._resolveSidebarMaxWidth()\n const aside = this.renderRoot.querySelector('aside')\n const current = this._resizeWidth\n ? this._parsePx(this._resizeWidth, min)\n : this._parsePx(aside ? window.getComputedStyle(aside).width : this.sidebarWidth, min)\n const step = e.shiftKey ? 64 : 16\n let next: number | null = null\n\n switch (e.key) {\n case 'ArrowLeft':\n next = Math.max(min, current - step)\n break\n case 'ArrowRight':\n next = Math.min(max, current + step)\n break\n case 'Home':\n next = min\n break\n case 'End':\n next = max\n break\n case 'Enter':\n // 无未提交调整时不派发(焦点停留时的回车噪声)\n if (this._resizeWidth !== null) {\n const width = this._resizeWidth\n this._resizeWidth = null\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', { detail: { width }, bubbles: true, composed: true })\n )\n }\n e.preventDefault()\n return\n case 'Escape':\n this._resizeWidth = null\n e.preventDefault()\n return\n default:\n return\n }\n e.preventDefault()\n this._resizeWidth = `${Math.round(next)}px`\n }\n\n private _checkMobile() {\n const nextMobile = window.innerWidth <= 640\n // 视口跨越移动端断点会卸载桌面 layout(handle 随之消失);必须先终结进行中的\n // 拖拽,否则 _resizePointerId 悬挂,切回桌面后所有新的拖拽在入口被拦截。\n if (nextMobile !== this._isMobile && this._isResizing()) this._resetActiveResize()\n this._isMobile = nextMobile\n }\n\n private _resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\n private _handleResize = () => {\n if (this._resizeTimeout !== null) clearTimeout(this._resizeTimeout)\n this._resizeTimeout = setTimeout(() => {\n this._checkMobile()\n this._resizeTimeout = null\n }, 100)\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._checkMobile()\n window.addEventListener('resize', this._handleResize)\n void this.updateComplete.then(() => {\n if (this.isConnected) this._syncBannerPresence()\n })\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._resetActiveResize()\n window.removeEventListener('resize', this._handleResize)\n if (this._resizeTimeout !== null) clearTimeout(this._resizeTimeout)\n this._resizeTimeout = null\n }\n\n override firstUpdated() {\n this._syncBannerPresence()\n }\n\n private _onBannerSlotChange() {\n this._syncBannerPresence()\n }\n\n private _syncBannerPresence() {\n const bannerSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"banner\"]')\n const hasBanner =\n bannerSlot\n ?.assignedNodes()\n .some(\n node =>\n node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim())\n ) ?? false\n const banner = this.shadowRoot?.querySelector<HTMLElement>('.layout-banner') ?? null\n this._visibleBanner.setTarget(hasBanner ? banner : null)\n }\n\n override render() {\n const isSidebarOpen = !this.sidebarCollapsed\n // 拖拽中的临时宽度优先于 prop 宽度;移动端宽度由 Drawer 内部变量管理。\n const sidebarStyle = this._isMobile\n ? {}\n : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }\n\n const toggleLabel = isSidebarOpen ? '折叠侧边栏' : '展开侧边栏'\n\n const desktopToggle = html`\n <web-ui-button\n class=\"sidebar-toggle\"\n icon\n variant=\"secondary\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"${toggleLabel}\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n `\n\n const sidebarViewport = html`\n <div class=\"sidebar-viewport\">\n <slot name=\"sidebar\"></slot>\n </div>\n `\n\n // 桌面端布局\n const desktopLayout = html`\n <div class=\"layout-body\">\n <div class=\"sidebar-wrapper\">\n <aside\n class=\"${classMap({\n collapsed: !isSidebarOpen\n })}\"\n style=\"${styleMap(sidebarStyle)}\"\n >\n <div class=\"aside-panel wui-glass\">\n ${sidebarViewport}\n <div class=\"sidebar-toggle-area\">${desktopToggle}</div>\n </div>\n ${\n this.sidebarResizable && !this.sidebarCollapsed\n ? html`\n <div\n class=\"sidebar-resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n aria-label=\"调整侧边栏宽度\"\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n <div class=\"layout-content\">\n <header>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n </div>\n `\n\n // 移动端布局\n const mobileLayout = html`\n <div class=\"layout-content\">\n <header>\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"打开导航菜单\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n dialog-label=\"主导航\"\n @open-change=\"${this._onDrawerChange}\"\n style=\"${styleMap({ '--wui-layout-mobile-sidebar-width': this.sidebarWidth })}\"\n headless\n >\n <div class=\"aside-panel wui-glass mobile-sidebar\">${sidebarViewport}</div>\n </web-ui-drawer>\n `\n\n return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${this._isMobile ? mobileLayout : desktopLayout}\n </div>\n `\n }\n\n declare readonly $events: {\n 'sidebar-collapsed-change': CustomEvent<{ collapsed: boolean }>\n 'sidebar-open-change': CustomEvent<{ open: boolean }>\n 'sidebar-width-change': CustomEvent<{ width: string }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-layout': WebUiLayout\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/components/layout/index.ts"],"sourcesContent":["import { html, LitElement, nothing, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { styleMap } from 'lit/directives/style-map.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport '@/components/icon'\nimport '@/components/button'\nimport '@/components/drawer'\nimport { radixIconsPanelLeftMinimized } from '@/icons'\nimport { attachDragGesture, clamp, type DragGestureHandle } from '@/shared/gesture'\nimport { defineVisibleAreaTracker, VisibleAreaController } from '@/shared/visible-area'\n\nimport style from './style.css?inline'\n\n/**\n * Layout 组件 - 提供页面布局框架,支持可折叠侧边栏\n *\n * @fires sidebar-collapsed-change - 桌面端侧边栏折叠状态变更请求时触发\n * @fires sidebar-open-change - 移动端侧边栏 Drawer 开关请求时触发\n * @fires sidebar-width-change - 桌面端侧边栏拖拽调宽结束时触发,携带最终宽度(受控请求)\n * @slot banner - 顶部 Banner 区域(全宽,随页面滚动,可选)\n * @slot header - 内容区顶部 header(sticky)\n * @slot sidebar - 侧边栏内容;Consumer 决定其内部固定区域与滚动容器\n * @slot tabbar - 底部 tabbar 区域\n * @slot default - 主内容区\n */\n@customElement('web-ui-layout')\nexport class WebUiLayout extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n /**\n * 桌面端 Sidebar 的持久布局偏好。\n *\n * `true` 只会将桌面 Sidebar 收窄到 `collapsedWidth`,不会影响移动端 Drawer。\n * 它不与 `sidebarOpen` 合并:前者是跨视口保留的密度选择,后者是移动端瞬时的 modal 可见性。\n */\n @property({ type: Boolean, attribute: 'sidebar-collapsed', reflect: true })\n sidebarCollapsed = false\n\n @property({ type: String, attribute: 'sidebar-width', reflect: true })\n sidebarWidth = '240px'\n\n @property({ type: String, attribute: 'collapsed-width', reflect: true })\n collapsedWidth = '68px'\n\n /** 启用桌面端 Sidebar 拖拽调宽(折叠态隐藏 handle)。 */\n @property({ type: Boolean, attribute: 'sidebar-resizable', reflect: true })\n sidebarResizable = false\n\n /**\n * 拖拽调宽的下限(px,其他单位不解析);未设置时回退为 `collapsedWidth`,\n * 防止拖到不可用宽度。上限 `sidebar-max-width` 受内置 50vw 硬上限钳制。\n */\n @property({ type: String, attribute: 'sidebar-min-width', reflect: true })\n sidebarMinWidth = ''\n\n /** 拖拽调宽的上限(px,其他单位不解析);始终受内置 50vw 硬上限钳制。 */\n @property({ type: String, attribute: 'sidebar-max-width', reflect: true })\n sidebarMaxWidth = ''\n\n /**\n * 移动端 web-ui-drawer 的瞬时可见性。\n *\n * Drawer Toggle、Escape 和遮罩关闭都通过 `sidebar-open-change` 请求更新它;它不会改变桌面端的折叠偏好。\n * 因而不与 `sidebarCollapsed` 合并,避免响应式切换时将一次移动端交互误写为桌面端布局选择。\n */\n @property({ type: Boolean, attribute: 'sidebar-open', reflect: true })\n sidebarOpen = false\n\n /**\n * 启用 header 晕染效果\n */\n @property({ type: Boolean, attribute: 'header-glow', reflect: true })\n headerGlow = false\n\n @state() private _isMobile = false\n\n private readonly _visibleBanner = defineVisibleAreaTracker({\n onVisibleAreaChange: area => {\n this.style.setProperty('--wui-layout-visible-banner-height', `${area.height}px`)\n }\n }).make()\n\n private readonly _visibleBannerController = new VisibleAreaController(this, this._visibleBanner)\n\n private _toggleSidebar() {\n if (this._isMobile) {\n this._emitSidebarOpenChange(!this.sidebarOpen)\n return\n }\n\n this._emitSidebarCollapsedChange(!this.sidebarCollapsed)\n }\n\n private _emitSidebarCollapsedChange(collapsed: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-collapsed-change', {\n detail: { collapsed },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _emitSidebarOpenChange(open: boolean) {\n this.dispatchEvent(\n new CustomEvent('sidebar-open-change', {\n detail: { open },\n bubbles: true,\n composed: true\n })\n )\n }\n\n private _onDrawerChange(e: CustomEvent<{ open: boolean }>) {\n e.stopPropagation()\n this._emitSidebarOpenChange(e.detail.open)\n }\n\n // ===== 桌面端 Sidebar 拖拽调宽 =====\n\n private _resizeGesture: DragGestureHandle | null = null\n private _resizeStartWidth = 0\n // 拖拽中的临时宽度;由 render 的 styleMap 统一写入,避免与 Lit 样式管理竞争。\n @state() private _resizeWidth: string | null = null\n\n private _parsePx(value: string, fallback: number): number {\n const parsed = Number.parseFloat(value)\n return Number.isFinite(parsed) ? parsed : fallback\n }\n\n private _resolveSidebarMinWidth(): number {\n // 未设置时回退为折叠宽度,防止拖到不可用宽度。\n return this.sidebarMinWidth\n ? this._parsePx(this.sidebarMinWidth, this._parsePx(this.collapsedWidth, 68))\n : this._parsePx(this.collapsedWidth, 68)\n }\n\n private _resolveSidebarMaxWidth(): number {\n // 组件内置硬上限:Sidebar 最多占视口一半(无需调用方设置)。\n // 调用方 sidebar-max-width 在该上限内生效,防止拖拽占据整个视口。\n const halfViewport = Math.floor(window.innerWidth / 2)\n const configured = this.sidebarMaxWidth ? this._parsePx(this.sidebarMaxWidth, halfViewport) : halfViewport\n const upper = Math.min(configured, halfViewport)\n return Math.max(upper, this._resolveSidebarMinWidth())\n }\n\n private _isResizing(): boolean {\n return this._resizeGesture?.isDragging() ?? false\n }\n\n private _handleResizePointerDown(e: PointerEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._isMobile || this._isResizing()) return\n // jsdom 等环境的 PointerEvent 可能缺失 isPrimary,仅在明确为 false(多点触控副指针)时拒绝。\n if (e.isPrimary === false) return\n\n const aside = this.renderRoot.querySelector('aside')\n if (!aside) return\n\n // 拖拽起点以 computed width 为准,并丢弃键盘未提交的调整(Enter 才提交、\n // Escape 撤回;抓取隐式放弃),保证零位移松手不会派发陈旧宽度。\n this._resizeStartWidth = this._parsePx(window.getComputedStyle(aside).width, this._parsePx(this.sidebarWidth, 240))\n this._resizeWidth = null\n aside.classList.add('is-resizing')\n\n this._resizeGesture = attachDragGesture(e, {\n axis: 'x',\n onMove: info => {\n // 向右拖增宽、向左拖收窄(handle 在右缘);钳制在 [min, max] 与视口内。\n const next = this._resizeStartWidth + info.deltaX\n const clamped = clamp(next, this._resolveSidebarMinWidth(), this._resolveSidebarMaxWidth())\n this._resizeWidth = `${Math.round(clamped)}px`\n },\n onEnd: () => {\n // 零位移松手(点击而非拖拽)不视为调宽动作:按 cancel 语义静默收尾。\n if (this._resizeWidth === null) {\n this._resetActiveResize()\n return\n }\n const width = this._resizeWidth\n this._resetActiveResize()\n // 受控契约:松手派发请求并交还 prop 管辖,由 Consumer 回写 sidebar-width。\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', {\n detail: { width },\n bubbles: true,\n composed: true\n })\n )\n },\n onCancel: () => {\n this._resetActiveResize()\n }\n })\n }\n\n // 终结进行中的拖拽:清手势状态、移除跟手 class,宽度过渡回 prop 管辖,不派发事件。\n // pointer cancel 与视口跨越移动端断点(handle 随桌面 layout 卸载)共用此收尾。\n private _resetActiveResize() {\n this._resizeGesture?.destroy()\n this._resizeGesture = null\n this.renderRoot.querySelector('aside')?.classList.remove('is-resizing')\n this._resizeWidth = null\n }\n\n // 键盘调宽(WAI-ARIA splitter 模式):方向键步进 16px(Shift 加速 64px)调整\n // 临时宽度,Enter 以受控请求派发 `sidebar-width-change`(与指针松手同语义),\n // Escape 撤回临时宽度交还 prop 管辖,Home/End 直接到 min/max。\n private _handleResizeKeydown(e: KeyboardEvent) {\n if (!this.sidebarResizable || this.sidebarCollapsed || this._isMobile || this._isResizing()) return\n\n const min = this._resolveSidebarMinWidth()\n const max = this._resolveSidebarMaxWidth()\n const aside = this.renderRoot.querySelector('aside')\n const current = this._resizeWidth\n ? this._parsePx(this._resizeWidth, min)\n : this._parsePx(aside ? window.getComputedStyle(aside).width : this.sidebarWidth, min)\n const step = e.shiftKey ? 64 : 16\n let next: number | null = null\n\n switch (e.key) {\n case 'ArrowLeft':\n next = Math.max(min, current - step)\n break\n case 'ArrowRight':\n next = Math.min(max, current + step)\n break\n case 'Home':\n next = min\n break\n case 'End':\n next = max\n break\n case 'Enter':\n // 无未提交调整时不派发(焦点停留时的回车噪声)\n if (this._resizeWidth !== null) {\n const width = this._resizeWidth\n this._resizeWidth = null\n this.dispatchEvent(\n new CustomEvent('sidebar-width-change', { detail: { width }, bubbles: true, composed: true })\n )\n }\n e.preventDefault()\n return\n case 'Escape':\n this._resizeWidth = null\n e.preventDefault()\n return\n default:\n return\n }\n e.preventDefault()\n this._resizeWidth = `${Math.round(next)}px`\n }\n\n private _checkMobile() {\n const nextMobile = window.innerWidth <= 640\n // 视口跨越移动端断点会卸载桌面 layout(handle 随之消失);必须先终结进行中的\n // 拖拽,否则 _resizePointerId 悬挂,切回桌面后所有新的拖拽在入口被拦截。\n if (nextMobile !== this._isMobile && this._isResizing()) this._resetActiveResize()\n this._isMobile = nextMobile\n }\n\n private _resizeTimeout: ReturnType<typeof setTimeout> | null = null\n\n private _handleResize = () => {\n if (this._resizeTimeout !== null) clearTimeout(this._resizeTimeout)\n this._resizeTimeout = setTimeout(() => {\n this._checkMobile()\n this._resizeTimeout = null\n }, 100)\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._checkMobile()\n window.addEventListener('resize', this._handleResize)\n void this.updateComplete.then(() => {\n if (this.isConnected) this._syncBannerPresence()\n })\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._resetActiveResize()\n window.removeEventListener('resize', this._handleResize)\n if (this._resizeTimeout !== null) clearTimeout(this._resizeTimeout)\n this._resizeTimeout = null\n }\n\n override firstUpdated() {\n this._syncBannerPresence()\n }\n\n private _onBannerSlotChange() {\n this._syncBannerPresence()\n }\n\n private _syncBannerPresence() {\n const bannerSlot = this.renderRoot.querySelector<HTMLSlotElement>('slot[name=\"banner\"]')\n const hasBanner =\n bannerSlot\n ?.assignedNodes()\n .some(\n node =>\n node.nodeType === Node.ELEMENT_NODE || (node.nodeType === Node.TEXT_NODE && !!node.textContent?.trim())\n ) ?? false\n const banner = this.shadowRoot?.querySelector<HTMLElement>('.layout-banner') ?? null\n this._visibleBanner.setTarget(hasBanner ? banner : null)\n }\n\n override render() {\n const isSidebarOpen = !this.sidebarCollapsed\n // 拖拽中的临时宽度优先于 prop 宽度;移动端宽度由 Drawer 内部变量管理。\n const sidebarStyle = this._isMobile\n ? {}\n : { width: this._resizeWidth ?? (this.sidebarCollapsed ? this.collapsedWidth : this.sidebarWidth) }\n\n const toggleLabel = isSidebarOpen ? '折叠侧边栏' : '展开侧边栏'\n\n const desktopToggle = html`\n <web-ui-button\n class=\"sidebar-toggle\"\n icon\n variant=\"secondary\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"${toggleLabel}\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n `\n\n const sidebarViewport = html`\n <div class=\"sidebar-viewport\">\n <slot name=\"sidebar\"></slot>\n </div>\n `\n\n // 桌面端布局\n const desktopLayout = html`\n <div class=\"layout-body\">\n <div class=\"sidebar-wrapper\">\n <aside\n class=\"${classMap({\n collapsed: !isSidebarOpen\n })}\"\n style=\"${styleMap(sidebarStyle)}\"\n >\n <div class=\"aside-panel wui-glass\">\n ${sidebarViewport}\n <div class=\"sidebar-toggle-area\">${desktopToggle}</div>\n </div>\n ${\n this.sidebarResizable && !this.sidebarCollapsed\n ? html`\n <div\n class=\"sidebar-resize-handle\"\n role=\"separator\"\n tabindex=\"0\"\n aria-orientation=\"vertical\"\n aria-label=\"调整侧边栏宽度\"\n @pointerdown=${this._handleResizePointerDown}\n @keydown=${this._handleResizeKeydown}\n ></div>\n `\n : nothing\n }\n </aside>\n </div>\n <div class=\"layout-content\">\n <header>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n </div>\n `\n\n // 移动端布局\n const mobileLayout = html`\n <div class=\"layout-content\">\n <header>\n <web-ui-button\n class=\"mobile-toggle\"\n icon\n variant=\"glass\"\n @click=\"${this._toggleSidebar}\"\n aria-label=\"打开导航菜单\"\n >\n <web-ui-icon .icon=${radixIconsPanelLeftMinimized}></web-ui-icon>\n </web-ui-button>\n <slot name=\"header\"></slot>\n </header>\n <main><slot></slot></main>\n <footer><slot name=\"tabbar\"></slot></footer>\n </div>\n\n <web-ui-drawer\n placement=\"left\"\n ?open=\"${this.sidebarOpen}\"\n controlled\n draggable\n dialog-label=\"主导航\"\n @open-change=\"${this._onDrawerChange}\"\n style=\"${styleMap({\n '--wui-drawer-width': this.sidebarWidth,\n '--wui-drawer-radius': 'var(--wui-layout-sidebar-radius, var(--wui-radius-overlay, 28px))'\n })}\"\n >\n ${sidebarViewport}\n </web-ui-drawer>\n `\n\n return html`\n <div class=\"layout-page\">\n <div class=\"layout-banner\">\n <slot name=\"banner\" @slotchange=\"${this._onBannerSlotChange}\"></slot>\n </div>\n ${this._isMobile ? mobileLayout : desktopLayout}\n </div>\n `\n }\n\n declare readonly $events: {\n 'sidebar-collapsed-change': CustomEvent<{ collapsed: boolean }>\n 'sidebar-open-change': CustomEvent<{ open: boolean }>\n 'sidebar-width-change': CustomEvent<{ width: string }>\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-layout': WebUiLayout\n }\n}\n"],"mappings":"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"}
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
//#region src/components/layout/style.css?inline
|
|
2
|
-
var e = "*,:before,:after{box-sizing:border-box}:host{--wui-layout-visible-banner-height:0px;--wui-layout-header-glow-color:var(--wui-color-page,#fff);display:block}.layout-page{flex-direction:column;min-height:100dvh;display:flex}.layout-banner{z-index:var(--wui-layer-banner,30);position:relative}.layout-body{flex:1;display:flex}.sidebar-wrapper{align-self:stretch;position:relative}.sidebar-viewport{flex:1;min-height:0}
|
|
2
|
+
var e = "*,:before,:after{box-sizing:border-box}:host{--wui-layout-visible-banner-height:0px;--wui-layout-header-glow-color:var(--wui-color-page,#fff);display:block}.layout-page{flex-direction:column;min-height:100dvh;display:flex}.layout-banner{z-index:var(--wui-layer-banner,30);position:relative}.layout-body{flex:1;display:flex}.sidebar-wrapper{align-self:stretch;position:relative}.sidebar-viewport{flex:1;min-height:0}aside{z-index:var(--wui-layer-sidebar,50);max-width:50vw;height:calc(100vh - var(--wui-layout-visible-banner-height));height:calc(100dvh - var(--wui-layout-visible-banner-height));max-height:calc(100vh - var(--wui-layout-visible-banner-height));max-height:calc(100dvh - var(--wui-layout-visible-banner-height));transition:width var(--wui-duration-layout,.2s) var(--wui-ease-enter,cubic-bezier(.23, 1, .32, 1));flex-direction:column;flex-shrink:0;padding:8px 0 8px 8px;display:flex;position:sticky;top:0}aside .aside-panel{--wui-glass-corner-radius:var(--wui-layout-sidebar-radius,var(--wui-radius-overlay,28px));border-radius:var(--wui-layout-sidebar-radius,var(--wui-radius-overlay,28px));background-color:var(--wui-color-surface-overlay,#f6f6f6d1);flex-direction:column;flex:1;min-height:0;display:flex}aside .sidebar-toggle-area{flex-shrink:0;align-items:center;display:flex}aside .sidebar-toggle{margin:8px}aside.collapsed .sidebar-viewport{overflow:hidden}.sidebar-resize-handle{touch-action:none;cursor:col-resize;z-index:1;width:12px;position:absolute;top:0;bottom:0;right:-6px}.sidebar-resize-handle:before{content:\"\";border-radius:var(--wui-radius-control,calc(infinity * 1px));opacity:0;background:var(--wui-color-accent,#08f);width:3px;transition:opacity var(--wui-duration-feedback,.1s) ease;position:absolute;top:0;bottom:0;left:50%;translate:-50%}.sidebar-resize-handle:hover:before,.sidebar-resize-handle:focus-visible:before,aside.is-resizing .sidebar-resize-handle:before{opacity:1}aside.is-resizing{transition:none}.layout-content{flex-direction:column;flex:1;min-width:0;display:flex}header{isolation:isolate;z-index:var(--wui-layer-header,10);align-items:center;gap:8px;display:flex;position:sticky;top:0;overflow-x:clip}.mobile-toggle{margin-left:var(--wui-layout-mobile-toggle-inset,8px)}:host([header-glow]) header:before{pointer-events:none;content:\"\";z-index:-1;opacity:.95;background-color:var(--wui-layout-header-glow-color);filter:blur(10px);position:absolute;inset:0;transform:translate(20px,-50%)scale(1.05,2)}main{flex:1}footer{z-index:var(--wui-layer-tabbar,40);position:sticky;bottom:0}@media (width<=640px){aside,.sidebar-wrapper{display:none}header{align-items:flex-start}.mobile-toggle{align-self:flex-start;margin-top:8px}}@media (prefers-reduced-motion:reduce){aside,.sidebar-resize-handle:before{transition:none}}";
|
|
3
3
|
//#endregion
|
|
4
4
|
export { e as default };
|
|
5
5
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/layout/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-layout-visible-banner-height: 0px;\n --wui-layout-header-glow-color: var(--wui-color-page, #fff);\n\n display: block;\n}\n\n/* 页面级滚动容器。Banner 位于正文前,随页面一起滚动。 */\n.layout-page {\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n min-height: 100dvh;\n}\n\n.layout-banner {\n position: relative;\n z-index: var(--wui-layer-banner, 30);\n}\n\n/* 参考 book-fe:正文区域负责撑高 sidebar 的 relative wrapper。 */\n.layout-body {\n display: flex;\n flex: 1;\n}\n\n.sidebar-wrapper {\n position: relative;\n align-self: stretch;\n}\n\n/* Layout
|
|
1
|
+
{"version":3,"file":"style.js","names":[],"sources":["../../../src/components/layout/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n --wui-layout-visible-banner-height: 0px;\n --wui-layout-header-glow-color: var(--wui-color-page, #fff);\n\n display: block;\n}\n\n/* 页面级滚动容器。Banner 位于正文前,随页面一起滚动。 */\n.layout-page {\n display: flex;\n flex-direction: column;\n min-height: 100vh;\n min-height: 100dvh;\n}\n\n.layout-banner {\n position: relative;\n z-index: var(--wui-layer-banner, 30);\n}\n\n/* 参考 book-fe:正文区域负责撑高 sidebar 的 relative wrapper。 */\n.layout-body {\n display: flex;\n flex: 1;\n}\n\n.sidebar-wrapper {\n position: relative;\n align-self: stretch;\n}\n\n/* Layout 只提供可用高度;桌面端由 Consumer 决定 scrollport,移动端由 Drawer content 提供。 */\n.sidebar-viewport {\n flex: 1;\n min-height: 0;\n}\n\n/*\n * Banner 可见时,组件用它的当前可见高度缩短 aside;Banner 滚出后该值归零。\n * 因而圆角卡片始终完整可见,同时 sticky Sidebar 可在 Banner 消失后填满视口。\n */\naside {\n position: sticky;\n z-index: var(--wui-layer-sidebar, 50);\n top: 0;\n\n display: flex;\n flex-direction: column;\n flex-shrink: 0;\n\n max-width: 50vw;\n height: calc(100vh - var(--wui-layout-visible-banner-height));\n height: calc(100dvh - var(--wui-layout-visible-banner-height));\n max-height: calc(100vh - var(--wui-layout-visible-banner-height));\n max-height: calc(100dvh - var(--wui-layout-visible-banner-height));\n padding: 8px 0 8px 8px;\n\n transition: width var(--wui-duration-layout, 200ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n .aside-panel {\n --wui-glass-corner-radius: var(--wui-layout-sidebar-radius, var(--wui-radius-overlay, 28px));\n\n display: flex;\n flex: 1;\n flex-direction: column;\n\n min-height: 0;\n border-radius: var(--wui-layout-sidebar-radius, var(--wui-radius-overlay, 28px));\n\n background-color: var(--wui-color-surface-overlay, rgb(246 246 246 / 0.82));\n }\n\n .sidebar-toggle-area {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n }\n\n .sidebar-toggle {\n margin: 8px;\n }\n\n &.collapsed {\n .sidebar-viewport {\n overflow: hidden;\n }\n }\n}\n\n/* ===== Sidebar 拖拽调宽 handle ===== */\n\n/*\n * 命中条贴 aside 右缘(约 12px 宽),hover 或拖拽中显示 3px accent 竖线。\n * aside 有 8px padding,handle 定位在 padding 区外缘,不遮挡 aside-panel 内容。\n */\n.sidebar-resize-handle {\n touch-action: none;\n cursor: col-resize;\n\n position: absolute;\n z-index: 1;\n top: 0;\n right: -6px;\n bottom: 0;\n\n width: 12px;\n}\n\n.sidebar-resize-handle::before {\n content: '';\n\n position: absolute;\n top: 0;\n bottom: 0;\n left: 50%;\n translate: -50%;\n\n width: 3px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n opacity: 0;\n background: var(--wui-color-accent, #08f);\n\n transition: opacity var(--wui-duration-feedback, 100ms) ease;\n}\n\n.sidebar-resize-handle:hover::before,\n.sidebar-resize-handle:focus-visible::before,\naside.is-resizing .sidebar-resize-handle::before {\n opacity: 1;\n}\n\n/* 拖拽中禁用宽度过渡,保证实时跟手。 */\naside.is-resizing {\n transition: none;\n}\n\n.layout-content {\n display: flex;\n flex: 1;\n flex-direction: column;\n min-width: 0;\n}\n\nheader {\n /* 将负层级 glow 限定在 Header 的局部 stacking context,slot 内容自然位于其上。 */\n isolation: isolate;\n position: sticky;\n z-index: var(--wui-layer-header, 10);\n top: 0;\n\n /*\n * glow 伪元素用 scale + translate 向外扩展,会溢出 header 盒子并撑出页面横向滚动条\n * (移动端约 30px)。这里只裁横向:纵向保持 visible,下拉浮层等槽位内容仍可溢出。\n * clip 不创建滚动容器,因此不影响 header 自身的 sticky。\n */\n overflow-x: clip;\n display: flex;\n gap: 8px;\n align-items: center;\n}\n\n/* 移动端 Toggle 属于组件自身,不随 Consumer 的 header 内边距缩进;\n 默认 8px 使其脱离视口边缘,Consumer 可用同名变量对齐自身内容左缘。 */\n.mobile-toggle {\n margin-left: var(--wui-layout-mobile-toggle-inset, 8px);\n}\n\n/*\n * Header 的装饰性背景晕染\n * ========================\n *\n * 伪元素随 header 的实际盒子尺寸和 sticky 位置变化,自然适配 header slot 的\n * 高度与位置,无需单独维护一层 DOM。\n *\n * 通过 translate + scale 将晕染区域从 header 中心向外扩展,\n * 并使用 blur 模糊边缘,实现柔和的渐变过渡效果。\n */\n:host([header-glow]) header::before {\n pointer-events: none;\n content: '';\n\n position: absolute;\n z-index: -1;\n inset: 0;\n transform: translate(20px, -50%) scale(1.05, 2);\n\n opacity: 0.95;\n background-color: var(--wui-layout-header-glow-color);\n filter: blur(10px);\n}\n\nmain {\n flex: 1;\n}\n\nfooter {\n position: sticky;\n z-index: var(--wui-layer-tabbar, 40);\n bottom: 0;\n}\n\n@media (width <= 640px) {\n aside,\n .sidebar-wrapper {\n display: none;\n }\n\n /* Slot 可能包含展开后的多行 header;Toggle 对齐首行,而不是被整段 header 高度居中。 */\n header {\n align-items: flex-start;\n }\n\n .mobile-toggle {\n align-self: flex-start;\n margin-top: 8px;\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n aside {\n transition: none;\n }\n\n .sidebar-resize-handle::before {\n transition: none;\n }\n}\n"],"mappings":""}
|
|
@@ -3,21 +3,26 @@ import t from "../../assets/glass.js";
|
|
|
3
3
|
import n from "../../assets/overlay-motion.js";
|
|
4
4
|
import { normalizeLiteral as r, normalizeNumber as i } from "../../shared/normalize/index.js";
|
|
5
5
|
import { dispatchOpenChangeEvent as a } from "../../shared/open-state/index.js";
|
|
6
|
-
import {
|
|
7
|
-
import {
|
|
8
|
-
import {
|
|
9
|
-
import {
|
|
10
|
-
import u from "
|
|
11
|
-
import {
|
|
12
|
-
import
|
|
6
|
+
import { overlayComposition as o } from "../../shared/overlay/composition.js";
|
|
7
|
+
import { defineAnchoredPanel as s } from "../../shared/overlay/anchored-panel.js";
|
|
8
|
+
import { defineOverlayPortal as c } from "../../shared/overlay/portal.js";
|
|
9
|
+
import { UserChangeController as l } from "../../shared/events/user-change.js";
|
|
10
|
+
import { defineOverlayLifecycle as u } from "../../shared/overlay/lifecycle.js";
|
|
11
|
+
import { FLOATING_PLACEMENTS as d } from "../../shared/overlay/placement-props.js";
|
|
12
|
+
import f from "./style.js";
|
|
13
|
+
import { LitElement as p, html as m, unsafeCSS as h } from "lit";
|
|
14
|
+
import { customElement as g, property as _ } from "lit/decorators.js";
|
|
13
15
|
//#region src/components/popover/index.ts
|
|
14
|
-
var
|
|
16
|
+
var v = [
|
|
15
17
|
"click",
|
|
16
18
|
"hover",
|
|
17
19
|
"manual"
|
|
18
|
-
],
|
|
20
|
+
], y = 0, b = class extends p {
|
|
19
21
|
constructor(...e) {
|
|
20
|
-
super(...e), this.open = !1, this.disabled = !1, this._placement = "bottom", this._offset = 8, this._trigger = "click", this.portal = !1, this._userOpenChange = new
|
|
22
|
+
super(...e), this.open = !1, this.disabled = !1, this._placement = "bottom", this._offset = 8, this._trigger = "click", this.portal = !1, this._userOpenChange = new l(), this._shouldOpenInstantly = !0, this._panelId = `wui-popover-panel-${++y}`, this._lifecycle = u().make({
|
|
23
|
+
isConnected: () => this.isConnected,
|
|
24
|
+
isOpen: () => this.open
|
|
25
|
+
}), this._panel = s().make({
|
|
21
26
|
getAnchor: () => this.shadowRoot?.querySelector(".popover-trigger") ?? null,
|
|
22
27
|
getLocalPanel: () => this.shadowRoot?.querySelector(".popover-panel") ?? null,
|
|
23
28
|
getPositioning: () => ({
|
|
@@ -30,11 +35,14 @@ var g = [
|
|
|
30
35
|
}), this._onTriggerClick = (e) => {
|
|
31
36
|
this.disabled || (clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this.trigger !== "hover" && (this.open || (this._shouldOpenInstantly = e.detail === 0), this._userOpenChange.mark(), this.toggle()));
|
|
32
37
|
}, this._onClickOutside = (e) => {
|
|
33
|
-
this.open
|
|
38
|
+
if (!this.open || this.trigger === "manual" || this.trigger === "hover") return;
|
|
39
|
+
let t = this._panel.getPanel();
|
|
40
|
+
t && o.containsEvent(t, e) || this._isInsideShadowRoot(e) || (this._userOpenChange.mark(), this.open = !1);
|
|
34
41
|
}, this._onFocusOut = () => {
|
|
35
|
-
this.trigger !== "manual" && this.trigger !== "hover" &&
|
|
36
|
-
|
|
37
|
-
|
|
42
|
+
this.trigger !== "manual" && this.trigger !== "hover" && this._lifecycle.scheduleFrame(() => {
|
|
43
|
+
let e = this._panel.getPanel();
|
|
44
|
+
!this.matches(":focus-within") && !(e && o.hasFocusWithin(e)) && (this._userOpenChange.mark(), this.open = !1);
|
|
45
|
+
}, { expectedOpen: !0 });
|
|
38
46
|
}, this._onKeydown = (e) => {
|
|
39
47
|
this.open && this.trigger !== "manual" && e.key === "Escape" && (this._userOpenChange.mark(), this.open = !1, e.preventDefault());
|
|
40
48
|
}, this._onPointerEnter = (e) => {
|
|
@@ -55,9 +63,9 @@ var g = [
|
|
|
55
63
|
}
|
|
56
64
|
static {
|
|
57
65
|
this.styles = [
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
66
|
+
h(t),
|
|
67
|
+
h(n),
|
|
68
|
+
h(f)
|
|
61
69
|
];
|
|
62
70
|
}
|
|
63
71
|
get placement() {
|
|
@@ -65,7 +73,7 @@ var g = [
|
|
|
65
73
|
}
|
|
66
74
|
set placement(e) {
|
|
67
75
|
let t = this._placement;
|
|
68
|
-
this._placement = r(e,
|
|
76
|
+
this._placement = r(e, d, "bottom"), this.requestUpdate("placement", t);
|
|
69
77
|
}
|
|
70
78
|
get offset() {
|
|
71
79
|
return this._offset;
|
|
@@ -79,26 +87,30 @@ var g = [
|
|
|
79
87
|
}
|
|
80
88
|
set trigger(e) {
|
|
81
89
|
let t = this._trigger;
|
|
82
|
-
this._trigger = r(e,
|
|
90
|
+
this._trigger = r(e, v, "click"), this.requestUpdate("trigger", t);
|
|
83
91
|
}
|
|
84
92
|
get isOpen() {
|
|
85
93
|
return this.open;
|
|
86
94
|
}
|
|
87
95
|
connectedCallback() {
|
|
88
|
-
super.connectedCallback(), document.addEventListener("click", this._onClickOutside), document.addEventListener("keydown", this._onKeydown), this.addEventListener("focusout", this._onFocusOut), this._syncTriggerListeners();
|
|
96
|
+
super.connectedCallback(), this._lifecycle.resume(), document.addEventListener("click", this._onClickOutside), document.addEventListener("keydown", this._onKeydown), this.addEventListener("focusout", this._onFocusOut), this._syncTriggerListeners();
|
|
89
97
|
}
|
|
90
98
|
firstUpdated() {
|
|
91
|
-
this.shadowRoot?.querySelector("slot[name=\"trigger\"]")?.addEventListener("slotchange", () => this.requestUpdate()), this.open && (
|
|
99
|
+
this.shadowRoot?.querySelector("slot[name=\"trigger\"]")?.addEventListener("slotchange", () => this.requestUpdate()), this.open && (this._lifecycle.scheduleFrame(() => {
|
|
100
|
+
this._openOverlay(this._shouldOpenInstantly);
|
|
101
|
+
}, { expectedOpen: !0 }), this._shouldOpenInstantly = !0);
|
|
92
102
|
}
|
|
93
103
|
disconnectedCallback() {
|
|
94
|
-
super.disconnectedCallback(), document.removeEventListener("click", this._onClickOutside), document.removeEventListener("keydown", this._onKeydown), this.removeEventListener("focusout", this._onFocusOut), this.removeEventListener("pointerenter", this._onPointerEnter), this.removeEventListener("pointerleave", this._onPointerLeave), clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this._panel.dispose();
|
|
104
|
+
super.disconnectedCallback(), document.removeEventListener("click", this._onClickOutside), document.removeEventListener("keydown", this._onKeydown), this.removeEventListener("focusout", this._onFocusOut), this.removeEventListener("pointerenter", this._onPointerEnter), this.removeEventListener("pointerleave", this._onPointerLeave), this._lifecycle.dispose(), clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this._panel.dispose();
|
|
95
105
|
}
|
|
96
106
|
updated(e) {
|
|
97
|
-
if (this._syncTriggerAria(), e.has("portal") || e.has("overlayContainer") ?
|
|
107
|
+
if (this._syncTriggerAria(), e.has("portal") || e.has("overlayContainer") ? (this._lifecycle.invalidate(), this._lifecycle.scheduleFrame(() => this._reconfigureOverlay())) : (e.has("placement") || e.has("offset")) && requestAnimationFrame(() => this._panel.updatePosition()), e.has("open")) {
|
|
98
108
|
if (this.open) {
|
|
99
109
|
let e = this._shouldOpenInstantly;
|
|
100
|
-
this._shouldOpenInstantly = !0,
|
|
101
|
-
|
|
110
|
+
this._shouldOpenInstantly = !0, this._lifecycle.scheduleFrame(() => {
|
|
111
|
+
this._openOverlay(e);
|
|
112
|
+
}, { expectedOpen: !0 }), this._userOpenChange.consume() && this._dispatchChange(!0), this._focusPanel();
|
|
113
|
+
} else this._lifecycle.invalidate(), this._returnFocus(), this._closeOverlay(), this._userOpenChange.consume() && this._dispatchChange(!1);
|
|
102
114
|
}
|
|
103
115
|
e.has("trigger") && (this.removeEventListener("pointerenter", this._onPointerEnter), this.removeEventListener("pointerleave", this._onPointerLeave), clearTimeout(this._showTimer), clearTimeout(this._hideTimer), this._syncTriggerListeners());
|
|
104
116
|
}
|
|
@@ -120,15 +132,17 @@ var g = [
|
|
|
120
132
|
}
|
|
121
133
|
_openOverlay(e = !1) {
|
|
122
134
|
this._panel.open(e);
|
|
135
|
+
let t = this._panel.getPanel();
|
|
136
|
+
t && o.registerPanelFromAncestry(t, this);
|
|
123
137
|
}
|
|
124
138
|
_migratableContentNodes(e) {
|
|
125
139
|
return e.filter((e) => !(e instanceof Comment) && !(e instanceof HTMLElement && e.slot === "trigger"));
|
|
126
140
|
}
|
|
127
141
|
_createPortal() {
|
|
128
|
-
let e =
|
|
142
|
+
let e = c().make({
|
|
129
143
|
container: this.overlayContainer,
|
|
130
144
|
target: this,
|
|
131
|
-
style: `${t}\n${n}\n${
|
|
145
|
+
style: `${t}\n${n}\n${f}`,
|
|
132
146
|
className: "popover-panel portal wui-glass wui-floating-panel",
|
|
133
147
|
migrateAddedNodes: (e) => this._migratableContentNodes(e)
|
|
134
148
|
});
|
|
@@ -138,7 +152,7 @@ var g = [
|
|
|
138
152
|
await this._panel.close(() => this.open);
|
|
139
153
|
}
|
|
140
154
|
_reconfigureOverlay() {
|
|
141
|
-
this._panel.reconfigure(this.open);
|
|
155
|
+
this.isConnected && this._panel.reconfigure(this.open);
|
|
142
156
|
}
|
|
143
157
|
_dispatchChange(e) {
|
|
144
158
|
a(this, e);
|
|
@@ -148,10 +162,10 @@ var g = [
|
|
|
148
162
|
e && (e.setAttribute("aria-expanded", String(this.open)), e.setAttribute("aria-controls", this._panelId));
|
|
149
163
|
}
|
|
150
164
|
_focusPanel() {
|
|
151
|
-
|
|
165
|
+
this._lifecycle.scheduleFrame(() => {
|
|
152
166
|
let e = this._panel.getPanel()?.querySelector("[autofocus]");
|
|
153
167
|
e && !e.matches(":disabled, [disabled]") && e.focus();
|
|
154
|
-
});
|
|
168
|
+
}, { expectedOpen: !0 });
|
|
155
169
|
}
|
|
156
170
|
_returnFocus() {
|
|
157
171
|
this._panel.getPanel()?.matches(":focus-within") && this._queryTrigger()?.focus();
|
|
@@ -161,7 +175,7 @@ var g = [
|
|
|
161
175
|
return e instanceof HTMLElement ? e : null;
|
|
162
176
|
}
|
|
163
177
|
render() {
|
|
164
|
-
return
|
|
178
|
+
return m`
|
|
165
179
|
<div class="popover-anchor">
|
|
166
180
|
<div
|
|
167
181
|
class="popover-trigger"
|
|
@@ -173,7 +187,7 @@ var g = [
|
|
|
173
187
|
</div>
|
|
174
188
|
<div
|
|
175
189
|
id=${this._panelId}
|
|
176
|
-
class="popover-panel wui-
|
|
190
|
+
class="popover-panel wui-floating-panel wui-glass"
|
|
177
191
|
hidden
|
|
178
192
|
role="dialog"
|
|
179
193
|
tabindex="-1"
|
|
@@ -186,23 +200,23 @@ var g = [
|
|
|
186
200
|
`;
|
|
187
201
|
}
|
|
188
202
|
};
|
|
189
|
-
e([
|
|
203
|
+
e([_({
|
|
190
204
|
type: Boolean,
|
|
191
205
|
reflect: !0
|
|
192
|
-
})],
|
|
206
|
+
})], b.prototype, "open", void 0), e([_({
|
|
193
207
|
type: Boolean,
|
|
194
208
|
reflect: !0
|
|
195
|
-
})],
|
|
209
|
+
})], b.prototype, "disabled", void 0), e([_({
|
|
196
210
|
type: String,
|
|
197
211
|
reflect: !0
|
|
198
|
-
})],
|
|
212
|
+
})], b.prototype, "placement", null), e([_({ type: Number })], b.prototype, "offset", null), e([_({
|
|
199
213
|
type: String,
|
|
200
214
|
reflect: !0
|
|
201
|
-
})],
|
|
215
|
+
})], b.prototype, "trigger", null), e([_({
|
|
202
216
|
type: Boolean,
|
|
203
217
|
reflect: !0
|
|
204
|
-
})],
|
|
218
|
+
})], b.prototype, "portal", void 0), e([_({ attribute: !1 })], b.prototype, "overlayContainer", void 0), b = e([g("web-ui-popover")], b);
|
|
205
219
|
//#endregion
|
|
206
|
-
export {
|
|
220
|
+
export { b as WebUiPopover };
|
|
207
221
|
|
|
208
222
|
//# sourceMappingURL=index.js.map
|