@sebgroup/green-core 3.24.0 → 3.24.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,12 @@ import {
28
28
  import { GdsFormControlElement } from "../form/form-control.js";
29
29
  import ButtonStyles from "./button.styles.js";
30
30
  const ariaForwards = ["aria-label", "aria-haspopup", "aria-expanded"];
31
- const attributeForwards = [...ariaForwards, "gds-tabindex", "gds-aria-selected"];
31
+ const attributeForwards = [
32
+ ...ariaForwards,
33
+ "gds-tabindex",
34
+ "gds-aria-selected",
35
+ "gds-aria-expanded"
36
+ ];
32
37
  const html = stripWhitespace(customElementHtml);
33
38
  let Button = class extends GdsFormControlElement {
34
39
  constructor() {
@@ -26,6 +26,7 @@ import { tokens } from "../../tokens.style.js";
26
26
  import { TransitionalStyles } from "../../transitional-styles.js";
27
27
  import { ariaForwarding } from "../../utils/decorators/aria-forwarding.js";
28
28
  import { watch } from "../../utils/decorators/watch.js";
29
+ import { dateHasChanged } from "../../utils/helpers/has-changed.js";
29
30
  import CalendarStyles from "./calendar.styles.js";
30
31
  import { renderMonthGridView } from "./functions.js";
31
32
  let GdsCalendar = class extends GdsElement {
@@ -268,7 +269,7 @@ GdsCalendar.shadowRootOptions = {
268
269
  delegatesFocus: true
269
270
  };
270
271
  __decorateClass([
271
- property()
272
+ property({ hasChanged: dateHasChanged })
272
273
  ], GdsCalendar.prototype, "value", 2);
273
274
  __decorateClass([
274
275
  property({ type: Date })
@@ -132,6 +132,9 @@ let GdsContextMenu = class extends withMarginProps(
132
132
  ${when(this.open, __privateGet(this, _renderPopover))}`;
133
133
  }
134
134
  _handleOpenChange() {
135
+ if (__privateGet(this, _elTriggerBtn)) {
136
+ applyTriggerAriaAttributes(__privateGet(this, _elTriggerBtn), this.open, "menu");
137
+ }
135
138
  if (!this.open) {
136
139
  requestAnimationFrame(() => __privateGet(this, _elTriggerBtn)?.focus());
137
140
  }
@@ -5,7 +5,7 @@ import {
5
5
  __privateMethod,
6
6
  __privateSet
7
7
  } from "../../chunks/chunk.CAV4X6PU.js";
8
- var _utcHours, _valueOnOpen, _shouldShowFooter, shouldShowFooter_fn, _renderBackToValidRangeButton, renderBackToValidRangeButton_fn, _updateOutOfRangeAction, updateOutOfRangeAction_fn, _normalizeDate, normalizeDate_fn, _focusDate, focusDate_fn, _shouldShowTodayButton, shouldShowTodayButton_fn, _getSpinnerLabel, getSpinnerLabel_fn, _getMinSpinnerValue, getMinSpinnerValue_fn, _getMaxSpinnerValue, getMaxSpinnerValue_fn, _getLocalizedPlaceholder, getLocalizedPlaceholder_fn, _getSpinnerValue, getSpinnerValue_fn, _dispatchChangeEvent, dispatchChangeEvent_fn, _dispatchInputEvent, dispatchInputEvent_fn, _handleSpinnerFocus, _handleClipboardCopy, _handleClipboardPaste, _handleFieldClick, _handleCalendarChange, _handleMonthChange, _handleYearChange, _handleIncrementFocusedMonth, _handleDecrementFocusedMonth, _handleCalendarFocusChange, _handlePopoverStateChange, _handleSpinnerKeydown, _parseDateFormat, parseDateFormat_fn, _handleSpinnerChange, _spinnerState, _years, years_get, _isValueOutsideRange, isValueOutsideRange_get;
8
+ var _utcHours, _valueOnOpen, _shouldShowFooter, shouldShowFooter_fn, _renderBackToValidRangeButton, renderBackToValidRangeButton_fn, _updateOutOfRangeAction, updateOutOfRangeAction_fn, _normalizeDate, normalizeDate_fn, _focusDate, focusDate_fn, _isDateSelectable, isDateSelectable_fn, _nearestSelectableDate, nearestSelectableDate_fn, _shouldShowTodayButton, shouldShowTodayButton_fn, _getSpinnerLabel, getSpinnerLabel_fn, _getMinSpinnerValue, getMinSpinnerValue_fn, _getMaxSpinnerValue, getMaxSpinnerValue_fn, _getLocalizedPlaceholder, getLocalizedPlaceholder_fn, _getSpinnerValue, getSpinnerValue_fn, _dispatchChangeEvent, dispatchChangeEvent_fn, _dispatchInputEvent, dispatchInputEvent_fn, _handleSpinnerFocus, _handleClipboardCopy, _handleClipboardPaste, _handleFieldClick, _handleCalendarChange, _handleMonthChange, _handleYearChange, _handleIncrementFocusedMonth, _handleDecrementFocusedMonth, _handleCalendarFocusChange, _handlePopoverStateChange, _handleSpinnerKeydown, _parseDateFormat, parseDateFormat_fn, _handleSpinnerChange, _spinnerState, _years, years_get, _isValueOutsideRange, isValueOutsideRange_get;
9
9
  import { localized, msg } from "@lit/localize";
10
10
  import { nothing } from "lit";
11
11
  import { property, query, queryAll, queryAsync, state } from "lit/decorators.js";
@@ -13,7 +13,7 @@ import { join } from "lit/directives/join.js";
13
13
  import { map } from "lit/directives/map.js";
14
14
  import { repeat } from "lit/directives/repeat.js";
15
15
  import { when } from "lit/directives/when.js";
16
- import { isSameDay, lastDayOfMonth, setHours } from "date-fns";
16
+ import { addDays, isSameDay, lastDayOfMonth, setHours } from "date-fns";
17
17
  import { GdsButton } from "../../components/button/button.component.js";
18
18
  import { GdsDropdown } from "../../components/dropdown/dropdown.component.js";
19
19
  import { GdsFlex } from "../../components/flex/flex.component.js";
@@ -27,6 +27,7 @@ import {
27
27
  dateArrayConverter,
28
28
  dateConverter
29
29
  } from "../../utils/helpers/attribute-converters.js";
30
+ import { dateHasChanged } from "../../utils/helpers/has-changed.js";
30
31
  import {
31
32
  withLayoutChildProps,
32
33
  withMarginProps,
@@ -52,7 +53,24 @@ let Datepicker = class extends GdsFormControlElement {
52
53
  * the local calendar day is preserved, then to the configured UTC hour.
53
54
  */
54
55
  __privateAdd(this, _normalizeDate);
56
+ /**
57
+ * Focuses (and selects) a date in the calendar. The month/year state and value are derived
58
+ * directly from `d` rather than read back from the calendar element asynchronously, so that a
59
+ * concurrent update (e.g. the host re-assigning an equivalent `value`) cannot race with, and
60
+ * overwrite, the requested focus change.
61
+ */
55
62
  __privateAdd(this, _focusDate);
63
+ /**
64
+ * Whether the given date can be selected, taking `min`/`max`, `disabledWeekends` and
65
+ * `disabledDates` into account.
66
+ */
67
+ __privateAdd(this, _isDateSelectable);
68
+ /**
69
+ * Finds the nearest selectable date starting from `from` (inclusive), stepping a day at a time in
70
+ * `direction` (`1` towards `max`, `-1` towards `min`). Falls back to `from` if the min/max range is
71
+ * exhausted without finding a selectable date.
72
+ */
73
+ __privateAdd(this, _nearestSelectableDate);
56
74
  __privateAdd(this, _shouldShowTodayButton);
57
75
  __privateAdd(this, _getSpinnerLabel);
58
76
  __privateAdd(this, _getMinSpinnerValue);
@@ -254,6 +272,8 @@ let Datepicker = class extends GdsFormControlElement {
254
272
  return super.value;
255
273
  }
256
274
  set value(value) {
275
+ if (!dateHasChanged(value, super.value))
276
+ return;
257
277
  super.value = value;
258
278
  }
259
279
  get utcHours() {
@@ -606,13 +626,14 @@ renderBackToValidRangeButton_fn = function() {
606
626
  if (!this._outOfRangeAction)
607
627
  return nothing;
608
628
  const isAfterMax = this._outOfRangeAction === "max";
629
+ const target = isAfterMax ? __privateMethod(this, _nearestSelectableDate, nearestSelectableDate_fn).call(this, this.max, -1) : __privateMethod(this, _nearestSelectableDate, nearestSelectableDate_fn).call(this, this.min, 1);
609
630
  return html`<gds-button
610
631
  id="back-to-valid-range-button"
611
632
  rank="tertiary"
612
633
  size="small"
613
634
  @click=${(e) => {
614
635
  e.stopPropagation();
615
- __privateMethod(this, _focusDate, focusDate_fn).call(this, isAfterMax ? this.max : this.min);
636
+ __privateMethod(this, _focusDate, focusDate_fn).call(this, target);
616
637
  }}
617
638
  >
618
639
  ${isAfterMax ? msg("Last available date") : msg("First available date")}
@@ -639,20 +660,43 @@ normalizeDate_fn = function(d) {
639
660
  _focusDate = new WeakSet();
640
661
  focusDate_fn = function(d) {
641
662
  const focusDate = __privateMethod(this, _normalizeDate, normalizeDate_fn).call(this, d);
642
- this._elCalendar.then((el) => el.focusedDate = focusDate).then(__privateGet(this, _handleCalendarFocusChange)).then(() => this._elCalendar).then((el) => el.updateComplete.then(() => el.focus()));
663
+ this._focusedMonth = focusDate.getMonth();
664
+ this._focusedYear = focusDate.getFullYear();
665
+ this.value = new Date(focusDate);
666
+ this.requestUpdate();
667
+ __privateMethod(this, _dispatchInputEvent, dispatchInputEvent_fn).call(this);
668
+ this._elCalendar.then((el) => {
669
+ el.focusedDate = focusDate;
670
+ el.updateComplete.then(() => el.focus());
671
+ });
643
672
  };
644
- _shouldShowTodayButton = new WeakSet();
645
- shouldShowTodayButton_fn = function() {
646
- if (this.hideTodayButton)
647
- return false;
648
- const today = __privateMethod(this, _normalizeDate, normalizeDate_fn).call(this, /* @__PURE__ */ new Date());
649
- const isOutsideMinMax = (today < this.min || today > this.max) && !isSameDay(today, this.min) && !isSameDay(today, this.max);
673
+ _isDateSelectable = new WeakSet();
674
+ isDateSelectable_fn = function(date) {
675
+ const isOutsideMinMax = (date < this.min || date > this.max) && !isSameDay(date, this.min) && !isSameDay(date, this.max);
650
676
  if (isOutsideMinMax)
651
677
  return false;
652
- const isWeekend = today.getDay() === 0 || today.getDay() === 6;
678
+ const isWeekend = date.getDay() === 0 || date.getDay() === 6;
653
679
  if (this.disabledWeekends && isWeekend)
654
680
  return false;
655
- return !this.disabledDates?.some((d) => isSameDay(d, today));
681
+ return !this.disabledDates?.some((d) => isSameDay(d, date));
682
+ };
683
+ _nearestSelectableDate = new WeakSet();
684
+ nearestSelectableDate_fn = function(from, direction) {
685
+ let candidate = new Date(from);
686
+ while (!__privateMethod(this, _isDateSelectable, isDateSelectable_fn).call(this, candidate)) {
687
+ const next = addDays(candidate, direction);
688
+ const passedBoundary = direction === 1 ? next > this.max : next < this.min;
689
+ if (passedBoundary)
690
+ return from;
691
+ candidate = next;
692
+ }
693
+ return candidate;
694
+ };
695
+ _shouldShowTodayButton = new WeakSet();
696
+ shouldShowTodayButton_fn = function() {
697
+ if (this.hideTodayButton)
698
+ return false;
699
+ return __privateMethod(this, _isDateSelectable, isDateSelectable_fn).call(this, __privateMethod(this, _normalizeDate, normalizeDate_fn).call(this, /* @__PURE__ */ new Date()));
656
700
  };
657
701
  _getSpinnerLabel = new WeakSet();
658
702
  getSpinnerLabel_fn = function(name) {
@@ -768,7 +812,7 @@ isValueOutsideRange_get = function() {
768
812
  };
769
813
  Datepicker.styles = [tokens, formControlHostStyle, DatepickerStyles];
770
814
  __decorateClass([
771
- property({ converter: dateConverter })
815
+ property({ converter: dateConverter, hasChanged: dateHasChanged })
772
816
  ], Datepicker.prototype, "value", 1);
773
817
  __decorateClass([
774
818
  property({ converter: dateConverter })
@@ -12,9 +12,16 @@ const style = css`
12
12
 
13
13
  @layer link {
14
14
  a {
15
- display: flex;
16
- align-items: center;
17
- gap: 1ch;
15
+ /*
16
+ * Intentionally not display: flex. A flex row only wraps onto a new
17
+ * flex line if the container wraps, but text wrapping inside a flex
18
+ * item happens within a single flex line. That caused lead/trail
19
+ * icons to center against the full height of multi-line wrapped text
20
+ * instead of following the specific line they're adjacent to.
21
+ * display: inline lets icons flow and wrap together with the text
22
+ * like any other inline content.
23
+ */
24
+ display: inline;
18
25
  text-decoration: none;
19
26
  text-underline-offset: 0.2lh;
20
27
  color: currentColor;
@@ -39,6 +46,19 @@ const style = css`
39
46
  }
40
47
  }
41
48
  }
49
+
50
+ ::slotted(*) {
51
+ display: inline-block;
52
+ vertical-align: middle;
53
+ }
54
+
55
+ slot[name='lead']::slotted(*) {
56
+ margin-inline-end: 1ch;
57
+ }
58
+
59
+ slot[name='trail']::slotted(*) {
60
+ margin-inline-start: 1ch;
61
+ }
42
62
  }
43
63
  `;
44
64
  var link_styles_default = style;
@@ -27,7 +27,8 @@ function roundByDPR(value) {
27
27
  return Math.round(value * dpr) / dpr;
28
28
  }
29
29
  function applyTriggerAriaAttributes(trigger, open, popupRole) {
30
- trigger.setAttribute("aria-expanded", String(open));
30
+ const ariaExpandedAttr = trigger.nodeName.startsWith("GDS-") ? "gds-aria-expanded" : "aria-expanded";
31
+ trigger.setAttribute(ariaExpandedAttr, String(open));
31
32
  const focusableNodeNames = ["A", "BUTTON", "INPUT", "TEXTAREA"];
32
33
  const isProbablyFocusable = trigger.nodeName.startsWith("GDS-") || focusableNodeNames.includes(trigger.nodeName);
33
34
  if (!isProbablyFocusable) {
@@ -222,7 +223,7 @@ let GdsPopover = class extends GdsElement {
222
223
  }
223
224
  _handleOpenChange() {
224
225
  this.updateComplete.then(() => {
225
- this._trigger?.setAttribute("aria-expanded", String(this.open));
226
+ __privateMethod(this, _setupTriggerAttributes, setupTriggerAttributes_fn).call(this);
226
227
  if (this.open) {
227
228
  !this.nonmodal ? this._elDialog?.showModal() : this._elDialog?.setAttribute("open", "true");
228
229
  if (this.autofocus) {