@enigmax/primitives 0.26.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-ISXB5RUH.js → chunk-CB7K6VW4.js} +10 -3
- package/dist/{chunk-QIIV2QSV.js → chunk-HUUINHPR.js} +28 -1
- package/dist/{chunk-4SZ5N7BA.js → chunk-IQMHDRJF.js} +2 -2
- package/dist/{chunk-RUEQ3TGP.js → chunk-JLZDS6CV.js} +23 -1
- package/dist/{chunk-JJVZYW5C.js → chunk-JPBVKXOL.js} +10 -3
- package/dist/{chunk-DZMHY3SU.js → chunk-RRPWJAIJ.js} +41 -17
- package/dist/chunk-RXEO4WPI.js +111 -0
- package/dist/{chunk-RMNXVWQ4.js → chunk-YHH5U2NW.js} +1 -1
- package/dist/{chunk-Z3VDE7OA.js → chunk-YQV5XZIA.js} +2 -2
- package/dist/{clipboard-menu-BvXJCSFN.d.ts → clipboard-menu-eemzEBqB.d.ts} +1 -1
- package/dist/{color-T63FLJNH.js → color-WBNVPFT5.js} +157 -158
- package/dist/{context-BfjYGPnH.d.ts → context-DR1dpO8b.d.ts} +1 -1
- package/dist/{context-menu-D3FtTn7v.d.ts → context-menu-qqpGBkny.d.ts} +6 -0
- package/dist/index.d.ts +29 -3
- package/dist/index.js +3 -3
- package/dist/{menu-PNQRTRNV.js → menu-5JZMGNOT.js} +3 -2
- package/dist/{menu-MSQSE6U5.js → menu-O3G4LM5O.js} +9 -4
- package/dist/next/index.d.ts +3 -3
- package/dist/next/index.js +8 -7
- package/dist/react/context-menu.d.ts +13 -7
- package/dist/react/context-menu.js +3 -2
- package/dist/react/image.d.ts +5 -2
- package/dist/react/image.js +1 -1
- package/dist/react/index.d.ts +3 -3
- package/dist/react/index.js +8 -7
- 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.d.ts +9 -3
- package/dist/react/video.js +2 -2
- package/dist/react-router/index.d.ts +3 -3
- package/dist/react-router/index.js +8 -7
- 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 +15 -1
- package/recipes/select/styles.css +24 -7
- package/registry.json +119 -10
- package/src/core/context-menu.ts +22 -3
- package/src/core/floating.ts +71 -0
- package/src/core/image-viewer.ts +47 -0
- package/src/core/player.ts +42 -0
- package/src/index.ts +3 -0
- package/src/react/context-menu/root.tsx +57 -20
- package/src/react/context-menu/styles.ts +15 -1
- package/src/react/floating.ts +185 -0
- package/src/react/image/icons.tsx +4 -0
- package/src/react/image/menu.tsx +5 -1
- package/src/react/image/types.ts +3 -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/src/react/video/index.tsx +8 -1
- package/src/react/video/types.ts +7 -1
- package/dist/viewer-73LSE6RR.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 }) {
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { IDENTITY, prefersReducedMotion, ZOOM_LIMITS, flightFrom, nextIndex, fittedSize, clampPan, zoomAt, pinchDistance, flightMs, ZOOM_STEP, ZOOM_DOUBLE, wheelFactor } from './chunk-
|
|
1
|
+
import { IDENTITY, prefersReducedMotion, ZOOM_LIMITS, flightFrom, nextIndex, fittedSize, clampPan, zoomAt, pinchDistance, flightMs, ZOOM_STEP, ZOOM_DOUBLE, wheelFactor } from './chunk-HUUINHPR.js';
|
|
2
2
|
import { injectImageStyles } from './chunk-EYZ366LP.js';
|
|
3
3
|
import { createPortal } from 'react-dom';
|
|
4
4
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
@@ -35,7 +35,14 @@ function Dots() {
|
|
|
35
35
|
function Download() {
|
|
36
36
|
return /* @__PURE__ */ jsx(Glyph, { children: /* @__PURE__ */ jsx("path", { d: "M12 3v12M7 11l5 5 5-5M4 20h16" }) });
|
|
37
37
|
}
|
|
38
|
-
|
|
38
|
+
function NewTab() {
|
|
39
|
+
return /* @__PURE__ */ jsxs(Glyph, { children: [
|
|
40
|
+
/* @__PURE__ */ jsx("path", { d: "M14 4h6v6" }),
|
|
41
|
+
/* @__PURE__ */ jsx("path", { d: "M20 4 11 13" }),
|
|
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
|
+
] });
|
|
44
|
+
}
|
|
45
|
+
var ImageMenu = lazy(() => import('./menu-O3G4LM5O.js').then((module) => ({ default: module.ImageMenu })));
|
|
39
46
|
function ImageViewer({
|
|
40
47
|
items,
|
|
41
48
|
index,
|
|
@@ -377,4 +384,4 @@ function MenuButton({ label, onPress, expanded }) {
|
|
|
377
384
|
);
|
|
378
385
|
}
|
|
379
386
|
|
|
380
|
-
export { Download, ImageViewer, MenuButton };
|
|
387
|
+
export { Download, ImageViewer, MenuButton, NewTab };
|
|
@@ -84,6 +84,33 @@ function saveAs(href, name, external = false) {
|
|
|
84
84
|
anchor.click();
|
|
85
85
|
anchor.remove();
|
|
86
86
|
}
|
|
87
|
+
function dataUrlToBlob(url) {
|
|
88
|
+
const comma = url.indexOf(",");
|
|
89
|
+
if (!url.startsWith("data:") || comma === -1) throw new Error("not a data URL");
|
|
90
|
+
const head = url.slice("data:".length, comma);
|
|
91
|
+
const body = url.slice(comma + 1);
|
|
92
|
+
const base64 = head.endsWith(";base64");
|
|
93
|
+
const type = (base64 ? head.slice(0, -";base64".length) : head) || "text/plain";
|
|
94
|
+
if (!base64) return new Blob([decodeURIComponent(body)], { type });
|
|
95
|
+
const binary = atob(body);
|
|
96
|
+
const bytes = new Uint8Array(binary.length);
|
|
97
|
+
for (let at = 0; at < binary.length; at += 1) bytes[at] = binary.charCodeAt(at);
|
|
98
|
+
return new Blob([bytes], { type });
|
|
99
|
+
}
|
|
100
|
+
function openInNewTab(url) {
|
|
101
|
+
if (typeof window === "undefined") return;
|
|
102
|
+
if (!url.startsWith("data:")) {
|
|
103
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
104
|
+
return;
|
|
105
|
+
}
|
|
106
|
+
try {
|
|
107
|
+
const href = URL.createObjectURL(dataUrlToBlob(url));
|
|
108
|
+
window.open(href, "_blank", "noopener,noreferrer");
|
|
109
|
+
setTimeout(() => URL.revokeObjectURL(href), 6e4);
|
|
110
|
+
} catch {
|
|
111
|
+
window.open(url, "_blank", "noopener,noreferrer");
|
|
112
|
+
}
|
|
113
|
+
}
|
|
87
114
|
var FLIGHT_MS = 300;
|
|
88
115
|
function flightMs(element) {
|
|
89
116
|
if (!element || typeof getComputedStyle === "undefined") return FLIGHT_MS;
|
|
@@ -105,4 +132,4 @@ function prefersReducedMotion() {
|
|
|
105
132
|
return window.matchMedia("(prefers-reduced-motion: reduce)").matches;
|
|
106
133
|
}
|
|
107
134
|
|
|
108
|
-
export { FLIGHT_MS, IDENTITY, ZOOM_DOUBLE, ZOOM_LIMITS, ZOOM_STEP, clampPan, clampScale, downloadFile, filenameFrom, fittedSize, flightFrom, flightMs, nextIndex, pinchDistance, prefersReducedMotion, wheelFactor, zoomAt };
|
|
135
|
+
export { FLIGHT_MS, IDENTITY, ZOOM_DOUBLE, ZOOM_LIMITS, ZOOM_STEP, clampPan, clampScale, dataUrlToBlob, downloadFile, filenameFrom, fittedSize, flightFrom, flightMs, nextIndex, openInNewTab, pinchDistance, prefersReducedMotion, wheelFactor, zoomAt };
|
|
@@ -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) {
|
|
@@ -79,6 +79,28 @@ async function togglePip(video) {
|
|
|
79
79
|
if (document.pictureInPictureElement) return void await document.exitPictureInPicture();
|
|
80
80
|
await video.requestPictureInPicture();
|
|
81
81
|
}
|
|
82
|
+
var SOURCE_TYPES = {
|
|
83
|
+
mp4: "video/mp4",
|
|
84
|
+
m4v: "video/mp4",
|
|
85
|
+
webm: "video/webm",
|
|
86
|
+
ogv: "video/ogg",
|
|
87
|
+
mov: "video/quicktime",
|
|
88
|
+
mkv: "video/x-matroska",
|
|
89
|
+
m3u8: "application/vnd.apple.mpegurl",
|
|
90
|
+
mpd: "application/dash+xml"
|
|
91
|
+
};
|
|
92
|
+
function sourceType(url) {
|
|
93
|
+
let path = url;
|
|
94
|
+
try {
|
|
95
|
+
path = new URL(url, typeof location === "undefined" ? "https://localhost" : location.href).pathname;
|
|
96
|
+
} catch {
|
|
97
|
+
path = url.split("?")[0].split("#")[0];
|
|
98
|
+
}
|
|
99
|
+
const name = path.split("/").pop() ?? "";
|
|
100
|
+
const dot = name.lastIndexOf(".");
|
|
101
|
+
if (dot <= 0) return void 0;
|
|
102
|
+
return SOURCE_TYPES[name.slice(dot + 1).toLowerCase()];
|
|
103
|
+
}
|
|
82
104
|
function captionTracks(list) {
|
|
83
105
|
if (!list) return [];
|
|
84
106
|
const found = [];
|
|
@@ -152,4 +174,4 @@ async function promptRemote(video) {
|
|
|
152
174
|
element.webkitShowPlaybackTargetPicker?.();
|
|
153
175
|
}
|
|
154
176
|
|
|
155
|
-
export { SEEK_JUMP, SEEK_STEP, SPEEDS, VOLUME_STEP, activeCaption, bufferedAhead, captionTracks, commandFor, formatTime, fractionAt, fullscreenElement, isTypingTarget, nextSpeed, progress, promptRemote, showCaption, supportsPip, supportsRemote, toggleFullscreen, togglePip, watchRemote };
|
|
177
|
+
export { SEEK_JUMP, SEEK_STEP, SPEEDS, VOLUME_STEP, activeCaption, bufferedAhead, captionTracks, commandFor, formatTime, fractionAt, fullscreenElement, isTypingTarget, nextSpeed, progress, promptRemote, showCaption, sourceType, supportsPip, supportsRemote, toggleFullscreen, togglePip, watchRemote };
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { SPEEDS, supportsPip, supportsRemote, watchRemote, showCaption, captionTracks, activeCaption, toggleFullscreen, togglePip, promptRemote, isTypingTarget, commandFor, progress, formatTime, bufferedAhead, fullscreenElement, fractionAt } from './chunk-
|
|
1
|
+
import { SPEEDS, supportsPip, supportsRemote, watchRemote, showCaption, captionTracks, activeCaption, toggleFullscreen, togglePip, promptRemote, isTypingTarget, commandFor, progress, sourceType, formatTime, bufferedAhead, fullscreenElement, fractionAt } from './chunk-JLZDS6CV.js';
|
|
2
2
|
import { lazy, useLayoutEffect, useRef, useState, useMemo, useEffect, useCallback, Suspense } from 'react';
|
|
3
3
|
import { jsx, jsxs, Fragment } from 'react/jsx-runtime';
|
|
4
4
|
|
|
@@ -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;
|
|
@@ -765,7 +765,14 @@ function Video(props) {
|
|
|
765
765
|
if (clickToPlay) toggle();
|
|
766
766
|
},
|
|
767
767
|
children: [
|
|
768
|
-
typeof src !== "string" && sources.map((source) =>
|
|
768
|
+
typeof src !== "string" && sources.map((source) => (
|
|
769
|
+
// The type is what lets the browser SKIP a file it cannot play without
|
|
770
|
+
// fetching it, which is the only reason to list several. Read from the
|
|
771
|
+
// extension when it was not given, and left off when the extension means
|
|
772
|
+
// nothing - a wrong type is worse than none, because the source is then
|
|
773
|
+
// skipped and the video does not play at all.
|
|
774
|
+
/* @__PURE__ */ jsx("source", { src: source.src, type: source.type ?? sourceType(source.src) }, source.src)
|
|
775
|
+
)),
|
|
769
776
|
tracks?.map((track) => /* @__PURE__ */ jsx(
|
|
770
777
|
"track",
|
|
771
778
|
{
|
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { createContextMenu, clipboardAction, performClipboardAction, inspectClipboardTarget, CLIPBOARD_PREFIX, clipboardEntries, isAction, clipboardHasText } from './chunk-
|
|
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';
|
|
@@ -23,6 +24,16 @@ var CONTEXT_MENU_STYLES = `
|
|
|
23
24
|
--enigma-menu-accent: #fbbf24;
|
|
24
25
|
--enigma-menu-danger: #f87171;
|
|
25
26
|
--enigma-menu-danger-bg: rgba(248, 113, 113, 0.12);
|
|
27
|
+
|
|
28
|
+
/* Above the lightbox, which sits at 9999, because a menu is opened FROM whatever is on
|
|
29
|
+
top: the image viewer's own three-dot menu was portalled to the same body and painted
|
|
30
|
+
underneath it, so its rows could be neither seen nor pressed. A menu is the topmost
|
|
31
|
+
transient surface on a page, and this is the number that says so. It belongs here and
|
|
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);
|
|
26
37
|
}
|
|
27
38
|
|
|
28
39
|
@media (prefers-color-scheme: light) {
|
|
@@ -43,7 +54,10 @@ var CONTEXT_MENU_STYLES = `
|
|
|
43
54
|
and what a menu opened at the pointer needs. Fixed rather than absolute so a scrolling
|
|
44
55
|
ancestor cannot drag it away from the place it was opened. */
|
|
45
56
|
[data-enigma-menu-panel] {
|
|
46
|
-
position: fixed; z-index:
|
|
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;
|
|
47
61
|
box-sizing: border-box;
|
|
48
62
|
min-width: var(--enigma-menu-min-width);
|
|
49
63
|
max-width: min(22rem, calc(100vw - 1rem));
|
|
@@ -98,6 +112,7 @@ var CONTEXT_MENU_STYLES = `
|
|
|
98
112
|
|
|
99
113
|
[data-enigma-menu-list] {
|
|
100
114
|
max-height: var(--enigma-menu-max-height);
|
|
115
|
+
min-height: 0; flex: 0 1 auto;
|
|
101
116
|
overflow-y: auto; overscroll-behavior: contain;
|
|
102
117
|
}
|
|
103
118
|
|
|
@@ -245,6 +260,7 @@ function ContextMenuRoot(props) {
|
|
|
245
260
|
const {
|
|
246
261
|
items,
|
|
247
262
|
title,
|
|
263
|
+
searchable,
|
|
248
264
|
disabled = false,
|
|
249
265
|
fuse,
|
|
250
266
|
fuseOptions,
|
|
@@ -272,6 +288,7 @@ function ContextMenuRoot(props) {
|
|
|
272
288
|
const instance = useMemo(() => createContextMenu({
|
|
273
289
|
items,
|
|
274
290
|
title,
|
|
291
|
+
searchable,
|
|
275
292
|
fuse,
|
|
276
293
|
fuseOptions,
|
|
277
294
|
matcher,
|
|
@@ -328,8 +345,8 @@ function ContextMenuRoot(props) {
|
|
|
328
345
|
return [...clip, { type: "separator", id: `${CLIPBOARD_PREFIX}rule` }, ...rows];
|
|
329
346
|
}, []);
|
|
330
347
|
useEffect(() => {
|
|
331
|
-
instance.update({ items: withClipboard(currentItems.current), title });
|
|
332
|
-
}, [instance, signature, title, withClipboard]);
|
|
348
|
+
instance.update({ items: withClipboard(currentItems.current), title, searchable });
|
|
349
|
+
}, [instance, signature, title, searchable, withClipboard]);
|
|
333
350
|
const cancelClose = useCallback(() => {
|
|
334
351
|
window.clearTimeout(closing.current);
|
|
335
352
|
}, []);
|
|
@@ -525,7 +542,7 @@ function ContextMenuContent({ chunk = 40, portal = true, ...props }) {
|
|
|
525
542
|
const panels = /* @__PURE__ */ jsx(Fragment, { children: menu.state.levels.map((_, level) => /* @__PURE__ */ jsx(ContextMenuPanel, { level, chunk, ...props }, level)) });
|
|
526
543
|
if (!portal) return panels;
|
|
527
544
|
if (!mounted || typeof document === "undefined") return null;
|
|
528
|
-
return createPortal(panels, document.body);
|
|
545
|
+
return createPortal(panels, floatingContainer(menu.triggerRef.current) ?? document.body);
|
|
529
546
|
}
|
|
530
547
|
function ContextMenuPanel({ level, chunk = 40, ...props }) {
|
|
531
548
|
const menu = useContextMenuContext("ContextMenu.Panel");
|
|
@@ -538,27 +555,33 @@ function ContextMenuPanel({ level, chunk = 40, ...props }) {
|
|
|
538
555
|
useLayoutEffect(() => {
|
|
539
556
|
const panel = ref.current;
|
|
540
557
|
if (!panel) return;
|
|
558
|
+
panel.style.maxHeight = "";
|
|
541
559
|
const width = panel.offsetWidth;
|
|
542
560
|
const height = panel.offsetHeight;
|
|
543
|
-
const
|
|
544
|
-
const
|
|
561
|
+
const frame = measureFrame(panel);
|
|
562
|
+
const { boundary } = frame;
|
|
545
563
|
let left;
|
|
546
564
|
let top;
|
|
547
565
|
if (level === 0) {
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
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`;
|
|
552
572
|
} else {
|
|
553
573
|
const parent = document.getElementById(menu.panelId(level - 1))?.getBoundingClientRect();
|
|
554
574
|
const row = document.getElementById(menu.itemId(level - 1, menu.state.levels[level - 1]?.active ?? -1))?.getBoundingClientRect();
|
|
555
575
|
const anchor = parent ?? { left: point?.x ?? MARGIN, right: point?.x ?? MARGIN, top: point?.y ?? MARGIN };
|
|
556
576
|
left = anchor.right - 2;
|
|
557
|
-
if (left + width >
|
|
577
|
+
if (left + width > boundary.right - MARGIN) left = Math.max(boundary.left + MARGIN, anchor.left - width + 2);
|
|
558
578
|
top = (row?.top ?? anchor.top) - 4;
|
|
559
|
-
if (top + height >
|
|
579
|
+
if (top + height > boundary.bottom - MARGIN) top = Math.max(boundary.top + MARGIN, boundary.bottom - MARGIN - height);
|
|
560
580
|
}
|
|
561
|
-
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
|
+
});
|
|
562
585
|
}, [level, point?.x, point?.y, rows, loading, menu, menu.state.levels.length]);
|
|
563
586
|
const isPlaced = placed !== null;
|
|
564
587
|
const isDeepest = menu.state.levels.length - 1 === level;
|
|
@@ -699,6 +722,7 @@ function ContextMenuRow({ level, index, entry, children, ...props }) {
|
|
|
699
722
|
if (entry.type === "separator") return /* @__PURE__ */ jsx("div", { ...props, role: "separator", "data-enigma-menu-separator": "" });
|
|
700
723
|
return /* @__PURE__ */ jsx("p", { ...props, "data-enigma-menu-label": "", "aria-hidden": "true", children: entry.label });
|
|
701
724
|
}
|
|
725
|
+
const reserveCheck = state?.entries.some((entry2) => isAction(entry2) && entry2.checked !== void 0) ?? false;
|
|
702
726
|
const checkable = item.checked !== void 0;
|
|
703
727
|
const role = checkable ? item.group ? "menuitemradio" : "menuitemcheckbox" : "menuitem";
|
|
704
728
|
return /* @__PURE__ */ jsx(
|
|
@@ -741,13 +765,13 @@ function ContextMenuRow({ level, index, entry, children, ...props }) {
|
|
|
741
765
|
if (event.defaultPrevented || item.disabled || event.detail !== 0) return;
|
|
742
766
|
menu.instance.select(level, index);
|
|
743
767
|
},
|
|
744
|
-
children: children ?? menu.renderItem?.(item, level, index) ?? /* @__PURE__ */ jsx(ContextMenuRowContent, { item, submenu })
|
|
768
|
+
children: children ?? menu.renderItem?.(item, level, index) ?? /* @__PURE__ */ jsx(ContextMenuRowContent, { item, submenu, reserveCheck })
|
|
745
769
|
}
|
|
746
770
|
);
|
|
747
771
|
}
|
|
748
|
-
function ContextMenuRowContent({ item, submenu }) {
|
|
772
|
+
function ContextMenuRowContent({ item, submenu, reserveCheck }) {
|
|
749
773
|
return /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
750
|
-
item.checked !== void 0 && /* @__PURE__ */ jsx("span", { "data-enigma-menu-check": "", "aria-hidden": "true" }),
|
|
774
|
+
(item.checked !== void 0 || reserveCheck) && /* @__PURE__ */ jsx("span", { "data-enigma-menu-check": "", "aria-hidden": "true" }),
|
|
751
775
|
item.icon ? /* @__PURE__ */ jsx("span", { "data-enigma-menu-icon": "", children: item.icon }) : null,
|
|
752
776
|
/* @__PURE__ */ jsxs("span", { "data-enigma-menu-item-text": "", children: [
|
|
753
777
|
/* @__PURE__ */ jsx("span", { "data-enigma-menu-item-label": "", children: item.label }),
|
|
@@ -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,
|
|
@@ -6,7 +6,7 @@ function isAction(entry) {
|
|
|
6
6
|
return (entry.type ?? "item") === "item";
|
|
7
7
|
}
|
|
8
8
|
var CONTEXT_MENU_SEARCH_KEYS = ["label", "description", "group", "keywords"];
|
|
9
|
-
var SEARCHABLE_FROM =
|
|
9
|
+
var SEARCHABLE_FROM = 12;
|
|
10
10
|
var PAGE = 5;
|
|
11
11
|
var DEFAULT_CACHE_MS = 5 * 60 * 1e3;
|
|
12
12
|
function fold(value) {
|
|
@@ -32,7 +32,7 @@ function createContextMenu(options = {}) {
|
|
|
32
32
|
return path.join("\0");
|
|
33
33
|
}
|
|
34
34
|
function searchableFor(item, entries) {
|
|
35
|
-
const asked = item
|
|
35
|
+
const asked = (item ? item.searchable : opts.searchable) ?? "auto";
|
|
36
36
|
if (asked !== "auto") return asked;
|
|
37
37
|
return entries.filter(isAction).length >= SEARCHABLE_FROM;
|
|
38
38
|
}
|