@jirawatpyk/aura-react 5.69.0 → 5.70.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.
@@ -8,6 +8,8 @@ import { IconLoaderCircle } from "./icons.js";
8
8
  function ButtonLink(props, ref, Link) {
9
9
  const variant = props.variant || "primary";
10
10
  const disabled = !!props.disabled;
11
+ const ad = props["aria-disabled"];
12
+ const gated = !disabled && (ad === true || ad === "true");
11
13
  const rest = omit(props, [
12
14
  "variant",
13
15
  "className",
@@ -29,7 +31,7 @@ function ButtonLink(props, ref, Link) {
29
31
  ref,
30
32
  href: disabled ? void 0 : props.href,
31
33
  role: disabled ? "link" : void 0,
32
- "aria-disabled": disabled || void 0,
34
+ "aria-disabled": disabled || gated || void 0,
33
35
  tabIndex: disabled ? -1 : props.tabIndex,
34
36
  className: buttonClass({
35
37
  variant,
@@ -38,7 +40,9 @@ function ButtonLink(props, ref, Link) {
38
40
  touchHeight: props.touchHeight,
39
41
  className: props.className
40
42
  }),
41
- onClick: disabled ? void 0 : props.onClick
43
+ onClick: disabled ? void 0 : gated ? function(e) {
44
+ e.preventDefault();
45
+ } : props.onClick
42
46
  },
43
47
  props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon }) : null,
44
48
  props.children,
@@ -56,6 +60,8 @@ const Button = React.forwardRef(
56
60
  const loading = !!props.loading;
57
61
  const ad = props["aria-disabled"];
58
62
  const blocked = loading || ad === true || ad === "true";
63
+ const ab = props["aria-busy"];
64
+ const busy = loading || ab === true || ab === "true";
59
65
  const rest = omit(props, [
60
66
  "variant",
61
67
  "className",
@@ -83,7 +89,7 @@ const Button = React.forwardRef(
83
89
  loading,
84
90
  className: props.className
85
91
  }),
86
- "aria-busy": loading || void 0,
92
+ "aria-busy": busy || void 0,
87
93
  "aria-disabled": blocked || void 0,
88
94
  onClick: blocked ? function(e) {
89
95
  e.preventDefault();
@@ -5,8 +5,10 @@ import { useStrings } from "./locale.js";
5
5
  import { Icon } from "./Icon.js";
6
6
  import { IconButton } from "./IconButton.js";
7
7
  import { Field } from "./Field.js";
8
+ import { Button } from "./Button.js";
9
+ import { Progress } from "./Progress.js";
8
10
  import { formatBytes } from "./text.js";
9
- import { IconCircleAlert, IconCircleCheck, IconCloudUpload, IconFile, IconImage, IconX } from "./icons.js";
11
+ import { IconCircleAlert, IconCircleCheck, IconCloudUpload, IconFile, IconImage, IconUpload, IconX } from "./icons.js";
10
12
  function matches(file, accept) {
11
13
  if (!accept) return true;
12
14
  const name = (file.name || "").toLowerCase(), type = (file.type || "").toLowerCase();
@@ -40,6 +42,22 @@ const FileUpload = React.forwardRef(function FileUpload2(props, ref) {
40
42
  const urls = React.useRef({});
41
43
  const maxFiles = props.multiple ? props.maxFiles : 1;
42
44
  const note = t.accepts(describeAccept(props.accept, t), props.maxSize ? formatBytes(props.maxSize) : "");
45
+ const pickState = React.useState(null);
46
+ const buttonRef = React.useRef(null);
47
+ const buttonMode = props.variant === "button";
48
+ React.useEffect(
49
+ function() {
50
+ const el = inputRef.current;
51
+ if (!buttonMode || !el) return;
52
+ el.focus = function(o) {
53
+ if (buttonRef.current) buttonRef.current.focus(o);
54
+ };
55
+ return function() {
56
+ delete el.focus;
57
+ };
58
+ },
59
+ [buttonMode]
60
+ );
43
61
  React.useEffect(function() {
44
62
  return function() {
45
63
  Object.keys(urls.current).forEach(function(k) {
@@ -107,6 +125,95 @@ const FileUpload = React.forwardRef(function FileUpload2(props, ref) {
107
125
  if (inputRef.current) inputRef.current.focus();
108
126
  }
109
127
  const error = props.error;
128
+ if (props.variant === "button") {
129
+ const err = error || pickState[0];
130
+ const desc = [err ? id + "-error" : props.hint ? id + "-hint" : null, note ? id + "-note" : null].filter(Boolean).join(" ") || void 0;
131
+ return /* @__PURE__ */ React.createElement(
132
+ Field,
133
+ {
134
+ id,
135
+ label: props.label,
136
+ labelAs: "span",
137
+ labelId: id + "-label",
138
+ hint: props.hint,
139
+ error: err,
140
+ required: props.required,
141
+ optional: props.optional,
142
+ disabled: props.disabled,
143
+ className: cx("aura-upload-field", props.className)
144
+ },
145
+ /* @__PURE__ */ React.createElement("div", { className: "aura-upload-button" }, /* @__PURE__ */ React.createElement(
146
+ Button,
147
+ {
148
+ ref: buttonRef,
149
+ id: id + "-button",
150
+ variant: "secondary",
151
+ icon: /* @__PURE__ */ React.createElement(IconUpload, null),
152
+ touchHeight: props.touchHeight,
153
+ disabled: props.disabled,
154
+ "aria-labelledby": props.label ? id + "-label " + id + "-button" : void 0,
155
+ "aria-describedby": desc,
156
+ onClick: function() {
157
+ if (inputRef.current) inputRef.current.click();
158
+ }
159
+ },
160
+ props.buttonLabel != null ? props.buttonLabel : props.multiple ? t.browse : t.browseOne
161
+ ), /* @__PURE__ */ React.createElement(
162
+ "input",
163
+ {
164
+ ref: inputMerged,
165
+ id,
166
+ type: "file",
167
+ className: "aura-upload__input",
168
+ tabIndex: -1,
169
+ "aria-hidden": true,
170
+ accept: props.accept,
171
+ multiple: !!props.multiple,
172
+ disabled: props.disabled,
173
+ name: props.name,
174
+ required: props.name ? props.required : void 0,
175
+ onInvalid: function(e) {
176
+ e.preventDefault();
177
+ pickState[1](props.multiple ? t.browse : t.browseOne);
178
+ if (buttonRef.current) buttonRef.current.focus();
179
+ },
180
+ onChange: function(e) {
181
+ const files = Array.prototype.slice.call(e.target.files || []);
182
+ if (!files.length) return;
183
+ let refused = null;
184
+ const ok = files.filter(function(f) {
185
+ const why = !matches(f, props.accept) ? t.fileWrongType : props.maxSize && f.size > props.maxSize ? t.fileTooBig(formatBytes(props.maxSize)) : null;
186
+ if (why && !refused) refused = why;
187
+ return !why;
188
+ });
189
+ pickState[1](refused);
190
+ if (!props.name) e.target.value = "";
191
+ else if (refused) {
192
+ try {
193
+ const dt = new DataTransfer();
194
+ ok.forEach(function(f) {
195
+ dt.items.add(f);
196
+ });
197
+ e.target.files = dt.files;
198
+ } catch (x) {
199
+ e.target.value = "";
200
+ }
201
+ }
202
+ if (ok.length && props.onSelect) props.onSelect(ok);
203
+ }
204
+ }
205
+ ), note ? /* @__PURE__ */ React.createElement("span", { className: "aura-upload__note", id: id + "-note" }, note) : null),
206
+ props.progress !== void 0 ? /* @__PURE__ */ React.createElement(
207
+ Progress,
208
+ {
209
+ className: "aura-upload-button__progress",
210
+ size: "sm",
211
+ value: props.progress == null ? void 0 : props.progress,
212
+ label: t.uploading
213
+ }
214
+ ) : null
215
+ );
216
+ }
110
217
  return /* @__PURE__ */ React.createElement(
111
218
  Field,
112
219
  {
@@ -7,6 +7,8 @@ const IconButton = React.forwardRef(function IconButton2(props, ref) {
7
7
  const rest = omit(props, ["icon", "label", "className", "size", "tone", "touchHeight", "variant", "pressed"]);
8
8
  useAutoTip();
9
9
  const own = props.title !== void 0;
10
+ const ad = props["aria-disabled"];
11
+ const blocked = ad === true || ad === "true";
10
12
  return /* @__PURE__ */ React.createElement(
11
13
  "button",
12
14
  {
@@ -16,6 +18,9 @@ const IconButton = React.forwardRef(function IconButton2(props, ref) {
16
18
  "aria-label": props.label,
17
19
  "aria-pressed": props.pressed != null ? props.pressed : props["aria-pressed"],
18
20
  "data-aura-tip": own ? void 0 : props.label,
21
+ onClick: blocked ? function(e) {
22
+ e.preventDefault();
23
+ } : props.onClick,
19
24
  className: cx(
20
25
  "aura-icon-btn",
21
26
  props.tone === "danger" && "aura-icon-btn--danger",
@@ -25,9 +25,56 @@ function pageList(page, count, sib) {
25
25
  if (count > 1) out.push(count);
26
26
  return out;
27
27
  }
28
- const Pagination = React.forwardRef(function Pagination2(props, ref) {
28
+ function cursorNav(props, ref, Link, t) {
29
+ const links = props.firstHref != null || props.previousHref != null || props.nextHref != null;
30
+ function step(key, href, on, has, label, rel, icon, after) {
31
+ const body = /* @__PURE__ */ React.createElement(React.Fragment, null, icon && !after ? /* @__PURE__ */ React.createElement(Icon, { name: icon, size: "sm" }) : null, /* @__PURE__ */ React.createElement("span", null, label), icon && after ? /* @__PURE__ */ React.createElement(Icon, { name: icon, size: "sm" }) : null);
32
+ const cls = "aura-page aura-page--step";
33
+ if (has === false || !href && !on)
34
+ return links ? /* @__PURE__ */ React.createElement("span", { key, className: cls, role: "link", "aria-disabled": true }, body) : /* @__PURE__ */ React.createElement("button", { key, type: "button", className: cls, disabled: true }, body);
35
+ if (href != null)
36
+ return /* @__PURE__ */ React.createElement(
37
+ Link,
38
+ {
39
+ key,
40
+ href,
41
+ rel,
42
+ className: cls,
43
+ onClick: function(e) {
44
+ if (on && plainClick(e)) {
45
+ e.preventDefault();
46
+ on();
47
+ }
48
+ }
49
+ },
50
+ body
51
+ );
52
+ return /* @__PURE__ */ React.createElement("button", { key, type: "button", className: cls, onClick: on }, body);
53
+ }
54
+ return /* @__PURE__ */ React.createElement(
55
+ "nav",
56
+ {
57
+ ref,
58
+ className: cx("aura-pagination", "aura-pagination--cursor", props.className),
59
+ "aria-label": props.label || t.pagination
60
+ },
61
+ props.status != null && props.status !== false && props.status !== "" ? /* @__PURE__ */ React.createElement("span", { className: "aura-pagination__status" }, props.status) : null,
62
+ /* @__PURE__ */ React.createElement("span", { className: "aura-pagination__steps" }, props.showFirst === false ? null : step("first", props.firstHref, props.onFirst, props.hasFirst, t.firstPage, void 0, null, false), step(
63
+ "prev",
64
+ props.previousHref,
65
+ props.onPrevious,
66
+ props.hasPrevious,
67
+ t.prevPage,
68
+ "prev",
69
+ /* @__PURE__ */ React.createElement(IconChevronLeft, null),
70
+ false
71
+ ), step("next", props.nextHref, props.onNext, props.hasNext, t.nextPage, "next", /* @__PURE__ */ React.createElement(IconChevronRight, null), true))
72
+ );
73
+ }
74
+ const Pagination = React.forwardRef(function Pagination2(all, ref) {
29
75
  const t = useStrings();
30
76
  useAutoTip();
77
+ const props = all;
31
78
  const count = Math.max(1, props.pageCount || 1);
32
79
  const st = useMaybeControlled(props.page, props.defaultPage || 1, props.onChange);
33
80
  const page = Math.min(count, Math.max(1, st[0]));
@@ -36,6 +83,7 @@ const Pagination = React.forwardRef(function Pagination2(props, ref) {
36
83
  }
37
84
  const link = props.getHref;
38
85
  const Link = useLinkComponent(props.linkComponent);
86
+ if (all.mode === "cursor") return cursorNav(all, ref, Link, t);
39
87
  function item(p, label, extra) {
40
88
  const common = Object.assign(
41
89
  {
@@ -0,0 +1,9 @@
1
+ 'use client';
2
+ import * as React from "react";
3
+ import { spinnerElement } from "./display.js";
4
+ const Spinner = React.forwardRef(function Spinner2(props, ref) {
5
+ return spinnerElement(props, ref);
6
+ });
7
+ export {
8
+ Spinner
9
+ };
package/dist/esm/Table.js CHANGED
@@ -1,7 +1,9 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
3
  import { cx, devWarnOnce, uid, useIsoLayoutEffect } from "./internal.js";
4
- import { useDensity } from "./locale.js";
4
+ import { useDensity, useLinkComponent } from "./locale.js";
5
+ import { Icon } from "./Icon.js";
6
+ import { IconArrowDown, IconArrowUp, IconArrowUpDown } from "./icons.js";
5
7
  import { inertProps, skeletonBody, skeletonColgroup } from "./TableSkeleton.js";
6
8
  const StackLabels = React.createContext(null);
7
9
  const ColumnIndex = React.createContext(-1);
@@ -242,8 +244,39 @@ function cellClass(base, p) {
242
244
  );
243
245
  }
244
246
  const Th = React.forwardRef(function Th2(props, ref) {
245
- const { align, numeric, mono, className, scope, label, card, skeleton, skeletonWidth, wrap, ...rest } = props;
247
+ const {
248
+ align,
249
+ numeric,
250
+ mono,
251
+ className,
252
+ scope,
253
+ label,
254
+ card,
255
+ skeleton,
256
+ skeletonWidth,
257
+ wrap,
258
+ sort,
259
+ onSort,
260
+ sortHref,
261
+ linkComponent,
262
+ ...rest
263
+ } = props;
246
264
  const stacked = React.useContext(StackLabels) != null;
265
+ const Link = useLinkComponent(linkComponent);
266
+ const sortable = !!onSort || sortHref != null;
267
+ const ariaSort = sort || (sortable ? "none" : void 0);
268
+ let children = props.children;
269
+ if (sortable) {
270
+ const icon = /* @__PURE__ */ React.createElement(
271
+ Icon,
272
+ {
273
+ name: sort === "ascending" ? /* @__PURE__ */ React.createElement(IconArrowUp, null) : sort === "descending" ? /* @__PURE__ */ React.createElement(IconArrowDown, null) : /* @__PURE__ */ React.createElement(IconArrowUpDown, null),
274
+ size: 12
275
+ }
276
+ );
277
+ const cls = cx("aura-table__sort", sort && sort !== "none" && "is-active");
278
+ children = sortHref != null ? /* @__PURE__ */ React.createElement(Link, { href: sortHref, className: cls }, props.children, icon) : /* @__PURE__ */ React.createElement("button", { type: "button", className: cls, onClick: onSort }, props.children, icon);
279
+ }
247
280
  return /* @__PURE__ */ React.createElement(
248
281
  "th",
249
282
  {
@@ -252,8 +285,10 @@ const Th = React.forwardRef(function Th2(props, ref) {
252
285
  className: cellClass("aura-tbl__th", props),
253
286
  role: stacked ? scope === "row" ? "rowheader" : "columnheader" : void 0,
254
287
  "data-card": stacked && (card === "title" || card === "action") ? card : void 0,
288
+ "aria-sort": ariaSort,
255
289
  ...rest
256
- }
290
+ },
291
+ children
257
292
  );
258
293
  });
259
294
  const Td = React.forwardRef(function Td2(props, ref) {
@@ -11,6 +11,7 @@ import {
11
11
  IconCircleDotDashed,
12
12
  IconInbox,
13
13
  IconInfo,
14
+ IconLoaderCircle,
14
15
  IconMinus,
15
16
  IconTrendingDown,
16
17
  IconTrendingUp,
@@ -101,7 +102,7 @@ const PILL_ICON = {
101
102
  };
102
103
  function statusPillElement(props, ref) {
103
104
  const tone = props.tone || toneFor(props.children);
104
- const rest = omit(props, ["tone", "className", "children", "wrap"]);
105
+ const rest = omit(props, ["tone", "className", "children", "wrap", "busy"]);
105
106
  return /* @__PURE__ */ React.createElement(
106
107
  "span",
107
108
  {
@@ -109,10 +110,26 @@ function statusPillElement(props, ref) {
109
110
  ref,
110
111
  className: cx("aura-pill", "aura-pill--" + tone, props.wrap && "is-wrap", props.className)
111
112
  },
112
- /* @__PURE__ */ React.createElement(Icon, { name: h(PILL_ICON[tone] || IconCircle), size: 12 }),
113
+ props.busy ? /* @__PURE__ */ React.createElement(Icon, { name: h(IconLoaderCircle), size: 12, className: "aura-spin" }) : /* @__PURE__ */ React.createElement(Icon, { name: h(PILL_ICON[tone] || IconCircle), size: 12 }),
113
114
  props.wrap ? /* @__PURE__ */ React.createElement("span", { className: "aura-pill__text" }, props.children) : props.children
114
115
  );
115
116
  }
117
+ function spinnerElement(props, ref) {
118
+ const rest = omit(props, ["size", "label", "className"]);
119
+ const label = props.label;
120
+ return /* @__PURE__ */ React.createElement(
121
+ "span",
122
+ {
123
+ ...rest,
124
+ ref,
125
+ className: cx("aura-spinner", "aura-spinner--" + (props.size || "md"), props.className),
126
+ role: label ? "status" : void 0,
127
+ "aria-hidden": label ? void 0 : true
128
+ },
129
+ /* @__PURE__ */ React.createElement(Icon, { name: h(IconLoaderCircle), size: props.size || "md", className: "aura-spin" }),
130
+ label ? /* @__PURE__ */ React.createElement("span", { className: "aura-sr-only" }, label) : null
131
+ );
132
+ }
116
133
  function badgeElement(props, ref) {
117
134
  const rest = omit(props, ["tone", "variant", "icon", "className", "children"]);
118
135
  return /* @__PURE__ */ React.createElement(
@@ -273,6 +290,9 @@ function ServerCard(props) {
273
290
  function ServerStatusPill(props) {
274
291
  return statusPillElement(props, void 0);
275
292
  }
293
+ function ServerSpinner(props) {
294
+ return spinnerElement(props, void 0);
295
+ }
276
296
  function ServerBadge(props) {
277
297
  return badgeElement(props, void 0);
278
298
  }
@@ -292,6 +312,7 @@ export {
292
312
  ServerBadge,
293
313
  ServerCard,
294
314
  ServerEmptyState,
315
+ ServerSpinner,
295
316
  ServerStat,
296
317
  ServerStatusPill,
297
318
  alertElement,
@@ -301,6 +322,7 @@ export {
301
322
  cardElement,
302
323
  emptyStateElement,
303
324
  linkClass,
325
+ spinnerElement,
304
326
  statElement,
305
327
  statusPillElement
306
328
  };
package/dist/esm/index.js CHANGED
@@ -60,6 +60,7 @@ import { Progress } from "./Progress.js";
60
60
  import { Skeleton } from "./Skeleton.js";
61
61
  import { EmptyState } from "./EmptyState.js";
62
62
  import { Pagination } from "./Pagination.js";
63
+ import { Spinner } from "./Spinner.js";
63
64
  import { Accordion } from "./Accordion.js";
64
65
  import { Popover } from "./Popover.js";
65
66
  import { NumberField } from "./NumberField.js";
@@ -117,6 +118,7 @@ export {
117
118
  SideNav,
118
119
  Skeleton,
119
120
  Sparkline,
121
+ Spinner,
120
122
  Stack,
121
123
  Stat,
122
124
  StatusPill,
@@ -134,6 +134,7 @@ const STRINGS = {
134
134
  statusUnknown: "No data",
135
135
  opensInNewTab: "opens in a new tab",
136
136
  required: "required",
137
+ firstPage: "First page",
137
138
  filtersApplied: function(n2) {
138
139
  return "Filters, " + n2.toLocaleString("en") + " applied";
139
140
  },
@@ -130,6 +130,7 @@ const sv = {
130
130
  statusUnknown: "Ingen data",
131
131
  opensInNewTab: "\xF6ppnas i en ny flik",
132
132
  required: "obligatoriskt",
133
+ firstPage: "F\xF6rsta sidan",
133
134
  filtersApplied: function(n) {
134
135
  return "Filter, " + n.toLocaleString("sv-SE") + " aktiva";
135
136
  },
@@ -130,6 +130,7 @@ const th = {
130
130
  statusUnknown: "\u0E22\u0E31\u0E07\u0E44\u0E21\u0E48\u0E21\u0E35\u0E02\u0E49\u0E2D\u0E21\u0E39\u0E25",
131
131
  opensInNewTab: "\u0E40\u0E1B\u0E34\u0E14\u0E43\u0E19\u0E41\u0E17\u0E47\u0E1A\u0E43\u0E2B\u0E21\u0E48",
132
132
  required: "\u0E08\u0E33\u0E40\u0E1B\u0E47\u0E19",
133
+ firstPage: "\u0E2B\u0E19\u0E49\u0E32\u0E41\u0E23\u0E01",
133
134
  filtersApplied: function(n2) {
134
135
  return "\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07, \u0E43\u0E0A\u0E49\u0E2D\u0E22\u0E39\u0E48 " + n2.toLocaleString("en") + " \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
135
136
  },
package/dist/index.d.cts CHANGED
@@ -332,10 +332,24 @@ export interface StatusPillProps extends Omit<React$1.HTMLAttributes<HTMLSpanEle
332
332
  /** 5.51 (Chamber-OS 158): in a box narrower than the words, the text wraps inside the pill (still rounded, the icon
333
333
  * on the first line) instead of running out of it. Without it a pill stays on one line. */
334
334
  wrap?: boolean | undefined;
335
+ /** 5.70 (Chamber-OS 189): the spinner in the icon slot, for a state still in progress ("Waiting for payment").
336
+ * The words still say it; the tone is unchanged. */
337
+ busy?: boolean | undefined;
335
338
  /** Force a tone. Default: matched from the word (Ready/Done → ready, In Progress/In Review → progress, Warning/Problem/Degraded/At risk → warning (5.1), Blocked/Failed → blocked, anything else → neutral). */
336
339
  tone?: StatusTone | undefined;
337
340
  className?: string | undefined;
338
341
  }
342
+ /** 5.70 (Chamber-OS 189): Button's loading spinner on its own, for "working" outside a button. */
343
+ export interface SpinnerProps extends Omit<React$1.HTMLAttributes<HTMLSpanElement>, "children"> {
344
+ /** `sm` 16px (Button's spinner) · `md` 20px (default) · `lg` 24px — the icon sizes. It takes the text colour. */
345
+ size?: "sm" | "md" | "lg" | undefined;
346
+ /** Without it the spinner is `aria-hidden`: the text beside it, or your live region, says what is happening. With it
347
+ * the spinner is `role="status"` with this text (visually hidden), for a spinner standing alone: it names it for
348
+ * browse mode, but a status inserted with its text already in it is often not announced — to announce the change,
349
+ * keep a live region on the page and put the words in it. */
350
+ label?: string | undefined;
351
+ className?: string | undefined;
352
+ }
339
353
  /** 16px checkbox with 4px corners; ink when checked, a dash when indeterminate. */
340
354
  export interface CheckboxProps extends Omit<React$1.InputHTMLAttributes<HTMLInputElement>, "onChange" | "type" | "checked" | "defaultChecked"> {
341
355
  /** Controlled state; pair with onChange. Or use defaultChecked. */
@@ -1497,6 +1511,16 @@ export interface TableCellProps extends Omit<React$1.TdHTMLAttributes<HTMLTableC
1497
1511
  /** Th in the head only (5.52): this column's width while loading — a share such as `'22%'` — when the loaded table
1498
1512
  * sizes it from its content. Default: the cell's `width`, else an even share. */
1499
1513
  skeletonWidth?: string | number | undefined;
1514
+ /** Th only (5.70, Chamber-OS 188): this column's order — `aria-sort` on the header. With `onSort` or `sortHref` the
1515
+ * label becomes a button or link holding AURA's sort icon (up, down, or both ways for `none`), as DataTable's
1516
+ * sortable header; alone, it only says the order. */
1517
+ sort?: "ascending" | "descending" | "none" | undefined;
1518
+ /** Th only (5.70): the header becomes a button that calls this — flip the order yourself. */
1519
+ onSort?: (() => void) | undefined;
1520
+ /** Th only (5.70): the header becomes a link (through `linkComponent`) to this, for a server-sorted page. */
1521
+ sortHref?: string | undefined;
1522
+ /** Th only (5.70): your router's link for `sortHref`; defaults to AuraProvider's `linkComponent`, then `<a>`. */
1523
+ linkComponent?: React$1.ElementType | undefined;
1500
1524
  className?: string | undefined;
1501
1525
  }
1502
1526
  export interface ActionBarProps {
@@ -1668,6 +1692,22 @@ export interface FileUploadProps extends FieldProps {
1668
1692
  name?: string | undefined;
1669
1693
  disabled?: boolean | undefined;
1670
1694
  className?: string | undefined;
1695
+ /** 5.70 (Chamber-OS 190): `dropzone` (default) is the drop area with its file list. `button` is a Button that opens
1696
+ * the picker, for a file the app sends itself (a logo, a certificate): it checks `accept` and `maxSize` with the same
1697
+ * messages, calls `onSelect` with the accepted files and keeps no list (`value` / `onChange` are not used). The
1698
+ * label, hint and error are Field's, as before; the Button is named by the label and its own text. */
1699
+ variant?: "dropzone" | "button" | undefined;
1700
+ /** `variant="button"`: the accepted files of a pick (none is passed when every file was refused; the error shows).
1701
+ * With a `name` the input posts only the accepted files, and `required` is checked by the browser on submit. The
1702
+ * forwarded ref is the input, but its `focus()` moves to the Button. */
1703
+ onSelect?: ((files: File[]) => void) | undefined;
1704
+ /** `variant="button"`: the Button's text. Default "Choose a file" ("Choose files" with `multiple`). */
1705
+ buttonLabel?: React$1.ReactNode | undefined;
1706
+ /** `variant="button"`: an upload in progress under the Button — 0–100, or `null` for indeterminate; leave it out
1707
+ * when nothing is uploading. */
1708
+ progress?: number | null | undefined;
1709
+ /** `variant="button"`: Button's `touchHeight` (44px below 640px or on a coarse pointer). */
1710
+ touchHeight?: boolean | undefined;
1671
1711
  }
1672
1712
  export type Tone = "neutral" | "accent" | "success" | "warning" | "danger";
1673
1713
  export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
@@ -1749,7 +1789,37 @@ export interface EmptyStateProps extends Omit<React$1.HTMLAttributes<HTMLDivElem
1749
1789
  headingLevel?: 2 | 3 | 4 | 5 | 6 | false | undefined;
1750
1790
  className?: string | undefined;
1751
1791
  }
1792
+ /** 5.70 (Chamber-OS 187): keyset / cursor lists — no page count, only whether there is a first, previous and next
1793
+ * page. Pass an href (rendered through `linkComponent`) or a callback for each end; one with neither, or with its
1794
+ * `has…` false, is shown disabled (out of the tab order). */
1795
+ export interface CursorPaginationProps {
1796
+ mode: "cursor";
1797
+ firstHref?: string | undefined;
1798
+ previousHref?: string | undefined;
1799
+ nextHref?: string | undefined;
1800
+ /** Run instead of following the href on a plain click, or as a button's action without one. */
1801
+ onFirst?: (() => void) | undefined;
1802
+ onPrevious?: (() => void) | undefined;
1803
+ onNext?: (() => void) | undefined;
1804
+ /** `false` disables that end even with an href or callback (default: enabled when it has one). */
1805
+ hasFirst?: boolean | undefined;
1806
+ hasPrevious?: boolean | undefined;
1807
+ hasNext?: boolean | undefined;
1808
+ /** Default true. `false` leaves the First link out (a list that only goes forward). */
1809
+ showFirst?: boolean | undefined;
1810
+ /** A line at the start, e.g. "Showing 50 of 1,240". Not a live region. */
1811
+ status?: React$1.ReactNode | undefined;
1812
+ /** Name of the nav. Default "Pagination". */
1813
+ label?: string | undefined;
1814
+ linkComponent?: React$1.ElementType | undefined;
1815
+ className?: string | undefined;
1816
+ }
1817
+ /** Either kind of Pagination's props (5.70). `PaginationProps` stays the numbered one, so wrappers that extend or
1818
+ * pick from it keep compiling. */
1819
+ export type AnyPaginationProps = PaginationProps | CursorPaginationProps;
1752
1820
  export interface PaginationProps {
1821
+ /** `pages` (default): numbered pages. `cursor` (5.70): see CursorPaginationProps. */
1822
+ mode?: "pages" | undefined;
1753
1823
  pageCount: number;
1754
1824
  /** 1-based, controlled; pair with onChange. Or defaultPage. */
1755
1825
  page?: number | undefined;
@@ -2287,6 +2357,8 @@ export interface AuraStrings {
2287
2357
  opensInNewTab: string;
2288
2358
  /** 5.67: read after a CheckboxGroup's legend when `required` (its `*` is hidden from screen readers). */
2289
2359
  required: string;
2360
+ /** 5.70: a cursor Pagination's link back to the start. */
2361
+ firstPage: string;
2290
2362
  /** 5.42: FilterBar's collapsed-filters button, read with its count ("Filters, 2 applied"). */
2291
2363
  filtersApplied: (n: number) => string;
2292
2364
  /** 5.42: closes FilterBar's "Filters" sheet. */
@@ -2412,7 +2484,8 @@ export declare const Tag: React$1.ForwardRefExoticComponent<TagProps & React$1.R
2412
2484
  export declare const Progress: React$1.ForwardRefExoticComponent<ProgressProps & React$1.RefAttributes<HTMLDivElement>>;
2413
2485
  export declare const Skeleton: React$1.ForwardRefExoticComponent<SkeletonProps & React$1.RefAttributes<HTMLSpanElement>>;
2414
2486
  export declare const EmptyState: React$1.ForwardRefExoticComponent<EmptyStateProps & React$1.RefAttributes<HTMLDivElement>>;
2415
- export declare const Pagination: React$1.ForwardRefExoticComponent<PaginationProps & React$1.RefAttributes<HTMLElement>>;
2487
+ export declare const Pagination: React$1.ForwardRefExoticComponent<AnyPaginationProps & React$1.RefAttributes<HTMLElement>>;
2488
+ export declare const Spinner: React$1.ForwardRefExoticComponent<SpinnerProps & React$1.RefAttributes<HTMLSpanElement>>;
2416
2489
  export declare const Accordion: React$1.ForwardRefExoticComponent<AccordionProps & React$1.RefAttributes<HTMLDivElement>>;
2417
2490
  export declare const Popover: React$1.ForwardRefExoticComponent<PopoverProps & React$1.RefAttributes<HTMLDivElement>>;
2418
2491
  /** Number input with thousands separators, +/− buttons and arrow keys (ARIA spinbutton). */