@versini/ui-panel 6.1.1 → 6.2.1
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/index.js +160 -219
- package/package.json +4 -4
package/dist/index.js
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
@versini/ui-panel v6.
|
|
2
|
+
@versini/ui-panel v6.2.1
|
|
3
3
|
© 2025 gizmette.com
|
|
4
4
|
*/
|
|
5
5
|
try {
|
|
6
6
|
if (!window.__VERSINI_UI_PANEL__) {
|
|
7
7
|
window.__VERSINI_UI_PANEL__ = {
|
|
8
|
-
version: "6.
|
|
9
|
-
buildTime: "12/
|
|
8
|
+
version: "6.2.1",
|
|
9
|
+
buildTime: "12/15/2025 06:38 PM EST",
|
|
10
10
|
homepage: "https://www.npmjs.com/package/@versini/ui-panel",
|
|
11
11
|
license: "MIT",
|
|
12
12
|
};
|
|
@@ -15,10 +15,11 @@ try {
|
|
|
15
15
|
// nothing to declare officer
|
|
16
16
|
}
|
|
17
17
|
|
|
18
|
-
import {
|
|
19
|
-
import {
|
|
18
|
+
import { jsx, jsxs } from "react/jsx-runtime";
|
|
19
|
+
import { useCallback, useEffect, useId, useRef, useState } from "react";
|
|
20
|
+
import { useFocusTrap } from "@versini/ui-hooks";
|
|
20
21
|
import clsx from "clsx";
|
|
21
|
-
import {
|
|
22
|
+
import { createPortal } from "react-dom";
|
|
22
23
|
|
|
23
24
|
;// CONCATENATED MODULE: ./src/common/constants.ts
|
|
24
25
|
const MESSAGEBOX_CLASSNAME = "av-messagebox";
|
|
@@ -34,177 +35,114 @@ const NONE = "none";
|
|
|
34
35
|
|
|
35
36
|
;// CONCATENATED MODULE: external "react/jsx-runtime"
|
|
36
37
|
|
|
37
|
-
;// CONCATENATED MODULE: external "
|
|
38
|
+
;// CONCATENATED MODULE: external "react"
|
|
39
|
+
|
|
40
|
+
;// CONCATENATED MODULE: external "@versini/ui-hooks"
|
|
38
41
|
|
|
39
42
|
;// CONCATENATED MODULE: external "clsx"
|
|
40
43
|
|
|
41
|
-
;// CONCATENATED MODULE: external "react"
|
|
44
|
+
;// CONCATENATED MODULE: external "react-dom"
|
|
42
45
|
|
|
43
|
-
;// CONCATENATED MODULE:
|
|
44
|
-
/*!
|
|
45
|
-
@versini/ui-modal v3.2.0
|
|
46
|
-
© 2025 gizmette.com
|
|
47
|
-
*/ try {
|
|
48
|
-
if (!window.__VERSINI_UI_MODAL__) {
|
|
49
|
-
window.__VERSINI_UI_MODAL__ = {
|
|
50
|
-
version: "3.2.0",
|
|
51
|
-
buildTime: "12/12/2025 10:28 AM EST",
|
|
52
|
-
homepage: "https://www.npmjs.com/package/@versini/ui-modal",
|
|
53
|
-
license: "MIT"
|
|
54
|
-
};
|
|
55
|
-
}
|
|
56
|
-
} catch (error) {
|
|
57
|
-
// nothing to declare officer
|
|
58
|
-
}
|
|
46
|
+
;// CONCATENATED MODULE: ./src/components/Panel/PanelPortal.tsx
|
|
59
47
|
|
|
60
48
|
|
|
61
49
|
|
|
62
50
|
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
const
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
const role = useRole(context);
|
|
83
|
-
const interactions = useInteractions([
|
|
84
|
-
click,
|
|
85
|
-
dismiss,
|
|
86
|
-
role
|
|
87
|
-
]);
|
|
88
|
-
return useMemo(()=>({
|
|
89
|
-
open,
|
|
90
|
-
setOpen,
|
|
91
|
-
...interactions,
|
|
92
|
-
...data,
|
|
93
|
-
labelId,
|
|
94
|
-
descriptionId,
|
|
95
|
-
setLabelId,
|
|
96
|
-
setDescriptionId,
|
|
97
|
-
initialFocus
|
|
98
|
-
}), [
|
|
99
|
-
open,
|
|
100
|
-
setOpen,
|
|
101
|
-
interactions,
|
|
102
|
-
data,
|
|
103
|
-
labelId,
|
|
104
|
-
descriptionId,
|
|
51
|
+
|
|
52
|
+
const DIALOG_OPEN_CLASS = "av-dialog-open";
|
|
53
|
+
/**
|
|
54
|
+
* Portal component for rendering the Panel as a modal dialog. Implements W3C
|
|
55
|
+
* WAI-ARIA dialog patterns:
|
|
56
|
+
* - Renders in a portal outside the DOM hierarchy
|
|
57
|
+
* - Locks body scroll when open
|
|
58
|
+
* - Traps focus within the dialog
|
|
59
|
+
* - Handles Escape key to close
|
|
60
|
+
* - Proper ARIA attributes (role="dialog", aria-modal="true", aria-labelledby)
|
|
61
|
+
*
|
|
62
|
+
* @see https://www.w3.org/WAI/ARIA/apg/patterns/dialog-modal/
|
|
63
|
+
*
|
|
64
|
+
*/ function PanelPortal({ open, onClose, children, className, style, title, initialFocus = 0 }) {
|
|
65
|
+
const labelId = useId();
|
|
66
|
+
const descriptionId = useId();
|
|
67
|
+
// Focus trap hook for managing focus within the dialog.
|
|
68
|
+
const { containerRef } = useFocusTrap({
|
|
69
|
+
enabled: open,
|
|
105
70
|
initialFocus
|
|
106
|
-
]);
|
|
107
|
-
}
|
|
108
|
-
const useModalContext = ()=>{
|
|
109
|
-
const context = useContext(ModalContext);
|
|
110
|
-
/* v8 ignore next 3 */ if (context == null) {
|
|
111
|
-
throw new Error("Modal components must be wrapped in <Modal />");
|
|
112
|
-
}
|
|
113
|
-
return context;
|
|
114
|
-
};
|
|
115
|
-
function Modal({ children, ...options }) {
|
|
116
|
-
const dialog = useModal(options);
|
|
117
|
-
return /*#__PURE__*/ jsx(ModalContext.Provider, {
|
|
118
|
-
value: dialog,
|
|
119
|
-
children: children
|
|
120
71
|
});
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
lockScroll: true,
|
|
140
|
-
children: /*#__PURE__*/ jsx(FloatingFocusManager, {
|
|
141
|
-
context: floatingContext,
|
|
142
|
-
initialFocus: context.initialFocus,
|
|
143
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
144
|
-
ref: ref,
|
|
145
|
-
"aria-labelledby": context.labelId,
|
|
146
|
-
"aria-describedby": context.descriptionId,
|
|
147
|
-
...context.getFloatingProps(rest),
|
|
148
|
-
children: rest.children
|
|
149
|
-
})
|
|
150
|
-
})
|
|
151
|
-
})
|
|
152
|
-
});
|
|
153
|
-
});
|
|
154
|
-
const Modal_ModalHeading = /*#__PURE__*/ forwardRef(function ModalHeading({ children, ...props }, ref) {
|
|
155
|
-
const { setLabelId } = useModalContext();
|
|
156
|
-
const id = useId();
|
|
157
|
-
// Only sets `aria-labelledby` on the Modal root element
|
|
158
|
-
// if this component is mounted inside it.
|
|
159
|
-
useLayoutEffect(()=>{
|
|
160
|
-
setLabelId(id);
|
|
161
|
-
return ()=>setLabelId(undefined);
|
|
72
|
+
/**
|
|
73
|
+
* Handle Escape key to close the dialog. Only handles the event if focus is
|
|
74
|
+
* within this panel's container to support nested panels (child panel should
|
|
75
|
+
* close first).
|
|
76
|
+
*/ const handleKeyDown = useCallback((event)=>{
|
|
77
|
+
if (event.key === "Escape") {
|
|
78
|
+
const activeElement = document.activeElement;
|
|
79
|
+
/* c8 ignore next 4 - menu focus check requires external menu component */ // Check if focus is inside an open menu - if so, let the menu handle Escape first.
|
|
80
|
+
if (activeElement?.closest('[role="menu"]')) {
|
|
81
|
+
return;
|
|
82
|
+
}
|
|
83
|
+
// Only handle if focus is within this panel's container.
|
|
84
|
+
if (containerRef.current?.contains(activeElement)) {
|
|
85
|
+
event.preventDefault();
|
|
86
|
+
event.stopPropagation();
|
|
87
|
+
onClose();
|
|
88
|
+
}
|
|
89
|
+
}
|
|
162
90
|
}, [
|
|
163
|
-
|
|
164
|
-
|
|
91
|
+
onClose,
|
|
92
|
+
containerRef.current?.contains
|
|
165
93
|
]);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
94
|
+
// Backdrop clicks are intentionally disabled (outsidePress: false).
|
|
95
|
+
/* c8 ignore next 1 - handler is disabled by design, no-op function */ const handleBackdropClick = useCallback(()=>{}, []);
|
|
96
|
+
// Lock body scroll and add escape key listener when open.
|
|
97
|
+
useEffect(()=>{
|
|
98
|
+
/* c8 ignore next 3 - effect cleanup path when not open */ if (!open) {
|
|
99
|
+
return;
|
|
100
|
+
}
|
|
101
|
+
// Lock body scroll.
|
|
102
|
+
const originalOverflow = document.body.style.overflow;
|
|
103
|
+
document.body.style.overflow = "hidden";
|
|
104
|
+
document.body.classList.add(DIALOG_OPEN_CLASS);
|
|
105
|
+
// Add escape key listener.
|
|
106
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
107
|
+
return ()=>{
|
|
108
|
+
// Restore body scroll.
|
|
109
|
+
document.body.style.overflow = originalOverflow;
|
|
110
|
+
document.body.classList.remove(DIALOG_OPEN_CLASS);
|
|
111
|
+
// Remove escape key listener.
|
|
112
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
113
|
+
};
|
|
181
114
|
}, [
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
]);
|
|
185
|
-
return /*#__PURE__*/ jsx("div", {
|
|
186
|
-
...props,
|
|
187
|
-
ref: ref,
|
|
188
|
-
id: id,
|
|
189
|
-
children: children
|
|
190
|
-
});
|
|
191
|
-
});
|
|
192
|
-
const Modal_ModalClose = /*#__PURE__*/ forwardRef(function ModalClose(props, ref) {
|
|
193
|
-
const { setOpen } = useModalContext();
|
|
194
|
-
const { trigger, className, ...rest } = props;
|
|
195
|
-
const handleClose = useCallback(()=>setOpen(false), [
|
|
196
|
-
setOpen
|
|
115
|
+
open,
|
|
116
|
+
handleKeyDown
|
|
197
117
|
]);
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
118
|
+
/* c8 ignore next 3 - early return when panel is closed */ if (!open) {
|
|
119
|
+
return null;
|
|
120
|
+
}
|
|
121
|
+
const overlayClass = clsx("fixed inset-0 z-50 grid place-items-center", "bg-black sm:bg-black/80");
|
|
122
|
+
return /*#__PURE__*/ createPortal(/*#__PURE__*/ jsx("div", {
|
|
123
|
+
className: overlayClass,
|
|
124
|
+
onClick: handleBackdropClick,
|
|
125
|
+
// Prevent clicks on the overlay from triggering parent handlers.
|
|
126
|
+
/* c8 ignore next 1 - event propagation handler */ onMouseDown: (e)=>e.stopPropagation(),
|
|
127
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
128
|
+
ref: containerRef,
|
|
129
|
+
role: "dialog",
|
|
130
|
+
"aria-modal": "true",
|
|
131
|
+
"aria-labelledby": labelId,
|
|
132
|
+
"aria-describedby": descriptionId,
|
|
133
|
+
className: className,
|
|
134
|
+
style: style,
|
|
135
|
+
children: [
|
|
136
|
+
/*#__PURE__*/ jsx("span", {
|
|
137
|
+
id: labelId,
|
|
138
|
+
className: "sr-only",
|
|
139
|
+
children: title
|
|
140
|
+
}),
|
|
141
|
+
children
|
|
142
|
+
]
|
|
204
143
|
})
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
/* v8 ignore next 1 */
|
|
144
|
+
}), document.body);
|
|
145
|
+
}
|
|
208
146
|
|
|
209
147
|
;// CONCATENATED MODULE: ./src/components/Panel/utilities.ts
|
|
210
148
|
|
|
@@ -306,9 +244,15 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
306
244
|
hasFooter: Boolean(footer)
|
|
307
245
|
});
|
|
308
246
|
/**
|
|
309
|
-
*
|
|
310
|
-
|
|
311
|
-
|
|
247
|
+
* Handle close button click.
|
|
248
|
+
*/ const handleClose = useCallback(()=>{
|
|
249
|
+
onOpenChange(false);
|
|
250
|
+
}, [
|
|
251
|
+
onOpenChange
|
|
252
|
+
]);
|
|
253
|
+
/**
|
|
254
|
+
* If the panel is opened, set the document title to the panel's title. If it's
|
|
255
|
+
* closed, restore the original document.title.
|
|
312
256
|
*/ useEffect(()=>{
|
|
313
257
|
if (open) {
|
|
314
258
|
originalTitleRef.current = document.title;
|
|
@@ -325,7 +269,7 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
325
269
|
]);
|
|
326
270
|
/**
|
|
327
271
|
* Effect to handle the opening and closing animations.
|
|
328
|
-
*/ /* v8 ignore next
|
|
272
|
+
*/ /* v8 ignore next 31 */ useEffect(()=>{
|
|
329
273
|
if (!animation) {
|
|
330
274
|
return;
|
|
331
275
|
}
|
|
@@ -335,9 +279,10 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
335
279
|
} : {
|
|
336
280
|
transform: "translateY(-666vh)"
|
|
337
281
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
282
|
+
/**
|
|
283
|
+
* Small delay to ensure the opening state is applied after the component is
|
|
284
|
+
* rendered.
|
|
285
|
+
*/ const timer = setTimeout(()=>{
|
|
341
286
|
setAnimationStyles(!animation ? {} : animationType === /* inlined export .ANIMATION_FADE */ ("fade") ? {
|
|
342
287
|
opacity: 1
|
|
343
288
|
} : {
|
|
@@ -351,65 +296,61 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
351
296
|
animation,
|
|
352
297
|
animationType
|
|
353
298
|
]);
|
|
354
|
-
return /*#__PURE__*/ jsx(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
className: panelClassName.
|
|
299
|
+
return /*#__PURE__*/ jsx(PanelPortal, {
|
|
300
|
+
open: open,
|
|
301
|
+
onClose: handleClose,
|
|
302
|
+
className: panelClassName.outerWrapper,
|
|
303
|
+
style: animationStyles,
|
|
304
|
+
title: title,
|
|
305
|
+
initialFocus: initialFocus,
|
|
306
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
307
|
+
className: panelClassName.innerWrapper,
|
|
308
|
+
children: [
|
|
309
|
+
/*#__PURE__*/ jsxs("div", {
|
|
310
|
+
className: panelClassName.header,
|
|
366
311
|
children: [
|
|
367
|
-
/*#__PURE__*/
|
|
368
|
-
className: panelClassName.
|
|
369
|
-
children:
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
d: "M297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256l105.3-105.4c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3z",
|
|
387
|
-
opacity: "1"
|
|
388
|
-
})
|
|
389
|
-
})
|
|
312
|
+
/*#__PURE__*/ jsx("div", {
|
|
313
|
+
className: panelClassName.closeWrapper,
|
|
314
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
315
|
+
className: panelClassName.closeButton,
|
|
316
|
+
type: "button",
|
|
317
|
+
"aria-label": "Close",
|
|
318
|
+
onClick: handleClose,
|
|
319
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
320
|
+
children: /*#__PURE__*/ jsx("svg", {
|
|
321
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
322
|
+
className: "size-3",
|
|
323
|
+
viewBox: "0 0 384 512",
|
|
324
|
+
fill: "currentColor",
|
|
325
|
+
role: "img",
|
|
326
|
+
"aria-hidden": "true",
|
|
327
|
+
focusable: "false",
|
|
328
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
329
|
+
d: "M297.4 406.6c12.5 12.5 32.8 12.5 45.3 0s12.5-32.8 0-45.3L237.3 256l105.3-105.4c12.5-12.5 12.5-32.8 0-45.3s-32.8-12.5-45.3 0L192 210.7 86.6 105.4c-12.5-12.5-32.8-12.5-45.3 0s-12.5 32.8 0 45.3L146.7 256 41.4 361.4c-12.5 12.5-12.5 32.8 0 45.3s32.8 12.5 45.3 0L192 301.3z",
|
|
330
|
+
opacity: "1"
|
|
390
331
|
})
|
|
391
332
|
})
|
|
392
|
-
}),
|
|
393
|
-
/*#__PURE__*/ jsx(Modal_ModalHeading, {
|
|
394
|
-
className: panelClassName.title,
|
|
395
|
-
children: title
|
|
396
333
|
})
|
|
397
|
-
]
|
|
398
|
-
}),
|
|
399
|
-
/*#__PURE__*/ jsx("div", {
|
|
400
|
-
className: panelClassName.scrollableContent,
|
|
401
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
402
|
-
className: panelClassName.content,
|
|
403
|
-
children: children
|
|
404
334
|
})
|
|
405
335
|
}),
|
|
406
|
-
|
|
407
|
-
className: panelClassName.
|
|
408
|
-
children:
|
|
336
|
+
/*#__PURE__*/ jsx("h1", {
|
|
337
|
+
className: panelClassName.title,
|
|
338
|
+
children: title
|
|
409
339
|
})
|
|
410
340
|
]
|
|
341
|
+
}),
|
|
342
|
+
/*#__PURE__*/ jsx("div", {
|
|
343
|
+
className: panelClassName.scrollableContent,
|
|
344
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
345
|
+
className: panelClassName.content,
|
|
346
|
+
children: children
|
|
347
|
+
})
|
|
348
|
+
}),
|
|
349
|
+
footer && /*#__PURE__*/ jsx("div", {
|
|
350
|
+
className: panelClassName.footer,
|
|
351
|
+
children: footer
|
|
411
352
|
})
|
|
412
|
-
|
|
353
|
+
]
|
|
413
354
|
})
|
|
414
355
|
});
|
|
415
356
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-panel",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.1",
|
|
4
4
|
"license": "MIT",
|
|
5
5
|
"author": "Arno Versini",
|
|
6
6
|
"publishConfig": {
|
|
@@ -38,16 +38,16 @@
|
|
|
38
38
|
"test": "vitest run"
|
|
39
39
|
},
|
|
40
40
|
"devDependencies": {
|
|
41
|
-
"@testing-library/jest-dom": "6.9.1"
|
|
42
|
-
"@versini/ui-modal": "3.2.0"
|
|
41
|
+
"@testing-library/jest-dom": "6.9.1"
|
|
43
42
|
},
|
|
44
43
|
"dependencies": {
|
|
45
44
|
"@tailwindcss/typography": "0.5.19",
|
|
45
|
+
"@versini/ui-hooks": "5.3.1",
|
|
46
46
|
"clsx": "2.1.1",
|
|
47
47
|
"tailwindcss": "4.1.18"
|
|
48
48
|
},
|
|
49
49
|
"sideEffects": [
|
|
50
50
|
"**/*.css"
|
|
51
51
|
],
|
|
52
|
-
"gitHead": "
|
|
52
|
+
"gitHead": "e525a00f1bf7288dffb5c49451e075cedc22aaa0"
|
|
53
53
|
}
|