use-wc 1.0.0-pre.89 → 1.0.0-pre.90

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.
@@ -89,6 +89,76 @@
89
89
  }
90
90
  ]
91
91
  },
92
+ {
93
+ "kind": "javascript-module",
94
+ "path": "src/elements/use-avatar/use-avatar.ts",
95
+ "declarations": [
96
+ {
97
+ "kind": "class",
98
+ "description": "`use-avatar` renders a small, fixed-size, clipped container for a person or entity's\nlikeness. Slot in an `<img>` (or `<picture>`) for a photo, an inline `<svg>` for a\ngenerated or stylized mark, or arbitrary markup (a `<div>` of initials, a calendar badge);\nwhatever is slotted is centered and clipped to the avatar's shape. With nothing slotted,\n`use-avatar` falls back to initials derived from the `name` attribute.\n\n```html\n<use-avatar><img src=\"ada.jpg\" alt=\"\" /></use-avatar>\n<use-avatar name=\"Riley Quinn\"></use-avatar>\n<use-avatar><svg viewBox=\"0 0 32 32\">…</svg></use-avatar>\n```\n\nSize it with plain CSS — `inline-size` for the size and `aspect-ratio` for the proportions —\nfrom a stylesheet, a style attribute, or the design system's theme layer. The element takes no\ncustom properties of its own: it is a query container, so the initials scale from whatever box\nit ends up with. A non-square `aspect-ratio` suits a wide thumbnail or poster.\n\nThe element clips its content but gives it no shape of its own — that is the theme's, through\nthe affordance classes it defines: `.circle` (the themed default), `.square`, `.squircle` in\n`theme/avatar.css`. A circle only reads right at a `1` ratio, so a wide avatar wants one of the\nothers.\n\nThe element takes no ARIA role of its own. A slotted `<img>` carries its own `alt`, and the\ninitials fallback is plain text in the accessibility tree — an avatar is decorative beside a\nname far more often than it is the name itself, so labelling it here would mean announcing\nthat name twice.",
99
+ "name": "UseAvatar",
100
+ "cssParts": [
101
+ {
102
+ "description": "The span that renders the `name` fallback.",
103
+ "name": "initials"
104
+ }
105
+ ],
106
+ "slots": [
107
+ {
108
+ "description": "The avatar's content: an `<img>`/`<picture>`, an `<svg>`, or arbitrary markup. Left empty, the `name` attribute's initials are shown instead, as the slot's own fallback. A photo fills the frame and crops; anything else keeps its own size, capped at the frame. Give an `<svg>` a `width`/`height` — without one it defaults to filling the box, and a rounded shape will clip its corners.",
109
+ "name": ""
110
+ }
111
+ ],
112
+ "members": [
113
+ {
114
+ "kind": "field",
115
+ "name": "name",
116
+ "type": {
117
+ "text": "string"
118
+ },
119
+ "default": "\"\"",
120
+ "attribute": "name",
121
+ "reflects": true
122
+ }
123
+ ],
124
+ "attributes": [
125
+ {
126
+ "description": "The full name, used for the initials fallback (first + last whitespace-separated token, uppercased) when the slot is empty.",
127
+ "name": "name",
128
+ "type": {
129
+ "text": "string"
130
+ },
131
+ "default": "\"\"",
132
+ "fieldName": "name"
133
+ }
134
+ ],
135
+ "superclass": {
136
+ "name": "LitElement",
137
+ "package": "lit"
138
+ },
139
+ "tagName": "use-avatar",
140
+ "customElement": true
141
+ }
142
+ ],
143
+ "exports": [
144
+ {
145
+ "kind": "js",
146
+ "name": "UseAvatar",
147
+ "declaration": {
148
+ "name": "UseAvatar",
149
+ "module": "src/elements/use-avatar/use-avatar.ts"
150
+ }
151
+ },
152
+ {
153
+ "kind": "custom-element-definition",
154
+ "name": "use-avatar",
155
+ "declaration": {
156
+ "name": "UseAvatar",
157
+ "module": "src/elements/use-avatar/use-avatar.ts"
158
+ }
159
+ }
160
+ ]
161
+ },
92
162
  {
93
163
  "kind": "javascript-module",
94
164
  "path": "src/elements/use-badge/use-badge.ts",
@@ -232,76 +302,6 @@
232
302
  }
233
303
  ]
234
304
  },
235
- {
236
- "kind": "javascript-module",
237
- "path": "src/elements/use-avatar/use-avatar.ts",
238
- "declarations": [
239
- {
240
- "kind": "class",
241
- "description": "`use-avatar` renders a small, fixed-size, clipped container for a person or entity's\nlikeness. Slot in an `<img>` (or `<picture>`) for a photo, an inline `<svg>` for a\ngenerated or stylized mark, or arbitrary markup (a `<div>` of initials, a calendar badge);\nwhatever is slotted is centered and clipped to the avatar's shape. With nothing slotted,\n`use-avatar` falls back to initials derived from the `name` attribute.\n\n```html\n<use-avatar><img src=\"ada.jpg\" alt=\"\" /></use-avatar>\n<use-avatar name=\"Riley Quinn\"></use-avatar>\n<use-avatar><svg viewBox=\"0 0 32 32\">…</svg></use-avatar>\n```\n\nSize it with plain CSS — `inline-size` for the size and `aspect-ratio` for the proportions —\nfrom a stylesheet, a style attribute, or the design system's theme layer. The element takes no\ncustom properties of its own: it is a query container, so the initials scale from whatever box\nit ends up with. A non-square `aspect-ratio` suits a wide thumbnail or poster.\n\nThe element clips its content but gives it no shape of its own — that is the theme's, through\nthe affordance classes it defines: `.circle` (the themed default), `.square`, `.squircle` in\n`theme/avatar.css`. A circle only reads right at a `1` ratio, so a wide avatar wants one of the\nothers.\n\nThe element takes no ARIA role of its own. A slotted `<img>` carries its own `alt`, and the\ninitials fallback is plain text in the accessibility tree — an avatar is decorative beside a\nname far more often than it is the name itself, so labelling it here would mean announcing\nthat name twice.",
242
- "name": "UseAvatar",
243
- "cssParts": [
244
- {
245
- "description": "The span that renders the `name` fallback.",
246
- "name": "initials"
247
- }
248
- ],
249
- "slots": [
250
- {
251
- "description": "The avatar's content: an `<img>`/`<picture>`, an `<svg>`, or arbitrary markup. Left empty, the `name` attribute's initials are shown instead, as the slot's own fallback. A photo fills the frame and crops; anything else keeps its own size, capped at the frame. Give an `<svg>` a `width`/`height` — without one it defaults to filling the box, and a rounded shape will clip its corners.",
252
- "name": ""
253
- }
254
- ],
255
- "members": [
256
- {
257
- "kind": "field",
258
- "name": "name",
259
- "type": {
260
- "text": "string"
261
- },
262
- "default": "\"\"",
263
- "attribute": "name",
264
- "reflects": true
265
- }
266
- ],
267
- "attributes": [
268
- {
269
- "description": "The full name, used for the initials fallback (first + last whitespace-separated token, uppercased) when the slot is empty.",
270
- "name": "name",
271
- "type": {
272
- "text": "string"
273
- },
274
- "default": "\"\"",
275
- "fieldName": "name"
276
- }
277
- ],
278
- "superclass": {
279
- "name": "LitElement",
280
- "package": "lit"
281
- },
282
- "tagName": "use-avatar",
283
- "customElement": true
284
- }
285
- ],
286
- "exports": [
287
- {
288
- "kind": "js",
289
- "name": "UseAvatar",
290
- "declaration": {
291
- "name": "UseAvatar",
292
- "module": "src/elements/use-avatar/use-avatar.ts"
293
- }
294
- },
295
- {
296
- "kind": "custom-element-definition",
297
- "name": "use-avatar",
298
- "declaration": {
299
- "name": "UseAvatar",
300
- "module": "src/elements/use-avatar/use-avatar.ts"
301
- }
302
- }
303
- ]
304
- },
305
305
  {
306
306
  "kind": "javascript-module",
307
307
  "path": "src/elements/use-calendar-base/use-calendar-base.ts",
@@ -592,7 +592,7 @@
592
592
  {
593
593
  "name": "data",
594
594
  "type": {
595
- "text": "{ day: number; date: string }"
595
+ "text": "UseCalendarDayData"
596
596
  }
597
597
  },
598
598
  {
@@ -602,7 +602,7 @@
602
602
  }
603
603
  }
604
604
  ],
605
- "description": "See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering."
605
+ "description": "See UseCalendarRenderDay. The default renders `localizedDay`, the\nday in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering."
606
606
  },
607
607
  {
608
608
  "kind": "method",
@@ -734,6 +734,10 @@
734
734
  "name": "InlineDirectionElement",
735
735
  "module": "/src/utils/inline-direction-element"
736
736
  },
737
+ {
738
+ "name": "LocaleDigitsElement",
739
+ "module": "/src/utils/locale-digits-element"
740
+ },
737
741
  {
738
742
  "name": "LocalizedElement",
739
743
  "module": "/src/elements/use-locale-element/use-locale-element"
@@ -1062,6 +1066,10 @@
1062
1066
  }
1063
1067
  ],
1064
1068
  "mixins": [
1069
+ {
1070
+ "name": "LocaleDigitsElement",
1071
+ "module": "/src/utils/locale-digits-element"
1072
+ },
1065
1073
  {
1066
1074
  "name": "LocalizedElement",
1067
1075
  "module": "/src/elements/use-locale-element/use-locale-element"
@@ -1100,7 +1108,7 @@
1100
1108
  "declarations": [
1101
1109
  {
1102
1110
  "kind": "class",
1103
- "description": "A day-granularity date picker. Value format: YYYY-MM-DD.\n\nCustomize day rendering on an instance:\n```ts\ndatePicker.renderDay = ({ day, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${day}</strong>` : String(day);\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyDatePicker extends UseDatePicker {\n renderDay(data: { day: number; date: string }, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.day}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```",
1111
+ "description": "A day-granularity date picker. Value format: YYYY-MM-DD.\n\nCustomize day rendering on an instance:\n```ts\ndatePicker.renderDay = ({ localizedDay, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${localizedDay}</strong>` : localizedDay;\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyDatePicker extends UseDatePicker {\n renderDay(data: UseCalendarDayData, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.localizedDay}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```",
1104
1112
  "name": "UseDatePicker",
1105
1113
  "members": [
1106
1114
  {
@@ -1319,7 +1327,7 @@
1319
1327
  {
1320
1328
  "name": "data",
1321
1329
  "type": {
1322
- "text": "{ day: number; date: string }"
1330
+ "text": "UseCalendarDayData"
1323
1331
  }
1324
1332
  },
1325
1333
  {
@@ -1329,7 +1337,7 @@
1329
1337
  }
1330
1338
  }
1331
1339
  ],
1332
- "description": "See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.",
1340
+ "description": "See UseCalendarRenderDay. The default renders `localizedDay`, the\nday in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.",
1333
1341
  "inheritedFrom": {
1334
1342
  "name": "UseCalendarBase",
1335
1343
  "module": "src/elements/use-calendar-base/use-calendar-base.ts"
@@ -2346,6 +2354,10 @@
2346
2354
  }
2347
2355
  ],
2348
2356
  "mixins": [
2357
+ {
2358
+ "name": "LocaleDigitsElement",
2359
+ "module": "/src/utils/locale-digits-element"
2360
+ },
2349
2361
  {
2350
2362
  "name": "LocalizedElement",
2351
2363
  "module": "/src/elements/use-locale-element/use-locale-element"
@@ -4887,6 +4899,10 @@
4887
4899
  }
4888
4900
  ],
4889
4901
  "mixins": [
4902
+ {
4903
+ "name": "LocaleDigitsElement",
4904
+ "module": "/src/utils/locale-digits-element"
4905
+ },
4890
4906
  {
4891
4907
  "name": "LocalizedElement",
4892
4908
  "module": "/src/elements/use-locale-element/use-locale-element"
@@ -5225,7 +5241,7 @@
5225
5241
  "declarations": [
5226
5242
  {
5227
5243
  "kind": "class",
5228
- "description": "A week-granularity date picker. Value format: YYYY-Www (ISO 8601 week).\n\nCustomize day rendering on an instance:\n```ts\nweekPicker.renderDay = ({ day, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${day}</strong>` : String(day);\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyWeekPicker extends UseWeekPicker {\n renderDay(data: { day: number; date: string }, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.day}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```",
5244
+ "description": "A week-granularity date picker. Value format: YYYY-Www (ISO 8601 week).\n\nCustomize day rendering on an instance:\n```ts\nweekPicker.renderDay = ({ localizedDay, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${localizedDay}</strong>` : localizedDay;\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyWeekPicker extends UseWeekPicker {\n renderDay(data: UseCalendarDayData, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.localizedDay}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```",
5229
5245
  "name": "UseWeekPicker",
5230
5246
  "members": [
5231
5247
  {
@@ -5444,7 +5460,7 @@
5444
5460
  {
5445
5461
  "name": "data",
5446
5462
  "type": {
5447
- "text": "{ day: number; date: string }"
5463
+ "text": "UseCalendarDayData"
5448
5464
  }
5449
5465
  },
5450
5466
  {
@@ -5454,7 +5470,7 @@
5454
5470
  }
5455
5471
  }
5456
5472
  ],
5457
- "description": "See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.",
5473
+ "description": "See UseCalendarRenderDay. The default renders `localizedDay`, the\nday in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.",
5458
5474
  "inheritedFrom": {
5459
5475
  "name": "UseCalendarBase",
5460
5476
  "module": "src/elements/use-calendar-base/use-calendar-base.ts"
@@ -1188,8 +1188,6 @@
1188
1188
  &::part(segment-input) {
1189
1189
  all: unset;
1190
1190
  box-sizing: content-box;
1191
- appearance: none;
1192
- -moz-appearance: textfield;
1193
1191
  field-sizing: content;
1194
1192
  outline: none;
1195
1193
  border-radius: var(--usewc-effect-input-border-radius);
@@ -4,17 +4,17 @@ A day-granularity date picker. Value format: YYYY-MM-DD.
4
4
 
5
5
  Customize day rendering on an instance:
6
6
  ```ts
7
- datePicker.renderDay = ({ day, date }, html) =>
8
- date === "2026-03-17" ? html`<strong>${day}</strong>` : String(day);
7
+ datePicker.renderDay = ({ localizedDay, date }, html) =>
8
+ date === "2026-03-17" ? html`<strong>${localizedDay}</strong>` : localizedDay;
9
9
  ```
10
10
 
11
11
  Or override on a subclass to apply it to every instance, falling back to
12
12
  `super.renderDay(...)` for dates you don't want to customize:
13
13
  ```ts
14
14
  class MyDatePicker extends UseDatePicker {
15
- renderDay(data: { day: number; date: string }, html: LitHtml) {
15
+ renderDay(data: UseCalendarDayData, html: LitHtml) {
16
16
  if (data.date === "2026-03-17") {
17
- return html`<strong>${data.day}</strong>`;
17
+ return html`<strong>${data.localizedDay}</strong>`;
18
18
  }
19
19
  return super.renderDay(data, html);
20
20
  }
@@ -56,7 +56,8 @@ the browser's language. |
56
56
  | Name | Signature | Description |
57
57
  | --- | --- | --- |
58
58
  | getDateForDay | `(day: number)` | - |
59
- | renderDay | `(data: { day: number; date: string }, _html: LitHtml): TemplateResult \| string` | See UseCalendarRenderDay. Assigning a new function on an
59
+ | renderDay | `(data: UseCalendarDayData, _html: LitHtml): TemplateResult \| string` | See UseCalendarRenderDay. The default renders `localizedDay`, the
60
+ day in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an
60
61
  instance (e.g. via `.renderDay=${fn}` in a Lit template) after first
61
62
  render does not schedule a re-render on its own — call `requestUpdate()`
62
63
  if needed.
@@ -4,17 +4,17 @@ A week-granularity date picker. Value format: YYYY-Www (ISO 8601 week).
4
4
 
5
5
  Customize day rendering on an instance:
6
6
  ```ts
7
- weekPicker.renderDay = ({ day, date }, html) =>
8
- date === "2026-03-17" ? html`<strong>${day}</strong>` : String(day);
7
+ weekPicker.renderDay = ({ localizedDay, date }, html) =>
8
+ date === "2026-03-17" ? html`<strong>${localizedDay}</strong>` : localizedDay;
9
9
  ```
10
10
 
11
11
  Or override on a subclass to apply it to every instance, falling back to
12
12
  `super.renderDay(...)` for dates you don't want to customize:
13
13
  ```ts
14
14
  class MyWeekPicker extends UseWeekPicker {
15
- renderDay(data: { day: number; date: string }, html: LitHtml) {
15
+ renderDay(data: UseCalendarDayData, html: LitHtml) {
16
16
  if (data.date === "2026-03-17") {
17
- return html`<strong>${data.day}</strong>`;
17
+ return html`<strong>${data.localizedDay}</strong>`;
18
18
  }
19
19
  return super.renderDay(data, html);
20
20
  }
@@ -56,7 +56,8 @@ the browser's language. |
56
56
  | Name | Signature | Description |
57
57
  | --- | --- | --- |
58
58
  | getDateForDay | `(day: number)` | - |
59
- | renderDay | `(data: { day: number; date: string }, _html: LitHtml): TemplateResult \| string` | See UseCalendarRenderDay. Assigning a new function on an
59
+ | renderDay | `(data: UseCalendarDayData, _html: LitHtml): TemplateResult \| string` | See UseCalendarRenderDay. The default renders `localizedDay`, the
60
+ day in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an
60
61
  instance (e.g. via `.renderDay=${fn}` in a Lit template) after first
61
62
  render does not schedule a re-render on its own — call `requestUpdate()`
62
63
  if needed.
package/dist/use-wc.d.mts CHANGED
@@ -140,6 +140,15 @@ declare class LocalizedElementInterface<Defaults extends StringDefaults> {
140
140
  protected intlStrings(): Partial<Record<keyof Defaults, string>>;
141
141
  }
142
142
  //#endregion
143
+ //#region src/utils/locale-digits-element.d.ts
144
+ declare class LocaleDigitsElementInterface {
145
+ protected toLocaleDigits(value: string | number | undefined): string;
146
+ protected toLatinDigits(text: string): string;
147
+ protected segmentNumber(text: string | undefined): number | undefined;
148
+ protected stepSegment(event: KeyboardEvent, minimum: number, maximum?: number): void;
149
+ protected filterSegmentInput(event: InputEvent, applyPastedValue: (text: string) => boolean): void;
150
+ }
151
+ //#endregion
143
152
  //#region src/utils/date-time-aria-labels.d.ts
144
153
  type DateTimeAriaLabelKeys = "am" | "pm" | "year" | "month" | "day" | "hour" | "minute" | "second" | "millisecond" | "dayPeriod";
145
154
  type DateTimeAriaLabels = Record<DateTimeAriaLabelKeys, string>;
@@ -189,7 +198,7 @@ type UncompiledTemplateResult<T extends ResultType = ResultType> = {
189
198
  type TemplateResult$1<T extends ResultType = ResultType> = UncompiledTemplateResult<T>;
190
199
  //#endregion
191
200
  //#region src/elements/use-date-input/use-date-input.d.ts
192
- declare const UseDateInput_base: (new (...args: any[]) => LocalizedElementInterface<{
201
+ declare const UseDateInput_base: (new (...args: any[]) => LocaleDigitsElementInterface) & (new (...args: any[]) => LocalizedElementInterface<{
193
202
  year: string;
194
203
  month: string;
195
204
  day: string;
@@ -242,7 +251,7 @@ declare global {
242
251
  }
243
252
  //#endregion
244
253
  //#region src/elements/use-duration-input/use-duration-input.d.ts
245
- declare const UseDurationInput_base: (new (...args: any[]) => LocalizedElementInterface<{
254
+ declare const UseDurationInput_base: (new (...args: any[]) => LocaleDigitsElementInterface) & (new (...args: any[]) => LocalizedElementInterface<{
246
255
  year: string;
247
256
  month: string;
248
257
  day: string;
@@ -364,7 +373,7 @@ declare global {
364
373
  }
365
374
  //#endregion
366
375
  //#region src/elements/use-time-input/use-time-input.d.ts
367
- declare const UseTimeInput_base: (new (...args: any[]) => LocalizedElementInterface<{
376
+ declare const UseTimeInput_base: (new (...args: any[]) => LocaleDigitsElementInterface) & (new (...args: any[]) => LocalizedElementInterface<{
368
377
  hour: string;
369
378
  minute: string;
370
379
  second: string;
@@ -1022,13 +1031,20 @@ declare global {
1022
1031
  //#endregion
1023
1032
  //#region src/elements/use-calendar-base/use-calendar-base.d.ts
1024
1033
  type LitHtml$1 = typeof html;
1034
+ /** The day cell passed to {@link UseCalendarRenderDay}. */
1035
+ export type UseCalendarDayData = {
1036
+ day: number;
1037
+ localizedDay: string;
1038
+ date: string;
1039
+ };
1025
1040
  /**
1026
1041
  * Renders the content of a single day cell. Called for every cell in the
1027
1042
  * grid, including leading and trailing filler cells from the previous and
1028
1043
  * next month, so `date` may fall outside the displayed month.
1029
1044
  *
1030
- * `day` is the numeric day of month (not zero-padded); `date` is the ISO
1031
- * `YYYY-MM-DD` string for that cell. The `html` argument is Lit's tag
1045
+ * `day` is the numeric day of month (not zero-padded); `localizedDay` is that
1046
+ * day written in the locale's numbering system (e.g. `١٤` for `ar-EG`); `date`
1047
+ * is the ISO `YYYY-MM-DD` string for that cell. The `html` argument is Lit's tag
1032
1048
  * function, passed in so consumers do not need to import `lit` themselves —
1033
1049
  * use it to return markup, since a plain returned string is escaped.
1034
1050
  *
@@ -1041,11 +1057,8 @@ type LitHtml$1 = typeof html;
1041
1057
  * analyzer (it only reads class and member JSDoc), so it never reaches the
1042
1058
  * generated docs or Storybook.
1043
1059
  */
1044
- export type UseCalendarRenderDay = (data: {
1045
- day: number;
1046
- date: string;
1047
- }, html: LitHtml$1) => TemplateResult | string;
1048
- declare const UseCalendarBase_base: (new (...args: any[]) => InlineDirectionElementInterface) & (new (...args: any[]) => LocalizedElementInterface<{
1060
+ export type UseCalendarRenderDay = (data: UseCalendarDayData, html: LitHtml$1) => TemplateResult | string;
1061
+ declare const UseCalendarBase_base: (new (...args: any[]) => InlineDirectionElementInterface) & (new (...args: any[]) => LocaleDigitsElementInterface) & (new (...args: any[]) => LocalizedElementInterface<{
1049
1062
  previousMonth: string;
1050
1063
  currentMonth: string;
1051
1064
  nextMonth: string;
@@ -1139,7 +1152,8 @@ export declare class UseCalendarBase extends UseCalendarBase_base {
1139
1152
  protected onGridFocusOut(): void;
1140
1153
  getDateForDay(day: number): string;
1141
1154
  /**
1142
- * See {@link UseCalendarRenderDay}. Assigning a new function on an
1155
+ * See {@link UseCalendarRenderDay}. The default renders `localizedDay`, the
1156
+ * day in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an
1143
1157
  * instance (e.g. via `.renderDay=${fn}` in a Lit template) after first
1144
1158
  * render does not schedule a re-render on its own — call `requestUpdate()`
1145
1159
  * if needed.
@@ -1148,10 +1162,7 @@ export declare class UseCalendarBase extends UseCalendarBase_base {
1148
1162
  * overrides can call `super.renderDay(data, html)` to fall back to the
1149
1163
  * default (or a parent override's) rendering.
1150
1164
  */
1151
- renderDay(data: {
1152
- day: number;
1153
- date: string;
1154
- }, _html: LitHtml$1): TemplateResult | string;
1165
+ renderDay(data: UseCalendarDayData, _html: LitHtml$1): TemplateResult | string;
1155
1166
  previousMonth(): void;
1156
1167
  today(): void;
1157
1168
  nextMonth(): void;
@@ -1172,17 +1183,17 @@ export declare class UseCalendarBase extends UseCalendarBase_base {
1172
1183
  *
1173
1184
  * Customize day rendering on an instance:
1174
1185
  * ```ts
1175
- * datePicker.renderDay = ({ day, date }, html) =>
1176
- * date === "2026-03-17" ? html`<strong>${day}</strong>` : String(day);
1186
+ * datePicker.renderDay = ({ localizedDay, date }, html) =>
1187
+ * date === "2026-03-17" ? html`<strong>${localizedDay}</strong>` : localizedDay;
1177
1188
  * ```
1178
1189
  *
1179
1190
  * Or override on a subclass to apply it to every instance, falling back to
1180
1191
  * `super.renderDay(...)` for dates you don't want to customize:
1181
1192
  * ```ts
1182
1193
  * class MyDatePicker extends UseDatePicker {
1183
- * renderDay(data: { day: number; date: string }, html: LitHtml) {
1194
+ * renderDay(data: UseCalendarDayData, html: LitHtml) {
1184
1195
  * if (data.date === "2026-03-17") {
1185
- * return html`<strong>${data.day}</strong>`;
1196
+ * return html`<strong>${data.localizedDay}</strong>`;
1186
1197
  * }
1187
1198
  * return super.renderDay(data, html);
1188
1199
  * }
@@ -1211,17 +1222,17 @@ declare global {
1211
1222
  *
1212
1223
  * Customize day rendering on an instance:
1213
1224
  * ```ts
1214
- * weekPicker.renderDay = ({ day, date }, html) =>
1215
- * date === "2026-03-17" ? html`<strong>${day}</strong>` : String(day);
1225
+ * weekPicker.renderDay = ({ localizedDay, date }, html) =>
1226
+ * date === "2026-03-17" ? html`<strong>${localizedDay}</strong>` : localizedDay;
1216
1227
  * ```
1217
1228
  *
1218
1229
  * Or override on a subclass to apply it to every instance, falling back to
1219
1230
  * `super.renderDay(...)` for dates you don't want to customize:
1220
1231
  * ```ts
1221
1232
  * class MyWeekPicker extends UseWeekPicker {
1222
- * renderDay(data: { day: number; date: string }, html: LitHtml) {
1233
+ * renderDay(data: UseCalendarDayData, html: LitHtml) {
1223
1234
  * if (data.date === "2026-03-17") {
1224
- * return html`<strong>${data.day}</strong>`;
1235
+ * return html`<strong>${data.localizedDay}</strong>`;
1225
1236
  * }
1226
1237
  * return super.renderDay(data, html);
1227
1238
  * }
package/dist/use-wc.mjs CHANGED
@@ -1,5 +1,5 @@
1
1
  import { a as dateTimeFormat, c as localeInfo, l as numberFormat, o as displayNames, r as resolveString, s as durationFormat, t as STRINGS_CHANGE_EVENT } from "./strings-I8gB8a3e.mjs";
2
- import { LitElement, css, html, unsafeCSS } from "lit";
2
+ import { LitElement, css, html, nothing, unsafeCSS } from "lit";
3
3
  import { customElement, property, query, queryAssignedElements } from "lit/decorators.js";
4
4
  import * as duration from "duration-fns";
5
5
  import { focusable, getTabIndex, isFocusable, isTabbable, tabbable } from "tabbable";
@@ -347,8 +347,116 @@ var use_date_input_strings_default = {
347
347
  day: "day"
348
348
  };
349
349
  //#endregion
350
+ //#region src/utils/locale-digits.ts
351
+ const LATIN_DIGITS = "0123456789";
352
+ const digitsByLocale = /* @__PURE__ */ new Map();
353
+ /** The locale's digits for 0 through 9, or `undefined` when it already writes Latin digits. */
354
+ function resolveLocaleDigits(locale) {
355
+ if (!digitsByLocale.has(locale)) {
356
+ const formatter = numberFormat(locale, { useGrouping: false });
357
+ digitsByLocale.set(locale, formatter.resolvedOptions().numberingSystem === "latn" ? void 0 : Array.from(LATIN_DIGITS, (digit) => formatter.format(Number(digit))));
358
+ }
359
+ return digitsByLocale.get(locale);
360
+ }
361
+ /** Writes the Latin digits of a value with the given digits, or as is when there are none. */
362
+ function toLocaleDigits(digits, value) {
363
+ if (value === void 0) return "";
364
+ if (!digits) return String(value);
365
+ return Array.from(String(value)).filter((character) => LATIN_DIGITS.includes(character)).map((character) => digits[Number(character)]).join("");
366
+ }
367
+ const FULLWIDTH_ZERO = 65296;
368
+ function latinDigit(digits, character) {
369
+ if (LATIN_DIGITS.includes(character)) return character;
370
+ const fullwidthOffset = character.charCodeAt(0) - FULLWIDTH_ZERO;
371
+ if (character.length === 1 && fullwidthOffset >= 0 && fullwidthOffset <= 9) return String(fullwidthOffset);
372
+ const index = digits ? digits.indexOf(character) : -1;
373
+ return index === -1 ? void 0 : String(index);
374
+ }
375
+ /**
376
+ * Reads the given digits, full-width digits, or Latin digits as Latin digits, dropping any other
377
+ * characters.
378
+ */
379
+ function toLatinDigits(digits, text) {
380
+ return Array.from(text).map((character) => latinDigit(digits, character) ?? "").join("");
381
+ }
382
+ /** Rewrites the given digits and full-width digits as Latin digits, keeping any other characters. */
383
+ function normalizeDigits(digits, text) {
384
+ return Array.from(text).map((character) => latinDigit(digits, character) ?? character).join("");
385
+ }
386
+ /** Whether every character is one of the given digits, a full-width digit, or a Latin digit. */
387
+ function hasOnlyDigits(digits, text) {
388
+ return Array.from(text).every((character) => latinDigit(digits, character) !== void 0);
389
+ }
390
+ //#endregion
391
+ //#region src/utils/locale-digits-element.ts
392
+ /**
393
+ * Writes and reads numbers in the element's numbering system, such as `١٤` for `ar-EG`. The
394
+ * locale's digits are resolved once per locale change, before the element renders, so formatting
395
+ * and parsing never look the locale up again. Locales that use Latin digits skip the conversion.
396
+ */
397
+ function LocaleDigitsElement(superClass) {
398
+ class LocaleDigits extends superClass {
399
+ #digits;
400
+ willUpdate(changedProperties) {
401
+ super.willUpdate(changedProperties);
402
+ if (!this.hasUpdated || changedProperties.has("locale")) this.#digits = resolveLocaleDigits(this.locale);
403
+ }
404
+ toLocaleDigits(value) {
405
+ return toLocaleDigits(this.#digits, value);
406
+ }
407
+ toLatinDigits(text) {
408
+ return toLatinDigits(this.#digits, text);
409
+ }
410
+ segmentNumber(text) {
411
+ const latin = toLatinDigits(this.#digits, text ?? "");
412
+ return latin === "" ? void 0 : parseInt(latin, 10);
413
+ }
414
+ /**
415
+ * Steps a numeric segment input by one on ArrowUp or ArrowDown, within its range, and
416
+ * dispatches an `input` event like a native number input does.
417
+ */
418
+ stepSegment(event, minimum, maximum) {
419
+ if (event.key !== "ArrowUp" && event.key !== "ArrowDown") return;
420
+ if (event.altKey || event.ctrlKey || event.metaKey) return;
421
+ event.preventDefault();
422
+ const input = event.target;
423
+ if (input.readOnly || input.disabled) return;
424
+ const step = event.key === "ArrowUp" ? 1 : -1;
425
+ const current = this.segmentNumber(input.value);
426
+ const next = current === void 0 ? step < 0 && maximum !== void 0 ? maximum : minimum : current + step;
427
+ const clamped = Math.max(minimum, maximum === void 0 ? next : Math.min(maximum, next));
428
+ input.value = this.toLocaleDigits(clamped);
429
+ input.dispatchEvent(new InputEvent("input", {
430
+ bubbles: true,
431
+ composed: true,
432
+ inputType: "insertReplacementText",
433
+ data: input.value
434
+ }));
435
+ }
436
+ /**
437
+ * Handles a numeric segment's `beforeinput` event. Typed, pasted, or dropped digits go through
438
+ * as usual. Other text is cancelled, unless `applyPastedValue` accepts it as a whole value, with
439
+ * its digits already rewritten as Latin digits, in which case an `input` event is dispatched
440
+ * from the element.
441
+ */
442
+ filterSegmentInput(event, applyPastedValue) {
443
+ if (event.isComposing || event.inputType === "insertCompositionText") return;
444
+ const text = event.data ?? event.dataTransfer?.getData("text/plain") ?? "";
445
+ if (text === "" || hasOnlyDigits(this.#digits, text)) return;
446
+ event.preventDefault();
447
+ if (applyPastedValue(normalizeDigits(this.#digits, text.trim()))) this.dispatchEvent(new InputEvent("input", {
448
+ bubbles: true,
449
+ composed: true,
450
+ inputType: "insertFromPaste",
451
+ data: text
452
+ }));
453
+ }
454
+ }
455
+ return LocaleDigits;
456
+ }
457
+ //#endregion
350
458
  //#region src/elements/use-date-input/use-date-input.ts
351
- let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement, use_date_input_strings_default) {
459
+ let UseDateInput = class UseDateInput extends LocaleDigitsElement(LocalizedElement(UseLocaleElement, use_date_input_strings_default)) {
352
460
  intlStrings() {
353
461
  return getDateTimeAriaLabels(this.locale, { plural: false });
354
462
  }
@@ -382,6 +490,7 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
382
490
  };
383
491
  else {
384
492
  const date = new Date(value.concat("T00:00:00"));
493
+ if (isNaN(date.getTime())) throw new RangeError(`Invalid date: ${value}`);
385
494
  this.#valueData = {
386
495
  year: date.getFullYear().toString(),
387
496
  month: (date.getMonth() + 1).toString(),
@@ -421,9 +530,9 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
421
530
  day: ""
422
531
  };
423
532
  #updateInputValues() {
424
- if (this.yearInput) this.yearInput.value = this.#valueData.year;
425
- if (this.monthInput) this.monthInput.value = this.#valueData.month;
426
- if (this.dayInput) this.dayInput.value = this.#valueData.day;
533
+ if (this.yearInput) this.yearInput.value = this.toLocaleDigits(this.#valueData.year);
534
+ if (this.monthInput) this.monthInput.value = this.toLocaleDigits(this.#valueData.month);
535
+ if (this.dayInput) this.dayInput.value = this.toLocaleDigits(this.#valueData.day);
427
536
  }
428
537
  #updateInternalValue() {
429
538
  const { year, month, day } = this.#valueData;
@@ -434,19 +543,42 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
434
543
  ].join("-");
435
544
  this.#internals.setFormValue(this.#internalValue);
436
545
  }
546
+ #segmentMinimum = {
547
+ year: 0,
548
+ month: 1,
549
+ day: 1
550
+ };
437
551
  #segmentMaximum = {
438
- year: null,
552
+ year: void 0,
439
553
  month: 12,
440
554
  day: 31
441
555
  };
442
556
  #handleSegmentInput(segment) {
443
557
  return (event) => {
558
+ if (event instanceof InputEvent && event.isComposing) return;
444
559
  const target = event.target;
445
560
  const maximum = this.#segmentMaximum[segment];
446
- const numericValue = parseInt(target.value, 10);
447
- if (maximum !== null && !isNaN(numericValue) && numericValue > maximum) target.value = String(maximum);
448
- this.#valueData[segment] = target.value;
561
+ let value = this.toLatinDigits(target.value);
562
+ const numericValue = parseInt(value, 10);
563
+ if (maximum !== void 0 && !isNaN(numericValue) && numericValue > maximum) value = String(maximum);
564
+ target.value = this.toLocaleDigits(value);
565
+ this.#valueData[segment] = value;
449
566
  this.#updateInternalValue();
567
+ this.requestUpdate();
568
+ };
569
+ }
570
+ #handleSegmentBeforeInput(event) {
571
+ this.filterSegmentInput(event, (text) => this.#applyPastedValue(text));
572
+ }
573
+ #applyPastedValue(text) {
574
+ const match = /^(\d{4}-(?:0[1-9]|1[0-2])-(?:0[1-9]|[12]\d|3[01]))(?:T.*)?$/.exec(text);
575
+ if (!match) return false;
576
+ this.value = match[1];
577
+ return true;
578
+ }
579
+ #handleSegmentKeydown(segment) {
580
+ return (event) => {
581
+ this.stepSegment(event, this.#segmentMinimum[segment], this.#segmentMaximum[segment]);
450
582
  };
451
583
  }
452
584
  #segmentId(unit) {
@@ -484,6 +616,7 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
484
616
  set value(value) {
485
617
  this.#initializeValue(value);
486
618
  this.#updateInputValues();
619
+ this.requestUpdate();
487
620
  }
488
621
  render() {
489
622
  return html`
@@ -492,23 +625,31 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
492
625
  "year",
493
626
  "month",
494
627
  "day"
495
- ].includes(part.type)) return html`
628
+ ].includes(part.type)) {
629
+ const segment = part.type;
630
+ return html`
496
631
  <input
497
- type="number"
498
- .value=${this.#valueData[part.type]}
632
+ type="text"
633
+ inputmode="numeric"
634
+ role="spinbutton"
635
+ .value=${this.toLocaleDigits(this.#valueData[segment])}
499
636
  ?disabled=${this.disabled}
500
637
  ?readonly=${this.readOnly}
501
- aria-label=${this.string(part.type)}
502
- min=${part.type === "year" ? "0" : "1"}
503
- max=${part.type === "month" ? "12" : part.type === "day" ? "31" : void 0}
504
- part="segment-input segment-input-${part.type}"
505
- id="${this.#segmentId(part.type)}"
638
+ aria-label=${this.string(segment)}
639
+ aria-valuemin=${this.#segmentMinimum[segment]}
640
+ aria-valuemax=${this.#segmentMaximum[segment] ?? nothing}
641
+ aria-valuenow=${this.segmentNumber(this.#valueData[segment]) ?? nothing}
642
+ part="segment-input segment-input-${segment}"
643
+ id="${this.#segmentId(segment)}"
506
644
  form=${this.#formId}
507
- size=${part.type === "year" ? 4 : 2}
508
- @input=${this.#handleSegmentInput(part.type)}
645
+ size=${segment === "year" ? 4 : 2}
646
+ @beforeinput=${this.#handleSegmentBeforeInput}
647
+ @input=${this.#handleSegmentInput(segment)}
648
+ @compositionend=${this.#handleSegmentInput(segment)}
649
+ @keydown=${this.#handleSegmentKeydown(segment)}
509
650
  />
510
651
  `;
511
- else if (part.type === "literal") return html`<span part="segment-literal segment-literal-date" aria-hidden="true"
652
+ } else if (part.type === "literal") return html`<span part="segment-literal segment-literal-date" aria-hidden="true"
512
653
  >${part.value}</span
513
654
  >`;
514
655
  return null;
@@ -523,7 +664,6 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
523
664
  gap: 0.5ch;
524
665
 
525
666
  input {
526
- -moz-appearance: textfield;
527
667
  font-variant-numeric: tabular-nums;
528
668
  field-sizing: content;
529
669
  text-align: end;
@@ -531,12 +671,6 @@ let UseDateInput = class UseDateInput extends LocalizedElement(UseLocaleElement,
531
671
  width: fit-content;
532
672
  }
533
673
  }
534
-
535
- input::-webkit-outer-spin-button,
536
- input::-webkit-inner-spin-button {
537
- -webkit-appearance: none;
538
- margin: 0;
539
- }
540
674
  `;
541
675
  }
542
676
  };
@@ -573,7 +707,7 @@ const ISO_DURATION_SEGMENTS = {
573
707
  second: "seconds",
574
708
  millisecond: "milliseconds"
575
709
  };
576
- let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocaleElement, use_duration_input_strings_default) {
710
+ let UseDurationInput = class UseDurationInput extends LocaleDigitsElement(LocalizedElement(UseLocaleElement, use_duration_input_strings_default)) {
577
711
  intlStrings() {
578
712
  return getDateTimeAriaLabels(this.locale, { plural: true });
579
713
  }
@@ -652,6 +786,7 @@ let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocale
652
786
  set value(value) {
653
787
  this.#initializeValue(value);
654
788
  this.#updateInputValues();
789
+ this.requestUpdate();
655
790
  }
656
791
  #valueData;
657
792
  #formatParts;
@@ -686,20 +821,37 @@ let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocale
686
821
  }
687
822
  #handleSegmentInput(segment) {
688
823
  return (event) => {
824
+ if (event instanceof InputEvent && event.isComposing) return;
689
825
  const target = event.target;
690
- this.#valueData[ISO_DURATION_SEGMENTS[segment]] = isNaN(target.valueAsNumber) ? 0 : target.valueAsNumber;
826
+ this.#valueData[ISO_DURATION_SEGMENTS[segment]] = this.segmentNumber(target.value) ?? 0;
691
827
  this.value = duration.toString(this.#valueData);
692
828
  };
693
829
  }
830
+ #handleSegmentBeforeInput(event) {
831
+ this.filterSegmentInput(event, (text) => this.#applyPastedValue(text));
832
+ }
833
+ #applyPastedValue(text) {
834
+ if (!text.startsWith("P")) return false;
835
+ try {
836
+ duration.parse(text);
837
+ } catch {
838
+ return false;
839
+ }
840
+ this.value = text;
841
+ return true;
842
+ }
843
+ #handleSegmentKeydown(event) {
844
+ this.stepSegment(event, 0);
845
+ }
694
846
  #updateInputValues() {
695
- if (this.yearInput) this.yearInput.value = String(this.#valueData.years ?? 0);
696
- if (this.monthInput) this.monthInput.value = String(this.#valueData.months ?? 0);
697
- if (this.weekInput) this.weekInput.value = String(this.#valueData.weeks ?? 0);
698
- if (this.dayInput) this.dayInput.value = String(this.#valueData.days ?? 0);
699
- if (this.hourInput) this.hourInput.value = String(this.#valueData.hours ?? 0);
700
- if (this.minuteInput) this.minuteInput.value = String(this.#valueData.minutes ?? 0);
701
- if (this.secondInput) this.secondInput.value = String(this.#valueData.seconds ?? 0);
702
- if (this.millisecondInput) this.millisecondInput.value = String(this.#valueData.milliseconds ?? 0);
847
+ if (this.yearInput) this.yearInput.value = this.toLocaleDigits(this.#valueData.years ?? 0);
848
+ if (this.monthInput) this.monthInput.value = this.toLocaleDigits(this.#valueData.months ?? 0);
849
+ if (this.weekInput) this.weekInput.value = this.toLocaleDigits(this.#valueData.weeks ?? 0);
850
+ if (this.dayInput) this.dayInput.value = this.toLocaleDigits(this.#valueData.days ?? 0);
851
+ if (this.hourInput) this.hourInput.value = this.toLocaleDigits(this.#valueData.hours ?? 0);
852
+ if (this.minuteInput) this.minuteInput.value = this.toLocaleDigits(this.#valueData.minutes ?? 0);
853
+ if (this.secondInput) this.secondInput.value = this.toLocaleDigits(this.#valueData.seconds ?? 0);
854
+ if (this.millisecondInput) this.millisecondInput.value = this.toLocaleDigits(this.#valueData.milliseconds ?? 0);
703
855
  }
704
856
  #unitLabel(unit) {
705
857
  return unit in use_duration_input_strings_default ? this.string(unit) : void 0;
@@ -711,16 +863,22 @@ let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocale
711
863
  ${parts.map((part) => {
712
864
  if (["integer", "fraction"].includes(part.type)) return html`
713
865
  <input
714
- type="number"
715
- .value=${this.#valueData[ISO_DURATION_SEGMENTS[unit]]}
866
+ type="text"
867
+ inputmode="numeric"
868
+ role="spinbutton"
869
+ .value=${this.toLocaleDigits(this.#valueData[ISO_DURATION_SEGMENTS[unit]])}
716
870
  ?disabled=${this.disabled}
717
871
  ?readonly=${this.readOnly}
718
872
  aria-label=${this.format === "digital" && part.type === "fraction" ? this.string("millisecond") : this.#unitLabel(unit)}
719
- min="0"
873
+ aria-valuemin="0"
874
+ aria-valuenow=${this.#valueData[ISO_DURATION_SEGMENTS[unit]] ?? nothing}
720
875
  part="segment-input segment-input-${unit}"
721
876
  id="${this.#segmentId(unit)}"
722
877
  form=${this.#formId}
878
+ @beforeinput=${this.#handleSegmentBeforeInput}
723
879
  @input=${this.#handleSegmentInput(unit)}
880
+ @compositionend=${this.#handleSegmentInput(unit)}
881
+ @keydown=${this.#handleSegmentKeydown}
724
882
  />
725
883
  `;
726
884
  if (["unit", "decimal"].includes(part.type)) return html`
@@ -740,7 +898,6 @@ let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocale
740
898
  this.styles = css`
741
899
  :host {
742
900
  input {
743
- -moz-appearance: textfield;
744
901
  font-variant-numeric: tabular-nums;
745
902
  field-sizing: content;
746
903
  text-align: end;
@@ -753,12 +910,6 @@ let UseDurationInput = class UseDurationInput extends LocalizedElement(UseLocale
753
910
  align-items: center;
754
911
  gap: 0.5ch;
755
912
  }
756
-
757
- input::-webkit-outer-spin-button,
758
- input::-webkit-inner-spin-button {
759
- -webkit-appearance: none;
760
- margin: 0;
761
- }
762
913
  `;
763
914
  }
764
915
  };
@@ -1026,7 +1177,7 @@ var use_time_input_strings_default = {
1026
1177
  };
1027
1178
  //#endregion
1028
1179
  //#region src/elements/use-time-input/use-time-input.ts
1029
- let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement, use_time_input_strings_default) {
1180
+ let UseTimeInput = class UseTimeInput extends LocaleDigitsElement(LocalizedElement(UseLocaleElement, use_time_input_strings_default)) {
1030
1181
  intlStrings() {
1031
1182
  return getDateTimeAriaLabels(this.locale, { plural: true });
1032
1183
  }
@@ -1147,16 +1298,18 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1147
1298
  set value(value) {
1148
1299
  this.#initializeValue(value);
1149
1300
  this.#updateInputValues();
1301
+ this.requestUpdate();
1150
1302
  }
1151
1303
  #valueData;
1304
+ #segmentText(segment) {
1305
+ const length = segment === "fractionalSecond" ? 3 : segment === "hour" && this.hourFormat === "12" ? 1 : 2;
1306
+ return this.toLocaleDigits(this.#valueData[segment]?.padStart(length, "0"));
1307
+ }
1152
1308
  #updateInputValues() {
1153
- if (this.hourInput) {
1154
- if (this.hourFormat === "12") this.hourInput.value = this.#valueData.hour ? this.#valueData.hour : "";
1155
- else this.hourInput.value = this.#valueData.hour ? this.#valueData.hour.padStart(2, "0") : "";
1156
- }
1157
- if (this.minuteInput) this.minuteInput.value = this.#valueData.minute ? this.#valueData.minute.padStart(2, "0") : "";
1158
- if (this.secondInput) this.secondInput.value = this.#valueData.second ? this.#valueData.second.padStart(2, "0") : "";
1159
- if (this.fractionalSecondInput) this.fractionalSecondInput.value = this.#valueData.fractionalSecond ? this.#valueData.fractionalSecond.padStart(3, "0") : "";
1309
+ if (this.hourInput) this.hourInput.value = this.#segmentText("hour");
1310
+ if (this.minuteInput) this.minuteInput.value = this.#segmentText("minute");
1311
+ if (this.secondInput) this.secondInput.value = this.#segmentText("second");
1312
+ if (this.fractionalSecondInput) this.fractionalSecondInput.value = this.#segmentText("fractionalSecond");
1160
1313
  if (this.dayPeriodInput) this.dayPeriodInput.value = this.#valueData.dayPeriod ? this.string(this.#valueData.dayPeriod) : "";
1161
1314
  }
1162
1315
  #formatParts;
@@ -1214,28 +1367,31 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1214
1367
  }
1215
1368
  #handleSegmentInput(segment) {
1216
1369
  return (event) => {
1370
+ if (event instanceof InputEvent && event.isComposing) return;
1217
1371
  const target = event.target;
1218
- if (target.value === "") {
1372
+ if (segment === "dayPeriod" ? target.value === "" : this.toLatinDigits(target.value) === "") {
1373
+ target.value = "";
1219
1374
  this.#valueData[segment] = void 0;
1220
1375
  this.#updateInternalValue();
1376
+ this.requestUpdate();
1221
1377
  return;
1222
1378
  }
1223
- const value = isNaN(target.valueAsNumber) ? 0 : target.valueAsNumber;
1379
+ const value = this.segmentNumber(target.value) ?? 0;
1224
1380
  if (segment === "hour") {
1225
1381
  const use12Hour = this.hourFormat === "12" && this.dayPeriod;
1226
1382
  const min = use12Hour ? 1 : 0;
1227
1383
  const max = use12Hour ? 12 : 23;
1228
1384
  this.#valueData.hour = Math.max(min, Math.min(max, value)).toString();
1229
- target.value = this.hourFormat === "12" ? this.#valueData.hour : this.#valueData.hour.padStart(2, "0");
1385
+ target.value = this.toLocaleDigits(this.hourFormat === "12" ? this.#valueData.hour : this.#valueData.hour.padStart(2, "0"));
1230
1386
  } else if (segment === "minute") {
1231
1387
  this.#valueData.minute = Math.max(0, Math.min(59, value)).toString();
1232
- target.value = this.#valueData.minute.padStart(2, "0");
1388
+ target.value = this.toLocaleDigits(this.#valueData.minute.padStart(2, "0"));
1233
1389
  } else if (segment === "second") {
1234
1390
  this.#valueData.second = Math.max(0, Math.min(59, value)).toString();
1235
- target.value = this.#valueData.second.padStart(2, "0");
1391
+ target.value = this.toLocaleDigits(this.#valueData.second.padStart(2, "0"));
1236
1392
  } else if (segment === "fractionalSecond") {
1237
1393
  this.#valueData.fractionalSecond = Math.max(0, Math.min(999, value)).toString();
1238
- target.value = this.#valueData.fractionalSecond.padStart(3, "0");
1394
+ target.value = this.toLocaleDigits(this.#valueData.fractionalSecond.padStart(3, "0"));
1239
1395
  } else if (segment === "dayPeriod") {
1240
1396
  event.preventDefault();
1241
1397
  const validValue = this.#computeValidAmPmValue(event);
@@ -1245,6 +1401,21 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1245
1401
  }
1246
1402
  }
1247
1403
  this.#updateInternalValue();
1404
+ this.requestUpdate();
1405
+ };
1406
+ }
1407
+ #handleNumericSegmentBeforeInput(event) {
1408
+ this.filterSegmentInput(event, (text) => this.#applyPastedValue(text));
1409
+ }
1410
+ #applyPastedValue(text) {
1411
+ const match = /^(?:\d{4}-\d{2}-\d{2}T)?((?:[01]\d|2[0-3]):[0-5]\d(?::[0-5]\d(?:\.\d{1,3})?)?)(?:Z|[+-]\d{2}:?\d{2})?$/.exec(text);
1412
+ if (!match) return false;
1413
+ this.value = match[1];
1414
+ return true;
1415
+ }
1416
+ #handleNumericSegmentKeydown(segment) {
1417
+ return (event) => {
1418
+ this.stepSegment(event, this.#getMinValue(segment), this.#getMaxValue(segment));
1248
1419
  };
1249
1420
  }
1250
1421
  #toggleDayPariod(target, value) {
@@ -1305,6 +1476,9 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1305
1476
  this.#internalValue = output.join(":").concat(fractionalSecond && this.fractionalSeconds ? "." + fractionalSecond.padStart(3, "0") : "");
1306
1477
  this.#internals.setFormValue(this.#internalValue);
1307
1478
  }
1479
+ #getMinValue(type) {
1480
+ return type === "hour" && this.hourFormat === "12" && this.dayPeriod ? 1 : 0;
1481
+ }
1308
1482
  #getMaxValue(type) {
1309
1483
  if (type === "hour") return this.#maxHours;
1310
1484
  if (type === "minute" || type === "second") return 59;
@@ -1314,20 +1488,36 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1314
1488
  return this.#formatParts.map((part) => {
1315
1489
  return part.type === "literal" ? html`<span part="segment-literal segment-literal-${part.type}" aria-hidden="true"
1316
1490
  >${part.value}</span
1317
- >` : html`<input
1318
- type=${part.type === "dayPeriod" ? "text" : "number"}
1319
- value=${part.type === "dayPeriod" ? this.#valueData.dayPeriod ? this.string(this.#valueData.dayPeriod) : "" : this.#valueData[part.type]}
1320
- ?disabled=${this.disabled}
1321
- ?readonly=${this.readOnly}
1322
- aria-label=${this.string(part.type === "fractionalSecond" ? "millisecond" : part.type)}
1323
- min="0"
1324
- max=${this.#getMaxValue(part.type)}
1325
- part="segment-input segment-input-${part.type}"
1326
- id="${this.#segmentId(part.type)}"
1327
- form=${this.#formId}
1328
- @input=${this.#handleSegmentInput(part.type)}
1329
- @keydown=${part.type === "dayPeriod" ? this.#handleSegmentKeydown : void 0}
1330
- />`;
1491
+ >` : part.type === "dayPeriod" ? html`<input
1492
+ type="text"
1493
+ value=${this.#valueData.dayPeriod ? this.string(this.#valueData.dayPeriod) : ""}
1494
+ ?disabled=${this.disabled}
1495
+ ?readonly=${this.readOnly}
1496
+ aria-label=${this.string(part.type)}
1497
+ part="segment-input segment-input-${part.type}"
1498
+ id="${this.#segmentId(part.type)}"
1499
+ form=${this.#formId}
1500
+ @input=${this.#handleSegmentInput(part.type)}
1501
+ @keydown=${this.#handleSegmentKeydown}
1502
+ />` : html`<input
1503
+ type="text"
1504
+ inputmode="numeric"
1505
+ role="spinbutton"
1506
+ .value=${this.#segmentText(part.type)}
1507
+ ?disabled=${this.disabled}
1508
+ ?readonly=${this.readOnly}
1509
+ aria-label=${this.string(part.type === "fractionalSecond" ? "millisecond" : part.type)}
1510
+ aria-valuemin=${this.#getMinValue(part.type)}
1511
+ aria-valuemax=${this.#getMaxValue(part.type) ?? nothing}
1512
+ aria-valuenow=${this.segmentNumber(this.#valueData[part.type]) ?? nothing}
1513
+ part="segment-input segment-input-${part.type}"
1514
+ id="${this.#segmentId(part.type)}"
1515
+ form=${this.#formId}
1516
+ @beforeinput=${this.#handleNumericSegmentBeforeInput}
1517
+ @input=${this.#handleSegmentInput(part.type)}
1518
+ @compositionend=${this.#handleSegmentInput(part.type)}
1519
+ @keydown=${this.#handleNumericSegmentKeydown(part.type)}
1520
+ />`;
1331
1521
  });
1332
1522
  }
1333
1523
  static {
@@ -1338,7 +1528,6 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1338
1528
  gap: 0.5ch;
1339
1529
 
1340
1530
  input {
1341
- -moz-appearance: textfield;
1342
1531
  font-variant-numeric: tabular-nums;
1343
1532
  field-sizing: content;
1344
1533
  text-align: end;
@@ -1349,12 +1538,6 @@ let UseTimeInput = class UseTimeInput extends LocalizedElement(UseLocaleElement,
1349
1538
  min-width: 3ch;
1350
1539
  }
1351
1540
  }
1352
-
1353
- input::-webkit-outer-spin-button,
1354
- input::-webkit-inner-spin-button {
1355
- -webkit-appearance: none;
1356
- margin: 0;
1357
- }
1358
1541
  `;
1359
1542
  }
1360
1543
  };
@@ -2823,7 +3006,7 @@ const INITIAL_TABINDEX_ATTR = "data-usewc-calendar-tabindex";
2823
3006
  * @csspart picker-month - An individual month button
2824
3007
  * @csspart picker-month-current - Applied to the currently selected month button
2825
3008
  */
2826
- var UseCalendarBase = class extends InlineDirectionElement(LocalizedElement(UseLocaleElement, use_calendar_strings_default)) {
3009
+ var UseCalendarBase = class extends InlineDirectionElement(LocaleDigitsElement(LocalizedElement(UseLocaleElement, use_calendar_strings_default))) {
2827
3010
  static {
2828
3011
  this.localeRoot = "use-calendar";
2829
3012
  }
@@ -3132,7 +3315,8 @@ var UseCalendarBase = class extends InlineDirectionElement(LocalizedElement(UseL
3132
3315
  });
3133
3316
  }
3134
3317
  /**
3135
- * See {@link UseCalendarRenderDay}. Assigning a new function on an
3318
+ * See {@link UseCalendarRenderDay}. The default renders `localizedDay`, the
3319
+ * day in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an
3136
3320
  * instance (e.g. via `.renderDay=${fn}` in a Lit template) after first
3137
3321
  * render does not schedule a re-render on its own — call `requestUpdate()`
3138
3322
  * if needed.
@@ -3142,7 +3326,7 @@ var UseCalendarBase = class extends InlineDirectionElement(LocalizedElement(UseL
3142
3326
  * default (or a parent override's) rendering.
3143
3327
  */
3144
3328
  renderDay(data, _html) {
3145
- return String(data.day);
3329
+ return data.localizedDay;
3146
3330
  }
3147
3331
  get #todayDate() {
3148
3332
  return this.getDateForDay((/* @__PURE__ */ new Date()).getDate());
@@ -3150,6 +3334,7 @@ var UseCalendarBase = class extends InlineDirectionElement(LocalizedElement(UseL
3150
3334
  #renderDayCell(year, month, day, rowIndex, columnIndex, empty = false) {
3151
3335
  const date = this.#formatDate(year, month, day);
3152
3336
  const disabled = this.#dateDisabled(date);
3337
+ const localizedDay = this.toLocaleDigits(day);
3153
3338
  return html`
3154
3339
  <div
3155
3340
  @focusin=${this.#handleCellFocusIn}
@@ -3173,6 +3358,7 @@ var UseCalendarBase = class extends InlineDirectionElement(LocalizedElement(UseL
3173
3358
  >
3174
3359
  <slot name="date-${date}">${this.renderDay({
3175
3360
  day,
3361
+ localizedDay,
3176
3362
  date
3177
3363
  }, html)}</slot>
3178
3364
  </div>
@@ -4137,9 +4323,27 @@ let UseDatetimeInput = class UseDatetimeInput extends UseLocaleElement {
4137
4323
  this.#internalValue = "";
4138
4324
  this.#dateValue = "";
4139
4325
  this.#timeValue = "";
4326
+ this.#handleBeforeInput = (event) => {
4327
+ if (event.isComposing) return;
4328
+ const text = event.data ?? event.dataTransfer?.getData("text/plain") ?? "";
4329
+ const match = /^(\d{4}-\d{2}-\d{2})T(\d{2}:\d{2}(?::\d{2}(?:\.\d{1,3})?)?)/.exec(normalizeDigits(resolveLocaleDigits(this.locale), text.trim()));
4330
+ if (!match || !this.dateElement || !this.timeElement) return;
4331
+ event.preventDefault();
4332
+ event.stopPropagation();
4333
+ this.dateElement.value = match[1];
4334
+ this.timeElement.value = match[2];
4335
+ this.#updateInternalValue();
4336
+ this.dispatchEvent(new InputEvent("input", {
4337
+ bubbles: true,
4338
+ composed: true,
4339
+ inputType: "insertFromPaste",
4340
+ data: text
4341
+ }));
4342
+ };
4140
4343
  this.#internals = this.attachInternals();
4141
4344
  this.#id = this.hasAttribute("id") ? this.getAttribute("id") : createId();
4142
4345
  if (this.hasAttribute("disabled")) this.#internals.states.add("disabled");
4346
+ this.addEventListener("beforeinput", this.#handleBeforeInput, { capture: true });
4143
4347
  }
4144
4348
  get id() {
4145
4349
  return this.#id;
@@ -4174,7 +4378,7 @@ let UseDatetimeInput = class UseDatetimeInput extends UseLocaleElement {
4174
4378
  this.#initializeValue(val);
4175
4379
  }
4176
4380
  #initializeValue(val) {
4177
- const [datePart, timePart] = this.value.replace("Z", "").split("T");
4381
+ const [datePart, timePart] = val.replace("Z", "").split("T");
4178
4382
  this.#dateValue = datePart ?? "";
4179
4383
  this.#timeValue = timePart ?? "";
4180
4384
  this.#internalValue = val;
@@ -4191,6 +4395,7 @@ let UseDatetimeInput = class UseDatetimeInput extends UseLocaleElement {
4191
4395
  else this.#internalValue = `${date}T${time}`;
4192
4396
  this.#internals.setFormValue(this.#internalValue);
4193
4397
  }
4398
+ #handleBeforeInput;
4194
4399
  #handleInput(event) {
4195
4400
  const target = event.target;
4196
4401
  if (target === this.dateElement || target === this.timeElement) this.#updateInternalValue();
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "use-wc",
3
- "version": "1.0.0-pre.89",
3
+ "version": "1.0.0-pre.90",
4
4
  "private": false,
5
5
  "homepage": "https://usewc.com",
6
6
  "bugs": "https://github.com/dang-rabbits/use-wc/issues",
@@ -19,6 +19,18 @@
19
19
  ],
20
20
  "references": []
21
21
  },
22
+ {
23
+ "name": "use-avatar",
24
+ "description": "`use-avatar` renders a small, fixed-size, clipped container for a person or entity's\nlikeness. Slot in an `<img>` (or `<picture>`) for a photo, an inline `<svg>` for a\ngenerated or stylized mark, or arbitrary markup (a `<div>` of initials, a calendar badge);\nwhatever is slotted is centered and clipped to the avatar's shape. With nothing slotted,\n`use-avatar` falls back to initials derived from the `name` attribute.\n\n```html\n<use-avatar><img src=\"ada.jpg\" alt=\"\" /></use-avatar>\n<use-avatar name=\"Riley Quinn\"></use-avatar>\n<use-avatar><svg viewBox=\"0 0 32 32\">…</svg></use-avatar>\n```\n\nSize it with plain CSS — `inline-size` for the size and `aspect-ratio` for the proportions —\nfrom a stylesheet, a style attribute, or the design system's theme layer. The element takes no\ncustom properties of its own: it is a query container, so the initials scale from whatever box\nit ends up with. A non-square `aspect-ratio` suits a wide thumbnail or poster.\n\nThe element clips its content but gives it no shape of its own — that is the theme's, through\nthe affordance classes it defines: `.circle` (the themed default), `.square`, `.squircle` in\n`theme/avatar.css`. A circle only reads right at a `1` ratio, so a wide avatar wants one of the\nothers.\n\nThe element takes no ARIA role of its own. A slotted `<img>` carries its own `alt`, and the\ninitials fallback is plain text in the accessibility tree — an avatar is decorative beside a\nname far more often than it is the name itself, so labelling it here would mean announcing\nthat name twice.\n---\n\n\n### **Slots:**\n - _default_ - The avatar's content: an `<img>`/`<picture>`, an `<svg>`, or arbitrary markup. Left empty, the `name` attribute's initials are shown instead, as the slot's own fallback. A photo fills the frame and crops; anything else keeps its own size, capped at the frame. Give an `<svg>` a `width`/`height` — without one it defaults to filling the box, and a rounded shape will clip its corners.\n\n### **CSS Parts:**\n - **initials** - The span that renders the `name` fallback.",
25
+ "attributes": [
26
+ {
27
+ "name": "name",
28
+ "description": "The full name, used for the initials fallback (first + last whitespace-separated token, uppercased) when the slot is empty.",
29
+ "values": []
30
+ }
31
+ ],
32
+ "references": []
33
+ },
22
34
  {
23
35
  "name": "use-badge",
24
36
  "description": "`use-badge` renders a small indicator anchored to another element's corner, using\n[CSS anchor positioning](https://developer.mozilla.org/en-US/docs/Web/CSS/CSS_anchor_positioning)\ninstead of the classic `position: absolute` + negative-offset trick. This lets the badge live\nanywhere in the DOM rather than being a child of the element it decorates. A badge's shape,\ncolor, and positioning ship built in — reading from the design system's tokens when\n`tokens.css` is loaded, and falling back to sensible defaults otherwise — so no theme is\nrequired to get a working, visible badge.\n\nPoint a badge at its target with the `anchortarget` attribute, set to the target's `id`\n(named to avoid colliding with the `anchor` content attribute HTML is gaining natively):\n\n```html\n<button type=\"button\" id=\"inbox\">Inbox</button>\n<use-badge anchortarget=\"inbox\">99+</use-badge>\n```\n\nThe badge and its anchor must live in the same DOM tree — a badge cannot reach an anchor\ninside another component's shadow root, and an anchor's `anchor-name` set inside a shadow\ntree's stylesheet is not visible here. No positioned ancestor or shared containing block is\nrequired: anchor positioning resolves the badge's location straight from the anchor's own\ngeometry, so the badge can sit anywhere else in the DOM tree.\n\nWithout a resolved anchor — `anchortarget` unset, pointing at a missing id, or `anchorElement`\nnever assigned — the badge has no `:state(anchored)` and renders as a plain inline chip in\nnormal document flow, still using the same color/shape styling.\n\nSome components render their host as `display: contents`, which has no box of its own for CSS\nanchor positioning to resolve against. If the anchor computes a non-empty `--usewc-anchor-name`\ncustom property, the badge uses that dashed-ident as its `position-anchor` directly instead of\ngenerating and writing its own onto the anchor — letting a component publish the name of a\nreal, box-generating part of itself (already established for its own purposes) for badges to\ntarget instead of its host, even though that part lives inside the component's own shadow root.\nThis mechanism is shared with `use-menu`/`use-anchored` via `AnchorController`; components\nwithout a `display: contents` host don't need to publish anything.\n\n**Known Firefox limitation:** anchoring across a shadow root boundary this way — the case\n`--usewc-anchor-name` exists for — is mispositioned in Firefox as of this writing. Verified\ndirectly: `anchor()`'s logical `start`/`end` keywords resolve backwards on both axes\nspecifically for a cross-shadow-root anchor reference, in Firefox only — the same anchor()\ncalls resolve correctly for a same-tree anchor in Firefox, and cross-shadow anchoring resolves\ncorrectly in Chromium and Safari. A badge anchored to a plain element (button, input, anything\nthat isn't itself relying on this escape hatch) is unaffected in every engine, including\nFirefox.\n\n## Side effects\n\nThe badge is a sibling of its anchor rather than a child, so **as a side effect, a badge\nmutates its anchor element**: it appends its own id to the anchor's `aria-describedby`\n(preserving any ids already there, the same way `anchor-name` is appended rather than\nreplaced) to keep its content connected to the anchor for assistive technology. This means a\nbadge's content, or its `aria-label` when set, is announced as part of interacting with the\nanchor, e.g. \"Inbox button, 3\". A `dot` badge has no content of its own, so set `aria-label`\ndirectly on `<use-badge>` for a meaningful description (e.g. `aria-label=\"3 unread messages\"`);\nwithout one, the description contributes nothing and the anchor announces as before.\n\nAvoid adding `aria-hidden` to `<use-badge>` if you rely on this: while the description\ncomputation is specified to still read a hidden node when it's the direct target of\n`aria-describedby`, support for that specific case is inconsistent across browsers and screen\nreaders.\n\nSet `options=\"noaria\"` to opt a badge out of this and leave the anchor's `aria-describedby`\nuntouched, e.g. when the anchor already documents the badge's meaning some other way.\n---\n\n\n### **Slots:**\n - _default_ - Badge content, e.g. a count or short label. Ignored when `dot` is set.",
@@ -51,18 +63,6 @@
51
63
  ],
52
64
  "references": []
53
65
  },
54
- {
55
- "name": "use-avatar",
56
- "description": "`use-avatar` renders a small, fixed-size, clipped container for a person or entity's\nlikeness. Slot in an `<img>` (or `<picture>`) for a photo, an inline `<svg>` for a\ngenerated or stylized mark, or arbitrary markup (a `<div>` of initials, a calendar badge);\nwhatever is slotted is centered and clipped to the avatar's shape. With nothing slotted,\n`use-avatar` falls back to initials derived from the `name` attribute.\n\n```html\n<use-avatar><img src=\"ada.jpg\" alt=\"\" /></use-avatar>\n<use-avatar name=\"Riley Quinn\"></use-avatar>\n<use-avatar><svg viewBox=\"0 0 32 32\">…</svg></use-avatar>\n```\n\nSize it with plain CSS — `inline-size` for the size and `aspect-ratio` for the proportions —\nfrom a stylesheet, a style attribute, or the design system's theme layer. The element takes no\ncustom properties of its own: it is a query container, so the initials scale from whatever box\nit ends up with. A non-square `aspect-ratio` suits a wide thumbnail or poster.\n\nThe element clips its content but gives it no shape of its own — that is the theme's, through\nthe affordance classes it defines: `.circle` (the themed default), `.square`, `.squircle` in\n`theme/avatar.css`. A circle only reads right at a `1` ratio, so a wide avatar wants one of the\nothers.\n\nThe element takes no ARIA role of its own. A slotted `<img>` carries its own `alt`, and the\ninitials fallback is plain text in the accessibility tree — an avatar is decorative beside a\nname far more often than it is the name itself, so labelling it here would mean announcing\nthat name twice.\n---\n\n\n### **Slots:**\n - _default_ - The avatar's content: an `<img>`/`<picture>`, an `<svg>`, or arbitrary markup. Left empty, the `name` attribute's initials are shown instead, as the slot's own fallback. A photo fills the frame and crops; anything else keeps its own size, capped at the frame. Give an `<svg>` a `width`/`height` — without one it defaults to filling the box, and a rounded shape will clip its corners.\n\n### **CSS Parts:**\n - **initials** - The span that renders the `name` fallback.",
57
- "attributes": [
58
- {
59
- "name": "name",
60
- "description": "The full name, used for the initials fallback (first + last whitespace-separated token, uppercased) when the slot is empty.",
61
- "values": []
62
- }
63
- ],
64
- "references": []
65
- },
66
66
  {
67
67
  "name": "use-calendarday",
68
68
  "description": "A helper component for customizing specific days within `use-calendar`.\nContent placed inside this element will replace the default day number display.\n---\n\n\n### **Slots:**\n - **default** - slot for custom day content (replaces the day number)",
@@ -94,7 +94,7 @@
94
94
  },
95
95
  {
96
96
  "name": "use-date-picker",
97
- "description": "A day-granularity date picker. Value format: YYYY-MM-DD.\n\nCustomize day rendering on an instance:\n```ts\ndatePicker.renderDay = ({ day, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${day}</strong>` : String(day);\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyDatePicker extends UseDatePicker {\n renderDay(data: { day: number; date: string }, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.day}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```\n---\n\n\n### **Events:**\n - **use-change** - Fired on selection with detail `{ value: string }`\n\n### **Methods:**\n - **renderDay(data: _{ day: number; date: string }_, _html: _LitHtml_): _TemplateResult | string_** - See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.\n\n### **Slots:**\n - **header-start** - Content before the title in the header\n- **title** - Title showing the month and year\n- **controls** - Navigation controls (previous, today, next buttons)\n- **header-end** - Content after the controls in the header\n- **date-{YYYY-MM-DD}** - Per-date content that replaces the default day number\n\n### **CSS Parts:**\n - **header** - The header container\n- **title** - The title text\n- **title-month** - The month part of the title\n- **title-literal** - Literal text in the title\n- **title-year** - The year part of the title\n- **controls** - The controls slot container\n- **control** - Individual control button\n- **control-previous** - Previous month button\n- **control-today** - Today button\n- **control-next** - Next month button\n- **control-icon** - Arrow glyph inside the previous and next buttons, mirrored in right-to-left layouts\n- **grid** - The main calendar grid\n- **grid-header** - The header row with day names\n- **grid-header-cell** - Individual day name header cell\n- **grid-body** - The container for calendar weeks\n- **row** - A row of days\n- **day** - A day cell\n- **day-empty** - An empty day cell (from previous/next month)\n- **day-selected** - A selected day cell\n- **day-today** - The current date cell\n- **day-disabled** - A disabled date cell\n- **title-button** - The clickable button wrapping the month/year title\n- **picker** - The scrollable month/year picker panel (replaces grid when open)\n- **picker-year-section** - Container for one year's label and month grid\n- **picker-year-label** - The year heading inside a year section\n- **picker-months** - The 4-column month grid inside a year section\n- **picker-month** - An individual month button\n- **picker-month-current** - Applied to the currently selected month button",
97
+ "description": "A day-granularity date picker. Value format: YYYY-MM-DD.\n\nCustomize day rendering on an instance:\n```ts\ndatePicker.renderDay = ({ localizedDay, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${localizedDay}</strong>` : localizedDay;\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyDatePicker extends UseDatePicker {\n renderDay(data: UseCalendarDayData, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.localizedDay}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```\n---\n\n\n### **Events:**\n - **use-change** - Fired on selection with detail `{ value: string }`\n\n### **Methods:**\n - **renderDay(data: _UseCalendarDayData_, _html: _LitHtml_): _TemplateResult | string_** - See UseCalendarRenderDay. The default renders `localizedDay`, the\nday in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.\n\n### **Slots:**\n - **header-start** - Content before the title in the header\n- **title** - Title showing the month and year\n- **controls** - Navigation controls (previous, today, next buttons)\n- **header-end** - Content after the controls in the header\n- **date-{YYYY-MM-DD}** - Per-date content that replaces the default day number\n\n### **CSS Parts:**\n - **header** - The header container\n- **title** - The title text\n- **title-month** - The month part of the title\n- **title-literal** - Literal text in the title\n- **title-year** - The year part of the title\n- **controls** - The controls slot container\n- **control** - Individual control button\n- **control-previous** - Previous month button\n- **control-today** - Today button\n- **control-next** - Next month button\n- **control-icon** - Arrow glyph inside the previous and next buttons, mirrored in right-to-left layouts\n- **grid** - The main calendar grid\n- **grid-header** - The header row with day names\n- **grid-header-cell** - Individual day name header cell\n- **grid-body** - The container for calendar weeks\n- **row** - A row of days\n- **day** - A day cell\n- **day-empty** - An empty day cell (from previous/next month)\n- **day-selected** - A selected day cell\n- **day-today** - The current date cell\n- **day-disabled** - A disabled date cell\n- **title-button** - The clickable button wrapping the month/year title\n- **picker** - The scrollable month/year picker panel (replaces grid when open)\n- **picker-year-section** - Container for one year's label and month grid\n- **picker-year-label** - The year heading inside a year section\n- **picker-months** - The 4-column month grid inside a year section\n- **picker-month** - An individual month button\n- **picker-month-current** - Applied to the currently selected month button",
98
98
  "attributes": [
99
99
  { "name": "year", "description": "1970-9999", "values": [] },
100
100
  { "name": "month", "description": "1-12", "values": [] },
@@ -541,7 +541,7 @@
541
541
  },
542
542
  {
543
543
  "name": "use-week-picker",
544
- "description": "A week-granularity date picker. Value format: YYYY-Www (ISO 8601 week).\n\nCustomize day rendering on an instance:\n```ts\nweekPicker.renderDay = ({ day, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${day}</strong>` : String(day);\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyWeekPicker extends UseWeekPicker {\n renderDay(data: { day: number; date: string }, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.day}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```\n---\n\n\n### **Events:**\n - **use-change** - Fired on selection with detail `{ value: string; dates: string[] }`\n\n### **Methods:**\n - **renderDay(data: _{ day: number; date: string }_, _html: _LitHtml_): _TemplateResult | string_** - See UseCalendarRenderDay. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.\n\n### **Slots:**\n - **header-start** - Content before the title in the header\n- **title** - Title showing the month and year\n- **controls** - Navigation controls (previous, today, next buttons)\n- **header-end** - Content after the controls in the header\n- **date-{YYYY-MM-DD}** - Per-date content that replaces the default day number\n\n### **CSS Parts:**\n - **header** - The header container\n- **title** - The title text\n- **title-month** - The month part of the title\n- **title-literal** - Literal text in the title\n- **title-year** - The year part of the title\n- **controls** - The controls slot container\n- **control** - Individual control button\n- **control-previous** - Previous month button\n- **control-today** - Today button\n- **control-next** - Next month button\n- **control-icon** - Arrow glyph inside the previous and next buttons, mirrored in right-to-left layouts\n- **grid** - The main calendar grid\n- **grid-header** - The header row with day names\n- **grid-header-cell** - Individual day name header cell\n- **grid-body** - The container for calendar weeks\n- **row** - A row of days\n- **day** - A day cell\n- **day-empty** - An empty day cell (from previous/next month)\n- **day-selected** - A selected day cell\n- **day-today** - The current date cell\n- **day-disabled** - A disabled date cell\n- **title-button** - The clickable button wrapping the month/year title\n- **picker** - The scrollable month/year picker panel (replaces grid when open)\n- **picker-year-section** - Container for one year's label and month grid\n- **picker-year-label** - The year heading inside a year section\n- **picker-months** - The 4-column month grid inside a year section\n- **picker-month** - An individual month button\n- **picker-month-current** - Applied to the currently selected month button",
544
+ "description": "A week-granularity date picker. Value format: YYYY-Www (ISO 8601 week).\n\nCustomize day rendering on an instance:\n```ts\nweekPicker.renderDay = ({ localizedDay, date }, html) =>\n date === \"2026-03-17\" ? html`<strong>${localizedDay}</strong>` : localizedDay;\n```\n\nOr override on a subclass to apply it to every instance, falling back to\n`super.renderDay(...)` for dates you don't want to customize:\n```ts\nclass MyWeekPicker extends UseWeekPicker {\n renderDay(data: UseCalendarDayData, html: LitHtml) {\n if (data.date === \"2026-03-17\") {\n return html`<strong>${data.localizedDay}</strong>`;\n }\n return super.renderDay(data, html);\n }\n}\n```\n---\n\n\n### **Events:**\n - **use-change** - Fired on selection with detail `{ value: string; dates: string[] }`\n\n### **Methods:**\n - **renderDay(data: _UseCalendarDayData_, _html: _LitHtml_): _TemplateResult | string_** - See UseCalendarRenderDay. The default renders `localizedDay`, the\nday in the locale's numbering system, e.g. `١٤` for `ar-EG`. Assigning a new function on an\ninstance (e.g. via `.renderDay=${fn}` in a Lit template) after first\nrender does not schedule a re-render on its own — call `requestUpdate()`\nif needed.\n\nDeclared as a regular method rather than a class field so subclass\noverrides can call `super.renderDay(data, html)` to fall back to the\ndefault (or a parent override's) rendering.\n\n### **Slots:**\n - **header-start** - Content before the title in the header\n- **title** - Title showing the month and year\n- **controls** - Navigation controls (previous, today, next buttons)\n- **header-end** - Content after the controls in the header\n- **date-{YYYY-MM-DD}** - Per-date content that replaces the default day number\n\n### **CSS Parts:**\n - **header** - The header container\n- **title** - The title text\n- **title-month** - The month part of the title\n- **title-literal** - Literal text in the title\n- **title-year** - The year part of the title\n- **controls** - The controls slot container\n- **control** - Individual control button\n- **control-previous** - Previous month button\n- **control-today** - Today button\n- **control-next** - Next month button\n- **control-icon** - Arrow glyph inside the previous and next buttons, mirrored in right-to-left layouts\n- **grid** - The main calendar grid\n- **grid-header** - The header row with day names\n- **grid-header-cell** - Individual day name header cell\n- **grid-body** - The container for calendar weeks\n- **row** - A row of days\n- **day** - A day cell\n- **day-empty** - An empty day cell (from previous/next month)\n- **day-selected** - A selected day cell\n- **day-today** - The current date cell\n- **day-disabled** - A disabled date cell\n- **title-button** - The clickable button wrapping the month/year title\n- **picker** - The scrollable month/year picker panel (replaces grid when open)\n- **picker-year-section** - Container for one year's label and month grid\n- **picker-year-label** - The year heading inside a year section\n- **picker-months** - The 4-column month grid inside a year section\n- **picker-month** - An individual month button\n- **picker-month-current** - Applied to the currently selected month button",
545
545
  "attributes": [
546
546
  { "name": "year", "description": "1970-9999", "values": [] },
547
547
  { "name": "month", "description": "1-12", "values": [] },