@eunomia/elements 1.0.0-beta.2 → 1.0.0-beta.3

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 (98) hide show
  1. package/dist/cdn/components/chunks/chunk-3LMI7KUA.js +48 -0
  2. package/dist/cdn/components/chunks/{chunk-74HHV4CX.js → chunk-53QH6MPM.js} +2 -2
  3. package/dist/cdn/components/chunks/{chunk-7WTSHE7M.js → chunk-EAGGN3ZI.js} +1 -1
  4. package/dist/cdn/components/chunks/chunk-GU6BVMFJ.js +1 -0
  5. package/dist/cdn/components/chunks/chunk-KNM5QHDK.js +38 -0
  6. package/dist/cdn/components/chunks/{chunk-AQQMW353.js → chunk-TBKKTJOX.js} +1 -1
  7. package/dist/cdn/components/chunks/chunk-YYCEPZUN.js +1 -0
  8. package/dist/cdn/components/chunks/{chunk-27Q2SLRY.js → chunk-Z6PJYSC3.js} +30 -11
  9. package/dist/cdn/components/eun-animate.js +178 -2
  10. package/dist/cdn/components/eun-button.js +1 -1
  11. package/dist/cdn/components/eun-card.js +15 -7
  12. package/dist/cdn/components/eun-carousel.js +1 -1
  13. package/dist/cdn/components/eun-check-card.js +1 -1
  14. package/dist/cdn/components/eun-checkbox-list.js +2 -2
  15. package/dist/cdn/components/eun-checkbox.js +1 -1
  16. package/dist/cdn/components/eun-chip.js +11 -14
  17. package/dist/cdn/components/eun-collapse.js +8 -15
  18. package/dist/cdn/components/eun-color-picker.js +19 -19
  19. package/dist/cdn/components/eun-counter.js +4 -4
  20. package/dist/cdn/components/eun-date-picker.js +2 -2
  21. package/dist/cdn/components/eun-icon.js +1 -1
  22. package/dist/cdn/components/eun-input-file.js +11 -11
  23. package/dist/cdn/components/eun-input-phone.js +3 -3
  24. package/dist/cdn/components/eun-input.js +1 -1
  25. package/dist/cdn/components/eun-radio-card.js +1 -1
  26. package/dist/cdn/components/eun-radio-list.js +3 -3
  27. package/dist/cdn/components/eun-radio.js +1 -1
  28. package/dist/cdn/components/eun-search.js +1 -1
  29. package/dist/cdn/components/eun-select.js +33 -12
  30. package/dist/cdn/components/eun-sidebar.js +1 -1
  31. package/dist/cdn/components/eun-slider.js +3 -3
  32. package/dist/cdn/components/eun-sliding-card.js +4 -8
  33. package/dist/cdn/components/eun-switch.js +1 -1
  34. package/dist/cdn/components/eun-table.js +1 -1
  35. package/dist/cdn/components/eun-tag.js +13 -9
  36. package/dist/cdn/components/eun-textarea.js +11 -11
  37. package/dist/cdn/components/eun-time-picker.js +2 -2
  38. package/dist/cdn/components/eun-toggle-group.js +2 -2
  39. package/dist/cdn/components/eun-toggle-item.js +1 -1
  40. package/dist/cdn/components/eun-toggle.js +2 -2
  41. package/dist/cdn/components/eun-toolbar.js +14 -12
  42. package/dist/cdn/components/eun-tooltip.js +2 -2
  43. package/dist/components/action/button/button.d.ts +5 -6
  44. package/dist/components/action/button/button.js +5 -6
  45. package/dist/components/action/button/button.styles.js +31 -9
  46. package/dist/components/action/toggle/toggle-group.js +1 -1
  47. package/dist/components/action/toggle/toggle-item.styles.js +1 -1
  48. package/dist/components/action/toggle/toggle.js +1 -1
  49. package/dist/components/content/card/card.d.ts +1 -1
  50. package/dist/components/content/card/card.js +1 -1
  51. package/dist/components/content/card/card.styles.js +13 -5
  52. package/dist/components/content/card/sliding-card.styles.js +0 -4
  53. package/dist/components/content/chip/chip.styles.js +6 -9
  54. package/dist/components/content/collapse/collapse.styles.js +0 -7
  55. package/dist/components/content/icon/icon.d.ts +48 -5
  56. package/dist/components/content/icon/icon.js +92 -20
  57. package/dist/components/content/tag/tag.styles.js +10 -6
  58. package/dist/components/content/tooltip/tooltip.styles.js +2 -2
  59. package/dist/components/form/checkbox/checkbox-list.js +1 -1
  60. package/dist/components/form/color-picker/color-picker.js +9 -7
  61. package/dist/components/form/counter/counter.js +3 -3
  62. package/dist/components/form/date-picker/date-picker.js +3 -3
  63. package/dist/components/form/input/input.js +2 -2
  64. package/dist/components/form/input-file/input-file.js +5 -5
  65. package/dist/components/form/input-phone/input-phone.js +3 -3
  66. package/dist/components/form/radio/radio-list.js +2 -2
  67. package/dist/components/form/select/select.d.ts +16 -0
  68. package/dist/components/form/select/select.js +28 -5
  69. package/dist/components/form/select/select.styles.js +11 -0
  70. package/dist/components/form/slider/slider.js +2 -2
  71. package/dist/components/form/textarea/textarea.js +4 -4
  72. package/dist/components/form/time-picker/time-picker.js +3 -3
  73. package/dist/components/layout/animate/animate.d.ts +40 -5
  74. package/dist/components/layout/animate/animate.js +30 -11
  75. package/dist/components/layout/animate/animate.styles.d.ts +1 -1
  76. package/dist/components/layout/animate/animate.styles.js +191 -1
  77. package/dist/components/navigation/carousel/carousel.styles.js +1 -1
  78. package/dist/components/navigation/toolbar/toolbar.d.ts +22 -0
  79. package/dist/components/navigation/toolbar/toolbar.js +28 -2
  80. package/dist/components/navigation/toolbar/toolbar.styles.js +4 -3
  81. package/dist/events/change.event.d.ts +13 -1
  82. package/dist/events/change.event.js +2 -1
  83. package/dist/index.d.ts +1 -1
  84. package/dist/index.js +1 -1
  85. package/dist/mixins/check.mixin.js +6 -2
  86. package/dist/mixins/form.mixin.d.ts +4 -0
  87. package/dist/mixins/form.mixin.js +78 -2
  88. package/dist/mixins/radio.mixin.js +6 -2
  89. package/dist/ssr/animate-ssr.css +149 -0
  90. package/dist/ssr/overlay-ssr.css +57 -0
  91. package/dist/ssr/sidebar-ssr.css +57 -0
  92. package/dist/ssr/toolbar-ssr.css +41 -0
  93. package/package.json +9 -3
  94. package/dist/cdn/components/chunks/chunk-2IQUMAMI.js +0 -38
  95. package/dist/cdn/components/chunks/chunk-7Q6AHVI5.js +0 -1
  96. package/dist/cdn/components/chunks/chunk-DENMN3WH.js +0 -48
  97. package/dist/cdn/components/chunks/chunk-ONYUXQV2.js +0 -1
  98. package/dist/components/layout/animate/animate-ssr.css +0 -64
@@ -36,7 +36,7 @@ declare const EunomiaCard_base: import("../../../utils/constructor.type.js").Con
36
36
  * @cssprop [--card-shadow] - Sets a box shadow for the default appearance, none by default. Replaced by a subtle elevation for the raised appearance, and unused by fill
37
37
  * @cssprop [--card-raised-hover-shadow] - Sets the box shadow on hover for the raised appearance
38
38
  * @cssprop [--card-hover-transform] - Sets a transform applied on hover or focus, for every appearance. Setting this always applies it, link or not. Left unset, a small lift only applies to linked cards, since a non-linked card gets the border, tint, or shadow change on hover but stays in place otherwise
39
- * @cssprop [--card-hover-border-color] - Sets the border color on hover or focus, for the default appearance only. Defaults to the primary color
39
+ * @cssprop [--card-hover-border-color] - Sets the border color on hover or focus, for every appearance, including fill and raised, whose border is otherwise transparent rather than absent, reserving the space so the color reveals with no layout shift. Defaults to the primary color
40
40
  * @cssprop [--card-hover-background] - Sets the background color on hover or focus, for the default appearance only, unchanged by default. See card-fill-hover-background for the fill appearance
41
41
  * @cssprop [--card-hover-duration] - Sets the duration of the hover transition
42
42
  * @cssprop [--card-transition] - Overrides the card's whole hover transition, for example to animate an extra property alongside the built-in ones. Defaults to transform, outline, box shadow, background color, and border color
@@ -44,7 +44,7 @@ export const EunomiaCardTagName = "eun-card";
44
44
  * @cssprop [--card-shadow] - Sets a box shadow for the default appearance, none by default. Replaced by a subtle elevation for the raised appearance, and unused by fill
45
45
  * @cssprop [--card-raised-hover-shadow] - Sets the box shadow on hover for the raised appearance
46
46
  * @cssprop [--card-hover-transform] - Sets a transform applied on hover or focus, for every appearance. Setting this always applies it, link or not. Left unset, a small lift only applies to linked cards, since a non-linked card gets the border, tint, or shadow change on hover but stays in place otherwise
47
- * @cssprop [--card-hover-border-color] - Sets the border color on hover or focus, for the default appearance only. Defaults to the primary color
47
+ * @cssprop [--card-hover-border-color] - Sets the border color on hover or focus, for every appearance, including fill and raised, whose border is otherwise transparent rather than absent, reserving the space so the color reveals with no layout shift. Defaults to the primary color
48
48
  * @cssprop [--card-hover-background] - Sets the background color on hover or focus, for the default appearance only, unchanged by default. See card-fill-hover-background for the fill appearance
49
49
  * @cssprop [--card-hover-duration] - Sets the duration of the hover transition
50
50
  * @cssprop [--card-transition] - Overrides the card's whole hover transition, for example to animate an extra property alongside the built-in ones. Defaults to transform, outline, box shadow, background color, and border color
@@ -72,15 +72,23 @@ const base = css `
72
72
  --card-hover-background,
73
73
  var(--card-background, var(--eun-surface-color, #ffffff))
74
74
  );
75
- border-color: var(
76
- --card-hover-border-color,
77
- var(--eun-color-primary-500, #3d6fff)
78
- );
79
75
  }
80
76
 
81
77
  :host([appearance="fill"]) #card,
82
78
  :host([appearance="raised"]) #card {
83
- border: none;
79
+ border-color: transparent;
80
+ }
81
+
82
+ :host([appearance="fill"]) #card:hover,
83
+ :host([appearance="fill"]) #card:focus-visible,
84
+ :host([appearance="raised"]) #card:hover,
85
+ :host([appearance="raised"]) #card:focus-visible,
86
+ #card:hover,
87
+ #card:focus-visible {
88
+ border-color: var(
89
+ --card-hover-border-color,
90
+ var(--eun-color-primary-500, #3d6fff)
91
+ );
84
92
  }
85
93
 
86
94
  :host([appearance="fill"]) #card {
@@ -38,10 +38,6 @@ const base = css `
38
38
  width: var(--sliding-card-width, 300px);
39
39
  height: var(--sliding-card-height, 280px);
40
40
  font-family: var(--eun-font-family, "Mulish", sans-serif);
41
- background: var(
42
- --sliding-card-front-background,
43
- var(--eun-surface-color, #ffffff)
44
- );
45
41
  clip-path: inset(0 0 calc(-1 * var(--sliding-card-fade-height, 60px)) 0);
46
42
  }
47
43
 
@@ -66,15 +66,15 @@ const base = css `
66
66
  :host([color="light"]:not([severity])) {
67
67
  --_background: var(
68
68
  --chip-background-color,
69
- var(--eun-color-grey-100, #f7f8fa)
69
+ var(--eun-color-grey-0, #ffffff)
70
70
  );
71
71
  --_backgroundFilled: var(
72
72
  --chip-background-color,
73
- var(--eun-color-grey-100, #f7f8fa)
73
+ var(--eun-color-grey-0, #ffffff)
74
74
  );
75
75
  --_border-color: var(
76
76
  --chip-border-color,
77
- var(--eun-color-grey-100, #f7f8fa)
77
+ var(--eun-color-grey-0, #ffffff)
78
78
  );
79
79
  --_color: var(--chip-color, var(--eun-color-grey-600, #666e8a));
80
80
  }
@@ -84,15 +84,12 @@ const base = css `
84
84
  :host([color="medium"]:not([severity])) {
85
85
  --_background: var(
86
86
  --chip-background-color,
87
- var(--eun-color-grey-200, #ecedf0)
88
- );
89
- --_backgroundFilled: var(
90
- --chip-background-color,
91
- var(--eun-color-grey-500, #8d93aa)
87
+ color-mix(in srgb, var(--eun-color-grey-700, #484e61) 20%, transparent)
92
88
  );
89
+ --_backgroundFilled: var(--_background);
93
90
  --_border-color: var(
94
91
  --chip-border-color,
95
- var(--eun-color-grey-300, #dee0e7)
92
+ var(--eun-color-grey-700, #484e61)
96
93
  );
97
94
  --_color: var(--chip-color, var(--eun-color-grey-900, #000000));
98
95
  }
@@ -186,13 +186,6 @@ export const collapseStyles = css `
186
186
  #rich-text :last-child {
187
187
  margin-block-end: 0;
188
188
  }
189
- #rich-text p {
190
- margin-block: var(--eun-space-xs, 4px) 0;
191
- }
192
- #rich-text ul {
193
- margin-block: var(--eun-space-xs, 4px) 0;
194
- padding-inline-start: var(--eun-space-m, 16px);
195
- }
196
189
  #rich-text a {
197
190
  color: var(--_richTextLinkColor);
198
191
  }
@@ -4,6 +4,18 @@ import { EunomiaIconName, EunomiaIconType } from "./icon.type.js";
4
4
  * Tag name of the icon component.
5
5
  */
6
6
  export declare const EunomiaIconTagName = "eun-icon";
7
+ /**
8
+ * Sets the URL the icon SVGs are served from, laid out as
9
+ * `<baseUrl>/<shape>/<name>.svg` (the `icons` folder shipped next to this
10
+ * component in the package). Needed when a bundler such as Turbopack
11
+ * rewrites `import.meta.url` to a `file:` URL, which can't be fetched from
12
+ * the browser. Copy or symlink that folder into your public assets, then
13
+ * call this once before any icon renders, e.g.
14
+ * `setIconBaseUrl("/eunomia-icons")`.
15
+ *
16
+ * @param url - Absolute or root-relative URL of the icons folder
17
+ */
18
+ export declare function setIconBaseUrl(url: string): void;
7
19
  /**
8
20
  * An SVG-based icon that loads and renders dynamically based on its name.
9
21
  *
@@ -55,6 +67,26 @@ export declare class EunomiaIcon extends LitElement {
55
67
  * Gets the current size value set for the icon.
56
68
  */
57
69
  get size(): string | undefined;
70
+ /**
71
+ * Registers this icon so a later `setIconBaseUrl` call can reload it.
72
+ */
73
+ connectedCallback(): void;
74
+ /**
75
+ * Unregisters this icon, so a detached one isn't kept alive or reloaded.
76
+ */
77
+ disconnectedCallback(): void;
78
+ /**
79
+ * Re-fetches the current icon, used when the icons' base URL changes
80
+ * after this icon already tried (and possibly failed) to load.
81
+ *
82
+ * @internal
83
+ */
84
+ reload(): void;
85
+ /**
86
+ * Loads the SVG whenever `name` or `shape` changes.
87
+ *
88
+ * @param _changedProperties - The properties changed by this update
89
+ */
58
90
  protected updated(_changedProperties: PropertyValues): void;
59
91
  /**
60
92
  * Dynamically imports the SVG markup for the given icon name
@@ -65,11 +97,22 @@ export declare class EunomiaIcon extends LitElement {
65
97
  private updateSvg;
66
98
  /**
67
99
  * Fetches the raw markup for a single icon file, co-located with this
68
- * component (see `./icons/<shape>/<name>.svg`), resolved against
69
- * `MODULE_URL` rather than `import.meta.url` directly — see that
70
- * constant's doc comment for why. This still resolves correctly
71
- * unbundled, straight off the filesystem/CDN, exactly like a direct
72
- * `new URL(..., import.meta.url)` would.
100
+ * component (see `./icons/<shape>/<name>.svg`). Deliberately never
101
+ * passes a templated path straight to `new URL(...)` : Turbopack
102
+ * special-cases that exact call shape (a string with `${...}`
103
+ * interpolation as the first argument) for static asset resolution
104
+ * even when the base is routed through `MODULE_URL` rather than a bare
105
+ * `import.meta.url` literal — with ~5500 icon files across 2
106
+ * interpolated segments, it can't resolve the pattern and silently
107
+ * falls back to a single wrong file for every request instead of
108
+ * erroring. Building the URL from `MODULE_URL`'s own directory via
109
+ * plain string ops keeps every `new URL()` call single-argument and
110
+ * literal-free, so there's no templated call shape left for any
111
+ * bundler to intercept. This still resolves correctly unbundled,
112
+ * straight off the filesystem/CDN, exactly like the original
113
+ * `new URL(templatedPath, import.meta.url)` would have. A base set
114
+ * through `setIconBaseUrl` takes priority, and a bundler-rewritten
115
+ * `file:` module URL throws an explicit error instead of a CSP one.
73
116
  *
74
117
  * @param shape - Icon subfolder to fetch from
75
118
  * @param name - Icon file name, without extension
@@ -30,20 +30,39 @@ export const EunomiaIconTagName = "eun-icon";
30
30
  const ICON_KEYWORD_REGEX = new RegExp(`_(outline|round|sharp)$`);
31
31
  /**
32
32
  * This module's own URL, captured once as a plain string rather than
33
- * read inline. Bundlers (Vite, webpack, Turbopack) special-case the
34
- * exact call shape `new URL(<literal>, import.meta.url)` to statically
35
- * resolve/copy the referenced asset. With two interpolated segments
36
- * (shape and name, see `fetchIconSvg`) across ~5500 icon files, that
37
- * static analysis has to fall back to a runtime lookup table keyed by
38
- * the computed path mature under webpack, but buggy under Turbopack,
39
- * where every request resolves to the same wrong file regardless of
40
- * the name/shape asked for. Routing the base through this variable
41
- * keeps that call shape out of `fetchIconSvg` in every bundler, so the
42
- * lookup is a plain runtime `fetch` against a file already shipped,
43
- * byte-for-byte, at this path (see `scripts/copy-assets.mjs`) — the
44
- * same result, whether bundled or not.
33
+ * read inline, so `fetchIconSvg` never has to pass a templated path
34
+ * straight to `new URL(...)` see that method's own doc comment for why
35
+ * that call shape is unsafe under Turbopack across ~5500 icon files.
36
+ * `import.meta.url` itself still needs reading somewhere, just not as
37
+ * part of a `new URL(<literal-with-interpolation>, ...)` call, which is
38
+ * the specific shape bundlers key their static asset resolution off of.
45
39
  */
46
40
  const MODULE_URL = import.meta.url;
41
+ /**
42
+ * Base URL set through `setIconBaseUrl`, taking priority over the URL
43
+ * derived from `MODULE_URL` when defined.
44
+ */
45
+ let iconBaseUrl;
46
+ /**
47
+ * Icons currently in the DOM, so `setIconBaseUrl` can reload the ones
48
+ * that already tried to load before the base URL was set.
49
+ */
50
+ const connectedIcons = new Set();
51
+ /**
52
+ * Sets the URL the icon SVGs are served from, laid out as
53
+ * `<baseUrl>/<shape>/<name>.svg` (the `icons` folder shipped next to this
54
+ * component in the package). Needed when a bundler such as Turbopack
55
+ * rewrites `import.meta.url` to a `file:` URL, which can't be fetched from
56
+ * the browser. Copy or symlink that folder into your public assets, then
57
+ * call this once before any icon renders, e.g.
58
+ * `setIconBaseUrl("/eunomia-icons")`.
59
+ *
60
+ * @param url - Absolute or root-relative URL of the icons folder
61
+ */
62
+ export function setIconBaseUrl(url) {
63
+ iconBaseUrl = url.endsWith("/") ? url.slice(0, -1) : url;
64
+ connectedIcons.forEach((icon) => icon.reload());
65
+ }
47
66
  /**
48
67
  * An SVG-based icon that loads and renders dynamically based on its name.
49
68
  *
@@ -98,6 +117,34 @@ let EunomiaIcon = class EunomiaIcon extends LitElement {
98
117
  get size() {
99
118
  return __classPrivateFieldGet(this, _EunomiaIcon_size, "f");
100
119
  }
120
+ /**
121
+ * Registers this icon so a later `setIconBaseUrl` call can reload it.
122
+ */
123
+ connectedCallback() {
124
+ super.connectedCallback();
125
+ connectedIcons.add(this);
126
+ }
127
+ /**
128
+ * Unregisters this icon, so a detached one isn't kept alive or reloaded.
129
+ */
130
+ disconnectedCallback() {
131
+ super.disconnectedCallback();
132
+ connectedIcons.delete(this);
133
+ }
134
+ /**
135
+ * Re-fetches the current icon, used when the icons' base URL changes
136
+ * after this icon already tried (and possibly failed) to load.
137
+ *
138
+ * @internal
139
+ */
140
+ reload() {
141
+ this.updateSvg(this.name);
142
+ }
143
+ /**
144
+ * Loads the SVG whenever `name` or `shape` changes.
145
+ *
146
+ * @param _changedProperties - The properties changed by this update
147
+ */
101
148
  updated(_changedProperties) {
102
149
  if (_changedProperties.has("name") || _changedProperties.has("shape")) {
103
150
  this.updateSvg(this.name);
@@ -125,24 +172,49 @@ let EunomiaIcon = class EunomiaIcon extends LitElement {
125
172
  if (targetShape === "default") {
126
173
  return;
127
174
  }
128
- this.fetchIconSvg("default", baseName).then((svg) => {
175
+ this.fetchIconSvg("default", baseName)
176
+ .then((svg) => {
129
177
  this.svgMarkup = svg;
130
- });
178
+ })
179
+ .catch(() => undefined);
131
180
  });
132
181
  }
133
182
  /**
134
183
  * Fetches the raw markup for a single icon file, co-located with this
135
- * component (see `./icons/<shape>/<name>.svg`), resolved against
136
- * `MODULE_URL` rather than `import.meta.url` directly — see that
137
- * constant's doc comment for why. This still resolves correctly
138
- * unbundled, straight off the filesystem/CDN, exactly like a direct
139
- * `new URL(..., import.meta.url)` would.
184
+ * component (see `./icons/<shape>/<name>.svg`). Deliberately never
185
+ * passes a templated path straight to `new URL(...)` : Turbopack
186
+ * special-cases that exact call shape (a string with `${...}`
187
+ * interpolation as the first argument) for static asset resolution
188
+ * even when the base is routed through `MODULE_URL` rather than a bare
189
+ * `import.meta.url` literal — with ~5500 icon files across 2
190
+ * interpolated segments, it can't resolve the pattern and silently
191
+ * falls back to a single wrong file for every request instead of
192
+ * erroring. Building the URL from `MODULE_URL`'s own directory via
193
+ * plain string ops keeps every `new URL()` call single-argument and
194
+ * literal-free, so there's no templated call shape left for any
195
+ * bundler to intercept. This still resolves correctly unbundled,
196
+ * straight off the filesystem/CDN, exactly like the original
197
+ * `new URL(templatedPath, import.meta.url)` would have. A base set
198
+ * through `setIconBaseUrl` takes priority, and a bundler-rewritten
199
+ * `file:` module URL throws an explicit error instead of a CSP one.
140
200
  *
141
201
  * @param shape - Icon subfolder to fetch from
142
202
  * @param name - Icon file name, without extension
143
203
  */
144
204
  async fetchIconSvg(shape, name) {
145
- const url = new URL(`./icons/${shape}/${name}.svg`, MODULE_URL);
205
+ let url;
206
+ if (iconBaseUrl) {
207
+ url = `${iconBaseUrl}/${shape}/${name}.svg`;
208
+ }
209
+ else {
210
+ const moduleUrl = new URL(MODULE_URL);
211
+ if (moduleUrl.protocol === "file:") {
212
+ throw new Error("eun-icon can't locate its icons: the bundler rewrote import.meta.url to a file: URL. Serve the icons folder and call setIconBaseUrl().");
213
+ }
214
+ const moduleDir = moduleUrl.pathname.slice(0, moduleUrl.pathname.lastIndexOf("/"));
215
+ moduleUrl.pathname = `${moduleDir}/icons/${shape}/${name}.svg`;
216
+ url = moduleUrl.href;
217
+ }
146
218
  const response = await fetch(url);
147
219
  if (!response.ok) {
148
220
  throw new Error(`Icon not found: ${url}`);
@@ -34,18 +34,22 @@ const base = css `
34
34
  --_color: var(--eun-color-secondary-500, #e5a030);
35
35
  }
36
36
  :host([color="light"]:not([severity])) {
37
- --_background: var(--eun-color-grey-100, #f7f8fa);
38
- --_backgroundFilled: var(--eun-color-grey-100, #f7f8fa);
39
- --_border-color: var(--eun-color-grey-100, #f7f8fa);
37
+ --_background: var(--eun-color-grey-0, #ffffff);
38
+ --_backgroundFilled: var(--eun-color-grey-0, #ffffff);
39
+ --_border-color: var(--eun-color-grey-0, #ffffff);
40
40
  --_color: var(--eun-color-grey-600, #666e8a);
41
41
  }
42
42
  :host([color="light"][appearance="outline"]:not([severity])) {
43
43
  --_color: var(--eun-color-grey-600, #666e8a);
44
44
  }
45
45
  :host([color="medium"]:not([severity])) {
46
- --_background: var(--eun-color-grey-300, #dee0e7);
47
- --_backgroundFilled: var(--eun-color-grey-500, #8d93aa);
48
- --_border-color: var(--eun-color-grey-300, #dee0e7);
46
+ --_background: color-mix(
47
+ in srgb,
48
+ var(--eun-color-grey-700, #484e61) 20%,
49
+ transparent
50
+ );
51
+ --_backgroundFilled: var(--_background);
52
+ --_border-color: var(--eun-color-grey-700, #484e61);
49
53
  --_color: var(--eun-color-grey-900, #000000);
50
54
  }
51
55
  :host([color="dark"]:not([severity])) {
@@ -71,12 +71,12 @@ const panel = css `
71
71
  .eun-tooltip-panel:not([data-severity])[data-color="light"] {
72
72
  --_accent: var(--eun-color-grey-600, #666e8a);
73
73
  --_borderColor: var(--eun-color-grey-300, #dee0e7);
74
- --_backgroundFilled: var(--eun-color-grey-100, #f7f8fa);
74
+ --_backgroundFilled: var(--eun-color-grey-0, #ffffff);
75
75
  }
76
76
  .eun-tooltip-panel:not([data-severity])[data-color="medium"] {
77
77
  --_accent: var(--eun-color-grey-900, #000000);
78
78
  --_borderColor: var(--eun-color-grey-300, #dee0e7);
79
- --_backgroundFilled: var(--eun-color-grey-500, #8d93aa);
79
+ --_backgroundFilled: var(--eun-color-grey-700, #484e61);
80
80
  }
81
81
  .eun-tooltip-panel:not([data-severity])[data-color="dark"] {
82
82
  --_accent: var(--eun-color-grey-800, #071621);
@@ -219,7 +219,7 @@ let EunomiaCheckboxList = class EunomiaCheckboxList extends FormLabelMixin(Reado
219
219
  */
220
220
  sendChangeEvent() {
221
221
  this.checkValidity(this.value);
222
- this.dispatchEvent(new ChangeEvent());
222
+ this.dispatchEvent(new ChangeEvent(this.value));
223
223
  }
224
224
  /**
225
225
  * Handler called when slot content changes.
@@ -555,7 +555,7 @@ let EunomiaColorPicker = class EunomiaColorPicker extends EunomiaColorPickerMixi
555
555
  handleSelect(value) {
556
556
  this.value = value;
557
557
  this.hexDraft = value;
558
- this.dispatchEvent(new ChangeEvent());
558
+ this.dispatchEvent(new ChangeEvent(this.value));
559
559
  this.closeListbox();
560
560
  }
561
561
  /**
@@ -775,7 +775,9 @@ let EunomiaColorPicker = class EunomiaColorPicker extends EunomiaColorPickerMixi
775
775
  type="text"
776
776
  id="hex-input"
777
777
  aria-label=${this.hexInputLabel}
778
- aria-invalid=${this.hexDraftInvalid ? "true" : "false"}
778
+ aria-invalid=${!this.hideError &&
779
+ !this.disabled &&
780
+ this.hexDraftInvalid}
779
781
  ?disabled=${this.disabled}
780
782
  ?readonly=${this.readonly}
781
783
  .value=${live(this.hexDraft)}
@@ -849,9 +851,9 @@ let EunomiaColorPicker = class EunomiaColorPicker extends EunomiaColorPickerMixi
849
851
  aria-label=${this.label || this.hexInputLabel}
850
852
  aria-describedby=${hasDescription ? "description" : nothing}
851
853
  aria-required=${this.required ? "true" : "false"}
852
- aria-invalid=${this.hexDraftInvalid || !this.valid
853
- ? "true"
854
- : "false"}
854
+ aria-invalid=${!this.hideError &&
855
+ !this.disabled &&
856
+ (this.hexDraftInvalid || !this.valid)}
855
857
  ?disabled=${this.disabled}
856
858
  ?readonly=${this.readonly}
857
859
  .value=${live(this.hexDraft)}
@@ -913,7 +915,7 @@ let EunomiaColorPicker = class EunomiaColorPicker extends EunomiaColorPickerMixi
913
915
  aria-describedby=${hasDescription ? "description" : nothing}
914
916
  ?disabled=${this.disabled}
915
917
  aria-required=${this.required ? "true" : "false"}
916
- aria-invalid=${this.valid ? "false" : "true"}
918
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
917
919
  @click=${this.handleFieldActivate}
918
920
  @keydown=${this.handleFieldKeydown}
919
921
  >
@@ -986,7 +988,7 @@ let EunomiaColorPicker = class EunomiaColorPicker extends EunomiaColorPickerMixi
986
988
  aria-label=${ifDefined(this.triggerAccessibleLabel)}
987
989
  aria-describedby=${hasDescription ? "description" : nothing}
988
990
  ?disabled=${this.disabled || this.readonly}
989
- aria-invalid=${this.valid ? "false" : "true"}
991
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
990
992
  .value=${live(this.value || "#000000")}
991
993
  @input=${this.handleNativeInput}
992
994
  />
@@ -213,7 +213,7 @@ let EunomiaCounter = class EunomiaCounter extends EunomiaCounterMixinBase {
213
213
  */
214
214
  commitValue(next) {
215
215
  this.value = next;
216
- this.dispatchEvent(new ChangeEvent());
216
+ this.dispatchEvent(new ChangeEvent(this.value));
217
217
  }
218
218
  /**
219
219
  * Increments or decrements the value by `step`, clamped to `min`/`max`.
@@ -246,7 +246,7 @@ let EunomiaCounter = class EunomiaCounter extends EunomiaCounterMixinBase {
246
246
  const parsed = raw === "" ? undefined : Number(raw);
247
247
  this.value =
248
248
  parsed === undefined || Number.isNaN(parsed) ? undefined : parsed;
249
- this.dispatchEvent(new ChangeEvent());
249
+ this.dispatchEvent(new ChangeEvent(this.value));
250
250
  }
251
251
  /**
252
252
  * Jumps to `min`/`max` on `Home`/`End`, the one bit of custom keyboard
@@ -313,7 +313,7 @@ let EunomiaCounter = class EunomiaCounter extends EunomiaCounterMixinBase {
313
313
  max=${ifDefined(this.max)}
314
314
  step=${this.step}
315
315
  inputmode=${hasDecimalStep ? "decimal" : "numeric"}
316
- aria-invalid=${this.valid ? "false" : "true"}
316
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
317
317
  aria-label=${ifDefined(this.label)}
318
318
  aria-describedby=${hasDescription ? "description" : nothing}
319
319
  @input=${this.handleInput}
@@ -1225,7 +1225,7 @@ let EunomiaDatePicker = EunomiaDatePicker_1 = class EunomiaDatePicker extends Eu
1225
1225
  end: dateToSegments(this.stagingRange.end),
1226
1226
  };
1227
1227
  }
1228
- this.dispatchEvent(new ChangeEvent());
1228
+ this.dispatchEvent(new ChangeEvent(this.mode === "single" ? this.value : this.rangeValue));
1229
1229
  }
1230
1230
  /**
1231
1231
  * Commits a directly-typed, complete value (bypassing staging entirely —
@@ -1250,7 +1250,7 @@ let EunomiaDatePicker = EunomiaDatePicker_1 = class EunomiaDatePicker extends Eu
1250
1250
  else {
1251
1251
  this.rangeValue = { ...this.rangeValue, [target]: date };
1252
1252
  }
1253
- this.dispatchEvent(new ChangeEvent());
1253
+ this.dispatchEvent(new ChangeEvent(target === "value" ? this.value : this.rangeValue));
1254
1254
  if (this.isOpen || this.isModalOpen) {
1255
1255
  this.resetStaging();
1256
1256
  }
@@ -1301,7 +1301,7 @@ let EunomiaDatePicker = EunomiaDatePicker_1 = class EunomiaDatePicker extends Eu
1301
1301
  */
1302
1302
  commitTypedTime(hour24, minute) {
1303
1303
  this.value = setZonedTime(this.value ?? new Date(), this.timezone, hour24, minute);
1304
- this.dispatchEvent(new ChangeEvent());
1304
+ this.dispatchEvent(new ChangeEvent(this.value));
1305
1305
  if (this.isOpen || this.isModalOpen) {
1306
1306
  this.resetStaging();
1307
1307
  }
@@ -262,7 +262,7 @@ let EunomiaInput = class EunomiaInput extends EunomiaInputMixinBase {
262
262
  */
263
263
  handleInput(event) {
264
264
  this.value = event.target.value;
265
- this.dispatchEvent(new ChangeEvent());
265
+ this.dispatchEvent(new ChangeEvent(this.value));
266
266
  }
267
267
  /**
268
268
  * Clears the field value.
@@ -270,7 +270,7 @@ let EunomiaInput = class EunomiaInput extends EunomiaInputMixinBase {
270
270
  */
271
271
  handleClear() {
272
272
  this.value = "";
273
- this.dispatchEvent(new ChangeEvent());
273
+ this.dispatchEvent(new ChangeEvent(this.value));
274
274
  this.fieldEl?.focus();
275
275
  }
276
276
  /**
@@ -413,7 +413,7 @@ let EunomiaInputFile = class EunomiaInputFile extends EunomiaInputFileMixinBase
413
413
  }
414
414
  if (accepted.length) {
415
415
  this.value = nextValue;
416
- this.dispatchEvent(new ChangeEvent());
416
+ this.dispatchEvent(new ChangeEvent(this.value));
417
417
  }
418
418
  if (rejected.length) {
419
419
  this.dispatchEvent(new FileRejectEvent(rejected));
@@ -445,7 +445,7 @@ let EunomiaInputFile = class EunomiaInputFile extends EunomiaInputFileMixinBase
445
445
  }
446
446
  const next = current.filter((candidate) => candidate !== file);
447
447
  this.value = next;
448
- this.dispatchEvent(new ChangeEvent());
448
+ this.dispatchEvent(new ChangeEvent(this.value));
449
449
  this.updateComplete.then(() => {
450
450
  if (!next.length) {
451
451
  this.firstFocusable()?.focus();
@@ -462,7 +462,7 @@ let EunomiaInputFile = class EunomiaInputFile extends EunomiaInputFileMixinBase
462
462
  removeAll() {
463
463
  this.selected = new Set();
464
464
  this.value = [];
465
- this.dispatchEvent(new ChangeEvent());
465
+ this.dispatchEvent(new ChangeEvent(this.value));
466
466
  this.updateComplete.then(() => this.firstFocusable()?.focus());
467
467
  }
468
468
  /**
@@ -474,7 +474,7 @@ let EunomiaInputFile = class EunomiaInputFile extends EunomiaInputFileMixinBase
474
474
  const next = current.filter((file) => !this.selected.has(file));
475
475
  this.selected = new Set();
476
476
  this.value = next;
477
- this.dispatchEvent(new ChangeEvent());
477
+ this.dispatchEvent(new ChangeEvent(this.value));
478
478
  this.updateComplete.then(() => this.firstFocusable()?.focus());
479
479
  }
480
480
  toggleSelected(file, checked) {
@@ -594,7 +594,7 @@ let EunomiaInputFile = class EunomiaInputFile extends EunomiaInputFileMixinBase
594
594
  ?multiple=${this.multiple}
595
595
  ?disabled=${disabled}
596
596
  aria-label=${ifDefined(this.label)}
597
- aria-invalid=${this.valid ? "false" : "true"}
597
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
598
598
  aria-describedby=${hasDescription ? "description" : nothing}
599
599
  @change=${this.handleNativeChange}
600
600
  />
@@ -529,7 +529,7 @@ let EunomiaInputPhone = EunomiaInputPhone_1 = class EunomiaInputPhone extends Eu
529
529
  input.value = formatted;
530
530
  }
531
531
  this.value = toE164(this.country, cappedSignificantDigits);
532
- this.dispatchEvent(new ChangeEvent());
532
+ this.dispatchEvent(new ChangeEvent(this.value));
533
533
  }
534
534
  /**
535
535
  * Updates the search query filtering the country dropdown.
@@ -561,7 +561,7 @@ let EunomiaInputPhone = EunomiaInputPhone_1 = class EunomiaInputPhone extends Eu
561
561
  const trimmedSignificantDigits = significantDigits.slice(0, maxLength);
562
562
  this.nationalDigits = (hasTrunkZero ? "0" : "") + trimmedSignificantDigits;
563
563
  this.value = toE164(this.country, trimmedSignificantDigits);
564
- this.dispatchEvent(new ChangeEvent());
564
+ this.dispatchEvent(new ChangeEvent(this.value));
565
565
  this.closeListbox();
566
566
  this.fieldEl?.focus();
567
567
  }
@@ -744,7 +744,7 @@ let EunomiaInputPhone = EunomiaInputPhone_1 = class EunomiaInputPhone extends Eu
744
744
  ?disabled=${this.disabled}
745
745
  ?readonly=${this.readonly}
746
746
  aria-describedby=${hasDescription ? "description" : nothing}
747
- aria-invalid=${this.valid ? "false" : "true"}
747
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
748
748
  @input=${this.handlePhoneInput}
749
749
  />
750
750
  </div>
@@ -370,7 +370,7 @@ let EunomiaRadioList = class EunomiaRadioList extends FormLabelMixin(ReadonlyMix
370
370
  */
371
371
  sendChangeEvent() {
372
372
  this.checkValidity(this.value);
373
- this.dispatchEvent(new ChangeEvent());
373
+ this.dispatchEvent(new ChangeEvent(this.value));
374
374
  }
375
375
  /**
376
376
  * Handler called when slot content changes.
@@ -404,7 +404,7 @@ let EunomiaRadioList = class EunomiaRadioList extends FormLabelMixin(ReadonlyMix
404
404
  aria-disabled=${this.disabled ? "true" : "false"}
405
405
  aria-readonly=${this.readonly ? "true" : "false"}
406
406
  aria-required=${this.required ? "true" : "false"}
407
- aria-invalid=${this.valid ? "false" : "true"}
407
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
408
408
  >
409
409
  <slot @slotchange=${this.handleSlotChange}></slot>
410
410
  </div>
@@ -45,6 +45,7 @@ declare const EunomiaSelectMixinBase: Constructor<EunomiaSelectBase>;
45
45
  * @slot [icon] - Replaces the leading icon entirely with any content, not just an icon element. Falls back to icon when empty
46
46
  * @slot [readonly-content] - Optional custom content displayed instead of the raw label in the static readonly display
47
47
  * @slot [confirm] - Optional custom content for the confirm trigger, such as an icon, displayed as a link while editing in readonlyAvailable mode, or while multiple and the dropdown is open. Overrides confirmLabel when used. The button itself always shows in either of those states unless confirmLabel is set to an empty string
48
+ * @slot [actions] - Optional content rendered in a footer row below the options, for actions unrelated to picking a value, such as a link to manage the list elsewhere. Hidden entirely when empty. Ignored while native is set
48
49
  * @prop {boolean} [searchable=false] - Whether the field filters its options as the user types. When false, the field is read only and driven by arrow keys, Enter, and type-ahead. Ignored while native is set
49
50
  * @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
50
51
  * @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
@@ -308,6 +309,21 @@ export declare class EunomiaSelect extends EunomiaSelectMixinBase {
308
309
  * @private
309
310
  */
310
311
  private handleIconSlotChange;
312
+ /**
313
+ * Elements assigned to the `actions` slot, if any ; queried live so
314
+ * {@link renderField} can decide whether to render the footer row around
315
+ * it at all.
316
+ * @internal
317
+ * @readonly
318
+ */
319
+ private readonly actionsSlotEls;
320
+ /**
321
+ * Re-renders when the `actions` slot's assigned content changes, so the
322
+ * footer row correctly appears/disappears as content is slotted in or
323
+ * removed after the initial render.
324
+ * @private
325
+ */
326
+ private handleActionsSlotChange;
311
327
  /**
312
328
  * The listbox's currently running open/close {@link Animation}, if any —
313
329
  * see {@link playListboxOpenAnimation}/{@link playListboxCloseAnimation}.