@sebgroup/green-core 3.25.0 → 3.25.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.
@@ -117,8 +117,18 @@ declare class Datepicker extends GdsFormControlElement<Date> {
117
117
  * Intended for use in integration tests.
118
118
  */
119
119
  test_getDateCell(dayNumber: number): Promise<Element | null | undefined>;
120
- private _focusedMonth;
121
- private _focusedYear;
120
+ /**
121
+ * The month currently displayed in the calendar popover. Defaults to today's month, clamped into
122
+ * `min`/`max`, until explicitly set (e.g. by a `value`, or user navigation) - so that opening the
123
+ * calendar without a value never lands on an out-of-range month where no date can be selected.
124
+ */
125
+ private get _focusedMonth();
126
+ private set _focusedMonth(value);
127
+ /**
128
+ * The year currently displayed in the calendar popover. See `_focusedMonth` for defaulting behaviour.
129
+ */
130
+ private get _focusedYear();
131
+ private set _focusedYear(value);
122
132
  private _dateFormatLayout;
123
133
  private _outOfRangeAction?;
124
134
  private _currentLocale;
@@ -5,7 +5,7 @@ import {
5
5
  __privateMethod,
6
6
  __privateSet
7
7
  } from "../../chunks/chunk.H2T4A5YY.js";
8
- var _utcHours, _valueOnOpen, _Datepicker_instances, shouldShowFooter_fn, renderBackToValidRangeButton_fn, updateOutOfRangeAction_fn, normalizeDate_fn, focusDate_fn, isDateSelectable_fn, nearestSelectableDate_fn, shouldShowTodayButton_fn, getSpinnerLabel_fn, getMinSpinnerValue_fn, getMaxSpinnerValue_fn, getLocalizedPlaceholder_fn, getSpinnerValue_fn, dispatchChangeEvent_fn, dispatchInputEvent_fn, _handleSpinnerFocus, _handleClipboardCopy, _handleClipboardPaste, _handleFieldClick, _handleCalendarChange, _handleMonthChange, _handleYearChange, _handleIncrementFocusedMonth, _handleDecrementFocusedMonth, _handleCalendarFocusChange, _handlePopoverStateChange, _handleSpinnerKeydown, parseDateFormat_fn, _handleSpinnerChange, _spinnerState, years_get, isValueOutsideRange_get;
8
+ var _utcHours, _focusedMonthOverride, _focusedYearOverride, _Datepicker_instances, defaultFocusedDate_get, _valueOnOpen, shouldShowFooter_fn, renderBackToValidRangeButton_fn, updateOutOfRangeAction_fn, normalizeDate_fn, focusDate_fn, isDateSelectable_fn, nearestSelectableDate_fn, shouldShowTodayButton_fn, getSpinnerLabel_fn, getMinSpinnerValue_fn, getMaxSpinnerValue_fn, getLocalizedPlaceholder_fn, getSpinnerValue_fn, dispatchChangeEvent_fn, dispatchInputEvent_fn, _handleSpinnerFocus, _handleClipboardCopy, _handleClipboardPaste, _handleFieldClick, _handleCalendarChange, _handleMonthChange, _handleYearChange, _handleIncrementFocusedMonth, _handleDecrementFocusedMonth, _handleCalendarFocusChange, _handlePopoverStateChange, _handleSpinnerKeydown, parseDateFormat_fn, _handleSpinnerChange, _spinnerState, years_get, 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";
@@ -59,8 +59,8 @@ let Datepicker = class extends GdsFormControlElement {
59
59
  __privateAdd(this, _utcHours, 12);
60
60
  this.disabledWeekends = false;
61
61
  this.showExtendedSupportingText = false;
62
- this._focusedMonth = (/* @__PURE__ */ new Date()).getMonth();
63
- this._focusedYear = (/* @__PURE__ */ new Date()).getFullYear();
62
+ __privateAdd(this, _focusedMonthOverride);
63
+ __privateAdd(this, _focusedYearOverride);
64
64
  this._dateFormatLayout = __privateMethod(this, _Datepicker_instances, parseDateFormat_fn).call(this, "y-m-d");
65
65
  this._currentLocale = navigator.language;
66
66
  __privateAdd(this, _valueOnOpen);
@@ -257,6 +257,18 @@ let Datepicker = class extends GdsFormControlElement {
257
257
  async test_getDateCell(dayNumber) {
258
258
  return this._elCalendar.then((el) => el.getDateCell(dayNumber));
259
259
  }
260
+ get _focusedMonth() {
261
+ return __privateGet(this, _focusedMonthOverride) ?? __privateGet(this, _Datepicker_instances, defaultFocusedDate_get).getMonth();
262
+ }
263
+ set _focusedMonth(value) {
264
+ __privateSet(this, _focusedMonthOverride, value);
265
+ }
266
+ get _focusedYear() {
267
+ return __privateGet(this, _focusedYearOverride) ?? __privateGet(this, _Datepicker_instances, defaultFocusedDate_get).getFullYear();
268
+ }
269
+ set _focusedYear(value) {
270
+ __privateSet(this, _focusedYearOverride, value);
271
+ }
260
272
  connectedCallback() {
261
273
  super.connectedCallback();
262
274
  TransitionalStyles.instance.apply(this, "gds-datepicker");
@@ -559,8 +571,16 @@ let Datepicker = class extends GdsFormControlElement {
559
571
  }
560
572
  };
561
573
  _utcHours = new WeakMap();
562
- _valueOnOpen = new WeakMap();
574
+ _focusedMonthOverride = new WeakMap();
575
+ _focusedYearOverride = new WeakMap();
563
576
  _Datepicker_instances = new WeakSet();
577
+ defaultFocusedDate_get = function() {
578
+ const today = /* @__PURE__ */ new Date();
579
+ if (today < this.min) return this.min;
580
+ if (today > this.max) return this.max;
581
+ return today;
582
+ };
583
+ _valueOnOpen = new WeakMap();
564
584
  shouldShowFooter_fn = function() {
565
585
  return !this.plain;
566
586
  };
@@ -815,10 +835,10 @@ __decorateClass([
815
835
  ], Datepicker.prototype, "test_backToValidRangeButton", 2);
816
836
  __decorateClass([
817
837
  state()
818
- ], Datepicker.prototype, "_focusedMonth", 2);
838
+ ], Datepicker.prototype, "_focusedMonth", 1);
819
839
  __decorateClass([
820
840
  state()
821
- ], Datepicker.prototype, "_focusedYear", 2);
841
+ ], Datepicker.prototype, "_focusedYear", 1);
822
842
  __decorateClass([
823
843
  state()
824
844
  ], Datepicker.prototype, "_dateFormatLayout", 2);
@@ -20,6 +20,17 @@ export declare class GdsDetails extends GdsDetails_base {
20
20
  #private;
21
21
  static styles: (import("lit").CSSResult | import("lit").CSSResult[])[];
22
22
  private _isAnimating;
23
+ /**
24
+ * Drives the native `<details>` element's `open` attribute. Kept as a
25
+ * separate state from `open` because its timing must be sequenced around
26
+ * the height transition below (see `__handleOpenChange`).
27
+ */
28
+ private _detailsOpen;
29
+ /**
30
+ * Drives the CSS classes that trigger the height/padding transition.
31
+ * Kept separate from `open` for the same reason as `_detailsOpen`.
32
+ */
33
+ private _visualOpen;
23
34
  /**
24
35
  * The summary text displayed in the details header
25
36
  */
@@ -39,6 +50,8 @@ export declare class GdsDetails extends GdsDetails_base {
39
50
  private _content?;
40
51
  private _openIconSlot?;
41
52
  private _closedIconSlot?;
53
+ connectedCallback(): void;
54
+ disconnectedCallback(): void;
42
55
  private __handleOpenChange;
43
56
  render(): any;
44
57
  }
@@ -1,9 +1,12 @@
1
1
  import {
2
2
  __decorateClass,
3
3
  __privateAdd,
4
- __privateGet
4
+ __privateGet,
5
+ __privateMethod,
6
+ __privateSet,
7
+ __privateWrapper
5
8
  } from "../../chunks/chunk.H2T4A5YY.js";
6
- var _handleToggle, _handleKeydown, _syncGroupState, _dispatchStateEvent, _handleContentTransitionEnd, _renderHeader, _renderIconButton, _renderContent;
9
+ var _pendingToggleId, _animationFallbackTimeoutId, _GdsDetails_instances, scheduleAnimationFallback_fn, _handleToggle, _handleKeydown, _syncGroupState, _dispatchStateEvent, _handleContentTransitionEnd, _renderHeader, _renderIconButton, _renderContent;
7
10
  import { property, query, state } from "lit/decorators.js";
8
11
  import { classMap } from "lit/directives/class-map.js";
9
12
  import { GdsElement } from "../../gds-element.js";
@@ -22,17 +25,32 @@ import {
22
25
  import { GdsButton } from "../button/button.component.js";
23
26
  import { GdsIconDetails } from "./details-icon/details-icon.component.js";
24
27
  import DetailsStyles from "./details.styles.js";
28
+ function parseCssTimeToMs(value) {
29
+ const trimmed = value.trim();
30
+ const num = parseFloat(trimmed);
31
+ if (Number.isNaN(num)) return 0;
32
+ return trimmed.endsWith("ms") ? num : num * 1e3;
33
+ }
34
+ function getTransitionDurationMs(el) {
35
+ return getComputedStyle(el).transitionDuration.split(",").reduce((max, value) => Math.max(max, parseCssTimeToMs(value)), 0);
36
+ }
25
37
  let GdsDetails = class extends withSizeXProps(
26
38
  withMarginProps(withLayoutChildProps(GdsElement))
27
39
  ) {
28
40
  constructor() {
29
41
  super(...arguments);
42
+ __privateAdd(this, _GdsDetails_instances);
30
43
  this._isAnimating = false;
44
+ this._detailsOpen = false;
45
+ this._visualOpen = false;
31
46
  this.summary = "";
32
47
  this.name = "";
33
48
  this.open = false;
34
49
  this.size = "large";
35
- __privateAdd(this, _handleToggle, () => {
50
+ __privateAdd(this, _pendingToggleId, 0);
51
+ __privateAdd(this, _animationFallbackTimeoutId);
52
+ __privateAdd(this, _handleToggle, (event) => {
53
+ event?.preventDefault();
36
54
  this.open = !this.open;
37
55
  __privateGet(this, _dispatchStateEvent).call(this);
38
56
  });
@@ -63,35 +81,34 @@ let GdsDetails = class extends withSizeXProps(
63
81
  __privateAdd(this, _handleContentTransitionEnd, (event) => {
64
82
  if (event.target !== this._content || event.propertyName !== "height")
65
83
  return;
84
+ if (__privateGet(this, _animationFallbackTimeoutId) !== void 0) {
85
+ clearTimeout(__privateGet(this, _animationFallbackTimeoutId));
86
+ __privateSet(this, _animationFallbackTimeoutId, void 0);
87
+ }
66
88
  this._isAnimating = false;
89
+ this._detailsOpen = this.open;
67
90
  });
68
91
  __privateAdd(this, _renderHeader, () => {
69
92
  return html`
70
- <div
93
+ <summary
71
94
  class="summary"
72
95
  part="summary"
73
- role="button"
74
- tabindex="0"
75
96
  @click=${__privateGet(this, _handleToggle)}
76
97
  @keydown=${__privateGet(this, _handleKeydown)}
77
- aria-expanded="${this.open}"
78
- aria-controls="content-${this.name || "default"}"
79
98
  >
80
99
  <div id="summary-${this.name || "default"}" class="summary-label">
81
100
  ${this.summary || "Summary"}
82
101
  </div>
83
102
  ${__privateGet(this, _renderIconButton).call(this)}
84
- </div>
103
+ </summary>
85
104
  `;
86
105
  });
87
106
  __privateAdd(this, _renderIconButton, () => {
88
107
  return html`
89
- <div class="summary-icon" role="presentation" aria-hidden="true">
108
+ <div class="summary-icon" inert>
90
109
  <gds-button
91
110
  rank="tertiary"
92
111
  size=${this.size === "small" ? "xs" : "medium"}
93
- role="presentation"
94
- gds-tabindex="-1"
95
112
  >
96
113
  <gds-icon-details .open=${this.open}></gds-icon-details>
97
114
  </gds-button>
@@ -104,7 +121,7 @@ let GdsDetails = class extends withSizeXProps(
104
121
  id="content-${this.name || "default"}"
105
122
  class=${classMap({
106
123
  content: true,
107
- open: this.open,
124
+ open: this._visualOpen,
108
125
  animating: this._isAnimating,
109
126
  small: this.size === "small"
110
127
  })}
@@ -117,26 +134,92 @@ let GdsDetails = class extends withSizeXProps(
117
134
  `;
118
135
  });
119
136
  }
137
+ connectedCallback() {
138
+ super.connectedCallback();
139
+ __privateWrapper(this, _pendingToggleId)._++;
140
+ if (__privateGet(this, _animationFallbackTimeoutId) !== void 0) {
141
+ clearTimeout(__privateGet(this, _animationFallbackTimeoutId));
142
+ __privateSet(this, _animationFallbackTimeoutId, void 0);
143
+ }
144
+ this._isAnimating = false;
145
+ this._detailsOpen = this.open;
146
+ this._visualOpen = this.open;
147
+ }
148
+ disconnectedCallback() {
149
+ super.disconnectedCallback();
150
+ __privateWrapper(this, _pendingToggleId)._++;
151
+ if (__privateGet(this, _animationFallbackTimeoutId) !== void 0) {
152
+ clearTimeout(__privateGet(this, _animationFallbackTimeoutId));
153
+ __privateSet(this, _animationFallbackTimeoutId, void 0);
154
+ }
155
+ }
120
156
  __handleOpenChange() {
121
157
  __privateGet(this, _syncGroupState).call(this);
122
- if (this.hasUpdated) {
123
- this._isAnimating = true;
158
+ if (!this.hasUpdated) {
159
+ this._detailsOpen = this.open;
160
+ this._visualOpen = this.open;
161
+ return;
162
+ }
163
+ this._isAnimating = true;
164
+ const toggleId = ++__privateWrapper(this, _pendingToggleId)._;
165
+ if (__privateGet(this, _animationFallbackTimeoutId) !== void 0) {
166
+ clearTimeout(__privateGet(this, _animationFallbackTimeoutId));
167
+ __privateSet(this, _animationFallbackTimeoutId, void 0);
168
+ }
169
+ if (this.open) {
170
+ this._detailsOpen = true;
171
+ this.updateComplete.then(() => {
172
+ if (toggleId !== __privateGet(this, _pendingToggleId) || !this.open) return;
173
+ this._content?.getBoundingClientRect();
174
+ this._visualOpen = true;
175
+ __privateMethod(this, _GdsDetails_instances, scheduleAnimationFallback_fn).call(this);
176
+ });
177
+ } else {
178
+ this._visualOpen = false;
179
+ this.updateComplete.then(() => {
180
+ if (toggleId !== __privateGet(this, _pendingToggleId) || this.open) return;
181
+ __privateMethod(this, _GdsDetails_instances, scheduleAnimationFallback_fn).call(this);
182
+ });
124
183
  }
125
184
  }
126
185
  render() {
127
186
  return html`
128
- <div
187
+ <details
129
188
  class=${classMap({
130
189
  details: true,
131
- open: this.open,
190
+ open: this._visualOpen,
132
191
  small: this.size === "small"
133
192
  })}
193
+ ?open=${this._detailsOpen}
134
194
  >
135
195
  ${__privateGet(this, _renderHeader).call(this)} ${__privateGet(this, _renderContent).call(this)}
136
- </div>
196
+ </details>
137
197
  `;
138
198
  }
139
199
  };
200
+ _pendingToggleId = new WeakMap();
201
+ _animationFallbackTimeoutId = new WeakMap();
202
+ _GdsDetails_instances = new WeakSet();
203
+ /**
204
+ * Guarantees `_isAnimating`/`_detailsOpen` eventually settle even if the
205
+ * `height` transitionend this normally waits for never fires -- e.g.
206
+ * browsers without support for animating to/from intrinsic sizing
207
+ * keywords (`interpolate-size`) don't actually transition `height` here,
208
+ * so no such event is ever dispatched.
209
+ */
210
+ scheduleAnimationFallback_fn = function() {
211
+ if (!this._content) {
212
+ this._isAnimating = false;
213
+ this._detailsOpen = this.open;
214
+ return;
215
+ }
216
+ const fallbackDelay = getTransitionDurationMs(this._content) + 100;
217
+ __privateSet(this, _animationFallbackTimeoutId, setTimeout(() => {
218
+ __privateSet(this, _animationFallbackTimeoutId, void 0);
219
+ this._isAnimating = false;
220
+ this._detailsOpen = this.open;
221
+ }, fallbackDelay));
222
+ };
140
223
  _handleToggle = new WeakMap();
141
224
  _handleKeydown = new WeakMap();
142
225
  _syncGroupState = new WeakMap();
@@ -149,6 +232,12 @@ GdsDetails.styles = [tokens, DetailsStyles];
149
232
  __decorateClass([
150
233
  state()
151
234
  ], GdsDetails.prototype, "_isAnimating", 2);
235
+ __decorateClass([
236
+ state()
237
+ ], GdsDetails.prototype, "_detailsOpen", 2);
238
+ __decorateClass([
239
+ state()
240
+ ], GdsDetails.prototype, "_visualOpen", 2);
152
241
  __decorateClass([
153
242
  property({ type: String })
154
243
  ], GdsDetails.prototype, "summary", 2);
@@ -101,6 +101,10 @@ const style = css`
101
101
  }
102
102
 
103
103
  .content {
104
+ /* Overrides the UA stylesheet's details:not([open]) > *:not(summary)
105
+ rule (display: none), which would otherwise prevent the
106
+ height-based open/close transition below from running. */
107
+ display: block;
104
108
  interpolate-size: allow-keywords;
105
109
  height: 0;
106
110
  transition:
@@ -13,6 +13,11 @@ const style = css`
13
13
  width: max-content;
14
14
  }
15
15
 
16
+ /* Match the icon-only button's inline size to the height boost above, so it renders as a circle instead of a stadium shape. */
17
+ .button.circle {
18
+ min-inline-size: calc(var(--_block-size) + var(--gds-sys-space-xs));
19
+ }
20
+
16
21
  ::slotted([gds-element='gds-signal']) {
17
22
  margin-left: var(--gds-sys-space-xs);
18
23
  }
@@ -519,6 +519,7 @@ renderCellContent_fn = function(row, column, index) {
519
519
  return html`
520
520
  <div
521
521
  class="cell-content"
522
+ role=${ifDefined(isMobile ? void 0 : "presentation")}
522
523
  aria-label=${ifDefined(isMobile ? column.label : void 0)}
523
524
  >
524
525
  ${isMobile ? __privateMethod(this, _GdsTable_instances, renderMobileLabel_fn).call(this, column) : null} ${content}
@@ -903,7 +904,7 @@ renderExpandedLoadingRow_fn = function(_row, _index, groupId, childIndex, rowCou
903
904
  const isActionsColumn = String(column.key) === "actions";
904
905
  return html`
905
906
  <td class=${classes} style=${style}>
906
- <div class="cell-content">
907
+ <div class="cell-content" role="presentation">
907
908
  ${isActionsColumn ? html`<span class="skeleton skeleton-action"></span>` : html`<span class="skeleton skeleton-text"></span>`}
908
909
  </div>
909
910
  </td>
@@ -1091,7 +1092,7 @@ renderSkeletonCell_fn = function(column) {
1091
1092
  const isResponsive = this._isMobile && this.responsive;
1092
1093
  const mobileLabel = isResponsive ? html`<span class="skeleton skeleton-text"></span>` : null;
1093
1094
  return html`
1094
- <div class="cell-content">
1095
+ <div class="cell-content" role="presentation">
1095
1096
  ${mobileLabel}
1096
1097
  <span class="skeleton skeleton-text"></span>
1097
1098
  </div>
@@ -1199,7 +1200,7 @@ renderTableFoot_fn = function() {
1199
1200
  column.sticky ? column.key : void 0
1200
1201
  )}
1201
1202
  >
1202
- <div class="cell-content">
1203
+ <div class="cell-content" role="presentation">
1203
1204
  <span class="tablefoot-label">${label}</span>
1204
1205
  <slot name="tfoot:${column.key}"></slot>
1205
1206
  </div>
@@ -1214,7 +1215,7 @@ renderTableFoot_fn = function() {
1214
1215
  column.sticky ? column.key : void 0
1215
1216
  )}
1216
1217
  >
1217
- <div class="cell-content">
1218
+ <div class="cell-content" role="presentation">
1218
1219
  <slot name="tfoot:${column.key}"></slot>
1219
1220
  </div>
1220
1221
  </td>