@versini/ui-panel 6.1.1 → 6.2.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.
- package/dist/index.js +155 -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.0
|
|
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.0",
|
|
9
|
+
buildTime: "12/14/2025 08:05 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,109 @@ 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
|
-
});
|
|
121
|
-
}
|
|
122
|
-
const Modal_ModalContent = /*#__PURE__*/ forwardRef(function ModalContent(props, propRef) {
|
|
123
|
-
const { context: floatingContext, ...context } = useModalContext();
|
|
124
|
-
const ref = useMergeRefs([
|
|
125
|
-
context.refs.setFloating,
|
|
126
|
-
propRef
|
|
127
|
-
]);
|
|
128
|
-
/* v8 ignore next 3 */ if (!floatingContext.open) {
|
|
129
|
-
return null;
|
|
130
|
-
}
|
|
131
|
-
const { overlayBackground, ...rest } = props;
|
|
132
|
-
const overlayClass = clsx("grid place-items-center", {
|
|
133
|
-
[`${overlayBackground}`]: overlayBackground,
|
|
134
|
-
"bg-black sm:bg-black/[.8]": !overlayBackground
|
|
135
71
|
});
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
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
|
+
// Only handle if focus is within this panel's container.
|
|
79
|
+
if (containerRef.current?.contains(document.activeElement)) {
|
|
80
|
+
event.preventDefault();
|
|
81
|
+
event.stopPropagation();
|
|
82
|
+
onClose();
|
|
83
|
+
}
|
|
84
|
+
}
|
|
162
85
|
}, [
|
|
163
|
-
|
|
164
|
-
|
|
86
|
+
onClose,
|
|
87
|
+
containerRef.current?.contains
|
|
165
88
|
]);
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
89
|
+
// Backdrop clicks are intentionally disabled (outsidePress: false).
|
|
90
|
+
/* c8 ignore next 1 - handler is disabled by design, no-op function */ const handleBackdropClick = useCallback(()=>{}, []);
|
|
91
|
+
// Lock body scroll and add escape key listener when open.
|
|
92
|
+
useEffect(()=>{
|
|
93
|
+
/* c8 ignore next 3 - effect cleanup path when not open */ if (!open) {
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
// Lock body scroll.
|
|
97
|
+
const originalOverflow = document.body.style.overflow;
|
|
98
|
+
document.body.style.overflow = "hidden";
|
|
99
|
+
document.body.classList.add(DIALOG_OPEN_CLASS);
|
|
100
|
+
// Add escape key listener.
|
|
101
|
+
document.addEventListener("keydown", handleKeyDown);
|
|
102
|
+
return ()=>{
|
|
103
|
+
// Restore body scroll.
|
|
104
|
+
document.body.style.overflow = originalOverflow;
|
|
105
|
+
document.body.classList.remove(DIALOG_OPEN_CLASS);
|
|
106
|
+
// Remove escape key listener.
|
|
107
|
+
document.removeEventListener("keydown", handleKeyDown);
|
|
108
|
+
};
|
|
181
109
|
}, [
|
|
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
|
|
110
|
+
open,
|
|
111
|
+
handleKeyDown
|
|
197
112
|
]);
|
|
198
|
-
return
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
113
|
+
/* c8 ignore next 3 - early return when panel is closed */ if (!open) {
|
|
114
|
+
return null;
|
|
115
|
+
}
|
|
116
|
+
const overlayClass = clsx("fixed inset-0 z-50 grid place-items-center", "bg-black sm:bg-black/80");
|
|
117
|
+
return /*#__PURE__*/ createPortal(/*#__PURE__*/ jsx("div", {
|
|
118
|
+
className: overlayClass,
|
|
119
|
+
onClick: handleBackdropClick,
|
|
120
|
+
// Prevent clicks on the overlay from triggering parent handlers.
|
|
121
|
+
/* c8 ignore next 1 - event propagation handler */ onMouseDown: (e)=>e.stopPropagation(),
|
|
122
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
123
|
+
ref: containerRef,
|
|
124
|
+
role: "dialog",
|
|
125
|
+
"aria-modal": "true",
|
|
126
|
+
"aria-labelledby": labelId,
|
|
127
|
+
"aria-describedby": descriptionId,
|
|
128
|
+
className: className,
|
|
129
|
+
style: style,
|
|
130
|
+
children: [
|
|
131
|
+
/*#__PURE__*/ jsx("span", {
|
|
132
|
+
id: labelId,
|
|
133
|
+
className: "sr-only",
|
|
134
|
+
children: title
|
|
135
|
+
}),
|
|
136
|
+
children
|
|
137
|
+
]
|
|
204
138
|
})
|
|
205
|
-
});
|
|
206
|
-
}
|
|
207
|
-
/* v8 ignore next 1 */
|
|
139
|
+
}), document.body);
|
|
140
|
+
}
|
|
208
141
|
|
|
209
142
|
;// CONCATENATED MODULE: ./src/components/Panel/utilities.ts
|
|
210
143
|
|
|
@@ -306,9 +239,15 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
306
239
|
hasFooter: Boolean(footer)
|
|
307
240
|
});
|
|
308
241
|
/**
|
|
309
|
-
*
|
|
310
|
-
|
|
311
|
-
|
|
242
|
+
* Handle close button click.
|
|
243
|
+
*/ const handleClose = useCallback(()=>{
|
|
244
|
+
onOpenChange(false);
|
|
245
|
+
}, [
|
|
246
|
+
onOpenChange
|
|
247
|
+
]);
|
|
248
|
+
/**
|
|
249
|
+
* If the panel is opened, set the document title to the panel's title. If it's
|
|
250
|
+
* closed, restore the original document.title.
|
|
312
251
|
*/ useEffect(()=>{
|
|
313
252
|
if (open) {
|
|
314
253
|
originalTitleRef.current = document.title;
|
|
@@ -325,7 +264,7 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
325
264
|
]);
|
|
326
265
|
/**
|
|
327
266
|
* Effect to handle the opening and closing animations.
|
|
328
|
-
*/ /* v8 ignore next
|
|
267
|
+
*/ /* v8 ignore next 31 */ useEffect(()=>{
|
|
329
268
|
if (!animation) {
|
|
330
269
|
return;
|
|
331
270
|
}
|
|
@@ -335,9 +274,10 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
335
274
|
} : {
|
|
336
275
|
transform: "translateY(-666vh)"
|
|
337
276
|
});
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
277
|
+
/**
|
|
278
|
+
* Small delay to ensure the opening state is applied after the component is
|
|
279
|
+
* rendered.
|
|
280
|
+
*/ const timer = setTimeout(()=>{
|
|
341
281
|
setAnimationStyles(!animation ? {} : animationType === /* inlined export .ANIMATION_FADE */ ("fade") ? {
|
|
342
282
|
opacity: 1
|
|
343
283
|
} : {
|
|
@@ -351,65 +291,61 @@ const Panel = ({ open, onOpenChange, title, children, footer, borderMode = "ligh
|
|
|
351
291
|
animation,
|
|
352
292
|
animationType
|
|
353
293
|
]);
|
|
354
|
-
return /*#__PURE__*/ jsx(
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
className: panelClassName.
|
|
294
|
+
return /*#__PURE__*/ jsx(PanelPortal, {
|
|
295
|
+
open: open,
|
|
296
|
+
onClose: handleClose,
|
|
297
|
+
className: panelClassName.outerWrapper,
|
|
298
|
+
style: animationStyles,
|
|
299
|
+
title: title,
|
|
300
|
+
initialFocus: initialFocus,
|
|
301
|
+
children: /*#__PURE__*/ jsxs("div", {
|
|
302
|
+
className: panelClassName.innerWrapper,
|
|
303
|
+
children: [
|
|
304
|
+
/*#__PURE__*/ jsxs("div", {
|
|
305
|
+
className: panelClassName.header,
|
|
366
306
|
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
|
-
})
|
|
307
|
+
/*#__PURE__*/ jsx("div", {
|
|
308
|
+
className: panelClassName.closeWrapper,
|
|
309
|
+
children: /*#__PURE__*/ jsx("button", {
|
|
310
|
+
className: panelClassName.closeButton,
|
|
311
|
+
type: "button",
|
|
312
|
+
"aria-label": "Close",
|
|
313
|
+
onClick: handleClose,
|
|
314
|
+
children: /*#__PURE__*/ jsx("span", {
|
|
315
|
+
children: /*#__PURE__*/ jsx("svg", {
|
|
316
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
317
|
+
className: "size-3",
|
|
318
|
+
viewBox: "0 0 384 512",
|
|
319
|
+
fill: "currentColor",
|
|
320
|
+
role: "img",
|
|
321
|
+
"aria-hidden": "true",
|
|
322
|
+
focusable: "false",
|
|
323
|
+
children: /*#__PURE__*/ jsx("path", {
|
|
324
|
+
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",
|
|
325
|
+
opacity: "1"
|
|
390
326
|
})
|
|
391
327
|
})
|
|
392
|
-
}),
|
|
393
|
-
/*#__PURE__*/ jsx(Modal_ModalHeading, {
|
|
394
|
-
className: panelClassName.title,
|
|
395
|
-
children: title
|
|
396
328
|
})
|
|
397
|
-
]
|
|
398
|
-
}),
|
|
399
|
-
/*#__PURE__*/ jsx("div", {
|
|
400
|
-
className: panelClassName.scrollableContent,
|
|
401
|
-
children: /*#__PURE__*/ jsx("div", {
|
|
402
|
-
className: panelClassName.content,
|
|
403
|
-
children: children
|
|
404
329
|
})
|
|
405
330
|
}),
|
|
406
|
-
|
|
407
|
-
className: panelClassName.
|
|
408
|
-
children:
|
|
331
|
+
/*#__PURE__*/ jsx("h1", {
|
|
332
|
+
className: panelClassName.title,
|
|
333
|
+
children: title
|
|
409
334
|
})
|
|
410
335
|
]
|
|
336
|
+
}),
|
|
337
|
+
/*#__PURE__*/ jsx("div", {
|
|
338
|
+
className: panelClassName.scrollableContent,
|
|
339
|
+
children: /*#__PURE__*/ jsx("div", {
|
|
340
|
+
className: panelClassName.content,
|
|
341
|
+
children: children
|
|
342
|
+
})
|
|
343
|
+
}),
|
|
344
|
+
footer && /*#__PURE__*/ jsx("div", {
|
|
345
|
+
className: panelClassName.footer,
|
|
346
|
+
children: footer
|
|
411
347
|
})
|
|
412
|
-
|
|
348
|
+
]
|
|
413
349
|
})
|
|
414
350
|
});
|
|
415
351
|
};
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@versini/ui-panel",
|
|
3
|
-
"version": "6.
|
|
3
|
+
"version": "6.2.0",
|
|
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.0",
|
|
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": "15f3028acf21328c70773147a6bedfbafaa3a2a4"
|
|
53
53
|
}
|