luxen-ui 0.16.2 → 0.17.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +50 -49
- package/cdn/custom-elements.json +237 -1
- package/cdn/elements/avatar/avatar.d.ts.map +1 -1
- package/cdn/elements/avatar/avatar.js +1 -1
- package/cdn/elements/avatar/avatar.js.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.d.ts +4 -0
- package/cdn/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/cdn/elements/dropdown-item/dropdown-item.js +2 -2
- package/cdn/elements/dropdown-item/dropdown-item.js.map +1 -1
- package/cdn/elements/segmented-control/index.d.ts +2 -0
- package/cdn/elements/segmented-control/index.d.ts.map +1 -0
- package/cdn/elements/segmented-control/index.js +2 -0
- package/cdn/elements/segmented-control/index.js.map +1 -0
- package/cdn/elements/segmented-control/segmented-control.d.ts +80 -0
- package/cdn/elements/segmented-control/segmented-control.d.ts.map +1 -0
- package/cdn/elements/segmented-control/segmented-control.js +2 -0
- package/cdn/elements/segmented-control/segmented-control.js.map +1 -0
- package/cdn/registry.d.ts +1 -1
- package/cdn/registry.d.ts.map +1 -1
- package/cdn/registry.js.map +1 -1
- package/cdn/standalone.css +167 -0
- package/cdn/standalone.js +25 -22
- package/cdn/standalone.js.map +1 -1
- package/cdn/styles/elements/segmented-control.css +165 -0
- package/dist/css/elements/segmented-control.css +165 -0
- package/dist/custom-elements.json +237 -1
- package/dist/elements/avatar/avatar.d.ts.map +1 -1
- package/dist/elements/avatar/avatar.js +27 -7
- package/dist/elements/dropdown-item/dropdown-item.css +18 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts +4 -0
- package/dist/elements/dropdown-item/dropdown-item.d.ts.map +1 -1
- package/dist/elements/dropdown-item/dropdown-item.js +11 -2
- package/dist/elements/segmented-control/index.d.ts +2 -0
- package/dist/elements/segmented-control/index.d.ts.map +1 -0
- package/dist/elements/segmented-control/index.js +4 -0
- package/dist/elements/segmented-control/segmented-control.d.ts +80 -0
- package/dist/elements/segmented-control/segmented-control.d.ts.map +1 -0
- package/dist/elements/segmented-control/segmented-control.js +307 -0
- package/dist/metadata/dropdown-item.json +16 -1
- package/dist/metadata/index.json +122 -2
- package/dist/metadata/segmented-control.json +105 -0
- package/dist/registry.d.ts +1 -1
- package/dist/registry.d.ts.map +1 -1
- package/dist/templates/elements/dropdown.md +52 -0
- package/dist/templates/elements/segmented-control.md +252 -0
- package/elements.json +8 -0
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -22,55 +22,56 @@ Visit https://luxen-ui.com to explore the documentation.
|
|
|
22
22
|
|
|
23
23
|
<!-- elements-table:start -->
|
|
24
24
|
|
|
25
|
-
| Name
|
|
26
|
-
|
|
|
27
|
-
| Alert
|
|
28
|
-
| Alert dialog
|
|
29
|
-
| Avatar
|
|
30
|
-
| Badge
|
|
31
|
-
| Breadcrumb
|
|
32
|
-
| Button
|
|
33
|
-
| Button group
|
|
34
|
-
| Carousel
|
|
35
|
-
| Carousel item
|
|
36
|
-
| Checkbox
|
|
37
|
-
| Close button
|
|
38
|
-
| Combobox
|
|
39
|
-
| Dialog
|
|
40
|
-
| Disclosure
|
|
41
|
-
| Divider
|
|
42
|
-
| Drawer
|
|
43
|
-
| Dropdown
|
|
44
|
-
| Dropdown item
|
|
45
|
-
| Dropdown label
|
|
46
|
-
| Form field
|
|
47
|
-
| Icon
|
|
48
|
-
| Input
|
|
49
|
-
| Input group
|
|
50
|
-
| Input OTP
|
|
51
|
-
| Input stepper
|
|
52
|
-
| Kbd
|
|
53
|
-
| Popover
|
|
54
|
-
| Progress
|
|
55
|
-
| Prose editor
|
|
56
|
-
| Radio
|
|
57
|
-
| Rating
|
|
58
|
-
|
|
|
59
|
-
|
|
|
60
|
-
|
|
|
61
|
-
|
|
|
62
|
-
|
|
|
63
|
-
|
|
|
64
|
-
| Stories
|
|
65
|
-
|
|
|
66
|
-
|
|
|
67
|
-
|
|
|
68
|
-
|
|
|
69
|
-
|
|
|
70
|
-
|
|
|
71
|
-
|
|
|
72
|
-
|
|
|
73
|
-
| Tree
|
|
25
|
+
| Name | HTML tag | Type |
|
|
26
|
+
| ----------------- | ----------------------- | --------------------------------------- |
|
|
27
|
+
| Alert | `<l-alert>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
28
|
+
| Alert dialog | `<l-alert-dialog>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
29
|
+
| Avatar | `<l-avatar>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
30
|
+
| Badge | `<l-badge>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
31
|
+
| Breadcrumb | `<nav>` | ⏣ Native HTML Element |
|
|
32
|
+
| Button | `<button>` | ⏣ Native HTML Element |
|
|
33
|
+
| Button group | `<l-button-group>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
34
|
+
| Carousel | `<l-carousel>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
35
|
+
| Carousel item | `<l-carousel-item>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
36
|
+
| Checkbox | `<input>` | ⏣ Native HTML Element |
|
|
37
|
+
| Close button | `<button>` | ⏣ Native HTML Element |
|
|
38
|
+
| Combobox | `<l-combobox>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
39
|
+
| Dialog | `<l-dialog>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
40
|
+
| Disclosure | `<details>` | ⏣ Native HTML Element |
|
|
41
|
+
| Divider | `<l-divider>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
42
|
+
| Drawer | `<l-drawer>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
43
|
+
| Dropdown | `<l-dropdown>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
44
|
+
| Dropdown item | `<l-dropdown-item>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
45
|
+
| Dropdown label | `<l-dropdown-label>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
46
|
+
| Form field | `<l-form-field>` | ⬡ Progressive Custom HTML Element |
|
|
47
|
+
| Icon | `<l-icon>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
48
|
+
| Input | `<input>` | ⏣ Native HTML Element |
|
|
49
|
+
| Input group | `<l-input-group>` | ⬡ Progressive Custom HTML Element |
|
|
50
|
+
| Input OTP | `<l-input-otp>` | ⬡ Progressive Custom HTML Element |
|
|
51
|
+
| Input stepper | `<l-input-stepper>` | ⬡ Progressive Custom HTML Element |
|
|
52
|
+
| Kbd | `<kbd>` | ⏣ Native HTML Element |
|
|
53
|
+
| Popover | `<l-popover>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
54
|
+
| Progress | `<progress>` | ⏣ Native HTML Element |
|
|
55
|
+
| Prose editor | `<l-prose-editor>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
56
|
+
| Radio | `<input>` | ⏣ Native HTML Element |
|
|
57
|
+
| Rating | `<l-rating>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
58
|
+
| Segmented control | `<l-segmented-control>` | ⬡ Progressive Custom HTML Element |
|
|
59
|
+
| Select | `<l-select>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
60
|
+
| Skeleton | `<l-skeleton>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
61
|
+
| Slider | `<l-slider>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
62
|
+
| Spinner | `<l-spinner>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
63
|
+
| Sticky bar | `<l-sticky-bar>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
64
|
+
| Stories | `<l-stories>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
65
|
+
| Stories viewer | `<l-stories-viewer>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
66
|
+
| Story | `<l-story>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
67
|
+
| Switch | `<input>` | ⏣ Native HTML Element |
|
|
68
|
+
| Tabs | `<l-tabs>` | ⬡ Progressive Custom HTML Element |
|
|
69
|
+
| Tag | `<l-tag>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
70
|
+
| Textarea | `<textarea>` | ⏣ Native HTML Element |
|
|
71
|
+
| Toast | `<l-toast>` | ◇ Custom HTML Element (no Shadow DOM) |
|
|
72
|
+
| Tooltip | `<l-tooltip>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
73
|
+
| Tree | `<l-tree>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
74
|
+
| Tree item | `<l-tree-item>` | ⬢ Custom HTML Element (with Shadow DOM) |
|
|
74
75
|
|
|
75
76
|
_⏣ Native HTML Element · ⬡ Progressive Custom HTML Element · ◇ Custom HTML Element (no Shadow DOM) · ⬢ Custom HTML Element (with Shadow DOM)_
|
|
76
77
|
|
package/cdn/custom-elements.json
CHANGED
|
@@ -3750,6 +3750,17 @@
|
|
|
3750
3750
|
"description": "The type of item: `normal` or `checkbox`.",
|
|
3751
3751
|
"attribute": "type"
|
|
3752
3752
|
},
|
|
3753
|
+
{
|
|
3754
|
+
"kind": "field",
|
|
3755
|
+
"name": "variant",
|
|
3756
|
+
"type": {
|
|
3757
|
+
"text": "'destructive' | undefined"
|
|
3758
|
+
},
|
|
3759
|
+
"default": "undefined",
|
|
3760
|
+
"description": "Visual variant. `destructive` marks a delete-style action in red.",
|
|
3761
|
+
"attribute": "variant",
|
|
3762
|
+
"reflects": true
|
|
3763
|
+
},
|
|
3753
3764
|
{
|
|
3754
3765
|
"kind": "field",
|
|
3755
3766
|
"name": "checked",
|
|
@@ -3813,6 +3824,15 @@
|
|
|
3813
3824
|
}
|
|
3814
3825
|
],
|
|
3815
3826
|
"attributes": [
|
|
3827
|
+
{
|
|
3828
|
+
"description": "Visual variant. `destructive` marks a delete-style action in red.",
|
|
3829
|
+
"name": "variant",
|
|
3830
|
+
"type": {
|
|
3831
|
+
"text": "'destructive' | undefined"
|
|
3832
|
+
},
|
|
3833
|
+
"default": "undefined",
|
|
3834
|
+
"fieldName": "variant"
|
|
3835
|
+
},
|
|
3816
3836
|
{
|
|
3817
3837
|
"name": "value",
|
|
3818
3838
|
"type": {
|
|
@@ -3864,7 +3884,13 @@
|
|
|
3864
3884
|
"module": "/src/html/shared/luxen-element.js"
|
|
3865
3885
|
},
|
|
3866
3886
|
"tagName": "l-dropdown-item",
|
|
3867
|
-
"customElement": true
|
|
3887
|
+
"customElement": true,
|
|
3888
|
+
"_attributes": [
|
|
3889
|
+
{
|
|
3890
|
+
"name": "variant",
|
|
3891
|
+
"description": "destructive — Renders the item as a destructive action (red), for delete-style entries."
|
|
3892
|
+
}
|
|
3893
|
+
]
|
|
3868
3894
|
}
|
|
3869
3895
|
],
|
|
3870
3896
|
"exports": [
|
|
@@ -6041,6 +6067,216 @@
|
|
|
6041
6067
|
}
|
|
6042
6068
|
]
|
|
6043
6069
|
},
|
|
6070
|
+
{
|
|
6071
|
+
"kind": "javascript-module",
|
|
6072
|
+
"path": "src/html/elements/segmented-control/index.ts",
|
|
6073
|
+
"declarations": [],
|
|
6074
|
+
"exports": [
|
|
6075
|
+
{
|
|
6076
|
+
"kind": "js",
|
|
6077
|
+
"name": "*",
|
|
6078
|
+
"declaration": {
|
|
6079
|
+
"name": "*",
|
|
6080
|
+
"module": "src/html/elements/segmented-control/segmented-control.js"
|
|
6081
|
+
}
|
|
6082
|
+
}
|
|
6083
|
+
]
|
|
6084
|
+
},
|
|
6085
|
+
{
|
|
6086
|
+
"kind": "javascript-module",
|
|
6087
|
+
"path": "src/html/elements/segmented-control/segmented-control.ts",
|
|
6088
|
+
"declarations": [
|
|
6089
|
+
{
|
|
6090
|
+
"kind": "class",
|
|
6091
|
+
"description": "Fired when the selected segment changes. Bubbles; not composed.",
|
|
6092
|
+
"name": "SegmentChangeEvent",
|
|
6093
|
+
"members": [
|
|
6094
|
+
{
|
|
6095
|
+
"kind": "field",
|
|
6096
|
+
"name": "value",
|
|
6097
|
+
"type": {
|
|
6098
|
+
"text": "string"
|
|
6099
|
+
},
|
|
6100
|
+
"readonly": true,
|
|
6101
|
+
"default": "value"
|
|
6102
|
+
},
|
|
6103
|
+
{
|
|
6104
|
+
"kind": "field",
|
|
6105
|
+
"name": "index",
|
|
6106
|
+
"type": {
|
|
6107
|
+
"text": "number"
|
|
6108
|
+
},
|
|
6109
|
+
"readonly": true,
|
|
6110
|
+
"default": "index"
|
|
6111
|
+
}
|
|
6112
|
+
],
|
|
6113
|
+
"superclass": {
|
|
6114
|
+
"name": "Event",
|
|
6115
|
+
"module": "src/html/elements/segmented-control/segmented-control.ts"
|
|
6116
|
+
}
|
|
6117
|
+
},
|
|
6118
|
+
{
|
|
6119
|
+
"kind": "class",
|
|
6120
|
+
"description": "",
|
|
6121
|
+
"name": "SegmentedControl",
|
|
6122
|
+
"cssProperties": [
|
|
6123
|
+
{
|
|
6124
|
+
"description": "Background of the sliding pill behind the selected segment.",
|
|
6125
|
+
"name": "--indicator-color",
|
|
6126
|
+
"default": "var(--l-color-surface)"
|
|
6127
|
+
},
|
|
6128
|
+
{
|
|
6129
|
+
"description": "Corner radius of the track.",
|
|
6130
|
+
"name": "--border-radius",
|
|
6131
|
+
"default": "var(--l-radius-lg)"
|
|
6132
|
+
}
|
|
6133
|
+
],
|
|
6134
|
+
"members": [
|
|
6135
|
+
{
|
|
6136
|
+
"kind": "field",
|
|
6137
|
+
"name": "value",
|
|
6138
|
+
"type": {
|
|
6139
|
+
"text": "string"
|
|
6140
|
+
},
|
|
6141
|
+
"default": "''",
|
|
6142
|
+
"description": "Value of the selected segment (its `value` attribute, else its index).",
|
|
6143
|
+
"attribute": "value",
|
|
6144
|
+
"reflects": true
|
|
6145
|
+
},
|
|
6146
|
+
{
|
|
6147
|
+
"kind": "field",
|
|
6148
|
+
"name": "size",
|
|
6149
|
+
"type": {
|
|
6150
|
+
"text": "SegmentedControlSize"
|
|
6151
|
+
},
|
|
6152
|
+
"default": "'md'",
|
|
6153
|
+
"description": "Control height, aligned with buttons and form controls at the same size.",
|
|
6154
|
+
"attribute": "size",
|
|
6155
|
+
"reflects": true
|
|
6156
|
+
},
|
|
6157
|
+
{
|
|
6158
|
+
"kind": "field",
|
|
6159
|
+
"name": "label",
|
|
6160
|
+
"type": {
|
|
6161
|
+
"text": "string | undefined"
|
|
6162
|
+
},
|
|
6163
|
+
"description": "Accessible label announced for the group. Not displayed on screen.",
|
|
6164
|
+
"attribute": "label",
|
|
6165
|
+
"reflects": true
|
|
6166
|
+
},
|
|
6167
|
+
{
|
|
6168
|
+
"kind": "field",
|
|
6169
|
+
"name": "fullWidth",
|
|
6170
|
+
"type": {
|
|
6171
|
+
"text": "boolean"
|
|
6172
|
+
},
|
|
6173
|
+
"default": "false",
|
|
6174
|
+
"description": "Stretch segments to fill the container width.",
|
|
6175
|
+
"attribute": "full-width",
|
|
6176
|
+
"reflects": true
|
|
6177
|
+
},
|
|
6178
|
+
{
|
|
6179
|
+
"kind": "method",
|
|
6180
|
+
"name": "formResetCallback"
|
|
6181
|
+
},
|
|
6182
|
+
{
|
|
6183
|
+
"kind": "method",
|
|
6184
|
+
"name": "formStateRestoreCallback",
|
|
6185
|
+
"parameters": [
|
|
6186
|
+
{
|
|
6187
|
+
"name": "state",
|
|
6188
|
+
"type": {
|
|
6189
|
+
"text": "string"
|
|
6190
|
+
}
|
|
6191
|
+
},
|
|
6192
|
+
{
|
|
6193
|
+
"name": "mode",
|
|
6194
|
+
"type": {
|
|
6195
|
+
"text": "'restore' | 'autocomplete'"
|
|
6196
|
+
}
|
|
6197
|
+
}
|
|
6198
|
+
]
|
|
6199
|
+
}
|
|
6200
|
+
],
|
|
6201
|
+
"events": [
|
|
6202
|
+
{
|
|
6203
|
+
"name": "change",
|
|
6204
|
+
"description": "Fired when the selected segment changes. Not cancelable. Bubbles. Properties: `value: string`, `index: number`.",
|
|
6205
|
+
"cancelable": false
|
|
6206
|
+
}
|
|
6207
|
+
],
|
|
6208
|
+
"attributes": [
|
|
6209
|
+
{
|
|
6210
|
+
"name": "value",
|
|
6211
|
+
"type": {
|
|
6212
|
+
"text": "string"
|
|
6213
|
+
},
|
|
6214
|
+
"default": "''",
|
|
6215
|
+
"description": "Value of the selected segment (its `value` attribute, else its index).",
|
|
6216
|
+
"fieldName": "value"
|
|
6217
|
+
},
|
|
6218
|
+
{
|
|
6219
|
+
"name": "size",
|
|
6220
|
+
"type": {
|
|
6221
|
+
"text": "SegmentedControlSize"
|
|
6222
|
+
},
|
|
6223
|
+
"default": "'md'",
|
|
6224
|
+
"description": "Control height, aligned with buttons and form controls at the same size.",
|
|
6225
|
+
"fieldName": "size"
|
|
6226
|
+
},
|
|
6227
|
+
{
|
|
6228
|
+
"name": "label",
|
|
6229
|
+
"type": {
|
|
6230
|
+
"text": "string | undefined"
|
|
6231
|
+
},
|
|
6232
|
+
"description": "Accessible label announced for the group. Not displayed on screen.",
|
|
6233
|
+
"fieldName": "label"
|
|
6234
|
+
},
|
|
6235
|
+
{
|
|
6236
|
+
"name": "full-width",
|
|
6237
|
+
"type": {
|
|
6238
|
+
"text": "boolean"
|
|
6239
|
+
},
|
|
6240
|
+
"default": "false",
|
|
6241
|
+
"description": "Stretch segments to fill the container width.",
|
|
6242
|
+
"fieldName": "fullWidth"
|
|
6243
|
+
}
|
|
6244
|
+
],
|
|
6245
|
+
"superclass": {
|
|
6246
|
+
"name": "LuxenFormAssociatedElement",
|
|
6247
|
+
"module": "/src/html/shared/luxen-form-associated-element.js"
|
|
6248
|
+
},
|
|
6249
|
+
"tagName": "l-segmented-control",
|
|
6250
|
+
"customElement": true,
|
|
6251
|
+
"summary": "A single-select segmented control: a compact, mutually-exclusive\nswitch between a few options (radio-group semantics with a sliding pill).\nProgressively enhances light-DOM `<button>`s so it works before JS runs.\nForm-associated: give it a `name` and its selected `value` is submitted with\nthe form (and restored on reset), like a native radio group.",
|
|
6252
|
+
"examples": [
|
|
6253
|
+
{
|
|
6254
|
+
"title": "Mark the initially-selected segment with `aria-checked=\"true\"` (or set",
|
|
6255
|
+
"language": "html",
|
|
6256
|
+
"code": "`value` to its `value` attribute) so it is styled before the script upgrades\nthe element.\n```html\n<l-segmented-control label=\"Metric\" value=\"cost\">\n <button value=\"volume\">Volume</button>\n <button value=\"cost\" aria-checked=\"true\">Cost</button>\n</l-segmented-control>\n```"
|
|
6257
|
+
}
|
|
6258
|
+
]
|
|
6259
|
+
}
|
|
6260
|
+
],
|
|
6261
|
+
"exports": [
|
|
6262
|
+
{
|
|
6263
|
+
"kind": "js",
|
|
6264
|
+
"name": "SegmentChangeEvent",
|
|
6265
|
+
"declaration": {
|
|
6266
|
+
"name": "SegmentChangeEvent",
|
|
6267
|
+
"module": "src/html/elements/segmented-control/segmented-control.ts"
|
|
6268
|
+
}
|
|
6269
|
+
},
|
|
6270
|
+
{
|
|
6271
|
+
"kind": "js",
|
|
6272
|
+
"name": "SegmentedControl",
|
|
6273
|
+
"declaration": {
|
|
6274
|
+
"name": "SegmentedControl",
|
|
6275
|
+
"module": "src/html/elements/segmented-control/segmented-control.ts"
|
|
6276
|
+
}
|
|
6277
|
+
}
|
|
6278
|
+
]
|
|
6279
|
+
},
|
|
6044
6280
|
{
|
|
6045
6281
|
"kind": "javascript-module",
|
|
6046
6282
|
"path": "src/html/elements/select/index.ts",
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../src/html/elements/avatar/avatar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;
|
|
1
|
+
{"version":3,"file":"avatar.d.ts","sourceRoot":"","sources":["../../../src/html/elements/avatar/avatar.ts"],"names":[],"mappings":"AACA,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AA0C7D;;;;;;;;;;;;GAYG;AACH,qBAAa,MAAO,SAAQ,YAAY;IACtC,MAAM,CAAC,MAAM,4BAAwB;IAErC,qFAAqF;IAErF,GAAG,SAAM;IAET,6EAA6E;IAE7E,IAAI,SAAM;IAEV,8DAA8D;IAE9D,IAAI,SAAQ;IAEZ,4DAA4D;IAE5D,KAAK,SAAK;IAEV,+DAA+D;IAE/D,WAAW,UAAS;IAEX,OAAO,CAAC,SAAS,CAAS;IAEnC,UAAU,CAAC,OAAO,EAAE,GAAG,CAAC,MAAM,EAAE,OAAO,CAAC;IAMxC,iBAAiB;IAOjB,OAAO;IAgBP,OAAO,KAAK,IAAI,GAEf;IAED,MAAM;IAkCN,OAAO,CAAC,QAAQ,CAEd;CACH"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{i as e}from"../../chunks/lit.js";import{i as t,r as n,t as r}from"../../chunks/decorate.js";import{a as i,o as a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{r as l,t as u}from"../../chunks/static-html.js";var d=e(`:host{--size:40px;--_font-size:calc(var(--size) * .4);width:var(--size);height:var(--size);border-radius:calc(var(--size) * .15);isolation:isolate;vertical-align:middle;color:var(--l-color-text-secondary);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex;position:relative}:host([size=xs]){--size:24px;--_font-size:.75rem}:host([size=sm]){--size:32px;--_font-size:.875rem}:host([size=lg]){--size:48px;--_font-size:1.25rem}:host([size=xl]){--size:56px;--_font-size:1.25rem}img{object-fit:cover;border-radius:inherit;width:100%;height:100%}.initials{margin:auto}.icon{color:currentColor;width:60%;height:60%}.badge{z-index:1;min-width:12px;height:12px;box-shadow:0 0 0 2px var(--l-color-surface);background-color:var(--l-color-surface);font-variant-numeric:tabular-nums;color:var(--l-color-text-primary);border-radius:3px;justify-content:center;align-items:center;padding-inline:2px;font-size:.625rem;font-weight:500;line-height:1;display:flex;position:absolute;bottom:0;right:0;overflow:hidden}.base:after{content:"";border-radius:inherit;pointer-events:none;position:absolute;inset:0;box-shadow:inset 0 0 0 1px var(--lightningcss-light,oklch(0% 0 0/.07))var(--lightningcss-dark,oklch(100% 0 0/.1))}.base{border-radius:calc(var(--size) * .15);background-color:var(--color,var(--l-color-bg-fill-neutral-soft));--_on:calc(255 * ( 1 - sign(.2126 * (r / 255) * (r / 255) + .7152 * (g / 255) * (g / 255) + .0722 * (b / 255) * (b / 255) - .2) ) / 2);width:100%;height:100%;color:var(--text-color,rgb(from var(--color) var(--_on) var(--_on) var(--_on)));font-size:var(--_font-size);text-transform:uppercase;cursor:inherit;border:none;justify-content:center;align-items:center;padding:0;font-weight:500;line-height:1;display:inline-flex}@supports (color:contrast-color(red)){.base{color:var(--text-color,contrast-color(var(--color)))}}button.base{cursor:pointer}button.base:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}button.base:hover{box-shadow:inset 0 0 0 1.5px var(--l-color-border-interactive)}@container style(--appearance:circle){.base{border-radius:50%}}`);function f(e){
|
|
1
|
+
import{i as e}from"../../chunks/lit.js";import{i as t,r as n,t as r}from"../../chunks/decorate.js";import{a as i,o as a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{r as l,t as u}from"../../chunks/static-html.js";var d=e(`:host{--size:40px;--_font-size:calc(var(--size) * .4);width:var(--size);height:var(--size);border-radius:calc(var(--size) * .15);isolation:isolate;vertical-align:middle;color:var(--l-color-text-secondary);flex-shrink:0;justify-content:center;align-items:center;display:inline-flex;position:relative}:host([size=xs]){--size:24px;--_font-size:.75rem}:host([size=sm]){--size:32px;--_font-size:.875rem}:host([size=lg]){--size:48px;--_font-size:1.25rem}:host([size=xl]){--size:56px;--_font-size:1.25rem}img{object-fit:cover;border-radius:inherit;width:100%;height:100%}.initials{margin:auto}.icon{color:currentColor;width:60%;height:60%}.badge{z-index:1;min-width:12px;height:12px;box-shadow:0 0 0 2px var(--l-color-surface);background-color:var(--l-color-surface);font-variant-numeric:tabular-nums;color:var(--l-color-text-primary);border-radius:3px;justify-content:center;align-items:center;padding-inline:2px;font-size:.625rem;font-weight:500;line-height:1;display:flex;position:absolute;bottom:0;right:0;overflow:hidden}.base:after{content:"";border-radius:inherit;pointer-events:none;position:absolute;inset:0;box-shadow:inset 0 0 0 1px var(--lightningcss-light,oklch(0% 0 0/.07))var(--lightningcss-dark,oklch(100% 0 0/.1))}.base{border-radius:calc(var(--size) * .15);background-color:var(--color,var(--l-color-bg-fill-neutral-soft));--_on:calc(255 * ( 1 - sign(.2126 * (r / 255) * (r / 255) + .7152 * (g / 255) * (g / 255) + .0722 * (b / 255) * (b / 255) - .2) ) / 2);width:100%;height:100%;color:var(--text-color,rgb(from var(--color) var(--_on) var(--_on) var(--_on)));font-size:var(--_font-size);text-transform:uppercase;cursor:inherit;border:none;justify-content:center;align-items:center;padding:0;font-weight:500;line-height:1;display:inline-flex}@supports (color:contrast-color(red)){.base{color:var(--text-color,contrast-color(var(--color)))}}button.base{cursor:pointer}button.base:focus-visible{outline:2px solid var(--l-focus-ring);outline-offset:2px}button.base:hover{box-shadow:inset 0 0 0 1.5px var(--l-color-border-interactive)}@container style(--appearance:circle){.base{border-radius:50%}}`);function f(e=``){let t=e.normalize(`NFC`).trim().split(/\s+/).filter(Boolean);if(t.length===0)return``;let n=e=>[...e][0];return(n(t[0])+(t.length>1?n(t[t.length-1]):``)).toUpperCase()}var p=a`<svg class="icon" viewBox="0 0 24 24" fill="currentColor" aria-hidden="true">
|
|
2
2
|
<path d="M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z" />
|
|
3
3
|
</svg>`,m=class extends s{constructor(...e){super(...e),this.src=``,this.name=``,this.size=`md`,this.badge=0,this.interactive=!1,this._hasError=!1,this._onError=()=>{this._hasError=!0}}static{this.styles=[c,d]}willUpdate(e){e.has(`src`)&&(this._hasError=!1)}connectedCallback(){super.connectedCallback(),!this.interactive&&!this.hasAttribute(`role`)&&this.setAttribute(`role`,`img`)}updated(){this.interactive?(this.getAttribute(`role`)===`img`&&this.removeAttribute(`role`),this.removeAttribute(`aria-label`)):(this.hasAttribute(`role`)||this.setAttribute(`role`,`img`),this.name&&this.setAttribute(`aria-label`,this.name));let e=getComputedStyle(this).getPropertyValue(`--appearance`).trim()===`circle`;this.style.borderRadius=e?`50%`:``}get _tag(){return this.interactive?u`button`:u`div`}render(){let e=this.src&&!this._hasError?i`<img
|
|
4
4
|
src=${this.src}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"avatar.js","names":[],"sources":["../../../src/html/elements/avatar/avatar.css?inline","../../../src/html/elements/avatar/avatar.ts"],"sourcesContent":[":host {\n --size: 40px;\n --_font-size: calc(var(--size) * 0.4);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n width: var(--size);\n height: var(--size);\n border-radius: calc(var(--size) * 0.15);\n isolation: isolate;\n vertical-align: middle;\n flex-shrink: 0;\n color: var(--l-color-text-secondary);\n}\n\n:host([size='xs']) {\n --size: 24px;\n --_font-size: 0.75rem;\n}\n:host([size='sm']) {\n --size: 32px;\n --_font-size: 0.875rem;\n}\n:host([size='lg']) {\n --size: 48px;\n --_font-size: 1.25rem;\n}\n:host([size='xl']) {\n --size: 56px;\n --_font-size: 1.25rem;\n}\n\nimg {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: inherit;\n}\n\n.initials {\n margin: auto;\n}\n\n.icon {\n width: 60%;\n height: 60%;\n color: currentColor;\n}\n\n.badge {\n position: absolute;\n bottom: 0;\n right: 0;\n z-index: 1;\n height: 12px;\n min-width: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-inline: 2px;\n border-radius: 3px;\n box-shadow: 0 0 0 2px var(--l-color-surface);\n background-color: var(--l-color-surface);\n font-size: 0.625rem;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--l-color-text-primary);\n overflow: hidden;\n}\n\n/*\n * Inset ring via ::after so it renders on top of the <img>.\n * A box-shadow on :host would be covered by the image since\n * it fills the entire element with object-fit: cover.\n */\n.base::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px light-dark(oklch(0 0 0 / 7%), oklch(100% 0 0 / 10%));\n pointer-events: none;\n}\n\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n border: none;\n border-radius: calc(var(--size) * 0.15);\n background-color: var(--color, var(--l-color-bg-fill-neutral-soft));\n /*\n * Text over --color, decided by the background's own luminance — never the\n * document mode, since --color is consumer-set and ignores light/dark.\n * The contrast-color() block below is the primary path (Baseline 2026); this\n * --_on calc() is the fallback for browsers predating it (Chrome ≤146,\n * Safari <26, Firefox <146). It approximates WCAG relative luminance with\n * squared sRGB channels (a cheap linearization, Rec. 709 weights) and flips at\n * 0.2, just above the black/white crossover (~0.179) to offset squaring\n * underestimating mid-tones — holding AA across saturated brand colors where an\n * OKLCH-lightness threshold collapses to ≈1:1 (OKLCH L tracks hue, not\n * luminance: a mid red reads \"light\" yet needs white text). --text-color lets\n * consumers force a specific brand text color, overriding both paths.\n */\n --_on: calc(\n 255 *\n (\n 1 -\n sign(\n 0.2126 * (r / 255) * (r / 255) + 0.7152 * (g / 255) * (g / 255) + 0.0722 * (b / 255) *\n (b / 255) - 0.2\n )\n ) /\n 2\n );\n color: var(--text-color, rgb(from var(--color) var(--_on) var(--_on) var(--_on)));\n font-size: var(--_font-size);\n font-weight: 500;\n line-height: 1;\n text-transform: uppercase;\n padding: 0;\n cursor: inherit;\n}\n\n/* Primary path: native, guaranteed-contrast black/white. contrast-color() is\n * Baseline Newly Available (2026) — Safari 26+, Firefox 146+, Chrome/Edge 147+ —\n * and supersedes the luminance approximation above where supported. @supports (not\n * declaration order) is required: the value contains var(), so a non-supporting\n * browser would treat contrast-color() as invalid-at-computed-value-time and reset\n * to `inherit` rather than falling back to the calc() line. No bg fallback inside,\n * so a bare avatar (no --color) keeps inheriting --l-color-text-secondary.\n * --text-color still wins here, overriding contrast-color()'s choice.\n * (The earlier `… vs black, white to AA` extended syntax was dropped from the spec.) */\n@supports (color: contrast-color(red)) {\n .base {\n color: var(--text-color, contrast-color(var(--color)));\n }\n}\n\nbutton.base {\n cursor: pointer;\n}\n\nbutton.base:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\nbutton.base:hover {\n box-shadow: inset 0 0 0 1.5px var(--l-color-border-interactive);\n}\n\n@container style(--appearance: circle) {\n .base {\n border-radius: 50%;\n }\n}\n","import { html, nothing, svg, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { html as staticHtml, literal } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './avatar.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\nfunction getInitials(name: string): string {\n return name\n .match(/(\\b\\S)?/g)!\n .join('')\n .match(/(^\\S|\\S$)?/g)!\n .join('')\n .toUpperCase();\n}\n\nconst defaultIcon = svg`<svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z\" />\n</svg>`;\n\n/**\n * @summary An avatar component for displaying user images, initials, or a default icon.\n * @customElement l-avatar\n *\n * @cssproperty [--size=40px] - Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.\n * @cssproperty --color - Background fill color for initials and the default icon.\n * @cssproperty --text-color - Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).\n * @cssproperty --appearance - Set to `circle` for a fully circular avatar (default is a rounded square).\n *\n * @csspart base - The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.\n *\n * @cssClass .l-avatar-group - Flex wrapper that overlaps a row of stacked avatars.\n */\nexport class Avatar extends LuxenElement {\n static styles = [hostStyles, styles];\n\n /** Image URL. Falls back to initials (then the default icon) if it fails to load. */\n @property()\n src = '';\n\n /** Name used as the accessible label and to derive the initials fallback. */\n @property()\n name = '';\n\n /** Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`. */\n @property({ reflect: true })\n size = 'md';\n\n /** Count shown in the corner badge. `0` hides the badge. */\n @property({ type: Number })\n badge = 0;\n\n /** Render as a `<button>` with focus ring and hover states. */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n @state() private _hasError = false;\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('src')) {\n this._hasError = false;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.interactive && !this.hasAttribute('role')) {\n this.setAttribute('role', 'img');\n }\n }\n\n updated() {\n // An interactive avatar renders a focusable <button>, and a role=\"img\" node\n // must stay a leaf in the accessibility tree — so the host carries no role\n // or label and the inner button holds the accessible name (see render()).\n if (this.interactive) {\n if (this.getAttribute('role') === 'img') this.removeAttribute('role');\n this.removeAttribute('aria-label');\n } else {\n if (!this.hasAttribute('role')) this.setAttribute('role', 'img');\n if (this.name) this.setAttribute('aria-label', this.name);\n }\n\n const isCircle = getComputedStyle(this).getPropertyValue('--appearance').trim() === 'circle';\n this.style.borderRadius = isCircle ? '50%' : '';\n }\n\n private get _tag() {\n return this.interactive ? literal`button` : literal`div`;\n }\n\n render() {\n const content =\n this.src && !this._hasError\n ? html`<img\n src=${this.src}\n alt=${this.name || ''}\n @error=${this._onError}\n />`\n : this.name\n ? html`<span class=\"initials\">${getInitials(this.name)}</span>`\n : html`<slot>${defaultIcon}</slot>`;\n\n return staticHtml`\n <${this._tag}\n class=\"base\"\n part=\"base\"\n type=${this.interactive ? 'button' : nothing}\n aria-label=${this.interactive && this.name ? this.name : nothing}\n >\n ${content}\n </${this._tag}>\n ${\n this.badge\n ? html`<div\n class=\"badge\"\n aria-hidden=\"true\"\n >\n ${this.badge}\n </div>`\n : nothing\n }\n `;\n }\n\n private _onError = () => {\n this._hasError = true;\n };\n}\n"],"mappings":"oUCOA,IAAM,EAAS,EAAU,4jEAAS,EAElC,SAAS,EAAY,EAAsB,CACzC,OAAO,EACJ,MAAM,UAAU,CAAC,CACjB,KAAK,EAAE,CAAC,CACR,MAAM,aAAa,CAAC,CACpB,KAAK,EAAE,CAAC,CACR,YAAY,CACjB,CAEA,IAAM,EAAc,CAAG;;QAiBV,EAAb,cAA4B,CAAa,wCAKjC,aAIC,aAIA,gBAIC,mBAIM,kBAEe,qBAqEJ,CACvB,KAAK,UAAY,EACnB,qBA7FgB,CAAC,EAAY,CAAM,EAwBnC,WAAW,EAA+B,CACpC,EAAQ,IAAI,KAAK,IACnB,KAAK,UAAY,GAErB,CAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACpB,CAAC,KAAK,aAAe,CAAC,KAAK,aAAa,MAAM,GAChD,KAAK,aAAa,OAAQ,KAAK,CAEnC,CAEA,SAAU,CAIJ,KAAK,aACH,KAAK,aAAa,MAAM,IAAM,OAAO,KAAK,gBAAgB,MAAM,EACpE,KAAK,gBAAgB,YAAY,IAE5B,KAAK,aAAa,MAAM,GAAG,KAAK,aAAa,OAAQ,KAAK,EAC3D,KAAK,MAAM,KAAK,aAAa,aAAc,KAAK,IAAI,GAG1D,IAAM,EAAW,iBAAiB,IAAI,CAAC,CAAC,iBAAiB,cAAc,CAAC,CAAC,KAAK,IAAM,SACpF,KAAK,MAAM,aAAe,EAAW,MAAQ,EAC/C,CAEA,IAAY,MAAO,CACjB,OAAO,KAAK,YAAc,CAAO,SAAW,CAAO,KACrD,CAEA,QAAS,CACP,IAAM,EACJ,KAAK,KAAO,CAAC,KAAK,UACd,CAAI;kBACI,KAAK,IAAI;kBACT,KAAK,MAAQ,GAAG;qBACb,KAAK,SAAS;cAEzB,KAAK,KACH,CAAI,0BAA0B,EAAY,KAAK,IAAI,EAAE,SACrD,CAAI,SAAS,EAAY,SAEjC,MAAO,EAAU;SACZ,KAAK,KAAK;;;eAGJ,KAAK,YAAc,SAAW,EAAQ;qBAChC,KAAK,aAAe,KAAK,KAAO,KAAK,KAAO,EAAQ;;UAE/D,EAAQ;UACR,KAAK,KAAK;QAEZ,KAAK,MACD,CAAI;;;;gBAIA,KAAK,MAAM;oBAEf,EACL;KAEL,CAKF,KA3FG,EAAS,CAAA,EAAA,EAAA,UAAA,MAAA,IAAA,EAAA,KAIT,EAAS,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAIT,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAI1B,EAAS,CAAE,KAAM,MAAO,CAAC,CAAA,EAAA,EAAA,UAAA,QAAA,IAAA,EAAA,KAIzB,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAGzC,EAAM,CAAA,EAAA,EAAA,UAAA,YAAA,IAAA,EAAA"}
|
|
1
|
+
{"version":3,"file":"avatar.js","names":[],"sources":["../../../src/html/elements/avatar/avatar.css?inline","../../../src/html/elements/avatar/avatar.ts"],"sourcesContent":[":host {\n --size: 40px;\n --_font-size: calc(var(--size) * 0.4);\n display: inline-flex;\n align-items: center;\n justify-content: center;\n position: relative;\n width: var(--size);\n height: var(--size);\n border-radius: calc(var(--size) * 0.15);\n isolation: isolate;\n vertical-align: middle;\n flex-shrink: 0;\n color: var(--l-color-text-secondary);\n}\n\n:host([size='xs']) {\n --size: 24px;\n --_font-size: 0.75rem;\n}\n:host([size='sm']) {\n --size: 32px;\n --_font-size: 0.875rem;\n}\n:host([size='lg']) {\n --size: 48px;\n --_font-size: 1.25rem;\n}\n:host([size='xl']) {\n --size: 56px;\n --_font-size: 1.25rem;\n}\n\nimg {\n width: 100%;\n height: 100%;\n object-fit: cover;\n border-radius: inherit;\n}\n\n.initials {\n margin: auto;\n}\n\n.icon {\n width: 60%;\n height: 60%;\n color: currentColor;\n}\n\n.badge {\n position: absolute;\n bottom: 0;\n right: 0;\n z-index: 1;\n height: 12px;\n min-width: 12px;\n display: flex;\n align-items: center;\n justify-content: center;\n padding-inline: 2px;\n border-radius: 3px;\n box-shadow: 0 0 0 2px var(--l-color-surface);\n background-color: var(--l-color-surface);\n font-size: 0.625rem;\n font-weight: 500;\n font-variant-numeric: tabular-nums;\n line-height: 1;\n color: var(--l-color-text-primary);\n overflow: hidden;\n}\n\n/*\n * Inset ring via ::after so it renders on top of the <img>.\n * A box-shadow on :host would be covered by the image since\n * it fills the entire element with object-fit: cover.\n */\n.base::after {\n content: '';\n position: absolute;\n inset: 0;\n border-radius: inherit;\n box-shadow: inset 0 0 0 1px light-dark(oklch(0 0 0 / 7%), oklch(100% 0 0 / 10%));\n pointer-events: none;\n}\n\n.base {\n display: inline-flex;\n align-items: center;\n justify-content: center;\n width: 100%;\n height: 100%;\n border: none;\n border-radius: calc(var(--size) * 0.15);\n background-color: var(--color, var(--l-color-bg-fill-neutral-soft));\n /*\n * Text over --color, decided by the background's own luminance — never the\n * document mode, since --color is consumer-set and ignores light/dark.\n * The contrast-color() block below is the primary path (Baseline 2026); this\n * --_on calc() is the fallback for browsers predating it (Chrome ≤146,\n * Safari <26, Firefox <146). It approximates WCAG relative luminance with\n * squared sRGB channels (a cheap linearization, Rec. 709 weights) and flips at\n * 0.2, just above the black/white crossover (~0.179) to offset squaring\n * underestimating mid-tones — holding AA across saturated brand colors where an\n * OKLCH-lightness threshold collapses to ≈1:1 (OKLCH L tracks hue, not\n * luminance: a mid red reads \"light\" yet needs white text). --text-color lets\n * consumers force a specific brand text color, overriding both paths.\n */\n --_on: calc(\n 255 *\n (\n 1 -\n sign(\n 0.2126 * (r / 255) * (r / 255) + 0.7152 * (g / 255) * (g / 255) + 0.0722 * (b / 255) *\n (b / 255) - 0.2\n )\n ) /\n 2\n );\n color: var(--text-color, rgb(from var(--color) var(--_on) var(--_on) var(--_on)));\n font-size: var(--_font-size);\n font-weight: 500;\n line-height: 1;\n text-transform: uppercase;\n padding: 0;\n cursor: inherit;\n}\n\n/* Primary path: native, guaranteed-contrast black/white. contrast-color() is\n * Baseline Newly Available (2026) — Safari 26+, Firefox 146+, Chrome/Edge 147+ —\n * and supersedes the luminance approximation above where supported. @supports (not\n * declaration order) is required: the value contains var(), so a non-supporting\n * browser would treat contrast-color() as invalid-at-computed-value-time and reset\n * to `inherit` rather than falling back to the calc() line. No bg fallback inside,\n * so a bare avatar (no --color) keeps inheriting --l-color-text-secondary.\n * --text-color still wins here, overriding contrast-color()'s choice.\n * (The earlier `… vs black, white to AA` extended syntax was dropped from the spec.) */\n@supports (color: contrast-color(red)) {\n .base {\n color: var(--text-color, contrast-color(var(--color)));\n }\n}\n\nbutton.base {\n cursor: pointer;\n}\n\nbutton.base:focus-visible {\n outline: 2px solid var(--l-focus-ring);\n outline-offset: 2px;\n}\n\nbutton.base:hover {\n box-shadow: inset 0 0 0 1.5px var(--l-color-border-interactive);\n}\n\n@container style(--appearance: circle) {\n .base {\n border-radius: 50%;\n }\n}\n","import { html, nothing, svg, unsafeCSS } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { html as staticHtml, literal } from 'lit/static-html.js';\nimport { property, state } from 'lit/decorators.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './avatar.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * Extracts up to two uppercase initials from a name: the first letter of the\n * first word, plus the first letter of the last word when there is more than one.\n *\n * Splits on whitespace rather than relying on a `\\b`/`\\w` regex, which only\n * recognises ASCII and mangles accented or non-ASCII names.\n *\n * @param name - The name to extract initials from. Defaults to an empty string.\n * @return The uppercased initials, or an empty string when `name` is blank.\n *\n * @example getInitials('John Doe') // 'JD'\n * @example getInitials('Markus Nösterer') // 'MN' (accents preserved)\n * @example getInitials('Cher') // 'C' (single word → one initial)\n * @example getInitials(' John Doe ') // 'JD' (extra whitespace ignored)\n * @example getInitials('') // ''\n */\nfunction getInitials(name: string = ''): string {\n const words = name.normalize('NFC').trim().split(/\\s+/).filter(Boolean);\n if (words.length === 0) return '';\n\n // Spreading iterates by code point, so an accented letter (or an astral\n // character like an emoji) is taken whole instead of a broken half.\n // oxlint-disable-next-line typescript/no-misused-spread -- code-point-safe first char is the intent\n const firstLetter = (word: string) => [...word][0];\n\n const first = firstLetter(words[0]);\n const last = words.length > 1 ? firstLetter(words[words.length - 1]) : '';\n return (first + last).toUpperCase();\n}\n\nconst defaultIcon = svg`<svg class=\"icon\" viewBox=\"0 0 24 24\" fill=\"currentColor\" aria-hidden=\"true\">\n <path d=\"M12,4A4,4 0 0,1 16,8A4,4 0 0,1 12,12A4,4 0 0,1 8,8A4,4 0 0,1 12,4M12,14C16.42,14 20,15.79 20,18V20H4V18C4,15.79 7.58,14 12,14Z\" />\n</svg>`;\n\n/**\n * @summary An avatar component for displaying user images, initials, or a default icon.\n * @customElement l-avatar\n *\n * @cssproperty [--size=40px] - Box size (any length). Set it inline (e.g. `style=\"--size: 20px\"`) for an arbitrary size beyond the `size` token scale; the font then follows proportionally. The `size` attribute sets it to the matching token.\n * @cssproperty --color - Background fill color for initials and the default icon.\n * @cssproperty --text-color - Initials/icon color over `--color`. Defaults to an auto-derived readable color; set it to enforce a specific brand color (overrides the automatic choice).\n * @cssproperty --appearance - Set to `circle` for a fully circular avatar (default is a rounded square).\n *\n * @csspart base - The avatar container that paints `--color`; style it (e.g. `color`) to override the auto-derived text color.\n *\n * @cssClass .l-avatar-group - Flex wrapper that overlaps a row of stacked avatars.\n */\nexport class Avatar extends LuxenElement {\n static styles = [hostStyles, styles];\n\n /** Image URL. Falls back to initials (then the default icon) if it fails to load. */\n @property()\n src = '';\n\n /** Name used as the accessible label and to derive the initials fallback. */\n @property()\n name = '';\n\n /** Avatar size: `xs`, `sm`, `md` (default), `lg`, or `xl`. */\n @property({ reflect: true })\n size = 'md';\n\n /** Count shown in the corner badge. `0` hides the badge. */\n @property({ type: Number })\n badge = 0;\n\n /** Render as a `<button>` with focus ring and hover states. */\n @property({ type: Boolean, reflect: true })\n interactive = false;\n\n @state() private _hasError = false;\n\n willUpdate(changed: Map<string, unknown>) {\n if (changed.has('src')) {\n this._hasError = false;\n }\n }\n\n connectedCallback() {\n super.connectedCallback();\n if (!this.interactive && !this.hasAttribute('role')) {\n this.setAttribute('role', 'img');\n }\n }\n\n updated() {\n // An interactive avatar renders a focusable <button>, and a role=\"img\" node\n // must stay a leaf in the accessibility tree — so the host carries no role\n // or label and the inner button holds the accessible name (see render()).\n if (this.interactive) {\n if (this.getAttribute('role') === 'img') this.removeAttribute('role');\n this.removeAttribute('aria-label');\n } else {\n if (!this.hasAttribute('role')) this.setAttribute('role', 'img');\n if (this.name) this.setAttribute('aria-label', this.name);\n }\n\n const isCircle = getComputedStyle(this).getPropertyValue('--appearance').trim() === 'circle';\n this.style.borderRadius = isCircle ? '50%' : '';\n }\n\n private get _tag() {\n return this.interactive ? literal`button` : literal`div`;\n }\n\n render() {\n const content =\n this.src && !this._hasError\n ? html`<img\n src=${this.src}\n alt=${this.name || ''}\n @error=${this._onError}\n />`\n : this.name\n ? html`<span class=\"initials\">${getInitials(this.name)}</span>`\n : html`<slot>${defaultIcon}</slot>`;\n\n return staticHtml`\n <${this._tag}\n class=\"base\"\n part=\"base\"\n type=${this.interactive ? 'button' : nothing}\n aria-label=${this.interactive && this.name ? this.name : nothing}\n >\n ${content}\n </${this._tag}>\n ${\n this.badge\n ? html`<div\n class=\"badge\"\n aria-hidden=\"true\"\n >\n ${this.badge}\n </div>`\n : nothing\n }\n `;\n }\n\n private _onError = () => {\n this._hasError = true;\n };\n}\n"],"mappings":"oUCOA,IAAM,EAAS,EAAU,4jEAAS,EAkBlC,SAAS,EAAY,EAAe,GAAY,CAC9C,IAAM,EAAQ,EAAK,UAAU,KAAK,CAAC,CAAC,KAAK,CAAC,CAAC,MAAM,KAAK,CAAC,CAAC,OAAO,OAAO,EACtE,GAAI,EAAM,SAAW,EAAG,MAAO,GAK/B,IAAM,EAAe,GAAiB,CAAC,GAAG,CAAI,CAAC,CAAC,GAIhD,OAFc,EAAY,EAAM,EAExB,GADK,EAAM,OAAS,EAAI,EAAY,EAAM,EAAM,OAAS,EAAE,EAAI,IAAA,CACjD,YAAY,CACpC,CAEA,IAAM,EAAc,CAAG;;QAiBV,EAAb,cAA4B,CAAa,wCAKjC,aAIC,aAIA,gBAIC,mBAIM,kBAEe,qBAqEJ,CACvB,KAAK,UAAY,EACnB,qBA7FgB,CAAC,EAAY,CAAM,EAwBnC,WAAW,EAA+B,CACpC,EAAQ,IAAI,KAAK,IACnB,KAAK,UAAY,GAErB,CAEA,mBAAoB,CAClB,MAAM,kBAAkB,EACpB,CAAC,KAAK,aAAe,CAAC,KAAK,aAAa,MAAM,GAChD,KAAK,aAAa,OAAQ,KAAK,CAEnC,CAEA,SAAU,CAIJ,KAAK,aACH,KAAK,aAAa,MAAM,IAAM,OAAO,KAAK,gBAAgB,MAAM,EACpE,KAAK,gBAAgB,YAAY,IAE5B,KAAK,aAAa,MAAM,GAAG,KAAK,aAAa,OAAQ,KAAK,EAC3D,KAAK,MAAM,KAAK,aAAa,aAAc,KAAK,IAAI,GAG1D,IAAM,EAAW,iBAAiB,IAAI,CAAC,CAAC,iBAAiB,cAAc,CAAC,CAAC,KAAK,IAAM,SACpF,KAAK,MAAM,aAAe,EAAW,MAAQ,EAC/C,CAEA,IAAY,MAAO,CACjB,OAAO,KAAK,YAAc,CAAO,SAAW,CAAO,KACrD,CAEA,QAAS,CACP,IAAM,EACJ,KAAK,KAAO,CAAC,KAAK,UACd,CAAI;kBACI,KAAK,IAAI;kBACT,KAAK,MAAQ,GAAG;qBACb,KAAK,SAAS;cAEzB,KAAK,KACH,CAAI,0BAA0B,EAAY,KAAK,IAAI,EAAE,SACrD,CAAI,SAAS,EAAY,SAEjC,MAAO,EAAU;SACZ,KAAK,KAAK;;;eAGJ,KAAK,YAAc,SAAW,EAAQ;qBAChC,KAAK,aAAe,KAAK,KAAO,KAAK,KAAO,EAAQ;;UAE/D,EAAQ;UACR,KAAK,KAAK;QAEZ,KAAK,MACD,CAAI;;;;gBAIA,KAAK,MAAM;oBAEf,EACL;KAEL,CAKF,KA3FG,EAAS,CAAA,EAAA,EAAA,UAAA,MAAA,IAAA,EAAA,KAIT,EAAS,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAIT,EAAS,CAAE,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,OAAA,IAAA,EAAA,KAI1B,EAAS,CAAE,KAAM,MAAO,CAAC,CAAA,EAAA,EAAA,UAAA,QAAA,IAAA,EAAA,KAIzB,EAAS,CAAE,KAAM,QAAS,QAAS,EAAK,CAAC,CAAA,EAAA,EAAA,UAAA,cAAA,IAAA,EAAA,KAGzC,EAAM,CAAA,EAAA,EAAA,UAAA,YAAA,IAAA,EAAA"}
|
|
@@ -12,6 +12,8 @@ import { LuxenElement } from '../../shared/luxen-element.js';
|
|
|
12
12
|
*
|
|
13
13
|
* @cssproperty --color - Text color.
|
|
14
14
|
*
|
|
15
|
+
* @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.
|
|
16
|
+
*
|
|
15
17
|
* @customElement l-dropdown-item
|
|
16
18
|
*/
|
|
17
19
|
export declare class DropdownItem extends LuxenElement {
|
|
@@ -23,6 +25,8 @@ export declare class DropdownItem extends LuxenElement {
|
|
|
23
25
|
accessor disabled: boolean;
|
|
24
26
|
/** The type of item: `normal` or `checkbox`. */
|
|
25
27
|
accessor type: 'normal' | 'checkbox';
|
|
28
|
+
/** Visual variant. `destructive` marks a delete-style action in red. */
|
|
29
|
+
accessor variant: 'destructive' | undefined;
|
|
26
30
|
/** Whether the checkbox item is checked. */
|
|
27
31
|
accessor checked: boolean;
|
|
28
32
|
/** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D
|
|
1
|
+
{"version":3,"file":"dropdown-item.d.ts","sourceRoot":"","sources":["../../../src/html/elements/dropdown-item/dropdown-item.ts"],"names":[],"mappings":"AAAA,OAAO,EAA4B,KAAK,cAAc,EAAE,MAAM,KAAK,CAAC;AACpE,OAAO,EAAE,YAAY,EAAE,MAAM,+BAA+B,CAAC;AAU7D;;;;;;;;;;;;;;;GAeG;AACH,qBAAa,YAAa,SAAQ,YAAY;IAC5C,OAAgB,MAAM,4BAAwB;IAE9C,OAAO,CAAC,gBAAgB,CAIrB;IAEH,2CAA2C;IAE3C,QAAQ,CAAC,KAAK,SAAM;IAEpB,yBAAyB;IAEzB,QAAQ,CAAC,QAAQ,UAAS;IAE1B,gDAAgD;IAEhD,QAAQ,CAAC,IAAI,EAAE,QAAQ,GAAG,UAAU,CAAY;IAEhD,wEAAwE;IAExE,QAAQ,CAAC,OAAO,EAAE,aAAa,GAAG,SAAS,CAAa;IAExD,4CAA4C;IAE5C,QAAQ,CAAC,OAAO,UAAS;IAEzB,+EAA+E;IAE/E,QAAQ,CAAC,WAAW,UAAS;IAG7B,OAAO,CAAC,QAAQ,CAAC,WAAW,CAAS;IAErC,2DAA2D;IAC3D,IAAI,UAAU,IAAI,OAAO,CAExB;IAEQ,iBAAiB;IAU1B,OAAO,KAAK,UAAU,GAErB;IAED,2EAA2E;IAC3E,YAAY,IAAI,MAAM;IAUtB,uFAAuF;IACvF,eAAe,IAAI,YAAY,EAAE;IAQjC,oEAAoE;IACpE,WAAW;IAKX,4EAA4E;IAC5E,YAAY;IAKH,OAAO,CAAC,OAAO,EAAE,cAAc,CAAC,IAAI,CAAC;IAM9C,OAAO,CAAC,YAAY;YAKN,wBAAwB;IA2BtC,OAAO,CAAC,oBAAoB,CAK1B;IAEO,MAAM;CAiEhB"}
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{box-sizing:border-box;cursor:pointer;min-block-size:36px;color:var(--l-color-text-primary);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r
|
|
1
|
+
import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.js";import{i as n,r,t as i}from"../../chunks/decorate.js";import{a,t as o}from"../../chunks/lit-html.js";import{LuxenElement as s}from"../../shared/luxen-element.js";import c from"../../shared/styles/host.styles.js";import{PopoverController as l}from"../../shared/controllers/popover.js";var u=t(`:host{display:block}:host([disabled]){pointer-events:none;opacity:.5}.item{box-sizing:border-box;cursor:pointer;min-block-size:36px;color:var(--l-color-text-primary);white-space:nowrap;text-align:start;border-radius:4px;outline:none;align-items:center;gap:8px;padding:.375rem .5rem;font-size:.875rem;line-height:1.5;display:flex}.item:focus-visible{background:var(--l-color-bg-state-hover);outline:2px solid var(--l-focus-ring);outline-offset:-2px}@media (hover:hover){.item:hover{background:var(--l-color-bg-state-hover)}}:host([submenu-open]) .item{background:var(--l-color-bg-state-hover)}:host([variant=destructive]) .item{color:var(--l-color-text-danger)}:host([variant=destructive]) .item:focus-visible{background:var(--l-color-bg-fill-danger-soft);outline-color:var(--l-color-text-danger)}@media (hover:hover){:host([variant=destructive]) .item:hover{background:var(--l-color-bg-fill-danger-soft)}}.check{flex-shrink:0;width:16px;display:flex}.submenu-indicator{background-color:currentColor;flex-shrink:0;block-size:16px;inline-size:16px;-webkit-mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat;mask:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E") 50%/contain no-repeat}:host(:dir(rtl)) .submenu-indicator{scale:-1 1}.submenu{inset:unset;box-sizing:border-box;width:max-content;padding:var(--padding,.25rem);border:1px solid var(--l-color-border-overlay);border-radius:var(--border-radius,6px);background:var(--background,var(--l-color-surface-overlay));color:var(--l-color-text-primary);box-shadow:var(--shadow,0 4px 6px -1px #00000014, 0 2px 4px -2px #0000000f);margin:0;font-size:.875rem;line-height:1.5;overflow:visible}slot[name=submenu]::slotted(hr){height:1px;margin-block:var(--padding,.25rem);margin-inline:calc(var(--padding,.25rem) * -1);background:var(--l-color-divider);border:0;display:block}:host(:not([checked])) .check svg{visibility:hidden}::slotted([slot=prefix]),::slotted([slot=suffix]){flex-shrink:0;display:flex}.label{flex:1}`),d=class extends s{constructor(...t){super(...t),this._submenuFloating=new l(this,{getTriggerElement:()=>this,getFloatingElement:()=>this._submenuEl,getArrowElement:()=>null}),this.#e=``,this.#t=!1,this.#n=`normal`,this.#r=void 0,this.#i=!1,this.#a=!1,this.#o=!1,this._onSubmenuSlotChange=t=>{let n=t.target;this._hasSubmenu=n.assignedElements().some(t=>t.tagName===e(`dropdown-item`).toUpperCase())}}static{this.styles=[c,u]}#e;get value(){return this.#e}set value(e){this.#e=e}#t;get disabled(){return this.#t}set disabled(e){this.#t=e}#n;get type(){return this.#n}set type(e){this.#n=e}#r;get variant(){return this.#r}set variant(e){this.#r=e}#i;get checked(){return this.#i}set checked(e){this.#i=e}#a;get submenuOpen(){return this.#a}set submenuOpen(e){this.#a=e}#o;get _hasSubmenu(){return this.#o}set _hasSubmenu(e){this.#o=e}get hasSubmenu(){return this._hasSubmenu}connectedCallback(){super.connectedCallback(),this.hasAttribute(`role`)||this.setAttribute(`role`,`none`)}get _submenuEl(){return this.shadowRoot.querySelector(`.submenu`)}getTextLabel(){let e=this.shadowRoot?.querySelector(`slot:not([name])`);return e?e.assignedNodes({flatten:!0}).map(e=>e.textContent??``).join(``).trim():(this.textContent??``).trim()}getSubmenuItems(){let t=this.shadowRoot?.querySelector(`slot[name="submenu"]`);return t?t.assignedElements().filter(t=>t.tagName===e(`dropdown-item`).toUpperCase()&&!t.disabled):[]}openSubmenu(){!this._hasSubmenu||this.disabled||this.submenuOpen||(this.submenuOpen=!0)}closeSubmenu(){for(let e of this.getSubmenuItems())e.closeSubmenu();this.submenuOpen&&=!1}updated(e){e.has(`submenuOpen`)&&this._handleSubmenuOpenChange()}_getDuration(e){let t=parseFloat(getComputedStyle(this).getPropertyValue(e));return Number.isNaN(t)?150:t}async _handleSubmenuOpenChange(){let e=this._submenuEl;if(!e)return;let t={placement:getComputedStyle(this).direction===`rtl`?`left-start`:`right-start`,distance:0,skidding:-parseFloat(getComputedStyle(e).paddingTop||`0`)};if(this.submenuOpen){if(!this.isConnected||(e.showPopover(),await this._submenuFloating.updatePosition(t),!this.submenuOpen))return;await this._submenuFloating.animateShow(e,this._getDuration(`--show-duration`)),this._submenuFloating.startPositioning(t)}else{if(!e.matches(`:popover-open`))return;this._submenuFloating.stopPositioning(),await this._submenuFloating.animateHide(e,this._getDuration(`--hide-duration`)),e.matches(`:popover-open`)&&e.hidePopover()}}render(){let e=this.type===`checkbox`;return a`
|
|
2
2
|
<div
|
|
3
3
|
class="item"
|
|
4
4
|
role=${e?`menuitemcheckbox`:`menuitem`}
|
|
@@ -54,5 +54,5 @@ import{tagName as e}from"../../registry.js";import{i as t}from"../../chunks/lit.
|
|
|
54
54
|
@slotchange=${this._onSubmenuSlotChange}
|
|
55
55
|
></slot>
|
|
56
56
|
</div>
|
|
57
|
-
`}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
|
|
57
|
+
`}};i([n()],d.prototype,`value`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`disabled`,null),i([n()],d.prototype,`type`,null),i([n({reflect:!0})],d.prototype,`variant`,null),i([n({type:Boolean,reflect:!0})],d.prototype,`checked`,null),i([n({type:Boolean,reflect:!0,attribute:`submenu-open`})],d.prototype,`submenuOpen`,null),i([r()],d.prototype,`_hasSubmenu`,null);export{d as DropdownItem};
|
|
58
58
|
//# sourceMappingURL=dropdown-item.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dropdown-item.js","names":[],"sources":["../../../src/html/elements/dropdown-item/dropdown-item.css?inline","../../../src/html/elements/dropdown-item/dropdown-item.ts"],"sourcesContent":[":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n box-sizing: border-box;\n min-block-size: 36px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n /* A ring distinct from the hover tint (which is the same low-contrast fill).\n Negative offset keeps the 2px ring inside the row so it never collides with\n the panel edge or a neighboring item. */\n background: var(--l-color-bg-state-hover);\n outline: 2px solid var(--l-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n/* Parent item stays highlighted while its submenu is open. */\n:host([submenu-open]) .item {\n background: var(--l-color-bg-state-hover);\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n/* Inline-end chevron marking an item that opens a submenu. */\n.submenu-indicator {\n inline-size: 16px;\n block-size: 16px;\n flex-shrink: 0;\n background-color: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n no-repeat center / contain;\n}\n\n:host(:dir(rtl)) .submenu-indicator {\n scale: -1 1;\n}\n\n/* Submenu panel — mirrors the l-dropdown panel. The public custom properties\n (--padding, --background, --border-radius, --shadow) inherit from the\n ancestor l-dropdown through the light DOM, so a themed dropdown themes its\n submenus too; their fallbacks repeat the dropdown defaults for standalone use. */\n.submenu {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding, 0.25rem);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius, 6px);\n background: var(--background, var(--l-color-surface-overlay));\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow, 0 4px 6px -1px rgb(0 0 0 / 8%), 0 2px 4px -2px rgb(0 0 0 / 6%));\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\nslot[name='submenu']::slotted(hr) {\n display: block;\n height: 1px;\n margin-block: var(--padding, 0.25rem);\n margin-inline: calc(var(--padding, 0.25rem) * -1);\n border: 0;\n background: var(--l-color-divider);\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property, state } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * A menu item for use inside `<l-dropdown>`.\n *\n * @slot - Label text.\n * @slot prefix - Leading content (e.g. icon).\n * @slot suffix - Trailing content.\n * @slot submenu - Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.\n *\n * @csspart submenu - The floating submenu panel.\n *\n * @cssproperty --color - Text color.\n *\n * @customElement l-dropdown-item\n */\nexport class DropdownItem extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _submenuFloating = new PopoverController(this, {\n getTriggerElement: () => this,\n getFloatingElement: () => this._submenuEl,\n getArrowElement: () => null,\n });\n\n /** The value associated with this item. */\n @property()\n accessor value = '';\n\n /** Disables the item. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** The type of item: `normal` or `checkbox`. */\n @property()\n accessor type: 'normal' | 'checkbox' = 'normal';\n\n /** Whether the checkbox item is checked. */\n @property({ type: Boolean, reflect: true })\n accessor checked = false;\n\n /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */\n @property({ type: Boolean, reflect: true, attribute: 'submenu-open' })\n accessor submenuOpen = false;\n\n @state()\n private accessor _hasSubmenu = false;\n\n /** Whether this item has nested `slot=\"submenu\"` items. */\n get hasSubmenu(): boolean {\n return this._hasSubmenu;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n // The host is a generic wrapper between the `role=\"menu\"` panel and the\n // `role=\"menuitem\"` row in its shadow root; `none` removes it from the\n // accessibility tree so the menu owns its menuitems directly (ARIA\n // required-owned-elements). The inner row keeps its menuitem role.\n // Guard preserves a consumer-set role.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'none');\n }\n\n private get _submenuEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('.submenu');\n }\n\n /** Returns the text label of this item (excludes nested submenu items). */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n\n /** Returns the enabled `l-dropdown-item` elements slotted into this item's submenu. */\n getSubmenuItems(): DropdownItem[] {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n if (!slot) return [];\n return (slot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n /** Opens this item's submenu panel. No-op without submenu items. */\n openSubmenu() {\n if (!this._hasSubmenu || this.disabled || this.submenuOpen) return;\n this.submenuOpen = true;\n }\n\n /** Closes this item's submenu panel, including any open nested submenus. */\n closeSubmenu() {\n for (const child of this.getSubmenuItems()) child.closeSubmenu();\n if (this.submenuOpen) this.submenuOpen = false;\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('submenuOpen')) {\n void this._handleSubmenuOpenChange();\n }\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n private async _handleSubmenuOpenChange() {\n const panel = this._submenuEl;\n if (!panel) return;\n\n const isRTL = getComputedStyle(this).direction === 'rtl';\n const posOpts = {\n placement: (isRTL ? 'left-start' : 'right-start') as Placement,\n distance: 0,\n // Pull the panel up by its own padding so its first item lines up with this item.\n skidding: -parseFloat(getComputedStyle(panel).paddingTop || '0'),\n };\n\n if (this.submenuOpen) {\n if (!this.isConnected) return;\n panel.showPopover();\n await this._submenuFloating.updatePosition(posOpts);\n if (!this.submenuOpen) return;\n await this._submenuFloating.animateShow(panel, this._getDuration('--show-duration'));\n this._submenuFloating.startPositioning(posOpts);\n } else {\n if (!panel.matches(':popover-open')) return;\n this._submenuFloating.stopPositioning();\n await this._submenuFloating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n }\n }\n\n private _onSubmenuSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement;\n this._hasSubmenu = slot\n .assignedElements()\n .some((el) => el.tagName === tagName('dropdown-item').toUpperCase());\n };\n\n override render() {\n const isCheckbox = this.type === 'checkbox';\n\n return html`\n <div\n class=\"item\"\n role=${isCheckbox ? 'menuitemcheckbox' : 'menuitem'}\n aria-checked=${isCheckbox ? String(this.checked) : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-haspopup=${this._hasSubmenu ? 'menu' : nothing}\n aria-expanded=${this._hasSubmenu ? String(this.submenuOpen) : nothing}\n aria-controls=${this._hasSubmenu ? 'submenu' : nothing}\n tabindex=\"-1\"\n >\n ${isCheckbox\n ? html`\n <span\n class=\"check\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n `\n : html` <slot name=\"prefix\"></slot> `}\n <span\n class=\"label\"\n id=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n ${this._hasSubmenu\n ? html`<span\n class=\"submenu-indicator\"\n aria-hidden=\"true\"\n ></span>`\n : nothing}\n </div>\n <div\n class=\"submenu\"\n part=\"submenu\"\n id=\"submenu\"\n popover=\"manual\"\n role=\"menu\"\n aria-labelledby=\"label\"\n >\n <slot\n name=\"submenu\"\n @slotchange=${this._onSubmenuSlotChange}\n ></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"dropdown-item.js","names":[],"sources":["../../../src/html/elements/dropdown-item/dropdown-item.css?inline","../../../src/html/elements/dropdown-item/dropdown-item.ts"],"sourcesContent":[":host {\n display: block;\n}\n\n:host([disabled]) {\n pointer-events: none;\n opacity: 0.5;\n}\n\n.item {\n display: flex;\n align-items: center;\n gap: 8px;\n box-sizing: border-box;\n min-block-size: 36px;\n padding: 0.375rem 0.5rem;\n cursor: pointer;\n outline: none;\n border-radius: 4px;\n font-size: 0.875rem;\n line-height: 1.5;\n color: var(--l-color-text-primary);\n white-space: nowrap;\n text-align: start;\n}\n\n.item:focus-visible {\n /* A ring distinct from the hover tint (which is the same low-contrast fill).\n Negative offset keeps the 2px ring inside the row so it never collides with\n the panel edge or a neighboring item. */\n background: var(--l-color-bg-state-hover);\n outline: 2px solid var(--l-focus-ring);\n outline-offset: -2px;\n}\n\n@media (hover: hover) {\n .item:hover {\n background: var(--l-color-bg-state-hover);\n }\n}\n\n/* Parent item stays highlighted while its submenu is open. */\n:host([submenu-open]) .item {\n background: var(--l-color-bg-state-hover);\n}\n\n/* Destructive variant — a delete-style action. Red label at rest, danger-tinted\n fill and focus ring on hover/focus so the highlight reads as \"danger\" rather\n than the neutral hover tint. */\n:host([variant='destructive']) .item {\n color: var(--l-color-text-danger);\n}\n\n:host([variant='destructive']) .item:focus-visible {\n background: var(--l-color-bg-fill-danger-soft);\n outline-color: var(--l-color-text-danger);\n}\n\n@media (hover: hover) {\n :host([variant='destructive']) .item:hover {\n background: var(--l-color-bg-fill-danger-soft);\n }\n}\n\n.check {\n display: flex;\n width: 16px;\n flex-shrink: 0;\n}\n\n/* Inline-end chevron marking an item that opens a submenu. */\n.submenu-indicator {\n inline-size: 16px;\n block-size: 16px;\n flex-shrink: 0;\n background-color: currentColor;\n mask: url(\"data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3E%3Cpath d='M6 4l4 4-4 4' fill='none' stroke='black' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E\")\n no-repeat center / contain;\n}\n\n:host(:dir(rtl)) .submenu-indicator {\n scale: -1 1;\n}\n\n/* Submenu panel — mirrors the l-dropdown panel. The public custom properties\n (--padding, --background, --border-radius, --shadow) inherit from the\n ancestor l-dropdown through the light DOM, so a themed dropdown themes its\n submenus too; their fallbacks repeat the dropdown defaults for standalone use. */\n.submenu {\n inset: unset;\n overflow: visible;\n box-sizing: border-box;\n width: max-content;\n padding: var(--padding, 0.25rem);\n margin: 0;\n border: 1px solid var(--l-color-border-overlay);\n border-radius: var(--border-radius, 6px);\n background: var(--background, var(--l-color-surface-overlay));\n color: var(--l-color-text-primary);\n box-shadow: var(--shadow, 0 4px 6px -1px rgb(0 0 0 / 8%), 0 2px 4px -2px rgb(0 0 0 / 6%));\n font-size: 0.875rem;\n line-height: 1.5;\n}\n\n/* Lightweight separator alternative to <l-divider> — same dimensions, no extra import. */\nslot[name='submenu']::slotted(hr) {\n display: block;\n height: 1px;\n margin-block: var(--padding, 0.25rem);\n margin-inline: calc(var(--padding, 0.25rem) * -1);\n border: 0;\n background: var(--l-color-divider);\n}\n\n:host(:not([checked])) .check svg {\n visibility: hidden;\n}\n\n::slotted([slot='prefix']),\n::slotted([slot='suffix']) {\n display: flex;\n flex-shrink: 0;\n}\n\n.label {\n flex: 1;\n}\n","import { html, nothing, unsafeCSS, type PropertyValues } from 'lit';\nimport { LuxenElement } from '../../shared/luxen-element.js';\nimport { property, state } from 'lit/decorators.js';\nimport type { Placement } from '@floating-ui/dom';\nimport { PopoverController } from '../../shared/controllers/popover.js';\nimport { tagName } from '../../registry.js';\nimport hostStyles from '../../shared/styles/host.styles.js';\nimport rawStyles from './dropdown-item.css?inline';\n\nconst styles = unsafeCSS(rawStyles);\n\n/**\n * A menu item for use inside `<l-dropdown>`.\n *\n * @slot - Label text.\n * @slot prefix - Leading content (e.g. icon).\n * @slot suffix - Trailing content.\n * @slot submenu - Nested `l-dropdown-item` elements rendered in a submenu panel anchored to this item. Drop an `<hr>` between them for a separator.\n *\n * @csspart submenu - The floating submenu panel.\n *\n * @cssproperty --color - Text color.\n *\n * @attribute variant - destructive — Renders the item as a destructive action (red), for delete-style entries.\n *\n * @customElement l-dropdown-item\n */\nexport class DropdownItem extends LuxenElement {\n static override styles = [hostStyles, styles];\n\n private _submenuFloating = new PopoverController(this, {\n getTriggerElement: () => this,\n getFloatingElement: () => this._submenuEl,\n getArrowElement: () => null,\n });\n\n /** The value associated with this item. */\n @property()\n accessor value = '';\n\n /** Disables the item. */\n @property({ type: Boolean, reflect: true })\n accessor disabled = false;\n\n /** The type of item: `normal` or `checkbox`. */\n @property()\n accessor type: 'normal' | 'checkbox' = 'normal';\n\n /** Visual variant. `destructive` marks a delete-style action in red. */\n @property({ reflect: true })\n accessor variant: 'destructive' | undefined = undefined;\n\n /** Whether the checkbox item is checked. */\n @property({ type: Boolean, reflect: true })\n accessor checked = false;\n\n /** Whether this item's submenu is open. Managed by the parent `l-dropdown`. */\n @property({ type: Boolean, reflect: true, attribute: 'submenu-open' })\n accessor submenuOpen = false;\n\n @state()\n private accessor _hasSubmenu = false;\n\n /** Whether this item has nested `slot=\"submenu\"` items. */\n get hasSubmenu(): boolean {\n return this._hasSubmenu;\n }\n\n override connectedCallback() {\n super.connectedCallback();\n // The host is a generic wrapper between the `role=\"menu\"` panel and the\n // `role=\"menuitem\"` row in its shadow root; `none` removes it from the\n // accessibility tree so the menu owns its menuitems directly (ARIA\n // required-owned-elements). The inner row keeps its menuitem role.\n // Guard preserves a consumer-set role.\n if (!this.hasAttribute('role')) this.setAttribute('role', 'none');\n }\n\n private get _submenuEl(): HTMLElement | null {\n return this.shadowRoot!.querySelector<HTMLElement>('.submenu');\n }\n\n /** Returns the text label of this item (excludes nested submenu items). */\n getTextLabel(): string {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot:not([name])');\n if (!slot) return (this.textContent ?? '').trim();\n return slot\n .assignedNodes({ flatten: true })\n .map((node) => node.textContent ?? '')\n .join('')\n .trim();\n }\n\n /** Returns the enabled `l-dropdown-item` elements slotted into this item's submenu. */\n getSubmenuItems(): DropdownItem[] {\n const slot = this.shadowRoot?.querySelector<HTMLSlotElement>('slot[name=\"submenu\"]');\n if (!slot) return [];\n return (slot.assignedElements() as DropdownItem[]).filter(\n (el) => el.tagName === tagName('dropdown-item').toUpperCase() && !el.disabled,\n );\n }\n\n /** Opens this item's submenu panel. No-op without submenu items. */\n openSubmenu() {\n if (!this._hasSubmenu || this.disabled || this.submenuOpen) return;\n this.submenuOpen = true;\n }\n\n /** Closes this item's submenu panel, including any open nested submenus. */\n closeSubmenu() {\n for (const child of this.getSubmenuItems()) child.closeSubmenu();\n if (this.submenuOpen) this.submenuOpen = false;\n }\n\n override updated(changed: PropertyValues<this>) {\n if (changed.has('submenuOpen')) {\n void this._handleSubmenuOpenChange();\n }\n }\n\n private _getDuration(prop: '--show-duration' | '--hide-duration'): number {\n const parsed = parseFloat(getComputedStyle(this).getPropertyValue(prop));\n return Number.isNaN(parsed) ? 150 : parsed;\n }\n\n private async _handleSubmenuOpenChange() {\n const panel = this._submenuEl;\n if (!panel) return;\n\n const isRTL = getComputedStyle(this).direction === 'rtl';\n const posOpts = {\n placement: (isRTL ? 'left-start' : 'right-start') as Placement,\n distance: 0,\n // Pull the panel up by its own padding so its first item lines up with this item.\n skidding: -parseFloat(getComputedStyle(panel).paddingTop || '0'),\n };\n\n if (this.submenuOpen) {\n if (!this.isConnected) return;\n panel.showPopover();\n await this._submenuFloating.updatePosition(posOpts);\n if (!this.submenuOpen) return;\n await this._submenuFloating.animateShow(panel, this._getDuration('--show-duration'));\n this._submenuFloating.startPositioning(posOpts);\n } else {\n if (!panel.matches(':popover-open')) return;\n this._submenuFloating.stopPositioning();\n await this._submenuFloating.animateHide(panel, this._getDuration('--hide-duration'));\n if (panel.matches(':popover-open')) panel.hidePopover();\n }\n }\n\n private _onSubmenuSlotChange = (e: Event) => {\n const slot = e.target as HTMLSlotElement;\n this._hasSubmenu = slot\n .assignedElements()\n .some((el) => el.tagName === tagName('dropdown-item').toUpperCase());\n };\n\n override render() {\n const isCheckbox = this.type === 'checkbox';\n\n return html`\n <div\n class=\"item\"\n role=${isCheckbox ? 'menuitemcheckbox' : 'menuitem'}\n aria-checked=${isCheckbox ? String(this.checked) : nothing}\n aria-disabled=${this.disabled ? 'true' : nothing}\n aria-haspopup=${this._hasSubmenu ? 'menu' : nothing}\n aria-expanded=${this._hasSubmenu ? String(this.submenuOpen) : nothing}\n aria-controls=${this._hasSubmenu ? 'submenu' : nothing}\n tabindex=\"-1\"\n >\n ${isCheckbox\n ? html`\n <span\n class=\"check\"\n aria-hidden=\"true\"\n >\n <svg\n width=\"16\"\n height=\"16\"\n viewBox=\"0 0 16 16\"\n fill=\"none\"\n >\n <path\n d=\"M3.5 8.5L6.5 11.5L12.5 4.5\"\n stroke=\"currentColor\"\n stroke-width=\"1.5\"\n stroke-linecap=\"round\"\n stroke-linejoin=\"round\"\n />\n </svg>\n </span>\n `\n : html` <slot name=\"prefix\"></slot> `}\n <span\n class=\"label\"\n id=\"label\"\n ><slot></slot\n ></span>\n <slot name=\"suffix\"></slot>\n ${this._hasSubmenu\n ? html`<span\n class=\"submenu-indicator\"\n aria-hidden=\"true\"\n ></span>`\n : nothing}\n </div>\n <div\n class=\"submenu\"\n part=\"submenu\"\n id=\"submenu\"\n popover=\"manual\"\n role=\"menu\"\n aria-labelledby=\"label\"\n >\n <slot\n name=\"submenu\"\n @slotchange=${this._onSubmenuSlotChange}\n ></slot>\n </div>\n `;\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/html/elements/segmented-control/index.ts"],"names":[],"mappings":"AAEA,cAAc,wBAAwB,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","names":[],"sources":["../../../src/html/elements/segmented-control/index.ts"],"sourcesContent":["import { define } from '../../define.js';\nimport { SegmentedControl } from './segmented-control.js';\nexport * from './segmented-control.js';\ndefine('segmented-control', SegmentedControl);\n"],"mappings":"mGAGA,EAAO,oBAAqB,CAAgB"}
|