@greypan/web-ui 2.0.1 → 2.1.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 CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  [English](./README.md) | 简体中文
6
6
 
7
+ ## 演示
8
+
9
+ [查看演示](https://pan-z2l0ahvi.github.io/mono/)
10
+
7
11
  ## 安装
8
12
 
9
13
  ```bash
@@ -56,6 +60,39 @@ function App() {
56
60
  }
57
61
  ```
58
62
 
63
+ #### React 自定义元素事件与布尔属性
64
+
65
+ React 19 按 JSX 键名中的 `on` 后缀原样注册 Custom Element 事件。事件名大小写敏感:`open-change`
66
+ 必须绑定为 `onopen-change`,不能写成 `onOpenChange`。标准 `input` 和 `change` 事件仍使用 React
67
+ 惯用的 `onInput` 和 `onChange`。默认值为 `true` 的属性应使用驼峰 JavaScript 属性传入,这样 `false`
68
+ 才会传递给组件。
69
+
70
+ ```tsx
71
+ <web-ui-dialog
72
+ open={open}
73
+ lockScroll={false}
74
+ onopen-change={event => setOpen(event.detail.open)}
75
+ />
76
+ <web-ui-select value={value} onChange={event => setValue(event.currentTarget.value)} />
77
+ ```
78
+
79
+ 当组件会把子元素移入自身的 Portal Shadow DOM(例如 `web-ui-dropdown` 的菜单项)时,React 根节点的
80
+ 合成 `onClick` 无法收到该子元素的事件。需要通过 `ref` 直接绑定原生事件:
81
+
82
+ ```tsx
83
+ const itemRef = useRef<HTMLElement>(null)
84
+
85
+ useEffect(() => {
86
+ const item = itemRef.current
87
+ if (!item) return
88
+ const close = () => setOpen(false)
89
+ item.addEventListener('click', close)
90
+ return () => item.removeEventListener('click', close)
91
+ }, [])
92
+
93
+ <web-ui-dropdown-item ref={itemRef}>粘贴并关闭</web-ui-dropdown-item>
94
+ ```
95
+
59
96
  ### Vue
60
97
 
61
98
  需要 `vue >= 3` 作为可选 peer 依赖。
@@ -296,12 +333,12 @@ ArrowUp/ArrowDown 键增减数值。
296
333
 
297
334
  分段控制——单选按钮组。
298
335
 
299
- | 属性 | 类型 | 默认值 | 说明 |
300
- | ---------- | --------- | ------- | ---------- |
301
- | `value` | `string` | `''` | 当前选中值 |
302
- | `name` | `string` | `''` | 表单字段名 |
303
- | `disabled` | `boolean` | `false` | 禁用状态 |
304
- | `required` | `boolean` | `false` | 必填校验 |
336
+ | 属性 | 类型 | 默认值 | 说明 |
337
+ | ---------- | --------- | ------- | ---------------- |
338
+ | `value` | `string` | `''` | 当前选中值 |
339
+ | `name` | `string` | `''` | 表单字段名 |
340
+ | `disabled` | `boolean` | `false` | 禁用全部 trigger |
341
+ | `required` | `boolean` | `false` | 必填校验 |
305
342
 
306
343
  **事件:** `input`, `change`
307
344
 
@@ -309,24 +346,24 @@ ArrowUp/ArrowDown 键增减数值。
309
346
 
310
347
  与原生 `<form>` 集成(通过 `ElementInternals`)。
311
348
 
312
- 根据 `value` 同步子 trigger 的 `checked` 状态。直接设 `value` 不派发事件。
349
+ 根据 `value` 同步子 trigger 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写 trigger 自身的 `disabled` 属性。直接设 `value` 不派发事件。
313
350
 
314
351
  #### `<web-ui-checkbox-group>`
315
352
 
316
353
  多选复选框组。
317
354
 
318
- | 属性 | 类型 | 默认值 | 说明 |
319
- | ---------- | ---------- | ------- | ---------------------- |
320
- | `value` | `string[]` | `[]` | 已选项的值数组 |
321
- | `name` | `string` | `''` | 表单字段名 |
322
- | `disabled` | `boolean` | `false` | 禁用状态(传递到子项) |
323
- | `required` | `boolean` | `false` | 必填校验 |
355
+ | 属性 | 类型 | 默认值 | 说明 |
356
+ | ---------- | ---------- | ------- | -------------- |
357
+ | `value` | `string[]` | `[]` | 已选项的值数组 |
358
+ | `name` | `string` | `''` | 表单字段名 |
359
+ | `disabled` | `boolean` | `false` | 禁用全部子项 |
360
+ | `required` | `boolean` | `false` | 必填校验 |
324
361
 
325
362
  **事件:** `input`, `change`
326
363
 
327
364
  **插槽:** `default`(投影 `<web-ui-checkbox>` 元素)
328
365
 
329
- 通过子项公开属性同步 `checked`/`disabled`。监听子项 `change` 事件。
366
+ 同步子 checkbox 的 `checked` 状态。`disabled` 提供继承的有效禁用状态,不改写子项自身的 `disabled` 属性。监听子项 `change` 事件。
330
367
 
331
368
  #### `<web-ui-radio-group>`
332
369
 
@@ -336,13 +373,15 @@ ArrowUp/ArrowDown 键增减数值。
336
373
  | ---------- | --------- | ------- | ------------------------ |
337
374
  | `value` | `string` | `''` | 当前选中值 |
338
375
  | `name` | `string` | `''` | 表单字段名(传递到子项) |
339
- | `disabled` | `boolean` | `false` | 禁用状态(传递到子项) |
376
+ | `disabled` | `boolean` | `false` | 禁用全部子项 |
340
377
  | `required` | `boolean` | `false` | 必填校验 |
341
378
 
342
379
  **事件:** `input`, `change`
343
380
 
344
381
  **插槽:** `default`(投影 `<web-ui-radio>` 元素)
345
382
 
383
+ `disabled` 提供继承的有效禁用状态,不改写子项自身的 `disabled` 属性。
384
+
346
385
  ---
347
386
 
348
387
  ### 按钮
@@ -760,11 +799,11 @@ toast.updateMessage(id, { message: '上传已完成 60%', heading: '正在上传
760
799
 
761
800
  `<web-ui-segmented>` 的分段按钮。
762
801
 
763
- | 属性 | 类型 | 默认值 | 说明 |
764
- | ---------- | --------- | ------- | -------- |
765
- | `value` | `string` | `''` | 分段值 |
766
- | `checked` | `boolean` | `false` | 当前选中 |
767
- | `disabled` | `boolean` | `false` | 禁用状态 |
802
+ | 属性 | 类型 | 默认值 | 说明 |
803
+ | ---------- | --------- | ------- | ------------------ |
804
+ | `value` | `string` | `''` | 分段值 |
805
+ | `checked` | `boolean` | `false` | 当前选中 |
806
+ | `disabled` | `boolean` | `false` | 单独禁用该 trigger |
768
807
 
769
808
  **事件:** `change`
770
809
 
package/README.md CHANGED
@@ -4,6 +4,10 @@
4
4
 
5
5
  English | [简体中文](./README.CN.md)
6
6
 
7
+ ## Demo
8
+
9
+ [View demo](https://pan-z2l0ahvi.github.io/mono/)
10
+
7
11
  ## Install
8
12
 
9
13
  ```bash
@@ -56,6 +60,40 @@ function App() {
56
60
  }
57
61
  ```
58
62
 
63
+ #### React custom-element events and boolean properties
64
+
65
+ React 19 registers Custom Element events using the suffix of the JSX `on` key unchanged. Event names are
66
+ case-sensitive: bind `open-change` as `onopen-change`, not `onOpenChange`. Standard `input` and `change`
67
+ events continue to use React's conventional `onInput` and `onChange`. For a default-`true` property, pass the
68
+ camel-cased JavaScript property so `false` reaches the element.
69
+
70
+ ```tsx
71
+ <web-ui-dialog
72
+ open={open}
73
+ lockScroll={false}
74
+ onopen-change={event => setOpen(event.detail.open)}
75
+ />
76
+ <web-ui-select value={value} onChange={event => setValue(event.currentTarget.value)} />
77
+ ```
78
+
79
+ When a component moves a child into its Portal Shadow DOM (such as a `web-ui-dropdown` menu item), React's
80
+ root-delegated synthetic `onClick` cannot receive that child's event. Bind a native listener directly through a
81
+ `ref` instead:
82
+
83
+ ```tsx
84
+ const itemRef = useRef<HTMLElement>(null)
85
+
86
+ useEffect(() => {
87
+ const item = itemRef.current
88
+ if (!item) return
89
+ const close = () => setOpen(false)
90
+ item.addEventListener('click', close)
91
+ return () => item.removeEventListener('click', close)
92
+ }, [])
93
+
94
+ <web-ui-dropdown-item ref={itemRef}>Paste and close</web-ui-dropdown-item>
95
+ ```
96
+
59
97
  ### Vue
60
98
 
61
99
  Requires `vue >= 3` as optional peer dependency.
@@ -304,7 +342,7 @@ Segmented control — single-select button group.
304
342
  | ---------- | --------- | ------- | ---------------------- |
305
343
  | `value` | `string` | `''` | Selected trigger value |
306
344
  | `name` | `string` | `''` | Form field name |
307
- | `disabled` | `boolean` | `false` | Disabled state |
345
+ | `disabled` | `boolean` | `false` | Disables all triggers |
308
346
  | `required` | `boolean` | `false` | Required validation |
309
347
 
310
348
  **Events:** `input`, `change`
@@ -313,24 +351,24 @@ Segmented control — single-select button group.
313
351
 
314
352
  Form-associated: integrates with native `<form>` via `ElementInternals`.
315
353
 
316
- Manages child trigger `checked` state based on `value`. Setting `value` directly does not dispatch `input`/`change`.
354
+ Manages child trigger `checked` state based on `value`. `disabled` supplies inherited effective disabled state without changing a trigger's own `disabled` property. Setting `value` directly does not dispatch `input`/`change`.
317
355
 
318
356
  #### `<web-ui-checkbox-group>`
319
357
 
320
358
  Checkbox group managing multiple selection.
321
359
 
322
- | Attribute | Type | Default | Description |
323
- | ---------- | ---------- | ------- | --------------------------------------- |
324
- | `value` | `string[]` | `[]` | Selected values |
325
- | `name` | `string` | `''` | Form field name |
326
- | `disabled` | `boolean` | `false` | Disabled state (propagates to children) |
327
- | `required` | `boolean` | `false` | Required validation |
360
+ | Attribute | Type | Default | Description |
361
+ | ---------- | ---------- | ------- | --------------------------- |
362
+ | `value` | `string[]` | `[]` | Selected values |
363
+ | `name` | `string` | `''` | Form field name |
364
+ | `disabled` | `boolean` | `false` | Disables all child controls |
365
+ | `required` | `boolean` | `false` | Required validation |
328
366
 
329
367
  **Events:** `input`, `change`
330
368
 
331
369
  **Slots:** `default` (project `<web-ui-checkbox>` elements)
332
370
 
333
- Syncs with child checkbox `checked`/`disabled` via public property access. Listens to child `change` events.
371
+ Syncs child checkbox `checked` state. `disabled` supplies inherited effective disabled state without changing a child's own `disabled` property. Listens to child `change` events.
334
372
 
335
373
  #### `<web-ui-radio-group>`
336
374
 
@@ -340,13 +378,15 @@ Radio group managing single selection.
340
378
  | ---------- | --------- | ------- | ---------------------------------------- |
341
379
  | `value` | `string` | `''` | Selected radio value |
342
380
  | `name` | `string` | `''` | Form field name (propagates to children) |
343
- | `disabled` | `boolean` | `false` | Disabled state (propagates to children) |
381
+ | `disabled` | `boolean` | `false` | Disables all child controls |
344
382
  | `required` | `boolean` | `false` | Required validation |
345
383
 
346
384
  **Events:** `input`, `change`
347
385
 
348
386
  **Slots:** `default` (project `<web-ui-radio>` elements)
349
387
 
388
+ `disabled` supplies inherited effective disabled state without changing a child's own `disabled` property.
389
+
350
390
  ---
351
391
 
352
392
  ### Buttons
@@ -764,11 +804,11 @@ Not form-associated (child of select, not independent submit).
764
804
 
765
805
  Segment trigger for `<web-ui-segmented>`.
766
806
 
767
- | Attribute | Type | Default | Description |
768
- | ---------- | --------- | ------- | ------------------ |
769
- | `value` | `string` | `''` | Segment value |
770
- | `checked` | `boolean` | `false` | Currently selected |
771
- | `disabled` | `boolean` | `false` | Disabled state |
807
+ | Attribute | Type | Default | Description |
808
+ | ---------- | --------- | ------- | ---------------------------------- |
809
+ | `value` | `string` | `''` | Segment value |
810
+ | `checked` | `boolean` | `false` | Currently selected |
811
+ | `disabled` | `boolean` | `false` | Individually disables this trigger |
772
812
 
773
813
  **Events:** `change`
774
814
 
@@ -78,7 +78,7 @@ var u = class extends r {
78
78
  "is-checked": this._checked,
79
79
  "is-disabled": this._isDisabled
80
80
  })}
81
- tabindex="0"
81
+ tabindex=${this._isDisabled ? "-1" : "0"}
82
82
  role="checkbox"
83
83
  aria-checked=${String(this._checked)}
84
84
  aria-disabled=${String(this._isDisabled)}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/checkbox/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'\n\nimport '@/components/icon'\nimport { heroiconsCheck16Solid } from '@/icons'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-checkbox')\nexport class WebUiCheckbox extends LitElement {\n static override styles = unsafeCSS(style)\n static formAssociated = true\n\n /** 内部表单关联实例(connectedCallback 中初始化) */\n private _internals?: ElementInternals\n @state() private _formDisabled = false\n @state() private _groupDisabled = false\n\n /** 内部 checked 状态,通过 getter/setter 暴露为公共 API */\n @state() private _checked = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._syncFormValue()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String }) value = ''\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 private get _isDisabled(): boolean {\n return this.disabled || this._formDisabled || this._groupDisabled\n }\n\n private get _isManagedByGroup(): boolean {\n return this.closest('web-ui-checkbox-group') !== null\n }\n\n setGroupDisabled(disabled: boolean) {\n this._groupDisabled = disabled\n }\n\n override connectedCallback() {\n super.connectedCallback()\n if (this._internals) return\n this._internals = this.attachInternals()\n if (this.hasAttribute('checked')) {\n this._checked = true\n }\n this._syncFormValue()\n }\n\n formResetCallback() {\n this.checked = this.hasAttribute('checked')\n }\n\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled\n }\n\n override updated() {\n this._syncFormValue()\n this._syncValidity()\n }\n\n private _syncFormValue() {\n this._internals?.setFormValue?.(!this._isManagedByGroup && this._checked ? this.value || 'on' : null)\n }\n\n private _syncValidity() {\n if (!this._internals || typeof this._internals.setValidity !== 'function') return\n if (this._isDisabled || this._isManagedByGroup || !this.required || this._checked) {\n this._internals.setValidity({})\n return\n }\n this._internals.setValidity({ valueMissing: true }, '请选择此项')\n }\n\n /** 用户点击切换 */\n private handleClick() {\n if (this._isDisabled) return\n const old = this._checked\n this._checked = !old\n this._syncFormValue()\n this.requestUpdate('checked', old)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-checkbox': true,\n 'is-checked': this._checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <label\n class=${classMap(cls)}\n tabindex=\"0\"\n role=\"checkbox\"\n aria-checked=${String(this._checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <span class=\"wui-checkbox-box\">\n <span class=\"wui-checkbox-icon\"\n ><web-ui-icon .icon=${heroiconsCheck16Solid} size=\"18\" color=\"#fff\"></web-ui-icon\n ></span>\n </span>\n <span class=\"wui-checkbox-label\"><slot></slot></span>\n </label>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-checkbox': WebUiCheckbox\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/checkbox/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'\n\nimport '@/components/icon'\nimport { heroiconsCheck16Solid } from '@/icons'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-checkbox')\nexport class WebUiCheckbox extends LitElement {\n static override styles = unsafeCSS(style)\n static formAssociated = true\n\n /** 内部表单关联实例(connectedCallback 中初始化) */\n private _internals?: ElementInternals\n @state() private _formDisabled = false\n @state() private _groupDisabled = false\n\n /** 内部 checked 状态,通过 getter/setter 暴露为公共 API */\n @state() private _checked = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._syncFormValue()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String }) value = ''\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 private get _isDisabled(): boolean {\n return this.disabled || this._formDisabled || this._groupDisabled\n }\n\n private get _isManagedByGroup(): boolean {\n return this.closest('web-ui-checkbox-group') !== null\n }\n\n setGroupDisabled(disabled: boolean) {\n this._groupDisabled = disabled\n }\n\n override connectedCallback() {\n super.connectedCallback()\n if (this._internals) return\n this._internals = this.attachInternals()\n if (this.hasAttribute('checked')) {\n this._checked = true\n }\n this._syncFormValue()\n }\n\n formResetCallback() {\n this.checked = this.hasAttribute('checked')\n }\n\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled\n }\n\n override updated() {\n this._syncFormValue()\n this._syncValidity()\n }\n\n private _syncFormValue() {\n this._internals?.setFormValue?.(!this._isManagedByGroup && this._checked ? this.value || 'on' : null)\n }\n\n private _syncValidity() {\n if (!this._internals || typeof this._internals.setValidity !== 'function') return\n if (this._isDisabled || this._isManagedByGroup || !this.required || this._checked) {\n this._internals.setValidity({})\n return\n }\n this._internals.setValidity({ valueMissing: true }, '请选择此项')\n }\n\n /** 用户点击切换 */\n private handleClick() {\n if (this._isDisabled) return\n const old = this._checked\n this._checked = !old\n this._syncFormValue()\n this.requestUpdate('checked', old)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-checkbox': true,\n 'is-checked': this._checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <label\n class=${classMap(cls)}\n tabindex=${this._isDisabled ? '-1' : '0'}\n role=\"checkbox\"\n aria-checked=${String(this._checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <span class=\"wui-checkbox-box\">\n <span class=\"wui-checkbox-icon\"\n ><web-ui-icon .icon=${heroiconsCheck16Solid} size=\"18\" color=\"#fff\"></web-ui-icon\n ></span>\n </span>\n <span class=\"wui-checkbox-label\"><slot></slot></span>\n </label>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-checkbox': WebUiCheckbox\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/checkbox/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{cursor:pointer;display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-checkbox{cursor:pointer;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}.wui-checkbox:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}:host([disabled]) .wui-checkbox{cursor:not-allowed;color:var(--wui-color-text-muted,#6a6a6a)}.wui-checkbox-box{background-color:var(--wui-color-surface-raised,#f2f2f7);border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:background-color .2s,border-color .2s;display:flex}.is-checked .wui-checkbox-box{border-color:var(--wui-color-accent,#08f);background-color:var(--wui-color-accent,#08f)}.wui-checkbox-icon{opacity:0;transition:opacity var(--wui-duration-fast,.16s) ease, transform var(--wui-duration-fast,.16s) var(--wui-ease-out,cubic-bezier(.23, 1, .32, 1));font-size:0;line-height:0;display:block;overflow:hidden;transform:scale(.85)}.is-checked .wui-checkbox-icon{opacity:1;transform:scale(1)}.wui-checkbox-label{pointer-events:none}@media (prefers-reduced-motion:reduce){.wui-checkbox-icon,.is-checked .wui-checkbox-icon{transform:none}}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{cursor:pointer;display:inline-block}:host([disabled]){cursor:not-allowed}.wui-checkbox{cursor:pointer;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}.wui-checkbox:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}.wui-checkbox.is-disabled{cursor:not-allowed;color:var(--wui-color-text-muted,#6a6a6a);opacity:.4}.wui-checkbox-box{background-color:var(--wui-color-surface-raised,#f2f2f7);border-radius:6px;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:background-color .2s,border-color .2s;display:flex}.is-checked .wui-checkbox-box{border-color:var(--wui-color-accent,#08f);background-color:var(--wui-color-accent,#08f)}.wui-checkbox-icon{opacity:0;transition:opacity var(--wui-duration-fast,.16s) ease, transform var(--wui-duration-fast,.16s) var(--wui-ease-out,cubic-bezier(.23, 1, .32, 1));font-size:0;line-height:0;display:block;overflow:hidden;transform:scale(.85)}.is-checked .wui-checkbox-icon{opacity:1;transform:scale(1)}.wui-checkbox-label{pointer-events:none}@media (prefers-reduced-motion:reduce){.wui-checkbox-icon,.is-checked .wui-checkbox-icon{transform:none}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/checkbox/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: pointer;\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-checkbox {\n cursor: pointer;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n:host([disabled]) .wui-checkbox {\n cursor: not-allowed;\n color: var(--wui-color-text-muted, #6a6a6a);\n}\n\n.wui-checkbox-box {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 18px;\n height: 18px;\n border-radius: 6px;\n\n background-color: var(--wui-color-surface-raised, #f2f2f7);\n\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n}\n\n.is-checked .wui-checkbox-box {\n border-color: var(--wui-color-accent, #08f);\n background-color: var(--wui-color-accent, #08f);\n}\n\n.wui-checkbox-icon {\n transform: scale(0.85);\n\n overflow: hidden;\n display: block;\n\n font-size: 0;\n line-height: 0;\n\n opacity: 0;\n\n transition:\n opacity var(--wui-duration-fast, 160ms) ease,\n transform var(--wui-duration-fast, 160ms) var(--wui-ease-out, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n.is-checked .wui-checkbox-icon {\n transform: scale(1);\n opacity: 1;\n}\n\n.wui-checkbox-label {\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-checkbox-icon,\n .is-checked .wui-checkbox-icon {\n transform: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/checkbox/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: pointer;\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n.wui-checkbox {\n cursor: pointer;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n.wui-checkbox.is-disabled {\n cursor: not-allowed;\n color: var(--wui-color-text-muted, #6a6a6a);\n opacity: 0.4;\n}\n\n.wui-checkbox-box {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 18px;\n height: 18px;\n border-radius: 6px;\n\n background-color: var(--wui-color-surface-raised, #f2f2f7);\n\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n}\n\n.is-checked .wui-checkbox-box {\n border-color: var(--wui-color-accent, #08f);\n background-color: var(--wui-color-accent, #08f);\n}\n\n.wui-checkbox-icon {\n transform: scale(0.85);\n\n overflow: hidden;\n display: block;\n\n font-size: 0;\n line-height: 0;\n\n opacity: 0;\n\n transition:\n opacity var(--wui-duration-fast, 160ms) ease,\n transform var(--wui-duration-fast, 160ms) var(--wui-ease-out, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n.is-checked .wui-checkbox-icon {\n transform: scale(1);\n opacity: 1;\n}\n\n.wui-checkbox-label {\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-checkbox-icon,\n .is-checked .wui-checkbox-icon {\n transform: none;\n }\n}\n"],"mappings":""}
@@ -74,7 +74,7 @@ var l = class extends n {
74
74
  "is-checked": this._checked,
75
75
  "is-disabled": this._isDisabled
76
76
  })}
77
- tabindex="0"
77
+ tabindex=${this._isDisabled ? "-1" : "0"}
78
78
  role="radio"
79
79
  aria-checked=${String(this._checked)}
80
80
  aria-disabled=${String(this._isDisabled)}
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/radio/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'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-radio')\nexport class WebUiRadio extends LitElement {\n static override styles = unsafeCSS(style)\n static formAssociated = true\n\n /** 内部表单关联实例(connectedCallback 中初始化) */\n private _internals?: ElementInternals\n @state() private _formDisabled = false\n @state() private _groupDisabled = false\n\n /** 内部 checked 状态,通过 getter/setter 暴露为公共 API */\n @state() private _checked = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._syncFormValue()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String }) value = ''\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 private get _isDisabled(): boolean {\n return this.disabled || this._formDisabled || this._groupDisabled\n }\n\n private get _isManagedByGroup(): boolean {\n return this.closest('web-ui-radio-group') !== null\n }\n\n setGroupDisabled(disabled: boolean) {\n this._groupDisabled = disabled\n }\n\n override connectedCallback() {\n super.connectedCallback()\n if (this._internals) return\n this._internals = this.attachInternals()\n if (this.hasAttribute('checked')) {\n this._checked = true\n }\n this._syncFormValue()\n }\n\n formResetCallback() {\n this.checked = this.hasAttribute('checked')\n }\n\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled\n }\n\n override updated() {\n this._syncFormValue()\n this._syncValidity()\n }\n\n private _syncFormValue() {\n this._internals?.setFormValue?.(!this._isManagedByGroup && this._checked ? this.value || 'on' : null)\n }\n\n private _syncValidity() {\n if (!this._internals || typeof this._internals.setValidity !== 'function') return\n if (this._isDisabled || this._isManagedByGroup || !this.required || this._checked) {\n this._internals.setValidity({})\n return\n }\n this._internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n /** 用户点击选中(已选中时不重复触发) */\n private handleClick() {\n if (this._isDisabled || this._checked) return\n this._checked = true\n this._syncFormValue()\n this.requestUpdate('checked', false)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-radio': true,\n 'is-checked': this._checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <label\n class=${classMap(cls)}\n tabindex=\"0\"\n role=\"radio\"\n aria-checked=${String(this._checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <span class=\"wui-radio-circle\">\n <span class=\"wui-radio-dot\"></span>\n </span>\n <span class=\"wui-radio-label\"><slot></slot></span>\n </label>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-radio': WebUiRadio\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/radio/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'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-radio')\nexport class WebUiRadio extends LitElement {\n static override styles = unsafeCSS(style)\n static formAssociated = true\n\n /** 内部表单关联实例(connectedCallback 中初始化) */\n private _internals?: ElementInternals\n @state() private _formDisabled = false\n @state() private _groupDisabled = false\n\n /** 内部 checked 状态,通过 getter/setter 暴露为公共 API */\n @state() private _checked = false\n\n get checked(): boolean {\n return this._checked\n }\n\n set checked(v: boolean) {\n const old = this._checked\n this._checked = v\n this._syncFormValue()\n this.requestUpdate('checked', old)\n }\n\n @property({ type: String }) value = ''\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 private get _isDisabled(): boolean {\n return this.disabled || this._formDisabled || this._groupDisabled\n }\n\n private get _isManagedByGroup(): boolean {\n return this.closest('web-ui-radio-group') !== null\n }\n\n setGroupDisabled(disabled: boolean) {\n this._groupDisabled = disabled\n }\n\n override connectedCallback() {\n super.connectedCallback()\n if (this._internals) return\n this._internals = this.attachInternals()\n if (this.hasAttribute('checked')) {\n this._checked = true\n }\n this._syncFormValue()\n }\n\n formResetCallback() {\n this.checked = this.hasAttribute('checked')\n }\n\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled\n }\n\n override updated() {\n this._syncFormValue()\n this._syncValidity()\n }\n\n private _syncFormValue() {\n this._internals?.setFormValue?.(!this._isManagedByGroup && this._checked ? this.value || 'on' : null)\n }\n\n private _syncValidity() {\n if (!this._internals || typeof this._internals.setValidity !== 'function') return\n if (this._isDisabled || this._isManagedByGroup || !this.required || this._checked) {\n this._internals.setValidity({})\n return\n }\n this._internals.setValidity({ valueMissing: true }, '请选择一项')\n }\n\n /** 用户点击选中(已选中时不重复触发) */\n private handleClick() {\n if (this._isDisabled || this._checked) return\n this._checked = true\n this._syncFormValue()\n this.requestUpdate('checked', false)\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-radio': true,\n 'is-checked': this._checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <label\n class=${classMap(cls)}\n tabindex=${this._isDisabled ? '-1' : '0'}\n role=\"radio\"\n aria-checked=${String(this._checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <span class=\"wui-radio-circle\">\n <span class=\"wui-radio-dot\"></span>\n </span>\n <span class=\"wui-radio-label\"><slot></slot></span>\n </label>\n `\n }\n\n declare readonly $events: {\n input: Event\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-radio': WebUiRadio\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/radio/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{cursor:pointer;display:inline-block}:host([disabled]){cursor:not-allowed;opacity:.4}.wui-radio{cursor:pointer;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}.wui-radio:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}:host([disabled]) .wui-radio{cursor:not-allowed;color:var(--wui-color-text-muted,#6a6a6a)}.wui-radio-circle{background-color:var(--wui-color-surface-raised,#f2f2f7);border-radius:2147483647px;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:background-color .2s,border-color .2s;display:flex}.is-checked .wui-radio-circle{background-color:var(--wui-color-accent,#08f)}.wui-radio-dot{opacity:0;background-color:var(--wui-color-surface,#fff);width:8px;height:8px;transition:opacity var(--wui-duration-fast,.16s) ease, transform var(--wui-duration-fast,.16s) var(--wui-ease-out,cubic-bezier(.23, 1, .32, 1));border-radius:2147483647px;transform:scale(.85);box-shadow:inset 0 -1px 2px #0000001f,inset 0 1px 2px #ffffff80,0 0 4px #0088ff4d}.is-checked .wui-radio-dot{opacity:1;transform:scale(1)}.wui-radio-label{pointer-events:none}@media (prefers-reduced-motion:reduce){.wui-radio-dot,.is-checked .wui-radio-dot{transform:none}}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{cursor:pointer;display:inline-block}:host([disabled]){cursor:not-allowed}.wui-radio{cursor:pointer;-webkit-user-select:none;user-select:none;font-size:var(--wui-font-size,14px);color:var(--wui-color-text,#1b1b1b);align-items:center;gap:10px;line-height:1;display:inline-flex}.wui-radio:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px;border-radius:2147483647px}.wui-radio.is-disabled{cursor:not-allowed;color:var(--wui-color-text-muted,#6a6a6a);opacity:.4}.wui-radio-circle{background-color:var(--wui-color-surface-raised,#f2f2f7);border-radius:2147483647px;flex-shrink:0;justify-content:center;align-items:center;width:18px;height:18px;transition:background-color .2s,border-color .2s;display:flex}.is-checked .wui-radio-circle{background-color:var(--wui-color-accent,#08f)}.wui-radio-dot{opacity:0;background-color:var(--wui-color-surface,#fff);width:8px;height:8px;transition:opacity var(--wui-duration-fast,.16s) ease, transform var(--wui-duration-fast,.16s) var(--wui-ease-out,cubic-bezier(.23, 1, .32, 1));border-radius:2147483647px;transform:scale(.85);box-shadow:inset 0 -1px 2px #0000001f,inset 0 1px 2px #ffffff80,0 0 4px #0088ff4d}.is-checked .wui-radio-dot{opacity:1;transform:scale(1)}.wui-radio-label{pointer-events:none}@media (prefers-reduced-motion:reduce){.wui-radio-dot,.is-checked .wui-radio-dot{transform:none}}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/radio/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: pointer;\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-radio {\n cursor: pointer;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n:host([disabled]) .wui-radio {\n cursor: not-allowed;\n color: var(--wui-color-text-muted, #6a6a6a);\n}\n\n.wui-radio-circle {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 18px;\n height: 18px;\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-raised, #f2f2f7);\n\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n}\n\n.is-checked .wui-radio-circle {\n background-color: var(--wui-color-accent, #08f);\n}\n\n.wui-radio-dot {\n transform: scale(0.85);\n\n width: 8px;\n height: 8px;\n border-radius: calc(infinity * 1px);\n\n opacity: 0;\n background-color: var(--wui-color-surface, #fff);\n box-shadow:\n inset 0 -1px 2px rgb(0 0 0 / 0.12),\n inset 0 1px 2px rgb(255 255 255 / 0.5),\n 0 0 4px rgb(0 136 255 / 0.3);\n\n transition:\n opacity var(--wui-duration-fast, 160ms) ease,\n transform var(--wui-duration-fast, 160ms) var(--wui-ease-out, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n.is-checked .wui-radio-dot {\n transform: scale(1);\n opacity: 1;\n}\n\n.wui-radio-label {\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-radio-dot,\n .is-checked .wui-radio-dot {\n transform: none;\n }\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/radio/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n cursor: pointer;\n display: inline-block;\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n}\n\n.wui-radio {\n cursor: pointer;\n user-select: none;\n\n display: inline-flex;\n gap: 10px;\n align-items: center;\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text, #1b1b1b);\n\n &:focus-visible {\n border-radius: calc(infinity * 1px);\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n}\n\n.wui-radio.is-disabled {\n cursor: not-allowed;\n color: var(--wui-color-text-muted, #6a6a6a);\n opacity: 0.4;\n}\n\n.wui-radio-circle {\n display: flex;\n flex-shrink: 0;\n align-items: center;\n justify-content: center;\n\n width: 18px;\n height: 18px;\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-raised, #f2f2f7);\n\n transition:\n background-color 0.2s ease,\n border-color 0.2s ease;\n}\n\n.is-checked .wui-radio-circle {\n background-color: var(--wui-color-accent, #08f);\n}\n\n.wui-radio-dot {\n transform: scale(0.85);\n\n width: 8px;\n height: 8px;\n border-radius: calc(infinity * 1px);\n\n opacity: 0;\n background-color: var(--wui-color-surface, #fff);\n box-shadow:\n inset 0 -1px 2px rgb(0 0 0 / 0.12),\n inset 0 1px 2px rgb(255 255 255 / 0.5),\n 0 0 4px rgb(0 136 255 / 0.3);\n\n transition:\n opacity var(--wui-duration-fast, 160ms) ease,\n transform var(--wui-duration-fast, 160ms) var(--wui-ease-out, cubic-bezier(0.23, 1, 0.32, 1));\n}\n\n.is-checked .wui-radio-dot {\n transform: scale(1);\n opacity: 1;\n}\n\n.wui-radio-label {\n pointer-events: none;\n}\n\n@media (prefers-reduced-motion: reduce) {\n .wui-radio-dot,\n .is-checked .wui-radio-dot {\n transform: none;\n }\n}\n"],"mappings":""}
@@ -7,11 +7,13 @@ export declare class WebUiSegmented extends LitElement {
7
7
  required: boolean;
8
8
  private _internals?;
9
9
  private _value;
10
+ private _formDisabled;
10
11
  private _initialValue;
11
12
  get value(): string;
12
13
  set value(v: string);
13
14
  private readonly childObserver;
14
15
  private _syncPropsToChildren;
16
+ private get _isDisabled();
15
17
  private _updateIndicator;
16
18
  updated(props: PropertyValues): void;
17
19
  connectedCallback(): void;
@@ -3,10 +3,11 @@ import t from "../../assets/glass.js";
3
3
  import n from "./style.js";
4
4
  import { LitElement as r, html as i, unsafeCSS as a } from "lit";
5
5
  import { customElement as o, property as s, state as c } from "lit/decorators.js";
6
+ import { classMap as l } from "lit/directives/class-map.js";
6
7
  //#region src/components/segmented/index.ts
7
- var l = class extends r {
8
+ var u = class extends r {
8
9
  constructor(...e) {
9
- super(...e), this.name = "", this.disabled = !1, this.required = !1, this._value = "", this._initialValue = "", this.childObserver = new MutationObserver(() => {
10
+ super(...e), this.name = "", this.disabled = !1, this.required = !1, this._value = "", this._formDisabled = !1, this._initialValue = "", this.childObserver = new MutationObserver(() => {
10
11
  this._syncPropsToChildren(), requestAnimationFrame(() => this._updateIndicator());
11
12
  });
12
13
  }
@@ -25,9 +26,12 @@ var l = class extends r {
25
26
  }
26
27
  _syncPropsToChildren() {
27
28
  this.querySelectorAll("web-ui-segmented-trigger").forEach((e) => {
28
- e.checked = e.value === this._value, e.disabled = this.disabled;
29
+ e.checked = e.value === this._value, e.setGroupDisabled(this._isDisabled);
29
30
  });
30
31
  }
32
+ get _isDisabled() {
33
+ return this.disabled || this._formDisabled;
34
+ }
31
35
  _updateIndicator() {
32
36
  let e = this.querySelectorAll("web-ui-segmented-trigger"), t = 0, n = 0;
33
37
  e.forEach((e) => {
@@ -38,7 +42,7 @@ var l = class extends r {
38
42
  }), this.style.setProperty("--indicator-left", `${t}px`), this.style.setProperty("--indicator-width", `${n}px`);
39
43
  }
40
44
  updated(e) {
41
- (e.has("value") || e.has("disabled")) && (this._syncPropsToChildren(), requestAnimationFrame(() => this._updateIndicator()));
45
+ (e.has("value") || e.has("disabled") || e.has("_formDisabled")) && (this._syncPropsToChildren(), requestAnimationFrame(() => this._updateIndicator()));
42
46
  }
43
47
  connectedCallback() {
44
48
  super.connectedCallback(), this._internals = this.attachInternals();
@@ -52,7 +56,7 @@ var l = class extends r {
52
56
  super.disconnectedCallback(), this.childObserver.disconnect(), this.removeEventListener("change", this._handleChildChange);
53
57
  }
54
58
  _handleChildChange(e) {
55
- if (this.disabled) return;
59
+ if (this._isDisabled) return;
56
60
  let t = e.target;
57
61
  if (!t.matches?.("web-ui-segmented-trigger")) return;
58
62
  let n = t;
@@ -68,11 +72,14 @@ var l = class extends r {
68
72
  this.value = this._initialValue;
69
73
  }
70
74
  formDisabledCallback(e) {
71
- this.disabled = e;
75
+ this._formDisabled = e;
72
76
  }
73
77
  render() {
74
78
  return i`
75
- <div class="wui-segmented">
79
+ <div class=${l({
80
+ "wui-segmented": !0,
81
+ "is-disabled": this._isDisabled
82
+ })}>
76
83
  <span class="wui-segmented-indicator wui-glass"></span>
77
84
  <slot></slot>
78
85
  </div>
@@ -82,14 +89,14 @@ var l = class extends r {
82
89
  e([s({
83
90
  type: String,
84
91
  reflect: !0
85
- })], l.prototype, "name", void 0), e([s({
92
+ })], u.prototype, "name", void 0), e([s({
86
93
  type: Boolean,
87
94
  reflect: !0
88
- })], l.prototype, "disabled", void 0), e([s({
95
+ })], u.prototype, "disabled", void 0), e([s({
89
96
  type: Boolean,
90
97
  reflect: !0
91
- })], l.prototype, "required", void 0), e([c()], l.prototype, "_value", void 0), l = e([o("web-ui-segmented")], l);
98
+ })], u.prototype, "required", void 0), e([c()], u.prototype, "_value", void 0), e([c()], u.prototype, "_formDisabled", void 0), u = e([o("web-ui-segmented")], u);
92
99
  //#endregion
93
- export { l as WebUiSegmented };
100
+ export { u as WebUiSegmented };
94
101
 
95
102
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/segmented/index.ts"],"sourcesContent":["import { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\n\nimport glass from '@/assets/glass.css?inline'\n\nimport type { WebUiSegmentedTrigger } from '../segmented-trigger'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-segmented')\nexport class WebUiSegmented extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n static formAssociated = true\n\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 private _internals?: ElementInternals\n @state() private _value = ''\n private _initialValue = ''\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._internals?.setFormValue?.(v)\n this.requestUpdate('value', old)\n }\n\n private readonly childObserver = new MutationObserver(() => {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n })\n\n private _syncPropsToChildren() {\n this.querySelectorAll<WebUiSegmentedTrigger>('web-ui-segmented-trigger').forEach(trigger => {\n trigger.checked = trigger.value === this._value\n trigger.disabled = this.disabled\n })\n }\n\n private _updateIndicator() {\n const triggers = this.querySelectorAll<WebUiSegmentedTrigger>('web-ui-segmented-trigger')\n let left = 0\n let width = 0\n\n triggers.forEach(trigger => {\n if (trigger.value === this._value) {\n const triggerRect = trigger.getBoundingClientRect()\n const groupRect = this.getBoundingClientRect()\n left = triggerRect.left - groupRect.left\n width = triggerRect.width\n }\n })\n\n this.style.setProperty('--indicator-left', `${left}px`)\n this.style.setProperty('--indicator-width', `${width}px`)\n }\n\n override updated(props: PropertyValues) {\n if (props.has('value') || props.has('disabled')) {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n }\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._internals = this.attachInternals()\n\n // 仅当属性在连接前未被 property setter 设值时,才从 attribute 读取初始值\n const attrValue = this.getAttribute('value')\n if (attrValue !== null && this._value === '') {\n this._value = attrValue\n }\n this._initialValue = attrValue ?? ''\n this._internals?.setFormValue?.(this._value)\n\n this.childObserver.observe(this, { childList: true })\n this.addEventListener('change', this._handleChildChange)\n }\n\n override firstUpdated() {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.childObserver.disconnect()\n this.removeEventListener('change', this._handleChildChange)\n }\n\n private _handleChildChange(e: Event) {\n if (this.disabled) return\n const target = e.target as HTMLElement\n if (!target.matches?.('web-ui-segmented-trigger')) return\n const trigger = target as WebUiSegmentedTrigger\n if (trigger.value === this._value) return\n\n this.value = trigger.value\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n formResetCallback() {\n this.value = this._initialValue\n }\n\n formDisabledCallback(disabled: boolean) {\n this.disabled = disabled\n }\n\n override render() {\n return html`\n <div class=\"wui-segmented\">\n <span class=\"wui-segmented-indicator wui-glass\"></span>\n <slot></slot>\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-segmented': WebUiSegmented\n }\n}\n"],"mappings":"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"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/segmented/index.ts"],"sourcesContent":["import { html, LitElement, type PropertyValues, unsafeCSS } from 'lit'\nimport { customElement, property, state } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\n\nimport glass from '@/assets/glass.css?inline'\n\nimport type { WebUiSegmentedTrigger } from '../segmented-trigger'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-segmented')\nexport class WebUiSegmented extends LitElement {\n static override styles = [unsafeCSS(glass), unsafeCSS(style)]\n static formAssociated = true\n\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 private _internals?: ElementInternals\n @state() private _value = ''\n @state() private _formDisabled = false\n private _initialValue = ''\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._internals?.setFormValue?.(v)\n this.requestUpdate('value', old)\n }\n\n private readonly childObserver = new MutationObserver(() => {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n })\n\n private _syncPropsToChildren() {\n this.querySelectorAll<WebUiSegmentedTrigger>('web-ui-segmented-trigger').forEach(trigger => {\n trigger.checked = trigger.value === this._value\n trigger.setGroupDisabled(this._isDisabled)\n })\n }\n\n private get _isDisabled(): boolean {\n return this.disabled || this._formDisabled\n }\n\n private _updateIndicator() {\n const triggers = this.querySelectorAll<WebUiSegmentedTrigger>('web-ui-segmented-trigger')\n let left = 0\n let width = 0\n\n triggers.forEach(trigger => {\n if (trigger.value === this._value) {\n const triggerRect = trigger.getBoundingClientRect()\n const groupRect = this.getBoundingClientRect()\n left = triggerRect.left - groupRect.left\n width = triggerRect.width\n }\n })\n\n this.style.setProperty('--indicator-left', `${left}px`)\n this.style.setProperty('--indicator-width', `${width}px`)\n }\n\n override updated(props: PropertyValues) {\n if (props.has('value') || props.has('disabled') || props.has('_formDisabled')) {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n }\n }\n\n override connectedCallback() {\n super.connectedCallback()\n this._internals = this.attachInternals()\n\n // 仅当属性在连接前未被 property setter 设值时,才从 attribute 读取初始值\n const attrValue = this.getAttribute('value')\n if (attrValue !== null && this._value === '') {\n this._value = attrValue\n }\n this._initialValue = attrValue ?? ''\n this._internals?.setFormValue?.(this._value)\n\n this.childObserver.observe(this, { childList: true })\n this.addEventListener('change', this._handleChildChange)\n }\n\n override firstUpdated() {\n this._syncPropsToChildren()\n requestAnimationFrame(() => this._updateIndicator())\n }\n\n override disconnectedCallback() {\n super.disconnectedCallback()\n this.childObserver.disconnect()\n this.removeEventListener('change', this._handleChildChange)\n }\n\n private _handleChildChange(e: Event) {\n if (this._isDisabled) return\n const target = e.target as HTMLElement\n if (!target.matches?.('web-ui-segmented-trigger')) return\n const trigger = target as WebUiSegmentedTrigger\n if (trigger.value === this._value) return\n\n this.value = trigger.value\n this.dispatchEvent(new Event('input', { bubbles: true, composed: true }))\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n formResetCallback() {\n this.value = this._initialValue\n }\n\n formDisabledCallback(disabled: boolean) {\n this._formDisabled = disabled\n }\n\n override render() {\n return html`\n <div class=${classMap({ 'wui-segmented': true, 'is-disabled': this._isDisabled })}>\n <span class=\"wui-segmented-indicator wui-glass\"></span>\n <slot></slot>\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-segmented': WebUiSegmented\n }\n}\n"],"mappings":"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"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/segmented/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{-webkit-user-select:none;user-select:none;display:inline-block}.wui-segmented{background-color:var(--wui-color-surface-raised-mid,#dfdfdf);border-radius:2147483647px;align-items:stretch;padding:4px;display:inline-flex;position:relative}:host([disabled]){cursor:not-allowed}:host([disabled]) .wui-segmented{opacity:.4}.wui-segmented-indicator{z-index:0;top:3px;bottom:3px;left:var(--indicator-left,0);width:var(--indicator-width,0);background-color:var(--wui-color-surface-glass,#fafafa8c);transition:left var(--wui-duration-fast,.16s) var(--wui-ease-standard,cubic-bezier(.77, 0, .175, 1)), width var(--wui-duration-fast,.16s) var(--wui-ease-standard,cubic-bezier(.77, 0, .175, 1));border-radius:2147483647px;position:absolute}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{-webkit-user-select:none;user-select:none;display:inline-block}.wui-segmented{background-color:var(--wui-color-surface-raised-mid,#dfdfdf);border-radius:2147483647px;align-items:stretch;padding:4px;display:inline-flex;position:relative}.wui-segmented.is-disabled{cursor:not-allowed;opacity:.4}.wui-segmented.is-disabled .wui-segmented-trigger.is-disabled{opacity:1}.wui-segmented-indicator{z-index:0;top:3px;bottom:3px;left:var(--indicator-left,0);width:var(--indicator-width,0);background-color:var(--wui-color-surface-glass,#fafafa8c);transition:left var(--wui-duration-fast,.16s) var(--wui-ease-standard,cubic-bezier(.77, 0, .175, 1)), width var(--wui-duration-fast,.16s) var(--wui-ease-standard,cubic-bezier(.77, 0, .175, 1));border-radius:2147483647px;position:absolute}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/segmented/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n user-select: none;\n display: inline-block;\n}\n\n.wui-segmented {\n position: relative;\n\n display: inline-flex;\n align-items: stretch;\n\n padding: 4px;\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-raised-mid, #dfdfdf);\n}\n\n:host([disabled]) {\n cursor: not-allowed;\n\n .wui-segmented {\n opacity: 0.4;\n }\n}\n\n.wui-segmented-indicator {\n position: absolute;\n z-index: 0;\n top: 3px;\n bottom: 3px;\n left: var(--indicator-left, 0);\n\n width: var(--indicator-width, 0);\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-glass, rgb(250 250 250 / 0.55));\n\n transition:\n left var(--wui-duration-fast, 160ms) var(--wui-ease-standard, cubic-bezier(0.77, 0, 0.175, 1)),\n width var(--wui-duration-fast, 160ms) var(--wui-ease-standard, cubic-bezier(0.77, 0, 0.175, 1));\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/segmented/style.css?inline"],"sourcesContent":["*,\n*::before,\n*::after {\n box-sizing: border-box;\n}\n\n:host {\n user-select: none;\n display: inline-block;\n}\n\n.wui-segmented {\n position: relative;\n\n display: inline-flex;\n align-items: stretch;\n\n padding: 4px;\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-raised-mid, #dfdfdf);\n}\n\n.wui-segmented.is-disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n\n.wui-segmented.is-disabled .wui-segmented-trigger.is-disabled {\n opacity: 1;\n}\n\n.wui-segmented-indicator {\n position: absolute;\n z-index: 0;\n top: 3px;\n bottom: 3px;\n left: var(--indicator-left, 0);\n\n width: var(--indicator-width, 0);\n border-radius: calc(infinity * 1px);\n\n background-color: var(--wui-color-surface-glass, rgb(250 250 250 / 0.55));\n\n transition:\n left var(--wui-duration-fast, 160ms) var(--wui-ease-standard, cubic-bezier(0.77, 0, 0.175, 1)),\n width var(--wui-duration-fast, 160ms) var(--wui-ease-standard, cubic-bezier(0.77, 0, 0.175, 1));\n}\n"],"mappings":""}
@@ -4,6 +4,9 @@ export declare class WebUiSegmentedTrigger extends LitElement {
4
4
  value: string;
5
5
  checked: boolean;
6
6
  disabled: boolean;
7
+ private _groupDisabled;
8
+ private get _isDisabled();
9
+ setGroupDisabled(disabled: boolean): void;
7
10
  private handleClick;
8
11
  private handleKeyDown;
9
12
  render(): import('lit').TemplateResult<1>;
@@ -1,18 +1,24 @@
1
1
  import e from "../../_virtual/_@oxc-project_runtime@0.140.0/helpers/esm/decorate.js";
2
2
  import t from "./style.js";
3
3
  import { LitElement as n, html as r, unsafeCSS as i } from "lit";
4
- import { customElement as a, property as o } from "lit/decorators.js";
5
- import { classMap as s } from "lit/directives/class-map.js";
4
+ import { customElement as a, property as o, state as s } from "lit/decorators.js";
5
+ import { classMap as c } from "lit/directives/class-map.js";
6
6
  //#region src/components/segmented-trigger/index.ts
7
- var c = class extends n {
7
+ var l = class extends n {
8
8
  constructor(...e) {
9
- super(...e), this.value = "", this.checked = !1, this.disabled = !1;
9
+ super(...e), this.value = "", this.checked = !1, this.disabled = !1, this._groupDisabled = !1;
10
10
  }
11
11
  static {
12
12
  this.styles = i(t);
13
13
  }
14
+ get _isDisabled() {
15
+ return this.disabled || this._groupDisabled;
16
+ }
17
+ setGroupDisabled(e) {
18
+ this._groupDisabled = e;
19
+ }
14
20
  handleClick() {
15
- this.disabled || this.checked || (this.checked = !0, this.dispatchEvent(new Event("change", {
21
+ this._isDisabled || this.checked || (this.checked = !0, this.dispatchEvent(new Event("change", {
16
22
  bubbles: !0,
17
23
  composed: !0
18
24
  })));
@@ -23,14 +29,15 @@ var c = class extends n {
23
29
  render() {
24
30
  return r`
25
31
  <div
26
- class=${s({
32
+ class=${c({
27
33
  "wui-segmented-trigger": !0,
28
34
  "is-checked": this.checked,
29
- "is-disabled": this.disabled
35
+ "is-disabled": this._isDisabled
30
36
  })}
31
- tabindex="0"
37
+ tabindex=${this._isDisabled ? "-1" : "0"}
32
38
  role="option"
33
39
  .ariaSelected=${String(this.checked)}
40
+ aria-disabled=${String(this._isDisabled)}
34
41
  @click=${this.handleClick}
35
42
  @keydown=${this.handleKeyDown}
36
43
  >
@@ -39,14 +46,14 @@ var c = class extends n {
39
46
  `;
40
47
  }
41
48
  };
42
- e([o({ type: String })], c.prototype, "value", void 0), e([o({
49
+ e([o({ type: String })], l.prototype, "value", void 0), e([o({
43
50
  type: Boolean,
44
51
  reflect: !0
45
- })], c.prototype, "checked", void 0), e([o({
52
+ })], l.prototype, "checked", void 0), e([o({
46
53
  type: Boolean,
47
54
  reflect: !0
48
- })], c.prototype, "disabled", void 0), c = e([a("web-ui-segmented-trigger")], c);
55
+ })], l.prototype, "disabled", void 0), e([s()], l.prototype, "_groupDisabled", void 0), l = e([a("web-ui-segmented-trigger")], l);
49
56
  //#endregion
50
- export { c as WebUiSegmentedTrigger };
57
+ export { l as WebUiSegmentedTrigger };
51
58
 
52
59
  //# sourceMappingURL=index.js.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/segmented-trigger/index.ts"],"sourcesContent":["import { html, LitElement, unsafeCSS } from 'lit'\nimport { customElement, property } from 'lit/decorators.js'\nimport { classMap } from 'lit/directives/class-map.js'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-segmented-trigger')\nexport class WebUiSegmentedTrigger extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) checked = false\n @property({ type: Boolean, reflect: true }) disabled = false\n\n private handleClick() {\n if (this.disabled || this.checked) return\n this.checked = true\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-segmented-trigger': true,\n 'is-checked': this.checked,\n 'is-disabled': this.disabled\n }\n\n return html`\n <div\n class=${classMap(cls)}\n tabindex=\"0\"\n role=\"option\"\n .ariaSelected=${String(this.checked)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <slot></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-segmented-trigger': WebUiSegmentedTrigger\n }\n}\n"],"mappings":";;;;;;AAOO,IAAM,IAAN,cAAoC,EAAW;;EAKG,aAFnB,KAAA,QAAA,IACkB,KAAA,UAAA,IACC,KAAA,WAAA;;;EAJ9B,KAAA,SAAA,EAAU,CAAK;;CAMxC,cAAsB;EAChB,KAAK,YAAY,KAAK,YAC1B,KAAK,UAAU,IACf,KAAK,cAAc,IAAI,MAAM,UAAU;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;CAC3E;CAEA,cAAsB,GAAkB;EACtC,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,GACjB,KAAK,YAAY;CAErB;CAEA,SAAkB;EAOhB,OAAO,CAAI;;gBAEC,EAAS;GAPnB,yBAAyB;GACzB,cAAc,KAAK;GACnB,eAAe,KAAK;EAKD,CAAG,EAAE;;;wBAGN,OAAO,KAAK,OAAO,EAAE;iBAC5B,KAAK,YAAY;mBACf,KAAK,cAAc;;;;;CAKpC;AAKF;AAzCG,EAAA,CAAA,EAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,KAAA,CAAA,GACzB,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GAN3C,IAAA,EAAA,CAAA,EAAc,0BAA0B,CAAA,GAAA,CAAA"}
1
+ {"version":3,"file":"index.js","names":[],"sources":["../../../src/components/segmented-trigger/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'\n\nimport style from './style.css?inline'\n\n@customElement('web-ui-segmented-trigger')\nexport class WebUiSegmentedTrigger extends LitElement {\n static override styles = unsafeCSS(style)\n\n @property({ type: String }) value = ''\n @property({ type: Boolean, reflect: true }) checked = false\n @property({ type: Boolean, reflect: true }) disabled = false\n @state() private _groupDisabled = false\n\n private get _isDisabled(): boolean {\n return this.disabled || this._groupDisabled\n }\n\n setGroupDisabled(disabled: boolean) {\n this._groupDisabled = disabled\n }\n\n private handleClick() {\n if (this._isDisabled || this.checked) return\n this.checked = true\n this.dispatchEvent(new Event('change', { bubbles: true, composed: true }))\n }\n\n private handleKeyDown(e: KeyboardEvent) {\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault()\n this.handleClick()\n }\n }\n\n override render() {\n const cls = {\n 'wui-segmented-trigger': true,\n 'is-checked': this.checked,\n 'is-disabled': this._isDisabled\n }\n\n return html`\n <div\n class=${classMap(cls)}\n tabindex=${this._isDisabled ? '-1' : '0'}\n role=\"option\"\n .ariaSelected=${String(this.checked)}\n aria-disabled=${String(this._isDisabled)}\n @click=${this.handleClick}\n @keydown=${this.handleKeyDown}\n >\n <slot></slot>\n </div>\n `\n }\n\n declare readonly $events: {\n change: Event\n }\n}\n\ndeclare global {\n interface HTMLElementTagNameMap {\n 'web-ui-segmented-trigger': WebUiSegmentedTrigger\n }\n}\n"],"mappings":";;;;;;AAOO,IAAM,IAAN,cAAoC,EAAW;;EAMlB,aAHE,KAAA,QAAA,IACkB,KAAA,UAAA,IACC,KAAA,WAAA,IACrB,KAAA,iBAAA;;;EALT,KAAA,SAAA,EAAU,CAAK;;CAOxC,IAAY,cAAuB;EACjC,OAAO,KAAK,YAAY,KAAK;CAC/B;CAEA,iBAAiB,GAAmB;EAClC,KAAK,iBAAiB;CACxB;CAEA,cAAsB;EAChB,KAAK,eAAe,KAAK,YAC7B,KAAK,UAAU,IACf,KAAK,cAAc,IAAI,MAAM,UAAU;GAAE,SAAS;GAAM,UAAU;EAAK,CAAC,CAAC;CAC3E;CAEA,cAAsB,GAAkB;EACtC,CAAI,EAAE,QAAQ,WAAW,EAAE,QAAQ,SACjC,EAAE,eAAe,GACjB,KAAK,YAAY;CAErB;CAEA,SAAkB;EAOhB,OAAO,CAAI;;gBAEC,EAAS;GAPnB,yBAAyB;GACzB,cAAc,KAAK;GACnB,eAAe,KAAK;EAKD,CAAG,EAAE;mBACX,KAAK,cAAc,OAAO,IAAI;;wBAEzB,OAAO,KAAK,OAAO,EAAE;wBACrB,OAAO,KAAK,WAAW,EAAE;iBAChC,KAAK,YAAY;mBACf,KAAK,cAAc;;;;;CAKpC;AAKF;AAnDG,EAAA,CAAA,EAAS,EAAE,MAAM,OAAO,CAAC,CAAA,GAAA,EAAA,WAAA,SAAA,KAAA,CAAA,GACzB,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,WAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAS;CAAE,MAAM;CAAS,SAAS;AAAK,CAAC,CAAA,GAAA,EAAA,WAAA,YAAA,KAAA,CAAA,GACzC,EAAA,CAAA,EAAM,CAAA,GAAA,EAAA,WAAA,kBAAA,KAAA,CAAA,GAPR,IAAA,EAAA,CAAA,EAAc,0BAA0B,CAAA,GAAA,CAAA"}
@@ -1,5 +1,5 @@
1
1
  //#region src/components/segmented-trigger/style.css?inline
2
- var e = "*,:before,:after{box-sizing:border-box}:host{display:inline-block}:host([disabled]){cursor:not-allowed}.wui-segmented-trigger{cursor:pointer;-webkit-user-select:none;user-select:none;z-index:1;height:34px;font-size:var(--wui-font-size,14px);color:var(--wui-color-text-muted,#6a6a6a);white-space:nowrap;border-radius:2147483647px;justify-content:center;align-items:center;padding:0 16px;line-height:1;transition:color .2s;display:inline-flex;position:relative}.wui-segmented-trigger:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px}.wui-segmented-trigger.is-checked{color:var(--wui-color-text,#1a1a1a)}:host([disabled]) .wui-segmented-trigger{cursor:not-allowed;opacity:.4}";
2
+ var e = "*,:before,:after{box-sizing:border-box}:host{display:inline-block}:host([disabled]){cursor:not-allowed}.wui-segmented-trigger{cursor:pointer;-webkit-user-select:none;user-select:none;z-index:1;height:34px;font-size:var(--wui-font-size,14px);color:var(--wui-color-text-muted,#6a6a6a);white-space:nowrap;border-radius:2147483647px;justify-content:center;align-items:center;padding:0 16px;line-height:1;transition:color .2s;display:inline-flex;position:relative}.wui-segmented-trigger:focus-visible{outline:3px solid var(--wui-focus-ring,#08f6);outline-offset:2px}.wui-segmented-trigger.is-checked{color:var(--wui-color-text,#1a1a1a)}.wui-segmented-trigger.is-disabled{cursor:not-allowed;opacity:.4}";
3
3
  //#endregion
4
4
  export { e as default };
5
5
 
@@ -1 +1 @@
1
- {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/segmented-trigger/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}\n\n.wui-segmented-trigger {\n cursor: pointer;\n user-select: none;\n\n position: relative;\n z-index: 1;\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n height: 34px;\n padding: 0 16px;\n border-radius: calc(infinity * 1px);\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text-muted, #6a6a6a);\n white-space: nowrap;\n\n transition: color 0.2s ease;\n\n &:focus-visible {\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n\n &.is-checked {\n color: var(--wui-color-text, #1a1a1a);\n }\n}\n\n:host([disabled]) .wui-segmented-trigger {\n cursor: not-allowed;\n opacity: 0.4;\n}\n"],"mappings":""}
1
+ {"version":3,"file":"style.js","names":[],"sources":["../../../src/components/segmented-trigger/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}\n\n.wui-segmented-trigger {\n cursor: pointer;\n user-select: none;\n\n position: relative;\n z-index: 1;\n\n display: inline-flex;\n align-items: center;\n justify-content: center;\n\n height: 34px;\n padding: 0 16px;\n border-radius: calc(infinity * 1px);\n\n font-size: var(--wui-font-size, 14px);\n line-height: 1;\n color: var(--wui-color-text-muted, #6a6a6a);\n white-space: nowrap;\n\n transition: color 0.2s ease;\n\n &:focus-visible {\n outline: 3px solid var(--wui-focus-ring, rgb(0 136 255 / 0.4));\n outline-offset: 2px;\n }\n\n &.is-checked {\n color: var(--wui-color-text, #1a1a1a);\n }\n}\n\n.wui-segmented-trigger.is-disabled {\n cursor: not-allowed;\n opacity: 0.4;\n}\n"],"mappings":""}
@@ -1,9 +1,9 @@
1
1
  import { DetailedHTMLProps, HTMLAttributes } from 'react';
2
- import { WebUiAvatar, WebUiBackTop, WebUiBadge, WebUiButton, WebUiButtonGroup, WebUiCheckbox, WebUiCheckboxGroup, WebUiContextMenu, WebUiDialog, WebUiDrawer, WebUiEmpty, WebUiDropdownDivider, WebUiDropdownHeader, WebUiDropdownItem, WebUiDropdown, WebUiIcon, WebUiInput, WebUiInputNumber, WebUiLayout, WebUiOption, WebUiPopover, WebUiRadio, WebUiRadioGroup, WebUiSegmented, WebUiSegmentedTrigger, WebUiSelect, WebUiSlider, WebUiSpinner, WebUiTextarea, WebUiTheme, WebUiToast, WebUiSwitch, WebUiTooltip } from '../components';
3
- import { ExtractProps, EventListeners, OmitLitBase } from './utils';
4
- export type LitReactWrapper<T> = T extends {
2
+ import { WebUiAvatar, WebUiBackTop, WebUiBadge, WebUiButton, WebUiButtonGroup, WebUiCheckbox, WebUiCheckboxGroup, WebUiContextMenu, WebUiDialog, WebUiDrawer, WebUiEmpty, WebUiDropdownDivider, WebUiDropdownHeader, WebUiDropdownItem, WebUiDropdown, WebUiIcon, WebUiInput, WebUiInputNumber, WebUiLayout, WebUiOption, WebUiPopover, WebUiRadio, WebUiRadioGroup, WebUiSegmented, WebUiSegmentedTrigger, WebUiSelect, WebUiSlider, WebUiSpinner, WebUiTextarea, WebUiTheme, WebUiToast, WebUiSwitch, WebUiSvgDrawLines, WebUiTooltip } from '../components';
3
+ import { ExactEventListeners, ExtractProps, OmitLitBase } from './utils';
4
+ export type LitReactWrapper<T extends HTMLElement> = T extends {
5
5
  readonly $events: infer E;
6
- } ? DetailedHTMLProps<ExtractProps<OmitLitBase<T>> & EventListeners<E> & HTMLAttributes<HTMLElement>, HTMLElement> : DetailedHTMLProps<ExtractProps<OmitLitBase<T>> & HTMLAttributes<HTMLElement>, HTMLElement>;
6
+ } ? DetailedHTMLProps<ExtractProps<OmitLitBase<T>> & ExactEventListeners<E> & HTMLAttributes<T>, T> : DetailedHTMLProps<ExtractProps<OmitLitBase<T>> & HTMLAttributes<T>, T>;
7
7
  export interface WebUiComponents {
8
8
  'web-ui-avatar': LitReactWrapper<WebUiAvatar>;
9
9
  'web-ui-back-top': LitReactWrapper<WebUiBackTop>;
@@ -26,6 +26,7 @@ export interface WebUiComponents {
26
26
  'web-ui-select': LitReactWrapper<WebUiSelect>;
27
27
  'web-ui-slider': LitReactWrapper<WebUiSlider>;
28
28
  'web-ui-spinner': LitReactWrapper<WebUiSpinner>;
29
+ 'web-ui-svg-draw-lines': LitReactWrapper<WebUiSvgDrawLines>;
29
30
  'web-ui-option': LitReactWrapper<WebUiOption>;
30
31
  'web-ui-popover': LitReactWrapper<WebUiPopover>;
31
32
  'web-ui-radio': LitReactWrapper<WebUiRadio>;
@@ -10,17 +10,10 @@ export type ExtractProps<T> = {
10
10
  };
11
11
  export type OmitLitBase<T> = Omit<T, keyof HTMLElement | keyof LitElement>;
12
12
  /**
13
- * kebab-case 转 PascalCase
14
- * 'visible-change' → 'VisibleChange'
15
- * 'input' → 'Input'
13
+ * 将 EventMap 转换为 React 19 Custom Element 的精确事件键。
14
+ * 例如 { 'open-change': CustomEvent<...> } → { 'onopen-change'?: (e: CustomEvent<...>) => void }
16
15
  */
17
- type KebabToPascal<S extends string> = S extends `${infer P}-${infer Q}` ? `${Capitalize<P>}${KebabToPascal<Q>}` : Capitalize<S>;
18
- /**
19
- * 将 EventMap 转换为 on* 事件监听器类型
20
- * 例如 { input: Event } → { onInput?: (e: Event) => void }
21
- * { 'visible-change': CustomEvent<...> } → { onVisibleChange?: (e: CustomEvent<...>) => void }
22
- */
23
- export type EventListeners<T> = {
24
- [K in keyof T & string as `on${KebabToPascal<K>}`]?: (e: T[K]) => void;
16
+ export type ExactEventListeners<T> = {
17
+ [K in keyof T & string as `on${K}`]?: (e: T[K]) => void;
25
18
  };
26
19
  export {};
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@greypan/web-ui",
3
- "version": "2.0.1",
3
+ "version": "2.1.0",
4
4
  "description": "Lit-based web components for React, Vue, and vanilla JS",
5
5
  "license": "MIT",
6
6
  "repository": {
@@ -41,8 +41,8 @@
41
41
  },
42
42
  "dependencies": {
43
43
  "@floating-ui/dom": "^1.8.0",
44
- "@greypan/js-kit": "1.6.4",
45
- "@greypan/browser-kit": "1.7.4"
44
+ "@greypan/browser-kit": "1.7.4",
45
+ "@greypan/js-kit": "1.6.4"
46
46
  },
47
47
  "devDependencies": {
48
48
  "@iconify-json/jam": "^1.2.3",