@jirawatpyk/aura-react 4.15.0 → 4.16.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
@@ -41,13 +41,15 @@ npm i @jirawatpyk/aura-react @jirawatpyk/aura-tokens # React 18 or 19; import
41
41
  npm i @aura/react@npm:@jirawatpyk/aura-react @aura/tokens@npm:@jirawatpyk/aura-tokens
42
42
  ```
43
43
 
44
+ React 18.3 and 19 are both tested: the dev dependencies pin 18, and CI switches the whole workspace to 19 (`node scripts/use-react.mjs 19 && npm install`) and runs every suite again — contrast, token lint, SSR, types, build, hydration with 0 warnings, layout before hydration, the three pilot pages and Storybook (axe + behaviour).
45
+
44
46
  Public on npmjs; internal projects can use GitHub Packages instead ([repository README](https://github.com/Jirawatpyk/Aura-design#use-it-in-a-project)).
45
47
 
46
48
  ```tsx
47
49
  // app root (once)
48
50
  import '@aura/tokens/aura.css'; // tokens (light + dark)
49
51
  import '@aura/react/styles.css'; // component styles
50
- // fonts: Google Fonts <link> tags in <head>, or '@aura/tokens/aura-fonts.css'
52
+ // fonts: '@jirawatpyk/aura-tokens/aura-fonts.local.css' (self-hosted, CSP font-src 'self'), next/font, or Google Fonts — see the tokens README
51
53
 
52
54
  import { AuraProvider, AppShell, SideNav, DataTable, DatePicker } from '@aura/react';
53
55
 
@@ -74,7 +76,7 @@ export default function Root() {
74
76
 
75
77
  ## Components (49)
76
78
 
77
- Actions: Button, IconButton, Menu, DropdownMenu, Tag · Forms: TextField, PasswordField, Textarea, NumberField, Select, RadioGroup, Checkbox, Switch, SegmentedControl, Combobox (one value, or `multiple`), FileUpload (+ `formatBytes`) · Dates & times: DatePicker, DateRangePicker, Calendar, TimePicker (+ `useFormatDate`, `formatDate`, `parseDate`, `parseTime`) · Feedback: Alert, FormErrorSummary, Toaster/`toast()` (+ `.success/.error/.warning/.info/.loading`), Tooltip, StatusPill, Badge, Progress, Skeleton, EmptyState · Overlays: Dialog, Drawer, Popover · Data: DataTable, FilterBar, Stat · Navigation: Command (⌘K palette) · Layout: AppShell, Container, Stack, Grid, Accordion, Pagination, Card, Tabs, Stepper, SideNav, Breadcrumb, Avatar, Surface, Icon · Theme: ColorSchemeToggle, ColorSchemeScript, `useColorScheme`, ThemeStyle/`createTheme` · Hooks: `useBreakpoint`, `useResponsive`, `breakpoints`, `useAuraLocale`, `useDensity`.
79
+ Actions: Button (`ghost`, `size="sm"`), IconButton, Menu, DropdownMenu, Tag · Forms: TextField, PasswordField, Textarea, NumberField, Select, RadioGroup, Checkbox, Switch, SegmentedControl, Combobox (one value, or `multiple`), FileUpload (+ `formatBytes`) · Dates & times: DatePicker, DateRangePicker, Calendar, TimePicker (+ `useFormatDate`, `formatDate`, `parseDate`, `parseTime`) · Feedback: Alert, FormErrorSummary, Toaster/`toast()` (+ `.success/.error/.warning/.info/.loading`), Tooltip, StatusPill, Badge, Progress, Skeleton, EmptyState · Overlays: Dialog, Drawer, Popover · Data: DataTable, FilterBar, Stat · Navigation: Command (⌘K palette) · Layout: AppShell, Container, Stack, Grid, Accordion, Pagination, Card, Tabs, Stepper, SideNav, Breadcrumb, Avatar, Surface, Icon · Theme: ColorSchemeToggle, ColorSchemeScript, `useColorScheme`, ThemeStyle/`createTheme` · Hooks: `useBreakpoint`, `useResponsive`, `breakpoints`, `useAuraLocale`, `useDensity`.
78
80
 
79
81
  ## Router links, Swedish, motion
80
82
 
@@ -123,7 +125,7 @@ toast.error('Could not save'); // danger = role="alert"; the rest
123
125
 
124
126
  ## Phones and touch
125
127
 
126
- Under 640px every text control uses 16px text (iOS Safari doesn't zoom). On touch screens (`pointer: coarse`) IconButton keeps its 32px look with a 44px hit area, and Radio, Checkbox and Switch rows are at least 44px with the whole row as the target. `<Button fullWidth>` fills its row and wraps long Thai or Swedish labels.
128
+ Under 640px every text control uses 16px text (iOS Safari doesn't zoom). On touch screens (`pointer: coarse`) IconButton and `<Button size="sm">` keep their 32px look with a 44px hit area, and Radio, Checkbox and Switch rows are at least 44px with the whole row as the target. `<Button fullWidth>` fills its row and wraps long Thai or Swedish labels.
127
129
 
128
130
  ## Light and dark
129
131
 
@@ -159,6 +161,12 @@ Values are ISO strings (`2026-09-18`); with `locale="th"` display is Thai with B
159
161
 
160
162
  Portals (Dialog, Drawer, menus, pickers, toasts) wait until after hydration; `useBreakpoint()` returns `lg` on the server and corrects on the client through `useSyncExternalStore`, so there are no hydration mismatches. `npm run test:ssr` renders every component with `react-dom/server` (ESM and CJS builds).
161
163
 
164
+ Layout is right before hydration (4.16). AppShell's sidebar-or-drawer switch is CSS (a 1024px media query), so a phone gets the menu button from the server's HTML. A DataTable with `stackBelow` renders cards and grid together until it has measured itself, and a container query shows the right one. It supports `stackBelow` 360, 400, 480, 520, 560, 600, 640, 720, 768, 800, 900, 960 and 1024; other widths stack once JavaScript runs. `npm run test:layout` loads both at 390 and 1280px with JavaScript off, then hydrated, and requires CLS 0. Columns with `hideBelow` still settle after hydration.
165
+
166
+ ## TypeScript
167
+
168
+ Every optional prop takes `undefined` (4.16), so apps on `exactOptionalPropertyTypes` can write `hint={t.hint}` or `icon={x ?? undefined}`. `types-test/exact-optional.tsx` checks every exported component against the published declarations under `strict`, `exactOptionalPropertyTypes` and `noUncheckedIndexedAccess`.
169
+
162
170
  ## Scripts
163
171
 
164
172
  ```bash
@@ -661,7 +661,8 @@ window.Aura = (() => {
661
661
  "disabled",
662
662
  "linkComponent",
663
663
  "href",
664
- "fullWidth"
664
+ "fullWidth",
665
+ "size"
665
666
  ]);
666
667
  const Tag3 = disabled ? "a" : Link;
667
668
  return /* @__PURE__ */ React4.createElement(
@@ -673,7 +674,13 @@ window.Aura = (() => {
673
674
  role: disabled ? "link" : void 0,
674
675
  "aria-disabled": disabled || void 0,
675
676
  tabIndex: disabled ? -1 : props.tabIndex,
676
- className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
677
+ className: cx(
678
+ "aura-btn",
679
+ "aura-btn--" + variant,
680
+ props.size === "sm" && "aura-btn--sm",
681
+ props.fullWidth && "aura-btn--full",
682
+ props.className
683
+ ),
677
684
  onClick: disabled ? void 0 : props.onClick
678
685
  },
679
686
  props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon }) : null,
@@ -699,7 +706,8 @@ window.Aura = (() => {
699
706
  "iconRight",
700
707
  "loading",
701
708
  "onClick",
702
- "fullWidth"
709
+ "fullWidth",
710
+ "size"
703
711
  ]);
704
712
  return /* @__PURE__ */ React4.createElement(
705
713
  "button",
@@ -710,6 +718,7 @@ window.Aura = (() => {
710
718
  className: cx(
711
719
  "aura-btn",
712
720
  "aura-btn--" + variant,
721
+ props.size === "sm" && "aura-btn--sm",
713
722
  props.fullWidth && "aura-btn--full",
714
723
  loading && "is-loading",
715
724
  props.className
@@ -3097,6 +3106,7 @@ window.Aura = (() => {
3097
3106
  { key: "owner", label: "OWNER" }
3098
3107
  ];
3099
3108
  var SKELETON_WIDTHS = ["72%", "56%", "84%", "44%", "64%"];
3109
+ var STACK_WIDTHS = [360, 400, 480, 520, 560, 600, 640, 720, 768, 800, 900, 960, 1024];
3100
3110
  var ROW_H_DEFAULT = 48;
3101
3111
  var OVERSCAN = 8;
3102
3112
  var FLEX_MIN = 160;
@@ -3163,6 +3173,7 @@ window.Aura = (() => {
3163
3173
  const measure = !!props.stackBelow || columns.some(function(c) {
3164
3174
  return c.hideBelow != null;
3165
3175
  });
3176
+ const dual = !!props.stackBelow && boxWidth[0] == null && STACK_WIDTHS.indexOf(props.stackBelow) >= 0;
3166
3177
  React28.useEffect(
3167
3178
  function() {
3168
3179
  if (!measure || !wrapRef.current || typeof ResizeObserver === "undefined") return;
@@ -3174,9 +3185,15 @@ window.Aura = (() => {
3174
3185
  ro.disconnect();
3175
3186
  };
3176
3187
  },
3177
- [measure]
3188
+ [measure, dual]
3178
3189
  );
3179
3190
  const stacked = !!props.stackBelow && boxWidth[0] != null && boxWidth[0] < props.stackBelow;
3191
+ useIsoLayoutEffect(
3192
+ function() {
3193
+ if (measure && boxWidth[0] == null && wrapRef.current) boxWidth[1](wrapRef.current.getBoundingClientRect().width);
3194
+ },
3195
+ [measure]
3196
+ );
3180
3197
  function tooNarrow(c) {
3181
3198
  if (c.hideBelow == null || boxWidth[0] == null || stacked) return false;
3182
3199
  const px = typeof c.hideBelow === "number" ? c.hideBelow : BP[c.hideBelow];
@@ -3988,7 +4005,7 @@ window.Aura = (() => {
3988
4005
  } else if (height && rows.length && !loading) {
3989
4006
  foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React28.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React28.createElement("span", null));
3990
4007
  }
3991
- if (stacked) {
4008
+ function renderStacked(wrapRefArg) {
3992
4009
  const titleCol = vis[0], pillCol = vis.filter(function(c) {
3993
4010
  return c.pill && c !== titleCol;
3994
4011
  })[0];
@@ -4052,7 +4069,7 @@ window.Aura = (() => {
4052
4069
  return /* @__PURE__ */ React28.createElement(
4053
4070
  "div",
4054
4071
  {
4055
- ref: wrapMerged,
4072
+ ref: wrapRefArg,
4056
4073
  "data-density": props.density,
4057
4074
  className: cx("aura-table aura-table--stacked", refreshing && "is-refreshing", props.className),
4058
4075
  role: "region",
@@ -4073,15 +4090,16 @@ window.Aura = (() => {
4073
4090
  foot
4074
4091
  );
4075
4092
  }
4093
+ if (stacked) return renderStacked(wrapMerged);
4076
4094
  const scrollStyle = {
4077
4095
  scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
4078
4096
  scrollPaddingTop: ROW_H + "px"
4079
4097
  };
4080
4098
  if (height) scrollStyle.height = height + "px";
4081
- return /* @__PURE__ */ React28.createElement(
4099
+ const gridEl = /* @__PURE__ */ React28.createElement(
4082
4100
  "div",
4083
4101
  {
4084
- ref: wrapMerged,
4102
+ ref: dual ? void 0 : wrapMerged,
4085
4103
  "data-density": props.density,
4086
4104
  className: cx("aura-table", scrolledX[0] && "is-scrolled-x", refreshing && "is-refreshing", props.className)
4087
4105
  },
@@ -4137,6 +4155,8 @@ window.Aura = (() => {
4137
4155
  }
4138
4156
  ) : null
4139
4157
  );
4158
+ if (!dual) return gridEl;
4159
+ return /* @__PURE__ */ React28.createElement("div", { ref: wrapMerged, className: "aura-table-dual", "data-stack-below": props.stackBelow }, renderStacked(void 0), gridEl);
4140
4160
  });
4141
4161
 
4142
4162
  // src/Card.tsx
@@ -4598,26 +4618,25 @@ window.Aura = (() => {
4598
4618
  var AppShell = React38.forwardRef(function AppShell2(props, ref) {
4599
4619
  const t = useStrings();
4600
4620
  const bp = useBreakpoint();
4601
- const compact = bp === "base" || bp === "sm" || bp === "md";
4621
+ const wide = bp === "lg" || bp === "xl";
4602
4622
  const st = React38.useState(false), open = st[0], setOpen = st[1];
4603
4623
  React38.useEffect(
4604
4624
  function() {
4605
- if (!compact) setOpen(false);
4625
+ if (wide) setOpen(false);
4606
4626
  },
4607
- [compact]
4627
+ [wide]
4608
4628
  );
4609
- const navEl = props.nav;
4610
- const nav = props.nav && React38.isValidElement(props.nav) && compact ? React38.cloneElement(navEl, {
4629
+ const navEl = props.nav && React38.isValidElement(props.nav) ? props.nav : null;
4630
+ const drawerNav = navEl ? React38.cloneElement(navEl, {
4611
4631
  onChange: function(id) {
4612
4632
  if (navEl.props.onChange) navEl.props.onChange(id);
4613
4633
  setOpen(false);
4614
4634
  },
4615
4635
  className: cx(navEl.props.className, "is-in-drawer"),
4616
- /* The drawer is already full-size: never a rail, no collapse button. */
4617
4636
  collapsed: false,
4618
4637
  collapsible: false
4619
4638
  }) : props.nav;
4620
- return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", compact && "is-compact", props.className) }, !compact ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, nav) : null, compact ? /* @__PURE__ */ React38.createElement(
4639
+ return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", props.className) }, props.nav ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, props.nav) : null, props.nav ? /* @__PURE__ */ React38.createElement(
4621
4640
  Drawer,
4622
4641
  {
4623
4642
  open,
@@ -4629,10 +4648,11 @@ window.Aura = (() => {
4629
4648
  "aria-label": props.navLabel || t.navigation,
4630
4649
  dismissible: true
4631
4650
  },
4632
- nav
4633
- ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || compact ? /* @__PURE__ */ React38.createElement("header", { className: "aura-shell__bar" }, compact ? /* @__PURE__ */ React38.createElement(
4651
+ drawerNav
4652
+ ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React38.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React38.createElement(
4634
4653
  IconButton,
4635
4654
  {
4655
+ className: "aura-shell__menu",
4636
4656
  icon: "menu",
4637
4657
  label: props.menuLabel || t.openNav,
4638
4658
  size: "md",
@@ -5668,7 +5688,10 @@ window.Aura = (() => {
5668
5688
  rows
5669
5689
  );
5670
5690
  }
5671
- const style = { width: props.width, height: props.height };
5691
+ const style = {
5692
+ width: props.width,
5693
+ height: props.height
5694
+ };
5672
5695
  if (v === "circle") {
5673
5696
  style.width = style.height = props.size || props.width || 40;
5674
5697
  }
@@ -643,7 +643,8 @@ function ButtonLink(props, ref, Link) {
643
643
  "disabled",
644
644
  "linkComponent",
645
645
  "href",
646
- "fullWidth"
646
+ "fullWidth",
647
+ "size"
647
648
  ]);
648
649
  const Tag3 = disabled ? "a" : Link;
649
650
  return /* @__PURE__ */ React4.createElement(
@@ -655,7 +656,13 @@ function ButtonLink(props, ref, Link) {
655
656
  role: disabled ? "link" : void 0,
656
657
  "aria-disabled": disabled || void 0,
657
658
  tabIndex: disabled ? -1 : props.tabIndex,
658
- className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
659
+ className: cx(
660
+ "aura-btn",
661
+ "aura-btn--" + variant,
662
+ props.size === "sm" && "aura-btn--sm",
663
+ props.fullWidth && "aura-btn--full",
664
+ props.className
665
+ ),
659
666
  onClick: disabled ? void 0 : props.onClick
660
667
  },
661
668
  props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon }) : null,
@@ -681,7 +688,8 @@ var Button = React4.forwardRef(
681
688
  "iconRight",
682
689
  "loading",
683
690
  "onClick",
684
- "fullWidth"
691
+ "fullWidth",
692
+ "size"
685
693
  ]);
686
694
  return /* @__PURE__ */ React4.createElement(
687
695
  "button",
@@ -692,6 +700,7 @@ var Button = React4.forwardRef(
692
700
  className: cx(
693
701
  "aura-btn",
694
702
  "aura-btn--" + variant,
703
+ props.size === "sm" && "aura-btn--sm",
695
704
  props.fullWidth && "aura-btn--full",
696
705
  loading && "is-loading",
697
706
  props.className
@@ -3079,6 +3088,7 @@ var DEFAULT_COLUMNS = [
3079
3088
  { key: "owner", label: "OWNER" }
3080
3089
  ];
3081
3090
  var SKELETON_WIDTHS = ["72%", "56%", "84%", "44%", "64%"];
3091
+ var STACK_WIDTHS = [360, 400, 480, 520, 560, 600, 640, 720, 768, 800, 900, 960, 1024];
3082
3092
  var ROW_H_DEFAULT = 48;
3083
3093
  var OVERSCAN = 8;
3084
3094
  var FLEX_MIN = 160;
@@ -3145,6 +3155,7 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
3145
3155
  const measure = !!props.stackBelow || columns.some(function(c) {
3146
3156
  return c.hideBelow != null;
3147
3157
  });
3158
+ const dual = !!props.stackBelow && boxWidth[0] == null && STACK_WIDTHS.indexOf(props.stackBelow) >= 0;
3148
3159
  React28.useEffect(
3149
3160
  function() {
3150
3161
  if (!measure || !wrapRef.current || typeof ResizeObserver === "undefined") return;
@@ -3156,9 +3167,15 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
3156
3167
  ro.disconnect();
3157
3168
  };
3158
3169
  },
3159
- [measure]
3170
+ [measure, dual]
3160
3171
  );
3161
3172
  const stacked = !!props.stackBelow && boxWidth[0] != null && boxWidth[0] < props.stackBelow;
3173
+ useIsoLayoutEffect(
3174
+ function() {
3175
+ if (measure && boxWidth[0] == null && wrapRef.current) boxWidth[1](wrapRef.current.getBoundingClientRect().width);
3176
+ },
3177
+ [measure]
3178
+ );
3162
3179
  function tooNarrow(c) {
3163
3180
  if (c.hideBelow == null || boxWidth[0] == null || stacked) return false;
3164
3181
  const px = typeof c.hideBelow === "number" ? c.hideBelow : BP[c.hideBelow];
@@ -3970,7 +3987,7 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
3970
3987
  } else if (height && rows.length && !loading) {
3971
3988
  foot = /* @__PURE__ */ React28.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React28.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React28.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React28.createElement("span", null));
3972
3989
  }
3973
- if (stacked) {
3990
+ function renderStacked(wrapRefArg) {
3974
3991
  const titleCol = vis[0], pillCol = vis.filter(function(c) {
3975
3992
  return c.pill && c !== titleCol;
3976
3993
  })[0];
@@ -4034,7 +4051,7 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
4034
4051
  return /* @__PURE__ */ React28.createElement(
4035
4052
  "div",
4036
4053
  {
4037
- ref: wrapMerged,
4054
+ ref: wrapRefArg,
4038
4055
  "data-density": props.density,
4039
4056
  className: cx("aura-table aura-table--stacked", refreshing && "is-refreshing", props.className),
4040
4057
  role: "region",
@@ -4055,15 +4072,16 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
4055
4072
  foot
4056
4073
  );
4057
4074
  }
4075
+ if (stacked) return renderStacked(wrapMerged);
4058
4076
  const scrollStyle = {
4059
4077
  scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
4060
4078
  scrollPaddingTop: ROW_H + "px"
4061
4079
  };
4062
4080
  if (height) scrollStyle.height = height + "px";
4063
- return /* @__PURE__ */ React28.createElement(
4081
+ const gridEl = /* @__PURE__ */ React28.createElement(
4064
4082
  "div",
4065
4083
  {
4066
- ref: wrapMerged,
4084
+ ref: dual ? void 0 : wrapMerged,
4067
4085
  "data-density": props.density,
4068
4086
  className: cx("aura-table", scrolledX[0] && "is-scrolled-x", refreshing && "is-refreshing", props.className)
4069
4087
  },
@@ -4119,6 +4137,8 @@ var DataTable = React28.forwardRef(function DataTable2(props, ref) {
4119
4137
  }
4120
4138
  ) : null
4121
4139
  );
4140
+ if (!dual) return gridEl;
4141
+ return /* @__PURE__ */ React28.createElement("div", { ref: wrapMerged, className: "aura-table-dual", "data-stack-below": props.stackBelow }, renderStacked(void 0), gridEl);
4122
4142
  });
4123
4143
 
4124
4144
  // src/Card.tsx
@@ -4580,26 +4600,25 @@ var React38 = __toESM(require("react"), 1);
4580
4600
  var AppShell = React38.forwardRef(function AppShell2(props, ref) {
4581
4601
  const t = useStrings();
4582
4602
  const bp = useBreakpoint();
4583
- const compact = bp === "base" || bp === "sm" || bp === "md";
4603
+ const wide = bp === "lg" || bp === "xl";
4584
4604
  const st = React38.useState(false), open = st[0], setOpen = st[1];
4585
4605
  React38.useEffect(
4586
4606
  function() {
4587
- if (!compact) setOpen(false);
4607
+ if (wide) setOpen(false);
4588
4608
  },
4589
- [compact]
4609
+ [wide]
4590
4610
  );
4591
- const navEl = props.nav;
4592
- const nav = props.nav && React38.isValidElement(props.nav) && compact ? React38.cloneElement(navEl, {
4611
+ const navEl = props.nav && React38.isValidElement(props.nav) ? props.nav : null;
4612
+ const drawerNav = navEl ? React38.cloneElement(navEl, {
4593
4613
  onChange: function(id) {
4594
4614
  if (navEl.props.onChange) navEl.props.onChange(id);
4595
4615
  setOpen(false);
4596
4616
  },
4597
4617
  className: cx(navEl.props.className, "is-in-drawer"),
4598
- /* The drawer is already full-size: never a rail, no collapse button. */
4599
4618
  collapsed: false,
4600
4619
  collapsible: false
4601
4620
  }) : props.nav;
4602
- return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", compact && "is-compact", props.className) }, !compact ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, nav) : null, compact ? /* @__PURE__ */ React38.createElement(
4621
+ return /* @__PURE__ */ React38.createElement("div", { ref, className: cx("aura-shell", props.className) }, props.nav ? /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__nav" }, props.nav) : null, props.nav ? /* @__PURE__ */ React38.createElement(
4603
4622
  Drawer,
4604
4623
  {
4605
4624
  open,
@@ -4611,10 +4630,11 @@ var AppShell = React38.forwardRef(function AppShell2(props, ref) {
4611
4630
  "aria-label": props.navLabel || t.navigation,
4612
4631
  dismissible: true
4613
4632
  },
4614
- nav
4615
- ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || compact ? /* @__PURE__ */ React38.createElement("header", { className: "aura-shell__bar" }, compact ? /* @__PURE__ */ React38.createElement(
4633
+ drawerNav
4634
+ ) : null, /* @__PURE__ */ React38.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React38.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React38.createElement(
4616
4635
  IconButton,
4617
4636
  {
4637
+ className: "aura-shell__menu",
4618
4638
  icon: "menu",
4619
4639
  label: props.menuLabel || t.openNav,
4620
4640
  size: "md",
@@ -5650,7 +5670,10 @@ var Skeleton = React47.forwardRef(function Skeleton2(props, ref) {
5650
5670
  rows
5651
5671
  );
5652
5672
  }
5653
- const style = { width: props.width, height: props.height };
5673
+ const style = {
5674
+ width: props.width,
5675
+ height: props.height
5676
+ };
5654
5677
  if (v === "circle") {
5655
5678
  style.width = style.height = props.size || props.width || 40;
5656
5679
  }
@@ -8,26 +8,25 @@ import { useBreakpoint } from "./responsive.js";
8
8
  const AppShell = React.forwardRef(function AppShell2(props, ref) {
9
9
  const t = useStrings();
10
10
  const bp = useBreakpoint();
11
- const compact = bp === "base" || bp === "sm" || bp === "md";
11
+ const wide = bp === "lg" || bp === "xl";
12
12
  const st = React.useState(false), open = st[0], setOpen = st[1];
13
13
  React.useEffect(
14
14
  function() {
15
- if (!compact) setOpen(false);
15
+ if (wide) setOpen(false);
16
16
  },
17
- [compact]
17
+ [wide]
18
18
  );
19
- const navEl = props.nav;
20
- const nav = props.nav && React.isValidElement(props.nav) && compact ? React.cloneElement(navEl, {
19
+ const navEl = props.nav && React.isValidElement(props.nav) ? props.nav : null;
20
+ const drawerNav = navEl ? React.cloneElement(navEl, {
21
21
  onChange: function(id) {
22
22
  if (navEl.props.onChange) navEl.props.onChange(id);
23
23
  setOpen(false);
24
24
  },
25
25
  className: cx(navEl.props.className, "is-in-drawer"),
26
- /* The drawer is already full-size: never a rail, no collapse button. */
27
26
  collapsed: false,
28
27
  collapsible: false
29
28
  }) : props.nav;
30
- return /* @__PURE__ */ React.createElement("div", { ref, className: cx("aura-shell", compact && "is-compact", props.className) }, !compact ? /* @__PURE__ */ React.createElement("div", { className: "aura-shell__nav" }, nav) : null, compact ? /* @__PURE__ */ React.createElement(
29
+ return /* @__PURE__ */ React.createElement("div", { ref, className: cx("aura-shell", props.className) }, props.nav ? /* @__PURE__ */ React.createElement("div", { className: "aura-shell__nav" }, props.nav) : null, props.nav ? /* @__PURE__ */ React.createElement(
31
30
  Drawer,
32
31
  {
33
32
  open,
@@ -39,10 +38,11 @@ const AppShell = React.forwardRef(function AppShell2(props, ref) {
39
38
  "aria-label": props.navLabel || t.navigation,
40
39
  dismissible: true
41
40
  },
42
- nav
43
- ) : null, /* @__PURE__ */ React.createElement("div", { className: "aura-shell__main" }, props.header || compact ? /* @__PURE__ */ React.createElement("header", { className: "aura-shell__bar" }, compact ? /* @__PURE__ */ React.createElement(
41
+ drawerNav
42
+ ) : null, /* @__PURE__ */ React.createElement("div", { className: "aura-shell__main" }, props.header || props.nav ? /* @__PURE__ */ React.createElement("header", { className: cx("aura-shell__bar", !props.header && "aura-shell__bar--menu-only") }, props.nav ? /* @__PURE__ */ React.createElement(
44
43
  IconButton,
45
44
  {
45
+ className: "aura-shell__menu",
46
46
  icon: "menu",
47
47
  label: props.menuLabel || t.openNav,
48
48
  size: "md",
@@ -15,7 +15,8 @@ function ButtonLink(props, ref, Link) {
15
15
  "disabled",
16
16
  "linkComponent",
17
17
  "href",
18
- "fullWidth"
18
+ "fullWidth",
19
+ "size"
19
20
  ]);
20
21
  const Tag = disabled ? "a" : Link;
21
22
  return /* @__PURE__ */ React.createElement(
@@ -27,7 +28,13 @@ function ButtonLink(props, ref, Link) {
27
28
  role: disabled ? "link" : void 0,
28
29
  "aria-disabled": disabled || void 0,
29
30
  tabIndex: disabled ? -1 : props.tabIndex,
30
- className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
31
+ className: cx(
32
+ "aura-btn",
33
+ "aura-btn--" + variant,
34
+ props.size === "sm" && "aura-btn--sm",
35
+ props.fullWidth && "aura-btn--full",
36
+ props.className
37
+ ),
31
38
  onClick: disabled ? void 0 : props.onClick
32
39
  },
33
40
  props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon }) : null,
@@ -53,7 +60,8 @@ const Button = React.forwardRef(
53
60
  "iconRight",
54
61
  "loading",
55
62
  "onClick",
56
- "fullWidth"
63
+ "fullWidth",
64
+ "size"
57
65
  ]);
58
66
  return /* @__PURE__ */ React.createElement(
59
67
  "button",
@@ -64,6 +72,7 @@ const Button = React.forwardRef(
64
72
  className: cx(
65
73
  "aura-btn",
66
74
  "aura-btn--" + variant,
75
+ props.size === "sm" && "aura-btn--sm",
67
76
  props.fullWidth && "aura-btn--full",
68
77
  loading && "is-loading",
69
78
  props.className
@@ -15,6 +15,7 @@ const DEFAULT_COLUMNS = [
15
15
  { key: "owner", label: "OWNER" }
16
16
  ];
17
17
  const SKELETON_WIDTHS = ["72%", "56%", "84%", "44%", "64%"];
18
+ const STACK_WIDTHS = [360, 400, 480, 520, 560, 600, 640, 720, 768, 800, 900, 960, 1024];
18
19
  const ROW_H_DEFAULT = 48, OVERSCAN = 8, FLEX_MIN = 160;
19
20
  const DataTable = React.forwardRef(function DataTable2(props, ref) {
20
21
  const t = useStrings();
@@ -79,6 +80,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
79
80
  const measure = !!props.stackBelow || columns.some(function(c) {
80
81
  return c.hideBelow != null;
81
82
  });
83
+ const dual = !!props.stackBelow && boxWidth[0] == null && STACK_WIDTHS.indexOf(props.stackBelow) >= 0;
82
84
  React.useEffect(
83
85
  function() {
84
86
  if (!measure || !wrapRef.current || typeof ResizeObserver === "undefined") return;
@@ -90,9 +92,15 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
90
92
  ro.disconnect();
91
93
  };
92
94
  },
93
- [measure]
95
+ [measure, dual]
94
96
  );
95
97
  const stacked = !!props.stackBelow && boxWidth[0] != null && boxWidth[0] < props.stackBelow;
98
+ useIsoLayoutEffect(
99
+ function() {
100
+ if (measure && boxWidth[0] == null && wrapRef.current) boxWidth[1](wrapRef.current.getBoundingClientRect().width);
101
+ },
102
+ [measure]
103
+ );
96
104
  function tooNarrow(c) {
97
105
  if (c.hideBelow == null || boxWidth[0] == null || stacked) return false;
98
106
  const px = typeof c.hideBelow === "number" ? c.hideBelow : BP[c.hideBelow];
@@ -904,7 +912,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
904
912
  } else if (height && rows.length && !loading) {
905
913
  foot = /* @__PURE__ */ React.createElement("div", { className: "aura-table__foot" }, /* @__PURE__ */ React.createElement("span", null, t.rowCount(shownTotal)), selectable && selected.length ? /* @__PURE__ */ React.createElement("span", null, t.selectedCount(selected.length)) : /* @__PURE__ */ React.createElement("span", null));
906
914
  }
907
- if (stacked) {
915
+ function renderStacked(wrapRefArg) {
908
916
  const titleCol = vis[0], pillCol = vis.filter(function(c) {
909
917
  return c.pill && c !== titleCol;
910
918
  })[0];
@@ -968,7 +976,7 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
968
976
  return /* @__PURE__ */ React.createElement(
969
977
  "div",
970
978
  {
971
- ref: wrapMerged,
979
+ ref: wrapRefArg,
972
980
  "data-density": props.density,
973
981
  className: cx("aura-table aura-table--stacked", refreshing && "is-refreshing", props.className),
974
982
  role: "region",
@@ -989,15 +997,16 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
989
997
  foot
990
998
  );
991
999
  }
1000
+ if (stacked) return renderStacked(wrapMerged);
992
1001
  const scrollStyle = {
993
1002
  scrollPaddingLeft: "calc(var(--aura-space-6)" + selW + " + " + acc + "px)",
994
1003
  scrollPaddingTop: ROW_H + "px"
995
1004
  };
996
1005
  if (height) scrollStyle.height = height + "px";
997
- return /* @__PURE__ */ React.createElement(
1006
+ const gridEl = /* @__PURE__ */ React.createElement(
998
1007
  "div",
999
1008
  {
1000
- ref: wrapMerged,
1009
+ ref: dual ? void 0 : wrapMerged,
1001
1010
  "data-density": props.density,
1002
1011
  className: cx("aura-table", scrolledX[0] && "is-scrolled-x", refreshing && "is-refreshing", props.className)
1003
1012
  },
@@ -1053,6 +1062,8 @@ const DataTable = React.forwardRef(function DataTable2(props, ref) {
1053
1062
  }
1054
1063
  ) : null
1055
1064
  );
1065
+ if (!dual) return gridEl;
1066
+ return /* @__PURE__ */ React.createElement("div", { ref: wrapMerged, className: "aura-table-dual", "data-stack-below": props.stackBelow }, renderStacked(void 0), gridEl);
1056
1067
  });
1057
1068
  export {
1058
1069
  DataTable
@@ -18,7 +18,10 @@ const Skeleton = React.forwardRef(function Skeleton2(props, ref) {
18
18
  rows
19
19
  );
20
20
  }
21
- const style = { width: props.width, height: props.height };
21
+ const style = {
22
+ width: props.width,
23
+ height: props.height
24
+ };
22
25
  if (v === "circle") {
23
26
  style.width = style.height = props.size || props.width || 40;
24
27
  }