@enigmax/primitives 0.27.0 → 0.28.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/{chunk-VKL3DEIQ.js → chunk-6WIGVIQF.js} +58 -37
- package/dist/{chunk-O2UJ5ZIX.js → chunk-CB7K6VW4.js} +1 -1
- package/dist/{chunk-2RIO4VDV.js → chunk-IQMHDRJF.js} +2 -2
- package/dist/{chunk-PVDU6AZU.js → chunk-JPBVKXOL.js} +1 -1
- package/dist/{chunk-CPJJ3FPW.js → chunk-RRPWJAIJ.js} +26 -12
- package/dist/chunk-RXEO4WPI.js +111 -0
- package/dist/{chunk-RMNXVWQ4.js → chunk-YHH5U2NW.js} +1 -1
- package/dist/{color-T63FLJNH.js → color-WBNVPFT5.js} +157 -158
- package/dist/{menu-52PXC5PL.js → menu-5JZMGNOT.js} +2 -1
- package/dist/{menu-YIGMPFUP.js → menu-O3G4LM5O.js} +3 -2
- package/dist/next/index.js +6 -5
- package/dist/react/context-menu.d.ts +1 -1
- package/dist/react/context-menu.js +2 -1
- package/dist/react/image.js +1 -1
- package/dist/react/index.js +6 -5
- package/dist/react/input.js +1 -1
- package/dist/react/select.d.ts +6 -1
- package/dist/react/select.js +2 -1
- package/dist/react/video.js +1 -1
- package/dist/react-router/index.js +6 -5
- package/dist/viewer-C323RYNZ.js +3 -0
- package/package.json +1 -1
- package/recipes/color/styles.css +12 -7
- package/recipes/context-menu/styles.css +9 -2
- package/recipes/select/styles.css +24 -7
- package/registry.json +98 -5
- package/src/core/floating.ts +71 -0
- package/src/react/context-menu/root.tsx +30 -15
- package/src/react/context-menu/styles.ts +9 -2
- package/src/react/floating.ts +185 -0
- package/src/react/input/color-styles.ts +12 -7
- package/src/react/input/color.tsx +30 -27
- package/src/react/select/context.ts +5 -0
- package/src/react/select/root.tsx +33 -21
- package/src/react/select/styles.ts +24 -7
- package/dist/viewer-N5WLFB24.js +0 -3
|
@@ -1,8 +1,10 @@
|
|
|
1
|
+
import { tabOut, useFloating } from './chunk-RXEO4WPI.js';
|
|
1
2
|
import { createSelect } from './chunk-NONGREXC.js';
|
|
2
3
|
import { Slot } from './chunk-HS3X3XCW.js';
|
|
3
4
|
import { groupRows } from './chunk-DTWZDONY.js';
|
|
4
5
|
import { shortenQuery } from './chunk-4ZPMP47J.js';
|
|
5
|
-
import {
|
|
6
|
+
import { createPortal } from 'react-dom';
|
|
7
|
+
import { createContext, useContext, useLayoutEffect, useId, useMemo, useRef, useCallback, useState, useEffect } from 'react';
|
|
6
8
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
7
9
|
|
|
8
10
|
// src/react/select/styles.ts
|
|
@@ -21,12 +23,17 @@ var SELECT_STYLES = `
|
|
|
21
23
|
--enigma-select-panel-border: #333333;
|
|
22
24
|
--enigma-select-panel-radius: 0.625rem;
|
|
23
25
|
--enigma-select-panel-shadow: 0 12px 32px rgba(0, 0, 0, 0.45);
|
|
24
|
-
--enigma-select-panel-width: 100%;
|
|
25
26
|
--enigma-select-list-height: 15rem;
|
|
26
27
|
|
|
27
28
|
--enigma-select-active-bg: #2a2a2a;
|
|
28
29
|
--enigma-select-accent: #fbbf24;
|
|
29
30
|
--enigma-select-tag-bg: #2a2a2a;
|
|
31
|
+
|
|
32
|
+
/* The layer every portaled popup shares: above the lightbox (9999), because a popup is
|
|
33
|
+
opened FROM whatever is on top. The select, the colour picker and the context menu
|
|
34
|
+
each declare it with the same value, so overriding it once moves all three. Here and
|
|
35
|
+
not in the light-scheme block, or half the readers get an invalid z-index. */
|
|
36
|
+
--enigma-floating-z: 10000;
|
|
30
37
|
}
|
|
31
38
|
|
|
32
39
|
@media (prefers-color-scheme: light) {
|
|
@@ -131,11 +138,24 @@ var SELECT_STYLES = `
|
|
|
131
138
|
[data-enigma-select-trigger][data-clearable]:focus [data-enigma-select-caret] { opacity: 0; }
|
|
132
139
|
[data-enigma-select-clear]:hover { color: var(--enigma-select-text); background: var(--enigma-select-active-bg); }
|
|
133
140
|
|
|
141
|
+
/* Portaled to <body> and placed in viewport coordinates against the trigger (see
|
|
142
|
+
react/floating.ts), so no ancestor's overflow can clip it and no dialog can stack over it.
|
|
143
|
+
left, top, max-height and data-side are written by the placement: below the trigger,
|
|
144
|
+
flipped above when there is more room there, and capped to the room it has. The column
|
|
145
|
+
is what makes the cap shrink the LIST, which scrolls, instead of cutting off the panel. */
|
|
134
146
|
[data-enigma-select-content] {
|
|
135
|
-
position:
|
|
136
|
-
|
|
137
|
-
|
|
147
|
+
position: fixed; z-index: var(--enigma-floating-z);
|
|
148
|
+
display: flex; flex-direction: column;
|
|
149
|
+
/* As wide as the trigger, measured - 100% would now be a percentage of the window - and
|
|
150
|
+
wider only for content that cannot shrink. min-content is what keeps the rest at the
|
|
151
|
+
trigger's width: fixed, the panel's room is the whole window, and a long label or a
|
|
152
|
+
long "Nothing matches" would otherwise widen it to max-width. --enigma-select-panel-width
|
|
153
|
+
overrides the trigger's width. */
|
|
154
|
+
width: min-content;
|
|
155
|
+
min-width: var(--enigma-select-panel-width, var(--enigma-anchor-width, auto)); box-sizing: border-box;
|
|
138
156
|
padding: 0.25rem;
|
|
157
|
+
color: var(--enigma-select-text);
|
|
158
|
+
font-size: var(--enigma-select-font-size);
|
|
139
159
|
background: var(--enigma-select-panel-bg);
|
|
140
160
|
border: 1px solid var(--enigma-select-panel-border);
|
|
141
161
|
border-radius: var(--enigma-select-panel-radius);
|
|
@@ -146,9 +166,6 @@ var SELECT_STYLES = `
|
|
|
146
166
|
trigger wider than this, which is what it is for. */
|
|
147
167
|
max-width: min(28rem, calc(100vw - 2rem));
|
|
148
168
|
}
|
|
149
|
-
/* Opening upwards is not a variant, it is the same panel measured against the window: near
|
|
150
|
-
the bottom of the screen the list would otherwise be unreachable. */
|
|
151
|
-
[data-enigma-select-content][data-side="top"] { top: auto; bottom: calc(100% + 0.25rem); }
|
|
152
169
|
[data-enigma-select-content][data-state="closed"] { animation: enigma-select-out 120ms ease-in forwards; pointer-events: none; }
|
|
153
170
|
|
|
154
171
|
@keyframes enigma-select-in {
|
|
@@ -180,6 +197,8 @@ var SELECT_STYLES = `
|
|
|
180
197
|
|
|
181
198
|
[data-enigma-select-list] {
|
|
182
199
|
max-height: var(--enigma-select-list-height);
|
|
200
|
+
/* Allowed below its content, so the panel's placement cap shrinks it and it scrolls. */
|
|
201
|
+
min-height: 0; flex: 0 1 auto;
|
|
183
202
|
margin-top: 0.25rem;
|
|
184
203
|
overflow-y: auto; overscroll-behavior: contain;
|
|
185
204
|
}
|
|
@@ -295,6 +314,8 @@ function SelectRoot(props) {
|
|
|
295
314
|
const triggerRef = useRef(null);
|
|
296
315
|
const fieldRef = useRef(null);
|
|
297
316
|
const rootRef = useRef(null);
|
|
317
|
+
const contentRef = useRef(null);
|
|
318
|
+
const owns = useCallback((node) => Boolean(node && (rootRef.current?.contains(node) || contentRef.current?.contains(node))), []);
|
|
298
319
|
const latest = useRef(props);
|
|
299
320
|
latest.current = props;
|
|
300
321
|
const instance = useMemo(() => createSelect({
|
|
@@ -367,10 +388,10 @@ function SelectRoot(props) {
|
|
|
367
388
|
const closedItself = wasOpen.current && !state.open;
|
|
368
389
|
wasOpen.current = state.open;
|
|
369
390
|
if (!closedItself || !open) return;
|
|
370
|
-
const held =
|
|
391
|
+
const held = owns(document.activeElement);
|
|
371
392
|
setOpen(false);
|
|
372
393
|
if (held) triggerRef.current?.focus();
|
|
373
|
-
}, [state.open, open, setOpen]);
|
|
394
|
+
}, [state.open, open, setOpen, owns]);
|
|
374
395
|
const close = useCallback(() => {
|
|
375
396
|
setOpen(false);
|
|
376
397
|
triggerRef.current?.focus();
|
|
@@ -378,12 +399,12 @@ function SelectRoot(props) {
|
|
|
378
399
|
useEffect(() => {
|
|
379
400
|
if (!open) return;
|
|
380
401
|
const onPointerDown = (event) => {
|
|
381
|
-
if (
|
|
402
|
+
if (owns(event.target)) return;
|
|
382
403
|
setOpen(false);
|
|
383
404
|
};
|
|
384
405
|
document.addEventListener("pointerdown", onPointerDown, true);
|
|
385
406
|
return () => document.removeEventListener("pointerdown", onPointerDown, true);
|
|
386
|
-
}, [open, setOpen]);
|
|
407
|
+
}, [open, setOpen, owns]);
|
|
387
408
|
const onListKeyDown = useCallback((event) => {
|
|
388
409
|
const move = MOVE_KEYS[event.key];
|
|
389
410
|
if (move) {
|
|
@@ -413,6 +434,7 @@ function SelectRoot(props) {
|
|
|
413
434
|
return;
|
|
414
435
|
}
|
|
415
436
|
if (event.key === "Tab" && open) {
|
|
437
|
+
if (contentRef.current?.contains(event.target)) tabOut(event, contentRef.current, triggerRef.current);
|
|
416
438
|
setOpen(false);
|
|
417
439
|
return;
|
|
418
440
|
}
|
|
@@ -450,6 +472,7 @@ function SelectRoot(props) {
|
|
|
450
472
|
optionId: (index) => `${id}-option-${index}`,
|
|
451
473
|
triggerRef,
|
|
452
474
|
fieldRef,
|
|
475
|
+
contentRef,
|
|
453
476
|
close,
|
|
454
477
|
onListKeyDown
|
|
455
478
|
}), [instance, state, setOpen, disabled, clearable, multiple, isSearchable, isEmpty, loading, emptyLabel, loadingLabel, ids, id, close, onListKeyDown]);
|
|
@@ -608,9 +631,7 @@ function SelectValue({ placeholder = "Select", tags, maxTags = 3, children, ...p
|
|
|
608
631
|
}
|
|
609
632
|
function SelectContent({ closeDuration = 120, children, ...props }) {
|
|
610
633
|
const select = useSelectContext("Select.Content");
|
|
611
|
-
const ref = useRef(null);
|
|
612
634
|
const [mounted, setMounted] = useState(select.state.open);
|
|
613
|
-
const [side, setSide] = useState("bottom");
|
|
614
635
|
useEffect(() => {
|
|
615
636
|
if (select.state.open) {
|
|
616
637
|
setMounted(true);
|
|
@@ -619,29 +640,29 @@ function SelectContent({ closeDuration = 120, children, ...props }) {
|
|
|
619
640
|
const timer = setTimeout(() => setMounted(false), closeDuration);
|
|
620
641
|
return () => clearTimeout(timer);
|
|
621
642
|
}, [select.state.open, closeDuration]);
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
}
|
|
643
|
-
|
|
644
|
-
|
|
643
|
+
const container = useFloating({
|
|
644
|
+
open: mounted && select.state.open,
|
|
645
|
+
anchorRef: select.triggerRef,
|
|
646
|
+
panelRef: select.contentRef,
|
|
647
|
+
deps: [select.state.visible.length]
|
|
648
|
+
});
|
|
649
|
+
if (!mounted || !container) return null;
|
|
650
|
+
return createPortal(
|
|
651
|
+
/* @__PURE__ */ jsx(
|
|
652
|
+
"div",
|
|
653
|
+
{
|
|
654
|
+
...props,
|
|
655
|
+
ref: select.contentRef,
|
|
656
|
+
"data-enigma-select-content": "",
|
|
657
|
+
"data-state": select.state.open ? "open" : "closed",
|
|
658
|
+
onKeyDown: (event) => {
|
|
659
|
+
props.onKeyDown?.(event);
|
|
660
|
+
if (!event.defaultPrevented) select.onListKeyDown(event);
|
|
661
|
+
},
|
|
662
|
+
children
|
|
663
|
+
}
|
|
664
|
+
),
|
|
665
|
+
container
|
|
645
666
|
);
|
|
646
667
|
}
|
|
647
668
|
function SelectSearch({ placeholder = "Search", onKeyDown, ...props }) {
|
|
@@ -42,7 +42,7 @@ function NewTab() {
|
|
|
42
42
|
/* @__PURE__ */ jsx("path", { d: "M18 14v5a1.5 1.5 0 0 1-1.5 1.5h-11A1.5 1.5 0 0 1 4 19V7.5A1.5 1.5 0 0 1 5.5 6H10" })
|
|
43
43
|
] });
|
|
44
44
|
}
|
|
45
|
-
var ImageMenu = lazy(() => import('./menu-
|
|
45
|
+
var ImageMenu = lazy(() => import('./menu-O3G4LM5O.js').then((module) => ({ default: module.ImageMenu })));
|
|
46
46
|
function ImageViewer({
|
|
47
47
|
items,
|
|
48
48
|
index,
|
|
@@ -6,12 +6,12 @@ import { jsx, jsxs } from 'react/jsx-runtime';
|
|
|
6
6
|
function toItem(source) {
|
|
7
7
|
return typeof source === "string" ? { src: source } : source;
|
|
8
8
|
}
|
|
9
|
-
var ImageViewer = lazy(() => import('./viewer-
|
|
9
|
+
var ImageViewer = lazy(() => import('./viewer-C323RYNZ.js').then((module) => ({ default: module.ImageViewer })));
|
|
10
10
|
var prefetched = false;
|
|
11
11
|
function prefetchViewer() {
|
|
12
12
|
if (prefetched) return;
|
|
13
13
|
prefetched = true;
|
|
14
|
-
void import('./viewer-
|
|
14
|
+
void import('./viewer-C323RYNZ.js');
|
|
15
15
|
}
|
|
16
16
|
var ZOOM_DEFAULTS = { min: 1, max: 8, wheel: true, doubleClick: true };
|
|
17
17
|
function Image(props) {
|
|
@@ -351,7 +351,7 @@ var CONTROL_DEFAULTS = {
|
|
|
351
351
|
fullscreen: true,
|
|
352
352
|
download: false
|
|
353
353
|
};
|
|
354
|
-
var VideoMenu = lazy(() => import('./menu-
|
|
354
|
+
var VideoMenu = lazy(() => import('./menu-5JZMGNOT.js').then((module) => ({ default: module.VideoMenu })));
|
|
355
355
|
var injected = false;
|
|
356
356
|
function injectStyles() {
|
|
357
357
|
if (injected || typeof document === "undefined") return;
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { createContextMenu, clipboardAction, performClipboardAction, inspectClipboardTarget, CLIPBOARD_PREFIX, clipboardEntries, isAction, clipboardHasText } from './chunk-YQV5XZIA.js';
|
|
2
|
+
import { floatingContainer, measureFrame, placeFloating } from './chunk-RXEO4WPI.js';
|
|
2
3
|
import { shortcutTokens } from './chunk-4VUHQFAT.js';
|
|
3
4
|
import { Slot } from './chunk-HS3X3XCW.js';
|
|
4
5
|
import { shortenQuery } from './chunk-4ZPMP47J.js';
|
|
@@ -28,8 +29,11 @@ var CONTEXT_MENU_STYLES = `
|
|
|
28
29
|
top: the image viewer's own three-dot menu was portalled to the same body and painted
|
|
29
30
|
underneath it, so its rows could be neither seen nor pressed. A menu is the topmost
|
|
30
31
|
transient surface on a page, and this is the number that says so. It belongs here and
|
|
31
|
-
not in the light-scheme block, or half the readers get an invalid z-index.
|
|
32
|
-
|
|
32
|
+
not in the light-scheme block, or half the readers get an invalid z-index.
|
|
33
|
+
--enigma-floating-z is the layer the select and the colour picker share, declared with
|
|
34
|
+
the same value in their sheets, so overriding it once moves every popup together. */
|
|
35
|
+
--enigma-floating-z: 10000;
|
|
36
|
+
--enigma-menu-z: var(--enigma-floating-z);
|
|
33
37
|
}
|
|
34
38
|
|
|
35
39
|
@media (prefers-color-scheme: light) {
|
|
@@ -51,6 +55,9 @@ var CONTEXT_MENU_STYLES = `
|
|
|
51
55
|
ancestor cannot drag it away from the place it was opened. */
|
|
52
56
|
[data-enigma-menu-panel] {
|
|
53
57
|
position: fixed; z-index: var(--enigma-menu-z);
|
|
58
|
+
/* A column, so the max-height the placement writes in a short window shrinks the LIST,
|
|
59
|
+
which scrolls, rather than letting rows spill past the panel's edge. */
|
|
60
|
+
display: flex; flex-direction: column;
|
|
54
61
|
box-sizing: border-box;
|
|
55
62
|
min-width: var(--enigma-menu-min-width);
|
|
56
63
|
max-width: min(22rem, calc(100vw - 1rem));
|
|
@@ -105,6 +112,7 @@ var CONTEXT_MENU_STYLES = `
|
|
|
105
112
|
|
|
106
113
|
[data-enigma-menu-list] {
|
|
107
114
|
max-height: var(--enigma-menu-max-height);
|
|
115
|
+
min-height: 0; flex: 0 1 auto;
|
|
108
116
|
overflow-y: auto; overscroll-behavior: contain;
|
|
109
117
|
}
|
|
110
118
|
|
|
@@ -534,7 +542,7 @@ function ContextMenuContent({ chunk = 40, portal = true, ...props }) {
|
|
|
534
542
|
const panels = /* @__PURE__ */ jsx(Fragment, { children: menu.state.levels.map((_, level) => /* @__PURE__ */ jsx(ContextMenuPanel, { level, chunk, ...props }, level)) });
|
|
535
543
|
if (!portal) return panels;
|
|
536
544
|
if (!mounted || typeof document === "undefined") return null;
|
|
537
|
-
return createPortal(panels, document.body);
|
|
545
|
+
return createPortal(panels, floatingContainer(menu.triggerRef.current) ?? document.body);
|
|
538
546
|
}
|
|
539
547
|
function ContextMenuPanel({ level, chunk = 40, ...props }) {
|
|
540
548
|
const menu = useContextMenuContext("ContextMenu.Panel");
|
|
@@ -547,27 +555,33 @@ function ContextMenuPanel({ level, chunk = 40, ...props }) {
|
|
|
547
555
|
useLayoutEffect(() => {
|
|
548
556
|
const panel = ref.current;
|
|
549
557
|
if (!panel) return;
|
|
558
|
+
panel.style.maxHeight = "";
|
|
550
559
|
const width = panel.offsetWidth;
|
|
551
560
|
const height = panel.offsetHeight;
|
|
552
|
-
const
|
|
553
|
-
const
|
|
561
|
+
const frame = measureFrame(panel);
|
|
562
|
+
const { boundary } = frame;
|
|
554
563
|
let left;
|
|
555
564
|
let top;
|
|
556
565
|
if (level === 0) {
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
566
|
+
const x = point?.x ?? MARGIN;
|
|
567
|
+
const y = point?.y ?? MARGIN;
|
|
568
|
+
const placedAt = placeFloating({ left: x, right: x, top: y, bottom: y }, { width, height }, { boundary, margin: MARGIN });
|
|
569
|
+
left = placedAt.left;
|
|
570
|
+
top = placedAt.top;
|
|
571
|
+
panel.style.maxHeight = `${placedAt.maxHeight}px`;
|
|
561
572
|
} else {
|
|
562
573
|
const parent = document.getElementById(menu.panelId(level - 1))?.getBoundingClientRect();
|
|
563
574
|
const row = document.getElementById(menu.itemId(level - 1, menu.state.levels[level - 1]?.active ?? -1))?.getBoundingClientRect();
|
|
564
575
|
const anchor = parent ?? { left: point?.x ?? MARGIN, right: point?.x ?? MARGIN, top: point?.y ?? MARGIN };
|
|
565
576
|
left = anchor.right - 2;
|
|
566
|
-
if (left + width >
|
|
577
|
+
if (left + width > boundary.right - MARGIN) left = Math.max(boundary.left + MARGIN, anchor.left - width + 2);
|
|
567
578
|
top = (row?.top ?? anchor.top) - 4;
|
|
568
|
-
if (top + height >
|
|
579
|
+
if (top + height > boundary.bottom - MARGIN) top = Math.max(boundary.top + MARGIN, boundary.bottom - MARGIN - height);
|
|
569
580
|
}
|
|
570
|
-
setPlaced({
|
|
581
|
+
setPlaced({
|
|
582
|
+
left: Math.max(boundary.left + MARGIN, Math.round(left)) - frame.x,
|
|
583
|
+
top: Math.max(boundary.top + MARGIN, Math.round(top)) - frame.y
|
|
584
|
+
});
|
|
571
585
|
}, [level, point?.x, point?.y, rows, loading, menu, menu.state.levels.length]);
|
|
572
586
|
const isPlaced = placed !== null;
|
|
573
587
|
const isDeepest = menu.state.levels.length - 1 === level;
|
|
@@ -0,0 +1,111 @@
|
|
|
1
|
+
import { useState, useLayoutEffect, useCallback, useEffect } from 'react';
|
|
2
|
+
|
|
3
|
+
// src/core/floating.ts
|
|
4
|
+
function placeFloating(anchor, size, options) {
|
|
5
|
+
const { boundary, margin = 8, gap = 0, side = "auto" } = options;
|
|
6
|
+
const below = boundary.bottom - margin - anchor.bottom - gap;
|
|
7
|
+
const above = anchor.top - gap - boundary.top - margin;
|
|
8
|
+
const chosen = side !== "auto" ? side : size.height <= below || below >= above ? "bottom" : "top";
|
|
9
|
+
const maxHeight = Math.max(0, Math.floor(chosen === "bottom" ? below : above));
|
|
10
|
+
const height = Math.min(size.height, maxHeight);
|
|
11
|
+
const top = chosen === "bottom" ? anchor.bottom + gap : anchor.top - gap - height;
|
|
12
|
+
const maxLeft = boundary.right - margin - size.width;
|
|
13
|
+
let left = anchor.left;
|
|
14
|
+
if (left > maxLeft) left = anchor.right - size.width;
|
|
15
|
+
left = Math.max(boundary.left + margin, Math.min(left, maxLeft));
|
|
16
|
+
return { left: Math.round(left), top: Math.round(top), side: chosen, maxHeight };
|
|
17
|
+
}
|
|
18
|
+
function floatingContainer(anchor) {
|
|
19
|
+
if (typeof document === "undefined") return null;
|
|
20
|
+
const host = anchor?.closest("dialog[open], [popover], [role=dialog], [role=alertdialog], [aria-modal=true]");
|
|
21
|
+
return host ?? document.body;
|
|
22
|
+
}
|
|
23
|
+
function measureFrame(panel) {
|
|
24
|
+
const root = document.documentElement;
|
|
25
|
+
const viewport = { left: 0, top: 0, right: root.clientWidth || window.innerWidth, bottom: root.clientHeight || window.innerHeight };
|
|
26
|
+
const host = panel.parentElement;
|
|
27
|
+
if (!host || host === document.body) return { x: 0, y: 0, boundary: viewport };
|
|
28
|
+
const probe = document.createElement("div");
|
|
29
|
+
probe.style.cssText = "position:fixed;left:0;top:0;width:0;height:0;visibility:hidden;pointer-events:none";
|
|
30
|
+
host.appendChild(probe);
|
|
31
|
+
const origin = probe.getBoundingClientRect();
|
|
32
|
+
probe.remove();
|
|
33
|
+
const box = host.getBoundingClientRect();
|
|
34
|
+
const isBlock = Math.abs(origin.left - (box.left + host.clientLeft)) < 1 && Math.abs(origin.top - (box.top + host.clientTop)) < 1;
|
|
35
|
+
const style = getComputedStyle(host);
|
|
36
|
+
const clips = style.overflowX !== "visible" || style.overflowY !== "visible";
|
|
37
|
+
const boundary = isBlock && clips ? {
|
|
38
|
+
left: Math.max(viewport.left, box.left),
|
|
39
|
+
top: Math.max(viewport.top, box.top),
|
|
40
|
+
right: Math.min(viewport.right, box.right),
|
|
41
|
+
bottom: Math.min(viewport.bottom, box.bottom)
|
|
42
|
+
} : viewport;
|
|
43
|
+
return { x: origin.left, y: origin.top, boundary };
|
|
44
|
+
}
|
|
45
|
+
function useFloating({ open, anchorRef, panelRef, gap = 4, margin = 8, side = "auto", deps = [] }) {
|
|
46
|
+
const [container, setContainer] = useState(null);
|
|
47
|
+
useLayoutEffect(() => {
|
|
48
|
+
if (open) setContainer(floatingContainer(anchorRef.current));
|
|
49
|
+
}, [open, anchorRef]);
|
|
50
|
+
const place = useCallback(() => {
|
|
51
|
+
const anchor = anchorRef.current;
|
|
52
|
+
const panel = panelRef.current;
|
|
53
|
+
if (!anchor || !panel) return;
|
|
54
|
+
const rect = anchor.getBoundingClientRect();
|
|
55
|
+
panel.style.setProperty("--enigma-anchor-width", `${rect.width}px`);
|
|
56
|
+
panel.style.maxHeight = "";
|
|
57
|
+
const frame = measureFrame(panel);
|
|
58
|
+
const placed = placeFloating(rect, { width: panel.offsetWidth, height: panel.offsetHeight }, { boundary: frame.boundary, margin, gap, side });
|
|
59
|
+
panel.style.left = `${placed.left - frame.x}px`;
|
|
60
|
+
panel.style.top = `${placed.top - frame.y}px`;
|
|
61
|
+
panel.style.maxHeight = `${placed.maxHeight}px`;
|
|
62
|
+
panel.setAttribute("data-side", placed.side);
|
|
63
|
+
}, [anchorRef, panelRef, margin, gap, side]);
|
|
64
|
+
useLayoutEffect(() => {
|
|
65
|
+
if (open) place();
|
|
66
|
+
}, [open, container, place, ...deps]);
|
|
67
|
+
useEffect(() => {
|
|
68
|
+
if (!open || !container) return;
|
|
69
|
+
let frame = 0;
|
|
70
|
+
const schedule = () => {
|
|
71
|
+
if (frame) return;
|
|
72
|
+
frame = requestAnimationFrame(() => {
|
|
73
|
+
frame = 0;
|
|
74
|
+
place();
|
|
75
|
+
});
|
|
76
|
+
};
|
|
77
|
+
const onScroll = (event) => {
|
|
78
|
+
if (panelRef.current?.contains(event.target)) return;
|
|
79
|
+
schedule();
|
|
80
|
+
};
|
|
81
|
+
document.addEventListener("scroll", onScroll, true);
|
|
82
|
+
window.addEventListener("resize", schedule);
|
|
83
|
+
const observer = typeof ResizeObserver === "undefined" ? null : new ResizeObserver(schedule);
|
|
84
|
+
if (anchorRef.current) observer?.observe(anchorRef.current);
|
|
85
|
+
if (panelRef.current) observer?.observe(panelRef.current);
|
|
86
|
+
return () => {
|
|
87
|
+
cancelAnimationFrame(frame);
|
|
88
|
+
document.removeEventListener("scroll", onScroll, true);
|
|
89
|
+
window.removeEventListener("resize", schedule);
|
|
90
|
+
observer?.disconnect();
|
|
91
|
+
};
|
|
92
|
+
}, [open, container, place, anchorRef, panelRef]);
|
|
93
|
+
return container;
|
|
94
|
+
}
|
|
95
|
+
var TABBABLE = "a[href], button:not(:disabled), input:not(:disabled):not([type=hidden]), select:not(:disabled), textarea:not(:disabled), [tabindex]:not([tabindex='-1'])";
|
|
96
|
+
function tabOut(event, panel, trigger) {
|
|
97
|
+
if (event.key !== "Tab" || !panel || !trigger) return false;
|
|
98
|
+
const stops = Array.from(panel.querySelectorAll(TABBABLE));
|
|
99
|
+
const at = stops.indexOf(document.activeElement);
|
|
100
|
+
if (event.shiftKey) {
|
|
101
|
+
if (at > 0) return false;
|
|
102
|
+
event.preventDefault();
|
|
103
|
+
trigger.focus();
|
|
104
|
+
return true;
|
|
105
|
+
}
|
|
106
|
+
if (at !== -1 && at < stops.length - 1) return false;
|
|
107
|
+
trigger.focus();
|
|
108
|
+
return true;
|
|
109
|
+
}
|
|
110
|
+
|
|
111
|
+
export { floatingContainer, measureFrame, placeFloating, tabOut, useFloating };
|
|
@@ -25,7 +25,7 @@ var COLOR_FIELD_PROPS = {
|
|
|
25
25
|
};
|
|
26
26
|
var PasswordExtras = lazy(() => import('./password-3DRQYAYQ.js').then((module) => ({ default: module.PasswordExtras })));
|
|
27
27
|
var SearchExtras = lazy(() => import('./search-PBZORZ7P.js').then((module) => ({ default: module.SearchExtras })));
|
|
28
|
-
var ColorExtras = lazy(() => import('./color-
|
|
28
|
+
var ColorExtras = lazy(() => import('./color-WBNVPFT5.js').then((module) => ({ default: module.ColorExtras })));
|
|
29
29
|
var Input = forwardRef(function Input2(props, forwardedRef) {
|
|
30
30
|
const {
|
|
31
31
|
reveal,
|