@lmvz-ds/components 0.33.0 → 0.35.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (174) hide show
  1. package/CHANGELOG.md +13 -0
  2. package/cjs/async.util-BQ60l-WC.js +20 -0
  3. package/cjs/{ds.constants-CKeXfK-s.js → ds.constants-C6eKRAEu.js} +5 -1
  4. package/cjs/{element.util-Cq4y-_iX.js → element.util-Dbyc8yUG.js} +35 -0
  5. package/cjs/index.cjs.js +3 -1
  6. package/cjs/{list-keyboard-controller-StN6Yn8W.js → list-keyboard-controller-Dm5S_l0D.js} +1 -1
  7. package/cjs/lmvz-action-list.cjs.entry.js +11 -4
  8. package/cjs/lmvz-button-group.cjs.entry.js +3 -3
  9. package/cjs/lmvz-button_2.cjs.entry.js +3 -3
  10. package/cjs/lmvz-card.cjs.entry.js +1 -1
  11. package/cjs/lmvz-checkbox.cjs.entry.js +1 -1
  12. package/cjs/lmvz-chip.cjs.entry.js +2 -2
  13. package/cjs/lmvz-components.cjs.js +1 -1
  14. package/cjs/lmvz-header_2.cjs.entry.js +3 -3
  15. package/cjs/lmvz-input.cjs.entry.js +3 -2
  16. package/cjs/lmvz-link.cjs.entry.js +2 -2
  17. package/cjs/lmvz-menuitem.cjs.entry.js +1 -1
  18. package/cjs/lmvz-message.cjs.entry.js +1 -1
  19. package/cjs/lmvz-modal.cjs.entry.js +2 -2
  20. package/cjs/lmvz-popover.cjs.entry.js +1570 -0
  21. package/cjs/lmvz-radio.cjs.entry.js +2 -2
  22. package/cjs/lmvz-select.cjs.entry.js +2 -2
  23. package/cjs/lmvz-snackbar.cjs.entry.js +2 -2
  24. package/cjs/lmvz-spinner.cjs.entry.js +1 -1
  25. package/cjs/lmvz-tab.cjs.entry.js +2 -2
  26. package/cjs/lmvz-tabs.cjs.entry.js +4 -4
  27. package/cjs/lmvz-toggle.cjs.entry.js +4 -3
  28. package/cjs/loader.cjs.js +1 -1
  29. package/cjs/{stencil.util-DzvzXSwk.js → stencil.util-DxdYjVoH.js} +2 -10
  30. package/collection/api/ds.constants.js +3 -1
  31. package/collection/collection-manifest.json +1 -0
  32. package/collection/components/lmvz-action-list/lmvz-action-list.css +151 -39
  33. package/collection/components/lmvz-action-list/lmvz-action-list.js +9 -1
  34. package/collection/components/lmvz-button/lmvz-button.css +15 -19
  35. package/collection/components/lmvz-button/lmvz-button.js +1 -1
  36. package/collection/components/lmvz-button-group/lmvz-button-group.css +1 -2
  37. package/collection/components/lmvz-card/lmvz-card.css +20 -23
  38. package/collection/components/lmvz-checkbox/lmvz-checkbox.css +9 -12
  39. package/collection/components/lmvz-chip/lmvz-chip.css +20 -20
  40. package/collection/components/lmvz-header/lmvz-header.css +3 -3
  41. package/collection/components/lmvz-icon/lmvz-icon.css +1 -1
  42. package/collection/components/lmvz-input/lmvz-input.css +3 -3
  43. package/collection/components/lmvz-link/lmvz-link.css +6 -4
  44. package/collection/components/lmvz-menuitem/lmvz-menuitem.css +1 -2
  45. package/collection/components/lmvz-message/lmvz-message.css +5 -5
  46. package/collection/components/lmvz-modal/lmvz-modal.css +6 -7
  47. package/collection/components/lmvz-popover/lmvz-popover.css +46 -0
  48. package/collection/components/lmvz-popover/lmvz-popover.js +470 -0
  49. package/collection/components/lmvz-radio/lmvz-radio.css +10 -12
  50. package/collection/components/lmvz-radio/lmvz-radio.js +1 -1
  51. package/collection/components/lmvz-select/lmvz-select.css +5 -5
  52. package/collection/components/lmvz-select/lmvz-select.js +1 -1
  53. package/collection/components/lmvz-snackbar/lmvz-snackbar.css +13 -13
  54. package/collection/components/lmvz-snackbar/lmvz-snackbar.js +1 -1
  55. package/collection/components/lmvz-spinner/lmvz-spinner.js +1 -1
  56. package/collection/components/lmvz-tab/lmvz-tab.css +9 -9
  57. package/collection/components/lmvz-tab/lmvz-tab.js +1 -1
  58. package/collection/components/lmvz-tabs/lmvz-tabs.css +2 -2
  59. package/collection/components/lmvz-tabs/lmvz-tabs.js +1 -1
  60. package/collection/components/lmvz-toggle/lmvz-toggle.css +4 -5
  61. package/collection/components/lmvz-toggle/lmvz-toggle.js +1 -1
  62. package/collection/integration/header-integration/header-integration.js +1 -1
  63. package/collection/utils/async/async.util.js +6 -0
  64. package/collection/utils/element/element.util.js +33 -0
  65. package/components/index.d.ts +2 -0
  66. package/components/index.d.ts.bak +2 -0
  67. package/components/index.js +1 -1
  68. package/components/lmvz-action-list.js +1 -1
  69. package/components/lmvz-button-group.js +1 -1
  70. package/components/lmvz-button.js +1 -1
  71. package/components/lmvz-card.js +1 -1
  72. package/components/lmvz-checkbox.js +1 -1
  73. package/components/lmvz-chip.js +1 -1
  74. package/components/lmvz-header.js +1 -1
  75. package/components/lmvz-icon.js +1 -1
  76. package/components/lmvz-input.js +1 -1
  77. package/components/lmvz-link.js +1 -1
  78. package/components/lmvz-menuitem.js +1 -1
  79. package/components/lmvz-message.js +1 -1
  80. package/components/lmvz-modal.js +1 -1
  81. package/components/lmvz-popover.d.ts +11 -0
  82. package/components/lmvz-popover.d.ts.bak +11 -0
  83. package/components/lmvz-popover.js +1 -0
  84. package/components/lmvz-radio.js +1 -1
  85. package/components/lmvz-select.js +1 -1
  86. package/components/lmvz-snackbar.js +1 -1
  87. package/components/lmvz-spinner.js +1 -1
  88. package/components/lmvz-tab.js +1 -1
  89. package/components/lmvz-tabs.js +1 -1
  90. package/components/lmvz-toggle.js +1 -1
  91. package/components/p-0ctLESas.js +1 -0
  92. package/components/p-BhSTYZC6.js +1 -0
  93. package/components/{p-CxfT9IA1.js → p-CK3Ixl1K.js} +1 -1
  94. package/components/{p-B4m1n4et.js → p-D-2gj6LP.js} +1 -1
  95. package/components/{p-Rf10bzJY.js → p-D7S1WiEC.js} +1 -1
  96. package/components/p-D9nUtvTm.js +1 -0
  97. package/components/{p-De-xQqdU.js → p-DbIm98qX.js} +1 -1
  98. package/components/p-JtkmlmeY.js +1 -0
  99. package/esm/async.util-D9nUtvTm.js +17 -0
  100. package/esm/{ds.constants-DuEtnKRj.js → ds.constants-0ctLESas.js} +4 -2
  101. package/esm/{element.util-CxfT9IA1.js → element.util-B0eCxdyI.js} +34 -1
  102. package/esm/index.js +1 -1
  103. package/esm/{list-keyboard-controller-BKa9Kd2t.js → list-keyboard-controller-tle6IRkv.js} +1 -1
  104. package/esm/lmvz-action-list.entry.js +11 -4
  105. package/esm/lmvz-button-group.entry.js +3 -3
  106. package/esm/lmvz-button_2.entry.js +3 -3
  107. package/esm/lmvz-card.entry.js +1 -1
  108. package/esm/lmvz-checkbox.entry.js +1 -1
  109. package/esm/lmvz-chip.entry.js +2 -2
  110. package/esm/lmvz-components.js +1 -1
  111. package/esm/lmvz-header_2.entry.js +3 -3
  112. package/esm/lmvz-input.entry.js +3 -2
  113. package/esm/lmvz-link.entry.js +2 -2
  114. package/esm/lmvz-menuitem.entry.js +1 -1
  115. package/esm/lmvz-message.entry.js +1 -1
  116. package/esm/lmvz-modal.entry.js +2 -2
  117. package/esm/lmvz-popover.entry.js +1568 -0
  118. package/esm/lmvz-radio.entry.js +2 -2
  119. package/esm/lmvz-select.entry.js +2 -2
  120. package/esm/lmvz-snackbar.entry.js +2 -2
  121. package/esm/lmvz-spinner.entry.js +1 -1
  122. package/esm/lmvz-tab.entry.js +2 -2
  123. package/esm/lmvz-tabs.entry.js +4 -4
  124. package/esm/lmvz-toggle.entry.js +4 -3
  125. package/esm/loader.js +1 -1
  126. package/esm/{stencil.util-CJRqhXjK.js → stencil.util-wGFRYLwZ.js} +1 -9
  127. package/hydrate/index.js +1660 -33
  128. package/hydrate/index.mjs +1660 -33
  129. package/lmvz-components/index.esm.js +1 -1
  130. package/lmvz-components/lmvz-components.esm.js +1 -1
  131. package/lmvz-components/{p-47824515.entry.js → p-0c05f24b.entry.js} +1 -1
  132. package/lmvz-components/p-0ctLESas.js +1 -0
  133. package/lmvz-components/{p-8be4dbb7.entry.js → p-1d2c11ea.entry.js} +1 -1
  134. package/lmvz-components/{p-3da087f4.entry.js → p-49e98627.entry.js} +1 -1
  135. package/lmvz-components/p-4c68d21a.entry.js +1 -0
  136. package/lmvz-components/p-50063516.entry.js +1 -0
  137. package/lmvz-components/{p-24125bcf.entry.js → p-5bbbcfc0.entry.js} +1 -1
  138. package/lmvz-components/p-76be3fc9.entry.js +1 -0
  139. package/lmvz-components/{p-703b10b5.entry.js → p-854f1c72.entry.js} +1 -1
  140. package/lmvz-components/p-886e42c7.entry.js +1 -0
  141. package/lmvz-components/{p-6140866f.entry.js → p-8fb4c347.entry.js} +1 -1
  142. package/lmvz-components/p-9f8fd077.entry.js +1 -0
  143. package/lmvz-components/{p-CxfT9IA1.js → p-B0eCxdyI.js} +1 -1
  144. package/lmvz-components/p-BhSTYZC6.js +1 -0
  145. package/lmvz-components/p-D9nUtvTm.js +1 -0
  146. package/lmvz-components/{p-B4m1n4et.js → p-DuLiExhX.js} +1 -1
  147. package/lmvz-components/{p-b4aa0978.entry.js → p-a4ef8e55.entry.js} +1 -1
  148. package/lmvz-components/p-bc237ab7.entry.js +1 -0
  149. package/lmvz-components/{p-3a644b99.entry.js → p-c082390b.entry.js} +1 -1
  150. package/lmvz-components/{p-b79af711.entry.js → p-c2544495.entry.js} +1 -1
  151. package/lmvz-components/p-c494bf8f.entry.js +1 -0
  152. package/lmvz-components/{p-48a7b033.entry.js → p-c87e2281.entry.js} +1 -1
  153. package/lmvz-components/{p-00ccaea4.entry.js → p-cb75c951.entry.js} +1 -1
  154. package/lmvz-components/{p-44781f01.entry.js → p-e0432f54.entry.js} +1 -1
  155. package/lmvz-components/{p-25a78ead.entry.js → p-ecbd9e93.entry.js} +1 -1
  156. package/manifest.json +386 -3
  157. package/package.json +5 -1
  158. package/types/api/ds.constants.d.ts +3 -1
  159. package/types/api/ds.types.d.ts +7 -0
  160. package/types/components/lmvz-popover/lmvz-popover.d.ts +37 -0
  161. package/types/components.d.ts +108 -1
  162. package/types/utils/async/async.util.d.ts +3 -0
  163. package/types/utils/element/element.util.d.ts +2 -0
  164. package/components/p-Bld_XR3X.js +0 -1
  165. package/components/p-CJRqhXjK.js +0 -1
  166. package/components/p-DuEtnKRj.js +0 -1
  167. package/lmvz-components/p-4072b2fb.entry.js +0 -1
  168. package/lmvz-components/p-8c7d9eaf.entry.js +0 -1
  169. package/lmvz-components/p-92d02c56.entry.js +0 -1
  170. package/lmvz-components/p-933e6033.entry.js +0 -1
  171. package/lmvz-components/p-CJRqhXjK.js +0 -1
  172. package/lmvz-components/p-DuEtnKRj.js +0 -1
  173. package/lmvz-components/p-d9619a4f.entry.js +0 -1
  174. package/lmvz-components/p-e1758128.entry.js +0 -1
@@ -0,0 +1,470 @@
1
+ import { Host, h } from "@stencil/core";
2
+ import { computePosition, flip, offset, shift } from "@floating-ui/dom";
3
+ import { isAriaValidationEnabled } from "../../utils/aria/aria-validation-controller";
4
+ import { waitFrame } from "../../utils/async/async.util";
5
+ import { canReceiveFocus, getDeepActiveElement, isInteractiveElement, walkShadowDOMUntil } from "../../utils/element/element.util";
6
+ export class LmvzPopover {
7
+ el;
8
+ closureController = new AbortController();
9
+ containerEl;
10
+ slotEl;
11
+ previouslyFocusedElement = null;
12
+ open = false;
13
+ anchor;
14
+ placement = 'bottom-start';
15
+ size = 'sm';
16
+ autoFocus = true;
17
+ label;
18
+ lmvzOpen;
19
+ lmvzClose;
20
+ lmvzCancel;
21
+ async show() {
22
+ this.open = true;
23
+ }
24
+ async hide() {
25
+ this.triggerClose();
26
+ }
27
+ async toggle() {
28
+ if (this.open) {
29
+ await this.hide();
30
+ }
31
+ else {
32
+ await this.show();
33
+ }
34
+ }
35
+ componentDidLoad() {
36
+ autoAssignId(this.el);
37
+ validatePopoverAria(this.el, this.label, this.anchor, this.resolveAnchor());
38
+ }
39
+ handleOpenChange(isOpen) {
40
+ if (isOpen) {
41
+ this.onOpen();
42
+ }
43
+ else {
44
+ this.onClose();
45
+ }
46
+ }
47
+ handleAnchorChange() {
48
+ if (this.open) {
49
+ this.updatePosition();
50
+ }
51
+ }
52
+ disconnectedCallback() {
53
+ this.removeCloseHandlers();
54
+ }
55
+ resolveAnchor() {
56
+ if (!this.anchor)
57
+ return null;
58
+ if (typeof this.anchor === 'string') {
59
+ return document.getElementById(this.anchor);
60
+ }
61
+ return this.anchor;
62
+ }
63
+ async updatePosition() {
64
+ const anchorEl = this.resolveAnchor();
65
+ const container = this.containerEl;
66
+ if (!anchorEl || !container)
67
+ return;
68
+ const placement = toFloatingUiPlacement(this.placement);
69
+ const { x, y } = await computePosition(anchorEl, container, {
70
+ placement,
71
+ middleware: [offset(4), flip(), shift({ padding: 8 })],
72
+ });
73
+ container.style.left = `${x}px`;
74
+ container.style.top = `${y}px`;
75
+ }
76
+ async onOpen() {
77
+ this.previouslyFocusedElement = getDeepActiveElement(document);
78
+ this.setupCloseHandlers();
79
+ console.log('onOpen');
80
+ await waitFrame();
81
+ await this.updatePosition();
82
+ autoAssignId(this.el);
83
+ this.applyAnchorAriaOpen();
84
+ this.lmvzOpen.emit();
85
+ await waitFrame();
86
+ if (this.autoFocus) {
87
+ this.focusFirst();
88
+ }
89
+ }
90
+ onClose() {
91
+ this.removeCloseHandlers();
92
+ this.applyAnchorAriaClose();
93
+ this.restoreFocus();
94
+ this.lmvzClose.emit();
95
+ }
96
+ applyAnchorAriaOpen() {
97
+ const anchorEl = this.resolveAnchor();
98
+ if (!anchorEl)
99
+ return;
100
+ if (!anchorEl.hasAttribute('aria-haspopup')) {
101
+ anchorEl.setAttribute('aria-haspopup', 'dialog');
102
+ }
103
+ anchorEl.setAttribute('aria-expanded', 'true');
104
+ anchorEl.setAttribute('aria-controls', this.el.getAttribute('id'));
105
+ }
106
+ applyAnchorAriaClose() {
107
+ const anchorEl = this.resolveAnchor();
108
+ if (!anchorEl)
109
+ return;
110
+ anchorEl.setAttribute('aria-expanded', 'false');
111
+ }
112
+ focusFirst() {
113
+ const { slotEl } = this;
114
+ if (!slotEl)
115
+ return;
116
+ const assigned = slotEl.assignedElements({ flatten: true });
117
+ for (const el of assigned) {
118
+ const focusable = walkShadowDOMUntil(el, canReceiveFocus);
119
+ if (focusable) {
120
+ focusable.focus();
121
+ return;
122
+ }
123
+ }
124
+ }
125
+ restoreFocus() {
126
+ const el = this.previouslyFocusedElement;
127
+ if (el?.isConnected) {
128
+ el.focus();
129
+ }
130
+ this.previouslyFocusedElement = null;
131
+ }
132
+ triggerClose() {
133
+ const event = this.lmvzCancel.emit();
134
+ if (!event.defaultPrevented) {
135
+ this.open = false;
136
+ }
137
+ }
138
+ setupCloseHandlers() {
139
+ const options = { signal: this.closureController.signal };
140
+ document.addEventListener('mousedown', (event) => {
141
+ if (!this.open)
142
+ return;
143
+ if (this.el.contains(event.target))
144
+ return;
145
+ this.triggerClose();
146
+ }, options);
147
+ document.addEventListener('keydown', (event) => {
148
+ if (!this.open)
149
+ return;
150
+ if (event.key !== 'Escape')
151
+ return;
152
+ this.triggerClose();
153
+ }, options);
154
+ }
155
+ removeCloseHandlers() {
156
+ this.closureController.abort();
157
+ }
158
+ render() {
159
+ return (h(Host, { key: '3dd462a66f17c1617e44787107379d9e4d8ca321' }, h("div", { key: '27106bc8f3169eb07e0d671635074ddb53feb652', role: "dialog", part: "container", class: `container lmvz-wrapper-card-${this.size}`, "aria-label": this.label || undefined, ref: (el) => (this.containerEl = el) }, h("slot", { key: '3401e621d999a225aeb7cb23fa4f5412544a6dab', ref: (el) => (this.slotEl = el) }))));
160
+ }
161
+ static get is() { return "lmvz-popover"; }
162
+ static get encapsulation() { return "shadow"; }
163
+ static get originalStyleUrls() {
164
+ return {
165
+ "$": ["lmvz-popover.css"]
166
+ };
167
+ }
168
+ static get styleUrls() {
169
+ return {
170
+ "$": ["lmvz-popover.css"]
171
+ };
172
+ }
173
+ static get properties() {
174
+ return {
175
+ "open": {
176
+ "type": "boolean",
177
+ "mutable": true,
178
+ "complexType": {
179
+ "original": "boolean",
180
+ "resolved": "boolean",
181
+ "references": {}
182
+ },
183
+ "required": false,
184
+ "optional": false,
185
+ "docs": {
186
+ "tags": [{
187
+ "name": "default",
188
+ "text": "false"
189
+ }],
190
+ "text": "Whether the popover is open."
191
+ },
192
+ "getter": false,
193
+ "setter": false,
194
+ "reflect": true,
195
+ "attribute": "open",
196
+ "defaultValue": "false"
197
+ },
198
+ "anchor": {
199
+ "type": "string",
200
+ "mutable": false,
201
+ "complexType": {
202
+ "original": "string | HTMLElement",
203
+ "resolved": "HTMLElement | string | undefined",
204
+ "references": {
205
+ "HTMLElement": {
206
+ "location": "global",
207
+ "id": "global::HTMLElement"
208
+ }
209
+ }
210
+ },
211
+ "required": false,
212
+ "optional": true,
213
+ "docs": {
214
+ "tags": [],
215
+ "text": "The anchor element or its id. Used for positioning."
216
+ },
217
+ "getter": false,
218
+ "setter": false,
219
+ "reflect": false,
220
+ "attribute": "anchor"
221
+ },
222
+ "placement": {
223
+ "type": "string",
224
+ "mutable": false,
225
+ "complexType": {
226
+ "original": "Popover.Placement",
227
+ "resolved": "\"bottom\" | \"bottom-end\" | \"bottom-start\" | \"end\" | \"start\" | \"top\" | \"top-end\" | \"top-start\"",
228
+ "references": {
229
+ "Popover": {
230
+ "location": "import",
231
+ "path": "../../api",
232
+ "id": "src/api/index.d.ts::Popover",
233
+ "referenceLocation": "Popover"
234
+ }
235
+ }
236
+ },
237
+ "required": false,
238
+ "optional": false,
239
+ "docs": {
240
+ "tags": [{
241
+ "name": "default",
242
+ "text": "'bottom-start'"
243
+ }],
244
+ "text": "Preferred placement relative to the anchor."
245
+ },
246
+ "getter": false,
247
+ "setter": false,
248
+ "reflect": false,
249
+ "attribute": "placement",
250
+ "defaultValue": "'bottom-start'"
251
+ },
252
+ "size": {
253
+ "type": "string",
254
+ "mutable": false,
255
+ "complexType": {
256
+ "original": "Popover.Size",
257
+ "resolved": "\"lg\" | \"md\" | \"sm\"",
258
+ "references": {
259
+ "Popover": {
260
+ "location": "import",
261
+ "path": "../../api",
262
+ "id": "src/api/index.d.ts::Popover",
263
+ "referenceLocation": "Popover"
264
+ }
265
+ }
266
+ },
267
+ "required": false,
268
+ "optional": false,
269
+ "docs": {
270
+ "tags": [{
271
+ "name": "default",
272
+ "text": "'sm'"
273
+ }],
274
+ "text": "Controls the size variant of the card surface."
275
+ },
276
+ "getter": false,
277
+ "setter": false,
278
+ "reflect": false,
279
+ "attribute": "size",
280
+ "defaultValue": "'sm'"
281
+ },
282
+ "autoFocus": {
283
+ "type": "boolean",
284
+ "mutable": false,
285
+ "complexType": {
286
+ "original": "boolean",
287
+ "resolved": "boolean",
288
+ "references": {}
289
+ },
290
+ "required": false,
291
+ "optional": false,
292
+ "docs": {
293
+ "tags": [{
294
+ "name": "default",
295
+ "text": "true"
296
+ }],
297
+ "text": "Whether to move focus to the first focusable element on open."
298
+ },
299
+ "getter": false,
300
+ "setter": false,
301
+ "reflect": false,
302
+ "attribute": "auto-focus",
303
+ "defaultValue": "true"
304
+ },
305
+ "label": {
306
+ "type": "string",
307
+ "mutable": false,
308
+ "complexType": {
309
+ "original": "string",
310
+ "resolved": "string | undefined",
311
+ "references": {}
312
+ },
313
+ "required": false,
314
+ "optional": true,
315
+ "docs": {
316
+ "tags": [],
317
+ "text": "Accessible label for the popover container."
318
+ },
319
+ "getter": false,
320
+ "setter": false,
321
+ "reflect": false,
322
+ "attribute": "label"
323
+ }
324
+ };
325
+ }
326
+ static get events() {
327
+ return [{
328
+ "method": "lmvzOpen",
329
+ "name": "lmvzOpen",
330
+ "bubbles": true,
331
+ "cancelable": true,
332
+ "composed": true,
333
+ "docs": {
334
+ "tags": [],
335
+ "text": "Fired after the popover opens."
336
+ },
337
+ "complexType": {
338
+ "original": "void",
339
+ "resolved": "void",
340
+ "references": {}
341
+ }
342
+ }, {
343
+ "method": "lmvzClose",
344
+ "name": "lmvzClose",
345
+ "bubbles": true,
346
+ "cancelable": true,
347
+ "composed": true,
348
+ "docs": {
349
+ "tags": [],
350
+ "text": "Fired after the popover closes."
351
+ },
352
+ "complexType": {
353
+ "original": "void",
354
+ "resolved": "void",
355
+ "references": {}
356
+ }
357
+ }, {
358
+ "method": "lmvzCancel",
359
+ "name": "lmvzCancel",
360
+ "bubbles": true,
361
+ "cancelable": true,
362
+ "composed": true,
363
+ "docs": {
364
+ "tags": [],
365
+ "text": "Fired before light-dismiss or Escape close.\nCall `event.preventDefault()` to veto the close."
366
+ },
367
+ "complexType": {
368
+ "original": "void",
369
+ "resolved": "void",
370
+ "references": {}
371
+ }
372
+ }];
373
+ }
374
+ static get methods() {
375
+ return {
376
+ "show": {
377
+ "complexType": {
378
+ "signature": "() => Promise<void>",
379
+ "parameters": [],
380
+ "references": {
381
+ "Promise": {
382
+ "location": "global",
383
+ "id": "global::Promise"
384
+ }
385
+ },
386
+ "return": "Promise<void>"
387
+ },
388
+ "docs": {
389
+ "text": "Opens the popover.",
390
+ "tags": []
391
+ }
392
+ },
393
+ "hide": {
394
+ "complexType": {
395
+ "signature": "() => Promise<void>",
396
+ "parameters": [],
397
+ "references": {
398
+ "Promise": {
399
+ "location": "global",
400
+ "id": "global::Promise"
401
+ }
402
+ },
403
+ "return": "Promise<void>"
404
+ },
405
+ "docs": {
406
+ "text": "Closes the popover, firing lmvzCancel first. If cancelled, the popover stays open.",
407
+ "tags": []
408
+ }
409
+ },
410
+ "toggle": {
411
+ "complexType": {
412
+ "signature": "() => Promise<void>",
413
+ "parameters": [],
414
+ "references": {
415
+ "Promise": {
416
+ "location": "global",
417
+ "id": "global::Promise"
418
+ }
419
+ },
420
+ "return": "Promise<void>"
421
+ },
422
+ "docs": {
423
+ "text": "Toggles the popover open state.",
424
+ "tags": []
425
+ }
426
+ }
427
+ };
428
+ }
429
+ static get elementRef() { return "el"; }
430
+ static get watchers() {
431
+ return [{
432
+ "propName": "open",
433
+ "methodName": "handleOpenChange"
434
+ }, {
435
+ "propName": "anchor",
436
+ "methodName": "handleAnchorChange"
437
+ }];
438
+ }
439
+ }
440
+ let popoverIdCounter = 0;
441
+ function autoAssignId(hostEl) {
442
+ if (!hostEl.getAttribute('id')) {
443
+ hostEl.setAttribute('id', `lmvz-popover-${popoverIdCounter++}`);
444
+ }
445
+ }
446
+ function toFloatingUiPlacement(placement) {
447
+ if (placement === 'start')
448
+ return 'left';
449
+ if (placement === 'end')
450
+ return 'right';
451
+ return placement;
452
+ }
453
+ function validatePopoverAria(hostEl, label, anchorProp, anchorEl) {
454
+ if (!isAriaValidationEnabled())
455
+ return;
456
+ const hasAriaLabel = !!label;
457
+ const hasAriaLabelledBy = hostEl.hasAttribute('aria-labelledby');
458
+ if (!hasAriaLabel && !hasAriaLabelledBy) {
459
+ console.warn('[lmvz-popover] Host element has no accessible label. Provide a `label` prop or an `aria-labelledby` attribute pointing to a valid element.');
460
+ }
461
+ if (!anchorProp)
462
+ return;
463
+ if (!anchorEl) {
464
+ console.warn('[lmvz-popover] Anchor prop is set but resolved to null. Ensure the anchor element exists in the DOM when the component loads.');
465
+ return;
466
+ }
467
+ if (!isInteractiveElement(anchorEl)) {
468
+ console.warn('[lmvz-popover] Anchor element is not interactive. Ensure the anchor is a button, link, input, select, textarea, has a `tabindex` attribute, or has an ARIA role that implies interactivity (button, link, menuitem, option, combobox, listbox, checkbox, radio, switch, tab).');
469
+ }
470
+ }
@@ -45,9 +45,9 @@
45
45
 
46
46
  --radio-bg: var(--lmvz-semantic-color-surface-input-primary, #ffffff);
47
47
  --radio-border-color: var(--lmvz-semantic-color-border-default, #e0e0e0);
48
- --radio-border-color-hover: var(--lmvz-semantic-color-border-hover, #c7c7c7);
48
+ --radio-border-color-hover: var(--lmvz-semantic-color-border-hover, #a1a1a1);
49
49
  --radio-border-color-checked: var(--lmvz-semantic-color-border-active, #0f8acc);
50
- --radio-border-color-error: var(--lmvz-semantic-color-status-on-danger, #e52a31);
50
+ --radio-border-color-error: var(--lmvz-semantic-color-status-on-danger, #a31419);
51
51
  --radio-dot-color: var(--lmvz-semantic-color-border-active, #0f8acc);
52
52
 
53
53
  --radio-wrapper-bg-hover: var(--lmvz-semantic-color-int-tertiary-hover, #f0f0f0);
@@ -55,7 +55,7 @@
55
55
 
56
56
  --radio-label-color: var(--lmvz-semantic-color-on-surface-primary, #000000);
57
57
  --radio-label-color-checked: var(--lmvz-semantic-color-status-on-active, #0e7ab4);
58
- --radio-helper-color: var(--lmvz-semantic-color-on-surface-secondary, #7a7a7a);
58
+ --radio-helper-color: var(--lmvz-semantic-color-on-surface-secondary, #545454);
59
59
 
60
60
  --radio-easing: var(--lmvz-global-easing-default, ease);
61
61
  --radio-duration: 0.2s;
@@ -63,8 +63,8 @@
63
63
  display: flex;
64
64
  align-items: center;
65
65
  gap: var(--lmvz-component-input-gap-md, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
66
- padding-block: var(--lmvz-dimension-2-8, clamp(0.13rem, 0.03rem + 0.39vw, 0.5rem));
67
- padding-inline: var(--lmvz-dimension-8-12, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
66
+ padding-block: var(--lmvz-dimension-8-2, clamp(0.13rem, 0.03rem + 0.39vw, 0.5rem));
67
+ padding-inline: var(--lmvz-dimension-12-8, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
68
68
  border-radius: var(--lmvz-semantic-border-radius-round, 999px);
69
69
  overflow: clip;
70
70
  cursor: pointer;
@@ -74,8 +74,8 @@
74
74
  }
75
75
  input {
76
76
  position: absolute;
77
- inset-block-start: var(--lmvz-dimension-2-8, clamp(0.13rem, 0.03rem + 0.39vw, 0.5rem));
78
- inset-inline-start: var(--lmvz-dimension-4-10, clamp(0.25rem, 0.16rem + 0.39vw, 0.63rem));
77
+ inset-block-start: var(--lmvz-dimension-8-2, clamp(0.13rem, 0.03rem + 0.39vw, 0.5rem));
78
+ inset-inline-start: var(--lmvz-dimension-10-4, clamp(0.25rem, 0.16rem + 0.39vw, 0.63rem));
79
79
  opacity: 0;
80
80
  width: var(--radio-circle-size);
81
81
  height: var(--radio-circle-size);
@@ -110,16 +110,14 @@ input {
110
110
  min-width: 0;
111
111
  }
112
112
  label {
113
- font: var(--lmvz-typography-body-md, 400 clamp(0.88rem, 0.84rem + 0.13vw, 1rem) / 1.5
114
- Router);
113
+ font: var(--lmvz-typography-body-md, 400 clamp(0.88rem, 0.84rem + 0.13vw, 1rem) / 1.5 Router);
115
114
  color: var(--radio-label-color);
116
115
  cursor: pointer;
117
116
  transition: color var(--radio-duration) var(--radio-easing);
118
117
  }
119
118
  .helper-text {
120
119
  display: block;
121
- font: var(--lmvz-typography-body-sm, 400 clamp(0.69rem, 0.67rem + 0.06vw, 0.75rem) / 1.5
122
- Router);
120
+ font: var(--lmvz-typography-body-sm, 400 clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem) / 1.5 Router);
123
121
  color: var(--radio-helper-color);
124
122
  margin-block-start: 2px;
125
123
  }
@@ -152,7 +150,7 @@ label {
152
150
  border-color: var(--radio-border-color-error);
153
151
  }
154
152
  :host([error]) .helper-text {
155
- color: var(--lmvz-semantic-color-status-on-danger, #e52a31);
153
+ color: var(--lmvz-semantic-color-status-on-danger, #a31419);
156
154
  }
157
155
  /* Disabled state */
158
156
  :host([disabled]) {
@@ -137,7 +137,7 @@ export class LmvzRadio extends ReactiveControllerHost {
137
137
  this.radioGroupController.setFocused(target);
138
138
  }
139
139
  render() {
140
- return (h(Host, { key: '9b8fbfe18376f9411f792630065dfd219f9ddb32' }, h("input", { key: '24a0675eff854bbd751c07905db4be11e5353d11', type: "radio", id: this.radioId, name: this.name ?? undefined, value: this.value, checked: this.checked, disabled: this.disabled, required: this.required, autoFocus: this.autofocus, "aria-invalid": this.error ? 'true' : undefined, "aria-describedby": this.helperId, ref: (el) => (this.nativeInput = el), onChange: this.handleChange }), h("span", { key: 'f888c1f76f6f929609584d378dbe0974fd980f68', class: "circle", "aria-hidden": "true" }, this.checked && h("span", { key: '57fbb1e7abdca10861cf8709d4300459e1f62e5c', class: "dot" })), h("span", { key: '1c845611162be86500e8475ac37412e2d3f2fd99', class: "content" }, h("label", { key: '1399f4d34ff0e6d369cefc9f5d08208a4dee593f', htmlFor: this.radioId }, this.label), h("span", { key: '1345435ff2f4773260ea3f73e943f043c2b88bdb', "aria-live": "polite", class: "helper-text", id: this.helperId }, this.helperText))));
140
+ return (h(Host, { key: 'c233b8f8e67016dd9a8622359bd91d3193735438' }, h("input", { key: '6a0a028f4ae346b60f86109d33058f7a04f0f2f8', type: "radio", id: this.radioId, name: this.name ?? undefined, value: this.value, checked: this.checked, disabled: this.disabled, required: this.required, autoFocus: this.autofocus, "aria-invalid": this.error ? 'true' : undefined, "aria-describedby": this.helperId, ref: (el) => (this.nativeInput = el), onChange: this.handleChange }), h("span", { key: 'f86096dd776236e07292dab77a37aeedffb8d8d4', class: "circle", "aria-hidden": "true" }, this.checked && h("span", { key: '8e93beefee742af62775d4d5945df8d13adb739f', class: "dot" })), h("span", { key: '19df96e6d015d0f74c96781ae49191a542a4f41d', class: "content" }, h("label", { key: 'b678b54ca139191ff78d5f503b73ea407eeb0708', htmlFor: this.radioId }, this.label), h("span", { key: '4a1e8a60594e959cb5549dd20b3d35a715ce1d99', "aria-live": "polite", class: "helper-text", id: this.helperId }, this.helperText))));
141
141
  }
142
142
  static get is() { return "lmvz-radio"; }
143
143
  static get encapsulation() { return "scoped"; }
@@ -49,20 +49,20 @@
49
49
  --lmvz-select-radius: var(--lmvz-component-input-radius-default, 999px);
50
50
  --lmvz-select-bg: var(--lmvz-semantic-color-surface-input-primary, #ffffff);
51
51
  --lmvz-select-border-color: var(--lmvz-semantic-color-border-default, #e0e0e0);
52
- --lmvz-select-border-color-hover: var(--lmvz-semantic-color-border-hover, #c7c7c7);
52
+ --lmvz-select-border-color-hover: var(--lmvz-semantic-color-border-hover, #a1a1a1);
53
53
  --lmvz-select-border-width: var(--lmvz-semantic-border-width-default, 1px);
54
54
  --lmvz-select-text-color: var(--lmvz-semantic-color-on-surface-input-primary, #000000);
55
55
  --lmvz-select-label-color: var(--lmvz-semantic-color-on-surface-input-primary, #000000);
56
56
  --lmvz-select-padding-x: var(--lmvz-component-input-padding-md, clamp(0.88rem, 0.84rem + 0.13vw, 1rem));
57
57
  --lmvz-select-gap: var(--lmvz-component-input-gap-md, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
58
58
  --lmvz-select-height: var(--lmvz-component-input-size-md, clamp(2.5rem, 2.44rem + 0.26vw, 2.75rem));
59
- --lmvz-select-label-font-size: var(--lmvz-component-body-xs-font-size, clamp(0.63rem, 0.61rem + 0.06vw, 0.69rem));
59
+ --lmvz-select-label-font-size: var(--lmvz-component-body-xs-font-size, clamp(0.63rem, 0.59rem + 0.13vw, 0.75rem));
60
60
  --lmvz-select-label-top-offset: calc(var(--lmvz-internal-select-floating-label-top-offset) * -1);
61
61
  --lmvz-select-label-minimized-padding-x: 4px;
62
62
  --lmvz-select-disabled-opacity: var(--lmvz-component-input-disabled-opacity, 40%);
63
63
  --lmvz-select-helper-color: var(--lmvz-semantic-color-on-surface-input-secondary, #545454);
64
64
  --lmvz-select-focus-color: var(--lmvz-semantic-color-status-on-active, #0e7ab4);
65
- --lmvz-select-error-color: var(--lmvz-semantic-color-status-on-danger, #e52a31);
65
+ --lmvz-select-error-color: var(--lmvz-semantic-color-status-on-danger, #a31419);
66
66
  }
67
67
  .select-wrapper {
68
68
  position: relative;
@@ -201,7 +201,7 @@ select {
201
201
  --lmvz-select-label-font-size: var(--lmvz-component-body-2xs-font-size, clamp(0.5rem, 0.47rem + 0.13vw, 0.63rem));
202
202
  }
203
203
  :host([size='small']) div[aria-hidden] > span:first-child {
204
- font-size: var(--lmvz-component-body-sm-font-size, clamp(0.69rem, 0.67rem + 0.06vw, 0.75rem));
204
+ font-size: var(--lmvz-component-body-sm-font-size, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
205
205
  }
206
206
  :host([size='small']) div[aria-hidden] svg {
207
207
  width: var(--lmvz-component-icon-size-4xs, clamp(0.5rem, 0.47rem + 0.13vw, 0.63rem));
@@ -211,7 +211,7 @@ select {
211
211
  --lmvz-select-height: var(--lmvz-component-input-size-lg, clamp(2.75rem, 2.69rem + 0.26vw, 3rem));
212
212
  --lmvz-select-padding-x: var(--lmvz-component-input-padding-lg, clamp(1rem, 0.94rem + 0.26vw, 1.25rem));
213
213
  --lmvz-select-gap: var(--lmvz-component-input-gap-lg, clamp(0.75rem, 0.69rem + 0.26vw, 1rem));
214
- --lmvz-select-label-font-size: var(--lmvz-component-body-sm-font-size, clamp(0.69rem, 0.67rem + 0.06vw, 0.75rem));
214
+ --lmvz-select-label-font-size: var(--lmvz-component-body-sm-font-size, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
215
215
  }
216
216
  :host([size='large']) div[aria-hidden] > span:first-child {
217
217
  font-size: var(--lmvz-component-body-lg-font-size, clamp(1rem, 0.97rem + 0.13vw, 1.13rem));
@@ -51,7 +51,7 @@ export class LmvzSelect extends ReactiveControllerHost {
51
51
  render() {
52
52
  const hasValue = this.hasValue;
53
53
  const shouldShowLabel = hasValue;
54
- return (h(Host, { key: 'c753006bdabf1723fa9ff61eb35f58d5bef3157b', "data-highlighted": String(this.highlighted) }, h("div", { key: '6b06056fd48ef4f837cbe17383756e3726c68f90', class: hasValue ? 'select-wrapper has-value' : 'select-wrapper' }, h("label", { key: '819901582fc449566a112cd4544fa0af903a7011', htmlFor: this.selectId, class: shouldShowLabel ? 'floating-label' : 'assistive-label' }, this.label, this.required && shouldShowLabel && h("span", { key: '14ad70c1cf1a96c7398698fa86baeadd5fe4257b', "aria-hidden": "true" }, " *")), h("div", { key: '589c6946bee6e320a36dfa0ed765ff9e02af4c2f', "aria-hidden": "true" }, h("span", { key: '04444223ed66a5ddff3eb7bd581cf5a7e615cf6b' }, hasValue ? this.selectedLabel : this.label, this.required && !hasValue && h("span", { key: '9631d4a655514957a4564be939d8bd16c3a036f0', "aria-hidden": "true" }, " *")), h("span", { key: '3950abf6ee9528adb5895fe6bc88be67b39d458e' }, h("svg", { key: '98a0a380e9ca5044ca1fefb03b74bfc178974a60', width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" }, h("path", { key: '19fe8b40e315fabee982ba37fe2d397493429519', d: "M20.3334 8.66663L12.0001 17L3.66675 8.66663", stroke: "currentColor", "stroke-width": "1.75", "stroke-linecap": "round", "stroke-linejoin": "round" })))), h("select", { key: '7480ff36ef3acfe10fb0122bfdf7feb5af406c67', id: this.selectId, ref: (el) => (this.nativeSelectEl = el), name: this.name, disabled: this.disabled, required: this.required, "aria-label": this.label, onChange: this.handleChange }, !hasValue && h("option", { key: '5d00f696c2c4d7c35c180018a4c487cbb0229d37', value: "", disabled: true, selected: true, hidden: true }), h("slot", { key: '218fe11d224d7ca72c8555301d41d8878b6b34b4' }))), this.helperText && h("div", { key: 'edbb738590305f6724a590a3532fd9284a187866', role: "status" }, this.helperText)));
54
+ return (h(Host, { key: 'f1180d4a5f1c30414b06c19314eb75630aa181f7', "data-highlighted": String(this.highlighted) }, h("div", { key: '01088c7f3315faab1fb23c99cf8b30f7d59abfc6', class: hasValue ? 'select-wrapper has-value' : 'select-wrapper' }, h("label", { key: '34d23292b6b65fac000f822e1e2839e28b1bfee9', htmlFor: this.selectId, class: shouldShowLabel ? 'floating-label' : 'assistive-label' }, this.label, this.required && shouldShowLabel && h("span", { key: 'ba171cdd9b53f8fa7b7fcb2c39a0b4aa69940b95', "aria-hidden": "true" }, " *")), h("div", { key: '798a6ef187b4aaaa45ecb3d6f94c30e7facb7a38', "aria-hidden": "true" }, h("span", { key: '33ea2169d94f4f8cf6263ed0c3bd6a9e593207a4' }, hasValue ? this.selectedLabel : this.label, this.required && !hasValue && h("span", { key: '0ea2d4987587a1a42dfd037dcefeed3f1b3356ef', "aria-hidden": "true" }, " *")), h("span", { key: '7bd24b3921cc5cd261f563476a79d61a288d60bb' }, h("svg", { key: '9887853724abada1fe36f7225db7903ee6e4040d', width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", "aria-hidden": "true" }, h("path", { key: 'b55a82230d46944ca3bd46f00f8c59b1a75ab8b8', d: "M20.3334 8.66663L12.0001 17L3.66675 8.66663", stroke: "currentColor", "stroke-width": "1.75", "stroke-linecap": "round", "stroke-linejoin": "round" })))), h("select", { key: 'd786f49b4522baf4c803ed3046b3606f29d6dae1', id: this.selectId, ref: (el) => (this.nativeSelectEl = el), name: this.name, disabled: this.disabled, required: this.required, "aria-label": this.label, onChange: this.handleChange }, !hasValue && h("option", { key: '5ffa7ffb7cd79c28a8bd212cc4d123dceb77aad4', value: "", disabled: true, selected: true, hidden: true }), h("slot", { key: '8a4141ead35cfd07c14d869715397d12ea8b64d7' }))), this.helperText && h("div", { key: 'dc5ccd28b0a903f6ed674d994e302caed1d9e9bb', role: "status" }, this.helperText)));
55
55
  }
56
56
  static get is() { return "lmvz-select"; }
57
57
  static get encapsulation() { return "scoped"; }
@@ -1,6 +1,6 @@
1
1
  :host {
2
2
  position: fixed;
3
- bottom: var(--lmvz-dimension-16-20, clamp(1rem, 0.94rem + 0.26vw, 1.25rem));
3
+ bottom: var(--lmvz-dimension-20-16, clamp(1rem, 0.94rem + 0.26vw, 1.25rem));
4
4
  left: 50%;
5
5
  transform: translateX(-50%);
6
6
  z-index: 2147483000; /* TODO(tokens): promote to design-token */
@@ -8,40 +8,40 @@
8
8
  width: max-content;
9
9
  border: 1px solid transparent;
10
10
  border-radius: var(--lmvz-semantic-border-radius-lg, 14px);
11
- padding: var(--lmvz-dimension-10-12, clamp(0.63rem, 0.59rem + 0.13vw, 0.75rem)) var(--lmvz-dimension-12-14, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
11
+ padding: var(--lmvz-dimension-12-10, clamp(0.63rem, 0.59rem + 0.13vw, 0.75rem)) var(--lmvz-dimension-14-12, clamp(0.75rem, 0.72rem + 0.13vw, 0.88rem));
12
12
 
13
13
  display: flex;
14
14
  flex-direction: row;
15
15
  align-items: center;
16
16
  justify-content: center;
17
- gap: var(--lmvz-dimension-8-12, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
17
+ gap: var(--lmvz-dimension-12-8, clamp(0.5rem, 0.44rem + 0.26vw, 0.75rem));
18
18
 
19
19
  pointer-events: auto;
20
20
  font: var(--lmvz-typography-body-md-strong, 500 clamp(0.88rem, 0.84rem + 0.13vw, 1rem) / 1.5
21
21
  Router);
22
22
 
23
23
  /* this will be picked up by lmvz-icon as well */
24
- --lmvz-component-color: var(--lmvz-semantic-color-status-on-success, #37867c);
24
+ --lmvz-component-color: var(--lmvz-semantic-color-status-on-success, #28625b);
25
25
 
26
26
  color: var(--lmvz-component-color);
27
27
  }
28
28
 
29
29
  :host([status='success']) {
30
- background-color: var(--lmvz-semantic-color-status-success, #f1f9f8);
31
- border-color: var(--lmvz-semantic-color-status-on-success-subtle, #a5dad3);
32
- --lmvz-component-color: var(--lmvz-semantic-color-status-on-success, #37867c);
30
+ background-color: var(--lmvz-semantic-color-status-success, #c2e6e1);
31
+ border-color: var(--lmvz-semantic-color-status-on-success-subtle, #4eb6a9);
32
+ --lmvz-component-color: var(--lmvz-semantic-color-status-on-success, #28625b);
33
33
  }
34
34
 
35
35
  :host([status='warning']) {
36
- background-color: var(--lmvz-semantic-color-status-warning, #fffbf0);
37
- border-color: var(--lmvz-semantic-color-status-on-warning-subtle, #ffdf75);
38
- --lmvz-component-color: var(--lmvz-semantic-color-status-on-warning, #7a5e00);
36
+ background-color: var(--lmvz-semantic-color-status-warning, #ffeeb8);
37
+ border-color: var(--lmvz-semantic-color-status-on-warning-subtle, #e6b000);
38
+ --lmvz-component-color: var(--lmvz-semantic-color-status-on-warning, #614a00);
39
39
  }
40
40
 
41
41
  :host([status='error']) {
42
- background-color: var(--lmvz-semantic-color-status-danger, #fdf1f2);
43
- border-color: var(--lmvz-semantic-color-status-on-danger-subtle, #f7bfc2);
44
- --lmvz-component-color: var(--lmvz-semantic-color-status-on-danger, #e52a31);
42
+ background-color: var(--lmvz-semantic-color-status-danger, #fbdfe0);
43
+ border-color: var(--lmvz-semantic-color-status-on-danger-subtle, #e8454b);
44
+ --lmvz-component-color: var(--lmvz-semantic-color-status-on-danger, #a31419);
45
45
  }
46
46
 
47
47
  lmvz-icon {