@jirawatpyk/aura-react 5.7.1 → 5.7.3
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 +8 -0
- package/dist/aura.bundle.js +40 -4
- package/dist/cjs/index.cjs +40 -4
- package/dist/esm/FormErrorSummary.js +40 -4
- package/dist/index.d.cts +4 -1
- package/dist/index.d.ts +4 -1
- package/dist/styles.css +5 -2
- package/dist/styles.layer.css +5 -2
- package/package.json +1 -1
package/README.md
CHANGED
|
@@ -186,6 +186,14 @@ DataTable with sort and page in the URL (4.17): `onStateChange={({ sort, page })
|
|
|
186
186
|
- **Time zone**: `timeZone` on AuraProvider, DatePicker, DateRangePicker and Calendar decides "today" (the marker, `min`/`max="today"`, the first month shown); or pass `today` as an ISO date. `todayIn('Asia/Bangkok')` is exported from the root and `/server`.
|
|
187
187
|
- **Toasts** queue past three instead of dropping: six in a row all show, in order, three at a time.
|
|
188
188
|
|
|
189
|
+
## 5.7.3 — FormErrorSummary focus follows submits
|
|
190
|
+
|
|
191
|
+
- **FormErrorSummary** (item 65): with `focusKey`, the summary takes focus only after a submit — never while someone types. Fed react-hook-form's live `errors`, the list empties and refills as fields are fixed and broken again (RHF re-validates on change after a submit); that used to pull focus out of the field being typed in (WCAG 3.2.2). Now a new `focusKey` arms one focus, taken as soon as the list has errors: at once for client errors, or when a server error arrives through `setError` after a valid submit; typing in the meantime cancels it. A click or key press elsewhere also cancels it, so a later live error (a field validated on blur, a picker set with `setValue`) never takes focus. With live RHF errors always pass `focusKey={formState.submitCount}` — a primitive, not an object made each render — and `useForm({ shouldFocusError: false })`, or RHF moves focus to the first field instead. Without `focusKey` nothing changes: it focuses whenever errors first appear.
|
|
192
|
+
|
|
193
|
+
## 5.7.2 — SideNav labels hyphenate
|
|
194
|
+
|
|
195
|
+
- **SideNav** (item 64): a long compound word hyphenates at a syllable (`hyphens: auto`) instead of breaking wherever the line runs out — "Marknadsförings-" / "målgrupp" rather than "Marknadsföringsmålgrup" / "p". The browser's dictionary for the page's `lang` decides — Safari and Firefox ship them; Chrome downloads them on demand, so they can be missing (headless and managed machines especially). Breaking anywhere stays the last resort. Only words of 12+ letters hyphenate, with at least 5 on each side (`hyphenate-limit-chars`, Chrome 109+ and Firefox 137+), so ordinary English labels still wrap between words; a long English word in a two-line label can now hyphenate too. Where no dictionary is available, put a soft hyphen in the label — `'Marknadsförings\u00ADmålgrupp'` — which breaks there everywhere, shows a hyphen only when it breaks, and adds no hyphen to the accessible name (it is an invisible format character screen readers skip). Set `<html lang>` per locale.
|
|
196
|
+
|
|
189
197
|
## 5.7.1 — SideNav labels wrap
|
|
190
198
|
|
|
191
199
|
- **SideNav** (item 63): a long label wraps to a second line instead of ending in an ellipsis, so Thai and Swedish names read in full in the 240px nav and the phone drawer. One-line rows stay 36px (44px on touch); a two-line row grows to 44px with its icon centred. Past two lines the label clamps; the accessible name is always complete. The collapsed rail is unchanged.
|
package/dist/aura.bundle.js
CHANGED
|
@@ -3394,14 +3394,50 @@ window.Aura = (() => {
|
|
|
3394
3394
|
const auto = uid(), id = props.id || auto;
|
|
3395
3395
|
const list = items(props.errors);
|
|
3396
3396
|
const box = React21.useRef(null);
|
|
3397
|
+
const keyed = props.focusKey !== void 0;
|
|
3398
|
+
const lastKey = React21.useRef(null), armed = React21.useRef(false), hadErrors = React21.useRef(false);
|
|
3399
|
+
const has = list.length > 0;
|
|
3397
3400
|
React21.useEffect(
|
|
3398
3401
|
function() {
|
|
3399
|
-
const
|
|
3400
|
-
|
|
3402
|
+
const appeared = has && !hadErrors.current;
|
|
3403
|
+
hadErrors.current = has;
|
|
3404
|
+
if (!keyed) {
|
|
3405
|
+
lastKey.current = null;
|
|
3406
|
+
if (appeared && box.current) box.current.focus();
|
|
3407
|
+
return;
|
|
3408
|
+
}
|
|
3409
|
+
if (!lastKey.current) {
|
|
3410
|
+
lastKey.current = { v: props.focusKey };
|
|
3411
|
+
armed.current = has;
|
|
3412
|
+
} else if (!Object.is(lastKey.current.v, props.focusKey)) {
|
|
3413
|
+
lastKey.current = { v: props.focusKey };
|
|
3414
|
+
armed.current = true;
|
|
3415
|
+
}
|
|
3416
|
+
if (armed.current && has && box.current) {
|
|
3417
|
+
armed.current = false;
|
|
3418
|
+
box.current.focus();
|
|
3419
|
+
}
|
|
3401
3420
|
},
|
|
3402
|
-
/* focus when errors first appear, and again whenever focusKey changes (each submit) */
|
|
3403
3421
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3404
|
-
[
|
|
3422
|
+
[has, props.focusKey, keyed]
|
|
3423
|
+
);
|
|
3424
|
+
React21.useEffect(
|
|
3425
|
+
function() {
|
|
3426
|
+
if (!keyed) return;
|
|
3427
|
+
function disarm() {
|
|
3428
|
+
armed.current = false;
|
|
3429
|
+
}
|
|
3430
|
+
const events = ["input", "change", "keydown", "pointerdown"];
|
|
3431
|
+
events.forEach(function(n2) {
|
|
3432
|
+
document.addEventListener(n2, disarm, true);
|
|
3433
|
+
});
|
|
3434
|
+
return function() {
|
|
3435
|
+
events.forEach(function(n2) {
|
|
3436
|
+
document.removeEventListener(n2, disarm, true);
|
|
3437
|
+
});
|
|
3438
|
+
};
|
|
3439
|
+
},
|
|
3440
|
+
[keyed]
|
|
3405
3441
|
);
|
|
3406
3442
|
if (!list.length) return null;
|
|
3407
3443
|
return /* @__PURE__ */ React21.createElement(
|
package/dist/cjs/index.cjs
CHANGED
|
@@ -3376,14 +3376,50 @@ var FormErrorSummary = React21.forwardRef(
|
|
|
3376
3376
|
const auto = uid(), id = props.id || auto;
|
|
3377
3377
|
const list = items(props.errors);
|
|
3378
3378
|
const box = React21.useRef(null);
|
|
3379
|
+
const keyed = props.focusKey !== void 0;
|
|
3380
|
+
const lastKey = React21.useRef(null), armed = React21.useRef(false), hadErrors = React21.useRef(false);
|
|
3381
|
+
const has = list.length > 0;
|
|
3379
3382
|
React21.useEffect(
|
|
3380
3383
|
function() {
|
|
3381
|
-
const
|
|
3382
|
-
|
|
3384
|
+
const appeared = has && !hadErrors.current;
|
|
3385
|
+
hadErrors.current = has;
|
|
3386
|
+
if (!keyed) {
|
|
3387
|
+
lastKey.current = null;
|
|
3388
|
+
if (appeared && box.current) box.current.focus();
|
|
3389
|
+
return;
|
|
3390
|
+
}
|
|
3391
|
+
if (!lastKey.current) {
|
|
3392
|
+
lastKey.current = { v: props.focusKey };
|
|
3393
|
+
armed.current = has;
|
|
3394
|
+
} else if (!Object.is(lastKey.current.v, props.focusKey)) {
|
|
3395
|
+
lastKey.current = { v: props.focusKey };
|
|
3396
|
+
armed.current = true;
|
|
3397
|
+
}
|
|
3398
|
+
if (armed.current && has && box.current) {
|
|
3399
|
+
armed.current = false;
|
|
3400
|
+
box.current.focus();
|
|
3401
|
+
}
|
|
3383
3402
|
},
|
|
3384
|
-
/* focus when errors first appear, and again whenever focusKey changes (each submit) */
|
|
3385
3403
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
3386
|
-
[
|
|
3404
|
+
[has, props.focusKey, keyed]
|
|
3405
|
+
);
|
|
3406
|
+
React21.useEffect(
|
|
3407
|
+
function() {
|
|
3408
|
+
if (!keyed) return;
|
|
3409
|
+
function disarm() {
|
|
3410
|
+
armed.current = false;
|
|
3411
|
+
}
|
|
3412
|
+
const events = ["input", "change", "keydown", "pointerdown"];
|
|
3413
|
+
events.forEach(function(n2) {
|
|
3414
|
+
document.addEventListener(n2, disarm, true);
|
|
3415
|
+
});
|
|
3416
|
+
return function() {
|
|
3417
|
+
events.forEach(function(n2) {
|
|
3418
|
+
document.removeEventListener(n2, disarm, true);
|
|
3419
|
+
});
|
|
3420
|
+
};
|
|
3421
|
+
},
|
|
3422
|
+
[keyed]
|
|
3387
3423
|
);
|
|
3388
3424
|
if (!list.length) return null;
|
|
3389
3425
|
return /* @__PURE__ */ React21.createElement(
|
|
@@ -37,14 +37,50 @@ const FormErrorSummary = React.forwardRef(
|
|
|
37
37
|
const auto = uid(), id = props.id || auto;
|
|
38
38
|
const list = items(props.errors);
|
|
39
39
|
const box = React.useRef(null);
|
|
40
|
+
const keyed = props.focusKey !== void 0;
|
|
41
|
+
const lastKey = React.useRef(null), armed = React.useRef(false), hadErrors = React.useRef(false);
|
|
42
|
+
const has = list.length > 0;
|
|
40
43
|
React.useEffect(
|
|
41
44
|
function() {
|
|
42
|
-
const
|
|
43
|
-
|
|
45
|
+
const appeared = has && !hadErrors.current;
|
|
46
|
+
hadErrors.current = has;
|
|
47
|
+
if (!keyed) {
|
|
48
|
+
lastKey.current = null;
|
|
49
|
+
if (appeared && box.current) box.current.focus();
|
|
50
|
+
return;
|
|
51
|
+
}
|
|
52
|
+
if (!lastKey.current) {
|
|
53
|
+
lastKey.current = { v: props.focusKey };
|
|
54
|
+
armed.current = has;
|
|
55
|
+
} else if (!Object.is(lastKey.current.v, props.focusKey)) {
|
|
56
|
+
lastKey.current = { v: props.focusKey };
|
|
57
|
+
armed.current = true;
|
|
58
|
+
}
|
|
59
|
+
if (armed.current && has && box.current) {
|
|
60
|
+
armed.current = false;
|
|
61
|
+
box.current.focus();
|
|
62
|
+
}
|
|
44
63
|
},
|
|
45
|
-
/* focus when errors first appear, and again whenever focusKey changes (each submit) */
|
|
46
64
|
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
47
|
-
[
|
|
65
|
+
[has, props.focusKey, keyed]
|
|
66
|
+
);
|
|
67
|
+
React.useEffect(
|
|
68
|
+
function() {
|
|
69
|
+
if (!keyed) return;
|
|
70
|
+
function disarm() {
|
|
71
|
+
armed.current = false;
|
|
72
|
+
}
|
|
73
|
+
const events = ["input", "change", "keydown", "pointerdown"];
|
|
74
|
+
events.forEach(function(n) {
|
|
75
|
+
document.addEventListener(n, disarm, true);
|
|
76
|
+
});
|
|
77
|
+
return function() {
|
|
78
|
+
events.forEach(function(n) {
|
|
79
|
+
document.removeEventListener(n, disarm, true);
|
|
80
|
+
});
|
|
81
|
+
};
|
|
82
|
+
},
|
|
83
|
+
[keyed]
|
|
48
84
|
);
|
|
49
85
|
if (!list.length) return null;
|
|
50
86
|
return /* @__PURE__ */ React.createElement(
|
package/dist/index.d.cts
CHANGED
|
@@ -355,7 +355,10 @@ export interface FormErrorSummaryProps {
|
|
|
355
355
|
title?: React$1.ReactNode | undefined;
|
|
356
356
|
/** Called with the field when a link is followed, e.g. react-hook-form's `setFocus`. Default: focus the element whose id (or name) is the field. */
|
|
357
357
|
onSelect?: ((field: string) => void) | undefined;
|
|
358
|
-
/** Change it on every submit (e.g. `formState.submitCount`)
|
|
358
|
+
/** Change it on every submit (e.g. `formState.submitCount`). With it, the summary takes focus only after a submit —
|
|
359
|
+
* never while someone types and live errors come and go (5.7.3). Without it, it takes focus whenever errors
|
|
360
|
+
* first appear; with react-hook-form's live `errors`, always pass `focusKey={formState.submitCount}` (a primitive, not a new object
|
|
361
|
+
* each render) and `useForm({ shouldFocusError: false })`, so RHF doesn't move focus to the field first. */
|
|
359
362
|
focusKey?: unknown;
|
|
360
363
|
className?: string | undefined;
|
|
361
364
|
id?: string | undefined;
|
package/dist/index.d.ts
CHANGED
|
@@ -355,7 +355,10 @@ export interface FormErrorSummaryProps {
|
|
|
355
355
|
title?: React$1.ReactNode | undefined;
|
|
356
356
|
/** Called with the field when a link is followed, e.g. react-hook-form's `setFocus`. Default: focus the element whose id (or name) is the field. */
|
|
357
357
|
onSelect?: ((field: string) => void) | undefined;
|
|
358
|
-
/** Change it on every submit (e.g. `formState.submitCount`)
|
|
358
|
+
/** Change it on every submit (e.g. `formState.submitCount`). With it, the summary takes focus only after a submit —
|
|
359
|
+
* never while someone types and live errors come and go (5.7.3). Without it, it takes focus whenever errors
|
|
360
|
+
* first appear; with react-hook-form's live `errors`, always pass `focusKey={formState.submitCount}` (a primitive, not a new object
|
|
361
|
+
* each render) and `useForm({ shouldFocusError: false })`, so RHF doesn't move focus to the field first. */
|
|
359
362
|
focusKey?: unknown;
|
|
360
363
|
className?: string | undefined;
|
|
361
364
|
id?: string | undefined;
|
package/dist/styles.css
CHANGED
|
@@ -653,9 +653,12 @@ a.aura-tab { text-decoration: none; }
|
|
|
653
653
|
/* Touch (5.7): rows are 44px on coarse pointers (AppShell's phone drawer); desktop keeps 36px. */
|
|
654
654
|
@media (pointer: coarse) { .aura-nav__item { min-height: var(--aura-touch-target); } }
|
|
655
655
|
/* 5.7.1: a long label wraps to a second line (the row grows; one-line rows stay 36px) instead of an ellipsis with no
|
|
656
|
-
* way to read the rest — Thai and Swedish names in a 240px nav or the phone drawer. Beyond two lines it clamps.
|
|
656
|
+
* way to read the rest — Thai and Swedish names in a 240px nav or the phone drawer. Beyond two lines it clamps.
|
|
657
|
+
* 5.7.2: a long compound word hyphenates at a syllable where the browser has a dictionary for the page's `lang`
|
|
658
|
+
* ("Marknadsförings-" / "målgrupp"); breaking anywhere is only the last resort. Only words of 12+ letters hyphenate,
|
|
659
|
+
* with 5+ on each side, so ordinary English labels keep wrapping between words. */
|
|
657
660
|
.aura-nav__label {
|
|
658
|
-
flex: 1; min-width: 0; line-height: 18px; overflow: hidden; overflow-wrap: anywhere;
|
|
661
|
+
flex: 1; min-width: 0; line-height: 18px; overflow: hidden; overflow-wrap: anywhere; -webkit-hyphens: auto; hyphens: auto; hyphenate-limit-chars: 12 5 5;
|
|
659
662
|
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
|
|
660
663
|
}
|
|
661
664
|
.aura-nav__count { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--aura-fg-secondary); }
|
package/dist/styles.layer.css
CHANGED
|
@@ -655,9 +655,12 @@ a.aura-tab { text-decoration: none; }
|
|
|
655
655
|
/* Touch (5.7): rows are 44px on coarse pointers (AppShell's phone drawer); desktop keeps 36px. */
|
|
656
656
|
@media (pointer: coarse) { .aura-nav__item { min-height: var(--aura-touch-target); } }
|
|
657
657
|
/* 5.7.1: a long label wraps to a second line (the row grows; one-line rows stay 36px) instead of an ellipsis with no
|
|
658
|
-
* way to read the rest — Thai and Swedish names in a 240px nav or the phone drawer. Beyond two lines it clamps.
|
|
658
|
+
* way to read the rest — Thai and Swedish names in a 240px nav or the phone drawer. Beyond two lines it clamps.
|
|
659
|
+
* 5.7.2: a long compound word hyphenates at a syllable where the browser has a dictionary for the page's `lang`
|
|
660
|
+
* ("Marknadsförings-" / "målgrupp"); breaking anywhere is only the last resort. Only words of 12+ letters hyphenate,
|
|
661
|
+
* with 5+ on each side, so ordinary English labels keep wrapping between words. */
|
|
659
662
|
.aura-nav__label {
|
|
660
|
-
flex: 1; min-width: 0; line-height: 18px; overflow: hidden; overflow-wrap: anywhere;
|
|
663
|
+
flex: 1; min-width: 0; line-height: 18px; overflow: hidden; overflow-wrap: anywhere; -webkit-hyphens: auto; hyphens: auto; hyphenate-limit-chars: 12 5 5;
|
|
661
664
|
display: -webkit-box; -webkit-box-orient: vertical; -webkit-line-clamp: 2; line-clamp: 2;
|
|
662
665
|
}
|
|
663
666
|
.aura-nav__count { font-size: 11px; font-variant-numeric: tabular-nums; color: var(--aura-fg-secondary); }
|
package/package.json
CHANGED