@navikt/ds-react 0.17.16 → 0.17.19
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/cjs/form/search/Search.js +11 -17
- package/cjs/form/search/SearchButton.js +1 -1
- package/cjs/index.js +1 -0
- package/cjs/read-more/ReadMore.js +1 -1
- package/cjs/tooltip/Tooltip.js +153 -0
- package/cjs/tooltip/index.js +23 -0
- package/cjs/tooltip/package.json +6 -0
- package/cjs/tooltip/portal.js +17 -0
- package/cjs/util/index.js +11 -1
- package/esm/form/search/Search.d.ts +4 -4
- package/esm/form/search/Search.js +13 -19
- package/esm/form/search/Search.js.map +1 -1
- package/esm/form/search/SearchButton.js +1 -1
- package/esm/form/search/SearchButton.js.map +1 -1
- package/esm/index.d.ts +1 -0
- package/esm/index.js +1 -0
- package/esm/index.js.map +1 -1
- package/esm/read-more/ReadMore.js +1 -1
- package/esm/read-more/ReadMore.js.map +1 -1
- package/esm/tooltip/Tooltip.d.ts +51 -0
- package/esm/tooltip/Tooltip.js +126 -0
- package/esm/tooltip/Tooltip.js.map +1 -0
- package/esm/tooltip/index.d.ts +2 -0
- package/esm/tooltip/index.js +3 -0
- package/esm/tooltip/index.js.map +1 -0
- package/esm/tooltip/portal.d.ts +5 -0
- package/esm/tooltip/portal.js +13 -0
- package/esm/tooltip/portal.js.map +1 -0
- package/esm/util/index.d.ts +1 -0
- package/esm/util/index.js +9 -0
- package/esm/util/index.js.map +1 -1
- package/package.json +5 -4
- package/src/form/search/Search.tsx +22 -24
- package/src/form/search/SearchButton.tsx +1 -1
- package/src/form/search/search-themes.stories.tsx +52 -0
- package/src/form/search/search.stories.tsx +10 -3
- package/src/index.ts +1 -0
- package/src/read-more/ReadMore.tsx +1 -0
- package/src/tooltip/Tooltip.tsx +301 -0
- package/src/tooltip/index.ts +2 -0
- package/src/tooltip/portal.tsx +15 -0
- package/src/tooltip/tooltip.stories.tsx +144 -0
- package/src/util/index.ts +14 -0
|
@@ -0,0 +1,301 @@
|
|
|
1
|
+
import cl from "classnames";
|
|
2
|
+
import React, {
|
|
3
|
+
cloneElement,
|
|
4
|
+
forwardRef,
|
|
5
|
+
HTMLAttributes,
|
|
6
|
+
useCallback,
|
|
7
|
+
useEffect,
|
|
8
|
+
useMemo,
|
|
9
|
+
useRef,
|
|
10
|
+
useState,
|
|
11
|
+
} from "react";
|
|
12
|
+
import { composeEventHandlers, Detail, useEventListener } from "..";
|
|
13
|
+
import {
|
|
14
|
+
useFloating,
|
|
15
|
+
arrow as flArrow,
|
|
16
|
+
shift,
|
|
17
|
+
autoUpdate,
|
|
18
|
+
flip,
|
|
19
|
+
hide,
|
|
20
|
+
} from "@floating-ui/react-dom";
|
|
21
|
+
import mergeRefs from "react-merge-refs";
|
|
22
|
+
import Portal from "./portal";
|
|
23
|
+
import { useId } from "../util";
|
|
24
|
+
|
|
25
|
+
export interface TooltipProps extends HTMLAttributes<HTMLDivElement> {
|
|
26
|
+
/**
|
|
27
|
+
* Element tooltip anchors to
|
|
28
|
+
*/
|
|
29
|
+
children: React.ReactElement & React.RefAttributes<HTMLElement>;
|
|
30
|
+
/**
|
|
31
|
+
* Open state for contolled tooltip
|
|
32
|
+
*/
|
|
33
|
+
open?: boolean;
|
|
34
|
+
/**
|
|
35
|
+
* Tells tooltip to start in open state
|
|
36
|
+
* @note "open"-prop overwrites this
|
|
37
|
+
*/
|
|
38
|
+
defaultOpen?: boolean;
|
|
39
|
+
/**
|
|
40
|
+
* Orientation for tooltip
|
|
41
|
+
* @default "top"
|
|
42
|
+
*/
|
|
43
|
+
placement?: "top" | "right" | "bottom" | "left";
|
|
44
|
+
/**
|
|
45
|
+
* Toggles rendering of arrow
|
|
46
|
+
* @default true
|
|
47
|
+
*/
|
|
48
|
+
arrow?: boolean;
|
|
49
|
+
/**
|
|
50
|
+
* Distance from anchor to tooltip
|
|
51
|
+
* @default 10px with arrow, 2px without arrow
|
|
52
|
+
*/
|
|
53
|
+
offset?: number;
|
|
54
|
+
/**
|
|
55
|
+
* Content shown in tooltip
|
|
56
|
+
*/
|
|
57
|
+
content: string;
|
|
58
|
+
/**
|
|
59
|
+
* Sets max allowed character length
|
|
60
|
+
* @default 80
|
|
61
|
+
*/
|
|
62
|
+
maxChar?: number;
|
|
63
|
+
/**
|
|
64
|
+
* Adds a delay in milliseconds before opening tooltip
|
|
65
|
+
* @default 300
|
|
66
|
+
*/
|
|
67
|
+
delay?: number;
|
|
68
|
+
/**
|
|
69
|
+
* List of Keyboard-keys for shortcuts
|
|
70
|
+
*/
|
|
71
|
+
keys?: string[];
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
const Tooltip = forwardRef<HTMLDivElement, TooltipProps>(
|
|
75
|
+
(
|
|
76
|
+
{
|
|
77
|
+
children,
|
|
78
|
+
className,
|
|
79
|
+
arrow: _arrow = true,
|
|
80
|
+
placement: _placement = "top",
|
|
81
|
+
open,
|
|
82
|
+
defaultOpen = false,
|
|
83
|
+
offset: _offset,
|
|
84
|
+
content,
|
|
85
|
+
delay = 150,
|
|
86
|
+
id,
|
|
87
|
+
keys,
|
|
88
|
+
maxChar = 80,
|
|
89
|
+
...rest
|
|
90
|
+
},
|
|
91
|
+
ref
|
|
92
|
+
) => {
|
|
93
|
+
const arrowRef = useRef<HTMLDivElement | null>(null);
|
|
94
|
+
const [isOpen, setIsOpen] = useState(defaultOpen);
|
|
95
|
+
const openTimerRef = useRef(0);
|
|
96
|
+
const leaveTimerRef = useRef(0);
|
|
97
|
+
const isMouseDownRef = useRef(false);
|
|
98
|
+
|
|
99
|
+
const ariaId = useId(id);
|
|
100
|
+
|
|
101
|
+
const {
|
|
102
|
+
x,
|
|
103
|
+
y,
|
|
104
|
+
update,
|
|
105
|
+
placement,
|
|
106
|
+
refs,
|
|
107
|
+
middlewareData: {
|
|
108
|
+
arrow: { x: arrowX, y: arrowY } = {},
|
|
109
|
+
hide: { referenceHidden } = {},
|
|
110
|
+
},
|
|
111
|
+
} = useFloating({
|
|
112
|
+
placement: _placement,
|
|
113
|
+
middleware: [
|
|
114
|
+
shift(),
|
|
115
|
+
flip({ padding: 5, fallbackPlacements: ["bottom", "top"] }),
|
|
116
|
+
flArrow({ element: arrowRef, padding: 5 }),
|
|
117
|
+
hide(),
|
|
118
|
+
],
|
|
119
|
+
});
|
|
120
|
+
|
|
121
|
+
/* https://floating-ui.com/docs/react-dom#updating */
|
|
122
|
+
useEffect(() => {
|
|
123
|
+
if (!refs.reference.current || !refs.floating.current) {
|
|
124
|
+
return;
|
|
125
|
+
}
|
|
126
|
+
|
|
127
|
+
// Only call this when the floating element is rendered
|
|
128
|
+
return autoUpdate(refs.reference.current, refs.floating.current, update);
|
|
129
|
+
}, [refs.reference, refs.floating, update, open, isOpen]);
|
|
130
|
+
|
|
131
|
+
const handleOpen = useCallback(() => {
|
|
132
|
+
window.clearTimeout(openTimerRef.current);
|
|
133
|
+
window.clearTimeout(leaveTimerRef.current);
|
|
134
|
+
setIsOpen(true);
|
|
135
|
+
}, [setIsOpen]);
|
|
136
|
+
|
|
137
|
+
const handleDelayedOpen = useCallback(() => {
|
|
138
|
+
window.clearTimeout(openTimerRef.current);
|
|
139
|
+
window.clearTimeout(leaveTimerRef.current);
|
|
140
|
+
openTimerRef.current = window.setTimeout(() => {
|
|
141
|
+
setIsOpen(true);
|
|
142
|
+
}, delay);
|
|
143
|
+
}, [delay, setIsOpen]);
|
|
144
|
+
|
|
145
|
+
const handleClose = useCallback(() => {
|
|
146
|
+
window.clearTimeout(openTimerRef.current);
|
|
147
|
+
leaveTimerRef.current = window.setTimeout(() => {
|
|
148
|
+
setIsOpen(false);
|
|
149
|
+
}, 50);
|
|
150
|
+
}, [setIsOpen]);
|
|
151
|
+
|
|
152
|
+
const handleMouseUp = useCallback(
|
|
153
|
+
() => (isMouseDownRef.current = false),
|
|
154
|
+
[]
|
|
155
|
+
);
|
|
156
|
+
|
|
157
|
+
useEffect(() => {
|
|
158
|
+
// eslint-disable-next-line react-hooks/exhaustive-deps
|
|
159
|
+
return () => window.clearTimeout(openTimerRef.current);
|
|
160
|
+
}, []);
|
|
161
|
+
|
|
162
|
+
useEffect(() => {
|
|
163
|
+
return () => document.removeEventListener("mouseup", handleMouseUp);
|
|
164
|
+
}, [handleMouseUp]);
|
|
165
|
+
|
|
166
|
+
useEventListener(
|
|
167
|
+
"keydown",
|
|
168
|
+
useCallback((e) => e.key === "Escape" && handleClose(), [handleClose]),
|
|
169
|
+
document
|
|
170
|
+
);
|
|
171
|
+
|
|
172
|
+
/* https://floating-ui.com/docs/react-dom#stable-ref-prop */
|
|
173
|
+
const stableRef = useMemo(() => mergeRefs([ref, refs.floating]), [
|
|
174
|
+
ref,
|
|
175
|
+
refs.floating,
|
|
176
|
+
]);
|
|
177
|
+
|
|
178
|
+
if (
|
|
179
|
+
!children ||
|
|
180
|
+
children?.type === React.Fragment ||
|
|
181
|
+
(children as any) === React.Fragment
|
|
182
|
+
) {
|
|
183
|
+
console.error(
|
|
184
|
+
"<Tooltip> children needs to be a single ReactElement and not <React.Fragment/>/<></>"
|
|
185
|
+
);
|
|
186
|
+
return null;
|
|
187
|
+
}
|
|
188
|
+
|
|
189
|
+
if (content?.length > maxChar) {
|
|
190
|
+
console.error(
|
|
191
|
+
`Because of strict accessibility concers we encourage all Tooltips to have less than 80 characters. Can be overwritten with the maxChar-prop`
|
|
192
|
+
);
|
|
193
|
+
return null;
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
return (
|
|
197
|
+
<>
|
|
198
|
+
{cloneElement(children, {
|
|
199
|
+
...children.props,
|
|
200
|
+
"aria-describedby":
|
|
201
|
+
open || isOpen
|
|
202
|
+
? cl(ariaId, children?.props["aria-describedby"])
|
|
203
|
+
: children?.props["aria-describedby"],
|
|
204
|
+
ref: mergeRefs([(children as any).ref, refs.reference]),
|
|
205
|
+
onMouseEnter: composeEventHandlers(
|
|
206
|
+
children.props.onMouseEnter,
|
|
207
|
+
handleDelayedOpen
|
|
208
|
+
),
|
|
209
|
+
onMouseLeave: composeEventHandlers(
|
|
210
|
+
children.props.onMouseLeave,
|
|
211
|
+
handleClose
|
|
212
|
+
),
|
|
213
|
+
onMouseDown: composeEventHandlers(children.props.onMouseDown, () => {
|
|
214
|
+
isMouseDownRef.current = true;
|
|
215
|
+
document.addEventListener("mouseup", handleMouseUp, { once: true });
|
|
216
|
+
}),
|
|
217
|
+
onFocus: composeEventHandlers(
|
|
218
|
+
children.props.onFocus,
|
|
219
|
+
() => !isMouseDownRef.current && handleOpen()
|
|
220
|
+
),
|
|
221
|
+
onBlur: composeEventHandlers(children.props.onBlur, handleClose),
|
|
222
|
+
})}
|
|
223
|
+
{(open ?? isOpen) && (
|
|
224
|
+
<Portal>
|
|
225
|
+
<div
|
|
226
|
+
ref={stableRef}
|
|
227
|
+
{...rest}
|
|
228
|
+
onMouseEnter={handleOpen}
|
|
229
|
+
onMouseLeave={handleClose}
|
|
230
|
+
role="tooltip"
|
|
231
|
+
id={ariaId}
|
|
232
|
+
style={{
|
|
233
|
+
position: "absolute",
|
|
234
|
+
top: y ?? "",
|
|
235
|
+
left: x ?? "",
|
|
236
|
+
visibility: referenceHidden ? "hidden" : "visible",
|
|
237
|
+
}}
|
|
238
|
+
data-side={placement}
|
|
239
|
+
className={cl(
|
|
240
|
+
"navds-tooltip",
|
|
241
|
+
"navds-detail navds-detail--small",
|
|
242
|
+
className
|
|
243
|
+
)}
|
|
244
|
+
>
|
|
245
|
+
<div
|
|
246
|
+
className="navds-tooltip__inner"
|
|
247
|
+
style={{
|
|
248
|
+
[{
|
|
249
|
+
top: "marginBottom",
|
|
250
|
+
right: "marginLeft",
|
|
251
|
+
bottom: "marginTop",
|
|
252
|
+
left: "marginRight",
|
|
253
|
+
}[placement]]: _offset ? _offset : _arrow ? 10 : 2,
|
|
254
|
+
}}
|
|
255
|
+
>
|
|
256
|
+
{content}
|
|
257
|
+
{keys && (
|
|
258
|
+
<span className="navds-tooltip__keys">
|
|
259
|
+
{keys.map((key) => (
|
|
260
|
+
<Detail
|
|
261
|
+
size="small"
|
|
262
|
+
as="kbd"
|
|
263
|
+
key={key}
|
|
264
|
+
className="navds-tooltip__key"
|
|
265
|
+
>
|
|
266
|
+
{key}
|
|
267
|
+
</Detail>
|
|
268
|
+
))}
|
|
269
|
+
</span>
|
|
270
|
+
)}
|
|
271
|
+
{_arrow && (
|
|
272
|
+
<div
|
|
273
|
+
ref={(node) => {
|
|
274
|
+
arrowRef.current = node;
|
|
275
|
+
}}
|
|
276
|
+
className="navds-tooltip__arrow"
|
|
277
|
+
style={{
|
|
278
|
+
left: arrowX != null ? `${arrowX}px` : "",
|
|
279
|
+
top: arrowY != null ? `${arrowY}px` : "",
|
|
280
|
+
right: "",
|
|
281
|
+
bottom: "",
|
|
282
|
+
|
|
283
|
+
[{
|
|
284
|
+
top: "bottom",
|
|
285
|
+
right: "left",
|
|
286
|
+
bottom: "top",
|
|
287
|
+
left: "right",
|
|
288
|
+
}[placement]]: "-3.5px",
|
|
289
|
+
}}
|
|
290
|
+
/>
|
|
291
|
+
)}
|
|
292
|
+
</div>
|
|
293
|
+
</div>
|
|
294
|
+
</Portal>
|
|
295
|
+
)}
|
|
296
|
+
</>
|
|
297
|
+
);
|
|
298
|
+
}
|
|
299
|
+
);
|
|
300
|
+
|
|
301
|
+
export default Tooltip;
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/* https://github.com/radix-ui/primitives/blob/main/packages/react/portal/src/Portal.tsx */
|
|
2
|
+
import ReactDOM from "react-dom";
|
|
3
|
+
|
|
4
|
+
const Portal = ({ children }) => {
|
|
5
|
+
const hostElement = globalThis?.document?.body;
|
|
6
|
+
|
|
7
|
+
if (hostElement) {
|
|
8
|
+
return ReactDOM.createPortal(children, hostElement);
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
// bail out of ssr
|
|
12
|
+
return null;
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
export default Portal;
|
|
@@ -0,0 +1,144 @@
|
|
|
1
|
+
import React, { useRef, useState } from "react";
|
|
2
|
+
import { Tooltip } from "../index";
|
|
3
|
+
import { Meta } from "@storybook/react/types-6-0";
|
|
4
|
+
import { Refresh } from "@navikt/ds-icons";
|
|
5
|
+
import { Button } from "../..";
|
|
6
|
+
import { ToggleGroup } from "../toggle-group";
|
|
7
|
+
|
|
8
|
+
export default {
|
|
9
|
+
title: "ds-react/tooltip",
|
|
10
|
+
component: Tooltip,
|
|
11
|
+
parameters: {
|
|
12
|
+
chromatic: { disable: true },
|
|
13
|
+
},
|
|
14
|
+
} as Meta;
|
|
15
|
+
|
|
16
|
+
export const Demo = () => {
|
|
17
|
+
const [open, setOpen] = useState(true);
|
|
18
|
+
const testRef = useRef(null);
|
|
19
|
+
return (
|
|
20
|
+
<div
|
|
21
|
+
style={{
|
|
22
|
+
width: "100vw",
|
|
23
|
+
height: "100vh",
|
|
24
|
+
display: "flex",
|
|
25
|
+
flexDirection: "column",
|
|
26
|
+
gap: 32,
|
|
27
|
+
justifyContent: "center",
|
|
28
|
+
alignItems: "center",
|
|
29
|
+
}}
|
|
30
|
+
>
|
|
31
|
+
<Tooltip
|
|
32
|
+
content="Tooltip example"
|
|
33
|
+
keys={["Cmd", "K"]}
|
|
34
|
+
placement="right"
|
|
35
|
+
open={open}
|
|
36
|
+
ref={testRef}
|
|
37
|
+
>
|
|
38
|
+
<Button aria-describedby="test123" onClick={() => setOpen(!open)}>
|
|
39
|
+
Tooltip C
|
|
40
|
+
</Button>
|
|
41
|
+
</Tooltip>
|
|
42
|
+
<Tooltip
|
|
43
|
+
content="Tooltip example"
|
|
44
|
+
keys={["Cmd", "K"]}
|
|
45
|
+
placement="right"
|
|
46
|
+
defaultOpen
|
|
47
|
+
>
|
|
48
|
+
<Button onClick={() => console.log(testRef.current)}>Tooltip C</Button>
|
|
49
|
+
</Tooltip>
|
|
50
|
+
<ToggleGroup onChange={null} defaultValue="321">
|
|
51
|
+
<Tooltip content="Tooltip" placement="bottom">
|
|
52
|
+
<ToggleGroup.Item value="123">Tekst</ToggleGroup.Item>
|
|
53
|
+
</Tooltip>
|
|
54
|
+
<Tooltip content="Tooltip" placement="bottom">
|
|
55
|
+
<ToggleGroup.Item value="321">tekst 2</ToggleGroup.Item>
|
|
56
|
+
</Tooltip>
|
|
57
|
+
<Tooltip content="Tooltip" placement="bottom">
|
|
58
|
+
<ToggleGroup.Item value="3212">tekst 2</ToggleGroup.Item>
|
|
59
|
+
</Tooltip>
|
|
60
|
+
<Tooltip content="Tooltip" placement="bottom">
|
|
61
|
+
<ToggleGroup.Item value="3213">tekst 2</ToggleGroup.Item>
|
|
62
|
+
</Tooltip>
|
|
63
|
+
<Tooltip content="Tooltip" placement="bottom">
|
|
64
|
+
<ToggleGroup.Item value="3214">tekst 2</ToggleGroup.Item>
|
|
65
|
+
</Tooltip>
|
|
66
|
+
</ToggleGroup>
|
|
67
|
+
</div>
|
|
68
|
+
);
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const All = () => {
|
|
72
|
+
const [open, setOpen] = useState(false);
|
|
73
|
+
return (
|
|
74
|
+
<div style={{ margin: "4rem 8rem 4rem 8rem" }}>
|
|
75
|
+
<h2>Controlled</h2>
|
|
76
|
+
<Tooltip open={open} content="Controlled tooltip example" placement="top">
|
|
77
|
+
<Button onClick={() => setOpen((x) => !x)}>Toggle tooltip</Button>
|
|
78
|
+
</Tooltip>
|
|
79
|
+
|
|
80
|
+
<h2>no arrow</h2>
|
|
81
|
+
<Tooltip content="no arrow" placement="top" arrow={false}>
|
|
82
|
+
<Button>Tooltip</Button>
|
|
83
|
+
</Tooltip>
|
|
84
|
+
|
|
85
|
+
<h2>Keys</h2>
|
|
86
|
+
<Tooltip content="Tooltip" placement="top" open keys={["Cmd", "K"]}>
|
|
87
|
+
<Button>Tooltip</Button>
|
|
88
|
+
</Tooltip>
|
|
89
|
+
<h2>more offset</h2>
|
|
90
|
+
<Tooltip content="Tooltip" placement="top" open offset={20}>
|
|
91
|
+
<Button>Tooltip</Button>
|
|
92
|
+
</Tooltip>
|
|
93
|
+
|
|
94
|
+
<h2>all placements</h2>
|
|
95
|
+
<div
|
|
96
|
+
style={{
|
|
97
|
+
display: "flex",
|
|
98
|
+
flexDirection: "column",
|
|
99
|
+
flexWrap: "wrap",
|
|
100
|
+
gap: "3rem",
|
|
101
|
+
}}
|
|
102
|
+
>
|
|
103
|
+
{["top", "left", "bottom", "right"].map((placement) => (
|
|
104
|
+
<div key={placement}>
|
|
105
|
+
<h3>{placement}</h3>
|
|
106
|
+
<div
|
|
107
|
+
style={{
|
|
108
|
+
display: "flex",
|
|
109
|
+
flexDirection: "column",
|
|
110
|
+
flexWrap: "wrap",
|
|
111
|
+
gap: "3rem",
|
|
112
|
+
}}
|
|
113
|
+
>
|
|
114
|
+
<Tooltip
|
|
115
|
+
key={placement}
|
|
116
|
+
defaultOpen
|
|
117
|
+
content={placement}
|
|
118
|
+
placement={placement as any}
|
|
119
|
+
>
|
|
120
|
+
<Refresh aria-hidden tabIndex={0} />
|
|
121
|
+
</Tooltip>
|
|
122
|
+
</div>
|
|
123
|
+
</div>
|
|
124
|
+
))}
|
|
125
|
+
</div>
|
|
126
|
+
</div>
|
|
127
|
+
);
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
export const UUDemo = () => {
|
|
131
|
+
return (
|
|
132
|
+
<div>
|
|
133
|
+
<Button>Placeholder</Button>
|
|
134
|
+
<br />
|
|
135
|
+
<br />
|
|
136
|
+
<Tooltip content="Shortcut" placement="right" keys={["Cmd", "S"]}>
|
|
137
|
+
<Button>Lagre</Button>
|
|
138
|
+
</Tooltip>
|
|
139
|
+
<br />
|
|
140
|
+
<br />
|
|
141
|
+
<Button>Placeholder</Button>
|
|
142
|
+
</div>
|
|
143
|
+
);
|
|
144
|
+
};
|
package/src/util/index.ts
CHANGED
|
@@ -44,3 +44,17 @@ export const useEventListener = <T extends ListenerT>(
|
|
|
44
44
|
};
|
|
45
45
|
}, [name, handler, target]);
|
|
46
46
|
};
|
|
47
|
+
|
|
48
|
+
/* https://github.com/radix-ui/primitives/blob/main/packages/core/primitive/src/primitive.tsx */
|
|
49
|
+
export const composeEventHandlers = <E>(
|
|
50
|
+
originalEventHandler?: (event: E) => void,
|
|
51
|
+
ourEventHandler?: (event: E) => void
|
|
52
|
+
) => {
|
|
53
|
+
return function handleEvent(event: E) {
|
|
54
|
+
originalEventHandler?.(event);
|
|
55
|
+
|
|
56
|
+
if (!((event as unknown) as Event).defaultPrevented) {
|
|
57
|
+
return ourEventHandler?.(event);
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
};
|