@jirawatpyk/aura-react 5.21.0 → 5.23.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.
package/README.md CHANGED
@@ -186,6 +186,18 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
186
186
  - **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
187
187
  - **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
188
188
 
189
+ ## 5.23 — Chamber-OS addenda 23–25
190
+
191
+ - **DataTable `card: 'wide'`** (item 120): in a stacked card the column is a field on a line of its own at the card's full inner width, after the half-width fields (and before a `footer`), its label above and its text wrapping — a reason with its evidence line. The grid is unchanged (an ordinary column at its `width`).
192
+ - **Underline Tabs show their whole 2px indicator** (item 121): the list scrolls sideways, and a scroll container clips at its padding box, so the indicator pulled 1px over the list's border showed only 1px. The track is now an inset shadow inside the list and the indicator paints over its bottom row; the tabs' size is unchanged. In forced colours the track is a border again, with the indicator just above it (the list is 1px taller there).
193
+ - **ActionBar `touchHeight`** (item 122): the bar's own Clear button takes Button's `touchHeight` — 44px below 640px — to match action buttons that use it.
194
+
195
+ ## 5.22 — Chamber-OS addendum 22
196
+
197
+ - **DataTable `card: 'footer'`** (item 118): in a stacked card the column becomes the card's last row, full width, after every other cell — row actions such as "Send reminder" and a menu. A Button placed directly in the cell grows to fill the row; an IconButton or menu keeps its size. The grid is unchanged (an ordinary column at its `width`).
198
+ - **Stacked titles wrap** (item 119): a card's title (`card: 'title'` or the automatic one) wraps onto more lines instead of being cut, on a 20px line, and the row box, the pill and top-right actions line up with its first line. Visible in every stacked DataTable: long titles wrap; one-line cards without a pill or actions are 4px taller (the 20px line), with a pill about 1px; the grid is unchanged.
199
+ - `card: 'footer'` moves the cell only visually: arrow keys and screen readers keep the grid's column order. A footer holding only an IconButton or a menu sits at the end edge.
200
+
189
201
  ## 5.21 — Chamber-OS addendum 21
190
202
 
191
203
  - **Table `rowHeight="density"`** (item 117): every body row is at least the density's row height (48px; 40px compact; 48px on touch screens, as DataTable — unlike the 5.20 cell padding, which stays compact on touch), and its cells' vertical padding shrinks to fit a small Button (7.5px at 48px, 3.5px compact, at most 8px), as in DataTable's `rowHeight="auto"` — so a row with a `sm` Button, an IconButton, a pill or one line of text are all the same height. Content is centred on the row unless you pass `align="top"` (footer cells centre too, as with `align="middle"`). Rows whose text wraps still grow, with the smaller padding. Header and footer row heights, stacked rows and cards (`stackBelow`), and tables without the prop are unchanged.
@@ -5298,12 +5298,13 @@ window.Aura = (() => {
5298
5298
  const part = cardPart(c, i), lv = hideLevel(c);
5299
5299
  return {
5300
5300
  "data-card": part,
5301
- "data-label": part === "field" ? c.label || void 0 : void 0,
5301
+ "data-label": part === "field" || part === "wide" ? c.label || void 0 : void 0,
5302
5302
  "data-hide": lv ? String(lv) : void 0
5303
5303
  };
5304
5304
  }
5305
5305
  const hasFields = vis.some(function(c, i) {
5306
- return cardPart(c, i) === "field";
5306
+ const p = cardPart(c, i);
5307
+ return p === "field" || p === "wide";
5307
5308
  });
5308
5309
  const hasFlex = vis.some(function(c) {
5309
5310
  return widthOf(c) == null;
@@ -7198,7 +7199,7 @@ window.Aura = (() => {
7198
7199
  props.className
7199
7200
  )
7200
7201
  },
7201
- /* @__PURE__ */ React42.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React42.createElement(Button, { variant: "ghost", size: "sm", onClick: clear }, t.clear()) : null, props.children) : null)
7202
+ /* @__PURE__ */ React42.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React42.createElement(Button, { variant: "ghost", size: "sm", touchHeight: props.touchHeight, onClick: clear }, t.clear()) : null, props.children) : null)
7202
7203
  );
7203
7204
  });
7204
7205
 
@@ -5280,12 +5280,13 @@ var DataTableImpl = React31.forwardRef(function DataTable(props, ref) {
5280
5280
  const part = cardPart(c, i), lv = hideLevel(c);
5281
5281
  return {
5282
5282
  "data-card": part,
5283
- "data-label": part === "field" ? c.label || void 0 : void 0,
5283
+ "data-label": part === "field" || part === "wide" ? c.label || void 0 : void 0,
5284
5284
  "data-hide": lv ? String(lv) : void 0
5285
5285
  };
5286
5286
  }
5287
5287
  const hasFields = vis.some(function(c, i) {
5288
- return cardPart(c, i) === "field";
5288
+ const p = cardPart(c, i);
5289
+ return p === "field" || p === "wide";
5289
5290
  });
5290
5291
  const hasFlex = vis.some(function(c) {
5291
5292
  return widthOf(c) == null;
@@ -7180,7 +7181,7 @@ var ActionBar = React42.forwardRef(function ActionBar2(props, ref) {
7180
7181
  props.className
7181
7182
  )
7182
7183
  },
7183
- /* @__PURE__ */ React42.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React42.createElement(Button, { variant: "ghost", size: "sm", onClick: clear }, t.clear()) : null, props.children) : null)
7184
+ /* @__PURE__ */ React42.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React42.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React42.createElement(Button, { variant: "ghost", size: "sm", touchHeight: props.touchHeight, onClick: clear }, t.clear()) : null, props.children) : null)
7184
7185
  );
7185
7186
  });
7186
7187
 
@@ -42,7 +42,7 @@ const ActionBar = React.forwardRef(function ActionBar2(props, ref) {
42
42
  props.className
43
43
  )
44
44
  },
45
- /* @__PURE__ */ React.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React.createElement(Button, { variant: "ghost", size: "sm", onClick: clear }, t.clear()) : null, props.children) : null)
45
+ /* @__PURE__ */ React.createElement("div", { className: cx("aura-actionbar__inner", hasStart && "has-start") }, /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__status", role: "status" }, count, count && props.status ? " \xB7 " : null, props.status), !idle && hasStart ? /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__start" }, props.start) : null, !idle ? /* @__PURE__ */ React.createElement("div", { className: "aura-actionbar__actions" }, count && props.onClearSelection ? /* @__PURE__ */ React.createElement(Button, { variant: "ghost", size: "sm", touchHeight: props.touchHeight, onClick: clear }, t.clear()) : null, props.children) : null)
46
46
  );
47
47
  });
48
48
  export {
@@ -299,12 +299,13 @@ const DataTableImpl = React.forwardRef(function DataTable(props, ref) {
299
299
  const part = cardPart(c, i), lv = hideLevel(c);
300
300
  return {
301
301
  "data-card": part,
302
- "data-label": part === "field" ? c.label || void 0 : void 0,
302
+ "data-label": part === "field" || part === "wide" ? c.label || void 0 : void 0,
303
303
  "data-hide": lv ? String(lv) : void 0
304
304
  };
305
305
  }
306
306
  const hasFields = vis.some(function(c, i) {
307
- return cardPart(c, i) === "field";
307
+ const p = cardPart(c, i);
308
+ return p === "field" || p === "wide";
308
309
  });
309
310
  const hasFlex = vis.some(function(c) {
310
311
  return widthOf(c) == null;
package/dist/index.d.cts CHANGED
@@ -106,8 +106,12 @@ export interface DataTableColumn<Row extends Record<string, any> = Record<string
106
106
  actions?: boolean | undefined;
107
107
  /** Its place in a stacked card (5.13): `'hide'` leaves it out of the card (the grid keeps it), `'title'` / `'pill'`
108
108
  * make it the card's title or the pill beside it (instead of the first column / first pill column), `'field'` a
109
- * label/value field. Default: today's rule. */
110
- card?: "hide" | "field" | "title" | "pill" | undefined;
109
+ * label/value field. `'footer'` (5.22, Chamber-OS 118) makes it the card's last row, full width — row actions
110
+ * such as "Send reminder" and a menu: a Button placed directly in the cell grows to fill the row, an IconButton or
111
+ * menu keeps its size. `'wide'` (5.23, Chamber-OS 120) is a field on a line of its own at the card's full width,
112
+ * after the half-width fields (in column order; `cardOrder` doesn't move it), its label above and its text wrapping —
113
+ * a reason with its evidence. Default: today's rule. The grid is unchanged by any of them. */
114
+ card?: "hide" | "field" | "title" | "pill" | "footer" | "wide" | undefined;
111
115
  /** The order of fields in a stacked card (5.13), lowest first; the grid's column order is unchanged. Fields
112
116
  * without one sort by their column index (0-based). Screen readers and arrow keys follow the grid's order. */
113
117
  cardOrder?: number | undefined;
@@ -1199,6 +1203,9 @@ export interface ActionBarProps {
1199
1203
  selected?: number | undefined;
1200
1204
  /** Adds a "Clear" button next to the count. */
1201
1205
  onClearSelection?: (() => void) | undefined;
1206
+ /** 5.23 (Chamber-OS 122): the bar's own Clear button takes Button's `touchHeight` — 44px below 640px — to match
1207
+ * action buttons that use it. */
1208
+ touchHeight?: boolean | undefined;
1202
1209
  /** Name of the region. Default "Actions". */
1203
1210
  label?: string | undefined;
1204
1211
  className?: string | undefined;
package/dist/index.d.ts CHANGED
@@ -106,8 +106,12 @@ export interface DataTableColumn<Row extends Record<string, any> = Record<string
106
106
  actions?: boolean | undefined;
107
107
  /** Its place in a stacked card (5.13): `'hide'` leaves it out of the card (the grid keeps it), `'title'` / `'pill'`
108
108
  * make it the card's title or the pill beside it (instead of the first column / first pill column), `'field'` a
109
- * label/value field. Default: today's rule. */
110
- card?: "hide" | "field" | "title" | "pill" | undefined;
109
+ * label/value field. `'footer'` (5.22, Chamber-OS 118) makes it the card's last row, full width — row actions
110
+ * such as "Send reminder" and a menu: a Button placed directly in the cell grows to fill the row, an IconButton or
111
+ * menu keeps its size. `'wide'` (5.23, Chamber-OS 120) is a field on a line of its own at the card's full width,
112
+ * after the half-width fields (in column order; `cardOrder` doesn't move it), its label above and its text wrapping —
113
+ * a reason with its evidence. Default: today's rule. The grid is unchanged by any of them. */
114
+ card?: "hide" | "field" | "title" | "pill" | "footer" | "wide" | undefined;
111
115
  /** The order of fields in a stacked card (5.13), lowest first; the grid's column order is unchanged. Fields
112
116
  * without one sort by their column index (0-based). Screen readers and arrow keys follow the grid's order. */
113
117
  cardOrder?: number | undefined;
@@ -1199,6 +1203,9 @@ export interface ActionBarProps {
1199
1203
  selected?: number | undefined;
1200
1204
  /** Adds a "Clear" button next to the count. */
1201
1205
  onClearSelection?: (() => void) | undefined;
1206
+ /** 5.23 (Chamber-OS 122): the bar's own Clear button takes Button's `touchHeight` — 44px below 640px — to match
1207
+ * action buttons that use it. */
1208
+ touchHeight?: boolean | undefined;
1202
1209
  /** Name of the region. Default "Actions". */
1203
1210
  label?: string | undefined;
1204
1211
  className?: string | undefined;
package/dist/styles.css CHANGED
@@ -669,10 +669,13 @@ a.aura-icon-btn { text-decoration: none; }
669
669
 
670
670
  /* ---------- Tabs ---------- */
671
671
  .aura-tabs { font-family: var(--font-sans); }
672
- .aura-tabs__list { display: flex; gap: var(--aura-space-6); border-bottom: 1px solid var(--aura-border-default); overflow-x: auto; scrollbar-width: none; }
672
+ /* 5.23 (Chamber-OS 121): the 1px track is an inset shadow inside the list's padding box, and the active tab's 2px
673
+ * indicator paints over its bottom row. The list scrolls sideways (overflow-x), and a scroll container clips at its
674
+ * padding box: an indicator pulled 1px over a border (as up to 5.22) showed only half its height. Same box height. */
675
+ .aura-tabs__list { display: flex; gap: var(--aura-space-6); box-shadow: inset 0 -1px 0 var(--aura-border-default); overflow-x: auto; scrollbar-width: none; }
673
676
  .aura-tab {
674
677
  position: relative; display: inline-flex; align-items: center; gap: var(--aura-space-2); flex: none;
675
- height: var(--aura-input-height); padding: 0; margin-bottom: -1px;
678
+ height: var(--aura-input-height); padding: 0;
676
679
  border: 0; border-bottom: 2px solid transparent; background: transparent;
677
680
  color: var(--aura-fg-secondary); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
678
681
  transition: color var(--aura-duration-fast) var(--aura-ease), border-color var(--aura-duration-fast) var(--aura-ease);
@@ -1335,9 +1338,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1335
1338
  * ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
1336
1339
  * full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
1337
1340
  * wider tab gap doesn't apply. */
1338
- .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
1341
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; box-shadow: none; }
1339
1342
  .aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
1340
- .aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
1341
1343
  /* 5.15 (Chamber-OS 90): underline tabs sharing the width, always or below a breakpoint; the segmented track below one. */
1342
1344
  .aura-tabs__list.is-fill { gap: 0; }
1343
1345
  .aura-tabs__list.is-fill > .aura-tab { flex: 1 1 0; min-width: max-content; justify-content: center; padding: 0 var(--aura-space-3); }
@@ -1854,9 +1856,31 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1854
1856
  .aura-table__td[data-card='title'] { order: 1; flex: 1 1 0; font-size: 14px; font-weight: 600; }
1855
1857
  .aura-table__td[data-card='pill'] { order: 2; flex: none; }
1856
1858
  .aura-table__td[data-card='actions'] { order: 3; flex: none; margin: calc(-1 * var(--aura-space-1)) calc(-1 * var(--aura-space-2)) calc(-1 * var(--aura-space-1)) 0; }
1859
+ /* 5.22 (Chamber-OS 119): the title wraps rather than being cut — a member's full name before a one-click action —
1860
+ * and the row box and the pill line up with its first line (a 20px line; the 16px box 2px down). */
1861
+ .aura-table__td[data-card='title'] { white-space: normal; overflow-wrap: anywhere; overflow: visible; line-height: 20px; align-self: flex-start; }
1862
+ .aura-table__td[data-card='pill'] { align-self: flex-start; line-height: 20px; }
1863
+ .aura-table__row > .aura-table__sel { align-self: flex-start; margin-top: 2px; }
1864
+ /* Actions top-right sit on that first line too (a 32px button centred on 20px, the same 24px footprint as before). */
1865
+ .aura-table__td[data-card='actions'] { align-self: flex-start; margin-top: calc(-1 * var(--aura-space-1) - 2px); margin-bottom: -2px; }
1866
+ /* 5.23 (Chamber-OS 120): card: 'wide' — a field on its own full-width line after the half-width ones, wrapping. */
1867
+ .aura-table__td[data-card='wide'] { order: 90; flex: 0 0 100%; white-space: normal; overflow: visible; overflow-wrap: anywhere; }
1868
+ /* 5.22 (Chamber-OS 118): card: 'footer' — the card's last row, full width; a Button in it grows, an IconButton or a
1869
+ * menu keeps its size. */
1870
+ .aura-table__td[data-card='footer'] {
1871
+ order: 99; flex: 0 0 100%; display: flex; align-items: center; gap: var(--aura-space-2);
1872
+ margin: var(--aura-space-1) 0 0; overflow: visible; white-space: normal;
1873
+ }
1874
+ .aura-table__td[data-card='footer'] > .aura-table__cell { display: flex; flex: 1 1 auto; align-items: center; gap: var(--aura-space-2); min-width: 0; }
1875
+ .aura-table__td[data-card='footer'] > .aura-btn, .aura-table__td[data-card='footer'] > .aura-table__cell > .aura-btn { flex: 1 1 auto; }
1876
+ /* Only an IconButton or a menu: at the end, as a card's ⋯ usually is. A totals row's label isn't shown there. */
1877
+ .aura-table__td[data-card='footer']:not(:has(.aura-btn)), .aura-table__td[data-card='footer'] > .aura-table__cell:not(:has(.aura-btn)) { justify-content: flex-end; }
1878
+ .aura-table__td[data-card='footer']::before { display: none; }
1857
1879
  /* Spacing: 12px under the title line, 8px between field lines (row gap 4 + the break's 4 + each field's 4 below). */
1858
1880
  .aura-table__break { display: block; order: 4; flex: 0 0 100%; height: var(--aura-space-1); }
1859
1881
  .aura-table__row:has(> .aura-table__break) { padding-bottom: calc(var(--aura-space-4) - var(--aura-space-1)); }
1882
+ /* 5.22: a footer row ends the card with no margin under it, so the card keeps its full 16px. */
1883
+ .aura-table__row:has(> [data-card='footer']:not(.is-blank)) { padding-bottom: var(--aura-space-4); }
1860
1884
  .aura-table__row--skeleton .aura-table__td { display: flex; }
1861
1885
  .aura-table__total { background: var(--aura-bg-canvas); }
1862
1886
  .aura-table__total > .is-blank { display: none; }
@@ -1881,6 +1905,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1881
1905
  .aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
1882
1906
  .aura-tab { border-bottom-color: Canvas; }
1883
1907
  .aura-tab.is-active { border-bottom-color: Highlight; }
1908
+ /* Shadows are dropped in forced colours: the track is a real border again, the indicator just above it. */
1909
+ .aura-tabs__list:not(.aura-segmented) { border-bottom: 1px solid CanvasText; }
1884
1910
  /* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
1885
1911
  * and the selected one never look alike (manual activation moves focus without selecting). */
1886
1912
  .aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
@@ -671,10 +671,13 @@ a.aura-icon-btn { text-decoration: none; }
671
671
 
672
672
  /* ---------- Tabs ---------- */
673
673
  .aura-tabs { font-family: var(--font-sans); }
674
- .aura-tabs__list { display: flex; gap: var(--aura-space-6); border-bottom: 1px solid var(--aura-border-default); overflow-x: auto; scrollbar-width: none; }
674
+ /* 5.23 (Chamber-OS 121): the 1px track is an inset shadow inside the list's padding box, and the active tab's 2px
675
+ * indicator paints over its bottom row. The list scrolls sideways (overflow-x), and a scroll container clips at its
676
+ * padding box: an indicator pulled 1px over a border (as up to 5.22) showed only half its height. Same box height. */
677
+ .aura-tabs__list { display: flex; gap: var(--aura-space-6); box-shadow: inset 0 -1px 0 var(--aura-border-default); overflow-x: auto; scrollbar-width: none; }
675
678
  .aura-tab {
676
679
  position: relative; display: inline-flex; align-items: center; gap: var(--aura-space-2); flex: none;
677
- height: var(--aura-input-height); padding: 0; margin-bottom: -1px;
680
+ height: var(--aura-input-height); padding: 0;
678
681
  border: 0; border-bottom: 2px solid transparent; background: transparent;
679
682
  color: var(--aura-fg-secondary); font: inherit; font-size: 13px; font-weight: 500; cursor: pointer;
680
683
  transition: color var(--aura-duration-fast) var(--aura-ease), border-color var(--aura-duration-fast) var(--aura-ease);
@@ -1337,9 +1340,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1337
1340
  * ring is drawn inside the pill, so neither the scrolling list (many tabs on a phone) nor the next pill can cover it;
1338
1341
  * full-width tabs share the track but never shrink below their label (the track scrolls instead). The phone rule's
1339
1342
  * wider tab gap doesn't apply. */
1340
- .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; }
1343
+ .aura-tabs--segmented .aura-tabs__list.aura-segmented { gap: 2px; box-shadow: none; }
1341
1344
  .aura-tabs--segmented .aura-tabs__list.aura-segmented.is-full .aura-tab { min-width: max-content; }
1342
- .aura-tabs--segmented .aura-tab.aura-segmented__option { margin-bottom: 0; }
1343
1345
  /* 5.15 (Chamber-OS 90): underline tabs sharing the width, always or below a breakpoint; the segmented track below one. */
1344
1346
  .aura-tabs__list.is-fill { gap: 0; }
1345
1347
  .aura-tabs__list.is-fill > .aura-tab { flex: 1 1 0; min-width: max-content; justify-content: center; padding: 0 var(--aura-space-3); }
@@ -1856,9 +1858,31 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1856
1858
  .aura-table__td[data-card='title'] { order: 1; flex: 1 1 0; font-size: 14px; font-weight: 600; }
1857
1859
  .aura-table__td[data-card='pill'] { order: 2; flex: none; }
1858
1860
  .aura-table__td[data-card='actions'] { order: 3; flex: none; margin: calc(-1 * var(--aura-space-1)) calc(-1 * var(--aura-space-2)) calc(-1 * var(--aura-space-1)) 0; }
1861
+ /* 5.22 (Chamber-OS 119): the title wraps rather than being cut — a member's full name before a one-click action —
1862
+ * and the row box and the pill line up with its first line (a 20px line; the 16px box 2px down). */
1863
+ .aura-table__td[data-card='title'] { white-space: normal; overflow-wrap: anywhere; overflow: visible; line-height: 20px; align-self: flex-start; }
1864
+ .aura-table__td[data-card='pill'] { align-self: flex-start; line-height: 20px; }
1865
+ .aura-table__row > .aura-table__sel { align-self: flex-start; margin-top: 2px; }
1866
+ /* Actions top-right sit on that first line too (a 32px button centred on 20px, the same 24px footprint as before). */
1867
+ .aura-table__td[data-card='actions'] { align-self: flex-start; margin-top: calc(-1 * var(--aura-space-1) - 2px); margin-bottom: -2px; }
1868
+ /* 5.23 (Chamber-OS 120): card: 'wide' — a field on its own full-width line after the half-width ones, wrapping. */
1869
+ .aura-table__td[data-card='wide'] { order: 90; flex: 0 0 100%; white-space: normal; overflow: visible; overflow-wrap: anywhere; }
1870
+ /* 5.22 (Chamber-OS 118): card: 'footer' — the card's last row, full width; a Button in it grows, an IconButton or a
1871
+ * menu keeps its size. */
1872
+ .aura-table__td[data-card='footer'] {
1873
+ order: 99; flex: 0 0 100%; display: flex; align-items: center; gap: var(--aura-space-2);
1874
+ margin: var(--aura-space-1) 0 0; overflow: visible; white-space: normal;
1875
+ }
1876
+ .aura-table__td[data-card='footer'] > .aura-table__cell { display: flex; flex: 1 1 auto; align-items: center; gap: var(--aura-space-2); min-width: 0; }
1877
+ .aura-table__td[data-card='footer'] > .aura-btn, .aura-table__td[data-card='footer'] > .aura-table__cell > .aura-btn { flex: 1 1 auto; }
1878
+ /* Only an IconButton or a menu: at the end, as a card's ⋯ usually is. A totals row's label isn't shown there. */
1879
+ .aura-table__td[data-card='footer']:not(:has(.aura-btn)), .aura-table__td[data-card='footer'] > .aura-table__cell:not(:has(.aura-btn)) { justify-content: flex-end; }
1880
+ .aura-table__td[data-card='footer']::before { display: none; }
1859
1881
  /* Spacing: 12px under the title line, 8px between field lines (row gap 4 + the break's 4 + each field's 4 below). */
1860
1882
  .aura-table__break { display: block; order: 4; flex: 0 0 100%; height: var(--aura-space-1); }
1861
1883
  .aura-table__row:has(> .aura-table__break) { padding-bottom: calc(var(--aura-space-4) - var(--aura-space-1)); }
1884
+ /* 5.22: a footer row ends the card with no margin under it, so the card keeps its full 16px. */
1885
+ .aura-table__row:has(> [data-card='footer']:not(.is-blank)) { padding-bottom: var(--aura-space-4); }
1862
1886
  .aura-table__row--skeleton .aura-table__td { display: flex; }
1863
1887
  .aura-table__total { background: var(--aura-bg-canvas); }
1864
1888
  .aura-table__total > .is-blank { display: none; }
@@ -1883,6 +1907,8 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1883
1907
  .aura-segmented__option.is-selected { outline: 2px solid Highlight; outline-offset: -2px; }
1884
1908
  .aura-tab { border-bottom-color: Canvas; }
1885
1909
  .aura-tab.is-active { border-bottom-color: Highlight; }
1910
+ /* Shadows are dropped in forced colours: the track is a real border again, the indicator just above it. */
1911
+ .aura-tabs__list:not(.aura-segmented) { border-bottom: 1px solid CanvasText; }
1886
1912
  /* 5.10: segmented tabs mark the selection with a border and focus with a dashed ring inside it, so a focused tab
1887
1913
  * and the selected one never look alike (manual activation moves focus without selecting). */
1888
1914
  .aura-tabs--segmented .aura-tab.aura-segmented__option.is-selected { outline: none; border: 2px solid Highlight; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.21.0",
3
+ "version": "5.23.0",
4
4
  "description": "AURA Design System — React components (Enterprise Standard, Human Creative). Thai-first: Buddhist-era dates, Thai collation.",
5
5
  "license": "MIT",
6
6
  "type": "module",