@jirawatpyk/aura-react 5.1.1 → 5.3.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.
@@ -1,9 +1,55 @@
1
1
  'use client';
2
2
  import * as React from "react";
3
+ import { createPortal } from "react-dom";
3
4
  import { FIELD_KEYS, Field, describedBy } from "./Field.js";
4
5
  import { Icon } from "./Icon.js";
5
- import { cx, omit, uid } from "./internal.js";
6
+ import { useDensity } from "./locale.js";
7
+ import { cx, omit, uid, useIsoLayoutEffect, useMergedRef, useMounted } from "./internal.js";
6
8
  const h = React.createElement;
9
+ const FORM_KEYS = ["name", "form", "value", "defaultValue", "disabled", "autoComplete", "onInput", "onInvalid"];
10
+ const HANDLED_KEYS = FORM_KEYS.concat([
11
+ "onChange",
12
+ "onBlur",
13
+ "onFocus",
14
+ "onKeyDown",
15
+ "onClick",
16
+ "autoFocus",
17
+ "tabIndex",
18
+ "aria-label",
19
+ "aria-labelledby",
20
+ "aria-describedby",
21
+ "multiple",
22
+ "size"
23
+ ]);
24
+ function pick(o, keys) {
25
+ const r = {};
26
+ for (const k of keys) if (k in o) r[k] = o[k];
27
+ return r;
28
+ }
29
+ function readItems(el) {
30
+ if (!el) return [];
31
+ return Array.prototype.map.call(el.options, function(o, i) {
32
+ const g = o.parentElement && o.parentElement.tagName === "OPTGROUP" ? o.parentElement : null;
33
+ return {
34
+ value: o.value,
35
+ label: o.textContent || "",
36
+ disabled: o.disabled || !!(g && g.disabled),
37
+ group: g ? g.label : null,
38
+ index: i
39
+ };
40
+ });
41
+ }
42
+ function initialLabel(props) {
43
+ const opts = (props.options || []).map(function(o) {
44
+ return typeof o === "object" ? o : { value: o, label: o };
45
+ });
46
+ const v = props.value !== void 0 && props.value !== null ? String(props.value) : props.defaultValue !== void 0 && props.defaultValue !== null ? String(props.defaultValue) : props.placeholder ? "" : opts[0] ? opts[0].value : "";
47
+ const hit = opts.filter(function(o) {
48
+ return o.value === v;
49
+ })[0];
50
+ if (hit) return { label: hit.label, empty: false };
51
+ return { label: props.placeholder || "", empty: true };
52
+ }
7
53
  const Select = React.forwardRef(function Select2(props, ref) {
8
54
  const auto = uid(), id = props.id || auto;
9
55
  const rest = omit(props, FIELD_KEYS.concat(["children"]));
@@ -16,10 +62,259 @@ const Select = React.forwardRef(function Select2(props, ref) {
16
62
  /* @__PURE__ */ React.createElement("option", { key: "__ph", value: "", disabled: true }, props.placeholder)
17
63
  );
18
64
  const extra = props.value === void 0 && props.defaultValue === void 0 && props.placeholder ? { defaultValue: "" } : {};
19
- return /* @__PURE__ */ React.createElement(
65
+ const native = !!props.multiple || props.size != null && props.size > 1;
66
+ const mounted = useMounted();
67
+ const live = !native && mounted;
68
+ const density = useDensity();
69
+ const selRef = React.useRef(null), trigRef = React.useRef(null), listRef = React.useRef(null);
70
+ const shown = React.useState(function() {
71
+ return initialLabel(props);
72
+ });
73
+ const openState = React.useState(false), open = openState[0];
74
+ const activeState = React.useState(-1), active = activeState[0];
75
+ const items = React.useState([]);
76
+ const pos = React.useState(null);
77
+ const typed = React.useRef({ text: "", at: 0 });
78
+ const sync = React.useCallback(function() {
79
+ const el = selRef.current;
80
+ if (!el) return;
81
+ const o = el.selectedIndex >= 0 ? el.options[el.selectedIndex] : null;
82
+ const next = o ? { label: o.textContent || "", empty: o.value === "" } : { label: "", empty: true };
83
+ shown[1](function(cur) {
84
+ return cur.label === next.label && cur.empty === next.empty ? cur : next;
85
+ });
86
+ }, []);
87
+ const hook = React.useCallback(
88
+ function(el) {
89
+ selRef.current = el;
90
+ if (!el || native || el.__auraHooked) return;
91
+ el.__auraHooked = true;
92
+ const proto = HTMLSelectElement.prototype;
93
+ const pv = Object.getOwnPropertyDescriptor(proto, "value"), pi = Object.getOwnPropertyDescriptor(proto, "selectedIndex");
94
+ if (pv && pv.set && pv.get)
95
+ Object.defineProperty(el, "value", {
96
+ configurable: true,
97
+ get: function() {
98
+ return pv.get.call(el);
99
+ },
100
+ set: function(v) {
101
+ pv.set.call(el, v);
102
+ sync();
103
+ }
104
+ });
105
+ if (pi && pi.set && pi.get)
106
+ Object.defineProperty(el, "selectedIndex", {
107
+ configurable: true,
108
+ get: function() {
109
+ return pi.get.call(el);
110
+ },
111
+ set: function(v) {
112
+ pi.set.call(el, v);
113
+ sync();
114
+ }
115
+ });
116
+ el.focus = function(o) {
117
+ if (trigRef.current) trigRef.current.focus(o);
118
+ else proto.focus.call(el, o);
119
+ };
120
+ },
121
+ [native, sync]
122
+ );
123
+ const selMerged = useMergedRef(ref, hook);
124
+ React.useEffect(
125
+ function() {
126
+ const f = selRef.current && selRef.current.form;
127
+ if (!f || native) return;
128
+ function onReset() {
129
+ setTimeout(sync, 0);
130
+ }
131
+ f.addEventListener("reset", onReset);
132
+ return function() {
133
+ f.removeEventListener("reset", onReset);
134
+ };
135
+ },
136
+ [native, sync]
137
+ );
138
+ const focusedOnce = React.useRef(false);
139
+ React.useEffect(
140
+ function() {
141
+ if (!live || focusedOnce.current || !trigRef.current) return;
142
+ focusedOnce.current = true;
143
+ if (props.autoFocus || selRef.current && document.activeElement === selRef.current) trigRef.current.focus();
144
+ },
145
+ [live]
146
+ );
147
+ useIsoLayoutEffect(function() {
148
+ if (!live) return;
149
+ sync();
150
+ if (!open) return;
151
+ const now = readItems(selRef.current), was = items[0];
152
+ const same = now.length === was.length && now.every(function(it, i) {
153
+ const w = was[i];
154
+ return w.value === it.value && w.label === it.label && w.disabled === it.disabled && w.group === it.group;
155
+ });
156
+ if (same) return;
157
+ items[1](now);
158
+ if (!choices(now).length) {
159
+ close(false);
160
+ return;
161
+ }
162
+ const hv = was[active] ? was[active].value : null;
163
+ const again = now.filter(function(it) {
164
+ return !it.disabled && it.value === hv;
165
+ })[0];
166
+ const cur = selRef.current ? selRef.current.selectedIndex : -1;
167
+ activeState[1](again ? again.index : now[cur] && !now[cur].disabled ? cur : choices(now)[0].index);
168
+ });
169
+ function choices(list) {
170
+ return list.filter(function(i) {
171
+ return !i.disabled;
172
+ });
173
+ }
174
+ function choose(it) {
175
+ const el = selRef.current;
176
+ if (!el || it.disabled) return;
177
+ if (el.value !== it.value) {
178
+ el.value = it.value;
179
+ el.dispatchEvent(new Event("input", { bubbles: true }));
180
+ el.dispatchEvent(new Event("change", { bubbles: true }));
181
+ }
182
+ sync();
183
+ close(true);
184
+ }
185
+ function openList(at) {
186
+ if (props.disabled || trigRef.current && trigRef.current.disabled) return;
187
+ const list = readItems(selRef.current);
188
+ const ok = choices(list);
189
+ if (!ok.length) return;
190
+ items[1](list);
191
+ const cur = selRef.current ? selRef.current.selectedIndex : -1;
192
+ let a = at === "first" ? ok[0].index : at === "last" ? ok[ok.length - 1].index : cur;
193
+ if (typeof at === "number") a = at;
194
+ if (a < 0 || !list[a] || list[a].disabled) a = ok[0].index;
195
+ activeState[1](a);
196
+ openState[1](true);
197
+ }
198
+ function close(focus) {
199
+ openState[1](false);
200
+ pos[1](null);
201
+ if (focus && trigRef.current) trigRef.current.focus();
202
+ }
203
+ function step(from, dir, list) {
204
+ for (let i = from + dir; i >= 0 && i < list.length; i += dir) if (!list[i].disabled) return i;
205
+ return from;
206
+ }
207
+ function typing() {
208
+ return !!typed.current.text && Date.now() - typed.current.at < 500;
209
+ }
210
+ function typeahead(ch, list, from) {
211
+ const now = Date.now(), t = typed.current;
212
+ t.text = now - t.at < 500 ? t.text + ch : ch;
213
+ t.at = now;
214
+ const q = t.text.toLocaleLowerCase();
215
+ for (let k = 1; k <= list.length; k++) {
216
+ const i = (from + (t.text.length > 1 ? 0 : 1) + k - 1 + list.length) % list.length;
217
+ if (!list[i].disabled && list[i].label.toLocaleLowerCase().indexOf(q) === 0) return i;
218
+ }
219
+ t.text = "";
220
+ return -1;
221
+ }
222
+ function onKeyDown(e) {
223
+ const k = e.key;
224
+ const printable = k.length === 1 && !e.ctrlKey && !e.metaKey && !e.altKey;
225
+ const search = printable && (k !== " " || typing());
226
+ if (!open) {
227
+ if (search) {
228
+ const list2 = readItems(selRef.current);
229
+ const i = typeahead(k, list2, selRef.current ? selRef.current.selectedIndex : -1);
230
+ e.preventDefault();
231
+ if (i >= 0) openList(i);
232
+ } else if (k === "ArrowDown" || k === "ArrowUp" || k === "Enter" || k === " ") {
233
+ e.preventDefault();
234
+ openList(k === "ArrowUp" && !(selRef.current && selRef.current.value) ? "last" : void 0);
235
+ }
236
+ return;
237
+ }
238
+ const list = items[0];
239
+ if (search) {
240
+ e.preventDefault();
241
+ const i = typeahead(k, list, active);
242
+ if (i >= 0) activeState[1](i);
243
+ } else if (k === "ArrowDown") {
244
+ e.preventDefault();
245
+ activeState[1](step(active, 1, list));
246
+ } else if (k === "ArrowUp") {
247
+ e.preventDefault();
248
+ activeState[1](step(active, -1, list));
249
+ } else if (k === "Home" || k === "PageUp") {
250
+ e.preventDefault();
251
+ activeState[1](k === "Home" ? step(-1, 1, list) : Math.max(step(-1, 1, list), step(active - 9, 1, list)));
252
+ } else if (k === "End" || k === "PageDown") {
253
+ e.preventDefault();
254
+ activeState[1](
255
+ k === "End" ? step(list.length, -1, list) : Math.min(step(list.length, -1, list), step(active + 9, -1, list))
256
+ );
257
+ } else if (k === "Enter" || k === " ") {
258
+ e.preventDefault();
259
+ if (list[active]) choose(list[active]);
260
+ } else if (k === "Escape") {
261
+ e.preventDefault();
262
+ e.stopPropagation();
263
+ close(true);
264
+ } else if (k === "Tab") {
265
+ close(false);
266
+ }
267
+ }
268
+ useIsoLayoutEffect(
269
+ function() {
270
+ if (!open) return;
271
+ function place() {
272
+ const t = trigRef.current && trigRef.current.parentElement;
273
+ if (!t) return;
274
+ const r = t.getBoundingClientRect(), below = window.innerHeight - r.bottom - 8, above = r.top - 8, want = listRef.current ? listRef.current.scrollHeight : 320, up = below < Math.min(want, 200) && above > below;
275
+ pos[1]({
276
+ left: r.left,
277
+ width: r.width,
278
+ top: up ? void 0 : r.bottom + 4,
279
+ bottom: up ? window.innerHeight - r.top + 4 : void 0,
280
+ maxHeight: Math.min(320, (up ? above : below) - 4)
281
+ });
282
+ }
283
+ place();
284
+ function outside(e) {
285
+ const n = e.target;
286
+ if (listRef.current && listRef.current.contains(n)) return;
287
+ if (trigRef.current && trigRef.current.contains(n)) return;
288
+ close(false);
289
+ }
290
+ function onScroll(e) {
291
+ if (!listRef.current || !listRef.current.contains(e.target)) place();
292
+ }
293
+ document.addEventListener("pointerdown", outside, true);
294
+ window.addEventListener("scroll", onScroll, true);
295
+ window.addEventListener("resize", place);
296
+ return function() {
297
+ document.removeEventListener("pointerdown", outside, true);
298
+ window.removeEventListener("scroll", onScroll, true);
299
+ window.removeEventListener("resize", place);
300
+ };
301
+ },
302
+ [open]
303
+ );
304
+ React.useEffect(
305
+ function() {
306
+ if (!open || !listRef.current || active < 0) return;
307
+ const el = listRef.current.querySelector('[data-idx="' + active + '"]');
308
+ if (el && el.scrollIntoView) el.scrollIntoView({ block: "nearest" });
309
+ },
310
+ [open, active, pos[0] != null]
311
+ );
312
+ const described = [describedBy(id, props), rest["aria-describedby"]].filter(Boolean).join(" ") || void 0;
313
+ const field = (child) => /* @__PURE__ */ React.createElement(
20
314
  Field,
21
315
  {
22
316
  id,
317
+ labelId: native ? void 0 : id + "-label",
23
318
  label: props.label,
24
319
  hint: props.hint,
25
320
  error: props.error,
@@ -28,19 +323,180 @@ const Select = React.forwardRef(function Select2(props, ref) {
28
323
  disabled: props.disabled,
29
324
  className: props.className
30
325
  },
31
- /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h(
32
- "select",
33
- Object.assign(extra, rest, {
34
- ref,
35
- id,
36
- className: "aura-input__control",
37
- required: props.required,
38
- "aria-invalid": props.error ? true : void 0,
39
- "aria-describedby": describedBy(id, props)
40
- }),
41
- opts,
42
- props.children
43
- ), /* @__PURE__ */ React.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }))
326
+ child
327
+ );
328
+ if (!live)
329
+ return field(
330
+ /* @__PURE__ */ React.createElement("div", { className: cx("aura-input aura-select", props.icon && "has-icon") }, props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null, h(
331
+ "select",
332
+ Object.assign(extra, rest, {
333
+ ref: native ? ref : selMerged,
334
+ id,
335
+ className: "aura-input__control",
336
+ required: props.required,
337
+ "aria-invalid": props.error ? true : void 0,
338
+ "aria-describedby": described
339
+ }),
340
+ opts,
341
+ props.children
342
+ ), /* @__PURE__ */ React.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }))
343
+ );
344
+ const listId = id + "-list", labelId = id + "-label";
345
+ const optId = function(i) {
346
+ return id + "-opt-" + i;
347
+ };
348
+ const labelledBy = rest["aria-labelledby"] || (props.label ? labelId : void 0);
349
+ const selIndex = selRef.current ? selRef.current.selectedIndex : -1;
350
+ const option = function(it) {
351
+ const sel = selIndex === it.index;
352
+ return /* @__PURE__ */ React.createElement(
353
+ "div",
354
+ {
355
+ key: it.index,
356
+ id: optId(it.index),
357
+ role: "option",
358
+ "data-idx": it.index,
359
+ "aria-selected": sel,
360
+ "aria-disabled": it.disabled || void 0,
361
+ className: cx(
362
+ "aura-combo__option",
363
+ it.index === active && "is-active",
364
+ sel && "is-selected",
365
+ it.disabled && "is-disabled"
366
+ ),
367
+ onPointerDown: function(e) {
368
+ e.preventDefault();
369
+ },
370
+ onPointerMove: function() {
371
+ if (!it.disabled && active !== it.index) activeState[1](it.index);
372
+ },
373
+ onClick: function() {
374
+ choose(it);
375
+ }
376
+ },
377
+ /* @__PURE__ */ React.createElement("span", { className: "aura-combo__text" }, /* @__PURE__ */ React.createElement("span", { className: "aura-combo__label" }, it.label)),
378
+ sel ? /* @__PURE__ */ React.createElement(Icon, { name: "check", className: "aura-combo__check" }) : null
379
+ );
380
+ };
381
+ const runs = [];
382
+ items[0].forEach(function(it) {
383
+ if (it.value === "" && it.disabled) return;
384
+ const last = runs[runs.length - 1];
385
+ if (last && last.group === it.group) last.items.push(it);
386
+ else runs.push({ group: it.group, items: [it] });
387
+ });
388
+ const popup = open ? createPortal(
389
+ /* @__PURE__ */ React.createElement(
390
+ "div",
391
+ {
392
+ ref: listRef,
393
+ "data-density": density,
394
+ className: "aura-combo__popover aura-select__popover",
395
+ style: pos[0] ? {
396
+ left: pos[0].left,
397
+ minWidth: pos[0].width,
398
+ top: pos[0].top,
399
+ bottom: pos[0].bottom,
400
+ maxHeight: pos[0].maxHeight
401
+ } : { left: -9999, top: -9999 }
402
+ },
403
+ /* @__PURE__ */ React.createElement(
404
+ "div",
405
+ {
406
+ id: listId,
407
+ role: "listbox",
408
+ "aria-labelledby": labelledBy,
409
+ "aria-label": labelledBy ? void 0 : rest["aria-label"],
410
+ className: "aura-combo__list"
411
+ },
412
+ runs.map(function(r, n) {
413
+ if (r.group === null) return /* @__PURE__ */ React.createElement(React.Fragment, { key: "r" + n }, r.items.map(option));
414
+ const gid = id + "-group-" + n;
415
+ return /* @__PURE__ */ React.createElement("div", { key: "r" + n, role: "group", "aria-labelledby": gid }, /* @__PURE__ */ React.createElement("div", { id: gid, className: "aura-select__group" }, r.group), r.items.map(option));
416
+ })
417
+ )
418
+ ),
419
+ document.body
420
+ ) : null;
421
+ const userFocus = rest.onFocus, userBlur = rest.onBlur, userKey = rest.onKeyDown, userClick = rest.onClick;
422
+ return field(
423
+ /* @__PURE__ */ React.createElement(
424
+ "div",
425
+ {
426
+ className: cx("aura-input aura-select aura-select--custom", props.icon && "has-icon", open && "is-open"),
427
+ "data-invalid": props.error ? "" : void 0
428
+ },
429
+ props.icon ? /* @__PURE__ */ React.createElement(Icon, { name: props.icon, className: "aura-input__icon" }) : null,
430
+ h(
431
+ "select",
432
+ Object.assign(extra, pick(rest, FORM_KEYS), {
433
+ ref: selMerged,
434
+ id: id + "-select",
435
+ className: "aura-select__native",
436
+ tabIndex: -1,
437
+ "aria-hidden": true,
438
+ required: props.required,
439
+ onChange: function(e) {
440
+ sync();
441
+ if (props.onChange) props.onChange(e);
442
+ },
443
+ /* A browser focusing the <select> (the required bubble, a label click) hands focus to the button. */
444
+ onFocus: function() {
445
+ if (trigRef.current) trigRef.current.focus();
446
+ },
447
+ /* Only the blur the button reports (below): the <select>'s own, while it hands focus over, is not one. */
448
+ onBlur: function(e) {
449
+ if (!e.nativeEvent.isTrusted && userBlur) userBlur(e);
450
+ }
451
+ }),
452
+ opts,
453
+ props.children
454
+ ),
455
+ /* @__PURE__ */ React.createElement(
456
+ "button",
457
+ {
458
+ ...omit(rest, HANDLED_KEYS),
459
+ ref: trigRef,
460
+ type: "button",
461
+ role: "combobox",
462
+ form: id + "-no-form",
463
+ className: "aura-input__control aura-select__trigger",
464
+ disabled: props.disabled,
465
+ tabIndex: rest.tabIndex,
466
+ "aria-haspopup": "listbox",
467
+ "aria-expanded": open,
468
+ "aria-controls": open ? listId : void 0,
469
+ "aria-activedescendant": open && active >= 0 ? optId(active) : void 0,
470
+ id,
471
+ "aria-label": rest["aria-label"],
472
+ "aria-labelledby": rest["aria-labelledby"],
473
+ "aria-required": props.required || void 0,
474
+ "aria-invalid": props.error ? true : void 0,
475
+ "aria-describedby": described,
476
+ onFocus: function(e) {
477
+ if (userFocus) userFocus(e);
478
+ },
479
+ onClick: function(e) {
480
+ if (userClick) userClick(e);
481
+ if (e.defaultPrevented) return;
482
+ if (open) close(true);
483
+ else openList();
484
+ },
485
+ onKeyDown: function(e) {
486
+ if (userKey) userKey(e);
487
+ if (!e.defaultPrevented) onKeyDown(e);
488
+ },
489
+ onBlur: function(e) {
490
+ if (listRef.current && e.relatedTarget && listRef.current.contains(e.relatedTarget)) return;
491
+ if (open) close(false);
492
+ if (selRef.current) selRef.current.dispatchEvent(new FocusEvent("focusout", { bubbles: true }));
493
+ }
494
+ },
495
+ /* @__PURE__ */ React.createElement("span", { id: id + "-value", className: cx("aura-select__value", shown[0].empty && "is-placeholder") }, shown[0].label || " ")
496
+ ),
497
+ /* @__PURE__ */ React.createElement(Icon, { name: "chevron-down", className: "aura-select__chevron" }),
498
+ popup
499
+ )
44
500
  );
45
501
  });
46
502
  export {
@@ -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;