@greypan/web-ui 10.1.2 → 10.2.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (31) hide show
  1. package/README.CN.md +41 -1547
  2. package/README.md +41 -1554
  3. package/dist/assets/overlay-motion.js +1 -1
  4. package/dist/assets/overlay-motion.js.map +1 -1
  5. package/dist/components/checkbox-group/index.d.ts +9 -0
  6. package/dist/components/checkbox-group/index.js +7 -1
  7. package/dist/components/checkbox-group/index.js.map +1 -1
  8. package/dist/components/context-menu/index.js +3 -3
  9. package/dist/components/context-menu/index.js.map +1 -1
  10. package/dist/components/image-preview/style.js +1 -1
  11. package/dist/components/image-preview/style.js.map +1 -1
  12. package/dist/components/input/index.js +3 -0
  13. package/dist/components/input/index.js.map +1 -1
  14. package/dist/components/input-number/index.d.ts +6 -0
  15. package/dist/components/input-number/index.js +27 -21
  16. package/dist/components/input-number/index.js.map +1 -1
  17. package/dist/components/radio-group/index.d.ts +7 -0
  18. package/dist/components/radio-group/index.js +20 -2
  19. package/dist/components/radio-group/index.js.map +1 -1
  20. package/dist/components/slider/index.js +2 -2
  21. package/dist/components/slider/index.js.map +1 -1
  22. package/dist/components/switch/index.d.ts +3 -0
  23. package/dist/components/switch/index.js +25 -7
  24. package/dist/components/switch/index.js.map +1 -1
  25. package/dist/components/theme/style.js +1 -1
  26. package/dist/components/theme/style.js.map +1 -1
  27. package/dist/components/toast/style.js +1 -1
  28. package/dist/components/toast/style.js.map +1 -1
  29. package/dist/shared/group-management/index.js +5 -1
  30. package/dist/shared/group-management/index.js.map +1 -1
  31. package/package.json +1 -1
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/image-preview/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/*\n * 全视口 dialog:遮罩由独立 .wui-image-preview-backdrop 层提供(而非 ::backdrop /\n * dialog 自身背景)。dialog 自身 opacity 恒 1、无任何 transform——opacity < 1 会让\n * dialog 成为 backdrop root,后代玻璃控件(toolbar/counter/nav)的 backdrop-filter\n * 在过渡期被禁用、blur 在端点生硬跳变;transform 会把遮罩一起缩放。进场缩放\n * (scale 0.95→1)由 .wui-image-preview-surface 图片舞台层承担(见下)。本组件是满视口\n * 表面,起点 >1 会越出视口,只能用 <1 的档位;缩放也不能落在包住控件层的\n * .wui-image-preview-content 上,否则贴边控件会随缩放从视口边缘向内飞行。遮罩、图片\n * 舞台各自用独立层 opacity 过渡;控件层不参与 opacity 过渡,玻璃控件永不在 opacity < 1\n * 的祖先内,blur 全程连续。\n */\ndialog {\n /* 显式 position: fixed(防御性):UA 的 dialog:modal 默认 fixed,但作者样式若覆盖\n position 会把它退回文档流——top layer 里的 modal dialog 变成按文档流定位,出现在\n 页面顶部且跟随页面滚动。image-preview 的 dialog 铺满视口,fixed 是硬前提。 */\n position: fixed;\n\n overflow: hidden;\n\n box-sizing: border-box;\n width: 100%;\n max-width: none;\n height: 100%;\n height: 100dvh;\n max-height: none;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n opacity: 1;\n background: transparent;\n}\n\n/* 遮罩层:承接原先 dialog 背景的淡入淡出,自身 opacity 过渡不构成 backdrop root。 */\n.wui-image-preview-backdrop {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n opacity: 0;\n background: var(--wui-image-preview-overlay-bg, rgb(0 0 0 / 0.72));\n\n transition: opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 内容包裹层:仅作「图片舞台层 + 控件层」的结构容器,自身不做任何过渡。\n scale 不能放在 dialog 上(会带着遮罩一起缩放),也不能放在本层(控件层在其内,\n 带 transform 会让贴边控件从视口边缘向内飞行)。 */\n.wui-image-preview-content {\n position: absolute;\n inset: 0;\n}\n\n/* 图片舞台层:与遮罩同尺寸叠放,承接图片的进场缩放与淡入淡出(0.95→1)。内部无玻璃\n 元素,自身 opacity 过渡不影响 backdrop-filter;transform 不构成 backdrop root,\n 也不会波及兄弟的控件层。 */\n.wui-image-preview-surface {\n position: absolute;\n inset: 0;\n transform: scale(var(--wui-scale-enter, 0.95));\n\n opacity: 0;\n\n transition:\n opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1)),\n transform var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 玻璃控件(counter/toolbar)各自做 own opacity 过渡:玻璃元素自身 opacity 淡入\n 不禁用自身 backdrop-filter(backdrop root 只关闭后代 blur),因此控件淡入淡出时\n blur 仍连续;nav/close 是 glass 按钮,blur 在其 shadow 内层原生 button 上,必须\n 经 ::part(button) 落在该玻璃元素自身,若对按钮 host 做 opacity 过渡反而会让\n host 成为 backdrop root、禁掉内层 blur(与祖先 opacity 禁后代 blur 同一机制)。 */\n.wui-image-preview-counter,\n.wui-image-preview-toolbar,\n.wui-image-preview-close::part(button),\n.wui-image-preview-nav::part(button) {\n opacity: 0;\n transition: opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 模态表面的焦点环抑制(<dialog> 壳 + 面板滚动区)集中在 assets/modal-surface.css,见组件 index.ts 的 static styles。 */\n\n/* dialog 已铺满视口,原生遮罩不再叠加着色,避免双重变暗。 */\ndialog::backdrop {\n background: transparent;\n}\n\n.wui-image-preview-stage {\n touch-action: none;\n cursor: zoom-in;\n\n position: relative;\n\n overflow: hidden;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-stage.is-zoomed {\n cursor: grab;\n}\n\n.wui-image-preview-stage.is-dragging {\n cursor: grabbing;\n}\n\n/* 单轨道轮播(相邻图渲染):轨道撑满舞台,当前图在原点、左右相邻用绝对定位放在\n ±100%(= 舞台宽度)处,与当前图同尺寸同基线;舞台 overflow 裁切,轨道整体随跟手\n 位移平移。相邻图只在当前索引两侧各渲染一张(loop 环绕、非 loop 缺省一侧),任何\n 索引拖拽(含首尾环绕)相邻图都真实跟手,不会越过轨道末端滑空。\n pointer-events: none 让空白的轨道区域穿透命中到舞台(空白点击仍算遮罩),图片自身\n 恢复 auto 接收手势。 */\n.wui-image-preview-track {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-slide {\n pointer-events: none;\n\n position: absolute;\n top: 0;\n left: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-slide.is-prev {\n left: -100%;\n}\n\n.wui-image-preview-slide.is-next {\n left: 100%;\n}\n\n.wui-image-preview-image {\n pointer-events: auto;\n user-select: none;\n\n display: block;\n\n max-width: 100%;\n max-height: 100%;\n\n opacity: 0;\n object-fit: contain;\n\n /* 拖拽(平移)不参与 transform 过渡(见 .is-dragging),避免跟手位移滞后;\n swipe 只动轨道,图片 transform 在滑动期间不变。 */\n transition:\n opacity var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n transform var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n -webkit-user-drag: none;\n}\n\n.wui-image-preview-image.is-loaded {\n opacity: 1;\n}\n\n.wui-image-preview-stage.is-dragging .wui-image-preview-image {\n transition: opacity var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n/* 轮播滑入/回位:跟手结束后把轨道 transform 过渡到目标位(当前图滑出、相邻图入位或\n 弹回)。transitionend 会触发组件内的收尾提交;时长/缓动可用组件级变量覆盖。 */\n.wui-image-preview-stage.is-settling .wui-image-preview-track {\n transition: transform var(--wui-duration-swipe-settle, 220ms) var(--wui-ease-swipe, cubic-bezier(0.32, 0.72, 0, 1));\n}\n\n.wui-image-preview-stage.is-dragging .wui-image-preview-track {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-image-preview-image,\n .wui-image-preview-stage.is-dragging .wui-image-preview-image,\n .wui-image-preview-stage.is-settling .wui-image-preview-track {\n transition: opacity var(--wui-duration-feedback, 100ms);\n }\n}\n\n.wui-image-preview-controls {\n pointer-events: none;\n position: absolute;\n inset: 0;\n}\n\n.wui-image-preview-controls > * {\n pointer-events: auto;\n}\n\n.wui-image-preview-counter {\n /* pill 高 ≈ 24px(13px 行盒 + 上下 4px padding),光影取短边一半 */\n --wui-glass-corner-radius: 12px;\n\n position: absolute;\n top: var(--wui-image-preview-edge-gap, 20px);\n left: var(--wui-image-preview-edge-gap, 20px);\n\n padding: var(--wui-space-1, 4px) var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n font-size: var(--wui-font-size-readout, 13px);\n font-variant-numeric: tabular-nums;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n.wui-image-preview-close {\n position: absolute;\n top: var(--wui-image-preview-edge-gap, 20px);\n right: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-nav {\n position: absolute;\n top: 50%;\n translate: 0 -50%;\n}\n\n.wui-image-preview-nav-prev {\n left: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-nav-next {\n right: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-toolbar {\n /* 高 = 30px 图标按钮 + 上下 6px padding,光影取短边一半 */\n --wui-glass-corner-radius: 21px;\n\n position: absolute;\n bottom: var(--wui-image-preview-edge-gap, 20px);\n left: 50%;\n translate: -50% 0;\n\n display: flex;\n gap: 6px;\n align-items: center;\n\n padding: 6px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\n.wui-image-preview-scale {\n min-width: 48px;\n\n font-size: var(--wui-font-size-readout, 13px);\n font-variant-numeric: tabular-nums;\n color: var(--wui-color-text, #1b1b1b);\n text-align: center;\n}\n\ndialog[open].is-visible {\n .wui-image-preview-surface {\n transform: scale(1);\n }\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n opacity: 1;\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n opacity: 1;\n }\n}\n\ndialog[open].is-closing {\n pointer-events: none;\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog,\n dialog[open].is-visible {\n transition-duration: var(--wui-duration-feedback, 100ms);\n\n .wui-image-preview-surface {\n transform: none;\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/image-preview/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n display: contents;\n}\n\n/*\n * 全视口 dialog:遮罩由独立 .wui-image-preview-backdrop 层提供(而非 ::backdrop /\n * dialog 自身背景)。dialog 自身 opacity 恒 1、无任何 transform——opacity < 1 会让\n * dialog 成为 backdrop root,后代玻璃控件(toolbar/counter/nav)的 backdrop-filter\n * 在过渡期被禁用、blur 在端点生硬跳变;transform 会把遮罩一起缩放。进场缩放\n * (scale 0.9→1)由 .wui-image-preview-surface 图片舞台层承担(见下)。本组件是满视口\n * 表面,起点 >1 会越出视口,只能用 <1 的档位;缩放也不能落在包住控件层的\n * .wui-image-preview-content 上,否则贴边控件会随缩放从视口边缘向内飞行。遮罩、图片\n * 舞台各自用独立层 opacity 过渡;控件层不参与 opacity 过渡,玻璃控件永不在 opacity < 1\n * 的祖先内,blur 全程连续。\n */\ndialog {\n /* 显式 position: fixed(防御性):UA 的 dialog:modal 默认 fixed,但作者样式若覆盖\n position 会把它退回文档流——top layer 里的 modal dialog 变成按文档流定位,出现在\n 页面顶部且跟随页面滚动。image-preview 的 dialog 铺满视口,fixed 是硬前提。 */\n position: fixed;\n\n overflow: hidden;\n\n box-sizing: border-box;\n width: 100%;\n max-width: none;\n height: 100%;\n height: 100dvh;\n max-height: none;\n margin: 0;\n padding: 0;\n border: none;\n\n font-family: inherit;\n color: var(--wui-color-text, #1b1b1b);\n\n opacity: 1;\n background: transparent;\n}\n\n/* 遮罩层:承接原先 dialog 背景的淡入淡出,自身 opacity 过渡不构成 backdrop root。 */\n.wui-image-preview-backdrop {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n opacity: 0;\n background: var(--wui-image-preview-overlay-bg, rgb(0 0 0 / 0.72));\n\n transition: opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 内容包裹层:仅作「图片舞台层 + 控件层」的结构容器,自身不做任何过渡。\n scale 不能放在 dialog 上(会带着遮罩一起缩放),也不能放在本层(控件层在其内,\n 带 transform 会让贴边控件从视口边缘向内飞行)。 */\n.wui-image-preview-content {\n position: absolute;\n inset: 0;\n}\n\n/* 图片舞台层:与遮罩同尺寸叠放,承接图片的进场缩放与淡入淡出(0.9→1)。内部无玻璃\n 元素,自身 opacity 过渡不影响 backdrop-filter;transform 不构成 backdrop root,\n 也不会波及兄弟的控件层。 */\n.wui-image-preview-surface {\n position: absolute;\n inset: 0;\n transform: scale(var(--wui-scale-enter, 0.9));\n\n opacity: 0;\n\n transition:\n opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1)),\n transform var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 玻璃控件(counter/toolbar)各自做 own opacity 过渡:玻璃元素自身 opacity 淡入\n 不禁用自身 backdrop-filter(backdrop root 只关闭后代 blur),因此控件淡入淡出时\n blur 仍连续;nav/close 是 glass 按钮,blur 在其 shadow 内层原生 button 上,必须\n 经 ::part(button) 落在该玻璃元素自身,若对按钮 host 做 opacity 过渡反而会让\n host 成为 backdrop root、禁掉内层 blur(与祖先 opacity 禁后代 blur 同一机制)。 */\n.wui-image-preview-counter,\n.wui-image-preview-toolbar,\n.wui-image-preview-close::part(button),\n.wui-image-preview-nav::part(button) {\n opacity: 0;\n transition: opacity var(--wui-duration-dialog-enter, 320ms) var(--wui-ease-dialog, cubic-bezier(0.2, 0, 0, 1));\n}\n\n/* 模态表面的焦点环抑制(<dialog> 壳 + 面板滚动区)集中在 assets/modal-surface.css,见组件 index.ts 的 static styles。 */\n\n/* dialog 已铺满视口,原生遮罩不再叠加着色,避免双重变暗。 */\ndialog::backdrop {\n background: transparent;\n}\n\n.wui-image-preview-stage {\n touch-action: none;\n cursor: zoom-in;\n\n position: relative;\n\n overflow: hidden;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-stage.is-zoomed {\n cursor: grab;\n}\n\n.wui-image-preview-stage.is-dragging {\n cursor: grabbing;\n}\n\n/* 单轨道轮播(相邻图渲染):轨道撑满舞台,当前图在原点、左右相邻用绝对定位放在\n ±100%(= 舞台宽度)处,与当前图同尺寸同基线;舞台 overflow 裁切,轨道整体随跟手\n 位移平移。相邻图只在当前索引两侧各渲染一张(loop 环绕、非 loop 缺省一侧),任何\n 索引拖拽(含首尾环绕)相邻图都真实跟手,不会越过轨道末端滑空。\n pointer-events: none 让空白的轨道区域穿透命中到舞台(空白点击仍算遮罩),图片自身\n 恢复 auto 接收手势。 */\n.wui-image-preview-track {\n pointer-events: none;\n\n position: absolute;\n inset: 0;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-slide {\n pointer-events: none;\n\n position: absolute;\n top: 0;\n left: 0;\n\n display: flex;\n align-items: center;\n justify-content: center;\n\n width: 100%;\n height: 100%;\n}\n\n.wui-image-preview-slide.is-prev {\n left: -100%;\n}\n\n.wui-image-preview-slide.is-next {\n left: 100%;\n}\n\n.wui-image-preview-image {\n pointer-events: auto;\n user-select: none;\n\n display: block;\n\n max-width: 100%;\n max-height: 100%;\n\n opacity: 0;\n object-fit: contain;\n\n /* 拖拽(平移)不参与 transform 过渡(见 .is-dragging),避免跟手位移滞后;\n swipe 只动轨道,图片 transform 在滑动期间不变。 */\n transition:\n opacity var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1)),\n transform var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n\n -webkit-user-drag: none;\n}\n\n.wui-image-preview-image.is-loaded {\n opacity: 1;\n}\n\n.wui-image-preview-stage.is-dragging .wui-image-preview-image {\n transition: opacity var(--wui-duration-trigger, 160ms) var(--wui-ease-enter, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n/* 轮播滑入/回位:跟手结束后把轨道 transform 过渡到目标位(当前图滑出、相邻图入位或\n 弹回)。transitionend 会触发组件内的收尾提交;时长/缓动可用组件级变量覆盖。 */\n.wui-image-preview-stage.is-settling .wui-image-preview-track {\n transition: transform var(--wui-duration-swipe-settle, 220ms) var(--wui-ease-swipe, cubic-bezier(0.32, 0.72, 0, 1));\n}\n\n.wui-image-preview-stage.is-dragging .wui-image-preview-track {\n transition: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-image-preview-image,\n .wui-image-preview-stage.is-dragging .wui-image-preview-image,\n .wui-image-preview-stage.is-settling .wui-image-preview-track {\n transition: opacity var(--wui-duration-feedback, 100ms);\n }\n}\n\n.wui-image-preview-controls {\n pointer-events: none;\n position: absolute;\n inset: 0;\n}\n\n.wui-image-preview-controls > * {\n pointer-events: auto;\n}\n\n.wui-image-preview-counter {\n /* pill 高 ≈ 24px(13px 行盒 + 上下 4px padding),光影取短边一半 */\n --wui-glass-corner-radius: 12px;\n\n position: absolute;\n top: var(--wui-image-preview-edge-gap, 20px);\n left: var(--wui-image-preview-edge-gap, 20px);\n\n padding: var(--wui-space-1, 4px) var(--wui-space-3, 12px);\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n\n font-size: var(--wui-font-size-readout, 13px);\n font-variant-numeric: tabular-nums;\n color: var(--wui-color-text, #1b1b1b);\n}\n\n.wui-image-preview-close {\n position: absolute;\n top: var(--wui-image-preview-edge-gap, 20px);\n right: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-nav {\n position: absolute;\n top: 50%;\n translate: 0 -50%;\n}\n\n.wui-image-preview-nav-prev {\n left: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-nav-next {\n right: var(--wui-image-preview-edge-gap, 20px);\n}\n\n.wui-image-preview-toolbar {\n /* 高 = 30px 图标按钮 + 上下 6px padding,光影取短边一半 */\n --wui-glass-corner-radius: 21px;\n\n position: absolute;\n bottom: var(--wui-image-preview-edge-gap, 20px);\n left: 50%;\n translate: -50% 0;\n\n display: flex;\n gap: 6px;\n align-items: center;\n\n padding: 6px;\n border-radius: var(--wui-radius-control, calc(infinity * 1px));\n}\n\n.wui-image-preview-scale {\n min-width: 48px;\n\n font-size: var(--wui-font-size-readout, 13px);\n font-variant-numeric: tabular-nums;\n color: var(--wui-color-text, #1b1b1b);\n text-align: center;\n}\n\ndialog[open].is-visible {\n .wui-image-preview-surface {\n transform: scale(1);\n }\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n opacity: 1;\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n opacity: 1;\n }\n}\n\ndialog[open].is-closing {\n pointer-events: none;\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n transition-duration: var(--wui-duration-dialog-exit, 260ms);\n }\n}\n\n@media (prefers-reduced-motion: reduce) {\n dialog,\n dialog[open].is-visible {\n transition-duration: var(--wui-duration-feedback, 100ms);\n\n .wui-image-preview-surface {\n transform: none;\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n .wui-image-preview-backdrop,\n .wui-image-preview-surface,\n .wui-image-preview-counter,\n .wui-image-preview-toolbar {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n\n .wui-image-preview-close::part(button),\n .wui-image-preview-nav::part(button) {\n transition-duration: var(--wui-duration-feedback, 100ms);\n }\n }\n}\n"],"mappings":""}
@@ -81,6 +81,9 @@ var _ = class extends r(l) {
81
81
  this._isDisabled || this.readonly || (this._value = "", this._formAssociation.sync(), this.dispatchEvent(new Event("input", {
82
82
  bubbles: !0,
83
83
  composed: !0
84
+ })), this.dispatchEvent(new Event("change", {
85
+ bubbles: !0,
86
+ composed: !0
84
87
  })));
85
88
  }
86
89
  preventMouseDownBlur(e) {
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/icon'\n\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input')\nexport class WebUiInput extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) type = 'text'\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n private readonly _fieldId = createFieldId('web-ui-input')\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const input = this.shadowRoot?.querySelector('input')\n if (input && input.value !== this._value) {\n input.value = this._value\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'typeMismatch', 'patternMismatch', 'tooLong', 'tooShort'],\n this._isDisabled\n )\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n private focusInput() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus()\n }\n\n // 公共 focus/blur 与 textarea 对齐:宿主自身没有 tab 位(不可聚焦),\n // 不重定向到内部原生控件的话,调用方拿到的是一次空操作\n override focus(options?: FocusOptions) {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('input')?.blur()\n }\n\n /**\n * 公共 API:全选当前值。\n *\n * `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。\n */\n select() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.select()\n }\n\n override render() {\n // readonly 下不可清除:清空按钮会修改值,与只读语义冲突\n const showClear = this.clearable && this._value && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-input-inner\" @click=${this.focusInput}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <input\n id=${this._fieldId}\n type=${this.type}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"清除\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input': WebUiInput\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input/index.ts"],"sourcesContent":["import '@/components/button'\nimport '@/components/icon'\n\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\nimport { ifDefined } from 'lit/directives/if-defined.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { jamCloseCircleF } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input')\nexport class WebUiInput extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) type = 'text'\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Boolean, reflect: true }) clearable = false\n @property({ type: Boolean, reflect: true }) full = false\n @property({ type: Boolean, reflect: true }) borderless = false\n @property({ type: String, attribute: 'aria-label' }) override ariaLabel: string | null = null\n\n @state() private _value = ''\n @state() private _focused = false\n @state() private _hasPrefix = false\n @state() private _hasSuffix = false\n\n private readonly _fieldId = createFieldId('web-ui-input')\n\n @property({ type: String, reflect: true })\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => this._value,\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated(changed: Map<string, unknown>) {\n if (changed.has('_value')) {\n const input = this.shadowRoot?.querySelector('input')\n if (input && input.value !== this._value) {\n input.value = this._value\n }\n }\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'typeMismatch', 'patternMismatch', 'tooLong', 'tooShort'],\n this._isDisabled\n )\n }\n\n private _onSlotChange(e: Event) {\n if (!(e.target instanceof HTMLSlotElement)) return\n const slot = e.target\n const hasContent = slot.assignedElements().length > 0\n if (slot.name === 'prefix') this._hasPrefix = hasContent\n if (slot.name === 'suffix') this._hasSuffix = hasContent\n }\n\n private handleInput(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;这里补发 composed 事件,\n // 兑现 $events/README 声明的公共 change 契约。\n private handleNativeChange(e: Event) {\n if (this._isDisabled || this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n this._value = e.target.value\n this._formAssociation.sync()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n private handleClear() {\n if (this._isDisabled || this.readonly) return\n this._value = ''\n this._formAssociation.sync()\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n // 清除是离散的已提交变更(不是连续输入):只派 input 会让 v-model.lazy\n // 等只监听 change 的消费者永远收不到清空,故与 input 一并派发。\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private preventMouseDownBlur(e: Event) {\n e.preventDefault()\n }\n\n private focusInput() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus()\n }\n\n // 公共 focus/blur 与 textarea 对齐:宿主自身没有 tab 位(不可聚焦),\n // 不重定向到内部原生控件的话,调用方拿到的是一次空操作\n override focus(options?: FocusOptions) {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.focus(options)\n }\n\n override blur() {\n this.shadowRoot?.querySelector('input')?.blur()\n }\n\n /**\n * 公共 API:全选当前值。\n *\n * `disabled` 时与 `focus()` 一致不产生效果;原生控件未渲染时安全 no-op。\n */\n select() {\n if (this._isDisabled) return\n this.shadowRoot?.querySelector('input')?.select()\n }\n\n override render() {\n // readonly 下不可清除:清空按钮会修改值,与只读语义冲突\n const showClear = this.clearable && this._value && !this.readonly\n\n return html`\n <div class=\"wui-glass wui-input-inner\" @click=${this.focusInput}>\n <slot name=\"prefix\" class=${classMap({ empty: !this._hasPrefix })} @slotchange=${this._onSlotChange}></slot>\n <input\n id=${this._fieldId}\n type=${this.type}\n placeholder=${this.placeholder}\n name=${this.name}\n aria-label=${ifDefined(this.ariaLabel)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n .value=${this._value}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n ${\n showClear\n ? html`<web-ui-button\n icon\n variant=\"ghost\"\n size=\"20\"\n aria-label=\"清除\"\n @pointerdown=${this.preventMouseDownBlur}\n @click=${this.handleClear}\n >\n <web-ui-icon .icon=${jamCloseCircleF} size=\"14\"></web-ui-icon>\n </web-ui-button>`\n : ''\n }\n <slot name=\"suffix\" class=${classMap({ empty: !this._hasSuffix })} @slotchange=${this._onSlotChange}></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n focus: FocusEvent\n blur: FocusEvent\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input': WebUiInput\n }\n}\n"],"mappings":"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"}
@@ -372,7 +372,13 @@ export declare class WebUiInputNumber extends WebUiInputNumber_base {
372
372
  required: boolean;
373
373
  get precision(): number;
374
374
  set precision(v: number);
375
+ /** 原始值:只做 min/max 收敛,不按精度取整(取整发生在 get value)。 */
375
376
  private _value;
377
+ /**
378
+ * 编辑中的原始文本。null 表示非编辑态,显示由 `value` 派生;
379
+ * 非 null 时显示原文,避免把用户正在输入的 "1." / "0.10" 立刻改写。
380
+ */
381
+ private _draft;
376
382
  private _min;
377
383
  private _max;
378
384
  private _step;
@@ -12,14 +12,14 @@ import { customElement as m, property as h, state as g } from "lit/decorators.js
12
12
  //#region src/components/input-number/index.ts
13
13
  var _ = class extends i(d) {
14
14
  constructor(...e) {
15
- super(...e), this.placeholder = "", this.name = "", this.disabled = !1, this.readonly = !1, this.required = !1, this._value = 0, this._min = 0, this._max = Infinity, this._step = 1, this._focused = !1, this._precision = 0, this._fieldId = l("web-ui-input-number"), this._formAssociation = o({
15
+ super(...e), this.placeholder = "", this.name = "", this.disabled = !1, this.readonly = !1, this.required = !1, this._value = 0, this._draft = null, this._min = 0, this._max = Infinity, this._step = 1, this._focused = !1, this._precision = 0, this._fieldId = l("web-ui-input-number"), this._formAssociation = o({
16
16
  host: this,
17
- getState: () => this._value,
17
+ getState: () => this.value,
18
18
  setState: (e) => {
19
19
  this.value = e;
20
20
  },
21
- getFormValue: () => String(this._value),
22
- getFormState: () => String(this._value),
21
+ getFormValue: () => String(this.value),
22
+ getFormState: () => String(this.value),
23
23
  restoreState: (e) => {
24
24
  if (typeof e != "string") return;
25
25
  let t = Number(e);
@@ -36,15 +36,13 @@ var _ = class extends i(d) {
36
36
  }
37
37
  set precision(e) {
38
38
  let t = this._precision;
39
- this._precision = e;
40
- let n = this.clamp(this.round(this._value));
41
- n !== this._value && (this._value = n, this._formAssociation.sync()), this.requestUpdate("precision", t);
39
+ this._precision = e, this.requestUpdate("precision", t);
42
40
  }
43
41
  get value() {
44
- return this._value;
42
+ return this.clamp(this.round(this._value));
45
43
  }
46
44
  set value(e) {
47
- let t = this.clamp(this.round(e)), n = this._value;
45
+ let t = this.clamp(e), n = this._value;
48
46
  this._value = t, this._formAssociation.sync(), this.requestUpdate("value", n);
49
47
  }
50
48
  get _isDisabled() {
@@ -72,10 +70,10 @@ var _ = class extends i(d) {
72
70
  return this._step;
73
71
  }
74
72
  get atMin() {
75
- return this._value <= this._min;
73
+ return this.value <= this._min;
76
74
  }
77
75
  get atMax() {
78
- return this._value >= this._max;
76
+ return this.value >= this._max;
79
77
  }
80
78
  updated() {
81
79
  this._syncValidity(), this.toggleAttribute("focused", this._focused);
@@ -97,32 +95,40 @@ var _ = class extends i(d) {
97
95
  return Math.round(e * t) / t;
98
96
  }
99
97
  setValueAndNotify(e) {
100
- this.value = this.clamp(this.round(e)), this.dispatchEvent(new Event("input", {
98
+ this._draft = null, this.value = this.clamp(this.round(e)), this.dispatchEvent(new Event("input", {
101
99
  bubbles: !0,
102
100
  composed: !0
103
101
  }));
104
102
  }
105
103
  handleStep(e) {
106
- this._isDisabled || this.readonly || this.setValueAndNotify(this._value + e * this._effectiveStep);
104
+ this._isDisabled || this.readonly || this.setValueAndNotify(this.value + e * this._effectiveStep);
107
105
  }
108
106
  handleInput(e) {
109
107
  if (this.readonly || !(e.target instanceof HTMLInputElement)) return;
110
108
  let t = e.target.value;
111
- t !== "" && t !== "-" && (this._value = this.clamp(this.round(Number(t))), this._formAssociation.sync(), this._syncValidity());
109
+ this._draft = t;
110
+ let n = t === "" || t === "-" ? NaN : Number(t);
111
+ Number.isFinite(n) && (this._value = this.clamp(n), this._formAssociation.sync()), this._syncValidity();
112
112
  }
113
113
  handleNativeChange(e) {
114
114
  if (this.readonly || !(e.target instanceof HTMLInputElement)) return;
115
115
  let t = e.target.value;
116
- t !== "" && t !== "-" && (this._value = this.clamp(this.round(Number(t))), this._formAssociation.sync(), this._syncValidity(), this.dispatchEvent(new Event("change", {
116
+ this._draft = null;
117
+ let n = t === "" || t === "-" ? NaN : Number(t);
118
+ if (!Number.isFinite(n)) {
119
+ this.requestUpdate(), this._syncValidity();
120
+ return;
121
+ }
122
+ this._value = this.clamp(n), this._formAssociation.sync(), this._syncValidity(), this.dispatchEvent(new Event("change", {
117
123
  bubbles: !0,
118
124
  composed: !0
119
- })));
125
+ }));
120
126
  }
121
127
  handleKeydown(e) {
122
- this._isDisabled || this.readonly || (e.key === "ArrowUp" ? (e.preventDefault(), this.setValueAndNotify(this._value + this._effectiveStep), this.dispatchEvent(new Event("change", {
128
+ this._isDisabled || this.readonly || (e.key === "ArrowUp" ? (e.preventDefault(), this.setValueAndNotify(this.value + this._effectiveStep), this.dispatchEvent(new Event("change", {
123
129
  bubbles: !0,
124
130
  composed: !0
125
- }))) : e.key === "ArrowDown" && (e.preventDefault(), this.setValueAndNotify(this._value - this._effectiveStep), this.dispatchEvent(new Event("change", {
131
+ }))) : e.key === "ArrowDown" && (e.preventDefault(), this.setValueAndNotify(this.value - this._effectiveStep), this.dispatchEvent(new Event("change", {
126
132
  bubbles: !0,
127
133
  composed: !0
128
134
  }))));
@@ -131,7 +137,7 @@ var _ = class extends i(d) {
131
137
  this._isDisabled || (this._focused = !0);
132
138
  }
133
139
  handleBlur() {
134
- this._focused = !1;
140
+ this._focused = !1, this._draft !== null && (this._draft = null, this.requestUpdate());
135
141
  }
136
142
  render() {
137
143
  return f`
@@ -149,7 +155,7 @@ var _ = class extends i(d) {
149
155
  type="number"
150
156
  placeholder=${this.placeholder}
151
157
  name=${this.name}
152
- .value=${String(this._value)}
158
+ .value=${this._draft ?? String(this.value)}
153
159
  ?disabled=${this._isDisabled}
154
160
  ?readonly=${this.readonly}
155
161
  ?required=${this.required}
@@ -189,7 +195,7 @@ e([h({
189
195
  })], _.prototype, "required", void 0), e([h({
190
196
  type: Number,
191
197
  reflect: !0
192
- })], _.prototype, "precision", null), e([g()], _.prototype, "_value", void 0), e([g()], _.prototype, "_min", void 0), e([g()], _.prototype, "_max", void 0), e([g()], _.prototype, "_step", void 0), e([g()], _.prototype, "_focused", void 0), e([h({
198
+ })], _.prototype, "precision", null), e([g()], _.prototype, "_value", void 0), e([g()], _.prototype, "_draft", void 0), e([g()], _.prototype, "_min", void 0), e([g()], _.prototype, "_max", void 0), e([g()], _.prototype, "_step", void 0), e([g()], _.prototype, "_focused", void 0), e([h({
193
199
  type: Number,
194
200
  reflect: !0
195
201
  })], _.prototype, "value", null), e([h({
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input-number/index.ts"],"sourcesContent":["import '@/components/icon'\n\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { lucideMinus, lucidePlus } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\nimport { normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input-number')\nexport class WebUiInputNumber extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Number, reflect: true })\n get precision(): number {\n return this._precision\n }\n // 精度变化时重算现有值,避免浮点累积误差\n set precision(v: number) {\n const old = this._precision\n this._precision = v\n const current = this.clamp(this.round(this._value))\n if (current !== this._value) {\n this._value = current\n this._formAssociation.sync()\n }\n this.requestUpdate('precision', old)\n }\n\n @state() private _value = 0\n @state() private _min = 0\n @state() private _max = Infinity\n @state() private _step = 1\n @state() private _focused = false\n private _precision = 0\n private readonly _fieldId = createFieldId('web-ui-input-number')\n\n @property({ type: Number, reflect: true })\n get value(): number {\n return this._value\n }\n\n set value(v: number) {\n const clamped = this.clamp(this.round(v))\n const old = this._value\n this._value = clamped\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @property({ type: Number, reflect: true })\n get min(): number {\n return this._min\n }\n\n set min(v: unknown) {\n this._min = normalizeNumber(v, -Infinity, Infinity, 0)\n }\n\n @property({ type: Number, reflect: true })\n get max(): number {\n return this._max\n }\n\n set max(v: unknown) {\n this._max = normalizeNumber(v, -Infinity, Infinity, Infinity)\n }\n\n @property({ type: Number, reflect: true })\n get step(): number {\n return this._step\n }\n\n // 步进值,默认 1;若声明了 precision 则以 precision 退化步进\n set step(v: unknown) {\n this._step = normalizeNumber(v, 0, Infinity, 1)\n }\n\n private get _effectiveStep(): number {\n return this._step\n }\n\n private get atMin(): boolean {\n return this._value <= this._min\n }\n\n private get atMax(): boolean {\n return this._value >= this._max\n }\n\n private readonly _formAssociation = defineFormAssociation<number>({\n host: this,\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => String(this._value),\n getFormState: () => String(this._value),\n restoreState: state => {\n if (typeof state !== 'string') return\n const value = Number(state)\n if (Number.isFinite(value)) this.value = value\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated() {\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'rangeUnderflow', 'rangeOverflow', 'stepMismatch'],\n this._isDisabled\n )\n }\n\n private clamp(v: number): number {\n return Math.min(this._max, Math.max(this._min, v))\n }\n\n private round(v: number): number {\n const f = 10 ** this.precision\n return Math.round(v * f) / f\n }\n\n private setValueAndNotify(v: number) {\n this.value = this.clamp(this.round(v))\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private handleStep(direction: 1 | -1) {\n if (this._isDisabled || this.readonly) return\n this.setValueAndNotify(this._value + direction * this._effectiveStep)\n }\n\n private handleInput(e: Event) {\n if (this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n const raw = e.target.value\n if (raw === '' || raw === '-') return\n this._value = this.clamp(this.round(Number(raw)))\n this._formAssociation.sync()\n this._syncValidity()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;文本失焦提交时补发 composed 事件,\n // 与 ArrowUp/ArrowDown 的 change 行为对齐。\n private handleNativeChange(e: Event) {\n if (this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n const raw = e.target.value\n if (raw === '' || raw === '-') return\n this._value = this.clamp(this.round(Number(raw)))\n this._formAssociation.sync()\n this._syncValidity()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (this._isDisabled || this.readonly) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n this.setValueAndNotify(this._value + this._effectiveStep)\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n this.setValueAndNotify(this._value - this._effectiveStep)\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n }\n\n override render() {\n return html`\n <div class=\"wui-glass wui-input-inner\">\n <button\n class=\"num-btn\"\n aria-label=\"Decrease\"\n ?disabled=${this._isDisabled || this.readonly || this.atMin}\n @click=${() => this.handleStep(-1)}\n >\n <web-ui-icon .icon=${lucideMinus}></web-ui-icon>\n </button>\n <input\n id=${this._fieldId}\n type=\"number\"\n placeholder=${this.placeholder}\n name=${this.name}\n .value=${String(this._value)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @keydown=${this.handleKeydown}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n <button\n class=\"num-btn\"\n aria-label=\"Increase\"\n ?disabled=${this._isDisabled || this.readonly || this.atMax}\n @click=${() => this.handleStep(1)}\n >\n <web-ui-icon .icon=${lucidePlus}></web-ui-icon>\n </button>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input-number': WebUiInputNumber\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/input-number/index.ts"],"sourcesContent":["import '@/components/icon'\n\nimport { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\nimport { lucideMinus, lucidePlus } from '@/icons'\nimport {\n FormAssociated,\n defineFormAssociation,\n forwardInputValidity,\n FormAssociationController\n} from '@/shared/form-association'\nimport { createFieldId } from '@/shared/form-association/field-id'\nimport { normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-input-number')\nexport class WebUiInputNumber extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: String, reflect: true }) placeholder = ''\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) readonly = false\n @property({ type: Boolean, reflect: true }) required = false\n @property({ type: Number, reflect: true })\n get precision(): number {\n return this._precision\n }\n // 精度只影响读取(get value)/提交时的规整,不在赋值时抹掉原始小数:\n // `<web-ui-input-number value=\"1.5\" precision=\"2\">` 先跑 value setter、\n // 后跑 precision setter,若赋值时就按当时的默认精度 0 取整,1.5 会先变 2,\n // 之后再声明 precision=2 也恢复不了。\n set precision(v: number) {\n const old = this._precision\n this._precision = v\n this.requestUpdate('precision', old)\n }\n\n /** 原始值:只做 min/max 收敛,不按精度取整(取整发生在 get value)。 */\n @state() private _value = 0\n\n /**\n * 编辑中的原始文本。null 表示非编辑态,显示由 `value` 派生;\n * 非 null 时显示原文,避免把用户正在输入的 \"1.\" / \"0.10\" 立刻改写。\n */\n @state() private _draft: string | null = null\n @state() private _min = 0\n @state() private _max = Infinity\n @state() private _step = 1\n @state() private _focused = false\n private _precision = 0\n private readonly _fieldId = createFieldId('web-ui-input-number')\n\n @property({ type: Number, reflect: true })\n get value(): number {\n return this.clamp(this.round(this._value))\n }\n\n set value(v: number) {\n const next = this.clamp(v)\n const old = this._value\n this._value = next\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n @property({ type: Number, reflect: true })\n get min(): number {\n return this._min\n }\n\n set min(v: unknown) {\n this._min = normalizeNumber(v, -Infinity, Infinity, 0)\n }\n\n @property({ type: Number, reflect: true })\n get max(): number {\n return this._max\n }\n\n set max(v: unknown) {\n this._max = normalizeNumber(v, -Infinity, Infinity, Infinity)\n }\n\n @property({ type: Number, reflect: true })\n get step(): number {\n return this._step\n }\n\n // 步进值,默认 1;若声明了 precision 则以 precision 退化步进\n set step(v: unknown) {\n this._step = normalizeNumber(v, 0, Infinity, 1)\n }\n\n private get _effectiveStep(): number {\n return this._step\n }\n\n private get atMin(): boolean {\n return this.value <= this._min\n }\n\n private get atMax(): boolean {\n return this.value >= this._max\n }\n\n private readonly _formAssociation = defineFormAssociation<number>({\n host: this,\n getState: () => this.value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => String(this.value),\n getFormState: () => String(this.value),\n restoreState: state => {\n if (typeof state !== 'string') return\n const value = Number(state)\n if (Number.isFinite(value)) this.value = value\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override updated() {\n this._syncValidity()\n this.toggleAttribute('focused', this._focused)\n }\n\n private _syncValidity() {\n const input = this.shadowRoot?.querySelector('input')\n const internals = this._formAssociation.getInternals()\n if (!internals || !input || typeof internals.setValidity !== 'function') return\n forwardInputValidity(\n internals,\n input,\n ['valueMissing', 'rangeUnderflow', 'rangeOverflow', 'stepMismatch'],\n this._isDisabled\n )\n }\n\n private clamp(v: number): number {\n return Math.min(this._max, Math.max(this._min, v))\n }\n\n private round(v: number): number {\n const f = 10 ** this.precision\n return Math.round(v * f) / f\n }\n\n private setValueAndNotify(v: number) {\n this._draft = null\n this.value = this.clamp(this.round(v))\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n }\n\n private handleStep(direction: 1 | -1) {\n if (this._isDisabled || this.readonly) return\n this.setValueAndNotify(this.value + direction * this._effectiveStep)\n }\n\n // 输入过程只更新草稿与可解析的原始值,显示保持在草稿上:\n // 逐字把 \"1.\" 规整成 \"1\" 会打断输入(与原生 number 输入框的编辑体验一致)。\n private handleInput(e: Event) {\n if (this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n const raw = e.target.value\n this._draft = raw\n const parsed = raw === '' || raw === '-' ? Number.NaN : Number(raw)\n if (Number.isFinite(parsed)) {\n this._value = this.clamp(parsed)\n this._formAssociation.sync()\n }\n this._syncValidity()\n }\n\n // 原生 change 不 composed,被 shadow root 挡住;文本失焦提交时补发 composed 事件,\n // 与 ArrowUp/ArrowDown 的 change 行为对齐。\n // 提交点上做规整:非法/空草稿回填已提交值(value 不变、不派发 change),\n // 合法草稿写入 value 后由 get value 按精度规整。\n private handleNativeChange(e: Event) {\n if (this.readonly) return\n if (!(e.target instanceof HTMLInputElement)) return\n const raw = e.target.value\n this._draft = null\n const parsed = raw === '' || raw === '-' ? Number.NaN : Number(raw)\n if (!Number.isFinite(parsed)) {\n // 回填:草稿清空后显示重新由 value 派生\n this.requestUpdate()\n this._syncValidity()\n return\n }\n this._value = this.clamp(parsed)\n this._formAssociation.sync()\n this._syncValidity()\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeydown(e: KeyboardEvent) {\n if (this._isDisabled || this.readonly) return\n if (e.key === 'ArrowUp') {\n e.preventDefault()\n this.setValueAndNotify(this.value + this._effectiveStep)\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n } else if (e.key === 'ArrowDown') {\n e.preventDefault()\n this.setValueAndNotify(this.value - this._effectiveStep)\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n }\n\n private handleFocus() {\n if (!this._isDisabled) this._focused = true\n }\n\n private handleBlur() {\n this._focused = false\n // change 的时序在部分平台晚于 blur;草稿仍存在说明还没提交,直接回填,\n // 避免「显示空、value 仍是旧值」的持久脱同步。\n if (this._draft !== null) {\n this._draft = null\n this.requestUpdate()\n }\n }\n\n override render() {\n return html`\n <div class=\"wui-glass wui-input-inner\">\n <button\n class=\"num-btn\"\n aria-label=\"Decrease\"\n ?disabled=${this._isDisabled || this.readonly || this.atMin}\n @click=${() => this.handleStep(-1)}\n >\n <web-ui-icon .icon=${lucideMinus}></web-ui-icon>\n </button>\n <input\n id=${this._fieldId}\n type=\"number\"\n placeholder=${this.placeholder}\n name=${this.name}\n .value=${this._draft ?? String(this.value)}\n ?disabled=${this._isDisabled}\n ?readonly=${this.readonly}\n ?required=${this.required}\n @input=${this.handleInput}\n @change=${this.handleNativeChange}\n @keydown=${this.handleKeydown}\n @focus=${this.handleFocus}\n @blur=${this.handleBlur}\n />\n <button\n class=\"num-btn\"\n aria-label=\"Increase\"\n ?disabled=${this._isDisabled || this.readonly || this.atMax}\n @click=${() => this.handleStep(1)}\n >\n <web-ui-icon .icon=${lucidePlus}></web-ui-icon>\n </button>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-input-number': WebUiInputNumber\n }\n}\n"],"mappings":"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"}
@@ -373,6 +373,13 @@ export declare class WebUiRadioGroup extends WebUiRadioGroup_base {
373
373
  private _direction;
374
374
  private _value;
375
375
  private readonly _groupController;
376
+ /**
377
+ * 成员被移除后,组值若仍指向它就成了幽灵值(表单照旧提交一个不存在的选项)。
378
+ * 剪枝条件是「曾经出现在成员里、现在消失了」——不能用「不在现存成员里就剪」,
379
+ * 否则 v-if / 异步渲染时先于成员到达的合法组值会被误清。
380
+ */
381
+ private _knownMemberValues;
382
+ private _pruneRemovedMembers;
376
383
  get value(): string;
377
384
  set value(v: string);
378
385
  private get _isDisabled();
@@ -17,13 +17,24 @@ var m = ["horizontal", "vertical"], h = class extends t(c) {
17
17
  },
18
18
  getDisabled: () => this._isDisabled,
19
19
  isItem: (e) => e instanceof HTMLElement && e.matches("web-ui-radio"),
20
- isItemSelected: (e, t) => e.value === t,
20
+ isItemSelected: (e, t) => {
21
+ if (!t) return !1;
22
+ let n;
23
+ for (let e of this.querySelectorAll("web-ui-radio")) if (e.value === t && (n ||= e, e.checked)) {
24
+ n = e;
25
+ break;
26
+ }
27
+ return e === n;
28
+ },
21
29
  getNextValue: (e, t) => e.checked ? e.value : t,
22
30
  valuesEqual: (e, t) => e === t,
23
31
  copyValue: (e) => e,
24
32
  setItemSelected: (e, t) => {
25
33
  e.checked = t;
26
34
  },
35
+ resyncOnUnchangedValue: (e) => {
36
+ for (let t of this.querySelectorAll("web-ui-radio")) t.checked = t === e;
37
+ },
27
38
  dispatchValueChange: () => {
28
39
  this.dispatchEvent(new Event("input", {
29
40
  bubbles: !0,
@@ -33,7 +44,7 @@ var m = ["horizontal", "vertical"], h = class extends t(c) {
33
44
  composed: !0
34
45
  }));
35
46
  }
36
- }).make()), this._formAssociation = r({
47
+ }).make(), { afterSync: () => this._pruneRemovedMembers() }), this._knownMemberValues = /* @__PURE__ */ new Set(), this._formAssociation = r({
37
48
  host: this,
38
49
  initialize: () => {
39
50
  let e = this.getAttribute("value");
@@ -61,6 +72,13 @@ var m = ["horizontal", "vertical"], h = class extends t(c) {
61
72
  let t = this._direction;
62
73
  this._direction = o(e, m, "vertical"), this.requestUpdate("direction", t);
63
74
  }
75
+ _pruneRemovedMembers() {
76
+ if (!this._value) return;
77
+ let e = [...this.querySelectorAll("web-ui-radio")];
78
+ if (e.length === 0) return;
79
+ let t = new Set(e.map((e) => e.value));
80
+ !t.has(this._value) && this._knownMemberValues.has(this._value) && (this.value = ""), this._knownMemberValues = t;
81
+ }
64
82
  get value() {
65
83
  return this._value;
66
84
  }
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/radio-group/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { defineGroupCoordinator, GroupController } from '@/shared/group-management'\nimport { normalizeLiteral } from '@/shared/normalize'\n\nimport type { WebUiRadio } from '../radio'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_DIRECTIONS = ['horizontal', 'vertical'] as const\n\n@customElement('web-ui-radio-group')\nexport class WebUiRadioGroup extends FormAssociated(LitElement) {\n static override styles = unsafeCSS(style)\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n\n @property({ type: String, reflect: true })\n get direction(): 'horizontal' | 'vertical' {\n return this._direction\n }\n set direction(v: string) {\n const old = this._direction\n this._direction = normalizeLiteral(v, ALLOWED_DIRECTIONS, 'vertical')\n this.requestUpdate('direction', old)\n }\n private _direction: 'horizontal' | 'vertical' = 'vertical'\n\n @state() private _value = ''\n\n private readonly _groupController = new GroupController(\n this,\n defineGroupCoordinator<WebUiRadio, string>({\n host: this,\n getItems: () => [...this.querySelectorAll<WebUiRadio>('web-ui-radio')],\n getValue: () => this._value,\n setValue: value => {\n this.value = value\n },\n getDisabled: () => this._isDisabled,\n isItem: (target): target is WebUiRadio => target instanceof HTMLElement && target.matches('web-ui-radio'),\n isItemSelected: (radio, value) => radio.value === value,\n getNextValue: (radio, value) => (radio.checked ? radio.value : value),\n valuesEqual: (a, b) => a === b,\n copyValue: value => value,\n setItemSelected: (radio, selected) => {\n radio.checked = selected\n },\n dispatchValueChange: () => {\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n }).make()\n )\n\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n const attributeValue = this.getAttribute('value')\n if (attributeValue !== null) this.value = attributeValue\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => (this.name && this._value ? this._value : null),\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override formDisabledCallback(disabled: boolean) {\n this._formAssociation.setDisabled(disabled)\n this._groupController.sync()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this._value) {\n internals.setValidity({})\n return\n }\n internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n override render() {\n return html`<div class=\"wui-radio-group\"><slot></slot></div>`\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-radio-group': WebUiRadioGroup\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/radio-group/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { defineGroupCoordinator, GroupController } from '@/shared/group-management'\nimport { normalizeLiteral } from '@/shared/normalize'\n\nimport type { WebUiRadio } from '../radio'\n\nimport style from './style.css?inline'\n\nconst ALLOWED_DIRECTIONS = ['horizontal', 'vertical'] as const\n\n@customElement('web-ui-radio-group')\nexport class WebUiRadioGroup extends FormAssociated(LitElement) {\n static override styles = unsafeCSS(style)\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) required = false\n\n @property({ type: String, reflect: true })\n get direction(): 'horizontal' | 'vertical' {\n return this._direction\n }\n set direction(v: string) {\n const old = this._direction\n this._direction = normalizeLiteral(v, ALLOWED_DIRECTIONS, 'vertical')\n this.requestUpdate('direction', old)\n }\n private _direction: 'horizontal' | 'vertical' = 'vertical'\n\n @state() private _value = ''\n\n private readonly _groupController = new GroupController(\n this,\n defineGroupCoordinator<WebUiRadio, string>({\n host: this,\n getItems: () => [...this.querySelectorAll<WebUiRadio>('web-ui-radio')],\n getValue: () => this._value,\n setValue: value => {\n this.value = value\n },\n getDisabled: () => this._isDisabled,\n isItem: (target): target is WebUiRadio => target instanceof HTMLElement && target.matches('web-ui-radio'),\n // 选中判定按元素身份而非 value 相等:value 重复时按值比较会把多个成员\n // 同时标为选中,破坏单选不变式。重复值取第一个匹配(或已选中的那个,\n // 避免每次 sync 把选中项跳回第一个)。\n isItemSelected: (radio, value) => {\n if (!value) return false\n let selected: WebUiRadio | undefined\n for (const item of this.querySelectorAll<WebUiRadio>('web-ui-radio')) {\n if (item.value !== value) continue\n if (!selected) selected = item\n if (item.checked) {\n selected = item\n break\n }\n }\n return radio === selected\n },\n getNextValue: (radio, value) => (radio.checked ? radio.value : value),\n valuesEqual: (a, b) => a === b,\n copyValue: value => value,\n setItemSelected: (radio, selected) => {\n radio.checked = selected\n },\n // 点击同值成员时 value 不变,仍需把选中态收到被点击的那一项\n resyncOnUnchangedValue: clicked => {\n for (const radio of this.querySelectorAll<WebUiRadio>('web-ui-radio')) {\n radio.checked = radio === clicked\n }\n },\n dispatchValueChange: () => {\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n }).make(),\n // 每次成员集合同步后把组值剪到现存成员范围内\n { afterSync: () => this._pruneRemovedMembers() }\n )\n\n /**\n * 成员被移除后,组值若仍指向它就成了幽灵值(表单照旧提交一个不存在的选项)。\n * 剪枝条件是「曾经出现在成员里、现在消失了」——不能用「不在现存成员里就剪」,\n * 否则 v-if / 异步渲染时先于成员到达的合法组值会被误清。\n */\n private _knownMemberValues = new Set<string>()\n\n private _pruneRemovedMembers() {\n if (!this._value) return\n const items = [...this.querySelectorAll<WebUiRadio>('web-ui-radio')]\n if (items.length === 0) return\n const alive = new Set(items.map(item => item.value))\n if (!alive.has(this._value) && this._knownMemberValues.has(this._value)) this.value = ''\n this._knownMemberValues = alive\n }\n\n get value(): string {\n return this._value\n }\n\n set value(v: string) {\n const old = this._value\n this._value = v\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n private readonly _formAssociation = defineFormAssociation<string>({\n host: this,\n initialize: () => {\n const attributeValue = this.getAttribute('value')\n if (attributeValue !== null) this.value = attributeValue\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => (this.name && this._value ? this._value : null),\n getFormState: () => this._value,\n restoreState: state => {\n if (typeof state === 'string') this.value = state\n },\n syncValidity: () => this._syncValidity()\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n override formDisabledCallback(disabled: boolean) {\n this._formAssociation.setDisabled(disabled)\n this._groupController.sync()\n }\n\n private _syncValidity() {\n const internals = this._formAssociation.getInternals()\n if (!internals || typeof internals.setValidity !== 'function') return\n if (this._isDisabled || !this.required || this._value) {\n internals.setValidity({})\n return\n }\n internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n override render() {\n return html`<div class=\"wui-radio-group\"><slot></slot></div>`\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-radio-group': WebUiRadioGroup\n }\n}\n"],"mappings":"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"}
@@ -100,8 +100,8 @@ var v = class extends r(u) {
100
100
  }
101
101
  _normalizeValue(e) {
102
102
  if (!Number.isFinite(e)) return this.min;
103
- let t = Math.min(this.min, this.max), n = Math.max(this.min, this.max), r = c(e, t, n), i = Math.round((r - t) / this._safeStep);
104
- return Number((t + i * this._safeStep).toFixed(this._precision));
103
+ let t = Math.min(this.min, this.max), n = Math.max(this.min, this.max), r = c(e, t, n), i = Math.round((r - t) / this._safeStep), a = Number((t + i * this._safeStep).toFixed(this._precision));
104
+ return c(a, t, n);
105
105
  }
106
106
  get _precision() {
107
107
  return Math.max(this._decimalPlaces(this.min), this._decimalPlaces(this.max), this._decimalPlaces(this._safeStep));
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/slider/index.ts"],"sourcesContent":["import { html, LitElement, 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 { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { attachDragGesture, type DragGestureHandle } from '@/shared/gesture/drag-gesture'\nimport { clamp } from '@/shared/gesture/physics'\nimport { normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-slider')\nexport class WebUiSlider extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) marks = false\n @property({ type: Boolean, reflect: true }) vertical = false\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) required = false\n\n // value 使用访问器模式同步表单关联状态。\n private _value = 0\n @property({ type: Number, reflect: true })\n get value(): number {\n return this._value\n }\n set value(v: number) {\n const normalized = normalizeNumber(v, -Infinity, Infinity, 0)\n const old = this._value\n this._value = normalized\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private _min = 0\n @property({ type: Number, reflect: true })\n get min(): number {\n return this._min\n }\n set min(v: number) {\n this._min = normalizeNumber(v, -Infinity, Infinity, 0)\n }\n\n private _max = 100\n @property({ type: Number, reflect: true })\n get max(): number {\n return this._max\n }\n set max(v: number) {\n this._max = normalizeNumber(v, -Infinity, Infinity, 100)\n }\n\n private _step = 1\n @property({ type: Number, reflect: true })\n get step(): number {\n return this._step\n }\n set step(v: number) {\n this._step = normalizeNumber(v, -Infinity, Infinity, 1)\n }\n\n @state() private _dragging = false\n @state() private _pressed = false\n // 避免未改变数值的点击被误认为一次表单提交\n private _interactionStartValue: number | undefined\n private _dragGestureHandle: DragGestureHandle | null = null\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = null\n }\n\n private readonly _formAssociation = defineFormAssociation<number>({\n host: this,\n initialize: () => {\n this._value = this._normalizeValue(this._value)\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => String(this._value),\n getFormState: () => String(this._value),\n restoreState: state => {\n if (typeof state !== 'string') return\n const value = Number(state)\n if (Number.isFinite(value)) this.value = value\n },\n syncValidity: internals => {\n if (typeof internals.setValidity === 'function') internals.setValidity({})\n }\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n override willUpdate(changed: Map<string, unknown>) {\n if (changed.has('value') || changed.has('min') || changed.has('max') || changed.has('step')) {\n const normalizedValue = this._normalizeValue(this.value)\n if (normalizedValue !== this.value) this.value = normalizedValue\n }\n }\n\n override focus(options?: FocusOptions) {\n this._slider?.focus(options)\n }\n\n override blur() {\n this._slider?.blur()\n }\n\n private get _slider(): HTMLDivElement | null {\n return this.renderRoot.querySelector<HTMLDivElement>('[role=\"slider\"]')\n }\n\n private get _range(): number {\n return Math.max(0, this.max - this.min)\n }\n\n private get _percent(): number {\n return this._range === 0 ? 0 : ((this.value - this.min) / this._range) * 100\n }\n\n private get _markValues(): number[] {\n if (!this.marks || this._range === 0) return []\n const count = Math.min(100, Math.floor(this._range / this._safeStep))\n return Array.from({ length: count + 1 }, (_, index) => this.min + index * this._safeStep)\n }\n\n private get _safeStep(): number {\n return Number.isFinite(this.step) && this.step > 0 ? this.step : 1\n }\n\n private _normalizeValue(value: number): number {\n if (!Number.isFinite(value)) return this.min\n const lower = Math.min(this.min, this.max)\n const upper = Math.max(this.min, this.max)\n const clamped = clamp(value, lower, upper)\n const steps = Math.round((clamped - lower) / this._safeStep)\n return Number((lower + steps * this._safeStep).toFixed(this._precision))\n }\n\n private get _precision(): number {\n return Math.max(this._decimalPlaces(this.min), this._decimalPlaces(this.max), this._decimalPlaces(this._safeStep))\n }\n\n private _decimalPlaces(value: number): number {\n const [, decimal = ''] = String(value).split('.')\n return decimal.length\n }\n\n private _setValue(value: number): boolean {\n if (this._isDisabled) return false\n const nextValue = this._normalizeValue(value)\n if (nextValue === this.value) return false\n this.value = nextValue\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n return true\n }\n\n private _setValueFromPointer(event: PointerEvent): boolean {\n const track = this._slider\n if (!track) return false\n const rect = track.getBoundingClientRect()\n if (this.vertical) {\n if (rect.height <= 0) return false\n // 视觉上的数值从下往上增长,与坐标系相反。\n const ratio = clamp(1 - (event.clientY - rect.top) / rect.height, 0, 1)\n return this._setValue(this.min + ratio * this._range)\n }\n if (rect.width <= 0) return false\n const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1)\n return this._setValue(this.min + ratio * this._range)\n }\n\n private _handlePointerDown(event: PointerEvent) {\n if (this._isDisabled) return\n this._pressed = true\n this._interactionStartValue = this.value\n this._setValueFromPointer(event)\n\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = attachDragGesture(event, {\n axis: this.vertical ? 'y' : 'x',\n threshold: 6,\n onMove: (_info, e) => {\n this._dragging = true\n this._setValueFromPointer(e)\n },\n onEnd: (_info, e) => {\n this._setValueFromPointer(e)\n this._pressed = false\n this._dragging = false\n if (this._interactionStartValue !== this.value) {\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n this._interactionStartValue = undefined\n },\n onTap: () => {\n if (this._interactionStartValue !== this.value) {\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n },\n onCancel: () => {\n this._pressed = false\n this._dragging = false\n this._interactionStartValue = undefined\n }\n })\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n if (this._isDisabled) return\n const pageStep = this._safeStep * 10\n const keyValues: Record<string, number> = {\n ArrowDown: this.value - this._safeStep,\n ArrowLeft: this.value - this._safeStep,\n ArrowUp: this.value + this._safeStep,\n ArrowRight: this.value + this._safeStep,\n PageDown: this.value - pageStep,\n PageUp: this.value + pageStep,\n Home: this.min,\n End: this.max\n }\n const nextValue = keyValues[event.key]\n if (nextValue === undefined) return\n event.preventDefault()\n if (this._setValue(nextValue)) this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n override render() {\n const trackClass = classMap({\n 'wui-slider-track': true,\n 'is-dragging': this._dragging,\n 'is-disabled': this._isDisabled\n })\n const thumbClass = classMap({\n 'wui-slider-thumb': true,\n 'wui-glass': true,\n 'is-pressed': this._pressed,\n 'is-dragging': this._dragging\n })\n const trackStyle = styleMap({ '--percent': `${this._percent}%` })\n\n return html`\n <div\n class=${trackClass}\n style=${trackStyle}\n role=\"slider\"\n tabindex=${this._isDisabled ? '-1' : '0'}\n aria-label=\"滑块\"\n aria-orientation=${this.vertical ? 'vertical' : 'horizontal'}\n aria-valuemin=${this.min}\n aria-valuemax=${this.max}\n aria-valuenow=${this.value}\n aria-disabled=${String(this._isDisabled)}\n @keydown=${this._handleKeyDown}\n @pointerdown=${this._handlePointerDown}\n >\n <div class=\"wui-slider-progress\"></div>\n <div class=\"wui-slider-marks\">\n ${this._markValues.map(\n mark =>\n html`<span\n class=\"wui-slider-mark\"\n style=${styleMap({ '--percent': `${((mark - this.min) / this._range) * 100}%` })}\n ></span>`\n )}\n </div>\n <div class=${thumbClass}></div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-slider': WebUiSlider\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/slider/index.ts"],"sourcesContent":["import { html, LitElement, 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 { installPointerFocusSuppression } from '@/shared/focus/pointer-focus'\nimport { FormAssociated, defineFormAssociation, FormAssociationController } from '@/shared/form-association'\nimport { attachDragGesture, type DragGestureHandle } from '@/shared/gesture/drag-gesture'\nimport { clamp } from '@/shared/gesture/physics'\nimport { normalizeNumber } from '@/shared/normalize'\n\nimport style from './style.css?inline'\n\ninstallPointerFocusSuppression()\n\n@customElement('web-ui-slider')\nexport class WebUiSlider extends FormAssociated(LitElement) {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n\n @property({ type: Boolean, reflect: true }) disabled = false\n @property({ type: Boolean, reflect: true }) marks = false\n @property({ type: Boolean, reflect: true }) vertical = false\n @property({ type: String, reflect: true }) name = ''\n @property({ type: Boolean, reflect: true }) required = false\n\n // value 使用访问器模式同步表单关联状态。\n private _value = 0\n @property({ type: Number, reflect: true })\n get value(): number {\n return this._value\n }\n set value(v: number) {\n const normalized = normalizeNumber(v, -Infinity, Infinity, 0)\n const old = this._value\n this._value = normalized\n this._formAssociation.sync()\n this.requestUpdate('value', old)\n }\n\n private _min = 0\n @property({ type: Number, reflect: true })\n get min(): number {\n return this._min\n }\n set min(v: number) {\n this._min = normalizeNumber(v, -Infinity, Infinity, 0)\n }\n\n private _max = 100\n @property({ type: Number, reflect: true })\n get max(): number {\n return this._max\n }\n set max(v: number) {\n this._max = normalizeNumber(v, -Infinity, Infinity, 100)\n }\n\n private _step = 1\n @property({ type: Number, reflect: true })\n get step(): number {\n return this._step\n }\n set step(v: number) {\n this._step = normalizeNumber(v, -Infinity, Infinity, 1)\n }\n\n @state() private _dragging = false\n @state() private _pressed = false\n // 避免未改变数值的点击被误认为一次表单提交\n private _interactionStartValue: number | undefined\n private _dragGestureHandle: DragGestureHandle | null = null\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = null\n }\n\n private readonly _formAssociation = defineFormAssociation<number>({\n host: this,\n initialize: () => {\n this._value = this._normalizeValue(this._value)\n },\n getState: () => this._value,\n setState: value => {\n this.value = value\n },\n getFormValue: () => String(this._value),\n getFormState: () => String(this._value),\n restoreState: state => {\n if (typeof state !== 'string') return\n const value = Number(state)\n if (Number.isFinite(value)) this.value = value\n },\n syncValidity: internals => {\n if (typeof internals.setValidity === 'function') internals.setValidity({})\n }\n }).make()\n\n private readonly _formAssociationController = new FormAssociationController(this, this._formAssociation)\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formAssociation.isFormDisabled()\n }\n\n override willUpdate(changed: Map<string, unknown>) {\n if (changed.has('value') || changed.has('min') || changed.has('max') || changed.has('step')) {\n const normalizedValue = this._normalizeValue(this.value)\n if (normalizedValue !== this.value) this.value = normalizedValue\n }\n }\n\n override focus(options?: FocusOptions) {\n this._slider?.focus(options)\n }\n\n override blur() {\n this._slider?.blur()\n }\n\n private get _slider(): HTMLDivElement | null {\n return this.renderRoot.querySelector<HTMLDivElement>('[role=\"slider\"]')\n }\n\n private get _range(): number {\n return Math.max(0, this.max - this.min)\n }\n\n private get _percent(): number {\n return this._range === 0 ? 0 : ((this.value - this.min) / this._range) * 100\n }\n\n private get _markValues(): number[] {\n if (!this.marks || this._range === 0) return []\n const count = Math.min(100, Math.floor(this._range / this._safeStep))\n return Array.from({ length: count + 1 }, (_, index) => this.min + index * this._safeStep)\n }\n\n private get _safeStep(): number {\n return Number.isFinite(this.step) && this.step > 0 ? this.step : 1\n }\n\n private _normalizeValue(value: number): number {\n if (!Number.isFinite(value)) return this.min\n const lower = Math.min(this.min, this.max)\n const upper = Math.max(this.min, this.max)\n const clamped = clamp(value, lower, upper)\n const steps = Math.round((clamped - lower) / this._safeStep)\n // 对齐到 step 网格后必须再收敛一次:`round` 会把值推到网格外侧\n // (max=100 / step=15 → 7 * 15 = 105),不收敛就会突破 max,\n // 连带 aria-valuenow 与 --percent 溢出轨道。\n const snapped = Number((lower + steps * this._safeStep).toFixed(this._precision))\n return clamp(snapped, lower, upper)\n }\n\n private get _precision(): number {\n return Math.max(this._decimalPlaces(this.min), this._decimalPlaces(this.max), this._decimalPlaces(this._safeStep))\n }\n\n private _decimalPlaces(value: number): number {\n const [, decimal = ''] = String(value).split('.')\n return decimal.length\n }\n\n private _setValue(value: number): boolean {\n if (this._isDisabled) return false\n const nextValue = this._normalizeValue(value)\n if (nextValue === this.value) return false\n this.value = nextValue\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n return true\n }\n\n private _setValueFromPointer(event: PointerEvent): boolean {\n const track = this._slider\n if (!track) return false\n const rect = track.getBoundingClientRect()\n if (this.vertical) {\n if (rect.height <= 0) return false\n // 视觉上的数值从下往上增长,与坐标系相反。\n const ratio = clamp(1 - (event.clientY - rect.top) / rect.height, 0, 1)\n return this._setValue(this.min + ratio * this._range)\n }\n if (rect.width <= 0) return false\n const ratio = clamp((event.clientX - rect.left) / rect.width, 0, 1)\n return this._setValue(this.min + ratio * this._range)\n }\n\n private _handlePointerDown(event: PointerEvent) {\n if (this._isDisabled) return\n this._pressed = true\n this._interactionStartValue = this.value\n this._setValueFromPointer(event)\n\n this._dragGestureHandle?.destroy()\n this._dragGestureHandle = attachDragGesture(event, {\n axis: this.vertical ? 'y' : 'x',\n threshold: 6,\n onMove: (_info, e) => {\n this._dragging = true\n this._setValueFromPointer(e)\n },\n onEnd: (_info, e) => {\n this._setValueFromPointer(e)\n this._pressed = false\n this._dragging = false\n if (this._interactionStartValue !== this.value) {\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n this._interactionStartValue = undefined\n },\n onTap: () => {\n if (this._interactionStartValue !== this.value) {\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n },\n onCancel: () => {\n this._pressed = false\n this._dragging = false\n this._interactionStartValue = undefined\n }\n })\n }\n\n private _handleKeyDown(event: KeyboardEvent) {\n if (this._isDisabled) return\n const pageStep = this._safeStep * 10\n const keyValues: Record<string, number> = {\n ArrowDown: this.value - this._safeStep,\n ArrowLeft: this.value - this._safeStep,\n ArrowUp: this.value + this._safeStep,\n ArrowRight: this.value + this._safeStep,\n PageDown: this.value - pageStep,\n PageUp: this.value + pageStep,\n Home: this.min,\n End: this.max\n }\n const nextValue = keyValues[event.key]\n if (nextValue === undefined) return\n event.preventDefault()\n if (this._setValue(nextValue)) this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n override render() {\n const trackClass = classMap({\n 'wui-slider-track': true,\n 'is-dragging': this._dragging,\n 'is-disabled': this._isDisabled\n })\n const thumbClass = classMap({\n 'wui-slider-thumb': true,\n 'wui-glass': true,\n 'is-pressed': this._pressed,\n 'is-dragging': this._dragging\n })\n const trackStyle = styleMap({ '--percent': `${this._percent}%` })\n\n return html`\n <div\n class=${trackClass}\n style=${trackStyle}\n role=\"slider\"\n tabindex=${this._isDisabled ? '-1' : '0'}\n aria-label=\"滑块\"\n aria-orientation=${this.vertical ? 'vertical' : 'horizontal'}\n aria-valuemin=${this.min}\n aria-valuemax=${this.max}\n aria-valuenow=${this.value}\n aria-disabled=${String(this._isDisabled)}\n @keydown=${this._handleKeyDown}\n @pointerdown=${this._handlePointerDown}\n >\n <div class=\"wui-slider-progress\"></div>\n <div class=\"wui-slider-marks\">\n ${this._markValues.map(\n mark =>\n html`<span\n class=\"wui-slider-mark\"\n style=${styleMap({ '--percent': `${((mark - this.min) / this._range) * 100}%` })}\n ></span>`\n )}\n </div>\n <div class=${thumbClass}></div>\n </div>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-slider': WebUiSlider\n }\n}\n"],"mappings":"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"}
@@ -388,6 +388,9 @@ export declare class WebUiSwitch extends WebUiSwitch_base {
388
388
  private readonly _formAssociationController;
389
389
  private _syncValidity;
390
390
  private handleClick;
391
+ private handleNativeChange;
392
+ private handleKeydown;
393
+ private _applyChecked;
391
394
  private handlePointerDown;
392
395
  private handlePointerUp;
393
396
  private handlePointerLeave;
@@ -54,19 +54,35 @@ var b = class extends i(f) {
54
54
  e && typeof e.setValidity == "function" && (this._isDisabled || !this.required || this._checked ? e.setValidity({}) : e.setValidity({ valueMissing: !0 }, "请启用此项"));
55
55
  }
56
56
  handleClick(e) {
57
- if (e.preventDefault(), this._dragged) {
58
- this._dragged = !1;
59
- return;
57
+ if (!(e.target instanceof HTMLInputElement)) {
58
+ if (e.preventDefault(), this._dragged) {
59
+ this._dragged = !1;
60
+ return;
61
+ }
62
+ this._isDisabled || this.loading || this._applyChecked(!this._checked);
60
63
  }
61
- if (this._isDisabled || this.loading) return;
62
- let t = this._checked;
63
- this._checked = !t, this._formAssociation.sync(), this.requestUpdate("checked", t), this.dispatchEvent(new Event("input", {
64
+ }
65
+ handleNativeChange(e) {
66
+ if (e.target instanceof HTMLInputElement) {
67
+ if (this._dragged) {
68
+ this._dragged = !1;
69
+ return;
70
+ }
71
+ this._applyChecked(e.target.checked, !1);
72
+ }
73
+ }
74
+ handleKeydown(e) {
75
+ e.key === "Enter" && (e.preventDefault(), !(this._isDisabled || this.loading) && this._applyChecked(!this._checked));
76
+ }
77
+ _applyChecked(e, t = !0) {
78
+ let n = this._checked;
79
+ e !== n && (this._checked = e, this._formAssociation.sync(), this.requestUpdate("checked", n), t && this.dispatchEvent(new Event("input", {
64
80
  bubbles: !0,
65
81
  composed: !0
66
82
  })), this.dispatchEvent(new Event("change", {
67
83
  bubbles: !0,
68
84
  composed: !0
69
- }));
85
+ })));
70
86
  }
71
87
  handlePointerDown(e) {
72
88
  if (this._isDisabled || this.loading) return;
@@ -141,6 +157,8 @@ var b = class extends i(f) {
141
157
  .checked=${this._checked}
142
158
  ?disabled=${this._isDisabled || this.loading}
143
159
  class="sr-only"
160
+ @change=${this.handleNativeChange}
161
+ @keydown=${this.handleKeydown}
144
162
  />
145
163
  <div class=${v(t)}>
146
164
  ${this.loading ? p`<div class="wui-switch-loading">