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.
- package/custom-elements.json +94 -78
- package/dist/design-system.css +0 -2
- package/dist/docs/use-date-picker.md +6 -5
- package/dist/docs/use-week-picker.md +6 -5
- package/dist/use-wc.d.mts +34 -23
- package/dist/use-wc.mjs +291 -86
- package/package.json +1 -1
- package/vscode.html-custom-data.json +14 -14
package/custom-elements.json
CHANGED
|
@@ -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": "
|
|
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 = ({
|
|
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": "
|
|
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 = ({
|
|
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": "
|
|
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"
|
package/dist/design-system.css
CHANGED
|
@@ -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 = ({
|
|
8
|
-
date === "2026-03-17" ? html`<strong>${
|
|
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:
|
|
15
|
+
renderDay(data: UseCalendarDayData, html: LitHtml) {
|
|
16
16
|
if (data.date === "2026-03-17") {
|
|
17
|
-
return html`<strong>${data.
|
|
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:
|
|
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 = ({
|
|
8
|
-
date === "2026-03-17" ? html`<strong>${
|
|
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:
|
|
15
|
+
renderDay(data: UseCalendarDayData, html: LitHtml) {
|
|
16
16
|
if (data.date === "2026-03-17") {
|
|
17
|
-
return html`<strong>${data.
|
|
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:
|
|
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); `
|
|
1031
|
-
*
|
|
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
|
-
|
|
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}.
|
|
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 = ({
|
|
1176
|
-
* date === "2026-03-17" ? html`<strong>${
|
|
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:
|
|
1194
|
+
* renderDay(data: UseCalendarDayData, html: LitHtml) {
|
|
1184
1195
|
* if (data.date === "2026-03-17") {
|
|
1185
|
-
* return html`<strong>${data.
|
|
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 = ({
|
|
1215
|
-
* date === "2026-03-17" ? html`<strong>${
|
|
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:
|
|
1233
|
+
* renderDay(data: UseCalendarDayData, html: LitHtml) {
|
|
1223
1234
|
* if (data.date === "2026-03-17") {
|
|
1224
|
-
* return html`<strong>${data.
|
|
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:
|
|
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
|
-
|
|
447
|
-
|
|
448
|
-
|
|
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))
|
|
628
|
+
].includes(part.type)) {
|
|
629
|
+
const segment = part.type;
|
|
630
|
+
return html`
|
|
496
631
|
<input
|
|
497
|
-
type="
|
|
498
|
-
|
|
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(
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
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=${
|
|
508
|
-
@
|
|
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]] =
|
|
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 =
|
|
696
|
-
if (this.monthInput) this.monthInput.value =
|
|
697
|
-
if (this.weekInput) this.weekInput.value =
|
|
698
|
-
if (this.dayInput) this.dayInput.value =
|
|
699
|
-
if (this.hourInput) this.hourInput.value =
|
|
700
|
-
if (this.minuteInput) this.minuteInput.value =
|
|
701
|
-
if (this.secondInput) this.secondInput.value =
|
|
702
|
-
if (this.millisecondInput) this.millisecondInput.value =
|
|
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="
|
|
715
|
-
|
|
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
|
-
|
|
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
|
-
|
|
1155
|
-
|
|
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 =
|
|
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
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
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}.
|
|
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
|
|
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] =
|
|
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
|
@@ -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 = ({
|
|
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 = ({
|
|
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": [] },
|