@jirawatpyk/aura-react 4.12.0 → 4.14.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/dist/esm/index.js CHANGED
@@ -16,6 +16,10 @@ import { Combobox, defaultFilter } from "./Combobox.js";
16
16
  import { DatePicker, DateRangePicker, Calendar, formatDate, parseDate, useFormatDate } from "./DatePicker.js";
17
17
  import { Alert } from "./Alert.js";
18
18
  import { Toaster, toast } from "./Toaster.js";
19
+ import { PasswordField } from "./PasswordField.js";
20
+ import { FormErrorSummary } from "./FormErrorSummary.js";
21
+ import { FilterBar } from "./FilterBar.js";
22
+ import { Command } from "./Command.js";
19
23
  import { Tooltip } from "./Tooltip.js";
20
24
  import { Dialog, Drawer } from "./Dialog.js";
21
25
  import { DataTable } from "./DataTable.js";
@@ -30,7 +34,7 @@ import { Container } from "./Container.js";
30
34
  import { AppShell } from "./AppShell.js";
31
35
  import { useBreakpoint, useResponsive, breakpoints } from "./responsive.js";
32
36
  import { Surface } from "./Surface.js";
33
- import { AuraProvider, useAuraLocale, STRINGS } from "./locale.js";
37
+ import { AuraProvider, useAuraLocale, useDensity, STRINGS } from "./locale.js";
34
38
  import { Stat } from "./Stat.js";
35
39
  import { TimePicker, parseTime } from "./TimePicker.js";
36
40
  import { FileUpload, formatBytes } from "./FileUpload.js";
@@ -63,6 +67,7 @@ export {
63
67
  ColorSchemeScript,
64
68
  ColorSchemeToggle,
65
69
  Combobox,
70
+ Command,
66
71
  Container,
67
72
  DataTable,
68
73
  DatePicker,
@@ -73,6 +78,8 @@ export {
73
78
  EmptyState,
74
79
  Field,
75
80
  FileUpload,
81
+ FilterBar,
82
+ FormErrorSummary,
76
83
  Grid,
77
84
  ICONS,
78
85
  Icon,
@@ -80,6 +87,7 @@ export {
80
87
  Menu,
81
88
  NumberField,
82
89
  Pagination,
90
+ PasswordField,
83
91
  Popover,
84
92
  Progress,
85
93
  RadioGroup,
@@ -118,6 +126,7 @@ export {
118
126
  useAuraLocale,
119
127
  useBreakpoint,
120
128
  useColorScheme,
129
+ useDensity,
121
130
  useFormatDate,
122
131
  useResponsive
123
132
  };
@@ -11,6 +11,19 @@ const STRINGS = {
11
11
  close: "Close",
12
12
  dismiss: "Dismiss",
13
13
  dismissToast: "Dismiss notification",
14
+ showPassword: "Show password",
15
+ filters: "Filters",
16
+ search: "Search",
17
+ clearFilters: "Clear all",
18
+ results: function(n2) {
19
+ return n2 === 1 ? "1 result" : n2.toLocaleString("en") + " results";
20
+ },
21
+ commandMenu: "Command menu",
22
+ commandPlaceholder: "Type a command or search\u2026",
23
+ commandHint: "\u2191\u2193 to move \xB7 Enter to open \xB7 Esc to close",
24
+ errorSummary: function(n2) {
25
+ return n2 === 1 ? "Fix 1 field to continue" : "Fix " + n2 + " fields to continue";
26
+ },
14
27
  notifications: "Notifications",
15
28
  mainNav: "Main",
16
29
  breadcrumb: "Breadcrumb",
@@ -109,6 +122,19 @@ const STRINGS = {
109
122
  close: "\u0E1B\u0E34\u0E14",
110
123
  dismiss: "\u0E1B\u0E34\u0E14",
111
124
  dismissToast: "\u0E1B\u0E34\u0E14\u0E01\u0E32\u0E23\u0E41\u0E08\u0E49\u0E07\u0E40\u0E15\u0E37\u0E2D\u0E19",
125
+ showPassword: "\u0E41\u0E2A\u0E14\u0E07\u0E23\u0E2B\u0E31\u0E2A\u0E1C\u0E48\u0E32\u0E19",
126
+ filters: "\u0E15\u0E31\u0E27\u0E01\u0E23\u0E2D\u0E07",
127
+ search: "\u0E04\u0E49\u0E19\u0E2B\u0E32",
128
+ clearFilters: "\u0E25\u0E49\u0E32\u0E07\u0E17\u0E31\u0E49\u0E07\u0E2B\u0E21\u0E14",
129
+ results: function(n2) {
130
+ return n2.toLocaleString("en") + " \u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
131
+ },
132
+ commandMenu: "\u0E40\u0E21\u0E19\u0E39\u0E04\u0E33\u0E2A\u0E31\u0E48\u0E07",
133
+ commandPlaceholder: "\u0E1E\u0E34\u0E21\u0E1E\u0E4C\u0E04\u0E33\u0E2A\u0E31\u0E48\u0E07\u0E2B\u0E23\u0E37\u0E2D\u0E04\u0E49\u0E19\u0E2B\u0E32\u2026",
134
+ commandHint: "\u2191\u2193 \u0E40\u0E25\u0E37\u0E48\u0E2D\u0E19 \xB7 Enter \u0E40\u0E1B\u0E34\u0E14 \xB7 Esc \u0E1B\u0E34\u0E14",
135
+ errorSummary: function(n2) {
136
+ return "\u0E41\u0E01\u0E49\u0E44\u0E02 " + n2 + " \u0E0A\u0E48\u0E2D\u0E07\u0E01\u0E48\u0E2D\u0E19\u0E44\u0E1B\u0E15\u0E48\u0E2D";
137
+ },
112
138
  notifications: "\u0E01\u0E32\u0E23\u0E41\u0E08\u0E49\u0E07\u0E40\u0E15\u0E37\u0E2D\u0E19",
113
139
  mainNav: "\u0E40\u0E21\u0E19\u0E39\u0E2B\u0E25\u0E31\u0E01",
114
140
  breadcrumb: "\u0E40\u0E2A\u0E49\u0E19\u0E17\u0E32\u0E07",
@@ -207,6 +233,19 @@ const STRINGS = {
207
233
  close: "St\xE4ng",
208
234
  dismiss: "St\xE4ng",
209
235
  dismissToast: "St\xE4ng aviseringen",
236
+ showPassword: "Visa l\xF6senord",
237
+ filters: "Filter",
238
+ search: "S\xF6k",
239
+ clearFilters: "Rensa alla",
240
+ results: function(n2) {
241
+ return n2 === 1 ? "1 tr\xE4ff" : n2.toLocaleString("sv-SE") + " tr\xE4ffar";
242
+ },
243
+ commandMenu: "Kommandomeny",
244
+ commandPlaceholder: "Skriv ett kommando eller s\xF6k\u2026",
245
+ commandHint: "\u2191\u2193 flytta \xB7 Enter \xF6ppna \xB7 Esc st\xE4ng",
246
+ errorSummary: function(n2) {
247
+ return n2 === 1 ? "R\xE4tta 1 f\xE4lt f\xF6r att forts\xE4tta" : "R\xE4tta " + n2 + " f\xE4lt f\xF6r att forts\xE4tta";
248
+ },
210
249
  notifications: "Aviseringar",
211
250
  mainNav: "Huvudmeny",
212
251
  breadcrumb: "Br\xF6dsmulor",
@@ -304,6 +343,8 @@ const STRINGS = {
304
343
  };
305
344
  const LocaleContext = React.createContext(null);
306
345
  function AuraProvider(props) {
346
+ const outer = React.useContext(LocaleContext);
347
+ const density = props.density || outer && outer.density || null;
307
348
  const value = React.useMemo(
308
349
  function() {
309
350
  const base = props.locale && STRINGS[props.locale] || STRINGS.en;
@@ -311,12 +352,13 @@ function AuraProvider(props) {
311
352
  locale: props.locale || "en",
312
353
  calendar: props.calendar || null,
313
354
  strings: props.strings ? Object.assign({}, base, props.strings) : base,
314
- linkComponent: props.linkComponent || null
355
+ linkComponent: props.linkComponent || null,
356
+ density
315
357
  };
316
358
  },
317
- [props.locale, props.calendar, props.strings, props.linkComponent]
359
+ [props.locale, props.calendar, props.strings, props.linkComponent, density]
318
360
  );
319
- return /* @__PURE__ */ React.createElement(LocaleContext.Provider, { value }, props.children);
361
+ return /* @__PURE__ */ React.createElement(LocaleContext.Provider, { value }, props.density ? /* @__PURE__ */ React.createElement("div", { className: "aura-density", "data-density": props.density }, props.children) : props.children);
320
362
  }
321
363
  function useAuraLocale() {
322
364
  return React.useContext(LocaleContext) || { locale: null, calendar: null, strings: STRINGS.en };
@@ -325,6 +367,10 @@ function useLinkComponent(own) {
325
367
  const ctx = React.useContext(LocaleContext);
326
368
  return own || ctx && ctx.linkComponent || "a";
327
369
  }
370
+ function useDensity() {
371
+ const ctx = React.useContext(LocaleContext);
372
+ return ctx && ctx.density || void 0;
373
+ }
328
374
  function useStrings() {
329
375
  return useAuraLocale().strings;
330
376
  }
@@ -332,6 +378,7 @@ export {
332
378
  AuraProvider,
333
379
  STRINGS,
334
380
  useAuraLocale,
381
+ useDensity,
335
382
  useLinkComponent,
336
383
  useStrings
337
384
  };
package/dist/index.d.ts CHANGED
@@ -149,6 +149,8 @@ export interface DataTableProps {
149
149
  /** Controlled pinned columns (keys); pair with onPinnedColumnsChange. */
150
150
  pinnedColumns?: string[];
151
151
  onPinnedColumnsChange?: (keys: string[]) => void;
152
+ /** `compact` = 40px rows (48px on touch screens). Default: the surrounding density. */
153
+ density?: "comfortable" | "compact";
152
154
  /** Container width in px below which rows render as stacked cards (phones). Try 640. Measured with ResizeObserver, so it follows the container, not the window. */
153
155
  stackBelow?: number;
154
156
  className?: string;
@@ -184,6 +186,8 @@ export interface AuraProviderProps {
184
186
  calendar?: "buddhist" | "gregory";
185
187
  /** Override individual strings. */
186
188
  strings?: Partial<Record<string, string | ((...args: any[]) => string)>>;
189
+ /** `compact`: 36px fields and buttons, 40px table rows — dense admin screens. Touch screens keep 44px. Default: comfortable (or an ancestor's `data-density`). */
190
+ density?: "comfortable" | "compact";
187
191
  /** Your router's link (e.g. `Link` from `next/link`), used by every AURA component that renders a link: Button with `href`, SideNav, Breadcrumb, Stat, Pagination, DataTable row links. It gets `href`, `className`, `aria-current`, the children and the ref. A component's own `linkComponent` wins. */
188
192
  linkComponent?: React$1.ElementType;
189
193
  children?: React$1.ReactNode;
@@ -268,6 +272,96 @@ export interface TextFieldProps extends FieldProps, Omit<React$1.InputHTMLAttrib
268
272
  /** Trailing unit, e.g. "THB". */
269
273
  suffix?: React$1.ReactNode;
270
274
  }
275
+ /** A password input with a show/hide button. All TextField props; `ref` reaches the input (react-hook-form `register`). */
276
+ export interface PasswordFieldProps extends Omit<TextFieldProps, "type" | "suffix"> {
277
+ /** Set false to hide the show/hide button. Default true. */
278
+ toggle?: boolean;
279
+ }
280
+ /** One entry of a FormErrorSummary: the field's `id` (or `name`) and what to fix. */
281
+ export interface FormErrorItem {
282
+ field: string;
283
+ message: React$1.ReactNode;
284
+ }
285
+ /** The list of problems at the top of a form after a failed submit; each links to its field (GOV.UK pattern). */
286
+ export interface FormErrorSummaryProps {
287
+ /** A list, or react-hook-form's `formState.errors` as is (`{ name: { message } }`). Empty → renders nothing. */
288
+ errors: FormErrorItem[] | Record<string, {
289
+ message?: React$1.ReactNode;
290
+ } | undefined>;
291
+ /** Default "Fix N fields to continue" in the provider's language. */
292
+ title?: React$1.ReactNode;
293
+ /** Called with the field when a link is followed, e.g. react-hook-form's `setFocus`. Default: focus the element whose id (or name) is the field. */
294
+ onSelect?: (field: string) => void;
295
+ /** Change it on every submit (e.g. `formState.submitCount`) to move focus to the summary again. It also takes focus when errors first appear. */
296
+ focusKey?: unknown;
297
+ className?: string;
298
+ id?: string;
299
+ }
300
+ /** An applied filter, shown as a removable chip. */
301
+ export interface ActiveFilter {
302
+ id: string;
303
+ label: React$1.ReactNode;
304
+ onRemove: () => void;
305
+ }
306
+ /** The row above a table: search, filter controls, applied-filter chips, result count and actions. Wraps below md. */
307
+ export interface FilterBarProps {
308
+ /** Search text (controlled). Omit `onSearchChange` to hide the search field. */
309
+ search?: string;
310
+ /** Called after typing pauses (`searchDelay`), and at once on Enter or clear — ready to write to the URL. */
311
+ onSearchChange?: (value: string) => void;
312
+ /** ms to wait after the last keystroke. Default 300. */
313
+ searchDelay?: number;
314
+ searchLabel?: string;
315
+ searchPlaceholder?: string;
316
+ /** Filter controls beside the search: Select, SegmentedControl, a Popover of options… */
317
+ children?: React$1.ReactNode;
318
+ /** Applied filters as chips, each with its own remove button. */
319
+ filters?: ActiveFilter[];
320
+ /** Adds "Clear all" while any filter or search is set. */
321
+ onClearAll?: () => void;
322
+ /** A number ("312 results", announced politely) or your own node. */
323
+ resultCount?: number | React$1.ReactNode;
324
+ /** Trailing slot: export, a New button… */
325
+ actions?: React$1.ReactNode;
326
+ /** Accessible name of the region. Default "Filters". */
327
+ label?: string;
328
+ className?: string;
329
+ }
330
+ /** One command in the palette. */
331
+ export interface CommandItem {
332
+ id: string;
333
+ /** Text shown and searched. */
334
+ label: string;
335
+ /** Items with the same group are listed together under its heading, in first-seen order. */
336
+ group?: string;
337
+ icon?: IconInput;
338
+ description?: string;
339
+ /** Extra words that find it ("invoice" → Billing). */
340
+ keywords?: string[];
341
+ /** A shortcut shown at the right, e.g. "G I". Display only. */
342
+ shortcut?: string;
343
+ disabled?: boolean;
344
+ /** Runs when chosen (Enter or click); the palette then closes. */
345
+ onSelect?: () => void;
346
+ }
347
+ /** A command palette: a modal search over commands and pages, grouped, fully keyboard-driven. */
348
+ export interface CommandProps {
349
+ open: boolean;
350
+ onOpenChange: (open: boolean) => void;
351
+ items: CommandItem[];
352
+ /** Also called with the chosen item (after its own onSelect). */
353
+ onSelect?: (item: CommandItem) => void;
354
+ /** Accessible name. Default "Command menu". */
355
+ label?: string;
356
+ placeholder?: string;
357
+ /** Default "No matches". */
358
+ emptyText?: string;
359
+ /** ⌘K / Ctrl+K toggles the palette from anywhere on the page. Default true. */
360
+ hotkey?: boolean;
361
+ /** Replace the Thai-aware default filter. */
362
+ filter?: (item: CommandItem, query: string) => boolean;
363
+ className?: string;
364
+ }
271
365
  export interface TextareaProps extends FieldProps, Omit<React$1.TextareaHTMLAttributes<HTMLTextAreaElement>, "required"> {
272
366
  }
273
367
  export type SelectOption = string | {
@@ -332,9 +426,11 @@ export interface ToastOptions {
332
426
  };
333
427
  /** ms before it closes itself. Default 5000; Infinity keeps it until dismissed. */
334
428
  duration?: number;
335
- /** Reuse an id to replace a toast in place. */
429
+ /** Reuse an id to replace a toast in place (same position, timer restarts) — never a second toast. */
336
430
  id?: string;
337
431
  }
432
+ /** Options for the shorthands (`toast.success(title, opts)` …): everything but the title and tone. */
433
+ export type ToastShorthandOptions = Omit<ToastOptions, "title" | "tone">;
338
434
  export interface TooltipProps {
339
435
  content: React$1.ReactNode;
340
436
  /** One focusable element. */
@@ -979,6 +1075,11 @@ export declare const Alert: React$1.ForwardRefExoticComponent<AlertProps & React
979
1075
  /** Show a toast; returns its id. Needs `<Toaster />` mounted once. */
980
1076
  export declare function toast(opts: ToastOptions | string): string;
981
1077
  export declare namespace toast {
1078
+ var success: (title: string, opts?: ToastShorthandOptions) => string;
1079
+ var error: (title: string, opts?: ToastShorthandOptions) => string;
1080
+ var warning: (title: string, opts?: ToastShorthandOptions) => string;
1081
+ var info: (title: string, opts?: ToastShorthandOptions) => string;
1082
+ var loading: (title: string, opts?: ToastShorthandOptions) => string;
982
1083
  var dismiss: (id: string) => void;
983
1084
  }
984
1085
  export interface ToasterProps {
@@ -986,6 +1087,10 @@ export interface ToasterProps {
986
1087
  position?: "bottom" | "top";
987
1088
  }
988
1089
  export declare function Toaster(props: ToasterProps): React$1.ReactElement | null;
1090
+ export declare const PasswordField: React$1.ForwardRefExoticComponent<PasswordFieldProps & React$1.RefAttributes<HTMLInputElement>>;
1091
+ export declare const FormErrorSummary: React$1.ForwardRefExoticComponent<FormErrorSummaryProps & React$1.RefAttributes<HTMLDivElement>>;
1092
+ export declare const FilterBar: React$1.ForwardRefExoticComponent<FilterBarProps & React$1.RefAttributes<HTMLDivElement>>;
1093
+ export declare function Command(props: CommandProps): React$1.ReactElement | null;
989
1094
  export declare const Tooltip: React$1.ForwardRefExoticComponent<TooltipProps & React$1.RefAttributes<HTMLSpanElement>>;
990
1095
  export declare const Dialog: React$1.ForwardRefExoticComponent<DialogProps & React$1.RefAttributes<HTMLDivElement>>;
991
1096
  /** Side panel over the page: record detail, filters, mobile navigation. Modal (focus trap, scroll lock, focus restore). */
@@ -1032,6 +1137,15 @@ export interface AuraStrings {
1032
1137
  close: string;
1033
1138
  dismiss: string;
1034
1139
  dismissToast: string;
1140
+ showPassword: string;
1141
+ filters: string;
1142
+ search: string;
1143
+ clearFilters: string;
1144
+ results: (n: number) => string;
1145
+ commandMenu: string;
1146
+ commandPlaceholder: string;
1147
+ commandHint: string;
1148
+ errorSummary: (n: number) => string;
1035
1149
  notifications: string;
1036
1150
  mainNav: string;
1037
1151
  breadcrumb: string;
@@ -1106,11 +1220,15 @@ export interface AuraLocaleValue {
1106
1220
  strings: AuraStrings;
1107
1221
  /** The router link set on AuraProvider, if any. */
1108
1222
  linkComponent?: React$1.ElementType | null;
1223
+ /** The density set on the nearest AuraProvider that sets one (null: none, i.e. comfortable unless an ancestor's data-density says otherwise). */
1224
+ density?: "comfortable" | "compact" | null;
1109
1225
  }
1110
1226
  /** Sets the language of built-in labels (and the default date locale) for everything inside. */
1111
1227
  export declare function AuraProvider(props: AuraProviderProps): React$1.ReactElement;
1112
1228
  /** { locale, calendar, strings } from the nearest AuraProvider (English strings when there is none). */
1113
1229
  export declare function useAuraLocale(): AuraLocaleValue;
1230
+ /** The provider's density, for portals that render outside its wrapper. */
1231
+ export declare function useDensity(): "comfortable" | "compact" | undefined;
1114
1232
  export declare const Stat: React$1.ForwardRefExoticComponent<StatProps & React$1.RefAttributes<HTMLElement>>;
1115
1233
  /** Parse typed time: 9 · 09 · 930 · 0930 · 9:30 · 9.30 · 09.30 น. · 9:30 pm → "HH:mm" (24-hour) or null. */
1116
1234
  export declare function parseTime(text: string | null | undefined): string | null;
package/dist/styles.css CHANGED
@@ -7,7 +7,7 @@
7
7
  box-sizing: border-box;
8
8
  height: var(--aura-button-height);
9
9
  white-space: nowrap; flex-shrink: 0; /* a pill never wraps its label */
10
- padding: 0 var(--aura-space-6);
10
+ padding: 0 var(--aura-button-padding-x, var(--aura-space-6));
11
11
  border-radius: var(--aura-button-radius);
12
12
  border: 0;
13
13
  background: transparent;
@@ -74,14 +74,14 @@ a.aura-btn { text-decoration: none; }
74
74
  }
75
75
  .aura-table__scroll { position: relative; overflow: auto; overscroll-behavior: contain; }
76
76
  .aura-table__head {
77
- position: sticky; top: 0; z-index: 3;
78
- display: flex; height: var(--aura-space-12);
77
+ position: sticky; top: 0; z-index: 3; box-sizing: border-box; /* the hairline is inside the row height */
78
+ display: flex; height: var(--aura-table-row-height);
79
79
  background: var(--aura-bg-canvas);
80
80
  border-bottom: 1px solid var(--aura-border-default);
81
81
  font-family: var(--font-mono); font-size: 12px; color: var(--aura-fg-secondary);
82
82
  }
83
83
  .aura-table__row {
84
- display: flex; height: var(--aura-space-12);
84
+ box-sizing: border-box; display: flex; height: var(--aura-table-row-height); /* exactly the virtual row height */
85
85
  background: var(--aura-bg-surface);
86
86
  border-bottom: 1px solid var(--aura-border-default);
87
87
  font-size: 13px;
@@ -105,7 +105,7 @@ a.aura-btn { text-decoration: none; }
105
105
  /* Server mode refresh: the previous page stays, inactive, under a progress bar until the next one arrives */
106
106
  .aura-table.is-refreshing .aura-table__row, .aura-table.is-refreshing .aura-table__card { opacity: 0.6; pointer-events: none; }
107
107
  .aura-table__busy-bar {
108
- position: absolute; z-index: 6; top: calc(var(--aura-space-12) - 2px); left: 0; right: 0; height: 2px; overflow: hidden;
108
+ position: absolute; z-index: 6; top: calc(var(--aura-table-row-height) - 2px); left: 0; right: 0; height: 2px; overflow: hidden;
109
109
  border-radius: var(--aura-radius-full); pointer-events: none;
110
110
  }
111
111
  .aura-table--stacked .aura-table__busy-bar { top: 0; }
@@ -117,7 +117,7 @@ a.aura-btn { text-decoration: none; }
117
117
 
118
118
  /* Cells: text cells stay block so long values end in an ellipsis */
119
119
  .aura-table__td {
120
- box-sizing: border-box; height: 100%; line-height: calc(var(--aura-space-12) - 1px);
120
+ box-sizing: border-box; height: 100%; line-height: calc(var(--aura-table-row-height) - 1px);
121
121
  overflow: hidden; text-overflow: ellipsis; white-space: nowrap; padding-right: var(--aura-space-4);
122
122
  }
123
123
  .aura-table__td .aura-pill, .aura-table__td .aura-dropdown, .aura-table__td > .aura-icon-btn, .aura-table__td > .aura-btn { vertical-align: middle; }
@@ -130,7 +130,7 @@ a.aura-btn { text-decoration: none; }
130
130
  .aura-table__gutter { flex: none; width: var(--aura-space-6); position: sticky; left: 0; z-index: 2; background: inherit; }
131
131
  .aura-table__gutter--end { position: static; }
132
132
  .aura-table__picker {
133
- position: absolute; top: 0; right: 0; z-index: 5; width: var(--aura-space-12); height: calc(var(--aura-space-12) - 1px);
133
+ position: absolute; top: 0; right: 0; z-index: 5; width: var(--aura-space-12); height: calc(var(--aura-table-row-height) - 1px);
134
134
  display: flex; align-items: center; justify-content: center; background: var(--aura-bg-canvas); border-top-right-radius: calc(var(--aura-card-radius) - 1px);
135
135
  }
136
136
  .aura-table__gutter--end.has-picker {
@@ -228,7 +228,7 @@ a.aura-btn { text-decoration: none; }
228
228
 
229
229
  /* Footer + pager */
230
230
  .aura-table__foot {
231
- height: var(--aura-space-12); display: flex; align-items: center; justify-content: space-between;
231
+ height: var(--aura-table-row-height); display: flex; align-items: center; justify-content: space-between;
232
232
  padding: 0 var(--aura-space-3) 0 var(--aura-space-6);
233
233
  border-top: 1px solid var(--aura-border-default); background: var(--aura-bg-surface);
234
234
  font-size: 13px; color: var(--aura-fg-secondary); font-variant-numeric: tabular-nums;
@@ -1141,3 +1141,119 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1141
1141
  .aura-radio-group__list { gap: 0; }
1142
1142
  .aura-radio-group__list.is-horizontal { gap: 0 var(--aura-space-6); }
1143
1143
  }
1144
+
1145
+ /* PasswordField: the show/hide IconButton sits in the suffix slot, 6px from the edge */
1146
+ .aura-password .aura-input__suffix { padding-right: 6px; display: flex; align-items: center; }
1147
+
1148
+ /* FormErrorSummary: a danger alert whose items link to their fields */
1149
+ .aura-error-summary:focus-visible, .aura-error-summary:focus { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
1150
+ .aura-error-summary h2.aura-alert__title { font-family: inherit; }
1151
+ .aura-error-summary__list { margin: var(--aura-space-2) 0 0; padding-left: var(--aura-space-5); display: grid; gap: var(--aura-space-1); font-size: 14px; line-height: 20px; }
1152
+ .aura-error-summary__list a { color: var(--aura-alert-danger-fg); text-decoration: underline; text-underline-offset: 2px; font-weight: 500; }
1153
+ .aura-error-summary__list a:hover { text-decoration-thickness: 2px; }
1154
+ .aura-error-summary__list a:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; border-radius: var(--aura-radius-xs); }
1155
+
1156
+ /* aura-prose: sanitised rich text (editor output, e-mail previews). Styles exactly p, h1–h4, ul/ol/li, blockquote,
1157
+ * hr, a, strong, em, u with AURA type and spacing, in both themes. */
1158
+ .aura-prose { font-family: var(--font-sans); font-size: 14px; line-height: 1.7; color: var(--aura-fg-primary); overflow-wrap: anywhere; }
1159
+ .aura-prose > :first-child { margin-top: 0; }
1160
+ .aura-prose > :last-child { margin-bottom: 0; }
1161
+ .aura-prose p { margin: 0 0 var(--aura-space-4); }
1162
+ .aura-prose h1, .aura-prose h2, .aura-prose h3, .aura-prose h4 { color: var(--aura-fg-primary); margin: var(--aura-space-8) 0 var(--aura-space-3); }
1163
+ .aura-prose h1 { font-family: var(--font-display); font-weight: 600; font-size: 30px; line-height: 1.2; letter-spacing: -0.01em; }
1164
+ .aura-prose h2 { font-weight: 600; font-size: 22px; line-height: 1.3; }
1165
+ .aura-prose h3 { font-weight: 600; font-size: 17px; line-height: 1.45; }
1166
+ .aura-prose h4 { font-weight: 600; font-size: 15px; line-height: 1.5; }
1167
+ .aura-prose ul, .aura-prose ol { margin: 0 0 var(--aura-space-4); padding-left: var(--aura-space-6); }
1168
+ .aura-prose li { margin: var(--aura-space-1) 0; }
1169
+ .aura-prose li > ul, .aura-prose li > ol { margin: var(--aura-space-1) 0 0; }
1170
+ .aura-prose ul { list-style: disc; }
1171
+ .aura-prose ol { list-style: decimal; }
1172
+ .aura-prose li::marker { color: var(--aura-fg-secondary); }
1173
+ .aura-prose blockquote { margin: 0 0 var(--aura-space-4); padding: var(--aura-space-1) 0 var(--aura-space-1) var(--aura-space-4); border-left: 3px solid var(--aura-border-control); color: var(--aura-fg-secondary); }
1174
+ .aura-prose hr { margin: var(--aura-space-8) 0; border: 0; border-top: 1px solid var(--aura-border-default); }
1175
+ .aura-prose a { color: var(--aura-fg-accent); text-decoration: underline; text-underline-offset: 2px; }
1176
+ .aura-prose a:hover { text-decoration-thickness: 2px; }
1177
+ .aura-prose a:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; border-radius: var(--aura-radius-xs); }
1178
+ .aura-prose strong { font-weight: 600; }
1179
+ .aura-prose em { font-style: italic; }
1180
+ .aura-prose u { text-decoration: underline; text-underline-offset: 2px; }
1181
+
1182
+ /* FilterBar: search + controls + count + actions on one row, applied-filter chips below; wraps on small screens */
1183
+ .aura-filterbar { display: flex; flex-direction: column; gap: var(--aura-space-3); font-family: var(--font-sans); }
1184
+ .aura-filterbar__row { display: flex; flex-wrap: wrap; align-items: center; gap: var(--aura-space-3); }
1185
+ .aura-filterbar__search { flex: 1 1 240px; max-width: 360px; width: auto; }
1186
+ .aura-filterbar__search .aura-input__control::-webkit-search-cancel-button { display: none; }
1187
+ .aura-filterbar__clear-search { margin-right: 6px; }
1188
+ .aura-filterbar__controls { display: flex; flex-wrap: wrap; align-items: center; gap: var(--aura-space-3); }
1189
+ .aura-filterbar__spacer { flex: 1 1 0; }
1190
+ .aura-filterbar__count { font-size: 13px; color: var(--aura-fg-secondary); font-variant-numeric: tabular-nums; white-space: nowrap; }
1191
+ .aura-filterbar__actions { display: flex; align-items: center; gap: var(--aura-space-2); }
1192
+ .aura-filterbar__chips { display: flex; flex-wrap: wrap; align-items: center; gap: var(--aura-space-2); }
1193
+ .aura-filterbar__clear {
1194
+ border: 0; background: transparent; padding: 4px var(--aura-space-2); border-radius: var(--aura-radius-sm);
1195
+ font: inherit; font-size: 13px; font-weight: 500; color: var(--aura-fg-accent); cursor: pointer; text-decoration: underline; text-underline-offset: 2px;
1196
+ }
1197
+ .aura-filterbar__clear:hover { background: var(--aura-bg-surface-hover); }
1198
+ .aura-filterbar__clear:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
1199
+ @media (max-width: 767.98px) {
1200
+ .aura-filterbar__search { flex-basis: 100%; max-width: none; }
1201
+ .aura-filterbar__spacer { display: none; }
1202
+ }
1203
+
1204
+ /* Command palette: a search field over a grouped list, above everything else (it can open from a Dialog) */
1205
+ .aura-dialog-layer.aura-command-layer { z-index: var(--aura-z-menu); align-items: flex-start; padding: 12vh var(--aura-space-4) var(--aura-space-4); }
1206
+ .aura-command {
1207
+ position: relative; box-sizing: border-box; width: 100%; max-width: 640px; max-height: 70vh; display: flex; flex-direction: column; overflow: hidden;
1208
+ background: var(--aura-bg-surface); color: var(--aura-fg-primary); font-family: var(--font-sans);
1209
+ border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-xl); box-shadow: var(--aura-shadow-overlay);
1210
+ animation: aura-dialog-in var(--aura-duration-base) var(--aura-ease);
1211
+ }
1212
+ .aura-command:focus { outline: none; }
1213
+ .aura-command__search { display: flex; align-items: center; gap: var(--aura-space-3); padding: 0 var(--aura-space-4); border-bottom: 1px solid var(--aura-border-default); }
1214
+ .aura-command__search-icon { color: var(--aura-fg-secondary); }
1215
+ .aura-command__input {
1216
+ flex: 1; min-width: 0; height: 52px; border: 0; background: transparent; color: inherit; outline: none;
1217
+ font: inherit; font-size: max(16px, var(--aura-input-font-size));
1218
+ }
1219
+ .aura-command__input::placeholder { color: var(--aura-fg-tertiary); opacity: 1; }
1220
+ .aura-command__list { flex: 1 1 auto; overflow-y: auto; overscroll-behavior: contain; padding: var(--aura-space-2); }
1221
+ .aura-command__group + .aura-command__group { margin-top: var(--aura-space-2); }
1222
+ .aura-command__heading { padding: var(--aura-space-2) var(--aura-space-2) var(--aura-space-1); font-family: var(--font-mono); font-size: 11px; text-transform: uppercase; letter-spacing: 0.04em; color: var(--aura-fg-secondary); }
1223
+ .aura-command__item { display: flex; align-items: center; gap: var(--aura-space-3); min-height: 40px; padding: 6px var(--aura-space-2); border-radius: var(--aura-radius-sm); cursor: pointer; font-size: 14px; }
1224
+ .aura-command__item > .aura-icon { color: var(--aura-fg-secondary); }
1225
+ .aura-command__item.is-active { background: var(--aura-bg-surface-hover); outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: calc(-1 * var(--aura-focus-ring-width)); }
1226
+ .aura-command__item.is-disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
1227
+ .aura-command__text { flex: 1; min-width: 0; display: flex; flex-direction: column; }
1228
+ .aura-command__label { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
1229
+ .aura-command__desc { font-size: 12px; line-height: 1.5; color: var(--aura-fg-secondary); }
1230
+ .aura-command__kbd {
1231
+ flex: none; padding: 2px 6px; border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-xs);
1232
+ font-family: var(--font-mono); font-size: 11px; line-height: 16px; color: var(--aura-fg-secondary); background: var(--aura-bg-canvas);
1233
+ }
1234
+ .aura-command__empty { margin: 0; padding: var(--aura-space-6) var(--aura-space-2); text-align: center; font-size: 14px; color: var(--aura-fg-secondary); }
1235
+ .aura-command__foot { display: flex; justify-content: space-between; gap: var(--aura-space-3); padding: var(--aura-space-2) var(--aura-space-4); border-top: 1px solid var(--aura-border-default); font-size: 12px; color: var(--aura-fg-secondary); }
1236
+ @media (max-width: 639.98px) {
1237
+ .aura-dialog-layer.aura-command-layer { align-items: flex-start; padding: var(--aura-space-4); }
1238
+ .aura-command { max-height: 80vh; }
1239
+ .aura-command__foot { display: none; }
1240
+ .aura-command__item { min-height: 44px; }
1241
+ }
1242
+ @media (prefers-reduced-motion: reduce) { .aura-command { animation: none; } }
1243
+
1244
+ /* Density. data-density="compact" on <html> or any container (AuraProvider density="compact" and DataTable
1245
+ * density do it for you): 36px fields and buttons, 40px table rows, less button padding — for dense admin screens.
1246
+ * Touch screens keep 44px targets whatever the density. "comfortable" inside compact switches back. */
1247
+ [data-density='compact'] {
1248
+ --aura-input-height: 36px; --aura-button-height: 36px; --aura-table-row-height: 40px;
1249
+ --aura-button-padding-x: var(--aura-space-4);
1250
+ }
1251
+ [data-density='comfortable'] {
1252
+ --aura-input-height: 44px; --aura-button-height: 44px; --aura-table-row-height: 48px;
1253
+ --aura-button-padding-x: var(--aura-space-6);
1254
+ }
1255
+ @media (pointer: coarse) {
1256
+ [data-density='compact'] { --aura-input-height: 44px; --aura-button-height: 44px; --aura-table-row-height: 48px; --aura-button-padding-x: var(--aura-space-6); }
1257
+ }
1258
+ .aura-density { display: contents; }
1259
+
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "4.12.0",
3
+ "version": "4.14.0",
4
4
  "description": "AURA Design System — React components (Enterprise Standard, Human Creative). Thai-first: Buddhist-era dates, Thai collation.",
5
5
  "license": "MIT",
6
6
  "type": "module",