@jirawatpyk/aura-react 5.13.0 → 5.15.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.
@@ -31,11 +31,13 @@ var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: tru
31
31
  var server_exports = {};
32
32
  __export(server_exports, {
33
33
  Alert: () => ServerAlert,
34
+ Avatar: () => ServerAvatar,
34
35
  Badge: () => ServerBadge,
35
36
  Card: () => ServerCard,
36
37
  EmptyState: () => ServerEmptyState,
37
38
  Icon: () => Icon,
38
39
  STRINGS: () => STRINGS,
40
+ Stat: () => ServerStat,
39
41
  StatusPill: () => ServerStatusPill,
40
42
  brandScale: () => scale,
41
43
  breakpoints: () => breakpoints,
@@ -325,6 +327,10 @@ var th = {
325
327
  pageN: function(p) {
326
328
  return "\u0E2B\u0E19\u0E49\u0E32 " + p;
327
329
  },
330
+ progressReserved: function(v, r, max) {
331
+ return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
332
+ },
333
+ breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
328
334
  accepts: function(list, max) {
329
335
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
330
336
  }
@@ -451,6 +457,10 @@ var sv = {
451
457
  pageN: function(p) {
452
458
  return "Sida " + p;
453
459
  },
460
+ progressReserved: function(v, r, max) {
461
+ return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
462
+ },
463
+ breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
454
464
  accepts: function(list, max) {
455
465
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
456
466
  }
@@ -578,6 +588,10 @@ var STRINGS = {
578
588
  pageN: function(p) {
579
589
  return "Page " + p;
580
590
  },
591
+ progressReserved: function(v, r, max) {
592
+ return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
593
+ },
594
+ breadcrumbMore: "Show the full path",
581
595
  accepts: function(list, max) {
582
596
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
583
597
  }
@@ -1015,6 +1029,7 @@ var Icon = React2.forwardRef(function Icon2(props, ref) {
1015
1029
  var iconNames = Object.keys(map);
1016
1030
 
1017
1031
  // src/icons.ts
1032
+ var IconMinus = /* @__PURE__ */ defineIcon("minus", [["path", { d: "M5 12h14" }]]);
1018
1033
  var IconCircleAlert = /* @__PURE__ */ defineIcon("circle-alert", [
1019
1034
  ["circle", { cx: "12", cy: "12", r: "10" }],
1020
1035
  ["line", { x1: "12", x2: "12", y1: "8", y2: "12" }],
@@ -1057,6 +1072,14 @@ var IconInbox = /* @__PURE__ */ defineIcon("inbox", [
1057
1072
  { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" }
1058
1073
  ]
1059
1074
  ]);
1075
+ var IconTrendingUp = /* @__PURE__ */ defineIcon("trending-up", [
1076
+ ["path", { d: "M16 7h6v6" }],
1077
+ ["path", { d: "m22 7-8.5 8.5-5-5L2 17" }]
1078
+ ]);
1079
+ var IconTrendingDown = /* @__PURE__ */ defineIcon("trending-down", [
1080
+ ["path", { d: "M16 17h6v-6" }],
1081
+ ["path", { d: "m22 17-8.5-8.5-5 5L2 7" }]
1082
+ ]);
1060
1083
 
1061
1084
  // src/display.tsx
1062
1085
  var h2 = React3.createElement;
@@ -1095,8 +1118,11 @@ function cardElement(props, ref) {
1095
1118
  "interactive",
1096
1119
  "as",
1097
1120
  "className",
1098
- "titleId"
1121
+ "titleId",
1122
+ "header",
1123
+ "flushBelow"
1099
1124
  ]);
1125
+ const head = props.header !== void 0 && props.header !== null && props.header !== false;
1100
1126
  return h2(
1101
1127
  props.as || "section",
1102
1128
  Object.assign({}, rest, {
@@ -1105,12 +1131,13 @@ function cardElement(props, ref) {
1105
1131
  "aura-card",
1106
1132
  creative && "aura-card--creative",
1107
1133
  props.interactive && "is-interactive",
1134
+ props.flushBelow && "aura-card--flush-below-" + props.flushBelow,
1108
1135
  props.className
1109
1136
  ),
1110
1137
  /* A titled card is labelled by its title; otherwise the caller's aria-labelledby stays (5.8). */
1111
- "aria-labelledby": props.title && props.titleId ? props.titleId : props["aria-labelledby"]
1138
+ "aria-labelledby": !head && props.title && props.titleId ? props.titleId : props["aria-labelledby"]
1112
1139
  }),
1113
- props.title || props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__head" }, /* @__PURE__ */ React3.createElement("div", { className: "aura-card__heading" }, props.title ? h2("h" + (props.headingLevel || 3), { className: "aura-card__title", id: props.titleId }, props.title) : null, props.description ? /* @__PURE__ */ React3.createElement("p", { className: "aura-card__desc" }, props.description) : null), props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__actions" }, props.actions) : null) : null,
1140
+ head || props.title || props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__head" }, /* @__PURE__ */ React3.createElement("div", { className: "aura-card__heading" }, head ? props.header : null, !head && props.title ? h2("h" + (props.headingLevel || 3), { className: "aura-card__title", id: props.titleId }, props.title) : null, !head && props.description ? /* @__PURE__ */ React3.createElement("p", { className: "aura-card__desc" }, props.description) : null), props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__actions" }, props.actions) : null) : null,
1114
1141
  props.children ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__body" }, props.children) : null,
1115
1142
  props.footer ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__foot" }, props.footer) : null
1116
1143
  );
@@ -1147,7 +1174,7 @@ function badgeElement(props, ref) {
1147
1174
  );
1148
1175
  }
1149
1176
  function emptyStateElement(props, ref) {
1150
- const HT = "h" + (props.headingLevel || 3);
1177
+ const HT = props.headingLevel === false ? "p" : "h" + (props.headingLevel || 3);
1151
1178
  const rest = omit(props, [
1152
1179
  "title",
1153
1180
  "description",
@@ -1179,12 +1206,73 @@ function emptyStateElement(props, ref) {
1179
1206
  props.action ? /* @__PURE__ */ React3.createElement("div", { className: "aura-empty__action" }, props.action) : null
1180
1207
  );
1181
1208
  }
1209
+ function statElement(props, ref, Link) {
1210
+ const ch = props.change;
1211
+ const dir = ch && (ch.direction || "flat");
1212
+ const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
1213
+ const v = props.value;
1214
+ const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
1215
+ const Tag = props.href ? Link : props.onClick ? "button" : "div";
1216
+ const interactive = !!(props.href || props.onClick);
1217
+ if (props.headingLevel && Tag === "button")
1218
+ devWarnOnce(
1219
+ "stat-heading-button",
1220
+ "Stat `headingLevel` is ignored with `onClick`: a heading can\u2019t sit in a button. Use `href`, or put the heading outside."
1221
+ );
1222
+ const LabelTag = props.headingLevel && Tag !== "button" ? "h" + props.headingLevel : "span";
1223
+ const HeadTag = LabelTag === "span" ? "span" : "div";
1224
+ return /* @__PURE__ */ React3.createElement(
1225
+ Tag,
1226
+ {
1227
+ ref,
1228
+ className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
1229
+ href: props.href,
1230
+ onClick: props.onClick,
1231
+ type: Tag === "button" ? "button" : void 0,
1232
+ "aria-busy": props.loading || void 0
1233
+ },
1234
+ /* @__PURE__ */ React3.createElement(HeadTag, { className: "aura-stat__head" }, /* @__PURE__ */ React3.createElement(LabelTag, { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React3.createElement(Icon, { name: props.icon })) : null),
1235
+ props.loading ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React3.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React3.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
1236
+ ch && !props.loading || props.caption ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React3.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React3.createElement(
1237
+ Icon,
1238
+ {
1239
+ name: dir === "up" ? /* @__PURE__ */ React3.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React3.createElement(IconTrendingDown, null) : /* @__PURE__ */ React3.createElement(IconMinus, null),
1240
+ size: 14
1241
+ }
1242
+ ), /* @__PURE__ */ React3.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
1243
+ );
1244
+ }
1245
+ var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
1246
+ function initials(name) {
1247
+ const parts = String(name || "?").trim().split(/\s+/);
1248
+ return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
1249
+ }
1250
+ function avatarElement(props, ref, broken, onError) {
1251
+ const size = props.size || "md";
1252
+ let hash = 0;
1253
+ String(props.name || "").split("").forEach(function(ch) {
1254
+ hash = hash * 31 + ch.charCodeAt(0) >>> 0;
1255
+ });
1256
+ const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
1257
+ return /* @__PURE__ */ React3.createElement(
1258
+ "span",
1259
+ {
1260
+ ref,
1261
+ className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
1262
+ role: "img",
1263
+ "aria-label": props.name + (props.status ? ", " + props.status : "")
1264
+ },
1265
+ props.src && broken !== props.src ? /* @__PURE__ */ React3.createElement("img", { key: props.src, src: props.src, alt: "", onError }) : /* @__PURE__ */ React3.createElement("span", { "aria-hidden": true }, initials(props.name)),
1266
+ props.status === "online" ? /* @__PURE__ */ React3.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
1267
+ );
1268
+ }
1182
1269
  function buttonClass(opts = {}) {
1183
1270
  return cx(
1184
1271
  "aura-btn",
1185
1272
  "aura-btn--" + (opts.variant || "primary"),
1186
1273
  opts.size === "sm" && "aura-btn--sm",
1187
1274
  opts.fullWidth && "aura-btn--full",
1275
+ opts.touchHeight && "aura-btn--touch",
1188
1276
  opts.loading && "is-loading",
1189
1277
  opts.className
1190
1278
  );
@@ -1204,3 +1292,9 @@ function ServerBadge(props) {
1204
1292
  function ServerEmptyState(props) {
1205
1293
  return emptyStateElement(props, void 0);
1206
1294
  }
1295
+ function ServerStat(props) {
1296
+ return statElement(omit(props, ["onClick"]), void 0, props.linkComponent || "a");
1297
+ }
1298
+ function ServerAvatar(props) {
1299
+ return avatarElement(props, void 0, null, void 0);
1300
+ }
@@ -20,6 +20,9 @@ export interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElem
20
20
  fullWidth?: boolean | undefined;
21
21
  /** `sm` is 32px with tighter padding, for toolbars, table rows and card footers. Default `md` (44px). */
22
22
  size?: ButtonSize | undefined;
23
+ /** 44px tall below 640px (the viewport's), whatever `size` — a small button that phones get at full touch height
24
+ * (5.15, Chamber-OS 100). From 640px up it keeps its size. */
25
+ touchHeight?: boolean | undefined;
23
26
  }
24
27
  /** An AURA icon name, or any icon element (e.g. `<Building />` from lucide-react). AURA sizes it and hides it from screen readers. */
25
28
  export type IconInput = IconName | React$1.ReactElement;
@@ -80,6 +83,21 @@ export interface CardProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "ti
80
83
  className?: string | undefined;
81
84
  /** id for the title element; the card is then labelled by it (aria-labelledby). */
82
85
  titleId?: string | undefined;
86
+ /** Free head content in place of `title` and `description` (5.15, Chamber-OS 87): skeleton bars while the card
87
+ * loads, a status pill above a title you mark up yourself. No heading is added; `actions` still sit top-right. */
88
+ header?: React$1.ReactNode | undefined;
89
+ /** Below this width (the viewport's) the card drops its border, surface and shadow — a list whose rows become
90
+ * cards of their own on a phone, so there are no cards in a card (5.15, Chamber-OS 93). Padding is kept. */
91
+ flushBelow?: "sm" | "md" | "lg" | undefined;
92
+ }
93
+ export interface AvatarProps {
94
+ /** Accessible name; also gives the initials and a stable colour. */
95
+ name: string;
96
+ src?: string | undefined;
97
+ /** `sm` 24 · `md` 32 (default) · `lg` 40. */
98
+ size?: "sm" | "md" | "lg" | undefined;
99
+ status?: "online" | undefined;
100
+ className?: string | undefined;
83
101
  }
84
102
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
85
103
  export type ISODate = string;
@@ -89,6 +107,35 @@ export interface DateDisplayOptions {
89
107
  /** `buddhist` (พ.ศ.; default for th) or `gregory` (ค.ศ.; default for en and sv). */
90
108
  calendar?: "buddhist" | "gregory" | undefined;
91
109
  }
110
+ export interface StatChange {
111
+ /** Shown as written, e.g. "+12%" or "−3". */
112
+ value: React$1.ReactNode;
113
+ direction?: "up" | "down" | "flat" | undefined;
114
+ /** Default: up = positive, down = negative. Set it when a rise is bad (cancellations, wait time). */
115
+ tone?: "positive" | "negative" | "neutral" | undefined;
116
+ /** e.g. "vs last month". */
117
+ label?: React$1.ReactNode | undefined;
118
+ }
119
+ export interface StatProps {
120
+ /** Your router's link (e.g. `Link` from `next/link`) for this component; defaults to AuraProvider's `linkComponent`, then `<a>`. */
121
+ linkComponent?: React$1.ElementType | undefined;
122
+ label: React$1.ReactNode;
123
+ value?: React$1.ReactNode | undefined;
124
+ /** Small unit after the value ("งาน", "คน", "%"). */
125
+ unit?: React$1.ReactNode | undefined;
126
+ change?: StatChange | undefined;
127
+ caption?: React$1.ReactNode | undefined;
128
+ icon?: IconInput | undefined;
129
+ /** Skeleton in place of the value. */
130
+ loading?: boolean | undefined;
131
+ /** Makes the whole card a link or a button (drill-down). */
132
+ href?: string | undefined;
133
+ onClick?: (() => void) | undefined;
134
+ /** The label as a heading of this level (5.14), when the tile titles its section. Not with `onClick` (a heading
135
+ * can't sit in a button); a link is fine. Default: a plain label. */
136
+ headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
137
+ className?: string | undefined;
138
+ }
92
139
  export type Tone = "neutral" | "accent" | "success" | "warning" | "danger";
93
140
  export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
94
141
  tone?: Tone | undefined;
@@ -107,7 +154,9 @@ export interface EmptyStateProps extends Omit<React$1.HTMLAttributes<HTMLDivElem
107
154
  /** `danger` (5.13) for a failure — the data couldn't load: the danger tint, a danger-coloured icon, and a solid
108
155
  * danger frame with `bordered`. It stays quiet; pass `role="alert"` if it must interrupt. Default `neutral`. */
109
156
  tone?: "neutral" | "danger" | undefined;
110
- headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
157
+ /** The title's heading level (default 3); `false` (5.14) makes it a paragraph, for a card or list whose outline
158
+ * mustn't change. EmptyState has no live-region role unless you pass `role`. */
159
+ headingLevel?: 2 | 3 | 4 | 5 | 6 | false | undefined;
111
160
  className?: string | undefined;
112
161
  }
113
162
  export interface ThemeOptions {
@@ -241,6 +290,10 @@ export interface AuraStrings {
241
290
  pagination: string;
242
291
  pageN: (p: number) => string;
243
292
  accepts: (list?: string, max?: number | string) => string;
293
+ /** Progress with a reserved segment (5.15): "2 of 6 used, 1 reserved". */
294
+ progressReserved: (value: number, reserved: number, max: number) => string;
295
+ /** The collapsed middle of a Breadcrumb (5.15). */
296
+ breadcrumbMore: string;
244
297
  }
245
298
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
246
299
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -298,6 +351,7 @@ export declare function buttonClass(opts?: {
298
351
  size?: ButtonProps["size"];
299
352
  fullWidth?: boolean | undefined;
300
353
  loading?: boolean | undefined;
354
+ touchHeight?: boolean | undefined;
301
355
  className?: string | undefined;
302
356
  }): string;
303
357
  declare function ServerAlert(props: Omit<AlertProps, "onDismiss">): React$1.ReactElement;
@@ -305,6 +359,11 @@ declare function ServerCard(props: CardProps): React$1.ReactElement;
305
359
  declare function ServerStatusPill(props: StatusPillProps): React$1.ReactElement;
306
360
  declare function ServerBadge(props: BadgeProps): React$1.ReactElement;
307
361
  declare function ServerEmptyState(props: EmptyStateProps): React$1.ReactElement;
362
+ /** Stat for Server Components (5.14): no `onClick` (a button needs the client); `href` renders `linkComponent`, else `<a>`. */
363
+ declare function ServerStat(props: Omit<StatProps, "onClick">): React$1.ReactElement;
364
+ /** Avatar for Server Components (5.14): initials and colour. Falling back to the initials when an image fails needs the
365
+ * client, so give a server avatar a `src` you know loads, or none. */
366
+ declare function ServerAvatar(props: AvatarProps): React$1.ReactElement;
308
367
  /** Makes icon names as strings work: `registerIcons(allIcons)` once at startup, with `allIcons` from
309
368
  * `@jirawatpyk/aura-react/icons`, or only the icons you use (`registerIcons([IconUsers, IconPlus])`). In 5.x every
310
369
  * name already works and this only says you are ready for 6.0 (it silences the notice); in 6.0 it is required. Call it in the module
@@ -315,9 +374,11 @@ export declare const Icon: React$1.ForwardRefExoticComponent<IconProps & React$1
315
374
 
316
375
  export {
317
376
  ServerAlert as Alert,
377
+ ServerAvatar as Avatar,
318
378
  ServerBadge as Badge,
319
379
  ServerCard as Card,
320
380
  ServerEmptyState as EmptyState,
381
+ ServerStat as Stat,
321
382
  ServerStatusPill as StatusPill,
322
383
  scale as brandScale,
323
384
  toneFor as statusTone,
@@ -20,6 +20,9 @@ export interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElem
20
20
  fullWidth?: boolean | undefined;
21
21
  /** `sm` is 32px with tighter padding, for toolbars, table rows and card footers. Default `md` (44px). */
22
22
  size?: ButtonSize | undefined;
23
+ /** 44px tall below 640px (the viewport's), whatever `size` — a small button that phones get at full touch height
24
+ * (5.15, Chamber-OS 100). From 640px up it keeps its size. */
25
+ touchHeight?: boolean | undefined;
23
26
  }
24
27
  /** An AURA icon name, or any icon element (e.g. `<Building />` from lucide-react). AURA sizes it and hides it from screen readers. */
25
28
  export type IconInput = IconName | React$1.ReactElement;
@@ -80,6 +83,21 @@ export interface CardProps extends Omit<React$1.HTMLAttributes<HTMLElement>, "ti
80
83
  className?: string | undefined;
81
84
  /** id for the title element; the card is then labelled by it (aria-labelledby). */
82
85
  titleId?: string | undefined;
86
+ /** Free head content in place of `title` and `description` (5.15, Chamber-OS 87): skeleton bars while the card
87
+ * loads, a status pill above a title you mark up yourself. No heading is added; `actions` still sit top-right. */
88
+ header?: React$1.ReactNode | undefined;
89
+ /** Below this width (the viewport's) the card drops its border, surface and shadow — a list whose rows become
90
+ * cards of their own on a phone, so there are no cards in a card (5.15, Chamber-OS 93). Padding is kept. */
91
+ flushBelow?: "sm" | "md" | "lg" | undefined;
92
+ }
93
+ export interface AvatarProps {
94
+ /** Accessible name; also gives the initials and a stable colour. */
95
+ name: string;
96
+ src?: string | undefined;
97
+ /** `sm` 24 · `md` 32 (default) · `lg` 40. */
98
+ size?: "sm" | "md" | "lg" | undefined;
99
+ status?: "online" | undefined;
100
+ className?: string | undefined;
83
101
  }
84
102
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
85
103
  export type ISODate = string;
@@ -89,6 +107,35 @@ export interface DateDisplayOptions {
89
107
  /** `buddhist` (พ.ศ.; default for th) or `gregory` (ค.ศ.; default for en and sv). */
90
108
  calendar?: "buddhist" | "gregory" | undefined;
91
109
  }
110
+ export interface StatChange {
111
+ /** Shown as written, e.g. "+12%" or "−3". */
112
+ value: React$1.ReactNode;
113
+ direction?: "up" | "down" | "flat" | undefined;
114
+ /** Default: up = positive, down = negative. Set it when a rise is bad (cancellations, wait time). */
115
+ tone?: "positive" | "negative" | "neutral" | undefined;
116
+ /** e.g. "vs last month". */
117
+ label?: React$1.ReactNode | undefined;
118
+ }
119
+ export interface StatProps {
120
+ /** Your router's link (e.g. `Link` from `next/link`) for this component; defaults to AuraProvider's `linkComponent`, then `<a>`. */
121
+ linkComponent?: React$1.ElementType | undefined;
122
+ label: React$1.ReactNode;
123
+ value?: React$1.ReactNode | undefined;
124
+ /** Small unit after the value ("งาน", "คน", "%"). */
125
+ unit?: React$1.ReactNode | undefined;
126
+ change?: StatChange | undefined;
127
+ caption?: React$1.ReactNode | undefined;
128
+ icon?: IconInput | undefined;
129
+ /** Skeleton in place of the value. */
130
+ loading?: boolean | undefined;
131
+ /** Makes the whole card a link or a button (drill-down). */
132
+ href?: string | undefined;
133
+ onClick?: (() => void) | undefined;
134
+ /** The label as a heading of this level (5.14), when the tile titles its section. Not with `onClick` (a heading
135
+ * can't sit in a button); a link is fine. Default: a plain label. */
136
+ headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
137
+ className?: string | undefined;
138
+ }
92
139
  export type Tone = "neutral" | "accent" | "success" | "warning" | "danger";
93
140
  export interface BadgeProps extends React$1.HTMLAttributes<HTMLSpanElement> {
94
141
  tone?: Tone | undefined;
@@ -107,7 +154,9 @@ export interface EmptyStateProps extends Omit<React$1.HTMLAttributes<HTMLDivElem
107
154
  /** `danger` (5.13) for a failure — the data couldn't load: the danger tint, a danger-coloured icon, and a solid
108
155
  * danger frame with `bordered`. It stays quiet; pass `role="alert"` if it must interrupt. Default `neutral`. */
109
156
  tone?: "neutral" | "danger" | undefined;
110
- headingLevel?: 2 | 3 | 4 | 5 | 6 | undefined;
157
+ /** The title's heading level (default 3); `false` (5.14) makes it a paragraph, for a card or list whose outline
158
+ * mustn't change. EmptyState has no live-region role unless you pass `role`. */
159
+ headingLevel?: 2 | 3 | 4 | 5 | 6 | false | undefined;
111
160
  className?: string | undefined;
112
161
  }
113
162
  export interface ThemeOptions {
@@ -241,6 +290,10 @@ export interface AuraStrings {
241
290
  pagination: string;
242
291
  pageN: (p: number) => string;
243
292
  accepts: (list?: string, max?: number | string) => string;
293
+ /** Progress with a reserved segment (5.15): "2 of 6 used, 1 reserved". */
294
+ progressReserved: (value: number, reserved: number, max: number) => string;
295
+ /** The collapsed middle of a Breadcrumb (5.15). */
296
+ breadcrumbMore: string;
244
297
  }
245
298
  /** Built-in strings by locale. th and sv also ship as packs (`@jirawatpyk/aura-react/locales/th`, `/sv`, 5.9); in 6.0
246
299
  * only en stays built in and a Thai or Swedish app passes its pack to AuraProvider `strings`. */
@@ -298,6 +351,7 @@ export declare function buttonClass(opts?: {
298
351
  size?: ButtonProps["size"];
299
352
  fullWidth?: boolean | undefined;
300
353
  loading?: boolean | undefined;
354
+ touchHeight?: boolean | undefined;
301
355
  className?: string | undefined;
302
356
  }): string;
303
357
  declare function ServerAlert(props: Omit<AlertProps, "onDismiss">): React$1.ReactElement;
@@ -305,6 +359,11 @@ declare function ServerCard(props: CardProps): React$1.ReactElement;
305
359
  declare function ServerStatusPill(props: StatusPillProps): React$1.ReactElement;
306
360
  declare function ServerBadge(props: BadgeProps): React$1.ReactElement;
307
361
  declare function ServerEmptyState(props: EmptyStateProps): React$1.ReactElement;
362
+ /** Stat for Server Components (5.14): no `onClick` (a button needs the client); `href` renders `linkComponent`, else `<a>`. */
363
+ declare function ServerStat(props: Omit<StatProps, "onClick">): React$1.ReactElement;
364
+ /** Avatar for Server Components (5.14): initials and colour. Falling back to the initials when an image fails needs the
365
+ * client, so give a server avatar a `src` you know loads, or none. */
366
+ declare function ServerAvatar(props: AvatarProps): React$1.ReactElement;
308
367
  /** Makes icon names as strings work: `registerIcons(allIcons)` once at startup, with `allIcons` from
309
368
  * `@jirawatpyk/aura-react/icons`, or only the icons you use (`registerIcons([IconUsers, IconPlus])`). In 5.x every
310
369
  * name already works and this only says you are ready for 6.0 (it silences the notice); in 6.0 it is required. Call it in the module
@@ -315,9 +374,11 @@ export declare const Icon: React$1.ForwardRefExoticComponent<IconProps & React$1
315
374
 
316
375
  export {
317
376
  ServerAlert as Alert,
377
+ ServerAvatar as Avatar,
318
378
  ServerBadge as Badge,
319
379
  ServerCard as Card,
320
380
  ServerEmptyState as EmptyState,
381
+ ServerStat as Stat,
321
382
  ServerStatusPill as StatusPill,
322
383
  scale as brandScale,
323
384
  toneFor as statusTone,
@@ -268,6 +268,10 @@ var th = {
268
268
  pageN: function(p) {
269
269
  return "\u0E2B\u0E19\u0E49\u0E32 " + p;
270
270
  },
271
+ progressReserved: function(v, r, max) {
272
+ return "\u0E43\u0E0A\u0E49\u0E41\u0E25\u0E49\u0E27 " + n(v) + " \u0E08\u0E32\u0E01 " + n(max) + " \u0E08\u0E2D\u0E07\u0E44\u0E27\u0E49 " + n(r);
273
+ },
274
+ breadcrumbMore: "\u0E41\u0E2A\u0E14\u0E07\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
271
275
  accepts: function(list, max) {
272
276
  return [list, max && "\u0E44\u0E21\u0E48\u0E40\u0E01\u0E34\u0E19 " + max + " \u0E15\u0E48\u0E2D\u0E44\u0E1F\u0E25\u0E4C"].filter(Boolean).join(" \xB7 ");
273
277
  }
@@ -394,6 +398,10 @@ var sv = {
394
398
  pageN: function(p) {
395
399
  return "Sida " + p;
396
400
  },
401
+ progressReserved: function(v, r, max) {
402
+ return nsv(v) + " av " + nsv(max) + " anv\xE4nda, " + nsv(r) + " reserverade";
403
+ },
404
+ breadcrumbMore: "Visa hela s\xF6kv\xE4gen",
397
405
  accepts: function(list, max) {
398
406
  return [list, max && "h\xF6gst " + max + " per fil"].filter(Boolean).join(", ");
399
407
  }
@@ -521,6 +529,10 @@ var STRINGS = {
521
529
  pageN: function(p) {
522
530
  return "Page " + p;
523
531
  },
532
+ progressReserved: function(v, r, max) {
533
+ return n2(v) + " of " + n2(max) + " used, " + n2(r) + " reserved";
534
+ },
535
+ breadcrumbMore: "Show the full path",
524
536
  accepts: function(list, max) {
525
537
  return [list, max && "up to " + max + " each"].filter(Boolean).join(", ");
526
538
  }
@@ -958,6 +970,7 @@ var Icon = React2.forwardRef(function Icon2(props, ref) {
958
970
  var iconNames = Object.keys(map);
959
971
 
960
972
  // src/icons.ts
973
+ var IconMinus = /* @__PURE__ */ defineIcon("minus", [["path", { d: "M5 12h14" }]]);
961
974
  var IconCircleAlert = /* @__PURE__ */ defineIcon("circle-alert", [
962
975
  ["circle", { cx: "12", cy: "12", r: "10" }],
963
976
  ["line", { x1: "12", x2: "12", y1: "8", y2: "12" }],
@@ -1000,6 +1013,14 @@ var IconInbox = /* @__PURE__ */ defineIcon("inbox", [
1000
1013
  { d: "M5.45 5.11 2 12v6a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2v-6l-3.45-6.89A2 2 0 0 0 16.76 4H7.24a2 2 0 0 0-1.79 1.11z" }
1001
1014
  ]
1002
1015
  ]);
1016
+ var IconTrendingUp = /* @__PURE__ */ defineIcon("trending-up", [
1017
+ ["path", { d: "M16 7h6v6" }],
1018
+ ["path", { d: "m22 7-8.5 8.5-5-5L2 17" }]
1019
+ ]);
1020
+ var IconTrendingDown = /* @__PURE__ */ defineIcon("trending-down", [
1021
+ ["path", { d: "M16 17h6v-6" }],
1022
+ ["path", { d: "m22 17-8.5-8.5-5 5L2 7" }]
1023
+ ]);
1003
1024
 
1004
1025
  // src/display.tsx
1005
1026
  var h2 = React3.createElement;
@@ -1038,8 +1059,11 @@ function cardElement(props, ref) {
1038
1059
  "interactive",
1039
1060
  "as",
1040
1061
  "className",
1041
- "titleId"
1062
+ "titleId",
1063
+ "header",
1064
+ "flushBelow"
1042
1065
  ]);
1066
+ const head = props.header !== void 0 && props.header !== null && props.header !== false;
1043
1067
  return h2(
1044
1068
  props.as || "section",
1045
1069
  Object.assign({}, rest, {
@@ -1048,12 +1072,13 @@ function cardElement(props, ref) {
1048
1072
  "aura-card",
1049
1073
  creative && "aura-card--creative",
1050
1074
  props.interactive && "is-interactive",
1075
+ props.flushBelow && "aura-card--flush-below-" + props.flushBelow,
1051
1076
  props.className
1052
1077
  ),
1053
1078
  /* A titled card is labelled by its title; otherwise the caller's aria-labelledby stays (5.8). */
1054
- "aria-labelledby": props.title && props.titleId ? props.titleId : props["aria-labelledby"]
1079
+ "aria-labelledby": !head && props.title && props.titleId ? props.titleId : props["aria-labelledby"]
1055
1080
  }),
1056
- props.title || props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__head" }, /* @__PURE__ */ React3.createElement("div", { className: "aura-card__heading" }, props.title ? h2("h" + (props.headingLevel || 3), { className: "aura-card__title", id: props.titleId }, props.title) : null, props.description ? /* @__PURE__ */ React3.createElement("p", { className: "aura-card__desc" }, props.description) : null), props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__actions" }, props.actions) : null) : null,
1081
+ head || props.title || props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__head" }, /* @__PURE__ */ React3.createElement("div", { className: "aura-card__heading" }, head ? props.header : null, !head && props.title ? h2("h" + (props.headingLevel || 3), { className: "aura-card__title", id: props.titleId }, props.title) : null, !head && props.description ? /* @__PURE__ */ React3.createElement("p", { className: "aura-card__desc" }, props.description) : null), props.actions ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__actions" }, props.actions) : null) : null,
1057
1082
  props.children ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__body" }, props.children) : null,
1058
1083
  props.footer ? /* @__PURE__ */ React3.createElement("div", { className: "aura-card__foot" }, props.footer) : null
1059
1084
  );
@@ -1090,7 +1115,7 @@ function badgeElement(props, ref) {
1090
1115
  );
1091
1116
  }
1092
1117
  function emptyStateElement(props, ref) {
1093
- const HT = "h" + (props.headingLevel || 3);
1118
+ const HT = props.headingLevel === false ? "p" : "h" + (props.headingLevel || 3);
1094
1119
  const rest = omit(props, [
1095
1120
  "title",
1096
1121
  "description",
@@ -1122,12 +1147,73 @@ function emptyStateElement(props, ref) {
1122
1147
  props.action ? /* @__PURE__ */ React3.createElement("div", { className: "aura-empty__action" }, props.action) : null
1123
1148
  );
1124
1149
  }
1150
+ function statElement(props, ref, Link) {
1151
+ const ch = props.change;
1152
+ const dir = ch && (ch.direction || "flat");
1153
+ const tone2 = ch && (ch.tone || (dir === "up" ? "positive" : dir === "down" ? "negative" : "neutral"));
1154
+ const v = props.value;
1155
+ const numeric = typeof v === "number" || typeof v === "string" && /\d/.test(v) && /^[\s\d.,:+\-\u2212%()\u0E3F$\u20AC\u00A3\u00A5kKmMbB]+$/.test(v.replace(/\b[A-Z]{3}\b/g, ""));
1156
+ const Tag = props.href ? Link : props.onClick ? "button" : "div";
1157
+ const interactive = !!(props.href || props.onClick);
1158
+ if (props.headingLevel && Tag === "button")
1159
+ devWarnOnce(
1160
+ "stat-heading-button",
1161
+ "Stat `headingLevel` is ignored with `onClick`: a heading can\u2019t sit in a button. Use `href`, or put the heading outside."
1162
+ );
1163
+ const LabelTag = props.headingLevel && Tag !== "button" ? "h" + props.headingLevel : "span";
1164
+ const HeadTag = LabelTag === "span" ? "span" : "div";
1165
+ return /* @__PURE__ */ React3.createElement(
1166
+ Tag,
1167
+ {
1168
+ ref,
1169
+ className: cx("aura-stat", interactive && "is-interactive", props.loading && "is-loading", props.className),
1170
+ href: props.href,
1171
+ onClick: props.onClick,
1172
+ type: Tag === "button" ? "button" : void 0,
1173
+ "aria-busy": props.loading || void 0
1174
+ },
1175
+ /* @__PURE__ */ React3.createElement(HeadTag, { className: "aura-stat__head" }, /* @__PURE__ */ React3.createElement(LabelTag, { className: "aura-stat__label" }, props.label), props.icon ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__icon" }, /* @__PURE__ */ React3.createElement(Icon, { name: props.icon })) : null),
1176
+ props.loading ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__value" }, /* @__PURE__ */ React3.createElement("span", { className: "aura-skel aura-stat__skel" })) : /* @__PURE__ */ React3.createElement("span", { className: cx("aura-stat__value", numeric && "is-numeric") }, props.value, props.unit ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__unit" }, props.unit) : null),
1177
+ ch && !props.loading || props.caption ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__foot" }, ch && !props.loading ? /* @__PURE__ */ React3.createElement("span", { className: cx("aura-stat__change", "is-" + tone2) }, /* @__PURE__ */ React3.createElement(
1178
+ Icon,
1179
+ {
1180
+ name: dir === "up" ? /* @__PURE__ */ React3.createElement(IconTrendingUp, null) : dir === "down" ? /* @__PURE__ */ React3.createElement(IconTrendingDown, null) : /* @__PURE__ */ React3.createElement(IconMinus, null),
1181
+ size: 14
1182
+ }
1183
+ ), /* @__PURE__ */ React3.createElement("span", null, ch.value)) : null, ch && ch.label && !props.loading ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__caption" }, ch.label) : null, props.caption ? /* @__PURE__ */ React3.createElement("span", { className: "aura-stat__caption" }, props.caption) : null) : null
1184
+ );
1185
+ }
1186
+ var AVATAR_TONES = ["progress", "ready", "neutral", "warning"];
1187
+ function initials(name) {
1188
+ const parts = String(name || "?").trim().split(/\s+/);
1189
+ return ((parts[0] || "?")[0] + (parts.length > 1 ? parts[parts.length - 1][0] : parts[0][1] || "")).toUpperCase();
1190
+ }
1191
+ function avatarElement(props, ref, broken, onError) {
1192
+ const size = props.size || "md";
1193
+ let hash = 0;
1194
+ String(props.name || "").split("").forEach(function(ch) {
1195
+ hash = hash * 31 + ch.charCodeAt(0) >>> 0;
1196
+ });
1197
+ const tone2 = AVATAR_TONES[hash % AVATAR_TONES.length];
1198
+ return /* @__PURE__ */ React3.createElement(
1199
+ "span",
1200
+ {
1201
+ ref,
1202
+ className: cx("aura-avatar", "aura-avatar--" + size, "aura-avatar--" + tone2, props.className),
1203
+ role: "img",
1204
+ "aria-label": props.name + (props.status ? ", " + props.status : "")
1205
+ },
1206
+ props.src && broken !== props.src ? /* @__PURE__ */ React3.createElement("img", { key: props.src, src: props.src, alt: "", onError }) : /* @__PURE__ */ React3.createElement("span", { "aria-hidden": true }, initials(props.name)),
1207
+ props.status === "online" ? /* @__PURE__ */ React3.createElement("span", { className: "aura-avatar__status", "aria-hidden": true }) : null
1208
+ );
1209
+ }
1125
1210
  function buttonClass(opts = {}) {
1126
1211
  return cx(
1127
1212
  "aura-btn",
1128
1213
  "aura-btn--" + (opts.variant || "primary"),
1129
1214
  opts.size === "sm" && "aura-btn--sm",
1130
1215
  opts.fullWidth && "aura-btn--full",
1216
+ opts.touchHeight && "aura-btn--touch",
1131
1217
  opts.loading && "is-loading",
1132
1218
  opts.className
1133
1219
  );
@@ -1147,13 +1233,21 @@ function ServerBadge(props) {
1147
1233
  function ServerEmptyState(props) {
1148
1234
  return emptyStateElement(props, void 0);
1149
1235
  }
1236
+ function ServerStat(props) {
1237
+ return statElement(omit(props, ["onClick"]), void 0, props.linkComponent || "a");
1238
+ }
1239
+ function ServerAvatar(props) {
1240
+ return avatarElement(props, void 0, null, void 0);
1241
+ }
1150
1242
  export {
1151
1243
  ServerAlert as Alert,
1244
+ ServerAvatar as Avatar,
1152
1245
  ServerBadge as Badge,
1153
1246
  ServerCard as Card,
1154
1247
  ServerEmptyState as EmptyState,
1155
1248
  Icon,
1156
1249
  STRINGS,
1250
+ ServerStat as Stat,
1157
1251
  ServerStatusPill as StatusPill,
1158
1252
  scale as brandScale,
1159
1253
  breakpoints,