@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
@@ -53,6 +53,7 @@ const EunomiaSelectMixinBase = AnchorPositionMixin(FieldVariantMixin(FormLabelMi
53
53
  * @slot [icon] - Replaces the leading icon entirely with any content, not just an icon element. Falls back to icon when empty
54
54
  * @slot [readonly-content] - Optional custom content displayed instead of the raw label in the static readonly display
55
55
  * @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
56
+ * @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
56
57
  * @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
57
58
  * @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
58
59
  * @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
@@ -229,6 +230,15 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
229
230
  this.handleIconSlotChange = () => {
230
231
  this.requestUpdate();
231
232
  };
233
+ /**
234
+ * Re-renders when the `actions` slot's assigned content changes, so the
235
+ * footer row correctly appears/disappears as content is slotted in or
236
+ * removed after the initial render.
237
+ * @private
238
+ */
239
+ this.handleActionsSlotChange = () => {
240
+ this.requestUpdate();
241
+ };
232
242
  /**
233
243
  * Redirects focus to the native field whenever the host itself receives
234
244
  * it. The host stays in the tab order (`DisabledMixin`) rather than
@@ -795,11 +805,11 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
795
805
  this.value = current.includes(option.value)
796
806
  ? current.filter((value) => value !== option.value)
797
807
  : [...current, option.value];
798
- this.dispatchEvent(new ChangeEvent());
808
+ this.dispatchEvent(new ChangeEvent(this.value));
799
809
  return;
800
810
  }
801
811
  this.value = option.value;
802
- this.dispatchEvent(new ChangeEvent());
812
+ this.dispatchEvent(new ChangeEvent(this.value));
803
813
  this.closeListbox();
804
814
  if (this.readonlyAvailable) {
805
815
  this.confirmEdit();
@@ -816,7 +826,7 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
816
826
  */
817
827
  handleNativeChange(event) {
818
828
  this.value = event.target.value;
819
- this.dispatchEvent(new ChangeEvent());
829
+ this.dispatchEvent(new ChangeEvent(this.value));
820
830
  if (this.readonlyAvailable) {
821
831
  this.confirmEdit();
822
832
  }
@@ -1166,7 +1176,7 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
1166
1176
  aria-describedby=${hasDescription ? "description" : nothing}
1167
1177
  ?disabled=${this.disabled || this.readonly}
1168
1178
  ?required=${this.required}
1169
- aria-invalid=${this.valid ? "false" : "true"}
1179
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
1170
1180
  @change=${this.handleNativeChange}
1171
1181
  ></select>
1172
1182
  ${this.showConfirmLink ? this.renderConfirmLink() : nothing}
@@ -1215,7 +1225,7 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
1215
1225
  ?disabled=${this.disabled}
1216
1226
  ?readonly=${this.readonly || !this.searchable}
1217
1227
  aria-required=${this.required ? "true" : "false"}
1218
- aria-invalid=${this.valid ? "false" : "true"}
1228
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
1219
1229
  @pointerdown=${this.handleFieldPointerDown}
1220
1230
  @focus=${this.handleFieldActivate}
1221
1231
  @click=${this.handleFieldActivate}
@@ -1234,6 +1244,16 @@ let EunomiaSelect = EunomiaSelect_1 = class EunomiaSelect extends EunomiaSelectM
1234
1244
  @toggle=${this.handlePopoverToggle}
1235
1245
  >
1236
1246
  ${this.renderListboxRows(options)}
1247
+ <li
1248
+ class="actions"
1249
+ role="presentation"
1250
+ ?hidden=${!this.actionsSlotEls?.length}
1251
+ >
1252
+ <slot
1253
+ name="actions"
1254
+ @slotchange=${this.handleActionsSlotChange}
1255
+ ></slot>
1256
+ </li>
1237
1257
  </ul>
1238
1258
  `;
1239
1259
  }
@@ -1381,6 +1401,9 @@ __decorate([
1381
1401
  __decorate([
1382
1402
  queryAssignedElements({ slot: "icon" })
1383
1403
  ], EunomiaSelect.prototype, "iconSlotEls", void 0);
1404
+ __decorate([
1405
+ queryAssignedElements({ slot: "actions" })
1406
+ ], EunomiaSelect.prototype, "actionsSlotEls", void 0);
1384
1407
  EunomiaSelect = EunomiaSelect_1 = __decorate([
1385
1408
  customElement(EunomiaSelectTagName)
1386
1409
  ], EunomiaSelect);
@@ -65,6 +65,17 @@ const select = css `
65
65
  border-color: var(--eun-color-primary-500, #3d6fff);
66
66
  --icon-color: var(--eun-color-on-primary-500, #ffffff);
67
67
  }
68
+ #listbox .actions {
69
+ display: flex;
70
+ align-items: center;
71
+ gap: var(--eun-space-s, 8px);
72
+ padding: var(--eun-space-s, 8px) var(--eun-space-m, 16px);
73
+ cursor: default;
74
+ border-block-start: var(--eun-borderWidth-s, 1px) solid var(--_borderColor);
75
+ }
76
+ #listbox .actions[hidden] {
77
+ display: none;
78
+ }
68
79
  #confirm-link {
69
80
  position: absolute;
70
81
  right: calc(var(--eun-space-l, 32px) + var(--eun-space-xs, 4px));
@@ -150,7 +150,7 @@ let EunomiaSlider = class EunomiaSlider extends EunomiaSliderMixinBase {
150
150
  */
151
151
  this.handleInput = (event) => {
152
152
  this.value = Number(event.target.value);
153
- this.dispatchEvent(new ChangeEvent());
153
+ this.dispatchEvent(new ChangeEvent(this.value));
154
154
  };
155
155
  /**
156
156
  * Blocks every native keyboard interaction (arrow keys, Home/End, Page
@@ -431,7 +431,7 @@ let EunomiaSlider = class EunomiaSlider extends EunomiaSliderMixinBase {
431
431
  step=${this.step}
432
432
  .value=${String(value)}
433
433
  ?disabled=${this.disabled}
434
- aria-invalid=${this.valid ? "false" : "true"}
434
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
435
435
  aria-label=${ifDefined(this.label)}
436
436
  aria-describedby=${hasDescription ? "description" : nothing}
437
437
  @input=${this.handleInput}
@@ -733,7 +733,7 @@ let EunomiaTextarea = class EunomiaTextarea extends EunomiaTextareaMixinBase {
733
733
  this.lastSyncedValue = newValue;
734
734
  this.value = newValue;
735
735
  this.resizeEditableField();
736
- this.dispatchEvent(new ChangeEvent());
736
+ this.dispatchEvent(new ChangeEvent(this.value));
737
737
  this.syncToolbarToSelection();
738
738
  };
739
739
  /**
@@ -1076,7 +1076,7 @@ let EunomiaTextarea = class EunomiaTextarea extends EunomiaTextareaMixinBase {
1076
1076
  handleInput(event) {
1077
1077
  this.value = event.target.value;
1078
1078
  this.resizeToContent();
1079
- this.dispatchEvent(new ChangeEvent());
1079
+ this.dispatchEvent(new ChangeEvent(this.value));
1080
1080
  }
1081
1081
  /**
1082
1082
  * @see handleSelectionChange
@@ -1539,7 +1539,7 @@ let EunomiaTextarea = class EunomiaTextarea extends EunomiaTextareaMixinBase {
1539
1539
  ?required=${this.required}
1540
1540
  minlength=${ifDefined(this.minlength)}
1541
1541
  maxlength=${ifDefined(this.maxlength)}
1542
- aria-invalid=${this.valid ? "false" : "true"}
1542
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
1543
1543
  aria-label=${ifDefined(this.label)}
1544
1544
  aria-describedby=${describedBy || nothing}
1545
1545
  @input=${this.handleInput}
@@ -1570,7 +1570,7 @@ let EunomiaTextarea = class EunomiaTextarea extends EunomiaTextareaMixinBase {
1570
1570
  role="textbox"
1571
1571
  aria-multiline="true"
1572
1572
  aria-required=${this.required ? "true" : nothing}
1573
- aria-invalid=${this.valid ? "false" : "true"}
1573
+ aria-invalid=${!this.hideError && !this.disabled && !this.valid}
1574
1574
  aria-label=${ifDefined(this.label)}
1575
1575
  aria-describedby=${describedBy || nothing}
1576
1576
  @beforeinput=${this.handleEditableBeforeInput}
@@ -535,7 +535,7 @@ let EunomiaTimePicker = class EunomiaTimePicker extends EunomiaTimePickerMixinBa
535
535
  */
536
536
  commitStaged() {
537
537
  this.value = this.stagingValue;
538
- this.dispatchEvent(new ChangeEvent());
538
+ this.dispatchEvent(new ChangeEvent(this.value));
539
539
  }
540
540
  /**
541
541
  * Applies a new hour/minute to {@link displaySource}'s calendar day (via
@@ -557,7 +557,7 @@ let EunomiaTimePicker = class EunomiaTimePicker extends EunomiaTimePickerMixinBa
557
557
  return;
558
558
  }
559
559
  this.value = next;
560
- this.dispatchEvent(new ChangeEvent());
560
+ this.dispatchEvent(new ChangeEvent(this.value));
561
561
  }
562
562
  /**
563
563
  * Commits a directly-typed, complete hour/minute (bypassing staging
@@ -572,7 +572,7 @@ let EunomiaTimePicker = class EunomiaTimePicker extends EunomiaTimePickerMixinBa
572
572
  */
573
573
  commitTypedTime(hour24, minute) {
574
574
  this.value = setZonedTime(this.value ?? new Date(), this.timezone, hour24, minute);
575
- this.dispatchEvent(new ChangeEvent());
575
+ this.dispatchEvent(new ChangeEvent(this.value));
576
576
  if (this.isOpen || this.isModalOpen) {
577
577
  this.resetStaging();
578
578
  }
@@ -25,6 +25,8 @@ export type EunomiaAnimateAnimation = "none" | "fade" | "scroll-right" | "scroll
25
25
  * @prop {boolean} [once=true] - Whether the animation plays only the first time it enters the viewport, or replays every time it re-enters
26
26
  * @prop {number} [threshold=0.1] - The ratio of the element, from 0 to 1, that must be visible before the animation triggers
27
27
  * @prop {string} [rootMargin='0px'] - The margin applied around the viewport before threshold is evaluated, accepting CSS-like values such as -50px 0px
28
+ * @prop {EunomiaAnimateAnimation} [tabletAnimation] - Overrides `animation` at tablet-width viewports and narrower (max-width: 768px). Left unset, the viewport keeps using `animation`
29
+ * @prop {EunomiaAnimateAnimation} [mobileAnimation] - Overrides `animation` (and `tabletAnimation`, if also set) at mobile-width viewports and narrower (max-width: 600px)
28
30
  * @cssprop [--animate-distance] - Sets the travel distance for the scroll and faded-scroll animations
29
31
  * @element eun-animate
30
32
  */
@@ -36,7 +38,7 @@ export declare class EunomiaAnimate extends LitElement {
36
38
  * @internal
37
39
  * @readonly
38
40
  */
39
- static readonly styles: import("lit").CSSResult;
41
+ static readonly styles: import("lit").CSSResult[];
40
42
  /**
41
43
  * Which transition to apply. `none` renders the content immediately, with
42
44
  * no transition, and skips setting up the `IntersectionObserver`
@@ -72,20 +74,53 @@ export declare class EunomiaAnimate extends LitElement {
72
74
  * @default 0px
73
75
  */
74
76
  rootMargin: string;
77
+ /**
78
+ * Overrides `animation` at tablet-width viewports and narrower
79
+ * (`max-width: 768px`, matching `EUNOMIA_BREAKPOINTS.tablet`). Purely a
80
+ * CSS `@media` switch (`animate.styles.ts`, mirrored in
81
+ * `animate-ssr.css` for the pre-hydration state) — never read from JS,
82
+ * so there's no `matchMedia` listener to keep in sync with the actual
83
+ * viewport, and no risk of the SSR/hydration mismatch that a JS-driven
84
+ * breakpoint switch would reintroduce. Left `undefined`, the viewport
85
+ * keeps using `animation`.
86
+ * @default undefined
87
+ */
88
+ tabletAnimation?: EunomiaAnimateAnimation;
89
+ /**
90
+ * Overrides `animation` (and `tabletAnimation`, if also set) at
91
+ * mobile-width viewports and narrower (`max-width: 600px`, matching
92
+ * `EUNOMIA_BREAKPOINTS.mobile`). Same pure-CSS mechanism as
93
+ * {@link tabletAnimation}.
94
+ * @default undefined
95
+ */
96
+ mobileAnimation?: EunomiaAnimateAnimation;
75
97
  /**
76
98
  * Observes the host's own viewport entry/exit. Left `undefined` while
77
- * `animation` is `"none"` — see {@link setupObserver}.
99
+ * every breakpoint's effective animation is `"none"` — see
100
+ * {@link setupObserver} and {@link hasAnyAnimation}.
78
101
  * @internal
79
102
  */
80
103
  private observer?;
104
+ /**
105
+ * Whether at least one of `animation`, `tabletAnimation`, or
106
+ * `mobileAnimation` is set to something other than `"none"` — i.e.
107
+ * whether *some* viewport width could actually reveal an animation, and
108
+ * the observer is therefore worth setting up at all. Deliberately
109
+ * ignores the current viewport width itself: which of the three ends up
110
+ * active is a CSS `@media` decision, not something this component reads
111
+ * from JS (see {@link tabletAnimation}).
112
+ * @private
113
+ */
114
+ private get hasAnyAnimation();
81
115
  connectedCallback(): void;
82
116
  disconnectedCallback(): void;
83
117
  protected updated(changed: PropertyValues): void;
84
118
  /**
85
119
  * (Re)builds the `IntersectionObserver` for the current `threshold`/
86
- * `rootMargin`, replacing any previous one — except while `animation` is
87
- * `"none"`, where no observer is created at all : that variant renders
88
- * unconditionally, so there'd be nothing for it to drive.
120
+ * `rootMargin`, replacing any previous one — except while
121
+ * {@link hasAnyAnimation} is `false`, where no observer is created at
122
+ * all : every viewport width renders unconditionally then, so there'd
123
+ * be nothing for it to drive.
89
124
  * @private
90
125
  */
91
126
  private setupObserver;
@@ -36,6 +36,8 @@ const IN_VIEW_ATTR = "in-view";
36
36
  * @prop {boolean} [once=true] - Whether the animation plays only the first time it enters the viewport, or replays every time it re-enters
37
37
  * @prop {number} [threshold=0.1] - The ratio of the element, from 0 to 1, that must be visible before the animation triggers
38
38
  * @prop {string} [rootMargin='0px'] - The margin applied around the viewport before threshold is evaluated, accepting CSS-like values such as -50px 0px
39
+ * @prop {EunomiaAnimateAnimation} [tabletAnimation] - Overrides `animation` at tablet-width viewports and narrower (max-width: 768px). Left unset, the viewport keeps using `animation`
40
+ * @prop {EunomiaAnimateAnimation} [mobileAnimation] - Overrides `animation` (and `tabletAnimation`, if also set) at mobile-width viewports and narrower (max-width: 600px)
39
41
  * @cssprop [--animate-distance] - Sets the travel distance for the scroll and faded-scroll animations
40
42
  * @element eun-animate
41
43
  */
@@ -78,6 +80,21 @@ let EunomiaAnimate = class EunomiaAnimate extends LitElement {
78
80
  */
79
81
  this.rootMargin = "0px";
80
82
  }
83
+ /**
84
+ * Whether at least one of `animation`, `tabletAnimation`, or
85
+ * `mobileAnimation` is set to something other than `"none"` — i.e.
86
+ * whether *some* viewport width could actually reveal an animation, and
87
+ * the observer is therefore worth setting up at all. Deliberately
88
+ * ignores the current viewport width itself: which of the three ends up
89
+ * active is a CSS `@media` decision, not something this component reads
90
+ * from JS (see {@link tabletAnimation}).
91
+ * @private
92
+ */
93
+ get hasAnyAnimation() {
94
+ return (this.animation !== "none" ||
95
+ (this.tabletAnimation !== undefined && this.tabletAnimation !== "none") ||
96
+ (this.mobileAnimation !== undefined && this.mobileAnimation !== "none"));
97
+ }
81
98
  connectedCallback() {
82
99
  super.connectedCallback?.();
83
100
  this.setupObserver();
@@ -94,15 +111,10 @@ let EunomiaAnimate = class EunomiaAnimate extends LitElement {
94
111
  if (changed.has("delay")) {
95
112
  this.style.setProperty("--_delay", `${this.delay}ms`);
96
113
  }
97
- // Rebuilds the observer whenever anything its own setup depends on
98
- // changes : `animation` (switching to/from "none" starts/stops
99
- // observing entirely), `threshold`/`rootMargin` (the observer's own
100
- // options, otherwise frozen at whatever they were on connect), and
101
- // `once` (so flipping it back to `false` after the element already
102
- // triggered once — and got unobserved — resumes watching for exit/
103
- // re-entry, rather than staying silently stuck).
104
114
  if (this.isConnected &&
105
115
  (changed.has("animation") ||
116
+ changed.has("tabletAnimation") ||
117
+ changed.has("mobileAnimation") ||
106
118
  changed.has("threshold") ||
107
119
  changed.has("rootMargin") ||
108
120
  changed.has("once"))) {
@@ -111,14 +123,15 @@ let EunomiaAnimate = class EunomiaAnimate extends LitElement {
111
123
  }
112
124
  /**
113
125
  * (Re)builds the `IntersectionObserver` for the current `threshold`/
114
- * `rootMargin`, replacing any previous one — except while `animation` is
115
- * `"none"`, where no observer is created at all : that variant renders
116
- * unconditionally, so there'd be nothing for it to drive.
126
+ * `rootMargin`, replacing any previous one — except while
127
+ * {@link hasAnyAnimation} is `false`, where no observer is created at
128
+ * all : every viewport width renders unconditionally then, so there'd
129
+ * be nothing for it to drive.
117
130
  * @private
118
131
  */
119
132
  setupObserver() {
120
133
  this.teardownObserver();
121
- if (this.animation === "none") {
134
+ if (!this.hasAnyAnimation) {
122
135
  return;
123
136
  }
124
137
  this.observer = new IntersectionObserver(([entry]) => {
@@ -172,6 +185,12 @@ __decorate([
172
185
  __decorate([
173
186
  property({ attribute: "root-margin" })
174
187
  ], EunomiaAnimate.prototype, "rootMargin", void 0);
188
+ __decorate([
189
+ property({ attribute: "tablet-animation", reflect: true })
190
+ ], EunomiaAnimate.prototype, "tabletAnimation", void 0);
191
+ __decorate([
192
+ property({ attribute: "mobile-animation", reflect: true })
193
+ ], EunomiaAnimate.prototype, "mobileAnimation", void 0);
175
194
  EunomiaAnimate = __decorate([
176
195
  customElement(EunomiaAnimateTagName)
177
196
  ], EunomiaAnimate);
@@ -1 +1 @@
1
- export declare const animateStyles: import("lit").CSSResult;
1
+ export declare const animateStyles: import("lit").CSSResult[];
@@ -100,4 +100,194 @@ const base = css `
100
100
  }
101
101
  }
102
102
  `;
103
- export const animateStyles = base;
103
+ /**
104
+ * \`tablet-animation\`/\`mobile-animation\` overrides — pure CSS \`@media\`
105
+ * switches, mirroring \`base\` above per animation value, so no JS ever
106
+ * needs to read the actual viewport width (see \`animate.ts\`'s own
107
+ * \`tabletAnimation\`/\`mobileAnimation\` doc comments). Placed after \`base\`
108
+ * so equal-specificity ties (e.g. both \`animation="fade"\` and
109
+ * \`tablet-animation="none"\` present at once) resolve in the narrower
110
+ * breakpoint's favor by source order, once its \`@media\` condition is true.
111
+ *
112
+ * \`max-width\` values match \`EUNOMIA_BREAKPOINTS.tablet\`/\`.mobile\`
113
+ * (\`breakpoints.ts\`) — hardcoded here since a plain CSS \`@media\` query
114
+ * can't read a JS constant.
115
+ */
116
+ const breakpoints = css `
117
+ @media (max-width: 768px) {
118
+ :host([tablet-animation="none"]) {
119
+ opacity: 1;
120
+ transform: none;
121
+ transition-property: none;
122
+ }
123
+
124
+ :host([tablet-animation="fade"]) {
125
+ opacity: 0;
126
+ transition-property: opacity;
127
+ }
128
+ :host([tablet-animation="fade"][in-view]) {
129
+ opacity: 1;
130
+ }
131
+
132
+ :host([tablet-animation="scroll-right"]) {
133
+ transform: translateX(calc(-1 * var(--_distance)));
134
+ transition-property: transform;
135
+ }
136
+ :host([tablet-animation="scroll-right"][in-view]) {
137
+ transform: translateX(0);
138
+ }
139
+
140
+ :host([tablet-animation="scroll-left"]) {
141
+ transform: translateX(var(--_distance));
142
+ transition-property: transform;
143
+ }
144
+ :host([tablet-animation="scroll-left"][in-view]) {
145
+ transform: translateX(0);
146
+ }
147
+
148
+ :host([tablet-animation="scroll-top"]) {
149
+ transform: translateY(var(--_distance));
150
+ transition-property: transform;
151
+ }
152
+ :host([tablet-animation="scroll-top"][in-view]) {
153
+ transform: translateY(0);
154
+ }
155
+
156
+ :host([tablet-animation="scroll-bottom"]) {
157
+ transform: translateY(calc(-1 * var(--_distance)));
158
+ transition-property: transform;
159
+ }
160
+ :host([tablet-animation="scroll-bottom"][in-view]) {
161
+ transform: translateY(0);
162
+ }
163
+
164
+ :host([tablet-animation="faded-scroll-right"]) {
165
+ opacity: 0;
166
+ transform: translateX(calc(-1 * var(--_distance)));
167
+ transition-property: opacity, transform;
168
+ }
169
+ :host([tablet-animation="faded-scroll-right"][in-view]) {
170
+ opacity: 1;
171
+ transform: translateX(0);
172
+ }
173
+
174
+ :host([tablet-animation="faded-scroll-left"]) {
175
+ opacity: 0;
176
+ transform: translateX(var(--_distance));
177
+ transition-property: opacity, transform;
178
+ }
179
+ :host([tablet-animation="faded-scroll-left"][in-view]) {
180
+ opacity: 1;
181
+ transform: translateX(0);
182
+ }
183
+
184
+ :host([tablet-animation="faded-scroll-top"]) {
185
+ opacity: 0;
186
+ transform: translateY(var(--_distance));
187
+ transition-property: opacity, transform;
188
+ }
189
+ :host([tablet-animation="faded-scroll-top"][in-view]) {
190
+ opacity: 1;
191
+ transform: translateY(0);
192
+ }
193
+
194
+ :host([tablet-animation="faded-scroll-bottom"]) {
195
+ opacity: 0;
196
+ transform: translateY(calc(-1 * var(--_distance)));
197
+ transition-property: opacity, transform;
198
+ }
199
+ :host([tablet-animation="faded-scroll-bottom"][in-view]) {
200
+ opacity: 1;
201
+ transform: translateY(0);
202
+ }
203
+ }
204
+
205
+ @media (max-width: 600px) {
206
+ :host([mobile-animation="none"]) {
207
+ opacity: 1;
208
+ transform: none;
209
+ transition-property: none;
210
+ }
211
+
212
+ :host([mobile-animation="fade"]) {
213
+ opacity: 0;
214
+ transition-property: opacity;
215
+ }
216
+ :host([mobile-animation="fade"][in-view]) {
217
+ opacity: 1;
218
+ }
219
+
220
+ :host([mobile-animation="scroll-right"]) {
221
+ transform: translateX(calc(-1 * var(--_distance)));
222
+ transition-property: transform;
223
+ }
224
+ :host([mobile-animation="scroll-right"][in-view]) {
225
+ transform: translateX(0);
226
+ }
227
+
228
+ :host([mobile-animation="scroll-left"]) {
229
+ transform: translateX(var(--_distance));
230
+ transition-property: transform;
231
+ }
232
+ :host([mobile-animation="scroll-left"][in-view]) {
233
+ transform: translateX(0);
234
+ }
235
+
236
+ :host([mobile-animation="scroll-top"]) {
237
+ transform: translateY(var(--_distance));
238
+ transition-property: transform;
239
+ }
240
+ :host([mobile-animation="scroll-top"][in-view]) {
241
+ transform: translateY(0);
242
+ }
243
+
244
+ :host([mobile-animation="scroll-bottom"]) {
245
+ transform: translateY(calc(-1 * var(--_distance)));
246
+ transition-property: transform;
247
+ }
248
+ :host([mobile-animation="scroll-bottom"][in-view]) {
249
+ transform: translateY(0);
250
+ }
251
+
252
+ :host([mobile-animation="faded-scroll-right"]) {
253
+ opacity: 0;
254
+ transform: translateX(calc(-1 * var(--_distance)));
255
+ transition-property: opacity, transform;
256
+ }
257
+ :host([mobile-animation="faded-scroll-right"][in-view]) {
258
+ opacity: 1;
259
+ transform: translateX(0);
260
+ }
261
+
262
+ :host([mobile-animation="faded-scroll-left"]) {
263
+ opacity: 0;
264
+ transform: translateX(var(--_distance));
265
+ transition-property: opacity, transform;
266
+ }
267
+ :host([mobile-animation="faded-scroll-left"][in-view]) {
268
+ opacity: 1;
269
+ transform: translateX(0);
270
+ }
271
+
272
+ :host([mobile-animation="faded-scroll-top"]) {
273
+ opacity: 0;
274
+ transform: translateY(var(--_distance));
275
+ transition-property: opacity, transform;
276
+ }
277
+ :host([mobile-animation="faded-scroll-top"][in-view]) {
278
+ opacity: 1;
279
+ transform: translateY(0);
280
+ }
281
+
282
+ :host([mobile-animation="faded-scroll-bottom"]) {
283
+ opacity: 0;
284
+ transform: translateY(calc(-1 * var(--_distance)));
285
+ transition-property: opacity, transform;
286
+ }
287
+ :host([mobile-animation="faded-scroll-bottom"][in-view]) {
288
+ opacity: 1;
289
+ transform: translateY(0);
290
+ }
291
+ }
292
+ `;
293
+ export const animateStyles = [base, breakpoints];
@@ -50,7 +50,7 @@ const base = css `
50
50
 
51
51
  #carousel-header {
52
52
  display: flex;
53
- align-items: var(--carousel-header-align, baseline);
53
+ align-items: var(--carousel-header-align, flex-end);
54
54
  justify-content: space-between;
55
55
  gap: var(--carousel-header-gap, var(--eun-space-m, 16px));
56
56
  margin-bottom: var(--eun-space-m, 16px);
@@ -54,6 +54,8 @@ declare const EunomiaToolbar_base: import("../../../utils/constructor.type.js").
54
54
  * everything behind it, including the logo and any other bar content;
55
55
  * the burger is the only thing that stays visible in front of it.
56
56
  *
57
+ * @slot [menu-icon] - Replaces the burger's default open icon (an `eun-icon name="menu"`) with any content, shown while the panel is closed. Useful wherever `eun-icon`'s own dynamic SVG resolution doesn't fit, e.g. some bundler setups
58
+ * @slot [close-icon] - Replaces the burger's default close icon (an `eun-icon name="close"`) with any content, shown while the panel is open. See `menu-icon` above
57
59
  * @slot start - Left-side content, rendered after a logo image linking to logoHref whenever logoSrc is set
58
60
  * @slot center - Center content, truly centered independently of the start and end widths
59
61
  * @slot end - Right-side content
@@ -135,6 +137,7 @@ declare const EunomiaToolbar_base: import("../../../utils/constructor.type.js").
135
137
  * @cssprop [--toolbar-skip-links-background] - Sets the background of the skip-links bar, defaulting to a darker mix of the bar's own background
136
138
  * @cssprop [--toolbar-skip-links-color] - Sets the text color of the skip-links bar
137
139
  * @cssprop [--toolbar-skip-links-separator-color] - Sets the color of the divider drawn between skip links
140
+ * @cssprop [--toolbar-skip-links-gap] - Sets the gap between skip links, with the divider centered in it
138
141
  * @element eun-toolbar
139
142
  */
140
143
  export declare class EunomiaToolbar extends EunomiaToolbar_base {
@@ -302,6 +305,25 @@ export declare class EunomiaToolbar extends EunomiaToolbar_base {
302
305
  * @readonly
303
306
  */
304
307
  private readonly burgerEl?;
308
+ /**
309
+ * Elements assigned to the `menu-icon` slot, if any ; queried live so the
310
+ * default `eun-icon` fallback correctly appears/disappears as content is
311
+ * slotted/removed, same pattern `eun-collapse`'s `chevron` slot uses.
312
+ * @internal
313
+ */
314
+ private readonly menuIconSlotEls;
315
+ /**
316
+ * Elements assigned to the `close-icon` slot, if any ; see
317
+ * {@link menuIconSlotEls}.
318
+ * @internal
319
+ */
320
+ private readonly closeIconSlotEls;
321
+ /**
322
+ * Re-renders once a `menu-icon`/`close-icon` slot's content changes, so
323
+ * the default `eun-icon` fallback correctly appears/disappears.
324
+ * @private
325
+ */
326
+ private handleIconSlotChange;
305
327
  /**
306
328
  * The live `matchMedia` list backing {@link mobile}, rebuilt whenever
307
329
  * `mobileBreakpoint` changes.