@jirawatpyk/aura-react 4.10.0 → 4.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -65,7 +65,7 @@ export default function Root() {
65
65
 
66
66
  ## Components (49)
67
67
 
68
- Actions: Button, IconButton, Menu, DropdownMenu, Tag · Forms: TextField, Textarea, NumberField, Select, RadioGroup, Checkbox, Switch, SegmentedControl, Combobox (one value, or `multiple`), FileUpload (+ `formatBytes`) · Dates & times: DatePicker, DateRangePicker, Calendar, TimePicker (+ `formatDate`, `parseDate`, `parseTime`) · Feedback: Alert, Toaster/`toast()`, Tooltip, StatusPill, Badge, Progress, Skeleton, EmptyState · Overlays: Dialog, Drawer, Popover · Data: DataTable, Stat · Layout: AppShell, Container, Stack, Grid, Accordion, Pagination, Card, Tabs, Stepper, SideNav, Breadcrumb, Avatar, Surface, Icon · Theme: ColorSchemeToggle, ColorSchemeScript, `useColorScheme`, ThemeStyle/`createTheme` · Hooks: `useBreakpoint`, `useResponsive`, `breakpoints`, `useAuraLocale`.
68
+ Actions: Button, IconButton, Menu, DropdownMenu, Tag · Forms: TextField, Textarea, NumberField, Select, RadioGroup, Checkbox, Switch, SegmentedControl, Combobox (one value, or `multiple`), FileUpload (+ `formatBytes`) · Dates & times: DatePicker, DateRangePicker, Calendar, TimePicker (+ `useFormatDate`, `formatDate`, `parseDate`, `parseTime`) · Feedback: Alert, Toaster/`toast()`, Tooltip, StatusPill, Badge, Progress, Skeleton, EmptyState · Overlays: Dialog, Drawer, Popover · Data: DataTable, Stat · Layout: AppShell, Container, Stack, Grid, Accordion, Pagination, Card, Tabs, Stepper, SideNav, Breadcrumb, Avatar, Surface, Icon · Theme: ColorSchemeToggle, ColorSchemeScript, `useColorScheme`, ThemeStyle/`createTheme` · Hooks: `useBreakpoint`, `useResponsive`, `breakpoints`, `useAuraLocale`.
69
69
 
70
70
  ## Router links, Swedish, motion
71
71
 
@@ -74,10 +74,14 @@ import Link from 'next/link';
74
74
  <AuraProvider locale="sv" linkComponent={Link}> {/* th | en | sv */}
75
75
  ```
76
76
 
77
- `linkComponent` is used by Button `href`, Breadcrumb, Pagination `getHref`, Stat `href`, SideNav and DataTable row/pager links. `sv` uses the Gregorian calendar and Monday first; `th` and `en` default to the Buddhist calendar.
77
+ `linkComponent` is used by Button `href`, Breadcrumb, Pagination `getHref`, Stat `href`, SideNav and DataTable row/pager links. Only `th` shows Buddhist-era years; `en` and `sv` are Gregorian (`sv` weeks start Monday). **Without a provider, components are English with Gregorian dates** — wrap Thai apps in `<AuraProvider locale="th">`. For dates in your own components use `useFormatDate()` — it follows the provider (`const fmt = useFormatDate(); fmt(iso, { format: 'long' })`). Plain `formatDate()` has no provider to read and stays Thai unless you pass `locale`. Values are always Gregorian ISO dates.
78
78
 
79
79
  Motion: skeleton pulses and indeterminate bars honour `prefers-reduced-motion: reduce` — skeletons stop (a static bar), spinners and progress bars slow to a third.
80
80
 
81
+ ## Phones and touch
82
+
83
+ Under 640px every text control uses 16px text (iOS Safari doesn't zoom). On touch screens (`pointer: coarse`) IconButton keeps its 32px look with a 44px hit area, and Radio, Checkbox and Switch rows are at least 44px with the whole row as the target. `<Button fullWidth>` fills its row and wraps long Thai or Swedish labels.
84
+
81
85
  ## Light and dark
82
86
 
83
87
  The tokens carry both schemes; `<html data-theme="light|dark|system">` picks one (`system` follows the OS in pure CSS).
@@ -106,7 +110,7 @@ Every component forwards `ref` to its real element — fields to the `<input>`/`
106
110
 
107
111
  ## Thai dates
108
112
 
109
- Values are ISO strings (`2026-09-18`); display is Thai with Buddhist-era years (`18 ก.ย. 2569`). People can type `18/09/2569`, `18/09/2026`, `2026-09-18` or `18 ก.ย. 2569`. Store ISO, never พ.ศ. `formatDate(iso, { format: 'long' })` → `18 กันยายน 2569`.
113
+ Values are ISO strings (`2026-09-18`); with `locale="th"` display is Thai with Buddhist-era years (`18 ก.ย. 2569`). People can type `18/09/2569`, `18/09/2026`, `2026-09-18` or `18 ก.ย. 2569`. Store ISO, never พ.ศ. In components, `useFormatDate()(iso, { format: 'long' })` → `18 กันยายน 2569` under `locale="th"`, `18 September 2026` under `en`. `parseDate(text)` reads any of these forms in every locale.
110
114
 
111
115
  ## Server rendering
112
116
 
@@ -119,6 +119,7 @@ window.Aura = (() => {
119
119
  useAuraLocale: () => useAuraLocale,
120
120
  useBreakpoint: () => useBreakpoint,
121
121
  useColorScheme: () => useColorScheme,
122
+ useFormatDate: () => useFormatDate,
122
123
  useResponsive: () => useResponsive
123
124
  });
124
125
 
@@ -602,7 +603,8 @@ window.Aura = (() => {
602
603
  "iconRight",
603
604
  "disabled",
604
605
  "linkComponent",
605
- "href"
606
+ "href",
607
+ "fullWidth"
606
608
  ]);
607
609
  const Tag3 = disabled ? "a" : Link;
608
610
  return /* @__PURE__ */ React4.createElement(
@@ -614,7 +616,7 @@ window.Aura = (() => {
614
616
  role: disabled ? "link" : void 0,
615
617
  "aria-disabled": disabled || void 0,
616
618
  tabIndex: disabled ? -1 : props.tabIndex,
617
- className: cx("aura-btn", "aura-btn--" + variant, props.className),
619
+ className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
618
620
  onClick: disabled ? void 0 : props.onClick
619
621
  },
620
622
  props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon }) : null,
@@ -631,14 +633,30 @@ window.Aura = (() => {
631
633
  const props = all;
632
634
  const variant = props.variant || "primary";
633
635
  const loading = !!props.loading;
634
- const rest = omit(props, ["variant", "className", "children", "type", "icon", "iconRight", "loading", "onClick"]);
636
+ const rest = omit(props, [
637
+ "variant",
638
+ "className",
639
+ "children",
640
+ "type",
641
+ "icon",
642
+ "iconRight",
643
+ "loading",
644
+ "onClick",
645
+ "fullWidth"
646
+ ]);
635
647
  return /* @__PURE__ */ React4.createElement(
636
648
  "button",
637
649
  {
638
650
  ...rest,
639
651
  ref,
640
652
  type: props.type || "button",
641
- className: cx("aura-btn", "aura-btn--" + variant, loading && "is-loading", props.className),
653
+ className: cx(
654
+ "aura-btn",
655
+ "aura-btn--" + variant,
656
+ props.fullWidth && "aura-btn--full",
657
+ loading && "is-loading",
658
+ props.className
659
+ ),
642
660
  "aria-busy": loading || void 0,
643
661
  "aria-disabled": loading || void 0,
644
662
  onClick: loading ? function(e) {
@@ -1081,25 +1099,37 @@ window.Aura = (() => {
1081
1099
  const auto = uid(), id = props.id || auto;
1082
1100
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
1083
1101
  const on = !!st[0];
1084
- return /* @__PURE__ */ React15.createElement("div", { className: cx("aura-switch-row", props.disabled && "is-disabled", props.className) }, /* @__PURE__ */ React15.createElement(
1085
- "button",
1102
+ return /* @__PURE__ */ React15.createElement(
1103
+ "div",
1086
1104
  {
1087
- ref,
1088
- type: "button",
1089
- role: "switch",
1090
- id,
1091
- "aria-checked": on,
1092
- disabled: props.disabled,
1093
- "aria-labelledby": props.label ? id + "-label" : void 0,
1094
- "aria-label": props.label ? void 0 : props["aria-label"],
1095
- "aria-describedby": props.description ? id + "-desc" : void 0,
1096
- className: cx("aura-switch", on && "is-on"),
1097
- onClick: function() {
1105
+ className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
1106
+ onClick: function(e) {
1107
+ const t = e.target;
1108
+ if (props.disabled || t.closest("button, label, a, input")) return;
1098
1109
  st[1](!on);
1099
1110
  }
1100
1111
  },
1101
- /* @__PURE__ */ React15.createElement("span", { className: "aura-switch__thumb" })
1102
- ), props.label ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React15.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null);
1112
+ /* @__PURE__ */ React15.createElement(
1113
+ "button",
1114
+ {
1115
+ ref,
1116
+ type: "button",
1117
+ role: "switch",
1118
+ id,
1119
+ "aria-checked": on,
1120
+ disabled: props.disabled,
1121
+ "aria-labelledby": props.label ? id + "-label" : void 0,
1122
+ "aria-label": props.label ? void 0 : props["aria-label"],
1123
+ "aria-describedby": props.description ? id + "-desc" : void 0,
1124
+ className: cx("aura-switch", on && "is-on"),
1125
+ onClick: function() {
1126
+ st[1](!on);
1127
+ }
1128
+ },
1129
+ /* @__PURE__ */ React15.createElement("span", { className: "aura-switch__thumb" })
1130
+ ),
1131
+ props.label ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React15.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null
1132
+ );
1103
1133
  });
1104
1134
 
1105
1135
  // src/Combobox.tsx
@@ -1473,11 +1503,11 @@ window.Aura = (() => {
1473
1503
  }
1474
1504
  var TAGS = { th: "th-TH", en: "en-GB", sv: "sv-SE" };
1475
1505
  function defaultCalendar(locale) {
1476
- return locale === "sv" ? "gregory" : "buddhist";
1506
+ return locale === "th" ? "buddhist" : "gregory";
1477
1507
  }
1478
1508
  function localeTag(locale, calendar) {
1479
1509
  const cal = calendar || defaultCalendar(locale);
1480
- return (TAGS[locale || "th"] || "th-TH") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
1510
+ return (TAGS[locale || "en"] || "en-GB") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
1481
1511
  }
1482
1512
  var DATE_TEXT = {
1483
1513
  th: {
@@ -1505,11 +1535,11 @@ window.Aura = (() => {
1505
1535
  today: "Today",
1506
1536
  clear: "Clear",
1507
1537
  chooseDate: "Choose date",
1508
- datePlaceholder: "dd/mm/yyyy",
1538
+ datePlaceholder: "DD/MM/YYYY",
1509
1539
  clearDate: "Clear date",
1510
1540
  openCalendar: "Open calendar",
1511
1541
  chooseDates: "Choose dates",
1512
- rangePlaceholder: "dd/mm/yyyy \u2013 dd/mm/yyyy",
1542
+ rangePlaceholder: "DD/MM/YYYY \u2013 DD/MM/YYYY",
1513
1543
  clearDates: "Clear dates",
1514
1544
  chooseStart: "Choose the start date",
1515
1545
  chooseEnd: "Choose the end date"
@@ -1533,7 +1563,7 @@ window.Aura = (() => {
1533
1563
  }
1534
1564
  };
1535
1565
  function dateText(locale) {
1536
- return DATE_TEXT[locale || "th"] || DATE_TEXT.th;
1566
+ return DATE_TEXT[locale || "en"] || DATE_TEXT.en;
1537
1567
  }
1538
1568
  var fmtCache = {};
1539
1569
  var PRESETS = {
@@ -1550,7 +1580,24 @@ window.Aura = (() => {
1550
1580
  const o = opts || {}, d = fromISO(iso);
1551
1581
  if (!d) return "";
1552
1582
  const f = typeof o.format === "object" ? o.format : PRESETS[o.format || "short"];
1553
- return fmt(localeTag(o.locale, o.calendar), f, d).replace(ERA, "");
1583
+ const loc = o.locale || "th";
1584
+ return fmt(localeTag(loc, o.calendar || defaultCalendar(loc)), f, d).replace(ERA, "");
1585
+ }
1586
+ function useFormatDate() {
1587
+ const ctx = useAuraLocale(), locale = ctx.locale || "en", calendar = ctx.calendar;
1588
+ return React17.useCallback(
1589
+ function(iso, opts) {
1590
+ const o = opts || {}, loc = o.locale || locale;
1591
+ return formatDate(
1592
+ iso,
1593
+ Object.assign({}, o, {
1594
+ locale: loc,
1595
+ calendar: o.calendar || (o.locale ? void 0 : calendar) || defaultCalendar(loc)
1596
+ })
1597
+ );
1598
+ },
1599
+ [locale, calendar]
1600
+ );
1554
1601
  }
1555
1602
  var MONTHS = null;
1556
1603
  function monthIndex(word) {
@@ -1590,7 +1637,7 @@ window.Aura = (() => {
1590
1637
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
1591
1638
  }
1592
1639
  var Calendar = React17.forwardRef(function Calendar2(props, ref) {
1593
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
1640
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
1594
1641
  const weekStart = props.weekStartsOn == null ? locale === "sv" ? 1 : 0 : props.weekStartsOn;
1595
1642
  let today = /* @__PURE__ */ new Date();
1596
1643
  today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
@@ -1775,6 +1822,7 @@ window.Aura = (() => {
1775
1822
  tabIndex: same(d, focusDate) ? 0 : -1,
1776
1823
  disabled: dis,
1777
1824
  "aria-label": fmt(tag, { weekday: "long", day: "numeric", month: "long", year: "numeric" }, d),
1825
+ suppressHydrationWarning: true,
1778
1826
  "aria-current": same(d, today) ? "date" : void 0,
1779
1827
  "aria-pressed": sel || void 0,
1780
1828
  className: cx(
@@ -1933,7 +1981,7 @@ window.Aura = (() => {
1933
1981
  }
1934
1982
  var DatePicker = React17.forwardRef(function DatePicker2(props, ref) {
1935
1983
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
1936
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
1984
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
1937
1985
  const st = useMaybeControlled(
1938
1986
  props.value,
1939
1987
  props.defaultValue == null ? null : props.defaultValue,
@@ -2028,7 +2076,7 @@ window.Aura = (() => {
2028
2076
  var DateRangePicker = React17.forwardRef(
2029
2077
  function DateRangePicker2(props, ref) {
2030
2078
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
2031
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
2079
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
2032
2080
  const st = useMaybeControlled(
2033
2081
  props.value,
2034
2082
  props.defaultValue || { start: null, end: null },
@@ -4666,8 +4714,8 @@ window.Aura = (() => {
4666
4714
  D["alert-info-bg"] = D["bg-selected"];
4667
4715
  D["alert-info-fg"] = fit(b[300], [D["alert-info-bg"]], 4.5, 1);
4668
4716
  D["alert-info-border"] = mix(ZINC[900], b[400], 0.4);
4669
- D["status-progress-bg"] = b[100];
4670
- D["status-progress-fg"] = L["status-progress-fg"];
4717
+ D["status-progress-bg"] = mix(ZINC[900], b[500], 0.2);
4718
+ D["status-progress-fg"] = fit(b[300], [D["status-progress-bg"]], 4.5, 1);
4671
4719
  if (s) {
4672
4720
  L["accent-dot"] = s[200];
4673
4721
  L["accent-lime"] = s[200];
@@ -4734,6 +4782,7 @@ window.Aura = (() => {
4734
4782
  check("light", "fg-primary", "bg-selected", 4.5, INK, L["bg-selected"]);
4735
4783
  check("dark", "fg-primary", "bg-selected", 4.5, "#ffffff", D["bg-selected"]);
4736
4784
  check("light", "status-progress-fg", "status-progress-bg", 4.5, L["status-progress-fg"], L["status-progress-bg"]);
4785
+ check("dark", "status-progress-fg", "status-progress-bg", 4.5, D["status-progress-fg"], D["status-progress-bg"]);
4737
4786
  check("light", "alert-info-fg", "alert-info-bg", 4.5, L["alert-info-fg"], L["alert-info-bg"]);
4738
4787
  check("dark", "alert-info-fg", "alert-info-bg", 4.5, D["alert-info-fg"], D["alert-info-bg"]);
4739
4788
  if (o.primary === "brand") {
@@ -100,6 +100,7 @@ __export(index_exports, {
100
100
  useAuraLocale: () => useAuraLocale,
101
101
  useBreakpoint: () => useBreakpoint,
102
102
  useColorScheme: () => useColorScheme,
103
+ useFormatDate: () => useFormatDate,
103
104
  useResponsive: () => useResponsive
104
105
  });
105
106
  module.exports = __toCommonJS(index_exports);
@@ -584,7 +585,8 @@ function ButtonLink(props, ref, Link) {
584
585
  "iconRight",
585
586
  "disabled",
586
587
  "linkComponent",
587
- "href"
588
+ "href",
589
+ "fullWidth"
588
590
  ]);
589
591
  const Tag3 = disabled ? "a" : Link;
590
592
  return /* @__PURE__ */ React4.createElement(
@@ -596,7 +598,7 @@ function ButtonLink(props, ref, Link) {
596
598
  role: disabled ? "link" : void 0,
597
599
  "aria-disabled": disabled || void 0,
598
600
  tabIndex: disabled ? -1 : props.tabIndex,
599
- className: cx("aura-btn", "aura-btn--" + variant, props.className),
601
+ className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
600
602
  onClick: disabled ? void 0 : props.onClick
601
603
  },
602
604
  props.icon ? /* @__PURE__ */ React4.createElement(Icon, { name: props.icon }) : null,
@@ -613,14 +615,30 @@ var Button = React4.forwardRef(
613
615
  const props = all;
614
616
  const variant = props.variant || "primary";
615
617
  const loading = !!props.loading;
616
- const rest = omit(props, ["variant", "className", "children", "type", "icon", "iconRight", "loading", "onClick"]);
618
+ const rest = omit(props, [
619
+ "variant",
620
+ "className",
621
+ "children",
622
+ "type",
623
+ "icon",
624
+ "iconRight",
625
+ "loading",
626
+ "onClick",
627
+ "fullWidth"
628
+ ]);
617
629
  return /* @__PURE__ */ React4.createElement(
618
630
  "button",
619
631
  {
620
632
  ...rest,
621
633
  ref,
622
634
  type: props.type || "button",
623
- className: cx("aura-btn", "aura-btn--" + variant, loading && "is-loading", props.className),
635
+ className: cx(
636
+ "aura-btn",
637
+ "aura-btn--" + variant,
638
+ props.fullWidth && "aura-btn--full",
639
+ loading && "is-loading",
640
+ props.className
641
+ ),
624
642
  "aria-busy": loading || void 0,
625
643
  "aria-disabled": loading || void 0,
626
644
  onClick: loading ? function(e) {
@@ -1063,25 +1081,37 @@ var Switch = React15.forwardRef(function Switch2(props, ref) {
1063
1081
  const auto = uid(), id = props.id || auto;
1064
1082
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
1065
1083
  const on = !!st[0];
1066
- return /* @__PURE__ */ React15.createElement("div", { className: cx("aura-switch-row", props.disabled && "is-disabled", props.className) }, /* @__PURE__ */ React15.createElement(
1067
- "button",
1084
+ return /* @__PURE__ */ React15.createElement(
1085
+ "div",
1068
1086
  {
1069
- ref,
1070
- type: "button",
1071
- role: "switch",
1072
- id,
1073
- "aria-checked": on,
1074
- disabled: props.disabled,
1075
- "aria-labelledby": props.label ? id + "-label" : void 0,
1076
- "aria-label": props.label ? void 0 : props["aria-label"],
1077
- "aria-describedby": props.description ? id + "-desc" : void 0,
1078
- className: cx("aura-switch", on && "is-on"),
1079
- onClick: function() {
1087
+ className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
1088
+ onClick: function(e) {
1089
+ const t = e.target;
1090
+ if (props.disabled || t.closest("button, label, a, input")) return;
1080
1091
  st[1](!on);
1081
1092
  }
1082
1093
  },
1083
- /* @__PURE__ */ React15.createElement("span", { className: "aura-switch__thumb" })
1084
- ), props.label ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React15.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null);
1094
+ /* @__PURE__ */ React15.createElement(
1095
+ "button",
1096
+ {
1097
+ ref,
1098
+ type: "button",
1099
+ role: "switch",
1100
+ id,
1101
+ "aria-checked": on,
1102
+ disabled: props.disabled,
1103
+ "aria-labelledby": props.label ? id + "-label" : void 0,
1104
+ "aria-label": props.label ? void 0 : props["aria-label"],
1105
+ "aria-describedby": props.description ? id + "-desc" : void 0,
1106
+ className: cx("aura-switch", on && "is-on"),
1107
+ onClick: function() {
1108
+ st[1](!on);
1109
+ }
1110
+ },
1111
+ /* @__PURE__ */ React15.createElement("span", { className: "aura-switch__thumb" })
1112
+ ),
1113
+ props.label ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React15.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React15.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null
1114
+ );
1085
1115
  });
1086
1116
 
1087
1117
  // src/Combobox.tsx
@@ -1455,11 +1485,11 @@ function same(a, b) {
1455
1485
  }
1456
1486
  var TAGS = { th: "th-TH", en: "en-GB", sv: "sv-SE" };
1457
1487
  function defaultCalendar(locale) {
1458
- return locale === "sv" ? "gregory" : "buddhist";
1488
+ return locale === "th" ? "buddhist" : "gregory";
1459
1489
  }
1460
1490
  function localeTag(locale, calendar) {
1461
1491
  const cal = calendar || defaultCalendar(locale);
1462
- return (TAGS[locale || "th"] || "th-TH") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
1492
+ return (TAGS[locale || "en"] || "en-GB") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
1463
1493
  }
1464
1494
  var DATE_TEXT = {
1465
1495
  th: {
@@ -1487,11 +1517,11 @@ var DATE_TEXT = {
1487
1517
  today: "Today",
1488
1518
  clear: "Clear",
1489
1519
  chooseDate: "Choose date",
1490
- datePlaceholder: "dd/mm/yyyy",
1520
+ datePlaceholder: "DD/MM/YYYY",
1491
1521
  clearDate: "Clear date",
1492
1522
  openCalendar: "Open calendar",
1493
1523
  chooseDates: "Choose dates",
1494
- rangePlaceholder: "dd/mm/yyyy \u2013 dd/mm/yyyy",
1524
+ rangePlaceholder: "DD/MM/YYYY \u2013 DD/MM/YYYY",
1495
1525
  clearDates: "Clear dates",
1496
1526
  chooseStart: "Choose the start date",
1497
1527
  chooseEnd: "Choose the end date"
@@ -1515,7 +1545,7 @@ var DATE_TEXT = {
1515
1545
  }
1516
1546
  };
1517
1547
  function dateText(locale) {
1518
- return DATE_TEXT[locale || "th"] || DATE_TEXT.th;
1548
+ return DATE_TEXT[locale || "en"] || DATE_TEXT.en;
1519
1549
  }
1520
1550
  var fmtCache = {};
1521
1551
  var PRESETS = {
@@ -1532,7 +1562,24 @@ function formatDate(iso, opts) {
1532
1562
  const o = opts || {}, d = fromISO(iso);
1533
1563
  if (!d) return "";
1534
1564
  const f = typeof o.format === "object" ? o.format : PRESETS[o.format || "short"];
1535
- return fmt(localeTag(o.locale, o.calendar), f, d).replace(ERA, "");
1565
+ const loc = o.locale || "th";
1566
+ return fmt(localeTag(loc, o.calendar || defaultCalendar(loc)), f, d).replace(ERA, "");
1567
+ }
1568
+ function useFormatDate() {
1569
+ const ctx = useAuraLocale(), locale = ctx.locale || "en", calendar = ctx.calendar;
1570
+ return React17.useCallback(
1571
+ function(iso, opts) {
1572
+ const o = opts || {}, loc = o.locale || locale;
1573
+ return formatDate(
1574
+ iso,
1575
+ Object.assign({}, o, {
1576
+ locale: loc,
1577
+ calendar: o.calendar || (o.locale ? void 0 : calendar) || defaultCalendar(loc)
1578
+ })
1579
+ );
1580
+ },
1581
+ [locale, calendar]
1582
+ );
1536
1583
  }
1537
1584
  var MONTHS = null;
1538
1585
  function monthIndex(word) {
@@ -1572,7 +1619,7 @@ function parseDate(text) {
1572
1619
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
1573
1620
  }
1574
1621
  var Calendar = React17.forwardRef(function Calendar2(props, ref) {
1575
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
1622
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
1576
1623
  const weekStart = props.weekStartsOn == null ? locale === "sv" ? 1 : 0 : props.weekStartsOn;
1577
1624
  let today = /* @__PURE__ */ new Date();
1578
1625
  today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
@@ -1757,6 +1804,7 @@ var Calendar = React17.forwardRef(function Calendar2(props, ref) {
1757
1804
  tabIndex: same(d, focusDate) ? 0 : -1,
1758
1805
  disabled: dis,
1759
1806
  "aria-label": fmt(tag, { weekday: "long", day: "numeric", month: "long", year: "numeric" }, d),
1807
+ suppressHydrationWarning: true,
1760
1808
  "aria-current": same(d, today) ? "date" : void 0,
1761
1809
  "aria-pressed": sel || void 0,
1762
1810
  className: cx(
@@ -1915,7 +1963,7 @@ function DateField(props) {
1915
1963
  }
1916
1964
  var DatePicker = React17.forwardRef(function DatePicker2(props, ref) {
1917
1965
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
1918
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
1966
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
1919
1967
  const st = useMaybeControlled(
1920
1968
  props.value,
1921
1969
  props.defaultValue == null ? null : props.defaultValue,
@@ -2010,7 +2058,7 @@ var DatePicker = React17.forwardRef(function DatePicker2(props, ref) {
2010
2058
  var DateRangePicker = React17.forwardRef(
2011
2059
  function DateRangePicker2(props, ref) {
2012
2060
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
2013
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
2061
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
2014
2062
  const st = useMaybeControlled(
2015
2063
  props.value,
2016
2064
  props.defaultValue || { start: null, end: null },
@@ -4648,8 +4696,8 @@ function createTheme(opts) {
4648
4696
  D["alert-info-bg"] = D["bg-selected"];
4649
4697
  D["alert-info-fg"] = fit(b[300], [D["alert-info-bg"]], 4.5, 1);
4650
4698
  D["alert-info-border"] = mix(ZINC[900], b[400], 0.4);
4651
- D["status-progress-bg"] = b[100];
4652
- D["status-progress-fg"] = L["status-progress-fg"];
4699
+ D["status-progress-bg"] = mix(ZINC[900], b[500], 0.2);
4700
+ D["status-progress-fg"] = fit(b[300], [D["status-progress-bg"]], 4.5, 1);
4653
4701
  if (s) {
4654
4702
  L["accent-dot"] = s[200];
4655
4703
  L["accent-lime"] = s[200];
@@ -4716,6 +4764,7 @@ function createTheme(opts) {
4716
4764
  check("light", "fg-primary", "bg-selected", 4.5, INK, L["bg-selected"]);
4717
4765
  check("dark", "fg-primary", "bg-selected", 4.5, "#ffffff", D["bg-selected"]);
4718
4766
  check("light", "status-progress-fg", "status-progress-bg", 4.5, L["status-progress-fg"], L["status-progress-bg"]);
4767
+ check("dark", "status-progress-fg", "status-progress-bg", 4.5, D["status-progress-fg"], D["status-progress-bg"]);
4719
4768
  check("light", "alert-info-fg", "alert-info-bg", 4.5, L["alert-info-fg"], L["alert-info-bg"]);
4720
4769
  check("dark", "alert-info-fg", "alert-info-bg", 4.5, D["alert-info-fg"], D["alert-info-bg"]);
4721
4770
  if (o.primary === "brand") {
@@ -14,7 +14,8 @@ function ButtonLink(props, ref, Link) {
14
14
  "iconRight",
15
15
  "disabled",
16
16
  "linkComponent",
17
- "href"
17
+ "href",
18
+ "fullWidth"
18
19
  ]);
19
20
  const Tag = disabled ? "a" : Link;
20
21
  return /* @__PURE__ */ React.createElement(
@@ -26,7 +27,7 @@ function ButtonLink(props, ref, Link) {
26
27
  role: disabled ? "link" : void 0,
27
28
  "aria-disabled": disabled || void 0,
28
29
  tabIndex: disabled ? -1 : props.tabIndex,
29
- className: cx("aura-btn", "aura-btn--" + variant, props.className),
30
+ className: cx("aura-btn", "aura-btn--" + variant, props.fullWidth && "aura-btn--full", props.className),
30
31
  onClick: disabled ? void 0 : props.onClick
31
32
  },
32
33
  props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon }) : null,
@@ -43,14 +44,30 @@ const Button = React.forwardRef(
43
44
  const props = all;
44
45
  const variant = props.variant || "primary";
45
46
  const loading = !!props.loading;
46
- const rest = omit(props, ["variant", "className", "children", "type", "icon", "iconRight", "loading", "onClick"]);
47
+ const rest = omit(props, [
48
+ "variant",
49
+ "className",
50
+ "children",
51
+ "type",
52
+ "icon",
53
+ "iconRight",
54
+ "loading",
55
+ "onClick",
56
+ "fullWidth"
57
+ ]);
47
58
  return /* @__PURE__ */ React.createElement(
48
59
  "button",
49
60
  {
50
61
  ...rest,
51
62
  ref,
52
63
  type: props.type || "button",
53
- className: cx("aura-btn", "aura-btn--" + variant, loading && "is-loading", props.className),
64
+ className: cx(
65
+ "aura-btn",
66
+ "aura-btn--" + variant,
67
+ props.fullWidth && "aura-btn--full",
68
+ loading && "is-loading",
69
+ props.className
70
+ ),
54
71
  "aria-busy": loading || void 0,
55
72
  "aria-disabled": loading || void 0,
56
73
  onClick: loading ? function(e) {
@@ -31,11 +31,11 @@ function same(a, b) {
31
31
  }
32
32
  const TAGS = { th: "th-TH", en: "en-GB", sv: "sv-SE" };
33
33
  function defaultCalendar(locale) {
34
- return locale === "sv" ? "gregory" : "buddhist";
34
+ return locale === "th" ? "buddhist" : "gregory";
35
35
  }
36
36
  function localeTag(locale, calendar) {
37
37
  const cal = calendar || defaultCalendar(locale);
38
- return (TAGS[locale || "th"] || "th-TH") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
38
+ return (TAGS[locale || "en"] || "en-GB") + "-u-ca-" + (cal === "gregory" ? "gregory" : "buddhist");
39
39
  }
40
40
  const DATE_TEXT = {
41
41
  th: {
@@ -63,11 +63,11 @@ const DATE_TEXT = {
63
63
  today: "Today",
64
64
  clear: "Clear",
65
65
  chooseDate: "Choose date",
66
- datePlaceholder: "dd/mm/yyyy",
66
+ datePlaceholder: "DD/MM/YYYY",
67
67
  clearDate: "Clear date",
68
68
  openCalendar: "Open calendar",
69
69
  chooseDates: "Choose dates",
70
- rangePlaceholder: "dd/mm/yyyy \u2013 dd/mm/yyyy",
70
+ rangePlaceholder: "DD/MM/YYYY \u2013 DD/MM/YYYY",
71
71
  clearDates: "Clear dates",
72
72
  chooseStart: "Choose the start date",
73
73
  chooseEnd: "Choose the end date"
@@ -91,7 +91,7 @@ const DATE_TEXT = {
91
91
  }
92
92
  };
93
93
  function dateText(locale) {
94
- return DATE_TEXT[locale || "th"] || DATE_TEXT.th;
94
+ return DATE_TEXT[locale || "en"] || DATE_TEXT.en;
95
95
  }
96
96
  const fmtCache = {};
97
97
  const PRESETS = {
@@ -108,7 +108,24 @@ function formatDate(iso, opts) {
108
108
  const o = opts || {}, d = fromISO(iso);
109
109
  if (!d) return "";
110
110
  const f = typeof o.format === "object" ? o.format : PRESETS[o.format || "short"];
111
- return fmt(localeTag(o.locale, o.calendar), f, d).replace(ERA, "");
111
+ const loc = o.locale || "th";
112
+ return fmt(localeTag(loc, o.calendar || defaultCalendar(loc)), f, d).replace(ERA, "");
113
+ }
114
+ function useFormatDate() {
115
+ const ctx = useAuraLocale(), locale = ctx.locale || "en", calendar = ctx.calendar;
116
+ return React.useCallback(
117
+ function(iso, opts) {
118
+ const o = opts || {}, loc = o.locale || locale;
119
+ return formatDate(
120
+ iso,
121
+ Object.assign({}, o, {
122
+ locale: loc,
123
+ calendar: o.calendar || (o.locale ? void 0 : calendar) || defaultCalendar(loc)
124
+ })
125
+ );
126
+ },
127
+ [locale, calendar]
128
+ );
112
129
  }
113
130
  let MONTHS = null;
114
131
  function monthIndex(word) {
@@ -148,7 +165,7 @@ function parseDate(text) {
148
165
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
149
166
  }
150
167
  const Calendar = React.forwardRef(function Calendar2(props, ref) {
151
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
168
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), tag = localeTag(locale, calendar);
152
169
  const weekStart = props.weekStartsOn == null ? locale === "sv" ? 1 : 0 : props.weekStartsOn;
153
170
  let today = /* @__PURE__ */ new Date();
154
171
  today = new Date(today.getFullYear(), today.getMonth(), today.getDate());
@@ -333,6 +350,7 @@ const Calendar = React.forwardRef(function Calendar2(props, ref) {
333
350
  tabIndex: same(d, focusDate) ? 0 : -1,
334
351
  disabled: dis,
335
352
  "aria-label": fmt(tag, { weekday: "long", day: "numeric", month: "long", year: "numeric" }, d),
353
+ suppressHydrationWarning: true,
336
354
  "aria-current": same(d, today) ? "date" : void 0,
337
355
  "aria-pressed": sel || void 0,
338
356
  className: cx(
@@ -491,7 +509,7 @@ function DateField(props) {
491
509
  }
492
510
  const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
493
511
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
494
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
512
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
495
513
  const st = useMaybeControlled(
496
514
  props.value,
497
515
  props.defaultValue == null ? null : props.defaultValue,
@@ -586,7 +604,7 @@ const DatePicker = React.forwardRef(function DatePicker2(props, ref) {
586
604
  const DateRangePicker = React.forwardRef(
587
605
  function DateRangePicker2(props, ref) {
588
606
  const auto = uid(), id = props.id || auto, dialogId = id + "-cal";
589
- const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "th", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
607
+ const ctx = useAuraLocale(), locale = props.locale || ctx.locale || "en", calendar = props.calendar || ctx.calendar || defaultCalendar(locale), dt = dateText(locale);
590
608
  const st = useMaybeControlled(
591
609
  props.value,
592
610
  props.defaultValue || { start: null, end: null },
@@ -706,5 +724,6 @@ export {
706
724
  formatDate,
707
725
  fromISO,
708
726
  parseDate,
709
- toISO
727
+ toISO,
728
+ useFormatDate
710
729
  };
@@ -5,25 +5,37 @@ const Switch = React.forwardRef(function Switch2(props, ref) {
5
5
  const auto = uid(), id = props.id || auto;
6
6
  const st = useMaybeControlled(props.checked, !!props.defaultChecked, props.onChange);
7
7
  const on = !!st[0];
8
- return /* @__PURE__ */ React.createElement("div", { className: cx("aura-switch-row", props.disabled && "is-disabled", props.className) }, /* @__PURE__ */ React.createElement(
9
- "button",
8
+ return /* @__PURE__ */ React.createElement(
9
+ "div",
10
10
  {
11
- ref,
12
- type: "button",
13
- role: "switch",
14
- id,
15
- "aria-checked": on,
16
- disabled: props.disabled,
17
- "aria-labelledby": props.label ? id + "-label" : void 0,
18
- "aria-label": props.label ? void 0 : props["aria-label"],
19
- "aria-describedby": props.description ? id + "-desc" : void 0,
20
- className: cx("aura-switch", on && "is-on"),
21
- onClick: function() {
11
+ className: cx("aura-switch-row", props.disabled && "is-disabled", props.className),
12
+ onClick: function(e) {
13
+ const t = e.target;
14
+ if (props.disabled || t.closest("button, label, a, input")) return;
22
15
  st[1](!on);
23
16
  }
24
17
  },
25
- /* @__PURE__ */ React.createElement("span", { className: "aura-switch__thumb" })
26
- ), props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null);
18
+ /* @__PURE__ */ React.createElement(
19
+ "button",
20
+ {
21
+ ref,
22
+ type: "button",
23
+ role: "switch",
24
+ id,
25
+ "aria-checked": on,
26
+ disabled: props.disabled,
27
+ "aria-labelledby": props.label ? id + "-label" : void 0,
28
+ "aria-label": props.label ? void 0 : props["aria-label"],
29
+ "aria-describedby": props.description ? id + "-desc" : void 0,
30
+ className: cx("aura-switch", on && "is-on"),
31
+ onClick: function() {
32
+ st[1](!on);
33
+ }
34
+ },
35
+ /* @__PURE__ */ React.createElement("span", { className: "aura-switch__thumb" })
36
+ ),
37
+ props.label ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__text" }, /* @__PURE__ */ React.createElement("label", { className: "aura-choice__label", id: id + "-label", htmlFor: id }, props.label), props.description ? /* @__PURE__ */ React.createElement("span", { className: "aura-choice__desc", id: id + "-desc" }, props.description) : null) : null
38
+ );
27
39
  });
28
40
  export {
29
41
  Switch
package/dist/esm/index.js CHANGED
@@ -13,7 +13,7 @@ import { Select } from "./Select.js";
13
13
  import { RadioGroup } from "./RadioGroup.js";
14
14
  import { Switch } from "./Switch.js";
15
15
  import { Combobox, defaultFilter } from "./Combobox.js";
16
- import { DatePicker, DateRangePicker, Calendar, formatDate, parseDate } from "./DatePicker.js";
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
19
  import { Tooltip } from "./Tooltip.js";
@@ -118,5 +118,6 @@ export {
118
118
  useAuraLocale,
119
119
  useBreakpoint,
120
120
  useColorScheme,
121
+ useFormatDate,
121
122
  useResponsive
122
123
  };
package/dist/esm/theme.js CHANGED
@@ -152,8 +152,8 @@ function createTheme(opts) {
152
152
  D["alert-info-bg"] = D["bg-selected"];
153
153
  D["alert-info-fg"] = fit(b[300], [D["alert-info-bg"]], 4.5, 1);
154
154
  D["alert-info-border"] = mix(ZINC[900], b[400], 0.4);
155
- D["status-progress-bg"] = b[100];
156
- D["status-progress-fg"] = L["status-progress-fg"];
155
+ D["status-progress-bg"] = mix(ZINC[900], b[500], 0.2);
156
+ D["status-progress-fg"] = fit(b[300], [D["status-progress-bg"]], 4.5, 1);
157
157
  if (s) {
158
158
  L["accent-dot"] = s[200];
159
159
  L["accent-lime"] = s[200];
@@ -220,6 +220,7 @@ function createTheme(opts) {
220
220
  check("light", "fg-primary", "bg-selected", 4.5, INK, L["bg-selected"]);
221
221
  check("dark", "fg-primary", "bg-selected", 4.5, "#ffffff", D["bg-selected"]);
222
222
  check("light", "status-progress-fg", "status-progress-bg", 4.5, L["status-progress-fg"], L["status-progress-bg"]);
223
+ check("dark", "status-progress-fg", "status-progress-bg", 4.5, D["status-progress-fg"], D["status-progress-bg"]);
223
224
  check("light", "alert-info-fg", "alert-info-bg", 4.5, L["alert-info-fg"], L["alert-info-bg"]);
224
225
  check("dark", "alert-info-fg", "alert-info-bg", 4.5, D["alert-info-fg"], D["alert-info-bg"]);
225
226
  if (o.primary === "brand") {
package/dist/index.d.ts CHANGED
@@ -14,6 +14,8 @@ export interface ButtonProps extends React$1.ButtonHTMLAttributes<HTMLButtonElem
14
14
  iconRight?: IconInput;
15
15
  /** Shows a spinner in place of the leading icon, sets aria-busy and swallows clicks. Keeps the label and width. */
16
16
  loading?: boolean;
17
+ /** Fills its container and lets a long label wrap onto more lines (at least 44px tall) — phones, Thai and Swedish labels. */
18
+ fullWidth?: boolean;
17
19
  }
18
20
  /** A link that looks like a Button: give `Button` an `href` and it renders an `<a>` (navigation, not actions). */
19
21
  export interface ButtonLinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLAnchorElement>, "children"> {
@@ -28,6 +30,8 @@ export interface ButtonLinkProps extends Omit<React$1.AnchorHTMLAttributes<HTMLA
28
30
  iconRight?: IconInput;
29
31
  /** Looks unavailable and can't be followed: no href, `aria-disabled`, out of the Tab order. */
30
32
  disabled?: boolean;
33
+ /** Fills its container and lets a long label wrap (at least 44px tall). */
34
+ fullWidth?: boolean;
31
35
  /** A router's link to render instead of `<a>`, e.g. `Link` from `next/link` (client-side navigation). It gets `href`, `className`, the children and the ref. */
32
36
  linkComponent?: React$1.ElementType;
33
37
  }
@@ -174,9 +178,9 @@ export interface SurfaceProps extends React$1.HTMLAttributes<HTMLElement> {
174
178
  }
175
179
  /** Sets the language of built-in labels (pagination, close buttons, empty states…) and the default date display for everything inside. */
176
180
  export interface AuraProviderProps {
177
- /** Built-in labels and date display. `sv` = Swedish labels, `sv-SE` dates, Monday weeks and the Gregorian calendar. Default `en` strings when there is no provider (dates still default to Thai / พ.ศ.). */
181
+ /** Built-in labels and date display. `sv` = Swedish labels, `sv-SE` dates, Monday weeks and the Gregorian calendar. Without a provider everything is English: labels, `en-GB` dates, Gregorian years. */
178
182
  locale?: "th" | "en" | "sv";
179
- /** Default calendar for DatePicker / DateRangePicker / Calendar / formatDate callers that read it. Unset: `buddhist` for `th` and `en` (unchanged), `gregory` for `sv`. */
183
+ /** Default calendar for DatePicker / DateRangePicker / Calendar / formatDate callers that read it. Unset: `buddhist` (พ.ศ.) for `th`, `gregory` for `en` and `sv`. */
180
184
  calendar?: "buddhist" | "gregory";
181
185
  /** Override individual strings. */
182
186
  strings?: Partial<Record<string, string | ((...args: any[]) => string)>>;
@@ -580,9 +584,9 @@ export interface SegmentedControlProps {
580
584
  /** ISO date string, `YYYY-MM-DD` (Gregorian — the era is display only). */
581
585
  export type ISODate = string;
582
586
  export interface DateDisplayOptions {
583
- /** `th` (default), `en` or `sv`. */
587
+ /** Components: the AuraProvider's locale, else `en`. `formatDate()` (no provider to read): `th` unless given. */
584
588
  locale?: "th" | "en" | "sv";
585
- /** `buddhist` (พ.ศ.; default for th and en) or `gregory` (ค.ศ.; default for sv). */
589
+ /** `buddhist` (พ.ศ.; default for th) or `gregory` (ค.ศ.; default for en and sv). */
586
590
  calendar?: "buddhist" | "gregory";
587
591
  }
588
592
  export interface CalendarProps extends DateDisplayOptions {
@@ -962,10 +966,13 @@ export type FormatDateOptions = DateDisplayOptions & {
962
966
  };
963
967
  /** Format an ISO date for display, e.g. "18 ก.ย. 2569" (th, Buddhist) or "18 Sept 2026" (en, Gregorian). */
964
968
  export declare function formatDate(iso: ISODate | null | undefined, opts?: FormatDateOptions): string;
969
+ /** formatDate bound to the nearest AuraProvider: its locale and calendar (English, Gregorian without one).
970
+ * Options you pass still win. Use it in components; plain formatDate() stays for code outside React. */
971
+ export declare function useFormatDate(): (iso: ISODate | null | undefined, opts?: FormatDateOptions) => string;
965
972
  /** Parse typed text: dd/mm/yyyy (Buddhist years ≥ 2400 are converted), d-m-yyyy, d.m.yyyy, yyyy-mm-dd or '18 ก.ย. 2569' / '18 Sep 2026'. */
966
973
  export declare function parseDate(text: string | null | undefined): ISODate | null;
967
974
  export declare const Calendar: React$1.ForwardRefExoticComponent<CalendarProps & React$1.RefAttributes<HTMLDivElement>>;
968
- /** Typed date field + calendar popover. Shows Buddhist-era dates (18 ก.ย. 2569); accepts dd/mm/yyyy in พ.ศ. or ค.ศ. and yyyy-mm-dd. */
975
+ /** Typed date field + calendar popover. English / Gregorian unless a locale is set; `th` shows Buddhist-era dates (18 ก.ย. 2569) and accepts พ.ศ. or ค.ศ. years. The value is always a Gregorian ISO date. */
969
976
  export declare const DatePicker: React$1.ForwardRefExoticComponent<DatePickerProps & React$1.RefAttributes<HTMLInputElement>>;
970
977
  export declare const DateRangePicker: React$1.ForwardRefExoticComponent<DateRangePickerProps & React$1.RefAttributes<HTMLInputElement>>;
971
978
  export declare const Alert: React$1.ForwardRefExoticComponent<AlertProps & React$1.RefAttributes<HTMLDivElement>>;
package/dist/styles.css CHANGED
@@ -28,6 +28,11 @@
28
28
  .aura-btn:active { transform: scale(0.98); }
29
29
  .aura-btn:disabled, a.aura-btn[aria-disabled='true'] { opacity: var(--aura-disabled-opacity); pointer-events: none; }
30
30
  a.aura-btn { text-decoration: none; }
31
+ /* fullWidth: fills the row and wraps a long label (Thai and Swedish run ~30% longer) instead of overflowing. */
32
+ .aura-btn--full {
33
+ width: 100%; height: auto; min-height: var(--aura-button-height); white-space: normal; flex-shrink: 1;
34
+ padding-block: var(--aura-space-2); line-height: 1.35; text-align: center; overflow-wrap: anywhere;
35
+ }
31
36
  .aura-btn.is-loading { cursor: progress; }
32
37
  .aura-btn.is-loading:active { transform: none; }
33
38
 
@@ -243,6 +248,11 @@ a.aura-btn { text-decoration: none; }
243
248
  .aura-icon-btn--danger:hover { background: var(--aura-alert-danger-bg); }
244
249
  .aura-icon-btn:disabled { opacity: var(--aura-disabled-opacity); pointer-events: none; }
245
250
  .aura-icon-btn:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: var(--aura-focus-ring-offset); }
251
+ /* Touch: same 32px look, a 44px hit area around it (WCAG 2.5.5). */
252
+ @media (pointer: coarse) {
253
+ .aura-icon-btn { position: relative; }
254
+ .aura-icon-btn::after { content: ''; position: absolute; top: 50%; left: 50%; width: max(100%, var(--aura-touch-target)); height: max(100%, var(--aura-touch-target)); transform: translate(-50%, -50%); }
255
+ }
246
256
 
247
257
  /* Skeleton */
248
258
  @keyframes aura-pulse { 50% { opacity: 0.6; } }
@@ -361,9 +371,11 @@ a.aura-btn { text-decoration: none; }
361
371
  min-height: var(--aura-input-height);
362
372
  background: var(--aura-bg-input); color: var(--aura-fg-primary);
363
373
  border: 1px solid var(--aura-border-control); border-radius: var(--aura-radius-md);
364
- font-family: var(--font-sans); font-size: 14px;
374
+ font-family: var(--font-sans); font-size: var(--aura-input-font-size);
365
375
  transition: border-color var(--aura-duration-fast) var(--aura-ease);
366
376
  }
377
+ /* Phones: iOS Safari zooms into any focused control under 16px and does not zoom back. */
378
+ @media (max-width: 639.98px) { .aura-input { font-size: max(16px, var(--aura-input-font-size)); } }
367
379
  .aura-input:hover { border-color: var(--aura-fg-secondary); }
368
380
  .aura-input:focus-within { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; border-color: var(--aura-border-strong); }
369
381
  .aura-input__control {
@@ -859,7 +871,7 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
859
871
  .aura-upload__name { font-size: 13px; font-weight: 500; color: var(--aura-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
860
872
  .aura-upload__sub { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--aura-fg-secondary); }
861
873
  .aura-upload__item.is-error .aura-upload__sub { color: var(--aura-fg-danger); }
862
- .aura-upload__bar { display: block; height: 4px; border-radius: var(--aura-radius-full); background: var(--aura-bg-skeleton); overflow: hidden; }
874
+ .aura-upload__bar { display: block; height: 4px; border-radius: var(--aura-radius-full); background: var(--aura-progress-track); box-shadow: inset 0 0 0 1px var(--aura-progress-track-edge); overflow: hidden; }
863
875
  .aura-upload__bar > span { display: block; height: 100%; background: var(--aura-fg-accent); transition: width var(--aura-duration-base) var(--aura-ease); }
864
876
 
865
877
  /* ---------- Checkbox with a visible label ---------- */
@@ -908,7 +920,7 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
908
920
  .aura-progress__head { display: flex; justify-content: space-between; gap: var(--aura-space-2); font-size: 13px; }
909
921
  .aura-progress__label { font-weight: 500; color: var(--aura-fg-primary); }
910
922
  .aura-progress__value { color: var(--aura-fg-secondary); font-variant-numeric: tabular-nums; }
911
- .aura-progress__track { position: relative; height: 8px; border-radius: var(--aura-radius-full); background: var(--aura-bg-skeleton); overflow: hidden; }
923
+ .aura-progress__track { position: relative; height: 8px; border-radius: var(--aura-radius-full); background: var(--aura-progress-track); box-shadow: inset 0 0 0 1px var(--aura-progress-track-edge); overflow: hidden; }
912
924
  .aura-progress.is-sm .aura-progress__track { height: 4px; }
913
925
  .aura-progress__bar { position: absolute; inset: 0 auto 0 0; border-radius: inherit; background: var(--aura-fg-accent); transition: width var(--aura-duration-base) var(--aura-ease); }
914
926
  .aura-progress--success .aura-progress__bar { background: var(--aura-fg-positive); }
@@ -1116,3 +1128,16 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1116
1128
  .aura-tabs__list { gap: var(--aura-space-4); }
1117
1129
  }
1118
1130
  @keyframes aura-sheet-in { from { transform: translateY(24px); opacity: 0; } }
1131
+
1132
+ /* Touch (pointer: coarse): Radio, Checkbox and Switch rows are at least 44px tall (WCAG 2.5.5), padded above and
1133
+ * below a one-line 20px label; the whole row is the target (label rows natively, Switch rows by a click handler). */
1134
+ @media (pointer: coarse) {
1135
+ .aura-choice, .aura-check--labelled, .aura-switch-row {
1136
+ box-sizing: border-box; min-height: var(--aura-touch-target);
1137
+ padding-block: calc((var(--aura-touch-target) - 20px) / 2);
1138
+ }
1139
+ .aura-check--labelled .aura-check__input { top: calc((var(--aura-touch-target) - 20px) / 2 + 3px); }
1140
+ .aura-switch-row:not(.is-disabled) { cursor: pointer; }
1141
+ .aura-radio-group__list { gap: 0; }
1142
+ .aura-radio-group__list.is-horizontal { gap: 0 var(--aura-space-6); }
1143
+ }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "4.10.0",
3
+ "version": "4.12.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",