@sebgroup/green-core 3.29.0 → 3.30.0

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.
@@ -27,6 +27,7 @@ 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
29
  import { dateHasChanged } from "../../utils/helpers/has-changed.js";
30
+ import { gdsGetLocale } from "../../utils/localization/localization.js";
30
31
  import CalendarStyles from "./calendar.styles.js";
31
32
  import { renderMonthGridView } from "./functions.js";
32
33
  let GdsCalendar = class extends GdsElement {
@@ -79,6 +80,7 @@ let GdsCalendar = class extends GdsElement {
79
80
  connectedCallback() {
80
81
  super.connectedCallback();
81
82
  TransitionalStyles.instance.apply(this, "gds-calendar");
83
+ this._currentLocale = gdsGetLocale();
82
84
  this.addEventListener("keydown", __privateMethod(this, _GdsCalendar_instances, handleKeyDown_fn));
83
85
  window.addEventListener("lit-localize-status", (e) => {
84
86
  if (e.detail.status === "ready")
@@ -29,6 +29,7 @@ import {
29
29
  dateConverter
30
30
  } from "../../utils/helpers/attribute-converters.js";
31
31
  import { dateHasChanged } from "../../utils/helpers/has-changed.js";
32
+ import { gdsGetLocale } from "../../utils/localization/localization.js";
32
33
  import {
33
34
  withLayoutChildProps,
34
35
  withMarginProps,
@@ -280,6 +281,14 @@ let Datepicker = class extends GdsFormControlElement {
280
281
  connectedCallback() {
281
282
  super.connectedCallback();
282
283
  TransitionalStyles.instance.apply(this, "gds-datepicker");
284
+ this._currentLocale = gdsGetLocale();
285
+ if (!this.value) {
286
+ __privateSet(this, _spinnerState, {
287
+ year: __privateMethod(this, _Datepicker_instances, getLocalizedPlaceholder_fn).call(this, "year"),
288
+ month: __privateMethod(this, _Datepicker_instances, getLocalizedPlaceholder_fn).call(this, "month"),
289
+ day: __privateMethod(this, _Datepicker_instances, getLocalizedPlaceholder_fn).call(this, "day")
290
+ });
291
+ }
283
292
  window.addEventListener("lit-localize-status", (e) => {
284
293
  if (e.detail.status === "ready") {
285
294
  this._currentLocale = e.detail.readyLocale;
@@ -309,20 +309,18 @@ let Dropdown = class extends GdsFormControlElement {
309
309
  */
310
310
  get displayValue() {
311
311
  if (this.value === void 0) {
312
- return this.placeholder?.innerText || "";
312
+ return this.placeholder?.textContent?.trim() || "";
313
313
  }
314
314
  let displayValue;
315
315
  if (Array.isArray(this.value)) {
316
316
  displayValue = this.value.slice(0, 5).reduce(
317
- (acc, cur) => acc + this.options.find((v) => this.compareWith(v.value, cur))?.innerText + ", ",
317
+ (acc, cur) => acc + this.options.find((v) => this.compareWith(v.value, cur))?.textContent?.trim() + ", ",
318
318
  ""
319
319
  ).slice(0, -2).replace(/(.{25})(.*)/, "$1...");
320
320
  } else {
321
- displayValue = this.options.find(
322
- (v) => this.compareWith(v.value, this.value)
323
- )?.innerText;
321
+ displayValue = this.options.find((v) => this.compareWith(v.value, this.value))?.textContent?.trim();
324
322
  }
325
- return displayValue || this.placeholder?.innerText || "";
323
+ return displayValue || this.placeholder?.textContent?.trim() || "";
326
324
  }
327
325
  /**
328
326
  * Moves focus to this element.
@@ -14,6 +14,7 @@ import { gdsCustomElement, html } from "../../scoping.js";
14
14
  import { tokens } from "../../tokens.style.js";
15
15
  import { ariaForwarding } from "../../utils/decorators/aria-forwarding.js";
16
16
  import { styleExpressionProperty } from "../../utils/decorators/style-expression-property.js";
17
+ import { gdsGetLocale } from "../../utils/localization/localization.js";
17
18
  import {
18
19
  withLayoutChildProps,
19
20
  withMarginProps,
@@ -42,6 +43,7 @@ let Progress = class extends GdsElement {
42
43
  }
43
44
  connectedCallback() {
44
45
  super.connectedCallback();
46
+ this._currentLocale = gdsGetLocale();
45
47
  window.addEventListener("lit-localize-status", __privateGet(this, _handleLocaleStatus));
46
48
  }
47
49
  disconnectedCallback() {
@@ -236,7 +236,7 @@ isExpandableEnabled_get = function() {
236
236
  return !__privateGet(this, _GdsTable_instances, isResponsiveStacked_get) && __privateGet(this, _GdsTable_instances, expandableIndices_get).length > 0;
237
237
  };
238
238
  isSelectableEnabled_get = function() {
239
- return this.selectable && !__privateGet(this, _GdsTable_instances, isResponsiveStacked_get);
239
+ return this.selectable;
240
240
  };
241
241
  Density_get = function() {
242
242
  return Types.DENSITY_CONFIG[this.density];
@@ -835,16 +835,22 @@ renderExpandableCell_fn = function(row, index) {
835
835
  </td>
836
836
  `;
837
837
  };
838
- renderSelectableCell_fn = function(index) {
838
+ renderSelectableCell_fn = function(row, index) {
839
839
  if (!__privateGet(this, _GdsTable_instances, isSelectableEnabled_get)) return null;
840
- const selectRowLabel = `${msg("Select row")} ${index + 1}`;
840
+ const selectRowLabel = row.selectLabel || `${msg("Select row")} ${index + 1}`;
841
+ const isMobile = this._isMobile && this.responsive;
842
+ const inputId = `select-row-${index}`;
841
843
  return html`
842
844
  <td class="checkbox-cell">
845
+ ${isMobile ? html`<label class="column-label" aria-hidden="true" for=${inputId}
846
+ >${selectRowLabel}</label
847
+ >` : null}
843
848
  <div class="cell-center" role="presentation">
844
849
  ${__privateMethod(this, _GdsTable_instances, renderCheckbox_fn).call(this, {
845
850
  checked: this._selected.has(index),
846
851
  indeterminate: false,
847
852
  ariaLabel: selectRowLabel,
853
+ inputId,
848
854
  onToggle: () => __privateMethod(this, _GdsTable_instances, handleRowSelect_fn).call(this, index, {
849
855
  detail: { checked: !this._selected.has(index) }
850
856
  })
@@ -1031,7 +1037,7 @@ renderTableRow_fn = function(row, index) {
1031
1037
  >
1032
1038
  ${[
1033
1039
  __privateMethod(this, _GdsTable_instances, renderExpandableCell_fn).call(this, row, index),
1034
- __privateMethod(this, _GdsTable_instances, renderSelectableCell_fn).call(this, index),
1040
+ __privateMethod(this, _GdsTable_instances, renderSelectableCell_fn).call(this, row, index),
1035
1041
  __privateMethod(this, _GdsTable_instances, renderRowCells_fn).call(this, row, index)
1036
1042
  ]}
1037
1043
  </tr>
@@ -1047,7 +1053,8 @@ renderCheckbox_fn = function({
1047
1053
  disabled = false,
1048
1054
  ariaLabel,
1049
1055
  onToggle,
1050
- skip = false
1056
+ skip = false,
1057
+ inputId
1051
1058
  }) {
1052
1059
  if (skip) return null;
1053
1060
  const toggle = (e) => {
@@ -1062,6 +1069,16 @@ renderCheckbox_fn = function({
1062
1069
  }
1063
1070
  };
1064
1071
  return html`
1072
+ <input
1073
+ id=${ifDefined(inputId)}
1074
+ type="checkbox"
1075
+ class="visually-hidden-checkbox"
1076
+ .checked=${checked}
1077
+ .indeterminate=${indeterminate}
1078
+ ?disabled=${disabled}
1079
+ aria-hidden="true"
1080
+ @change=${toggle}
1081
+ />
1065
1082
  <div
1066
1083
  class="rbcb-wrapper"
1067
1084
  role="checkbox"
@@ -1070,15 +1087,6 @@ renderCheckbox_fn = function({
1070
1087
  @click=${toggle}
1071
1088
  @keydown=${onKey}
1072
1089
  >
1073
- <input
1074
- type="checkbox"
1075
- class="visually-hidden-checkbox"
1076
- .checked=${checked}
1077
- .indeterminate=${indeterminate}
1078
- ?disabled=${disabled}
1079
- aria-hidden="true"
1080
- @change=${toggle}
1081
- />
1082
1090
  ${Table.Checkbox({
1083
1091
  checked,
1084
1092
  indeterminate,
@@ -1146,7 +1154,8 @@ renderTableBody_fn = function() {
1146
1154
  this._expanding.has(index),
1147
1155
  this._completingExpansion.has(index),
1148
1156
  this._expandedRowsCache.get(index),
1149
- this._expandedFullContentResolved.has(index)
1157
+ this._expandedFullContentResolved.has(index),
1158
+ __privateGet(this, _GdsTable_instances, isResponsiveStacked_get)
1150
1159
  ],
1151
1160
  () => __privateMethod(this, _GdsTable_instances, renderTableRow_fn).call(this, row, index)
1152
1161
  )
@@ -16,7 +16,9 @@ const defineSlots = (row) => ({
16
16
  account: Slot(row.account, ["main"]),
17
17
  login: Slot(row.lastLogin, ["main"]),
18
18
  download: Slot(row.download ?? "#", ["main"]),
19
- actions: Slot(void 0, ["main"])
19
+ actions: Slot(void 0, ["main"]),
20
+ // Demonstrates the per-row `selectLabel` override, derived from the row's own name.
21
+ selectLabel: `Select ${String(row.name).split(" ")[0]}`
20
22
  });
21
23
  let usersRawCache = null;
22
24
  let usersRawPromise = null;
@@ -604,7 +606,9 @@ const financeRows = financeBaseRows.map((row) => ({
604
606
  available: Slot(row.available, ["available-value", "available-currency"]),
605
607
  status: Slot(row.status, ["status"]),
606
608
  accountNumberDisplay: Slot(row.accountNumber, ["main"]),
607
- actions: Slot(void 0, ["main"])
609
+ actions: Slot(void 0, ["main"]),
610
+ // Demonstrates the per-row `selectLabel` override, derived from the row's own account name.
611
+ selectLabel: `Select: ${row.account}`
608
612
  }));
609
613
  const financeExpandableData = async (request) => {
610
614
  let data = [...financeRows];
@@ -349,6 +349,12 @@ const TableStyles = css`
349
349
  height: 100%;
350
350
  }
351
351
 
352
+ /* The input sits beside the wrapper (not inside) so label clicks don't toggle twice. */
353
+ .visually-hidden-checkbox:focus-visible + .rbcb-wrapper .rbcb__perimeter {
354
+ outline-width: var(--gds-sys-space-4xs);
355
+ scale: 1;
356
+ }
357
+
352
358
  /* Expanded */
353
359
 
354
360
  .expander-cell {
@@ -784,11 +790,13 @@ const TableStyles = css`
784
790
  .responsive tbody tr {
785
791
  display: flex;
786
792
  flex-direction: column;
787
- padding: var(--table-cell-padding-x);
788
793
  border: var(--gds-sys-space-5xs) solid
789
794
  var(--gds-sys-color-border-neutral-02);
790
795
  border-radius: var(--gds-sys-space-s);
791
796
  margin: 0;
797
+ /* No card padding: each cell carries its own, so a sticky cell's
798
+ divider/background can reach the card edge without negative margins. */
799
+ overflow: hidden;
792
800
  }
793
801
 
794
802
  .responsive tbody tr:hover,
@@ -807,7 +815,6 @@ const TableStyles = css`
807
815
  display: flex;
808
816
  justify-content: flex-start;
809
817
  align-items: center;
810
- padding: var(--gds-sys-space-xs) 0;
811
818
  border: none;
812
819
  }
813
820
 
@@ -815,8 +822,63 @@ const TableStyles = css`
815
822
  .responsive td.checkbox-cell {
816
823
  width: auto;
817
824
  max-width: none;
818
- padding-top: 0;
819
- padding-bottom: var(--gds-sys-space-xs);
825
+ }
826
+
827
+ .responsive td.sticky-left,
828
+ .responsive td.sticky-right {
829
+ position: static;
830
+ z-index: auto;
831
+ }
832
+
833
+ /* Leading cells sit above the rest of the card; the divider goes under
834
+ whichever is the outermost one (mirrors the desktop sticky divider
835
+ logic), with margin to compensate for the card's own padding being
836
+ removed. */
837
+ .responsive.is-selectable:not(.has-sticky-left) td.checkbox-cell,
838
+ .responsive.is-expandable:not(.is-selectable):not(.has-sticky-left)
839
+ td.expander-cell,
840
+ .responsive.has-sticky-left td.sticky-left-last {
841
+ border-bottom: var(--gds-sys-space-5xs) solid
842
+ var(--gds-sys-color-border-neutral-02);
843
+ margin-bottom: var(--table-cell-padding-x);
844
+ }
845
+
846
+ /* Trailing sticky-right cells (e.g. actions) sit below the rest of the card. */
847
+ .responsive.has-sticky-right td.sticky-right {
848
+ border-top: var(--gds-sys-space-5xs) solid
849
+ var(--gds-sys-color-border-neutral-02);
850
+ margin-top: var(--table-cell-padding-x);
851
+ }
852
+
853
+ /* Whole cell gets the header-matching highlight on mobile, not just the control. */
854
+ .responsive td.checkbox-cell {
855
+ margin: var(--gds-sys-space-2xs);
856
+ background: var(--_table-header-bg);
857
+ border-radius: var(--gds-sys-radius-s);
858
+ }
859
+
860
+ /* Reset width so the label's flex: 1 pushes the checkbox flush right. */
861
+ .responsive td.checkbox-cell .cell-center {
862
+ width: auto;
863
+ flex: 0 0 auto;
864
+ }
865
+
866
+ /* Cells stack into a card, not a row, so the desktop hover/selected
867
+ edge-rounding (first/last column) doesn't apply here. */
868
+ .responsive tbody tr:hover td:first-child,
869
+ .responsive tbody tr:hover td:last-child,
870
+ .responsive tbody tr.selected td:first-child,
871
+ .responsive tbody tr.selected td:last-child,
872
+ .responsive.striped tbody tr td:first-child,
873
+ .responsive.striped tbody tr td:last-child {
874
+ border-radius: 0;
875
+ }
876
+
877
+ /* The checkbox-cell keeps its own fixed pill radius regardless of hover/selected state. */
878
+ .responsive tbody tr:hover td.checkbox-cell,
879
+ .responsive tbody tr.selected td.checkbox-cell,
880
+ .responsive.striped tbody tr td.checkbox-cell {
881
+ border-radius: var(--gds-sys-radius-s);
820
882
  }
821
883
 
822
884
  .responsive .expand-toggle {
@@ -847,6 +909,23 @@ const TableStyles = css`
847
909
  }
848
910
  }
849
911
 
912
+ /* Narrow screens: stack label above content, except the select column.
913
+ Wrap (not column-direction) so multi-slot content (e.g. value + currency
914
+ badge) stays on one line together, after the full-width label forces a break. */
915
+ @media (max-width: 400px) {
916
+ .responsive td:not(.checkbox-cell) .cell-content {
917
+ flex-wrap: wrap;
918
+ align-items: center;
919
+ row-gap: var(--gds-sys-space-4xs);
920
+ column-gap: var(--gds-sys-space-s);
921
+ }
922
+
923
+ .responsive td:not(.checkbox-cell) .column-label {
924
+ flex: initial;
925
+ width: 100%;
926
+ }
927
+ }
928
+
850
929
  /* Medium screens: input and dropdown side by side */
851
930
  @media (max-width: 760px) and (min-width: 480px) {
852
931
  .header {
@@ -55,6 +55,8 @@ export type RowMeta<T extends Record<string, unknown> = Record<string, unknown>>
55
55
  expandLabel?: string;
56
56
  /** Optional per-row label for expanded state toggle action. */
57
57
  collapseLabel?: string;
58
+ /** Optional per-row label for the row-selection checkbox, replacing the default "Select row N". */
59
+ selectLabel?: string;
58
60
  /**
59
61
  * Async readiness signal for slotted full-width expanded content.
60
62
  * Called when the row is expanded. The table shows a loading skeleton