@jirawatpyk/aura-react 5.1.1 → 5.2.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.
@@ -14,13 +14,20 @@ import {
14
14
  import { useDensity, useStrings } from "./locale.js";
15
15
  import { createPortal } from "react-dom";
16
16
  function position(anchor, pop, placement) {
17
- const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.offsetHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
17
+ const r = anchor.getBoundingClientRect(), pw = pop.offsetWidth, ph = pop.scrollHeight, vw = window.innerWidth, vh = window.innerHeight, gap = 6;
18
18
  let side = (placement || "bottom-start").split("-")[0], align = (placement || "bottom-start").split("-")[1] || "start";
19
19
  if (side === "bottom" && r.bottom + gap + ph > vh - 8 && r.top - gap - ph > 8) side = "top";
20
20
  else if (side === "top" && r.top - gap - ph < 8 && r.bottom + gap + ph < vh - 8) side = "bottom";
21
- const top = side === "top" ? r.top - gap - ph : r.bottom + gap;
21
+ const below = vh - r.bottom - gap - 8, above = r.top - gap - 8;
22
+ let maxHeight;
23
+ if (ph > below && ph > above) {
24
+ side = above > below ? "top" : "bottom";
25
+ maxHeight = Math.max(120, side === "top" ? above : below);
26
+ }
27
+ const h = maxHeight ? Math.min(ph, maxHeight) : ph;
28
+ const top = side === "top" ? r.top - gap - h : r.bottom + gap;
22
29
  const left = align === "end" ? r.right - pw : align === "center" ? r.left + r.width / 2 - pw / 2 : r.left;
23
- return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side };
30
+ return { top: Math.max(8, top), left: Math.max(8, Math.min(left, vw - pw - 8)), side, maxHeight };
24
31
  }
25
32
  const Popover = React.forwardRef(function Popover2(props, ref) {
26
33
  const t = useStrings();
@@ -100,7 +107,11 @@ const Popover = React.forwardRef(function Popover2(props, ref) {
100
107
  },
101
108
  className: cx("aura-popover", pos[0] && "is-" + pos[0].side, props.className),
102
109
  style: Object.assign(
103
- { top: pos[0] ? pos[0].top : -9999, left: pos[0] ? pos[0].left : -9999 },
110
+ {
111
+ top: pos[0] ? pos[0].top : -9999,
112
+ left: pos[0] ? pos[0].left : -9999,
113
+ maxHeight: pos[0] && pos[0].maxHeight ? pos[0].maxHeight : void 0
114
+ },
104
115
  props.width ? { width: props.width } : null
105
116
  ),
106
117
  onKeyDown: function(e) {
@@ -113,7 +113,8 @@ const TimePicker = React.forwardRef(function TimePicker2(props, ref) {
113
113
  return true;
114
114
  }
115
115
  if (blocked(v)) {
116
- errState[1](t.timeOutOfRange(fromMin(lo), fromMin(hi)));
116
+ const n = toMin(v);
117
+ errState[1](n != null && n >= lo && n <= hi ? t.timeUnavailable : t.timeOutOfRange(fromMin(lo), fromMin(hi)));
117
118
  return false;
118
119
  }
119
120
  errState[1](null);
package/dist/esm/dates.js CHANGED
@@ -4,20 +4,29 @@ const pad = function(n) {
4
4
  return (n < 10 ? "0" : "") + n;
5
5
  };
6
6
  function toISO(d) {
7
- return d ? d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) : null;
7
+ if (!d) return null;
8
+ const y = String(d.getFullYear());
9
+ return ("000" + y).slice(-Math.max(4, y.length)) + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
10
+ }
11
+ function makeDate(y, monthIndex2, day) {
12
+ const d = new Date(2e3, 0, 1);
13
+ d.setFullYear(y, monthIndex2, day);
14
+ return d;
8
15
  }
9
16
  function fromISO(s) {
10
17
  if (!s) return null;
11
18
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
12
- return m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;
19
+ if (!m) return null;
20
+ const d = makeDate(+m[1], +m[2] - 1, +m[3]);
21
+ return d.getMonth() === +m[2] - 1 && d.getDate() === +m[3] ? d : null;
13
22
  }
14
23
  function addDays(d, n) {
15
- return new Date(d.getFullYear(), d.getMonth(), d.getDate() + n);
24
+ return makeDate(d.getFullYear(), d.getMonth(), d.getDate() + n);
16
25
  }
17
26
  function addMonths(d, n) {
18
- const t = new Date(d.getFullYear(), d.getMonth() + n, 1);
19
- const last = new Date(t.getFullYear(), t.getMonth() + 1, 0).getDate();
20
- return new Date(t.getFullYear(), t.getMonth(), Math.min(d.getDate(), last));
27
+ const t = makeDate(d.getFullYear(), d.getMonth() + n, 1);
28
+ const last = makeDate(t.getFullYear(), t.getMonth() + 1, 0).getDate();
29
+ return makeDate(t.getFullYear(), t.getMonth(), Math.min(d.getDate(), last));
21
30
  }
22
31
  function same(a, b) {
23
32
  return !!a && !!b && a.getFullYear() === b.getFullYear() && a.getMonth() === b.getMonth() && a.getDate() === b.getDate();
@@ -125,7 +134,15 @@ function monthIndex(word) {
125
134
  return MONTHS[k] != null ? MONTHS[k] : -1;
126
135
  }
127
136
  function parseDate(text) {
128
- const s = String(text || "").trim();
137
+ const raw = String(text || "").trim();
138
+ const gregorian = /ค\.ศ\.|\d\s*(AD|CE)\b|^\s*(AD|CE)\s/i.test(raw);
139
+ let s = raw.replace(/พ\.ศ\.|ค\.ศ\./g, " ").replace(/(\d)\s*(BE|AD|CE)\b/gi, "$1 ").replace(/^\s*(BE|AD|CE)\s+/i, " ").replace(/\s+/g, " ").trim();
140
+ const eight = /^(\d{2})(\d{2})(\d{4})$/.exec(s);
141
+ if (eight) {
142
+ const a = +eight[3] >= 1e3 ? parseDate(eight[1] + "/" + eight[2] + "/" + eight[3] + (gregorian ? " \u0E04.\u0E28." : "")) : null;
143
+ if (a) return a;
144
+ s = s.slice(0, 4) + "-" + s.slice(4, 6) + "-" + s.slice(6);
145
+ }
129
146
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(s), y, mo, d;
130
147
  if (m) {
131
148
  y = +m[1];
@@ -135,13 +152,13 @@ function parseDate(text) {
135
152
  d = +m[1];
136
153
  mo = +m[2];
137
154
  y = +m[3];
138
- } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s.replace(ERA, "").trim())) && monthIndex(m[2]) >= 0) {
155
+ } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s)) && monthIndex(m[2]) >= 0) {
139
156
  d = +m[1];
140
157
  mo = monthIndex(m[2]) + 1;
141
158
  y = +m[3];
142
159
  } else return null;
143
- if (y >= 2400) y -= 543;
144
- const dt = new Date(y, mo - 1, d);
160
+ if (y >= 2400 && !gregorian) y -= 543;
161
+ const dt = makeDate(y, mo - 1, d);
145
162
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
146
163
  }
147
164
  function todayIn(timeZone) {
@@ -173,6 +190,7 @@ export {
173
190
  formatDate,
174
191
  fromISO,
175
192
  localeTag,
193
+ makeDate,
176
194
  monthIndex,
177
195
  pad,
178
196
  parseDate,
@@ -74,6 +74,12 @@ const STRINGS = {
74
74
  page: function(p, total) {
75
75
  return "Page " + p + " of " + total;
76
76
  },
77
+ rangeOpen: function(a, b) {
78
+ return a + "\u2013" + b + " of many";
79
+ },
80
+ pageOpen: function(p) {
81
+ return "Page " + p;
82
+ },
77
83
  prevPage: "Previous page",
78
84
  nextPage: "Next page",
79
85
  totals: "Totals",
@@ -87,6 +93,7 @@ const STRINGS = {
87
93
  timeOutOfRange: function(a, b) {
88
94
  return "Choose a time between " + a + " and " + b;
89
95
  },
96
+ timeUnavailable: "That time isn't available. Choose another.",
90
97
  dropFiles: "Drag files here or",
91
98
  browse: "Choose files",
92
99
  browseOne: "Choose a file",
@@ -188,6 +195,12 @@ const STRINGS = {
188
195
  page: function(p, total) {
189
196
  return "\u0E2B\u0E19\u0E49\u0E32 " + p + " / " + total;
190
197
  },
198
+ rangeOpen: function(a, b) {
199
+ return a + "\u2013" + b + " \u0E08\u0E32\u0E01\u0E2B\u0E25\u0E32\u0E22\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
200
+ },
201
+ pageOpen: function(p) {
202
+ return "\u0E2B\u0E19\u0E49\u0E32 " + p;
203
+ },
191
204
  prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
192
205
  nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
193
206
  totals: "\u0E23\u0E27\u0E21",
@@ -201,6 +214,7 @@ const STRINGS = {
201
214
  timeOutOfRange: function(a, b) {
202
215
  return "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07 " + a + "\u2013" + b + " \u0E19.";
203
216
  },
217
+ timeUnavailable: "\u0E40\u0E27\u0E25\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E27\u0E48\u0E32\u0E07 \u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E2D\u0E37\u0E48\u0E19",
204
218
  dropFiles: "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D",
205
219
  browse: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
206
220
  browseOne: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
@@ -302,6 +316,12 @@ const STRINGS = {
302
316
  page: function(p, total) {
303
317
  return "Sida " + p + " av " + total;
304
318
  },
319
+ rangeOpen: function(a, b) {
320
+ return a + "\u2013" + b + " av m\xE5nga";
321
+ },
322
+ pageOpen: function(p) {
323
+ return "Sida " + p;
324
+ },
305
325
  prevPage: "F\xF6reg\xE5ende sida",
306
326
  nextPage: "N\xE4sta sida",
307
327
  totals: "Summa",
@@ -315,6 +335,7 @@ const STRINGS = {
315
335
  timeOutOfRange: function(a, b) {
316
336
  return "V\xE4lj en tid mellan " + a + " och " + b;
317
337
  },
338
+ timeUnavailable: "Den tiden \xE4r inte ledig. V\xE4lj en annan.",
318
339
  dropFiles: "Dra filer hit eller",
319
340
  browse: "V\xE4lj filer",
320
341
  browseOne: "V\xE4lj en fil",
package/dist/esm/text.js CHANGED
@@ -19,6 +19,7 @@ function parseTime(text) {
19
19
  const m = /^(\d{1,2})(?:[:.](\d{2}))?$/.exec(s) || /^(\d{1,2})(\d{2})$/.exec(s);
20
20
  if (!m) return null;
21
21
  let hh = +m[1], mm = m[2] ? +m[2] : 0;
22
+ if ((am || pm) && (hh < 1 || hh > 12)) return null;
22
23
  if (pm && hh < 12) hh += 12;
23
24
  if (am && hh === 12) hh = 0;
24
25
  if (hh > 23 || mm > 59) return null;
package/dist/index.d.cts CHANGED
@@ -1381,6 +1381,9 @@ export interface AuraStrings {
1381
1381
  loadingRows: string;
1382
1382
  range: (a: number | string, b: number | string, total: number) => string;
1383
1383
  page: (p: number, total: number) => string;
1384
+ /** Server paging without totalRows (5.2): the range and page when the total isn't known. */
1385
+ rangeOpen: (a: number | string, b: number | string) => string;
1386
+ pageOpen: (p: number) => string;
1384
1387
  prevPage: string;
1385
1388
  nextPage: string;
1386
1389
  rowCount: (c: number) => string;
@@ -1390,6 +1393,8 @@ export interface AuraStrings {
1390
1393
  timePlaceholder: string;
1391
1394
  timeInvalid: string;
1392
1395
  timeOutOfRange: (a: number | string, b: number | string) => string;
1396
+ /** A time inside the range that isTimeDisabled refuses (5.2). */
1397
+ timeUnavailable: string;
1393
1398
  dropFiles: string;
1394
1399
  browse: string;
1395
1400
  browseOne: string;
package/dist/index.d.ts CHANGED
@@ -1381,6 +1381,9 @@ export interface AuraStrings {
1381
1381
  loadingRows: string;
1382
1382
  range: (a: number | string, b: number | string, total: number) => string;
1383
1383
  page: (p: number, total: number) => string;
1384
+ /** Server paging without totalRows (5.2): the range and page when the total isn't known. */
1385
+ rangeOpen: (a: number | string, b: number | string) => string;
1386
+ pageOpen: (p: number) => string;
1384
1387
  prevPage: string;
1385
1388
  nextPage: string;
1386
1389
  rowCount: (c: number) => string;
@@ -1390,6 +1393,8 @@ export interface AuraStrings {
1390
1393
  timePlaceholder: string;
1391
1394
  timeInvalid: string;
1392
1395
  timeOutOfRange: (a: number | string, b: number | string) => string;
1396
+ /** A time inside the range that isTimeDisabled refuses (5.2). */
1397
+ timeUnavailable: string;
1393
1398
  dropFiles: string;
1394
1399
  browse: string;
1395
1400
  browseOne: string;
@@ -43,12 +43,21 @@ var pad = function(n2) {
43
43
  return (n2 < 10 ? "0" : "") + n2;
44
44
  };
45
45
  function toISO(d) {
46
- return d ? d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) : null;
46
+ if (!d) return null;
47
+ const y = String(d.getFullYear());
48
+ return ("000" + y).slice(-Math.max(4, y.length)) + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
49
+ }
50
+ function makeDate(y, monthIndex2, day) {
51
+ const d = new Date(2e3, 0, 1);
52
+ d.setFullYear(y, monthIndex2, day);
53
+ return d;
47
54
  }
48
55
  function fromISO(s) {
49
56
  if (!s) return null;
50
57
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
51
- return m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;
58
+ if (!m) return null;
59
+ const d = makeDate(+m[1], +m[2] - 1, +m[3]);
60
+ return d.getMonth() === +m[2] - 1 && d.getDate() === +m[3] ? d : null;
52
61
  }
53
62
  var TAGS = { th: "th-TH", en: "en-GB", sv: "sv-SE" };
54
63
  function defaultCalendar(locale) {
@@ -94,7 +103,15 @@ function monthIndex(word) {
94
103
  return MONTHS[k] != null ? MONTHS[k] : -1;
95
104
  }
96
105
  function parseDate(text) {
97
- const s = String(text || "").trim();
106
+ const raw = String(text || "").trim();
107
+ const gregorian = /ค\.ศ\.|\d\s*(AD|CE)\b|^\s*(AD|CE)\s/i.test(raw);
108
+ let s = raw.replace(/พ\.ศ\.|ค\.ศ\./g, " ").replace(/(\d)\s*(BE|AD|CE)\b/gi, "$1 ").replace(/^\s*(BE|AD|CE)\s+/i, " ").replace(/\s+/g, " ").trim();
109
+ const eight = /^(\d{2})(\d{2})(\d{4})$/.exec(s);
110
+ if (eight) {
111
+ const a = +eight[3] >= 1e3 ? parseDate(eight[1] + "/" + eight[2] + "/" + eight[3] + (gregorian ? " \u0E04.\u0E28." : "")) : null;
112
+ if (a) return a;
113
+ s = s.slice(0, 4) + "-" + s.slice(4, 6) + "-" + s.slice(6);
114
+ }
98
115
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(s), y, mo, d;
99
116
  if (m) {
100
117
  y = +m[1];
@@ -104,13 +121,13 @@ function parseDate(text) {
104
121
  d = +m[1];
105
122
  mo = +m[2];
106
123
  y = +m[3];
107
- } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s.replace(ERA, "").trim())) && monthIndex(m[2]) >= 0) {
124
+ } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s)) && monthIndex(m[2]) >= 0) {
108
125
  d = +m[1];
109
126
  mo = monthIndex(m[2]) + 1;
110
127
  y = +m[3];
111
128
  } else return null;
112
- if (y >= 2400) y -= 543;
113
- const dt = new Date(y, mo - 1, d);
129
+ if (y >= 2400 && !gregorian) y -= 543;
130
+ const dt = makeDate(y, mo - 1, d);
114
131
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
115
132
  }
116
133
  function todayIn(timeZone) {
@@ -149,6 +166,7 @@ function parseTime(text) {
149
166
  const m = /^(\d{1,2})(?:[:.](\d{2}))?$/.exec(s) || /^(\d{1,2})(\d{2})$/.exec(s);
150
167
  if (!m) return null;
151
168
  let hh = +m[1], mm = m[2] ? +m[2] : 0;
169
+ if ((am || pm) && (hh < 1 || hh > 12)) return null;
152
170
  if (pm && hh < 12) hh += 12;
153
171
  if (am && hh === 12) hh = 0;
154
172
  if (hh > 23 || mm > 59) return null;
@@ -244,6 +262,12 @@ var STRINGS = {
244
262
  page: function(p, total) {
245
263
  return "Page " + p + " of " + total;
246
264
  },
265
+ rangeOpen: function(a, b) {
266
+ return a + "\u2013" + b + " of many";
267
+ },
268
+ pageOpen: function(p) {
269
+ return "Page " + p;
270
+ },
247
271
  prevPage: "Previous page",
248
272
  nextPage: "Next page",
249
273
  totals: "Totals",
@@ -257,6 +281,7 @@ var STRINGS = {
257
281
  timeOutOfRange: function(a, b) {
258
282
  return "Choose a time between " + a + " and " + b;
259
283
  },
284
+ timeUnavailable: "That time isn't available. Choose another.",
260
285
  dropFiles: "Drag files here or",
261
286
  browse: "Choose files",
262
287
  browseOne: "Choose a file",
@@ -358,6 +383,12 @@ var STRINGS = {
358
383
  page: function(p, total) {
359
384
  return "\u0E2B\u0E19\u0E49\u0E32 " + p + " / " + total;
360
385
  },
386
+ rangeOpen: function(a, b) {
387
+ return a + "\u2013" + b + " \u0E08\u0E32\u0E01\u0E2B\u0E25\u0E32\u0E22\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
388
+ },
389
+ pageOpen: function(p) {
390
+ return "\u0E2B\u0E19\u0E49\u0E32 " + p;
391
+ },
361
392
  prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
362
393
  nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
363
394
  totals: "\u0E23\u0E27\u0E21",
@@ -371,6 +402,7 @@ var STRINGS = {
371
402
  timeOutOfRange: function(a, b) {
372
403
  return "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07 " + a + "\u2013" + b + " \u0E19.";
373
404
  },
405
+ timeUnavailable: "\u0E40\u0E27\u0E25\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E27\u0E48\u0E32\u0E07 \u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E2D\u0E37\u0E48\u0E19",
374
406
  dropFiles: "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D",
375
407
  browse: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
376
408
  browseOne: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
@@ -472,6 +504,12 @@ var STRINGS = {
472
504
  page: function(p, total) {
473
505
  return "Sida " + p + " av " + total;
474
506
  },
507
+ rangeOpen: function(a, b) {
508
+ return a + "\u2013" + b + " av m\xE5nga";
509
+ },
510
+ pageOpen: function(p) {
511
+ return "Sida " + p;
512
+ },
475
513
  prevPage: "F\xF6reg\xE5ende sida",
476
514
  nextPage: "N\xE4sta sida",
477
515
  totals: "Summa",
@@ -485,6 +523,7 @@ var STRINGS = {
485
523
  timeOutOfRange: function(a, b) {
486
524
  return "V\xE4lj en tid mellan " + a + " och " + b;
487
525
  },
526
+ timeUnavailable: "Den tiden \xE4r inte ledig. V\xE4lj en annan.",
488
527
  dropFiles: "Dra filer hit eller",
489
528
  browse: "V\xE4lj filer",
490
529
  browseOne: "V\xE4lj en fil",
@@ -42,6 +42,7 @@ export type FormatDateOptions = DateDisplayOptions & {
42
42
  format?: "short" | "long" | "numeric" | Intl.DateTimeFormatOptions | undefined;
43
43
  };
44
44
  export declare function toISO(d: Date | null | undefined): ISODate | null;
45
+ /** ISO → local Date; null for text that isn't a real date ("2026-02-31" no longer rolls over into March, 5.2). */
45
46
  export declare function fromISO(s: ISODate | null | undefined): Date | null;
46
47
  /** Format an ISO date for display: "18 Sept 2026" by default (English, Gregorian — 5.0); `{ locale: 'th' }` gives
47
48
  * "18 ก.ย. 2569" (Buddhist era), `{ locale: 'sv' }` "18 sep. 2026". The same function from the package root and from
@@ -106,6 +107,9 @@ export interface AuraStrings {
106
107
  loadingRows: string;
107
108
  range: (a: number | string, b: number | string, total: number) => string;
108
109
  page: (p: number, total: number) => string;
110
+ /** Server paging without totalRows (5.2): the range and page when the total isn't known. */
111
+ rangeOpen: (a: number | string, b: number | string) => string;
112
+ pageOpen: (p: number) => string;
109
113
  prevPage: string;
110
114
  nextPage: string;
111
115
  rowCount: (c: number) => string;
@@ -115,6 +119,8 @@ export interface AuraStrings {
115
119
  timePlaceholder: string;
116
120
  timeInvalid: string;
117
121
  timeOutOfRange: (a: number | string, b: number | string) => string;
122
+ /** A time inside the range that isTimeDisabled refuses (5.2). */
123
+ timeUnavailable: string;
118
124
  dropFiles: string;
119
125
  browse: string;
120
126
  browseOne: string;
@@ -42,6 +42,7 @@ export type FormatDateOptions = DateDisplayOptions & {
42
42
  format?: "short" | "long" | "numeric" | Intl.DateTimeFormatOptions | undefined;
43
43
  };
44
44
  export declare function toISO(d: Date | null | undefined): ISODate | null;
45
+ /** ISO → local Date; null for text that isn't a real date ("2026-02-31" no longer rolls over into March, 5.2). */
45
46
  export declare function fromISO(s: ISODate | null | undefined): Date | null;
46
47
  /** Format an ISO date for display: "18 Sept 2026" by default (English, Gregorian — 5.0); `{ locale: 'th' }` gives
47
48
  * "18 ก.ย. 2569" (Buddhist era), `{ locale: 'sv' }` "18 sep. 2026". The same function from the package root and from
@@ -106,6 +107,9 @@ export interface AuraStrings {
106
107
  loadingRows: string;
107
108
  range: (a: number | string, b: number | string, total: number) => string;
108
109
  page: (p: number, total: number) => string;
110
+ /** Server paging without totalRows (5.2): the range and page when the total isn't known. */
111
+ rangeOpen: (a: number | string, b: number | string) => string;
112
+ pageOpen: (p: number) => string;
109
113
  prevPage: string;
110
114
  nextPage: string;
111
115
  rowCount: (c: number) => string;
@@ -115,6 +119,8 @@ export interface AuraStrings {
115
119
  timePlaceholder: string;
116
120
  timeInvalid: string;
117
121
  timeOutOfRange: (a: number | string, b: number | string) => string;
122
+ /** A time inside the range that isTimeDisabled refuses (5.2). */
123
+ timeUnavailable: string;
118
124
  dropFiles: string;
119
125
  browse: string;
120
126
  browseOne: string;
@@ -4,12 +4,21 @@ var pad = function(n2) {
4
4
  return (n2 < 10 ? "0" : "") + n2;
5
5
  };
6
6
  function toISO(d) {
7
- return d ? d.getFullYear() + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate()) : null;
7
+ if (!d) return null;
8
+ const y = String(d.getFullYear());
9
+ return ("000" + y).slice(-Math.max(4, y.length)) + "-" + pad(d.getMonth() + 1) + "-" + pad(d.getDate());
10
+ }
11
+ function makeDate(y, monthIndex2, day) {
12
+ const d = new Date(2e3, 0, 1);
13
+ d.setFullYear(y, monthIndex2, day);
14
+ return d;
8
15
  }
9
16
  function fromISO(s) {
10
17
  if (!s) return null;
11
18
  const m = /^(\d{4})-(\d{2})-(\d{2})$/.exec(s);
12
- return m ? new Date(+m[1], +m[2] - 1, +m[3]) : null;
19
+ if (!m) return null;
20
+ const d = makeDate(+m[1], +m[2] - 1, +m[3]);
21
+ return d.getMonth() === +m[2] - 1 && d.getDate() === +m[3] ? d : null;
13
22
  }
14
23
  var TAGS = { th: "th-TH", en: "en-GB", sv: "sv-SE" };
15
24
  function defaultCalendar(locale) {
@@ -55,7 +64,15 @@ function monthIndex(word) {
55
64
  return MONTHS[k] != null ? MONTHS[k] : -1;
56
65
  }
57
66
  function parseDate(text) {
58
- const s = String(text || "").trim();
67
+ const raw = String(text || "").trim();
68
+ const gregorian = /ค\.ศ\.|\d\s*(AD|CE)\b|^\s*(AD|CE)\s/i.test(raw);
69
+ let s = raw.replace(/พ\.ศ\.|ค\.ศ\./g, " ").replace(/(\d)\s*(BE|AD|CE)\b/gi, "$1 ").replace(/^\s*(BE|AD|CE)\s+/i, " ").replace(/\s+/g, " ").trim();
70
+ const eight = /^(\d{2})(\d{2})(\d{4})$/.exec(s);
71
+ if (eight) {
72
+ const a = +eight[3] >= 1e3 ? parseDate(eight[1] + "/" + eight[2] + "/" + eight[3] + (gregorian ? " \u0E04.\u0E28." : "")) : null;
73
+ if (a) return a;
74
+ s = s.slice(0, 4) + "-" + s.slice(4, 6) + "-" + s.slice(6);
75
+ }
59
76
  let m = /^(\d{4})-(\d{1,2})-(\d{1,2})$/.exec(s), y, mo, d;
60
77
  if (m) {
61
78
  y = +m[1];
@@ -65,13 +82,13 @@ function parseDate(text) {
65
82
  d = +m[1];
66
83
  mo = +m[2];
67
84
  y = +m[3];
68
- } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s.replace(ERA, "").trim())) && monthIndex(m[2]) >= 0) {
85
+ } else if ((m = /^(\d{1,2})\s+(\S+)\s+(\d{4})$/.exec(s)) && monthIndex(m[2]) >= 0) {
69
86
  d = +m[1];
70
87
  mo = monthIndex(m[2]) + 1;
71
88
  y = +m[3];
72
89
  } else return null;
73
- if (y >= 2400) y -= 543;
74
- const dt = new Date(y, mo - 1, d);
90
+ if (y >= 2400 && !gregorian) y -= 543;
91
+ const dt = makeDate(y, mo - 1, d);
75
92
  return dt.getMonth() === mo - 1 && dt.getDate() === d ? toISO(dt) : null;
76
93
  }
77
94
  function todayIn(timeZone) {
@@ -110,6 +127,7 @@ function parseTime(text) {
110
127
  const m = /^(\d{1,2})(?:[:.](\d{2}))?$/.exec(s) || /^(\d{1,2})(\d{2})$/.exec(s);
111
128
  if (!m) return null;
112
129
  let hh = +m[1], mm = m[2] ? +m[2] : 0;
130
+ if ((am || pm) && (hh < 1 || hh > 12)) return null;
113
131
  if (pm && hh < 12) hh += 12;
114
132
  if (am && hh === 12) hh = 0;
115
133
  if (hh > 23 || mm > 59) return null;
@@ -205,6 +223,12 @@ var STRINGS = {
205
223
  page: function(p, total) {
206
224
  return "Page " + p + " of " + total;
207
225
  },
226
+ rangeOpen: function(a, b) {
227
+ return a + "\u2013" + b + " of many";
228
+ },
229
+ pageOpen: function(p) {
230
+ return "Page " + p;
231
+ },
208
232
  prevPage: "Previous page",
209
233
  nextPage: "Next page",
210
234
  totals: "Totals",
@@ -218,6 +242,7 @@ var STRINGS = {
218
242
  timeOutOfRange: function(a, b) {
219
243
  return "Choose a time between " + a + " and " + b;
220
244
  },
245
+ timeUnavailable: "That time isn't available. Choose another.",
221
246
  dropFiles: "Drag files here or",
222
247
  browse: "Choose files",
223
248
  browseOne: "Choose a file",
@@ -319,6 +344,12 @@ var STRINGS = {
319
344
  page: function(p, total) {
320
345
  return "\u0E2B\u0E19\u0E49\u0E32 " + p + " / " + total;
321
346
  },
347
+ rangeOpen: function(a, b) {
348
+ return a + "\u2013" + b + " \u0E08\u0E32\u0E01\u0E2B\u0E25\u0E32\u0E22\u0E23\u0E32\u0E22\u0E01\u0E32\u0E23";
349
+ },
350
+ pageOpen: function(p) {
351
+ return "\u0E2B\u0E19\u0E49\u0E32 " + p;
352
+ },
322
353
  prevPage: "\u0E2B\u0E19\u0E49\u0E32\u0E01\u0E48\u0E2D\u0E19\u0E2B\u0E19\u0E49\u0E32",
323
354
  nextPage: "\u0E2B\u0E19\u0E49\u0E32\u0E16\u0E31\u0E14\u0E44\u0E1B",
324
355
  totals: "\u0E23\u0E27\u0E21",
@@ -332,6 +363,7 @@ var STRINGS = {
332
363
  timeOutOfRange: function(a, b) {
333
364
  return "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E23\u0E30\u0E2B\u0E27\u0E48\u0E32\u0E07 " + a + "\u2013" + b + " \u0E19.";
334
365
  },
366
+ timeUnavailable: "\u0E40\u0E27\u0E25\u0E32\u0E19\u0E35\u0E49\u0E44\u0E21\u0E48\u0E27\u0E48\u0E32\u0E07 \u0E40\u0E25\u0E37\u0E2D\u0E01\u0E40\u0E27\u0E25\u0E32\u0E2D\u0E37\u0E48\u0E19",
335
367
  dropFiles: "\u0E25\u0E32\u0E01\u0E44\u0E1F\u0E25\u0E4C\u0E21\u0E32\u0E27\u0E32\u0E07 \u0E2B\u0E23\u0E37\u0E2D",
336
368
  browse: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
337
369
  browseOne: "\u0E40\u0E25\u0E37\u0E2D\u0E01\u0E44\u0E1F\u0E25\u0E4C",
@@ -433,6 +465,12 @@ var STRINGS = {
433
465
  page: function(p, total) {
434
466
  return "Sida " + p + " av " + total;
435
467
  },
468
+ rangeOpen: function(a, b) {
469
+ return a + "\u2013" + b + " av m\xE5nga";
470
+ },
471
+ pageOpen: function(p) {
472
+ return "Sida " + p;
473
+ },
436
474
  prevPage: "F\xF6reg\xE5ende sida",
437
475
  nextPage: "N\xE4sta sida",
438
476
  totals: "Summa",
@@ -446,6 +484,7 @@ var STRINGS = {
446
484
  timeOutOfRange: function(a, b) {
447
485
  return "V\xE4lj en tid mellan " + a + " och " + b;
448
486
  },
487
+ timeUnavailable: "Den tiden \xE4r inte ledig. V\xE4lj en annan.",
449
488
  dropFiles: "Dra filer hit eller",
450
489
  browse: "V\xE4lj filer",
451
490
  browseOne: "V\xE4lj en fil",
package/dist/styles.css CHANGED
@@ -679,6 +679,8 @@ a.aura-tab { text-decoration: none; }
679
679
  .aura-avatar__status {
680
680
  position: absolute; right: -1px; bottom: -1px; width: 30%; height: 30%; min-width: 8px; min-height: 8px;
681
681
  border-radius: var(--aura-radius-full); background: var(--aura-accent-dot); box-shadow: 0 0 0 2px var(--aura-bg-surface);
682
+ /* 5.2: the lime fill is 1.2:1 on the white ring; the edge carries the 3:1 (WCAG 1.4.11). */
683
+ border: 1.5px solid var(--aura-fg-positive); box-sizing: border-box;
682
684
  }
683
685
 
684
686
  /* ---------- Combobox ---------- */
@@ -921,6 +923,8 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
921
923
  .aura-upload__thumb.is-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--aura-fg-secondary); }
922
924
  .aura-upload__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
923
925
  .aura-upload__name { font-size: 13px; font-weight: 500; color: var(--aura-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
926
+ a.aura-upload__name { color: var(--aura-fg-accent); text-decoration: underline; text-underline-offset: 2px; border-radius: var(--aura-radius-xs); }
927
+ a.aura-upload__name:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; }
924
928
  .aura-upload__sub { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--aura-fg-secondary); }
925
929
  .aura-upload__item.is-error .aura-upload__sub { color: var(--aura-fg-danger); }
926
930
  .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; }
@@ -1042,8 +1046,9 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
1042
1046
  position: fixed; z-index: var(--aura-z-menu); box-sizing: border-box; width: max-content; min-width: 200px; max-width: min(360px, calc(100vw - 16px));
1043
1047
  background: var(--aura-bg-surface); color: var(--aura-fg-primary); border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-lg);
1044
1048
  box-shadow: var(--aura-shadow-overlay); font-family: var(--font-sans); font-size: 14px; outline: none; animation: aura-fade-in var(--aura-duration-fast) var(--aura-ease);
1049
+ overflow-y: auto; overscroll-behavior: contain;
1045
1050
  }
1046
- .aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); }
1051
+ .aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); position: sticky; top: 0; z-index: 1; background: var(--aura-bg-surface); } /* 5.2: stays while a tall popover scrolls */
1047
1052
  .aura-popover__title { margin: 0; font-size: 14px; font-weight: 600; }
1048
1053
  .aura-popover__body { padding: var(--aura-space-4); }
1049
1054
  .aura-popover__head + .aura-popover__body { padding-top: var(--aura-space-2); }
@@ -1143,7 +1148,7 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1143
1148
  .aura-segmented.is-full .aura-segmented__option { flex: 1 1 0; min-width: 0; }
1144
1149
  .aura-segmented__option.is-icon { padding: 0; width: 36px; }
1145
1150
  .aura-segmented__option:hover:not(:disabled):not(.is-selected) { color: var(--aura-fg-primary); }
1146
- .aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: var(--aura-shadow-enterprise); }
1151
+ .aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: inset 0 0 0 1px var(--aura-border-control), var(--aura-shadow-enterprise); } /* 5.2: the edge marks the choice at 3:1; the fill alone was 1.04:1 on the track */
1147
1152
  .aura-segmented__option:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; }
1148
1153
  .aura-segmented__option:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
1149
1154
  .aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
@@ -681,6 +681,8 @@ a.aura-tab { text-decoration: none; }
681
681
  .aura-avatar__status {
682
682
  position: absolute; right: -1px; bottom: -1px; width: 30%; height: 30%; min-width: 8px; min-height: 8px;
683
683
  border-radius: var(--aura-radius-full); background: var(--aura-accent-dot); box-shadow: 0 0 0 2px var(--aura-bg-surface);
684
+ /* 5.2: the lime fill is 1.2:1 on the white ring; the edge carries the 3:1 (WCAG 1.4.11). */
685
+ border: 1.5px solid var(--aura-fg-positive); box-sizing: border-box;
684
686
  }
685
687
 
686
688
  /* ---------- Combobox ---------- */
@@ -923,6 +925,8 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
923
925
  .aura-upload__thumb.is-icon { display: inline-flex; align-items: center; justify-content: center; color: var(--aura-fg-secondary); }
924
926
  .aura-upload__meta { flex: 1; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
925
927
  .aura-upload__name { font-size: 13px; font-weight: 500; color: var(--aura-fg-primary); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
928
+ a.aura-upload__name { color: var(--aura-fg-accent); text-decoration: underline; text-underline-offset: 2px; border-radius: var(--aura-radius-xs); }
929
+ a.aura-upload__name:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 2px; }
926
930
  .aura-upload__sub { display: inline-flex; align-items: center; gap: 4px; font-size: 12px; color: var(--aura-fg-secondary); }
927
931
  .aura-upload__item.is-error .aura-upload__sub { color: var(--aura-fg-danger); }
928
932
  .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; }
@@ -1044,8 +1048,9 @@ button.aura-stat { font: inherit; width: 100%; cursor: pointer; }
1044
1048
  position: fixed; z-index: var(--aura-z-menu); box-sizing: border-box; width: max-content; min-width: 200px; max-width: min(360px, calc(100vw - 16px));
1045
1049
  background: var(--aura-bg-surface); color: var(--aura-fg-primary); border: 1px solid var(--aura-border-default); border-radius: var(--aura-radius-lg);
1046
1050
  box-shadow: var(--aura-shadow-overlay); font-family: var(--font-sans); font-size: 14px; outline: none; animation: aura-fade-in var(--aura-duration-fast) var(--aura-ease);
1051
+ overflow-y: auto; overscroll-behavior: contain;
1047
1052
  }
1048
- .aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); }
1053
+ .aura-popover__head { display: flex; align-items: center; justify-content: space-between; gap: var(--aura-space-2); padding: var(--aura-space-2) var(--aura-space-2) 0 var(--aura-space-4); position: sticky; top: 0; z-index: 1; background: var(--aura-bg-surface); } /* 5.2: stays while a tall popover scrolls */
1049
1054
  .aura-popover__title { margin: 0; font-size: 14px; font-weight: 600; }
1050
1055
  .aura-popover__body { padding: var(--aura-space-4); }
1051
1056
  .aura-popover__head + .aura-popover__body { padding-top: var(--aura-space-2); }
@@ -1145,7 +1150,7 @@ button.aura-stepper__step:hover .aura-stepper__label { text-decoration: underlin
1145
1150
  .aura-segmented.is-full .aura-segmented__option { flex: 1 1 0; min-width: 0; }
1146
1151
  .aura-segmented__option.is-icon { padding: 0; width: 36px; }
1147
1152
  .aura-segmented__option:hover:not(:disabled):not(.is-selected) { color: var(--aura-fg-primary); }
1148
- .aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: var(--aura-shadow-enterprise); }
1153
+ .aura-segmented__option.is-selected { background: var(--aura-bg-surface); color: var(--aura-fg-primary); box-shadow: inset 0 0 0 1px var(--aura-border-control), var(--aura-shadow-enterprise); } /* 5.2: the edge marks the choice at 3:1; the fill alone was 1.04:1 on the track */
1149
1154
  .aura-segmented__option:focus-visible { outline: var(--aura-focus-ring-width) solid var(--aura-focus-ring); outline-offset: 1px; }
1150
1155
  .aura-segmented__option:disabled { opacity: var(--aura-disabled-opacity); cursor: default; }
1151
1156
  .aura-segmented--sm .aura-segmented__option { height: 24px; padding: 0 var(--aura-space-3); font-size: 12px; }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@jirawatpyk/aura-react",
3
- "version": "5.1.1",
3
+ "version": "5.2.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",