@eunomia/elements 1.0.0-beta.7 → 1.0.0-beta.8

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 (177) hide show
  1. package/dist/cdn/components/chunks/{chunk-3PNGQEG4.js → chunk-32L2QYWQ.js} +2 -2
  2. package/dist/cdn/components/chunks/{chunk-Z3OFIE7S.js → chunk-3UUZM7X2.js} +1 -1
  3. package/dist/cdn/components/chunks/{chunk-BVASCT2V.js → chunk-55PUDL7D.js} +13 -13
  4. package/dist/cdn/components/chunks/chunk-5LA5DQMP.js +1 -0
  5. package/dist/cdn/components/chunks/chunk-5LXGJVJR.js +1 -0
  6. package/dist/cdn/components/chunks/chunk-6TIEBY34.js +1 -0
  7. package/dist/cdn/components/chunks/{chunk-LVKMAHMU.js → chunk-AH2SKZ7T.js} +4 -1
  8. package/dist/cdn/components/chunks/chunk-JAGZL7JH.js +1 -0
  9. package/dist/cdn/components/chunks/{chunk-DGI7TKCU.js → chunk-LD35BFNK.js} +5 -9
  10. package/dist/cdn/components/chunks/chunk-P6LGU3FT.js +1 -0
  11. package/dist/cdn/components/chunks/{chunk-UYZR4JXZ.js → chunk-QSZO4GIH.js} +45 -9
  12. package/dist/cdn/components/chunks/{chunk-PXF554MD.js → chunk-QYB6Z6FG.js} +5 -5
  13. package/dist/cdn/components/chunks/{chunk-RIFF2D2F.js → chunk-RGC3I6MK.js} +1 -1
  14. package/dist/cdn/components/chunks/{chunk-N6ZW5CDN.js → chunk-TCSB2MLU.js} +1 -1
  15. package/dist/cdn/components/chunks/chunk-WIUERJII.js +1 -0
  16. package/dist/cdn/components/eun-animate.js +4 -4
  17. package/dist/cdn/components/eun-button.js +1 -1
  18. package/dist/cdn/components/eun-carousel.js +10 -9
  19. package/dist/cdn/components/eun-chip.js +17 -11
  20. package/dist/cdn/components/eun-collapse.js +1 -1
  21. package/dist/cdn/components/eun-color-picker.js +18 -5
  22. package/dist/cdn/components/eun-counter.js +10 -8
  23. package/dist/cdn/components/eun-date-picker.js +46 -43
  24. package/dist/cdn/components/eun-drawer.js +19 -20
  25. package/dist/cdn/components/eun-dropdown-button.js +1 -1
  26. package/dist/cdn/components/eun-dropdown-item.js +3 -3
  27. package/dist/cdn/components/eun-fab.js +1 -1
  28. package/dist/cdn/components/eun-image-carousel.js +4 -4
  29. package/dist/cdn/components/eun-input-phone.js +11 -8
  30. package/dist/cdn/components/eun-input.js +1 -1
  31. package/dist/cdn/components/eun-link.js +59 -13
  32. package/dist/cdn/components/eun-modal.js +23 -13
  33. package/dist/cdn/components/eun-scheduler-event-editor.js +1 -1
  34. package/dist/cdn/components/eun-search.js +34 -28
  35. package/dist/cdn/components/eun-select.js +3 -3
  36. package/dist/cdn/components/eun-sidebar.js +66 -21
  37. package/dist/cdn/components/eun-sliding-card.js +1 -1
  38. package/dist/cdn/components/eun-stepper.js +16 -16
  39. package/dist/cdn/components/eun-tab-panel.js +1 -1
  40. package/dist/cdn/components/eun-table-action-bar.js +1 -1
  41. package/dist/cdn/components/eun-table.js +2 -2
  42. package/dist/cdn/components/eun-textarea.js +16 -13
  43. package/dist/cdn/components/eun-time-picker.js +25 -23
  44. package/dist/cdn/components/eun-toast.js +8 -8
  45. package/dist/cdn/components/eun-toggle-group.js +71 -35
  46. package/dist/cdn/components/eun-toggle-item.js +8 -8
  47. package/dist/cdn/components/eun-toolbar.js +20 -20
  48. package/dist/cdn/components/eun-tooltip.js +6 -6
  49. package/dist/cdn/styles/eunomia-themes.css +1 -1
  50. package/dist/cdn/styles/themes/mixins/eunomia-neutrals.mixin.scss +6 -2
  51. package/dist/cdn/styles/themes/prebuilt-themes/eunomia/eunomia-dark.mixin.scss +6 -2
  52. package/dist/cdn/styles/themes/prebuilt-themes/eunomia/eunomia.theme.css +1 -1
  53. package/dist/cdn/styles/themes/prebuilt-themes/fire/eunomia-fire-dark.mixin.scss +6 -2
  54. package/dist/cdn/styles/themes/prebuilt-themes/fire/eunomia-fire.theme.css +1 -1
  55. package/dist/cdn/styles/themes/prebuilt-themes/forest/eunomia-forest-dark.mixin.scss +6 -2
  56. package/dist/cdn/styles/themes/prebuilt-themes/forest/eunomia-forest.theme.css +1 -1
  57. package/dist/cdn/styles/themes/prebuilt-themes/gold/eunomia-gold-dark.mixin.scss +6 -2
  58. package/dist/cdn/styles/themes/prebuilt-themes/gold/eunomia-gold.theme.css +1 -1
  59. package/dist/cdn/styles/themes/prebuilt-themes/ocean/eunomia-ocean-dark.mixin.scss +6 -2
  60. package/dist/cdn/styles/themes/prebuilt-themes/ocean/eunomia-ocean.theme.css +1 -1
  61. package/dist/cdn/styles/themes/prebuilt-themes/salmon/eunomia-salmon-dark.mixin.scss +6 -2
  62. package/dist/cdn/styles/themes/prebuilt-themes/salmon/eunomia-salmon.theme.css +1 -1
  63. package/dist/cdn/styles/themes/prebuilt-themes/violet/eunomia-violet-dark.mixin.scss +6 -2
  64. package/dist/cdn/styles/themes/prebuilt-themes/violet/eunomia-violet.theme.css +1 -1
  65. package/dist/components/action/button/button.d.ts +2 -2
  66. package/dist/components/action/button/button.js +3 -7
  67. package/dist/components/action/button/button.styles.js +3 -3
  68. package/dist/components/action/dropdown-button/dropdown-button.d.ts +4 -6
  69. package/dist/components/action/dropdown-button/dropdown-button.js +6 -8
  70. package/dist/components/action/dropdown-button/dropdown-item.d.ts +10 -2
  71. package/dist/components/action/dropdown-button/dropdown-item.js +24 -9
  72. package/dist/components/action/link/link.d.ts +10 -1
  73. package/dist/components/action/link/link.js +13 -1
  74. package/dist/components/action/link/link.styles.js +50 -3
  75. package/dist/components/action/toggle/toggle-group.d.ts +12 -2
  76. package/dist/components/action/toggle/toggle-group.js +17 -2
  77. package/dist/components/action/toggle/toggle-group.styles.js +54 -18
  78. package/dist/components/action/toggle/toggle-item.js +1 -3
  79. package/dist/components/action/toggle/toggle-item.styles.js +7 -7
  80. package/dist/components/alert/drawer/drawer.d.ts +25 -0
  81. package/dist/components/alert/drawer/drawer.js +42 -3
  82. package/dist/components/alert/drawer/drawer.styles.js +6 -6
  83. package/dist/components/alert/modal/modal.d.ts +25 -0
  84. package/dist/components/alert/modal/modal.js +42 -3
  85. package/dist/components/alert/modal/modal.styles.js +17 -6
  86. package/dist/components/alert/toast/toast.js +3 -2
  87. package/dist/components/alert/toast/toast.styles.js +3 -3
  88. package/dist/components/content/chip/chip.js +3 -2
  89. package/dist/components/content/chip/chip.styles.js +9 -3
  90. package/dist/components/content/image-carousel/image-carousel.js +5 -4
  91. package/dist/components/content/tooltip/tooltip.d.ts +16 -9
  92. package/dist/components/content/tooltip/tooltip.js +28 -12
  93. package/dist/components/content/tooltip/tooltip.styles.js +1 -1
  94. package/dist/components/form/color-picker/color-picker.d.ts +7 -1
  95. package/dist/components/form/color-picker/color-picker.js +8 -2
  96. package/dist/components/form/color-picker/color-picker.styles.js +13 -0
  97. package/dist/components/form/counter/counter.d.ts +3 -1
  98. package/dist/components/form/counter/counter.js +3 -1
  99. package/dist/components/form/counter/counter.styles.js +6 -4
  100. package/dist/components/form/date-picker/date-picker.d.ts +6 -1
  101. package/dist/components/form/date-picker/date-picker.js +7 -2
  102. package/dist/components/form/date-picker/date-picker.styles.js +9 -6
  103. package/dist/components/form/input/input.d.ts +6 -1
  104. package/dist/components/form/input/input.js +7 -2
  105. package/dist/components/form/input-phone/input-phone.d.ts +7 -1
  106. package/dist/components/form/input-phone/input-phone.js +7 -1
  107. package/dist/components/form/input-phone/input-phone.styles.js +6 -3
  108. package/dist/components/form/search/search.d.ts +64 -10
  109. package/dist/components/form/search/search.js +112 -31
  110. package/dist/components/form/search/search.styles.js +2 -0
  111. package/dist/components/form/select/select.d.ts +7 -1
  112. package/dist/components/form/select/select.js +7 -1
  113. package/dist/components/form/shared-styles/field.styles.js +45 -8
  114. package/dist/components/form/shared-styles/listbox.styles.js +5 -1
  115. package/dist/components/form/textarea/textarea.d.ts +1 -0
  116. package/dist/components/form/textarea/textarea.js +2 -1
  117. package/dist/components/form/textarea/textarea.styles.js +3 -0
  118. package/dist/components/form/time-picker/time-picker.d.ts +6 -1
  119. package/dist/components/form/time-picker/time-picker.js +7 -2
  120. package/dist/components/form/time-picker/time-picker.styles.js +7 -5
  121. package/dist/components/layout/animate/animate.js +2 -1
  122. package/dist/components/navigation/carousel/carousel.d.ts +1 -2
  123. package/dist/components/navigation/carousel/carousel.js +14 -6
  124. package/dist/components/navigation/carousel/carousel.styles.js +5 -4
  125. package/dist/components/navigation/sidebar/sidebar.d.ts +98 -52
  126. package/dist/components/navigation/sidebar/sidebar.js +140 -85
  127. package/dist/components/navigation/sidebar/sidebar.styles.d.ts +1 -1
  128. package/dist/components/navigation/sidebar/sidebar.styles.js +69 -21
  129. package/dist/components/navigation/stepper/stepper.d.ts +1 -3
  130. package/dist/components/navigation/stepper/stepper.js +3 -21
  131. package/dist/components/navigation/toolbar/toolbar.d.ts +10 -0
  132. package/dist/components/navigation/toolbar/toolbar.js +8 -1
  133. package/dist/components/navigation/toolbar/toolbar.styles.js +18 -17
  134. package/dist/events/select.event.d.ts +3 -2
  135. package/dist/events/select.event.js +2 -1
  136. package/dist/features/scheduler/scheduler-event-editor/scheduler-event-editor.js +1 -1
  137. package/dist/features/table/table/table.js +1 -1
  138. package/dist/features/table/table-action-bar/table-action-bar.js +2 -2
  139. package/dist/index.d.ts +1 -0
  140. package/dist/mixins/button.mixin.d.ts +1 -1
  141. package/dist/mixins/button.mixin.js +6 -4
  142. package/dist/mixins/dialog-surface.mixin.d.ts +8 -0
  143. package/dist/mixins/field-size.mixin.d.ts +19 -0
  144. package/dist/mixins/field-size.mixin.js +33 -0
  145. package/dist/mixins/rounded.mixin.d.ts +18 -0
  146. package/dist/mixins/rounded.mixin.js +32 -0
  147. package/dist/ssr/button-ssr.css +5 -5
  148. package/dist/ssr/carousel-ssr.css +9 -0
  149. package/dist/ssr/chip-ssr.css +8 -0
  150. package/dist/ssr/collapse-ssr.css +29 -0
  151. package/dist/ssr/field-ssr.css +43 -4
  152. package/dist/ssr/image-carousel-ssr.css +10 -0
  153. package/dist/ssr/links-ssr.css +38 -1
  154. package/dist/ssr/sidebar-ssr.css +95 -4
  155. package/dist/ssr/toggle-group-ssr.css +64 -13
  156. package/dist/ssr/toolbar-ssr.css +50 -0
  157. package/dist/types/variant.type.d.ts +4 -1
  158. package/dist/utils/boolean.utils.d.ts +14 -0
  159. package/dist/utils/boolean.utils.js +20 -0
  160. package/dist/utils/dropdown-menu.utils.d.ts +1 -1
  161. package/dist/utils/focusable-elements.utils.d.ts +12 -0
  162. package/dist/utils/focusable-elements.utils.js +26 -0
  163. package/dist/utils/input-modality.utils.d.ts +13 -0
  164. package/dist/utils/input-modality.utils.js +34 -0
  165. package/dist/utils/theme-generator.utils.d.ts +8 -2
  166. package/dist/utils/theme-generator.utils.js +30 -9
  167. package/package.json +1 -1
  168. package/styles/themes/mixins/eunomia-neutrals.mixin.scss +6 -2
  169. package/styles/themes/prebuilt-themes/eunomia/eunomia-dark.mixin.scss +6 -2
  170. package/styles/themes/prebuilt-themes/fire/eunomia-fire-dark.mixin.scss +6 -2
  171. package/styles/themes/prebuilt-themes/forest/eunomia-forest-dark.mixin.scss +6 -2
  172. package/styles/themes/prebuilt-themes/gold/eunomia-gold-dark.mixin.scss +6 -2
  173. package/styles/themes/prebuilt-themes/ocean/eunomia-ocean-dark.mixin.scss +6 -2
  174. package/styles/themes/prebuilt-themes/salmon/eunomia-salmon-dark.mixin.scss +6 -2
  175. package/styles/themes/prebuilt-themes/violet/eunomia-violet-dark.mixin.scss +6 -2
  176. package/dist/cdn/components/chunks/chunk-6VK6SHCG.js +0 -1
  177. package/dist/cdn/components/chunks/chunk-UDOZRIJZ.js +0 -1
@@ -5,6 +5,12 @@ import { EunomiaIconName } from "../../content/icon/icon.type.js";
5
5
  * Tag name of the search component.
6
6
  */
7
7
  export declare const EunomiaSearchTagName = "eun-search";
8
+ /**
9
+ * How the suggestions dropdown filters its options against the typed value.
10
+ * `contains` keeps the options whose label contains it, `none` shows every
11
+ * option as is, for results already filtered by a server.
12
+ */
13
+ export type EunomiaSearchFilter = "contains" | "none";
8
14
  declare const EunomiaSearch_base: import("../../../utils/constructor.type.js").Constructor<import("../../../mixins/anchor-position.mixin.js").AnchorPositionMixinInterface> & typeof EunomiaInput;
9
15
  /**
10
16
  * An input field specialized for search: the native type is locked to
@@ -24,6 +30,8 @@ declare const EunomiaSearch_base: import("../../../utils/constructor.type.js").C
24
30
  *
25
31
  * @slot [icon] - Replaces the leading icon entirely with any content, not just an icon element. Falls back to icon when empty. Applies to the real field, inline or inside the panel modal. The compact panel trigger button always shows the plain icon glyph, since it can't project the same slotted content a second time
26
32
  * @slot results - Content rendered below the field inside the panel, such as a result list. Only relevant when panel is set
33
+ * @slot trigger - Replaces the panel's built-in trigger button, for example with an icon button. A click on it opens the panel, and focus returns to it on close. Give it its own accessible name. Only relevant when panel is set
34
+ * @csspart trigger - The panel's built-in trigger button, shown while nothing is slotted in trigger
27
35
  * @prop {boolean} [shortcut=false] - Enables the Ctrl+K or Cmd+K global shortcut and its visual key hint badge
28
36
  * @prop {boolean} [panel=false] - Opens a modal command palette panel on shortcut or click instead of focusing inline
29
37
  * @prop {string} [panelLabel] - The accessible name of the panel's dialog, used when label isn't set. Defaults to the localized string for the current locale (English: "Search")
@@ -31,8 +39,11 @@ declare const EunomiaSearch_base: import("../../../utils/constructor.type.js").C
31
39
  * @prop {string} [loadingLabel='Loading…'] - The accessible label announced by the loading indicator while loading is set
32
40
  * @prop {boolean} [suggestions=false] - Opens a dropdown listbox showing light DOM option and optgroup children matching the typed value, highlighting the matched substring. Ignored while panel is set
33
41
  * @prop {string} [noResultsLabel='No results'] - Text displayed in the dropdown when no option matches, while suggestions is set
34
- * @fires {ChangeEvent} eunchange - Fired on every value change
42
+ * @prop {'contains' | 'none'} [filter='contains'] - How the suggestions are filtered against the typed value. Contains keeps the options whose label contains it, none shows every option as is, for results already filtered by a server
43
+ * @fires {ChangeEvent} eunchange - Fired on every value change, typed or picked from the suggestions
44
+ * @fires {SelectEvent} eunselect - Fired when a suggestion is picked, with the option's value as key
35
45
  * @cssprop [--field-listbox-max-height=260px] - Sets the maximum height of the suggestions dropdown, past which it scrolls. Shared with the select component
46
+ * @cssprop [--field-listbox-border-radius] - Sets the corner radius of the suggestions dropdown, independent of the field's own radius. Shared with the select component
36
47
  * @cssprop [--field-listbox-transition-duration=200ms] - Sets the duration of the suggestions dropdown's open and close transition. Shared with the select component
37
48
  * @cssprop [--field-match-color] - Sets the color of the matched search substring, highlighted while suggestions is set. Shared with the select component
38
49
  * @cssprop [--field-match-background] - Sets the background of the matched search substring, none by default. Shared with the select component
@@ -120,9 +131,10 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
120
131
  * mechanic (`AnchorPositionMixin`, CSS Anchor Positioning with a JS
121
132
  * fallback) as `eun-select`'s own dropdown, sharing its CSS custom
122
133
  * properties (`--field-listbox-max-height`, `--field-match-*`, etc.).
123
- * Picking a suggestion sets `value` to its label (not a separate stored
124
- * id, unlike `eun-select` — see the Suggestions example in the docs for
125
- * why). Ignored while `panel` is set — combining the two isn't supported.
134
+ * Picking a suggestion sets `value` to its label, then fires `eunselect`
135
+ * with the option's own `value` attribute, so two options sharing a label
136
+ * stay distinguishable. Ignored while `panel` is set — combining the two
137
+ * isn't supported.
126
138
  * @default false
127
139
  */
128
140
  suggestions: boolean;
@@ -132,6 +144,14 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
132
144
  * @default No results
133
145
  */
134
146
  noResultsLabel: string;
147
+ /**
148
+ * How the suggestions are filtered against the typed value : `contains`
149
+ * keeps the options whose label contains it, `none` shows every option as
150
+ * is, for results a server already filtered, possibly on another field
151
+ * than the label.
152
+ * @default contains
153
+ */
154
+ filter: EunomiaSearchFilter;
135
155
  /**
136
156
  * Whether the suggestions dropdown is currently open.
137
157
  * @internal
@@ -156,6 +176,12 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
156
176
  * @readonly
157
177
  */
158
178
  private readonly fieldWrapperEl?;
179
+ /**
180
+ * The `trigger` slot, rendered in place of the field while `panel` is set,
181
+ * with the built-in trigger button as its fallback content.
182
+ * @internal
183
+ */
184
+ private readonly triggerSlot?;
159
185
  /**
160
186
  * The collapsed trigger button rendered in place of the field while
161
187
  * `panel` is set.
@@ -191,6 +217,14 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
191
217
  * @private
192
218
  */
193
219
  private static readonly FIELD_POINTER_DOWN_WINDOW_MS;
220
+ /**
221
+ * Re-renders the suggestions whenever the light DOM `option`/`optgroup`
222
+ * children change, since they aren't reactive properties : a consumer
223
+ * replacing them with a server's results would otherwise stay unseen until
224
+ * some unrelated property changed.
225
+ * @internal
226
+ */
227
+ private readonly optionsObserver;
194
228
  connectedCallback(): void;
195
229
  disconnectedCallback(): void;
196
230
  /**
@@ -207,7 +241,7 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
207
241
  * still-closed modal at this point, and focusing hidden popover content
208
242
  * is a no-op — leaving focus stranded on the (invisible) host with
209
243
  * nowhere to land, e.g. on a Shift+Tab back onto it from just past the
210
- * trigger.
244
+ * trigger. A trigger slotted in `trigger` takes its place.
211
245
  * @protected
212
246
  */
213
247
  protected focusTarget(): HTMLElement | undefined;
@@ -219,6 +253,12 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
219
253
  * Closes the command-palette panel. No-op when `panel` isn't set.
220
254
  */
221
255
  closePanel(): void;
256
+ /**
257
+ * Opens the panel on a click anywhere in the `trigger` slot, its slotted
258
+ * content or the built-in button it falls back to, unless disabled.
259
+ * @private
260
+ */
261
+ private readonly handleTriggerClick;
222
262
  /**
223
263
  * Lifecycle hook called after the component's first render. Attaches the
224
264
  * suggestions dropdown's event listeners directly on the field element,
@@ -250,7 +290,7 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
250
290
  private get effectiveQuery();
251
291
  /**
252
292
  * Options filtered by {@link effectiveQuery}. Shows every option when the
253
- * field is empty.
293
+ * field is empty, or when {@link filter} is `none`.
254
294
  * @internal
255
295
  */
256
296
  private get filteredOptions();
@@ -318,6 +358,19 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
318
358
  * @private
319
359
  */
320
360
  private readonly handleSuggestionsFieldActivate;
361
+ /**
362
+ * Closes the dropdown once focus has left the component entirely, e.g.
363
+ * `Tab`-ing away, same as `eun-select`'s own `handleFocusOut` : the
364
+ * Popover API's light-dismiss only reacts to outside clicks, not to focus
365
+ * moving via the keyboard. `closeSuggestions` checks the popover itself
366
+ * rather than `isOpen`, which only follows the asynchronous `toggle`
367
+ * event. Checked a frame later, since the newly focused
368
+ * element hasn't received focus yet when `focusout` fires. Picking an
369
+ * option never gets here, its row cancels `mousedown` so the field keeps
370
+ * focus.
371
+ * @private
372
+ */
373
+ private readonly handleSuggestionsFocusOut;
321
374
  /**
322
375
  * Opens the dropdown and resets the highlight on every keystroke. Bound to
323
376
  * the field's native `input` event (rather than reacting to `value`
@@ -329,9 +382,9 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
329
382
  */
330
383
  private readonly handleSuggestionsInput;
331
384
  /**
332
- * Picks a suggestion : sets `value` to its label (see the class doc's
333
- * `suggestions` entry for why not its `value` attribute), notifies
334
- * consumers, and closes the dropdown.
385
+ * Picks a suggestion : sets `value` to its label, fires `eunchange` with
386
+ * it like a keystroke would, then `eunselect` with the option's own
387
+ * `value`, and closes the dropdown.
335
388
  *
336
389
  * @param option the chosen option.
337
390
  * @private
@@ -495,7 +548,8 @@ export declare class EunomiaSearch extends EunomiaSearch_base {
495
548
  private syncSuggestionsAria;
496
549
  /**
497
550
  * Renders the search component: the inherited inline field, or — when
498
- * `panel` is set — a trigger button plus a `eun-modal[kind=search]`
551
+ * `panel` is set — a trigger (the `trigger` slot, falling back to a
552
+ * built-in button) plus a `eun-modal[kind=search]`
499
553
  * (flush, no padding, no close button) containing the real field (reused
500
554
  * via `renderField`, wrapped for its own full-width/no-border styling) and
501
555
  * a `results` slot.
@@ -13,10 +13,12 @@ import { EunomiaInput } from "../input/input.js";
13
13
  import { searchStyles } from "./search.styles.js";
14
14
  import { AnchorPositionMixin } from "../../../mixins/anchor-position.mixin.js";
15
15
  import { ChangeEvent } from "../../../events/change.event.js";
16
+ import { SelectEvent } from "../../../events/select.event.js";
16
17
  import { listboxStyles, listboxAnchorPositioningStyles, } from "../shared-styles/listbox.styles.js";
17
18
  import { parseListboxOptions, renderHighlightedLabel, renderListboxRows, } from "../../../utils/listbox-options.utils.js";
18
19
  import { isWebKit } from "../../../utils/browser.utils.js";
19
20
  import { resolveDurationMs, resolveEasing, } from "../../../utils/motion.utils.js";
21
+ import { defaultTrueConverter } from "../../../utils/boolean.utils.js";
20
22
  /**
21
23
  * Tag name of the search component.
22
24
  */
@@ -39,6 +41,8 @@ export const EunomiaSearchTagName = "eun-search";
39
41
  *
40
42
  * @slot [icon] - Replaces the leading icon entirely with any content, not just an icon element. Falls back to icon when empty. Applies to the real field, inline or inside the panel modal. The compact panel trigger button always shows the plain icon glyph, since it can't project the same slotted content a second time
41
43
  * @slot results - Content rendered below the field inside the panel, such as a result list. Only relevant when panel is set
44
+ * @slot trigger - Replaces the panel's built-in trigger button, for example with an icon button. A click on it opens the panel, and focus returns to it on close. Give it its own accessible name. Only relevant when panel is set
45
+ * @csspart trigger - The panel's built-in trigger button, shown while nothing is slotted in trigger
42
46
  * @prop {boolean} [shortcut=false] - Enables the Ctrl+K or Cmd+K global shortcut and its visual key hint badge
43
47
  * @prop {boolean} [panel=false] - Opens a modal command palette panel on shortcut or click instead of focusing inline
44
48
  * @prop {string} [panelLabel] - The accessible name of the panel's dialog, used when label isn't set. Defaults to the localized string for the current locale (English: "Search")
@@ -46,8 +50,11 @@ export const EunomiaSearchTagName = "eun-search";
46
50
  * @prop {string} [loadingLabel='Loading…'] - The accessible label announced by the loading indicator while loading is set
47
51
  * @prop {boolean} [suggestions=false] - Opens a dropdown listbox showing light DOM option and optgroup children matching the typed value, highlighting the matched substring. Ignored while panel is set
48
52
  * @prop {string} [noResultsLabel='No results'] - Text displayed in the dropdown when no option matches, while suggestions is set
49
- * @fires {ChangeEvent} eunchange - Fired on every value change
53
+ * @prop {'contains' | 'none'} [filter='contains'] - How the suggestions are filtered against the typed value. Contains keeps the options whose label contains it, none shows every option as is, for results already filtered by a server
54
+ * @fires {ChangeEvent} eunchange - Fired on every value change, typed or picked from the suggestions
55
+ * @fires {SelectEvent} eunselect - Fired when a suggestion is picked, with the option's value as key
50
56
  * @cssprop [--field-listbox-max-height=260px] - Sets the maximum height of the suggestions dropdown, past which it scrolls. Shared with the select component
57
+ * @cssprop [--field-listbox-border-radius] - Sets the corner radius of the suggestions dropdown, independent of the field's own radius. Shared with the select component
51
58
  * @cssprop [--field-listbox-transition-duration=200ms] - Sets the duration of the suggestions dropdown's open and close transition. Shared with the select component
52
59
  * @cssprop [--field-match-color] - Sets the color of the matched search substring, highlighted while suggestions is set. Shared with the select component
53
60
  * @cssprop [--field-match-background] - Sets the background of the matched search substring, none by default. Shared with the select component
@@ -123,9 +130,10 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
123
130
  * mechanic (`AnchorPositionMixin`, CSS Anchor Positioning with a JS
124
131
  * fallback) as `eun-select`'s own dropdown, sharing its CSS custom
125
132
  * properties (`--field-listbox-max-height`, `--field-match-*`, etc.).
126
- * Picking a suggestion sets `value` to its label (not a separate stored
127
- * id, unlike `eun-select` — see the Suggestions example in the docs for
128
- * why). Ignored while `panel` is set — combining the two isn't supported.
133
+ * Picking a suggestion sets `value` to its label, then fires `eunselect`
134
+ * with the option's own `value` attribute, so two options sharing a label
135
+ * stay distinguishable. Ignored while `panel` is set — combining the two
136
+ * isn't supported.
129
137
  * @default false
130
138
  */
131
139
  this.suggestions = false;
@@ -135,6 +143,14 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
135
143
  * @default No results
136
144
  */
137
145
  this.noResultsLabel = "No results";
146
+ /**
147
+ * How the suggestions are filtered against the typed value : `contains`
148
+ * keeps the options whose label contains it, `none` shows every option as
149
+ * is, for results a server already filtered, possibly on another field
150
+ * than the label.
151
+ * @default contains
152
+ */
153
+ this.filter = "contains";
138
154
  /**
139
155
  * Whether the suggestions dropdown is currently open.
140
156
  * @internal
@@ -146,6 +162,29 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
146
162
  * @internal
147
163
  */
148
164
  this.highlightedIndex = -1;
165
+ /**
166
+ * Re-renders the suggestions whenever the light DOM `option`/`optgroup`
167
+ * children change, since they aren't reactive properties : a consumer
168
+ * replacing them with a server's results would otherwise stay unseen until
169
+ * some unrelated property changed.
170
+ * @internal
171
+ */
172
+ this.optionsObserver = new MutationObserver(() => {
173
+ if (this.highlightedIndex >= this.filteredOptions.length) {
174
+ this.highlightedIndex = -1;
175
+ }
176
+ this.requestUpdate();
177
+ });
178
+ /**
179
+ * Opens the panel on a click anywhere in the `trigger` slot, its slotted
180
+ * content or the built-in button it falls back to, unless disabled.
181
+ * @private
182
+ */
183
+ this.handleTriggerClick = () => {
184
+ if (!this.disabled) {
185
+ this.openPanel();
186
+ }
187
+ };
149
188
  /**
150
189
  * Records that the field itself was the pointerdown target — see
151
190
  * {@link fieldPointerDownAt}.
@@ -165,6 +204,25 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
165
204
  }
166
205
  this.openSuggestions();
167
206
  };
207
+ /**
208
+ * Closes the dropdown once focus has left the component entirely, e.g.
209
+ * `Tab`-ing away, same as `eun-select`'s own `handleFocusOut` : the
210
+ * Popover API's light-dismiss only reacts to outside clicks, not to focus
211
+ * moving via the keyboard. `closeSuggestions` checks the popover itself
212
+ * rather than `isOpen`, which only follows the asynchronous `toggle`
213
+ * event. Checked a frame later, since the newly focused
214
+ * element hasn't received focus yet when `focusout` fires. Picking an
215
+ * option never gets here, its row cancels `mousedown` so the field keeps
216
+ * focus.
217
+ * @private
218
+ */
219
+ this.handleSuggestionsFocusOut = () => {
220
+ requestAnimationFrame(() => {
221
+ if (!this.matches(":focus-within")) {
222
+ this.closeSuggestions();
223
+ }
224
+ });
225
+ };
168
226
  /**
169
227
  * Opens the dropdown and resets the highlight on every keystroke. Bound to
170
228
  * the field's native `input` event (rather than reacting to `value`
@@ -289,9 +347,17 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
289
347
  connectedCallback() {
290
348
  super.connectedCallback?.();
291
349
  window.addEventListener("keydown", this.handleShortcut);
350
+ this.optionsObserver.observe(this, {
351
+ childList: true,
352
+ subtree: true,
353
+ characterData: true,
354
+ attributes: true,
355
+ attributeFilter: ["value", "label"],
356
+ });
292
357
  }
293
358
  disconnectedCallback() {
294
359
  window.removeEventListener("keydown", this.handleShortcut);
360
+ this.optionsObserver.disconnect();
295
361
  super.disconnectedCallback?.();
296
362
  }
297
363
  /**
@@ -313,12 +379,13 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
313
379
  * still-closed modal at this point, and focusing hidden popover content
314
380
  * is a no-op — leaving focus stranded on the (invisible) host with
315
381
  * nowhere to land, e.g. on a Shift+Tab back onto it from just past the
316
- * trigger.
382
+ * trigger. A trigger slotted in `trigger` takes its place.
317
383
  * @protected
318
384
  */
319
385
  focusTarget() {
320
386
  if (this.panel) {
321
- return this.triggerEl;
387
+ const [slotted] = this.triggerSlot?.assignedElements() ?? [];
388
+ return slotted instanceof HTMLElement ? slotted : this.triggerEl;
322
389
  }
323
390
  return super.focusTarget();
324
391
  }
@@ -357,6 +424,7 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
357
424
  field.addEventListener("click", this.handleSuggestionsFieldActivate);
358
425
  field.addEventListener("input", this.handleSuggestionsInput);
359
426
  field.addEventListener("keydown", this.handleSuggestionsKeydown);
427
+ this.addEventListener("focusout", this.handleSuggestionsFocusOut);
360
428
  }
361
429
  /**
362
430
  * The available suggestions, parsed from the light DOM `option`/`optgroup`
@@ -380,12 +448,12 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
380
448
  }
381
449
  /**
382
450
  * Options filtered by {@link effectiveQuery}. Shows every option when the
383
- * field is empty.
451
+ * field is empty, or when {@link filter} is `none`.
384
452
  * @internal
385
453
  */
386
454
  get filteredOptions() {
387
455
  const query = this.effectiveQuery;
388
- if (!query) {
456
+ if (!query || this.filter === "none") {
389
457
  return this.options;
390
458
  }
391
459
  return this.options.filter((option) => option.label.toLowerCase().includes(query));
@@ -501,16 +569,17 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
501
569
  });
502
570
  }
503
571
  /**
504
- * Picks a suggestion : sets `value` to its label (see the class doc's
505
- * `suggestions` entry for why not its `value` attribute), notifies
506
- * consumers, and closes the dropdown.
572
+ * Picks a suggestion : sets `value` to its label, fires `eunchange` with
573
+ * it like a keystroke would, then `eunselect` with the option's own
574
+ * `value`, and closes the dropdown.
507
575
  *
508
576
  * @param option the chosen option.
509
577
  * @private
510
578
  */
511
579
  handleSelect(option) {
512
580
  this.value = option.label;
513
- this.dispatchEvent(new ChangeEvent());
581
+ this.dispatchEvent(new ChangeEvent(this.value));
582
+ this.dispatchEvent(new SelectEvent(option.value));
514
583
  this.closeSuggestions();
515
584
  }
516
585
  /**
@@ -779,12 +848,14 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
779
848
  }
780
849
  /**
781
850
  * Renders the search component: the inherited inline field, or — when
782
- * `panel` is set — a trigger button plus a `eun-modal[kind=search]`
851
+ * `panel` is set — a trigger (the `trigger` slot, falling back to a
852
+ * built-in button) plus a `eun-modal[kind=search]`
783
853
  * (flush, no padding, no close button) containing the real field (reused
784
854
  * via `renderField`, wrapped for its own full-width/no-border styling) and
785
855
  * a `results` slot.
786
856
  * @returns The rendered HTML template
787
857
  */
858
+ /* eslint-disable lit-a11y/click-events-have-key-events */
788
859
  render() {
789
860
  if (!this.panel) {
790
861
  return super.render();
@@ -793,25 +864,29 @@ let EunomiaSearch = EunomiaSearch_1 = class EunomiaSearch extends AnchorPosition
793
864
  return html `
794
865
  <div id="wrapper">
795
866
  ${this.renderFormLabels("search-trigger")}
796
- <button
797
- id="search-trigger"
798
- type="button"
799
- aria-haspopup="dialog"
800
- aria-label=${ifDefined(this.label)}
801
- aria-keyshortcuts=${ifDefined(this.shortcut ? "Control+K Meta+K" : undefined)}
802
- ?disabled=${this.disabled}
803
- @click=${this.openPanel}
804
- >
805
- ${this.icon
867
+ <slot name="trigger" @click=${this.handleTriggerClick}>
868
+ <button
869
+ id="search-trigger"
870
+ part="trigger"
871
+ type="button"
872
+ aria-haspopup="dialog"
873
+ aria-label=${ifDefined(this.label)}
874
+ aria-keyshortcuts=${ifDefined(this.shortcut ? "Control+K Meta+K" : undefined)}
875
+ ?disabled=${this.disabled}
876
+ >
877
+ ${this.icon
806
878
  ? html `<eun-icon
807
- id="trigger-icon"
808
- name=${this.icon}
809
- size="16"
810
- ></eun-icon>`
879
+ id="trigger-icon"
880
+ name=${this.icon}
881
+ size="16"
882
+ ></eun-icon>`
811
883
  : nothing}
812
- <span id="trigger-text" class="empty">${this.placeholder || ""}</span>
813
- ${this.renderTriggerIndicator()}
814
- </button>
884
+ <span id="trigger-text" class="empty"
885
+ >${this.placeholder || ""}</span
886
+ >
887
+ ${this.renderTriggerIndicator()}
888
+ </button>
889
+ </slot>
815
890
  <eun-modal
816
891
  id="panel"
817
892
  kind="search"
@@ -857,7 +932,7 @@ __decorate([
857
932
  property()
858
933
  ], EunomiaSearch.prototype, "icon", void 0);
859
934
  __decorate([
860
- property({ type: Boolean })
935
+ property({ converter: defaultTrueConverter })
861
936
  ], EunomiaSearch.prototype, "clearable", void 0);
862
937
  __decorate([
863
938
  property({ type: Boolean })
@@ -880,6 +955,9 @@ __decorate([
880
955
  __decorate([
881
956
  property({ attribute: "no-results-label" })
882
957
  ], EunomiaSearch.prototype, "noResultsLabel", void 0);
958
+ __decorate([
959
+ property()
960
+ ], EunomiaSearch.prototype, "filter", void 0);
883
961
  __decorate([
884
962
  state()
885
963
  ], EunomiaSearch.prototype, "isOpen", void 0);
@@ -892,6 +970,9 @@ __decorate([
892
970
  __decorate([
893
971
  query("#field-wrapper")
894
972
  ], EunomiaSearch.prototype, "fieldWrapperEl", void 0);
973
+ __decorate([
974
+ query('slot[name="trigger"]')
975
+ ], EunomiaSearch.prototype, "triggerSlot", void 0);
895
976
  __decorate([
896
977
  query("#search-trigger")
897
978
  ], EunomiaSearch.prototype, "triggerEl", void 0);
@@ -113,6 +113,8 @@ const search = css `
113
113
  position: relative;
114
114
  }
115
115
  #panel-field #field {
116
+ box-sizing: border-box;
117
+ height: auto;
116
118
  width: 100%;
117
119
  border: none;
118
120
  border-radius: 0;
@@ -6,6 +6,8 @@ import { FormMixinInterface } from "../../../mixins/form.mixin.js";
6
6
  import { FormLabelMixinInterface } from "../../../mixins/form-label.mixin.js";
7
7
  import { EditableMixinInterface } from "../../../mixins/editable.mixin.js";
8
8
  import { FieldAppearanceMixinInterface } from "../../../mixins/field-appearance.mixin.js";
9
+ import { RoundedMixinInterface } from "../../../mixins/rounded.mixin.js";
10
+ import { FieldSizeMixinInterface } from "../../../mixins/field-size.mixin.js";
9
11
  import { AnchorPositionMixinInterface } from "../../../mixins/anchor-position.mixin.js";
10
12
  import { EunomiaIconName } from "../../content/icon/icon.type.js";
11
13
  import { Constructor } from "../../../utils/constructor.type.js";
@@ -23,7 +25,7 @@ type SelectValue = string | string[];
23
25
  * Type of the select base used to declare every necessary Mixin interface elements.
24
26
  * @internal
25
27
  */
26
- type EunomiaSelectBase = LitElement & DisabledMixinInterface & ReadonlyMixinInterface & ValidatorsMixinInterface<SelectValue> & FormMixinInterface<SelectValue> & EditableMixinInterface & FormLabelMixinInterface & FieldAppearanceMixinInterface & AnchorPositionMixinInterface & LocalizableMixinInterface;
28
+ type EunomiaSelectBase = LitElement & DisabledMixinInterface & ReadonlyMixinInterface & ValidatorsMixinInterface<SelectValue> & FormMixinInterface<SelectValue> & EditableMixinInterface & FormLabelMixinInterface & FieldAppearanceMixinInterface & RoundedMixinInterface & FieldSizeMixinInterface & AnchorPositionMixinInterface & LocalizableMixinInterface;
27
29
  declare const EunomiaSelectMixinBase: Constructor<EunomiaSelectBase>;
28
30
  /**
29
31
  * A field that picks one or more values from a list of option and optgroup
@@ -51,6 +53,8 @@ declare const EunomiaSelectMixinBase: Constructor<EunomiaSelectBase>;
51
53
  * @prop {boolean} [native=false] - Renders the field as a real select, using the browser's own dropdown instead of the listbox panel. Takes priority over searchable and multiple, neither of which a native dropdown can support
52
54
  * @prop {boolean} [multiple=false] - Whether more than one option can be picked at once. Each option then renders a checkbox, and picking one toggles it without closing the dropdown, so value becomes an array of every picked option's value. Ignored while native is set
53
55
  * @prop {'outline' | 'flat' | 'underline'} [appearance='outline'] - How the field's surface is drawn: a bordered box, a muted background with no border, or a bottom border only
56
+ * @prop {boolean} [rounded=false] - Whether the field is fully rounded, as a pill. A custom border radius still wins over it
57
+ * @prop {'xs' | 's' | 'm' | 'l' | 'xl'} [size='m'] - The field size. A field and a button of the same size share the same height
54
58
  * @prop {string} label - The title label of the field, rendered through the label component
55
59
  * @prop {string} instructions - Instructions displayed below the label
56
60
  * @prop {string} name - The field name
@@ -104,10 +108,12 @@ declare const EunomiaSelectMixinBase: Constructor<EunomiaSelectBase>;
104
108
  * @cssprop [--field-confirm-link-color] - Sets the color of the confirm slot's link, while editing in readonlyAvailable mode
105
109
  * @cssprop [--field-confirm-link-color-hover] - Sets the hover color of the confirm slot's link
106
110
  * @cssprop [--field-listbox-max-height=260px] - Sets the maximum height of the dropdown panel, past which it scrolls
111
+ * @cssprop [--field-listbox-border-radius] - Sets the corner radius of the dropdown panel, independent of the field's own radius
107
112
  * @cssprop [--field-listbox-transition-duration=200ms] - Sets the duration of the dropdown panel's open and close height transition, disabled entirely by the no-animation attribute or reduced motion settings
108
113
  * @cssprop [--eun-viewport-inset-top] - Reserves space, such as a page's sticky header height, that the dropdown panel keeps clear of at the top of the viewport. Set on the root or an ancestor rather than on the field itself
109
114
  * @cssprop [--eun-viewport-inset-bottom] - The same as eun-viewport-inset-top, reserved at the bottom of the viewport, such as for a sticky footer
110
115
  * @cssprop [--field-font-size] - Sets the font size of the field's value and placeholder
116
+ * @cssprop [--field-line-height] - Sets the line height of the field's value and placeholder
111
117
  * @cssprop [--field-label-font-size] - Sets the font size of the label
112
118
  * @cssprop [--field-message-font-size] - Sets the font size of the hint, error messages and instructions
113
119
  * @element eun-select
@@ -16,6 +16,8 @@ import { FormMixin } from "../../../mixins/form.mixin.js";
16
16
  import { FormLabelMixin, } from "../../../mixins/form-label.mixin.js";
17
17
  import { EditableMixin, } from "../../../mixins/editable.mixin.js";
18
18
  import { FieldAppearanceMixin, } from "../../../mixins/field-appearance.mixin.js";
19
+ import { RoundedMixin, } from "../../../mixins/rounded.mixin.js";
20
+ import { FieldSizeMixin, } from "../../../mixins/field-size.mixin.js";
19
21
  import { AnchorPositionMixin, } from "../../../mixins/anchor-position.mixin.js";
20
22
  import { ChangeEvent } from "../../../events/change.event.js";
21
23
  import { formLabelsStyles } from "../shared-styles/form-labels.styles.js";
@@ -33,7 +35,7 @@ export const EunomiaSelectTagName = "eun-select";
33
35
  * @internal
34
36
  */
35
37
  const Base = DisabledMixin(ValidatorsMixin(LitElement));
36
- const EunomiaSelectMixinBase = LocalizableMixin(AnchorPositionMixin(FieldAppearanceMixin(FormLabelMixin(ReadonlyMixin(EditableMixin(FormMixin(Base)))))));
38
+ const EunomiaSelectMixinBase = LocalizableMixin(AnchorPositionMixin(RoundedMixin(FieldSizeMixin(FieldAppearanceMixin(FormLabelMixin(ReadonlyMixin(EditableMixin(FormMixin(Base)))))))));
37
39
  /**
38
40
  * A field that picks one or more values from a list of option and optgroup
39
41
  * children. Set searchable to let the field also filter that list as the
@@ -60,6 +62,8 @@ const EunomiaSelectMixinBase = LocalizableMixin(AnchorPositionMixin(FieldAppeara
60
62
  * @prop {boolean} [native=false] - Renders the field as a real select, using the browser's own dropdown instead of the listbox panel. Takes priority over searchable and multiple, neither of which a native dropdown can support
61
63
  * @prop {boolean} [multiple=false] - Whether more than one option can be picked at once. Each option then renders a checkbox, and picking one toggles it without closing the dropdown, so value becomes an array of every picked option's value. Ignored while native is set
62
64
  * @prop {'outline' | 'flat' | 'underline'} [appearance='outline'] - How the field's surface is drawn: a bordered box, a muted background with no border, or a bottom border only
65
+ * @prop {boolean} [rounded=false] - Whether the field is fully rounded, as a pill. A custom border radius still wins over it
66
+ * @prop {'xs' | 's' | 'm' | 'l' | 'xl'} [size='m'] - The field size. A field and a button of the same size share the same height
63
67
  * @prop {string} label - The title label of the field, rendered through the label component
64
68
  * @prop {string} instructions - Instructions displayed below the label
65
69
  * @prop {string} name - The field name
@@ -113,10 +117,12 @@ const EunomiaSelectMixinBase = LocalizableMixin(AnchorPositionMixin(FieldAppeara
113
117
  * @cssprop [--field-confirm-link-color] - Sets the color of the confirm slot's link, while editing in readonlyAvailable mode
114
118
  * @cssprop [--field-confirm-link-color-hover] - Sets the hover color of the confirm slot's link
115
119
  * @cssprop [--field-listbox-max-height=260px] - Sets the maximum height of the dropdown panel, past which it scrolls
120
+ * @cssprop [--field-listbox-border-radius] - Sets the corner radius of the dropdown panel, independent of the field's own radius
116
121
  * @cssprop [--field-listbox-transition-duration=200ms] - Sets the duration of the dropdown panel's open and close height transition, disabled entirely by the no-animation attribute or reduced motion settings
117
122
  * @cssprop [--eun-viewport-inset-top] - Reserves space, such as a page's sticky header height, that the dropdown panel keeps clear of at the top of the viewport. Set on the root or an ancestor rather than on the field itself
118
123
  * @cssprop [--eun-viewport-inset-bottom] - The same as eun-viewport-inset-top, reserved at the bottom of the viewport, such as for a sticky footer
119
124
  * @cssprop [--field-font-size] - Sets the font size of the field's value and placeholder
125
+ * @cssprop [--field-line-height] - Sets the line height of the field's value and placeholder
120
126
  * @cssprop [--field-label-font-size] - Sets the font size of the label
121
127
  * @cssprop [--field-message-font-size] - Sets the font size of the hint, error messages and instructions
122
128
  * @element eun-select
@@ -16,7 +16,8 @@ import { css } from "lit";
16
16
  * [--field-icon-color] - Set a custom color to the leading icon and action buttons
17
17
  * [--field-error-color] - Set a custom color to the error state content
18
18
  * [--field-hint-color] - Set a custom color to the hint message
19
- * [--field-font-size] - Set a custom font size for the field's value and placeholder
19
+ * [--field-font-size] - Set a custom font size for the field's value and placeholder, for every size
20
+ * [--field-line-height] - Set a custom line height for the field's value and placeholder, for every size
20
21
  * [--field-label-font-size] - Set a custom font size for the label (see label.styles.ts)
21
22
  * [--field-message-font-size] - Set a custom font size for the hint, error and instructions (see form-labels.styles.ts)
22
23
  * [--field-focus-outline-color] - Set a custom color to the focus outline
@@ -43,22 +44,50 @@ const base = css `
43
44
  --field-focus-outline-color,
44
45
  var(--eun-color-primary-500, #3d6fff)
45
46
  );
46
- --_borderRadius: var(
47
- --field-border-radius,
48
- var(--eun-border-radius-m, 8px)
49
- );
47
+ --_defaultBorderRadius: var(--eun-border-radius-m, 8px);
48
+ --_borderRadius: var(--field-border-radius, var(--_defaultBorderRadius));
49
+ --_defaultPaddingInline: var(--eun-space-s, 8px);
50
+ --_defaultPaddingBlock: var(--eun-space-s, 8px);
51
+ --_defaultFontSize: var(--eun-font-size-s, 14px);
52
+ --_defaultLineHeight: 1rem;
53
+ --_paddingBlock: var(--field-padding-block, var(--_defaultPaddingBlock));
50
54
  --_padding: var(
51
55
  --field-padding,
52
- var(--field-padding-block, var(--eun-space-s, 8px))
53
- var(--field-padding-inline, var(--eun-space-s, 8px))
56
+ var(--_paddingBlock)
57
+ var(--field-padding-inline, var(--_defaultPaddingInline))
54
58
  );
59
+ --_lineHeight: var(--field-line-height, var(--_defaultLineHeight));
55
60
  --_shadow: var(--field-shadow, none);
56
61
  font-family: var(--eun-font-family, "Mulish", sans-serif);
57
- font-size: var(--field-font-size, var(--eun-font-size-s, 14px));
62
+ font-size: var(--field-font-size, var(--_defaultFontSize));
58
63
  display: block;
59
64
  width: 100%;
60
65
  outline: none;
61
66
  }
67
+ :host([rounded]) {
68
+ --_defaultBorderRadius: var(--eun-border-radius-xl, 999px);
69
+ --_defaultPaddingInline: var(--eun-space-m, 16px);
70
+ }
71
+ :host([size="xs"]) {
72
+ --_defaultFontSize: var(--eun-font-size-xs, 10px);
73
+ --_defaultPaddingBlock: var(--eun-space-xxs, 1.6px);
74
+ --_defaultLineHeight: 10px;
75
+ }
76
+ :host([size="s"]) {
77
+ --_defaultFontSize: var(--eun-font-size-xs, 10px);
78
+ --_defaultPaddingBlock: calc(var(--eun-space-s, 8px) / 2);
79
+ --_defaultLineHeight: 10px;
80
+ }
81
+ :host([size="l"]) {
82
+ --_defaultFontSize: var(--eun-font-size-m, 16px);
83
+ --_defaultPaddingBlock: calc(var(--eun-space-s, 8px) * 1.25);
84
+ --_defaultLineHeight: 1.1rem;
85
+ }
86
+ :host([size="xl"]) {
87
+ --_defaultFontSize: var(--eun-font-size-l, 1.3rem);
88
+ --_defaultPaddingBlock: calc(var(--eun-space-s, 8px) * 1.5);
89
+ --_defaultLineHeight: 1.3rem;
90
+ }
62
91
  #wrapper {
63
92
  display: flex;
64
93
  flex-direction: column;
@@ -77,6 +106,7 @@ const base = css `
77
106
  border: var(--eun-border-width-m, 1.5px) solid var(--_borderColor);
78
107
  border-radius: var(--_borderRadius);
79
108
  padding: var(--_padding);
109
+ line-height: var(--_lineHeight);
80
110
  outline: 1px solid transparent;
81
111
  box-shadow: var(--_shadow);
82
112
  box-sizing: border-box;
@@ -85,6 +115,12 @@ const base = css `
85
115
  outline-color var(--eun-duration-s, 150ms) var(--eun-easing-default, ease);
86
116
  appearance: none;
87
117
  }
118
+ input#field,
119
+ button#field {
120
+ box-sizing: content-box;
121
+ min-width: 0;
122
+ height: var(--_lineHeight);
123
+ }
88
124
  #field::placeholder {
89
125
  color: var(--_placeholderColor);
90
126
  }
@@ -181,6 +217,7 @@ const base = css `
181
217
  }
182
218
  #readonly-display {
183
219
  padding: var(--_padding);
220
+ line-height: var(--_lineHeight);
184
221
  border: var(--eun-border-width-m, 1.5px) solid transparent;
185
222
  border-radius: var(--_borderRadius);
186
223
  box-sizing: border-box;