@12-apps/ui 6.39.10 → 6.39.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/_internal/components/data-display/DataViews/DataViewsTableBase.js +4 -4
- package/dist/_internal/components/data-display/DataViews/SaveViewModal.js +2 -2
- package/dist/_internal/components/data-display/DataViews/data-views-table-parts.js +3 -3
- package/dist/_internal/components/data-display/HoverCard/HoverCard.hooks.js +1 -1
- package/dist/_internal/components/data-display/HoverCard/HoverCard.js +2 -2
- package/dist/_internal/components/data-display/index.js +2 -2
- package/dist/_internal/components/form/CepField/CepField.js +2 -2
- package/dist/_internal/components/form/Input/Input.js +1 -1
- package/dist/_internal/components/form/RichTextEditor/RichTextEditor.hooks.js +1 -1
- package/dist/_internal/components/form/RichTextEditor/RichTextEditor.js +2 -2
- package/dist/_internal/components/form/index.js +4 -4
- package/dist/_internal/components/form/total-form/Fields/CepField.js +3 -3
- package/dist/_internal/components/form/total-form/Fields/TextField.js +2 -2
- package/dist/_internal/components/form/total-form/Fields/index.js +5 -5
- package/dist/_internal/components/index.js +6 -6
- package/dist/{chunk-F6D6HXZU.js → chunk-7X7ZHJHM.js} +2 -2
- package/dist/{chunk-DAHYLPK7.js → chunk-CMV6BRQ3.js} +13 -4
- package/dist/chunk-CMV6BRQ3.js.map +1 -0
- package/dist/{chunk-774B27QA.js → chunk-EUACQR6N.js} +4 -3
- package/dist/chunk-EUACQR6N.js.map +1 -0
- package/dist/{chunk-YS67XMOP.js → chunk-I5ZVCY2N.js} +3 -3
- package/dist/{chunk-A5B7B4HT.js → chunk-INMKA5SF.js} +2 -2
- package/dist/{chunk-VJG7NMGL.js → chunk-IYF246UA.js} +2 -2
- package/dist/{chunk-VURQZBFH.js → chunk-JRJCBDGN.js} +16 -3
- package/dist/chunk-JRJCBDGN.js.map +1 -0
- package/dist/{chunk-7EM4THID.js → chunk-RTEY5COP.js} +2 -2
- package/dist/{chunk-2XX45PC5.js → chunk-TGXCG62P.js} +2 -2
- package/dist/{chunk-SXYIXCUB.js → chunk-VNWZWQF3.js} +41 -23
- package/dist/chunk-VNWZWQF3.js.map +1 -0
- package/dist/{chunk-SSS64KYB.js → chunk-Z43N7ASP.js} +2 -2
- package/dist/{chunk-6MFMLQ72.js → chunk-ZNDDILNQ.js} +2 -2
- package/dist/data-display/DataViews.js +4 -4
- package/dist/data-display/HoverCard.js +2 -2
- package/dist/form/CepField.js +2 -2
- package/dist/form/Input.js +1 -1
- package/dist/form/RichTextEditor.js +2 -2
- package/dist/form/total-form.js +5 -5
- package/dist/types/components/data-display/HoverCard/HoverCard.d.ts.map +1 -1
- package/dist/types/components/data-display/HoverCard/HoverCard.hooks.d.ts +1 -0
- package/dist/types/components/data-display/HoverCard/HoverCard.hooks.d.ts.map +1 -1
- package/dist/types/components/form/Input/Input.d.ts.map +1 -1
- package/dist/types/components/form/RichTextEditor/RichTextEditor.hooks.d.ts.map +1 -1
- package/package.json +1 -1
- package/src/components/data-display/HoverCard/HoverCard.hooks.ts +80 -25
- package/src/components/data-display/HoverCard/HoverCard.md +1 -0
- package/src/components/data-display/HoverCard/HoverCard.tsx +11 -2
- package/src/components/form/Input/Input.tsx +40 -1
- package/src/components/form/RichTextEditor/RichTextEditor.hooks.ts +20 -3
- package/dist/chunk-774B27QA.js.map +0 -1
- package/dist/chunk-DAHYLPK7.js.map +0 -1
- package/dist/chunk-SXYIXCUB.js.map +0 -1
- package/dist/chunk-VURQZBFH.js.map +0 -1
- /package/dist/{chunk-F6D6HXZU.js.map → chunk-7X7ZHJHM.js.map} +0 -0
- /package/dist/{chunk-YS67XMOP.js.map → chunk-I5ZVCY2N.js.map} +0 -0
- /package/dist/{chunk-A5B7B4HT.js.map → chunk-INMKA5SF.js.map} +0 -0
- /package/dist/{chunk-VJG7NMGL.js.map → chunk-IYF246UA.js.map} +0 -0
- /package/dist/{chunk-7EM4THID.js.map → chunk-RTEY5COP.js.map} +0 -0
- /package/dist/{chunk-2XX45PC5.js.map → chunk-TGXCG62P.js.map} +0 -0
- /package/dist/{chunk-SSS64KYB.js.map → chunk-Z43N7ASP.js.map} +0 -0
- /package/dist/{chunk-6MFMLQ72.js.map → chunk-ZNDDILNQ.js.map} +0 -0
|
@@ -1,5 +1,9 @@
|
|
|
1
1
|
// src/components/data-display/HoverCard/HoverCard.hooks.ts
|
|
2
2
|
import React from "react";
|
|
3
|
+
var clearPending = (ref) => {
|
|
4
|
+
if (ref.current !== void 0) window.clearTimeout(ref.current);
|
|
5
|
+
ref.current = void 0;
|
|
6
|
+
};
|
|
3
7
|
var useHoverHandlers = ({
|
|
4
8
|
isTouchDevice,
|
|
5
9
|
touchEnabled,
|
|
@@ -25,7 +29,7 @@ var useHoverHandlers = ({
|
|
|
25
29
|
const triggerHandlers = {
|
|
26
30
|
onMouseEnter: (event) => {
|
|
27
31
|
if (hoverIgnored) return;
|
|
28
|
-
openCard(event);
|
|
32
|
+
openCard(event.currentTarget);
|
|
29
33
|
},
|
|
30
34
|
onMouseLeave: () => {
|
|
31
35
|
if (hoverIgnored) return;
|
|
@@ -34,16 +38,15 @@ var useHoverHandlers = ({
|
|
|
34
38
|
onTouchStart: (event) => {
|
|
35
39
|
if (!touchEnabled) return;
|
|
36
40
|
if (touchTimeout) window.clearTimeout(touchTimeout);
|
|
37
|
-
|
|
41
|
+
const trigger = event.currentTarget;
|
|
42
|
+
setTouchTimeout(window.setTimeout(() => openCard(trigger), enterDelay));
|
|
38
43
|
},
|
|
39
44
|
// End and cancel do the same thing: the press did not become a long press.
|
|
40
45
|
onTouchEnd: cancelTouch,
|
|
41
46
|
onTouchCancel: cancelTouch
|
|
42
47
|
};
|
|
43
48
|
const cardHandlers = {
|
|
44
|
-
onMouseEnter: () =>
|
|
45
|
-
if (exitTimeoutRef.current) window.clearTimeout(exitTimeoutRef.current);
|
|
46
|
-
},
|
|
49
|
+
onMouseEnter: () => clearPending(exitTimeoutRef),
|
|
47
50
|
onMouseLeave: closeCard
|
|
48
51
|
};
|
|
49
52
|
return { triggerHandlers, cardHandlers };
|
|
@@ -58,6 +61,25 @@ var useEscapeKey = (active, onEscape) => {
|
|
|
58
61
|
return () => document.removeEventListener("keydown", handleEscape);
|
|
59
62
|
}, [active, onEscape]);
|
|
60
63
|
};
|
|
64
|
+
var useClickAway = (active, inside, onAway) => {
|
|
65
|
+
React.useEffect(() => {
|
|
66
|
+
if (!active) return;
|
|
67
|
+
const handlePointerDown = (event) => {
|
|
68
|
+
const target = event.target;
|
|
69
|
+
if (target && inside().some((el) => el?.contains(target))) return;
|
|
70
|
+
onAway();
|
|
71
|
+
};
|
|
72
|
+
document.addEventListener("pointerdown", handlePointerDown, true);
|
|
73
|
+
return () => document.removeEventListener("pointerdown", handlePointerDown, true);
|
|
74
|
+
}, [active, inside, onAway]);
|
|
75
|
+
};
|
|
76
|
+
var useClearOnUnmount = (...refs) => {
|
|
77
|
+
React.useEffect(
|
|
78
|
+
() => () => refs.forEach(clearPending),
|
|
79
|
+
// The refs are stable objects: a mount/unmount pair, as before.
|
|
80
|
+
[]
|
|
81
|
+
);
|
|
82
|
+
};
|
|
61
83
|
var useHoverCard = ({
|
|
62
84
|
disabled,
|
|
63
85
|
touchEnabled,
|
|
@@ -71,18 +93,17 @@ var useHoverCard = ({
|
|
|
71
93
|
const [isTouchDevice, setIsTouchDevice] = React.useState(false);
|
|
72
94
|
const enterTimeoutRef = React.useRef(void 0);
|
|
73
95
|
const exitTimeoutRef = React.useRef(void 0);
|
|
96
|
+
const contentRef = React.useRef(null);
|
|
74
97
|
React.useEffect(() => {
|
|
75
98
|
setIsTouchDevice("ontouchstart" in window);
|
|
76
99
|
}, []);
|
|
77
100
|
const openCard = React.useCallback(
|
|
78
|
-
(
|
|
101
|
+
(trigger) => {
|
|
79
102
|
if (disabled) return;
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
}
|
|
83
|
-
const target = event.currentTarget;
|
|
103
|
+
clearPending(exitTimeoutRef);
|
|
104
|
+
clearPending(enterTimeoutRef);
|
|
84
105
|
enterTimeoutRef.current = window.setTimeout(() => {
|
|
85
|
-
setAnchorEl(
|
|
106
|
+
setAnchorEl(trigger);
|
|
86
107
|
setIsOpen(true);
|
|
87
108
|
onOpen?.();
|
|
88
109
|
}, enterDelay);
|
|
@@ -90,9 +111,8 @@ var useHoverCard = ({
|
|
|
90
111
|
[disabled, enterDelay, onOpen]
|
|
91
112
|
);
|
|
92
113
|
const closeCard = React.useCallback(() => {
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
}
|
|
114
|
+
clearPending(enterTimeoutRef);
|
|
115
|
+
clearPending(exitTimeoutRef);
|
|
96
116
|
exitTimeoutRef.current = window.setTimeout(() => {
|
|
97
117
|
setIsOpen(false);
|
|
98
118
|
setAnchorEl(null);
|
|
@@ -100,6 +120,8 @@ var useHoverCard = ({
|
|
|
100
120
|
}, exitDelay);
|
|
101
121
|
}, [exitDelay, onClose]);
|
|
102
122
|
const handleClose = React.useCallback(() => {
|
|
123
|
+
clearPending(enterTimeoutRef);
|
|
124
|
+
clearPending(exitTimeoutRef);
|
|
103
125
|
setIsOpen(false);
|
|
104
126
|
setAnchorEl(null);
|
|
105
127
|
onClose?.();
|
|
@@ -112,18 +134,14 @@ var useHoverCard = ({
|
|
|
112
134
|
closeCard,
|
|
113
135
|
exitTimeoutRef
|
|
114
136
|
});
|
|
137
|
+
const insideCard = React.useCallback(() => [anchorEl, contentRef.current], [anchorEl]);
|
|
115
138
|
useEscapeKey(isOpen, handleClose);
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
if (exitTimeoutRef.current) window.clearTimeout(exitTimeoutRef.current);
|
|
120
|
-
},
|
|
121
|
-
[]
|
|
122
|
-
);
|
|
123
|
-
return { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers };
|
|
139
|
+
useClickAway(isOpen, insideCard, handleClose);
|
|
140
|
+
useClearOnUnmount(enterTimeoutRef, exitTimeoutRef);
|
|
141
|
+
return { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers, contentRef };
|
|
124
142
|
};
|
|
125
143
|
|
|
126
144
|
export {
|
|
127
145
|
useHoverCard
|
|
128
146
|
};
|
|
129
|
-
//# sourceMappingURL=chunk-
|
|
147
|
+
//# sourceMappingURL=chunk-VNWZWQF3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/data-display/HoverCard/HoverCard.hooks.ts"],"sourcesContent":["import React from 'react';\n\ntype TimerRef = React.MutableRefObject<number | undefined>;\n\n/**\n * Cancel the timer a ref holds, and forget it. Every place that schedules\n * or dismisses goes through this, so no timer outlives the ref that could\n * cancel it (FUT-2619: an overwritten enter timer reopened a dismissed card).\n */\nconst clearPending = (ref: TimerRef) => {\n if (ref.current !== undefined) window.clearTimeout(ref.current);\n ref.current = undefined;\n};\n\ninterface HandlerInput {\n isTouchDevice: boolean;\n touchEnabled: boolean;\n enterDelay: number;\n openCard: (trigger: HTMLElement) => void;\n closeCard: () => void;\n exitTimeoutRef: TimerRef;\n}\n\n/**\n * The pointer and touch handlers, over the open/close pair. Touch gets a\n * long-press because there is no hover to respond to; the card's own handlers\n * keep it open while the pointer is travelling onto it.\n */\nconst useHoverHandlers = ({\n isTouchDevice,\n touchEnabled,\n enterDelay,\n openCard,\n closeCard,\n exitTimeoutRef,\n}: HandlerInput) => {\n const [touchTimeout, setTouchTimeout] = React.useState<number>();\n\n /** A touch device with touch disabled has no hover to respond to. */\n const hoverIgnored = isTouchDevice && !touchEnabled;\n\n const cancelTouch = React.useCallback(() => {\n if (touchTimeout) {\n window.clearTimeout(touchTimeout);\n setTouchTimeout(undefined);\n }\n }, [touchTimeout]);\n\n React.useEffect(\n () => () => {\n if (touchTimeout) window.clearTimeout(touchTimeout);\n },\n [touchTimeout],\n );\n\n const triggerHandlers = {\n onMouseEnter: (event: React.MouseEvent<HTMLElement>) => {\n if (hoverIgnored) return;\n openCard(event.currentTarget);\n },\n onMouseLeave: () => {\n if (hoverIgnored) return;\n closeCard();\n },\n onTouchStart: (event: React.TouchEvent<HTMLElement>) => {\n if (!touchEnabled) return;\n if (touchTimeout) window.clearTimeout(touchTimeout);\n // Read NOW: React nulls `currentTarget` once the handler returns, so\n // the long-press timer used to open the card with no anchor at all.\n const trigger = event.currentTarget;\n setTouchTimeout(window.setTimeout(() => openCard(trigger), enterDelay));\n },\n // End and cancel do the same thing: the press did not become a long press.\n onTouchEnd: cancelTouch,\n onTouchCancel: cancelTouch,\n };\n\n /** Moving onto the card keeps it open; leaving it starts the exit delay. */\n const cardHandlers = {\n onMouseEnter: () => clearPending(exitTimeoutRef),\n onMouseLeave: closeCard,\n };\n\n return { triggerHandlers, cardHandlers };\n};\n\n/** Escape dismisses the card while it is open. */\nconst useEscapeKey = (active: boolean, onEscape: () => void) => {\n React.useEffect(() => {\n if (!active) return;\n\n const handleEscape = (event: globalThis.KeyboardEvent) => {\n if (event.key === 'Escape') onEscape();\n };\n\n document.addEventListener('keydown', handleEscape);\n return () => document.removeEventListener('keydown', handleEscape);\n }, [active, onEscape]);\n};\n\n/**\n * A press anywhere outside the trigger and the card dismisses it.\n *\n * Touch has no mouse-leave, and MUI's own click-away was the popover's\n * backdrop, which `pointer-events: none` on the popover root (so the page\n * under an open card stays usable) takes out of play. So this listens on the\n * document itself, in the CAPTURE phase, where a `stopPropagation` further in\n * cannot swallow it. It only observes: no `preventDefault`, so the tap or\n * click still reaches whatever it landed on. Attached while open, removed on\n * close and on unmount.\n */\nconst useClickAway = (\n active: boolean,\n inside: () => ReadonlyArray<Element | null>,\n onAway: () => void,\n) => {\n React.useEffect(() => {\n if (!active) return;\n\n const handlePointerDown = (event: globalThis.PointerEvent) => {\n const target = event.target as Node | null;\n if (target && inside().some((el) => el?.contains(target))) return;\n onAway();\n };\n\n document.addEventListener('pointerdown', handlePointerDown, true);\n return () => document.removeEventListener('pointerdown', handlePointerDown, true);\n }, [active, inside, onAway]);\n};\n\n/** Whatever is still pending when the card unmounts is cancelled with it. */\nconst useClearOnUnmount = (...refs: TimerRef[]) => {\n React.useEffect(\n () => () => refs.forEach(clearPending),\n // The refs are stable objects: a mount/unmount pair, as before.\n [],\n );\n};\n\ninterface HoverCardTimingInput {\n disabled: boolean;\n touchEnabled: boolean;\n enterDelay: number;\n exitDelay: number;\n onOpen?: () => void;\n onClose?: () => void;\n}\n\n/**\n * When the card is open, and the delays that decide it.\n *\n * Three timers are in play: the enter delay before opening, the exit delay\n * before closing (so the pointer can travel from the trigger onto the card\n * without dismissing it), and the long-press timer on touch. Each cancels the\n * others' pending work, which is why they live together.\n */\nexport const useHoverCard = ({\n disabled,\n touchEnabled,\n enterDelay,\n exitDelay,\n onOpen,\n onClose,\n}: HoverCardTimingInput) => {\n const [anchorEl, setAnchorEl] = React.useState<HTMLElement | null>(null);\n const [isOpen, setIsOpen] = React.useState(false);\n const [isTouchDevice, setIsTouchDevice] = React.useState(false);\n const enterTimeoutRef = React.useRef<number | undefined>(undefined);\n const exitTimeoutRef = React.useRef<number | undefined>(undefined);\n /** The card's own box, inside the popover paper: a press here is not \"away\". */\n const contentRef = React.useRef<HTMLDivElement | null>(null);\n\n // Probed in an effect, not during render, so SSR and hydration stay intact.\n React.useEffect(() => {\n setIsTouchDevice('ontouchstart' in window);\n }, []);\n\n const openCard = React.useCallback(\n (trigger: HTMLElement) => {\n if (disabled) return;\n\n clearPending(exitTimeoutRef);\n // A second enter before the delay ran out (a click and then a hover, or\n // the pointer jittering across the edge) must REPLACE the pending open,\n // not add one: an overwritten timer can no longer be cancelled, and it\n // reopened the card after Escape had closed it.\n clearPending(enterTimeoutRef);\n\n enterTimeoutRef.current = window.setTimeout(() => {\n setAnchorEl(trigger);\n setIsOpen(true);\n onOpen?.();\n }, enterDelay);\n },\n [disabled, enterDelay, onOpen],\n );\n\n const closeCard = React.useCallback(() => {\n clearPending(enterTimeoutRef);\n clearPending(exitTimeoutRef);\n\n exitTimeoutRef.current = window.setTimeout(() => {\n setIsOpen(false);\n setAnchorEl(null);\n onClose?.();\n }, exitDelay);\n }, [exitDelay, onClose]);\n\n // Escape and the popover's own close are FINAL: nothing still pending may\n // reopen the card behind them. It opens again on the next enter.\n const handleClose = React.useCallback(() => {\n clearPending(enterTimeoutRef);\n clearPending(exitTimeoutRef);\n setIsOpen(false);\n setAnchorEl(null);\n onClose?.();\n }, [onClose]);\n\n const { triggerHandlers, cardHandlers } = useHoverHandlers({\n isTouchDevice,\n touchEnabled,\n enterDelay,\n openCard,\n closeCard,\n exitTimeoutRef,\n });\n\n const insideCard = React.useCallback(() => [anchorEl, contentRef.current], [anchorEl]);\n\n useEscapeKey(isOpen, handleClose);\n useClickAway(isOpen, insideCard, handleClose);\n useClearOnUnmount(enterTimeoutRef, exitTimeoutRef);\n\n return { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers, contentRef };\n};\n"],"mappings":"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","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "./chunk-4XK6JQ4D.js";
|
|
4
4
|
import {
|
|
5
5
|
Input
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-JRJCBDGN.js";
|
|
7
7
|
import {
|
|
8
8
|
FormControl,
|
|
9
9
|
FormLabel,
|
|
@@ -38,4 +38,4 @@ function TextField({ name, label, type = "text", placeholder }) {
|
|
|
38
38
|
export {
|
|
39
39
|
TextField
|
|
40
40
|
};
|
|
41
|
-
//# sourceMappingURL=chunk-
|
|
41
|
+
//# sourceMappingURL=chunk-Z43N7ASP.js.map
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
import {
|
|
11
11
|
ViewDialogs,
|
|
12
12
|
ViewMutationErrorAlert
|
|
13
|
-
} from "./chunk-
|
|
13
|
+
} from "./chunk-INMKA5SF.js";
|
|
14
14
|
import {
|
|
15
15
|
useSavedViewsController
|
|
16
16
|
} from "./chunk-HR7YTBR7.js";
|
|
@@ -178,4 +178,4 @@ function renderViewDialogs(ctl, fields, columns, views, testIdPrefix, onSaved) {
|
|
|
178
178
|
export {
|
|
179
179
|
DataViewsTableBase
|
|
180
180
|
};
|
|
181
|
-
//# sourceMappingURL=chunk-
|
|
181
|
+
//# sourceMappingURL=chunk-ZNDDILNQ.js.map
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
} from "../chunk-I5T5PQQW.js";
|
|
4
4
|
import {
|
|
5
5
|
DataViewsTableBase
|
|
6
|
-
} from "../chunk-
|
|
6
|
+
} from "../chunk-ZNDDILNQ.js";
|
|
7
7
|
import "../chunk-3333KCAW.js";
|
|
8
8
|
import {
|
|
9
9
|
DataViewsGrid
|
|
@@ -68,8 +68,8 @@ import {
|
|
|
68
68
|
cardScaleForZoom
|
|
69
69
|
} from "../chunk-XZJM6EPC.js";
|
|
70
70
|
import "../chunk-M4KVKQRN.js";
|
|
71
|
-
import "../chunk-
|
|
72
|
-
import "../chunk-
|
|
71
|
+
import "../chunk-INMKA5SF.js";
|
|
72
|
+
import "../chunk-IYF246UA.js";
|
|
73
73
|
import "../chunk-ECIHOZPK.js";
|
|
74
74
|
import "../chunk-5M4TYFR3.js";
|
|
75
75
|
import "../chunk-HR7YTBR7.js";
|
|
@@ -151,7 +151,7 @@ import "../chunk-LXT3X33C.js";
|
|
|
151
151
|
import "../chunk-DMNZD3Q7.js";
|
|
152
152
|
import "../chunk-JC22FNFA.js";
|
|
153
153
|
import "../chunk-QYKDY4EI.js";
|
|
154
|
-
import "../chunk-
|
|
154
|
+
import "../chunk-JRJCBDGN.js";
|
|
155
155
|
import "../chunk-SL6454K7.js";
|
|
156
156
|
import "../chunk-THMT3G3L.js";
|
|
157
157
|
import "../chunk-RNIU447I.js";
|
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "../chunk-6OS4AKWT.js";
|
|
2
2
|
import {
|
|
3
3
|
HoverCard
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-CMV6BRQ3.js";
|
|
5
5
|
import "../chunk-BSYPPO2I.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-VNWZWQF3.js";
|
|
7
7
|
import "../chunk-NRIV2CQ2.js";
|
|
8
8
|
import "../chunk-ZHV7TCAC.js";
|
|
9
9
|
import "../chunk-HPK33ACY.js";
|
package/dist/form/CepField.js
CHANGED
|
@@ -1,9 +1,9 @@
|
|
|
1
1
|
import "../chunk-6XLMTOJ3.js";
|
|
2
2
|
import {
|
|
3
3
|
CepField
|
|
4
|
-
} from "../chunk-
|
|
4
|
+
} from "../chunk-7X7ZHJHM.js";
|
|
5
5
|
import "../chunk-QYKDY4EI.js";
|
|
6
|
-
import "../chunk-
|
|
6
|
+
import "../chunk-JRJCBDGN.js";
|
|
7
7
|
import "../chunk-SL6454K7.js";
|
|
8
8
|
import "../chunk-THMT3G3L.js";
|
|
9
9
|
import "../chunk-RNIU447I.js";
|
package/dist/form/Input.js
CHANGED
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
import "../chunk-WMY276HD.js";
|
|
2
2
|
import {
|
|
3
3
|
RichTextEditor
|
|
4
|
-
} from "../chunk-
|
|
5
|
-
import "../chunk-
|
|
4
|
+
} from "../chunk-RTEY5COP.js";
|
|
5
|
+
import "../chunk-EUACQR6N.js";
|
|
6
6
|
import "../chunk-UZOG6HNN.js";
|
|
7
7
|
import "../chunk-33LAPBYB.js";
|
|
8
8
|
import "../chunk-WVZBYTIK.js";
|
package/dist/form/total-form.js
CHANGED
|
@@ -9,15 +9,15 @@ import {
|
|
|
9
9
|
} from "../chunk-CPAJYJUD.js";
|
|
10
10
|
import {
|
|
11
11
|
Fields_exports
|
|
12
|
-
} from "../chunk-
|
|
12
|
+
} from "../chunk-I5ZVCY2N.js";
|
|
13
13
|
import "../chunk-FNG33MKA.js";
|
|
14
14
|
import {
|
|
15
15
|
TextField
|
|
16
|
-
} from "../chunk-
|
|
16
|
+
} from "../chunk-Z43N7ASP.js";
|
|
17
17
|
import {
|
|
18
18
|
SelectField
|
|
19
19
|
} from "../chunk-PWZHCBG3.js";
|
|
20
|
-
import "../chunk-
|
|
20
|
+
import "../chunk-TGXCG62P.js";
|
|
21
21
|
import {
|
|
22
22
|
CategoryField
|
|
23
23
|
} from "../chunk-T6EKPFKZ.js";
|
|
@@ -33,7 +33,7 @@ import "../chunk-W5TI46QN.js";
|
|
|
33
33
|
import "../chunk-JD5BVIM7.js";
|
|
34
34
|
import "../chunk-VQYR64QB.js";
|
|
35
35
|
import "../chunk-6XLMTOJ3.js";
|
|
36
|
-
import "../chunk-
|
|
36
|
+
import "../chunk-7X7ZHJHM.js";
|
|
37
37
|
import "../chunk-N5ZWC6CB.js";
|
|
38
38
|
import "../chunk-BKASAYQL.js";
|
|
39
39
|
import "../chunk-2JNHWKQK.js";
|
|
@@ -52,7 +52,7 @@ import "../chunk-LXT3X33C.js";
|
|
|
52
52
|
import "../chunk-DMNZD3Q7.js";
|
|
53
53
|
import "../chunk-JC22FNFA.js";
|
|
54
54
|
import "../chunk-QYKDY4EI.js";
|
|
55
|
-
import "../chunk-
|
|
55
|
+
import "../chunk-JRJCBDGN.js";
|
|
56
56
|
import "../chunk-SL6454K7.js";
|
|
57
57
|
import "../chunk-THMT3G3L.js";
|
|
58
58
|
import "../chunk-RNIU447I.js";
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HoverCard.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/HoverCard/HoverCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,OAAO,KAAK,EAA0C,cAAc,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"HoverCard.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/HoverCard/HoverCard.tsx"],"names":[],"mappings":"AAKA,OAAO,KAAK,MAAM,OAAO,CAAC;AAS1B,OAAO,KAAK,EAA0C,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAsEhG,eAAO,MAAM,SAAS,oGA8EpB,CAAC"}
|
|
@@ -30,6 +30,7 @@ export declare const useHoverCard: ({ disabled, touchEnabled, enterDelay, exitDe
|
|
|
30
30
|
onMouseEnter: () => void;
|
|
31
31
|
onMouseLeave: () => void;
|
|
32
32
|
};
|
|
33
|
+
contentRef: React.RefObject<HTMLDivElement | null>;
|
|
33
34
|
};
|
|
34
35
|
export {};
|
|
35
36
|
//# sourceMappingURL=HoverCard.hooks.d.ts.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"HoverCard.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/HoverCard/HoverCard.hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"HoverCard.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/data-display/HoverCard/HoverCard.hooks.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,MAAM,OAAO,CAAC;AA2I1B,UAAU,oBAAoB;IAC5B,QAAQ,EAAE,OAAO,CAAC;IAClB,YAAY,EAAE,OAAO,CAAC;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;CACtB;AAED;;;;;;;GAOG;AACH,eAAO,MAAM,YAAY,GAAI,qEAO1B,oBAAoB;;;;;8BA3GG,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC;;8BAQ7B,KAAK,CAAC,UAAU,CAAC,WAAW,CAAC;;;;;;4BA7CpC,IAAI;;;CAuNtB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Input/Input.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAgB1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;
|
|
1
|
+
{"version":3,"file":"Input.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/Input/Input.tsx"],"names":[],"mappings":"AAGA,OAAO,KAAK,MAAM,OAAO,CAAC;AAgB1B,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,eAAe,CAAC;AAyGhD,eAAO,MAAM,KAAK,qFAsEjB,CAAC"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"RichTextEditor.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;
|
|
1
|
+
{"version":3,"file":"RichTextEditor.hooks.d.ts","sourceRoot":"","sources":["../../../../../src/components/form/RichTextEditor/RichTextEditor.hooks.ts"],"names":[],"mappings":"AACA,OAAO,KAAK,KAAK,MAAM,OAAO,CAAC;AAyF/B,eAAO,MAAM,iBAAiB,GAAI,sEAQ/B;IACD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,CAAC,KAAK,EAAE,MAAM,KAAK,IAAI,CAAC;IACnC,OAAO,CAAC,EAAE,MAAM,IAAI,CAAC;IACrB,MAAM,CAAC,EAAE,MAAM,IAAI,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;;;;;;;8BAWgB,MAAM,gBAAgB,MAAM;CAyC5C,CAAC"}
|
package/package.json
CHANGED
|
@@ -1,12 +1,24 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
|
|
3
|
+
type TimerRef = React.MutableRefObject<number | undefined>;
|
|
4
|
+
|
|
5
|
+
/**
|
|
6
|
+
* Cancel the timer a ref holds, and forget it. Every place that schedules
|
|
7
|
+
* or dismisses goes through this, so no timer outlives the ref that could
|
|
8
|
+
* cancel it (FUT-2619: an overwritten enter timer reopened a dismissed card).
|
|
9
|
+
*/
|
|
10
|
+
const clearPending = (ref: TimerRef) => {
|
|
11
|
+
if (ref.current !== undefined) window.clearTimeout(ref.current);
|
|
12
|
+
ref.current = undefined;
|
|
13
|
+
};
|
|
14
|
+
|
|
3
15
|
interface HandlerInput {
|
|
4
16
|
isTouchDevice: boolean;
|
|
5
17
|
touchEnabled: boolean;
|
|
6
18
|
enterDelay: number;
|
|
7
|
-
openCard: (
|
|
19
|
+
openCard: (trigger: HTMLElement) => void;
|
|
8
20
|
closeCard: () => void;
|
|
9
|
-
exitTimeoutRef:
|
|
21
|
+
exitTimeoutRef: TimerRef;
|
|
10
22
|
}
|
|
11
23
|
|
|
12
24
|
/**
|
|
@@ -44,7 +56,7 @@ const useHoverHandlers = ({
|
|
|
44
56
|
const triggerHandlers = {
|
|
45
57
|
onMouseEnter: (event: React.MouseEvent<HTMLElement>) => {
|
|
46
58
|
if (hoverIgnored) return;
|
|
47
|
-
openCard(event);
|
|
59
|
+
openCard(event.currentTarget);
|
|
48
60
|
},
|
|
49
61
|
onMouseLeave: () => {
|
|
50
62
|
if (hoverIgnored) return;
|
|
@@ -53,7 +65,10 @@ const useHoverHandlers = ({
|
|
|
53
65
|
onTouchStart: (event: React.TouchEvent<HTMLElement>) => {
|
|
54
66
|
if (!touchEnabled) return;
|
|
55
67
|
if (touchTimeout) window.clearTimeout(touchTimeout);
|
|
56
|
-
|
|
68
|
+
// Read NOW: React nulls `currentTarget` once the handler returns, so
|
|
69
|
+
// the long-press timer used to open the card with no anchor at all.
|
|
70
|
+
const trigger = event.currentTarget;
|
|
71
|
+
setTouchTimeout(window.setTimeout(() => openCard(trigger), enterDelay));
|
|
57
72
|
},
|
|
58
73
|
// End and cancel do the same thing: the press did not become a long press.
|
|
59
74
|
onTouchEnd: cancelTouch,
|
|
@@ -62,9 +77,7 @@ const useHoverHandlers = ({
|
|
|
62
77
|
|
|
63
78
|
/** Moving onto the card keeps it open; leaving it starts the exit delay. */
|
|
64
79
|
const cardHandlers = {
|
|
65
|
-
onMouseEnter: () =>
|
|
66
|
-
if (exitTimeoutRef.current) window.clearTimeout(exitTimeoutRef.current);
|
|
67
|
-
},
|
|
80
|
+
onMouseEnter: () => clearPending(exitTimeoutRef),
|
|
68
81
|
onMouseLeave: closeCard,
|
|
69
82
|
};
|
|
70
83
|
|
|
@@ -85,6 +98,45 @@ const useEscapeKey = (active: boolean, onEscape: () => void) => {
|
|
|
85
98
|
}, [active, onEscape]);
|
|
86
99
|
};
|
|
87
100
|
|
|
101
|
+
/**
|
|
102
|
+
* A press anywhere outside the trigger and the card dismisses it.
|
|
103
|
+
*
|
|
104
|
+
* Touch has no mouse-leave, and MUI's own click-away was the popover's
|
|
105
|
+
* backdrop, which `pointer-events: none` on the popover root (so the page
|
|
106
|
+
* under an open card stays usable) takes out of play. So this listens on the
|
|
107
|
+
* document itself, in the CAPTURE phase, where a `stopPropagation` further in
|
|
108
|
+
* cannot swallow it. It only observes: no `preventDefault`, so the tap or
|
|
109
|
+
* click still reaches whatever it landed on. Attached while open, removed on
|
|
110
|
+
* close and on unmount.
|
|
111
|
+
*/
|
|
112
|
+
const useClickAway = (
|
|
113
|
+
active: boolean,
|
|
114
|
+
inside: () => ReadonlyArray<Element | null>,
|
|
115
|
+
onAway: () => void,
|
|
116
|
+
) => {
|
|
117
|
+
React.useEffect(() => {
|
|
118
|
+
if (!active) return;
|
|
119
|
+
|
|
120
|
+
const handlePointerDown = (event: globalThis.PointerEvent) => {
|
|
121
|
+
const target = event.target as Node | null;
|
|
122
|
+
if (target && inside().some((el) => el?.contains(target))) return;
|
|
123
|
+
onAway();
|
|
124
|
+
};
|
|
125
|
+
|
|
126
|
+
document.addEventListener('pointerdown', handlePointerDown, true);
|
|
127
|
+
return () => document.removeEventListener('pointerdown', handlePointerDown, true);
|
|
128
|
+
}, [active, inside, onAway]);
|
|
129
|
+
};
|
|
130
|
+
|
|
131
|
+
/** Whatever is still pending when the card unmounts is cancelled with it. */
|
|
132
|
+
const useClearOnUnmount = (...refs: TimerRef[]) => {
|
|
133
|
+
React.useEffect(
|
|
134
|
+
() => () => refs.forEach(clearPending),
|
|
135
|
+
// The refs are stable objects: a mount/unmount pair, as before.
|
|
136
|
+
[],
|
|
137
|
+
);
|
|
138
|
+
};
|
|
139
|
+
|
|
88
140
|
interface HoverCardTimingInput {
|
|
89
141
|
disabled: boolean;
|
|
90
142
|
touchEnabled: boolean;
|
|
@@ -115,6 +167,8 @@ export const useHoverCard = ({
|
|
|
115
167
|
const [isTouchDevice, setIsTouchDevice] = React.useState(false);
|
|
116
168
|
const enterTimeoutRef = React.useRef<number | undefined>(undefined);
|
|
117
169
|
const exitTimeoutRef = React.useRef<number | undefined>(undefined);
|
|
170
|
+
/** The card's own box, inside the popover paper: a press here is not "away". */
|
|
171
|
+
const contentRef = React.useRef<HTMLDivElement | null>(null);
|
|
118
172
|
|
|
119
173
|
// Probed in an effect, not during render, so SSR and hydration stay intact.
|
|
120
174
|
React.useEffect(() => {
|
|
@@ -122,16 +176,18 @@ export const useHoverCard = ({
|
|
|
122
176
|
}, []);
|
|
123
177
|
|
|
124
178
|
const openCard = React.useCallback(
|
|
125
|
-
(
|
|
179
|
+
(trigger: HTMLElement) => {
|
|
126
180
|
if (disabled) return;
|
|
127
181
|
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
182
|
+
clearPending(exitTimeoutRef);
|
|
183
|
+
// A second enter before the delay ran out (a click and then a hover, or
|
|
184
|
+
// the pointer jittering across the edge) must REPLACE the pending open,
|
|
185
|
+
// not add one: an overwritten timer can no longer be cancelled, and it
|
|
186
|
+
// reopened the card after Escape had closed it.
|
|
187
|
+
clearPending(enterTimeoutRef);
|
|
131
188
|
|
|
132
|
-
const target = event.currentTarget;
|
|
133
189
|
enterTimeoutRef.current = window.setTimeout(() => {
|
|
134
|
-
setAnchorEl(
|
|
190
|
+
setAnchorEl(trigger);
|
|
135
191
|
setIsOpen(true);
|
|
136
192
|
onOpen?.();
|
|
137
193
|
}, enterDelay);
|
|
@@ -140,9 +196,8 @@ export const useHoverCard = ({
|
|
|
140
196
|
);
|
|
141
197
|
|
|
142
198
|
const closeCard = React.useCallback(() => {
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
}
|
|
199
|
+
clearPending(enterTimeoutRef);
|
|
200
|
+
clearPending(exitTimeoutRef);
|
|
146
201
|
|
|
147
202
|
exitTimeoutRef.current = window.setTimeout(() => {
|
|
148
203
|
setIsOpen(false);
|
|
@@ -151,7 +206,11 @@ export const useHoverCard = ({
|
|
|
151
206
|
}, exitDelay);
|
|
152
207
|
}, [exitDelay, onClose]);
|
|
153
208
|
|
|
209
|
+
// Escape and the popover's own close are FINAL: nothing still pending may
|
|
210
|
+
// reopen the card behind them. It opens again on the next enter.
|
|
154
211
|
const handleClose = React.useCallback(() => {
|
|
212
|
+
clearPending(enterTimeoutRef);
|
|
213
|
+
clearPending(exitTimeoutRef);
|
|
155
214
|
setIsOpen(false);
|
|
156
215
|
setAnchorEl(null);
|
|
157
216
|
onClose?.();
|
|
@@ -166,15 +225,11 @@ export const useHoverCard = ({
|
|
|
166
225
|
exitTimeoutRef,
|
|
167
226
|
});
|
|
168
227
|
|
|
169
|
-
|
|
228
|
+
const insideCard = React.useCallback(() => [anchorEl, contentRef.current], [anchorEl]);
|
|
170
229
|
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
if (exitTimeoutRef.current) window.clearTimeout(exitTimeoutRef.current);
|
|
175
|
-
},
|
|
176
|
-
[],
|
|
177
|
-
);
|
|
230
|
+
useEscapeKey(isOpen, handleClose);
|
|
231
|
+
useClickAway(isOpen, insideCard, handleClose);
|
|
232
|
+
useClearOnUnmount(enterTimeoutRef, exitTimeoutRef);
|
|
178
233
|
|
|
179
|
-
return { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers };
|
|
234
|
+
return { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers, contentRef };
|
|
180
235
|
};
|
|
@@ -107,6 +107,7 @@ The HoverCard component displays additional information in a card-style popup wh
|
|
|
107
107
|
## Accessibility Notes
|
|
108
108
|
|
|
109
109
|
- The component supports keyboard navigation (Escape key to close)
|
|
110
|
+
- The card is dismissed by moving the pointer off the trigger and the card (after `exitDelay`), by Escape, or by a tap or click anywhere outside them. The outside press still reaches what it landed on: the page under an open card stays usable
|
|
110
111
|
- Proper ARIA attributes are applied to the popover
|
|
111
112
|
- Focus management is handled automatically
|
|
112
113
|
- Touch support for mobile devices can be enabled/disabled
|
|
@@ -26,6 +26,15 @@ const StyledCard = styled(Card, {
|
|
|
26
26
|
const StyledPopover = styled(Popover, {
|
|
27
27
|
shouldForwardProp: (prop) => prop !== 'customAnimation',
|
|
28
28
|
})<{ customAnimation?: HoverCardAnimation }>(({ customAnimation }) => ({
|
|
29
|
+
// Only the card takes the pointer (its paper sets `pointer-events: auto`).
|
|
30
|
+
// MUI's popover root and its invisible backdrop cover the whole viewport, so
|
|
31
|
+
// while a card was open the pointer was "on the card" wherever it went: the
|
|
32
|
+
// backdrop's mouseover reached the card's onMouseEnter and cancelled the
|
|
33
|
+
// close the trigger's mouseleave had just scheduled. A card left by moving
|
|
34
|
+
// the mouse away stayed open, and the page's next click hit the backdrop
|
|
35
|
+
// instead of its target (FUT-2619). The backdrop was also the only
|
|
36
|
+
// click-away; `useClickAway` in the hooks replaces it.
|
|
37
|
+
pointerEvents: 'none',
|
|
29
38
|
// The popover only positions; StyledCard draws the surface.
|
|
30
39
|
'& .MuiPopover-paper': {
|
|
31
40
|
backgroundColor: 'transparent',
|
|
@@ -81,7 +90,7 @@ export const HoverCard = React.forwardRef<HTMLDivElement, HoverCardProps>((props
|
|
|
81
90
|
...rest
|
|
82
91
|
} = withDefaults(props, DEFAULTS) as ResolvedProps;
|
|
83
92
|
|
|
84
|
-
const { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers } = useHoverCard({
|
|
93
|
+
const { anchorEl, isOpen, handleClose, triggerHandlers, cardHandlers, contentRef } = useHoverCard({
|
|
85
94
|
disabled,
|
|
86
95
|
touchEnabled,
|
|
87
96
|
enterDelay,
|
|
@@ -124,7 +133,7 @@ export const HoverCard = React.forwardRef<HTMLDivElement, HoverCardProps>((props
|
|
|
124
133
|
slotProps={{ paper: { style: { pointerEvents: 'auto' } } }}
|
|
125
134
|
{...rest}
|
|
126
135
|
>
|
|
127
|
-
<Box sx={{ position: 'relative' }}>
|
|
136
|
+
<Box ref={contentRef} sx={{ position: 'relative' }}>
|
|
128
137
|
{showArrow && <ArrowContainer placement={placement} offset={offset} />}
|
|
129
138
|
<StyledCard
|
|
130
139
|
customVariant={variant}
|
|
@@ -89,6 +89,39 @@ const interactionProps = (
|
|
|
89
89
|
onClick: loading ? undefined : onClick,
|
|
90
90
|
});
|
|
91
91
|
|
|
92
|
+
/**
|
|
93
|
+
* The ARIA that describes the field has to sit on the `<input>`: the text
|
|
94
|
+
* field puts every attribute it does not recognise on its root `FormControl`,
|
|
95
|
+
* a `<div>` with no role, where a screen reader never reads it.
|
|
96
|
+
*
|
|
97
|
+
* `aria-label` has ridden `inputProps` since FUT-755. `aria-describedby` and
|
|
98
|
+
* `aria-busy` fell through to the div until FUT-2619, so CepField's input
|
|
99
|
+
* pointed at nothing while its lookup status lived in a live region beside it.
|
|
100
|
+
*
|
|
101
|
+
* `inputProps` are spread onto the `<input>` AFTER the `aria-describedby` MUI
|
|
102
|
+
* writes for helper text, so a caller's ids REPLACE the helper text's: MUI's
|
|
103
|
+
* `TextField` on its own keeps only one of the two. This joins both, helper
|
|
104
|
+
* text first. The helper id is MUI's `<id>-helper-text` (the parity test pins
|
|
105
|
+
* that spelling), which is why the field's id is fixed here rather than left
|
|
106
|
+
* to the text field.
|
|
107
|
+
*/
|
|
108
|
+
function inputAriaOf(a: {
|
|
109
|
+
id: string;
|
|
110
|
+
helperText: React.ReactNode;
|
|
111
|
+
label?: string;
|
|
112
|
+
describedBy?: string;
|
|
113
|
+
busy?: InputProps['aria-busy'];
|
|
114
|
+
}): Record<string, unknown> {
|
|
115
|
+
const describedBy = [a.helperText ? `${a.id}-helper-text` : undefined, a.describedBy]
|
|
116
|
+
.filter(Boolean)
|
|
117
|
+
.join(' ');
|
|
118
|
+
return {
|
|
119
|
+
...(a.label === undefined ? {} : { 'aria-label': a.label }),
|
|
120
|
+
...(a.describedBy === undefined ? {} : { 'aria-describedby': describedBy }),
|
|
121
|
+
...(a.busy === undefined ? {} : { 'aria-busy': a.busy }),
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
92
125
|
export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
93
126
|
(
|
|
94
127
|
{
|
|
@@ -108,11 +141,15 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
108
141
|
onFocus,
|
|
109
142
|
onBlur,
|
|
110
143
|
'aria-label': ariaLabel,
|
|
144
|
+
'aria-describedby': ariaDescribedBy,
|
|
145
|
+
'aria-busy': ariaBusy,
|
|
111
146
|
...rest
|
|
112
147
|
},
|
|
113
148
|
ref,
|
|
114
149
|
) => {
|
|
115
150
|
const { testId: dataTestId, rest: props } = splitTestId(rest);
|
|
151
|
+
const generatedId = React.useId();
|
|
152
|
+
const id = props.id ?? generatedId;
|
|
116
153
|
return (
|
|
117
154
|
<StyledTextField
|
|
118
155
|
ref={ref}
|
|
@@ -137,10 +174,11 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
137
174
|
* no accessible name, and a source grep saying "this input is labelled"
|
|
138
175
|
* disagreed with the DOM. The reports search box and the block-title
|
|
139
176
|
* inputs were both named in source and anonymous to a screen reader.
|
|
177
|
+
* `aria-describedby` and `aria-busy` are the same case: `inputAriaOf`.
|
|
140
178
|
*/
|
|
141
179
|
inputProps={{
|
|
142
180
|
'data-testid': dataTestId,
|
|
143
|
-
...(
|
|
181
|
+
...inputAriaOf({ id, helperText, label: ariaLabel, describedBy: ariaDescribedBy, busy: ariaBusy }),
|
|
144
182
|
}}
|
|
145
183
|
InputProps={{
|
|
146
184
|
startAdornment: startAdornment && (
|
|
@@ -150,6 +188,7 @@ export const Input = React.forwardRef<HTMLInputElement, InputProps>(
|
|
|
150
188
|
}}
|
|
151
189
|
{...SIZE_MAP[size]}
|
|
152
190
|
{...props}
|
|
191
|
+
id={id}
|
|
153
192
|
/>
|
|
154
193
|
);
|
|
155
194
|
},
|