@nextlyhq/ui 0.0.2-alpha.54 → 0.0.2-alpha.57
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/color.cjs +193 -0
- package/dist/color.cjs.map +1 -0
- package/dist/color.d.cts +251 -0
- package/dist/color.d.ts +251 -0
- package/dist/color.mjs +179 -0
- package/dist/color.mjs.map +1 -0
- package/dist/index.cjs +1246 -51
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +445 -1
- package/dist/index.d.ts +445 -1
- package/dist/index.mjs +1230 -44
- package/dist/index.mjs.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/styles.scoped.css +1 -1
- package/dist/theme.css +138 -104
- package/docs/plugin-ui-authoring.md +12 -10
- package/package.json +22 -8
package/dist/index.cjs
CHANGED
|
@@ -29,8 +29,8 @@ var __toESM = (mod, isNodeMode, target) => (target = mod != null ? __create(__ge
|
|
|
29
29
|
var __toCommonJS = (mod) => __copyProps(__defProp({}, "__esModule", { value: true }), mod);
|
|
30
30
|
|
|
31
31
|
// src/index.ts
|
|
32
|
-
var
|
|
33
|
-
__export(
|
|
32
|
+
var src_exports = {};
|
|
33
|
+
__export(src_exports, {
|
|
34
34
|
Accordion: () => Accordion,
|
|
35
35
|
AccordionContent: () => AccordionContent,
|
|
36
36
|
AccordionItem: () => AccordionItem,
|
|
@@ -65,6 +65,7 @@ __export(index_exports, {
|
|
|
65
65
|
Collapsible: () => Collapsible,
|
|
66
66
|
CollapsibleContent: () => CollapsibleContent2,
|
|
67
67
|
CollapsibleTrigger: () => CollapsibleTrigger2,
|
|
68
|
+
ColorPicker: () => ColorPicker,
|
|
68
69
|
Command: () => Command,
|
|
69
70
|
CommandDialog: () => CommandDialog,
|
|
70
71
|
CommandEmpty: () => CommandEmpty,
|
|
@@ -149,7 +150,10 @@ __export(index_exports, {
|
|
|
149
150
|
SheetPortal: () => SheetPortal,
|
|
150
151
|
SheetTitle: () => SheetTitle,
|
|
151
152
|
SheetTrigger: () => SheetTrigger,
|
|
153
|
+
ShortcutProvider: () => ShortcutProvider,
|
|
154
|
+
ShortcutScope: () => ShortcutScope,
|
|
152
155
|
Skeleton: () => Skeleton,
|
|
156
|
+
Slider: () => Slider,
|
|
153
157
|
Spinner: () => Spinner,
|
|
154
158
|
Stack: () => Stack,
|
|
155
159
|
Stat: () => Stat,
|
|
@@ -183,16 +187,21 @@ __export(index_exports, {
|
|
|
183
187
|
badgeVariants: () => badgeVariants,
|
|
184
188
|
buttonVariants: () => buttonVariants,
|
|
185
189
|
cardVariants: () => cardVariants,
|
|
190
|
+
createShortcutManager: () => createShortcutManager,
|
|
186
191
|
dialogContentVariants: () => dialogContentVariants,
|
|
187
192
|
inputVariants: () => inputVariants,
|
|
193
|
+
parseKeys: () => parseKeys,
|
|
188
194
|
progressVariants: () => progressVariants,
|
|
189
195
|
selectTriggerVariants: () => selectTriggerVariants,
|
|
190
196
|
sheetVariants: () => sheetVariants,
|
|
191
197
|
spinnerVariants: () => spinnerVariants,
|
|
192
198
|
toast: () => import_sonner.toast,
|
|
193
|
-
|
|
199
|
+
useActiveShortcuts: () => useActiveShortcuts,
|
|
200
|
+
usePortalContainer: () => usePortalContainer,
|
|
201
|
+
useShortcutManager: () => useShortcutManager,
|
|
202
|
+
useShortcuts: () => useShortcuts
|
|
194
203
|
});
|
|
195
|
-
module.exports = __toCommonJS(
|
|
204
|
+
module.exports = __toCommonJS(src_exports);
|
|
196
205
|
|
|
197
206
|
// src/components/button.tsx
|
|
198
207
|
var import_react_slot = require("@radix-ui/react-slot");
|
|
@@ -216,11 +225,15 @@ var buttonVariants = (0, import_class_variance_authority.cva)(
|
|
|
216
225
|
variant: {
|
|
217
226
|
default: "bg-primary text-primary-foreground border border-transparent hover:opacity-90",
|
|
218
227
|
primary: "bg-primary text-primary-foreground border border-transparent hover:opacity-90",
|
|
219
|
-
// Solid fill uses the emphasis token so
|
|
220
|
-
//
|
|
221
|
-
// Hover darkens to a deeper shade
|
|
222
|
-
// composite the fill toward the page and drop
|
|
223
|
-
|
|
228
|
+
// Solid fill uses the emphasis token so on-color text stays AA in dark
|
|
229
|
+
// mode (the base token is the readable text color, too light here).
|
|
230
|
+
// Hover darkens to a deeper shade rather than opacity-90, which would
|
|
231
|
+
// composite the fill toward the page and drop the label under 4.5:1.
|
|
232
|
+
// One step, not two: the label is white in light mode and black in
|
|
233
|
+
// dark, so mixing the fill toward black moves it away from the label in
|
|
234
|
+
// one mode and into it in the other. `-600` clears both (5.92:1 light,
|
|
235
|
+
// 5.67:1 dark); `-700` reads at 3.70:1 against the dark label.
|
|
236
|
+
destructive: "bg-destructive-solid text-destructive-foreground border border-transparent hover:bg-destructive-600",
|
|
224
237
|
// border-border is the decorative separator token, and it is the right
|
|
225
238
|
// one here: a button is identified by its label and fill, so its edge
|
|
226
239
|
// carries no meaning on its own and is not held to the 3:1 minimum that
|
|
@@ -2280,7 +2293,7 @@ var TableSkeleton = ({
|
|
|
2280
2293
|
] })
|
|
2281
2294
|
] }) : /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GrayBar, { className: "h-4 w-[60%] max-w-[120px]" }) }, colIdx)) }, rowIdx)) })
|
|
2282
2295
|
] }) }),
|
|
2283
|
-
!hideFooter && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "table-footer border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between px-2 py-4
|
|
2296
|
+
!hideFooter && /* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "table-footer border-t border-border", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center justify-between px-2 py-4", children: [
|
|
2284
2297
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsx)("div", { className: "flex items-center gap-2 text-sm", children: /* @__PURE__ */ (0, import_jsx_runtime33.jsx)(GrayBar, { className: "h-4 w-[120px]" }) }),
|
|
2285
2298
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-6", children: [
|
|
2286
2299
|
/* @__PURE__ */ (0, import_jsx_runtime33.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
@@ -2305,11 +2318,353 @@ var TableSkeleton = ({
|
|
|
2305
2318
|
};
|
|
2306
2319
|
TableSkeleton.displayName = "TableSkeleton";
|
|
2307
2320
|
|
|
2308
|
-
// src/components/
|
|
2309
|
-
var ContextMenuPrimitive = __toESM(require("@radix-ui/react-context-menu"), 1);
|
|
2321
|
+
// src/components/color-picker.tsx
|
|
2310
2322
|
var import_lucide_react12 = require("lucide-react");
|
|
2311
2323
|
var React9 = __toESM(require("react"), 1);
|
|
2324
|
+
|
|
2325
|
+
// src/lib/color/convert.ts
|
|
2326
|
+
var clamp01 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
2327
|
+
function normalizeHue(hue) {
|
|
2328
|
+
if (!Number.isFinite(hue)) return 0;
|
|
2329
|
+
const wrapped = hue % 360;
|
|
2330
|
+
return wrapped < 0 ? wrapped + 360 : wrapped;
|
|
2331
|
+
}
|
|
2332
|
+
function hsvToRgb({ h, s, v }) {
|
|
2333
|
+
const hue = normalizeHue(h);
|
|
2334
|
+
const sat = clamp01(s);
|
|
2335
|
+
const val = clamp01(v);
|
|
2336
|
+
const sector = hue / 60;
|
|
2337
|
+
const chroma = val * sat;
|
|
2338
|
+
const x = chroma * (1 - Math.abs(sector % 2 - 1));
|
|
2339
|
+
const base = val - chroma;
|
|
2340
|
+
let rgb;
|
|
2341
|
+
if (sector < 1) rgb = [chroma, x, 0];
|
|
2342
|
+
else if (sector < 2) rgb = [x, chroma, 0];
|
|
2343
|
+
else if (sector < 3) rgb = [0, chroma, x];
|
|
2344
|
+
else if (sector < 4) rgb = [0, x, chroma];
|
|
2345
|
+
else if (sector < 5) rgb = [x, 0, chroma];
|
|
2346
|
+
else rgb = [chroma, 0, x];
|
|
2347
|
+
return { r: rgb[0] + base, g: rgb[1] + base, b: rgb[2] + base };
|
|
2348
|
+
}
|
|
2349
|
+
function rgbToHsv({ r, g, b }) {
|
|
2350
|
+
const red = clamp01(r);
|
|
2351
|
+
const green = clamp01(g);
|
|
2352
|
+
const blue = clamp01(b);
|
|
2353
|
+
const max = Math.max(red, green, blue);
|
|
2354
|
+
const min = Math.min(red, green, blue);
|
|
2355
|
+
const chroma = max - min;
|
|
2356
|
+
let hue = 0;
|
|
2357
|
+
if (chroma !== 0) {
|
|
2358
|
+
if (max === red) hue = (green - blue) / chroma % 6;
|
|
2359
|
+
else if (max === green) hue = (blue - red) / chroma + 2;
|
|
2360
|
+
else hue = (red - green) / chroma + 4;
|
|
2361
|
+
hue *= 60;
|
|
2362
|
+
}
|
|
2363
|
+
return {
|
|
2364
|
+
h: normalizeHue(hue),
|
|
2365
|
+
s: max === 0 ? 0 : chroma / max,
|
|
2366
|
+
v: max
|
|
2367
|
+
};
|
|
2368
|
+
}
|
|
2369
|
+
|
|
2370
|
+
// src/lib/color/hex.ts
|
|
2371
|
+
var HEX = /^#?(?:[0-9a-f]{3,4}|[0-9a-f]{6}|[0-9a-f]{8})$/i;
|
|
2372
|
+
var clamp012 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
2373
|
+
function pair(channel) {
|
|
2374
|
+
const value = Number.isFinite(channel) ? clamp012(channel) : 0;
|
|
2375
|
+
return Math.round(value * 255).toString(16).padStart(2, "0");
|
|
2376
|
+
}
|
|
2377
|
+
function parseHex(input) {
|
|
2378
|
+
const text = input.trim();
|
|
2379
|
+
if (!HEX.test(text)) return null;
|
|
2380
|
+
const digits = text.replace("#", "");
|
|
2381
|
+
const short = digits.length < 6;
|
|
2382
|
+
const size = short ? 1 : 2;
|
|
2383
|
+
const channel = (index) => {
|
|
2384
|
+
const slice = digits.slice(index * size, index * size + size);
|
|
2385
|
+
return parseInt(short ? slice + slice : slice, 16) / 255;
|
|
2386
|
+
};
|
|
2387
|
+
const hasAlpha = digits.length === 4 || digits.length === 8;
|
|
2388
|
+
return {
|
|
2389
|
+
r: channel(0),
|
|
2390
|
+
g: channel(1),
|
|
2391
|
+
b: channel(2),
|
|
2392
|
+
alpha: hasAlpha ? channel(3) : 1
|
|
2393
|
+
};
|
|
2394
|
+
}
|
|
2395
|
+
function toHex(color, alpha = 1) {
|
|
2396
|
+
const opacity = Number.isFinite(alpha) ? clamp012(alpha) : 1;
|
|
2397
|
+
const opaque = `#${pair(color.r)}${pair(color.g)}${pair(color.b)}`;
|
|
2398
|
+
return opacity === 1 ? opaque : `${opaque}${pair(opacity)}`;
|
|
2399
|
+
}
|
|
2400
|
+
|
|
2401
|
+
// src/lib/color/picker-geometry.ts
|
|
2402
|
+
var clamp013 = (n) => n < 0 ? 0 : n > 1 ? 1 : n;
|
|
2403
|
+
function pointOnSurface(clientX, clientY, rect) {
|
|
2404
|
+
return {
|
|
2405
|
+
x: rect.width === 0 ? 0 : clamp013((clientX - rect.left) / rect.width),
|
|
2406
|
+
y: rect.height === 0 ? 0 : clamp013((clientY - rect.top) / rect.height)
|
|
2407
|
+
};
|
|
2408
|
+
}
|
|
2409
|
+
function saturationValueAt(point) {
|
|
2410
|
+
return { s: clamp013(point.x), v: 1 - clamp013(point.y) };
|
|
2411
|
+
}
|
|
2412
|
+
function surfacePointFor(s, v) {
|
|
2413
|
+
return { x: clamp013(s), y: 1 - clamp013(v) };
|
|
2414
|
+
}
|
|
2415
|
+
function hueAt(fraction) {
|
|
2416
|
+
const hue = clamp013(fraction) * 360;
|
|
2417
|
+
return hue >= 360 ? 0 : hue;
|
|
2418
|
+
}
|
|
2419
|
+
function huePosition(hue) {
|
|
2420
|
+
const wrapped = (hue % 360 + 360) % 360;
|
|
2421
|
+
return wrapped / 360;
|
|
2422
|
+
}
|
|
2423
|
+
function hueSliderValue(hue, max) {
|
|
2424
|
+
const step = Math.round(huePosition(hue) * (max + 1));
|
|
2425
|
+
return step > max ? max : step;
|
|
2426
|
+
}
|
|
2427
|
+
|
|
2428
|
+
// src/components/color-picker.tsx
|
|
2312
2429
|
var import_jsx_runtime34 = require("react/jsx-runtime");
|
|
2430
|
+
function toHsva(hex) {
|
|
2431
|
+
const parsed = parseHex(hex);
|
|
2432
|
+
if (!parsed) return { h: 0, s: 0, v: 0, a: 1 };
|
|
2433
|
+
const { r, g, b, alpha } = parsed;
|
|
2434
|
+
return { ...rgbToHsv({ r, g, b }), a: alpha };
|
|
2435
|
+
}
|
|
2436
|
+
function toHexString(hsva, withAlpha) {
|
|
2437
|
+
return toHex(hsvToRgb(hsva), withAlpha ? hsva.a : 1);
|
|
2438
|
+
}
|
|
2439
|
+
var HUE_MAX = 359;
|
|
2440
|
+
function hsvaFrom(color, alpha, currentHue) {
|
|
2441
|
+
const hsv = rgbToHsv(color);
|
|
2442
|
+
return { ...hsv, h: hsv.s === 0 ? currentHue : hsv.h, a: alpha };
|
|
2443
|
+
}
|
|
2444
|
+
var SLIDER = "h-3 w-full cursor-pointer appearance-none rounded-full";
|
|
2445
|
+
var CHECKERBOARD = "repeating-conic-gradient(#c8c8c8 0% 25%, #ffffff 0% 50%)";
|
|
2446
|
+
function eyeDropperSupported() {
|
|
2447
|
+
return typeof window !== "undefined" && "EyeDropper" in window;
|
|
2448
|
+
}
|
|
2449
|
+
function ColorPicker({
|
|
2450
|
+
color,
|
|
2451
|
+
onColorChange,
|
|
2452
|
+
swatches = [],
|
|
2453
|
+
onSwatchSelect,
|
|
2454
|
+
recentColors = [],
|
|
2455
|
+
showAlpha = false,
|
|
2456
|
+
className
|
|
2457
|
+
}) {
|
|
2458
|
+
const fieldId = React9.useId();
|
|
2459
|
+
const surfaceRef = React9.useRef(null);
|
|
2460
|
+
const [hsva, setHsva] = React9.useState(() => toHsva(color));
|
|
2461
|
+
const [draftHex, setDraftHex] = React9.useState(null);
|
|
2462
|
+
const rendered = toHexString(hsva, showAlpha);
|
|
2463
|
+
React9.useEffect(() => {
|
|
2464
|
+
const incoming = parseHex(color);
|
|
2465
|
+
if (incoming && toHex(incoming, showAlpha ? incoming.alpha : 1) !== rendered) {
|
|
2466
|
+
setHsva((prev) => hsvaFrom(incoming, incoming.alpha, prev.h));
|
|
2467
|
+
}
|
|
2468
|
+
}, [color, rendered, showAlpha]);
|
|
2469
|
+
const commit = (next) => {
|
|
2470
|
+
setHsva(next);
|
|
2471
|
+
setDraftHex(null);
|
|
2472
|
+
onColorChange(toHexString(next, showAlpha));
|
|
2473
|
+
};
|
|
2474
|
+
const trackPointer = (event) => {
|
|
2475
|
+
const rect = surfaceRef.current?.getBoundingClientRect();
|
|
2476
|
+
if (!rect) return;
|
|
2477
|
+
const { s, v } = saturationValueAt(
|
|
2478
|
+
pointOnSurface(event.clientX, event.clientY, rect)
|
|
2479
|
+
);
|
|
2480
|
+
commit({ ...hsva, s, v });
|
|
2481
|
+
};
|
|
2482
|
+
const nudge = (ds, dv) => {
|
|
2483
|
+
const { s, v } = saturationValueAt(
|
|
2484
|
+
surfacePointFor(hsva.s + ds, hsva.v + dv)
|
|
2485
|
+
);
|
|
2486
|
+
commit({ ...hsva, s, v });
|
|
2487
|
+
};
|
|
2488
|
+
const handleSurfaceKey = (event) => {
|
|
2489
|
+
const step = event.shiftKey ? 0.1 : 0.01;
|
|
2490
|
+
const moves = {
|
|
2491
|
+
ArrowLeft: [-step, 0],
|
|
2492
|
+
ArrowRight: [step, 0],
|
|
2493
|
+
ArrowUp: [0, step],
|
|
2494
|
+
ArrowDown: [0, -step]
|
|
2495
|
+
};
|
|
2496
|
+
const move = moves[event.key];
|
|
2497
|
+
if (!move) return;
|
|
2498
|
+
event.preventDefault();
|
|
2499
|
+
nudge(move[0], move[1]);
|
|
2500
|
+
};
|
|
2501
|
+
const [canPickFromScreen, setCanPickFromScreen] = React9.useState(false);
|
|
2502
|
+
React9.useEffect(() => {
|
|
2503
|
+
setCanPickFromScreen(eyeDropperSupported());
|
|
2504
|
+
}, []);
|
|
2505
|
+
const handle = surfacePointFor(hsva.s, hsva.v);
|
|
2506
|
+
const hueOnly = toHex(hsvToRgb({ h: hsva.h, s: 1, v: 1 }));
|
|
2507
|
+
const pickFromScreen = async () => {
|
|
2508
|
+
const ctor = window.EyeDropper;
|
|
2509
|
+
if (!ctor) return;
|
|
2510
|
+
let sampled;
|
|
2511
|
+
try {
|
|
2512
|
+
sampled = (await new ctor().open()).sRGBHex;
|
|
2513
|
+
} catch {
|
|
2514
|
+
return;
|
|
2515
|
+
}
|
|
2516
|
+
const parsed = parseHex(sampled);
|
|
2517
|
+
if (parsed) commit(hsvaFrom(parsed, hsva.a, hsva.h));
|
|
2518
|
+
};
|
|
2519
|
+
return /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: cn("w-64 space-y-3", className), children: [
|
|
2520
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2521
|
+
"div",
|
|
2522
|
+
{
|
|
2523
|
+
ref: surfaceRef,
|
|
2524
|
+
role: "application",
|
|
2525
|
+
tabIndex: 0,
|
|
2526
|
+
"aria-label": `Saturation and brightness: ${Math.round(hsva.s * 100)}% saturation, ${Math.round(hsva.v * 100)}% brightness. Arrow keys adjust.`,
|
|
2527
|
+
className: "ring-offset-background focus-visible:ring-ring relative h-40 w-full cursor-crosshair touch-none rounded-md focus-visible:ring-2 focus-visible:ring-offset-2 focus-visible:outline-none",
|
|
2528
|
+
style: {
|
|
2529
|
+
backgroundColor: hueOnly,
|
|
2530
|
+
// Value on TOP of saturation. CSS paints the first layer nearest the
|
|
2531
|
+
// viewer, so the reverse order lets the opaque white end of the
|
|
2532
|
+
// saturation ramp cover the black end of the value ramp: the
|
|
2533
|
+
// bottom-left corner displays white while selecting black.
|
|
2534
|
+
backgroundImage: "linear-gradient(to top, #000, transparent), linear-gradient(to right, #fff, transparent)"
|
|
2535
|
+
},
|
|
2536
|
+
onKeyDown: handleSurfaceKey,
|
|
2537
|
+
onPointerDown: (event) => {
|
|
2538
|
+
event.currentTarget.setPointerCapture(event.pointerId);
|
|
2539
|
+
trackPointer(event);
|
|
2540
|
+
},
|
|
2541
|
+
onPointerMove: (event) => {
|
|
2542
|
+
if (event.buttons === 1) trackPointer(event);
|
|
2543
|
+
},
|
|
2544
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2545
|
+
"span",
|
|
2546
|
+
{
|
|
2547
|
+
"aria-hidden": "true",
|
|
2548
|
+
className: "pointer-events-none absolute size-3 -translate-x-1/2 -translate-y-1/2 rounded-full border-2 border-white shadow-sm ring-1 ring-black/60",
|
|
2549
|
+
style: { left: `${handle.x * 100}%`, top: `${handle.y * 100}%` }
|
|
2550
|
+
}
|
|
2551
|
+
)
|
|
2552
|
+
}
|
|
2553
|
+
),
|
|
2554
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-hue`, children: "Hue" }),
|
|
2555
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2556
|
+
"input",
|
|
2557
|
+
{
|
|
2558
|
+
id: `${fieldId}-hue`,
|
|
2559
|
+
type: "range",
|
|
2560
|
+
min: 0,
|
|
2561
|
+
max: HUE_MAX,
|
|
2562
|
+
step: 1,
|
|
2563
|
+
value: hueSliderValue(hsva.h, HUE_MAX),
|
|
2564
|
+
onChange: (event) => commit({ ...hsva, h: hueAt(+event.target.value / (HUE_MAX + 1)) }),
|
|
2565
|
+
className: SLIDER,
|
|
2566
|
+
style: {
|
|
2567
|
+
backgroundImage: "linear-gradient(to right, #f00, #ff0, #0f0, #0ff, #00f, #f0f, #f00)"
|
|
2568
|
+
}
|
|
2569
|
+
}
|
|
2570
|
+
),
|
|
2571
|
+
showAlpha && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)(import_jsx_runtime34.Fragment, { children: [
|
|
2572
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-alpha`, children: "Opacity" }),
|
|
2573
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2574
|
+
"input",
|
|
2575
|
+
{
|
|
2576
|
+
id: `${fieldId}-alpha`,
|
|
2577
|
+
type: "range",
|
|
2578
|
+
min: 0,
|
|
2579
|
+
max: 100,
|
|
2580
|
+
step: 1,
|
|
2581
|
+
value: Math.round(hsva.a * 100),
|
|
2582
|
+
onChange: (event) => commit({ ...hsva, a: +event.target.value / 100 }),
|
|
2583
|
+
className: SLIDER,
|
|
2584
|
+
style: {
|
|
2585
|
+
// The ramp runs to the colour being edited, over a chequerboard,
|
|
2586
|
+
// so the track shows what the slider actually controls. Without
|
|
2587
|
+
// any background this rendered as a blank 12px strip whose only
|
|
2588
|
+
// label was screen-reader-only.
|
|
2589
|
+
backgroundImage: `linear-gradient(to right, transparent, ${toHexString({ ...hsva, a: 1 }, false)}), ${CHECKERBOARD}`,
|
|
2590
|
+
backgroundSize: "100% 100%, 8px 8px"
|
|
2591
|
+
}
|
|
2592
|
+
}
|
|
2593
|
+
)
|
|
2594
|
+
] }),
|
|
2595
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { className: "flex items-center gap-2", children: [
|
|
2596
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("label", { className: "sr-only", htmlFor: `${fieldId}-hex`, children: "Hex colour" }),
|
|
2597
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2598
|
+
Input,
|
|
2599
|
+
{
|
|
2600
|
+
id: `${fieldId}-hex`,
|
|
2601
|
+
className: "font-mono",
|
|
2602
|
+
value: draftHex ?? rendered,
|
|
2603
|
+
onChange: (event) => {
|
|
2604
|
+
const text = event.target.value;
|
|
2605
|
+
setDraftHex(text);
|
|
2606
|
+
const parsed = parseHex(text);
|
|
2607
|
+
if (parsed) {
|
|
2608
|
+
setHsva(hsvaFrom(parsed, parsed.alpha, hsva.h));
|
|
2609
|
+
onColorChange(toHex(parsed, showAlpha ? parsed.alpha : 1));
|
|
2610
|
+
}
|
|
2611
|
+
},
|
|
2612
|
+
onBlur: () => setDraftHex(null)
|
|
2613
|
+
}
|
|
2614
|
+
),
|
|
2615
|
+
canPickFromScreen && /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2616
|
+
Button,
|
|
2617
|
+
{
|
|
2618
|
+
type: "button",
|
|
2619
|
+
variant: "outline",
|
|
2620
|
+
size: "icon",
|
|
2621
|
+
"aria-label": "Pick a colour from the screen",
|
|
2622
|
+
onClick: () => void pickFromScreen(),
|
|
2623
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(import_lucide_react12.Pipette, { className: "size-4" })
|
|
2624
|
+
}
|
|
2625
|
+
)
|
|
2626
|
+
] }),
|
|
2627
|
+
swatches.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { children: [
|
|
2628
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-muted-foreground mb-1 text-xs", children: "Presets" }),
|
|
2629
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-wrap gap-1", children: swatches.map((swatch) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2630
|
+
"button",
|
|
2631
|
+
{
|
|
2632
|
+
type: "button",
|
|
2633
|
+
title: swatch.label,
|
|
2634
|
+
"aria-label": swatch.label,
|
|
2635
|
+
className: "size-6 rounded border shadow-sm",
|
|
2636
|
+
style: { backgroundColor: swatch.color },
|
|
2637
|
+
onClick: () => onSwatchSelect?.(swatch)
|
|
2638
|
+
},
|
|
2639
|
+
swatch.id
|
|
2640
|
+
)) })
|
|
2641
|
+
] }),
|
|
2642
|
+
recentColors.length > 0 && /* @__PURE__ */ (0, import_jsx_runtime34.jsxs)("div", { children: [
|
|
2643
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("p", { className: "text-muted-foreground mb-1 text-xs", children: "Recent" }),
|
|
2644
|
+
/* @__PURE__ */ (0, import_jsx_runtime34.jsx)("div", { className: "flex flex-wrap gap-1", children: recentColors.map((recent) => /* @__PURE__ */ (0, import_jsx_runtime34.jsx)(
|
|
2645
|
+
"button",
|
|
2646
|
+
{
|
|
2647
|
+
type: "button",
|
|
2648
|
+
title: recent,
|
|
2649
|
+
"aria-label": recent,
|
|
2650
|
+
className: "size-6 rounded border shadow-sm",
|
|
2651
|
+
style: { backgroundColor: recent },
|
|
2652
|
+
onClick: () => {
|
|
2653
|
+
const parsed = parseHex(recent);
|
|
2654
|
+
if (parsed) commit(hsvaFrom(parsed, parsed.alpha, hsva.h));
|
|
2655
|
+
}
|
|
2656
|
+
},
|
|
2657
|
+
recent
|
|
2658
|
+
)) })
|
|
2659
|
+
] })
|
|
2660
|
+
] });
|
|
2661
|
+
}
|
|
2662
|
+
|
|
2663
|
+
// src/components/context-menu.tsx
|
|
2664
|
+
var ContextMenuPrimitive = __toESM(require("@radix-ui/react-context-menu"), 1);
|
|
2665
|
+
var import_lucide_react13 = require("lucide-react");
|
|
2666
|
+
var React10 = __toESM(require("react"), 1);
|
|
2667
|
+
var import_jsx_runtime35 = require("react/jsx-runtime");
|
|
2313
2668
|
var menuItemBase2 = "cursor-pointer transition-colors data-[highlighted]:bg-muted data-[highlighted]:text-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50";
|
|
2314
2669
|
var menuSurface = "z-50 max-h-[var(--radix-context-menu-content-available-height)] min-w-[8rem] overflow-y-auto overflow-x-hidden rounded-lg border border-border bg-popover p-1 text-popover-foreground shadow-md data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 origin-[--radix-context-menu-content-transform-origin]";
|
|
2315
2670
|
var ContextMenu = ContextMenuPrimitive.Root;
|
|
@@ -2317,7 +2672,7 @@ var ContextMenuTrigger = ContextMenuPrimitive.Trigger;
|
|
|
2317
2672
|
var ContextMenuGroup = ContextMenuPrimitive.Group;
|
|
2318
2673
|
var ContextMenuSub = ContextMenuPrimitive.Sub;
|
|
2319
2674
|
var ContextMenuRadioGroup = ContextMenuPrimitive.RadioGroup;
|
|
2320
|
-
var ContextMenuSubTrigger =
|
|
2675
|
+
var ContextMenuSubTrigger = React10.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2321
2676
|
ContextMenuPrimitive.SubTrigger,
|
|
2322
2677
|
{
|
|
2323
2678
|
ref,
|
|
@@ -2330,14 +2685,14 @@ var ContextMenuSubTrigger = React9.forwardRef(({ className, inset, children, ...
|
|
|
2330
2685
|
...props,
|
|
2331
2686
|
children: [
|
|
2332
2687
|
children,
|
|
2333
|
-
/* @__PURE__ */ (0,
|
|
2688
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.ChevronRight, { className: "ml-auto" })
|
|
2334
2689
|
]
|
|
2335
2690
|
}
|
|
2336
2691
|
));
|
|
2337
2692
|
ContextMenuSubTrigger.displayName = ContextMenuPrimitive.SubTrigger.displayName;
|
|
2338
|
-
var ContextMenuSubContent =
|
|
2693
|
+
var ContextMenuSubContent = React10.forwardRef(({ className, ...props }, ref) => {
|
|
2339
2694
|
const portalContainer = usePortalContainer();
|
|
2340
|
-
return /* @__PURE__ */ (0,
|
|
2695
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2341
2696
|
ContextMenuPrimitive.SubContent,
|
|
2342
2697
|
{
|
|
2343
2698
|
ref,
|
|
@@ -2347,9 +2702,9 @@ var ContextMenuSubContent = React9.forwardRef(({ className, ...props }, ref) =>
|
|
|
2347
2702
|
) });
|
|
2348
2703
|
});
|
|
2349
2704
|
ContextMenuSubContent.displayName = ContextMenuPrimitive.SubContent.displayName;
|
|
2350
|
-
var ContextMenuContent =
|
|
2705
|
+
var ContextMenuContent = React10.forwardRef(({ className, ...props }, ref) => {
|
|
2351
2706
|
const portalContainer = usePortalContainer();
|
|
2352
|
-
return /* @__PURE__ */ (0,
|
|
2707
|
+
return /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.Portal, { container: portalContainer, children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2353
2708
|
ContextMenuPrimitive.Content,
|
|
2354
2709
|
{
|
|
2355
2710
|
ref,
|
|
@@ -2359,7 +2714,7 @@ var ContextMenuContent = React9.forwardRef(({ className, ...props }, ref) => {
|
|
|
2359
2714
|
) });
|
|
2360
2715
|
});
|
|
2361
2716
|
ContextMenuContent.displayName = ContextMenuPrimitive.Content.displayName;
|
|
2362
|
-
var ContextMenuItem =
|
|
2717
|
+
var ContextMenuItem = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2363
2718
|
ContextMenuPrimitive.Item,
|
|
2364
2719
|
{
|
|
2365
2720
|
ref,
|
|
@@ -2373,7 +2728,7 @@ var ContextMenuItem = React9.forwardRef(({ className, inset, ...props }, ref) =>
|
|
|
2373
2728
|
}
|
|
2374
2729
|
));
|
|
2375
2730
|
ContextMenuItem.displayName = ContextMenuPrimitive.Item.displayName;
|
|
2376
|
-
var ContextMenuCheckboxItem =
|
|
2731
|
+
var ContextMenuCheckboxItem = React10.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2377
2732
|
ContextMenuPrimitive.CheckboxItem,
|
|
2378
2733
|
{
|
|
2379
2734
|
ref,
|
|
@@ -2385,13 +2740,13 @@ var ContextMenuCheckboxItem = React9.forwardRef(({ className, children, checked,
|
|
|
2385
2740
|
checked,
|
|
2386
2741
|
...props,
|
|
2387
2742
|
children: [
|
|
2388
|
-
/* @__PURE__ */ (0,
|
|
2743
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Check, { className: "h-4 w-4" }) }) }),
|
|
2389
2744
|
children
|
|
2390
2745
|
]
|
|
2391
2746
|
}
|
|
2392
2747
|
));
|
|
2393
2748
|
ContextMenuCheckboxItem.displayName = ContextMenuPrimitive.CheckboxItem.displayName;
|
|
2394
|
-
var ContextMenuRadioItem =
|
|
2749
|
+
var ContextMenuRadioItem = React10.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsxs)(
|
|
2395
2750
|
ContextMenuPrimitive.RadioItem,
|
|
2396
2751
|
{
|
|
2397
2752
|
ref,
|
|
@@ -2402,13 +2757,13 @@ var ContextMenuRadioItem = React9.forwardRef(({ className, children, ...props },
|
|
|
2402
2757
|
),
|
|
2403
2758
|
...props,
|
|
2404
2759
|
children: [
|
|
2405
|
-
/* @__PURE__ */ (0,
|
|
2760
|
+
/* @__PURE__ */ (0, import_jsx_runtime35.jsx)("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(ContextMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(import_lucide_react13.Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
2406
2761
|
children
|
|
2407
2762
|
]
|
|
2408
2763
|
}
|
|
2409
2764
|
));
|
|
2410
2765
|
ContextMenuRadioItem.displayName = ContextMenuPrimitive.RadioItem.displayName;
|
|
2411
|
-
var ContextMenuLabel =
|
|
2766
|
+
var ContextMenuLabel = React10.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2412
2767
|
ContextMenuPrimitive.Label,
|
|
2413
2768
|
{
|
|
2414
2769
|
ref,
|
|
@@ -2421,7 +2776,7 @@ var ContextMenuLabel = React9.forwardRef(({ className, inset, ...props }, ref) =
|
|
|
2421
2776
|
}
|
|
2422
2777
|
));
|
|
2423
2778
|
ContextMenuLabel.displayName = ContextMenuPrimitive.Label.displayName;
|
|
2424
|
-
var ContextMenuSeparator =
|
|
2779
|
+
var ContextMenuSeparator = React10.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2425
2780
|
ContextMenuPrimitive.Separator,
|
|
2426
2781
|
{
|
|
2427
2782
|
ref,
|
|
@@ -2433,7 +2788,7 @@ ContextMenuSeparator.displayName = ContextMenuPrimitive.Separator.displayName;
|
|
|
2433
2788
|
var ContextMenuShortcut = ({
|
|
2434
2789
|
className,
|
|
2435
2790
|
...props
|
|
2436
|
-
}) => /* @__PURE__ */ (0,
|
|
2791
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime35.jsx)(
|
|
2437
2792
|
"span",
|
|
2438
2793
|
{
|
|
2439
2794
|
className: cn(
|
|
@@ -2446,13 +2801,13 @@ var ContextMenuShortcut = ({
|
|
|
2446
2801
|
ContextMenuShortcut.displayName = "ContextMenuShortcut";
|
|
2447
2802
|
|
|
2448
2803
|
// src/components/resizable.tsx
|
|
2449
|
-
var
|
|
2804
|
+
var import_lucide_react14 = require("lucide-react");
|
|
2450
2805
|
var ResizablePrimitive = __toESM(require("react-resizable-panels"), 1);
|
|
2451
|
-
var
|
|
2806
|
+
var import_jsx_runtime36 = require("react/jsx-runtime");
|
|
2452
2807
|
var ResizablePanelGroup = ({
|
|
2453
2808
|
className,
|
|
2454
2809
|
...props
|
|
2455
|
-
}) => /* @__PURE__ */ (0,
|
|
2810
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(
|
|
2456
2811
|
ResizablePrimitive.Group,
|
|
2457
2812
|
{
|
|
2458
2813
|
className: cn("h-full w-full", className),
|
|
@@ -2465,7 +2820,7 @@ var ResizableHandle = ({
|
|
|
2465
2820
|
className,
|
|
2466
2821
|
children,
|
|
2467
2822
|
...props
|
|
2468
|
-
}) => /* @__PURE__ */ (0,
|
|
2823
|
+
}) => /* @__PURE__ */ (0, import_jsx_runtime36.jsxs)(
|
|
2469
2824
|
ResizablePrimitive.Separator,
|
|
2470
2825
|
{
|
|
2471
2826
|
className: cn(
|
|
@@ -2485,7 +2840,7 @@ var ResizableHandle = ({
|
|
|
2485
2840
|
),
|
|
2486
2841
|
...props,
|
|
2487
2842
|
children: [
|
|
2488
|
-
withGrip ? /* @__PURE__ */ (0,
|
|
2843
|
+
withGrip ? /* @__PURE__ */ (0, import_jsx_runtime36.jsx)("div", { className: "z-10 flex h-4 w-3 items-center justify-center rounded-sm border border-border bg-border", children: /* @__PURE__ */ (0, import_jsx_runtime36.jsx)(import_lucide_react14.GripVertical, { className: "h-2.5 w-2.5 text-muted-foreground" }) }) : null,
|
|
2489
2844
|
children
|
|
2490
2845
|
]
|
|
2491
2846
|
}
|
|
@@ -2493,9 +2848,9 @@ var ResizableHandle = ({
|
|
|
2493
2848
|
|
|
2494
2849
|
// src/components/tree-view.tsx
|
|
2495
2850
|
var import_react_virtual = require("@tanstack/react-virtual");
|
|
2496
|
-
var
|
|
2497
|
-
var
|
|
2498
|
-
var
|
|
2851
|
+
var import_lucide_react15 = require("lucide-react");
|
|
2852
|
+
var React11 = __toESM(require("react"), 1);
|
|
2853
|
+
var import_jsx_runtime37 = require("react/jsx-runtime");
|
|
2499
2854
|
var ROW_HEIGHT = 28;
|
|
2500
2855
|
var INDENT_PER_LEVEL = 12;
|
|
2501
2856
|
function textOf(node) {
|
|
@@ -2536,13 +2891,13 @@ function flatten(nodes, expanded) {
|
|
|
2536
2891
|
return rows;
|
|
2537
2892
|
}
|
|
2538
2893
|
function useControllable(controlled, fallback) {
|
|
2539
|
-
const [uncontrolled, setUncontrolled] =
|
|
2894
|
+
const [uncontrolled, setUncontrolled] = React11.useState(fallback);
|
|
2540
2895
|
return [
|
|
2541
2896
|
controlled === void 0 ? uncontrolled : controlled,
|
|
2542
2897
|
setUncontrolled
|
|
2543
2898
|
];
|
|
2544
2899
|
}
|
|
2545
|
-
var TreeView =
|
|
2900
|
+
var TreeView = React11.forwardRef(
|
|
2546
2901
|
({
|
|
2547
2902
|
nodes,
|
|
2548
2903
|
expandedIds,
|
|
@@ -2557,8 +2912,8 @@ var TreeView = React10.forwardRef(
|
|
|
2557
2912
|
"aria-describedby": ariaDescribedBy,
|
|
2558
2913
|
...props
|
|
2559
2914
|
}, forwardedRef) => {
|
|
2560
|
-
const scrollRef =
|
|
2561
|
-
const attachScroll =
|
|
2915
|
+
const scrollRef = React11.useRef(null);
|
|
2916
|
+
const attachScroll = React11.useCallback(
|
|
2562
2917
|
(node) => {
|
|
2563
2918
|
scrollRef.current = node;
|
|
2564
2919
|
if (typeof forwardedRef === "function") forwardedRef(node);
|
|
@@ -2572,7 +2927,7 @@ var TreeView = React10.forwardRef(
|
|
|
2572
2927
|
expandedIds === void 0 ? void 0 : [...expandedIds],
|
|
2573
2928
|
[...defaultExpandedIds ?? []]
|
|
2574
2929
|
);
|
|
2575
|
-
const expanded =
|
|
2930
|
+
const expanded = React11.useMemo(
|
|
2576
2931
|
() => new Set(expandedIds ?? expandedState),
|
|
2577
2932
|
[expandedIds, expandedState]
|
|
2578
2933
|
);
|
|
@@ -2580,11 +2935,11 @@ var TreeView = React10.forwardRef(
|
|
|
2580
2935
|
selectedId === void 0 ? void 0 : selectedId,
|
|
2581
2936
|
defaultSelectedId ?? null
|
|
2582
2937
|
);
|
|
2583
|
-
const rows =
|
|
2938
|
+
const rows = React11.useMemo(
|
|
2584
2939
|
() => flatten(nodes, expanded),
|
|
2585
2940
|
[nodes, expanded]
|
|
2586
2941
|
);
|
|
2587
|
-
const [activeId, setActiveId] =
|
|
2942
|
+
const [activeId, setActiveId] = React11.useState(null);
|
|
2588
2943
|
const activeIndex = Math.max(
|
|
2589
2944
|
0,
|
|
2590
2945
|
rows.findIndex((row) => row.node.id === (activeId ?? selected))
|
|
@@ -2628,7 +2983,7 @@ var TreeView = React10.forwardRef(
|
|
|
2628
2983
|
}
|
|
2629
2984
|
return from;
|
|
2630
2985
|
};
|
|
2631
|
-
const typeahead =
|
|
2986
|
+
const typeahead = React11.useRef({ query: "", at: 0 });
|
|
2632
2987
|
const onKeyDown = (event) => {
|
|
2633
2988
|
const index = activeIndex;
|
|
2634
2989
|
const row = rows[index];
|
|
@@ -2725,13 +3080,13 @@ var TreeView = React10.forwardRef(
|
|
|
2725
3080
|
const virtualItems = virtualizer.getVirtualItems();
|
|
2726
3081
|
const usable = (index) => rows[index]?.node.disabled !== true;
|
|
2727
3082
|
const tabStopIndex = virtualItems.some((item) => item.index === activeIndex) && usable(activeIndex) ? activeIndex : virtualItems.find((item) => usable(item.index))?.index ?? -1;
|
|
2728
|
-
return /* @__PURE__ */ (0,
|
|
3083
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2729
3084
|
"div",
|
|
2730
3085
|
{
|
|
2731
3086
|
ref: attachScroll,
|
|
2732
3087
|
className: cn("overflow-auto", className),
|
|
2733
3088
|
...props,
|
|
2734
|
-
children: /* @__PURE__ */ (0,
|
|
3089
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2735
3090
|
"div",
|
|
2736
3091
|
{
|
|
2737
3092
|
role: "tree",
|
|
@@ -2744,7 +3099,7 @@ var TreeView = React10.forwardRef(
|
|
|
2744
3099
|
const row = rows[item.index];
|
|
2745
3100
|
if (row === void 0) return null;
|
|
2746
3101
|
const isSelected = selected === row.node.id;
|
|
2747
|
-
return /* @__PURE__ */ (0,
|
|
3102
|
+
return /* @__PURE__ */ (0, import_jsx_runtime37.jsxs)(
|
|
2748
3103
|
"div",
|
|
2749
3104
|
{
|
|
2750
3105
|
"data-tree-index": item.index,
|
|
@@ -2774,7 +3129,7 @@ var TreeView = React10.forwardRef(
|
|
|
2774
3129
|
paddingLeft: 4 + row.level * INDENT_PER_LEVEL
|
|
2775
3130
|
},
|
|
2776
3131
|
children: [
|
|
2777
|
-
/* @__PURE__ */ (0,
|
|
3132
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
2778
3133
|
"span",
|
|
2779
3134
|
{
|
|
2780
3135
|
"aria-hidden": "true",
|
|
@@ -2784,8 +3139,8 @@ var TreeView = React10.forwardRef(
|
|
|
2784
3139
|
event.stopPropagation();
|
|
2785
3140
|
setExpansion(row.node.id, !expanded.has(row.node.id));
|
|
2786
3141
|
},
|
|
2787
|
-
children: row.hasChildren ? /* @__PURE__ */ (0,
|
|
2788
|
-
|
|
3142
|
+
children: row.hasChildren ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)(
|
|
3143
|
+
import_lucide_react15.ChevronRight,
|
|
2789
3144
|
{
|
|
2790
3145
|
className: cn(
|
|
2791
3146
|
"size-3.5 text-muted-foreground transition-transform",
|
|
@@ -2795,8 +3150,8 @@ var TreeView = React10.forwardRef(
|
|
|
2795
3150
|
) : null
|
|
2796
3151
|
}
|
|
2797
3152
|
),
|
|
2798
|
-
row.node.icon !== void 0 ? /* @__PURE__ */ (0,
|
|
2799
|
-
/* @__PURE__ */ (0,
|
|
3153
|
+
row.node.icon !== void 0 ? /* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "flex size-4 shrink-0 items-center justify-center text-muted-foreground", children: row.node.icon }) : null,
|
|
3154
|
+
/* @__PURE__ */ (0, import_jsx_runtime37.jsx)("span", { className: "truncate", children: row.node.label })
|
|
2800
3155
|
]
|
|
2801
3156
|
},
|
|
2802
3157
|
row.node.id
|
|
@@ -2809,6 +3164,837 @@ var TreeView = React10.forwardRef(
|
|
|
2809
3164
|
}
|
|
2810
3165
|
);
|
|
2811
3166
|
TreeView.displayName = "TreeView";
|
|
3167
|
+
|
|
3168
|
+
// src/components/slider.tsx
|
|
3169
|
+
var SliderPrimitive = __toESM(require("@radix-ui/react-slider"), 1);
|
|
3170
|
+
var React12 = __toESM(require("react"), 1);
|
|
3171
|
+
|
|
3172
|
+
// src/lib/dev-warn.ts
|
|
3173
|
+
var emitted = /* @__PURE__ */ new Set();
|
|
3174
|
+
var SPEAKING_ENVIRONMENTS = /* @__PURE__ */ new Set(["development", "test"]);
|
|
3175
|
+
function isDevelopmentRuntime() {
|
|
3176
|
+
if (typeof process === "undefined") return false;
|
|
3177
|
+
const env = process?.env?.NODE_ENV;
|
|
3178
|
+
return env !== void 0 && SPEAKING_ENVIRONMENTS.has(env);
|
|
3179
|
+
}
|
|
3180
|
+
function devWarnOnce(condition, message) {
|
|
3181
|
+
if (condition) return;
|
|
3182
|
+
if (!isDevelopmentRuntime()) return;
|
|
3183
|
+
if (emitted.has(message)) return;
|
|
3184
|
+
emitted.add(message);
|
|
3185
|
+
console.warn(`[@nextlyhq/ui] ${message}`);
|
|
3186
|
+
}
|
|
3187
|
+
|
|
3188
|
+
// src/components/slider.tsx
|
|
3189
|
+
var import_jsx_runtime38 = (
|
|
3190
|
+
// `aria-label`/`aria-labelledby` are destructured out above rather than
|
|
3191
|
+
// spread here: left on the root they would be a second, roleless copy
|
|
3192
|
+
// of a name only the thumb is read for.
|
|
3193
|
+
require("react/jsx-runtime")
|
|
3194
|
+
);
|
|
3195
|
+
function thumbCount(value, defaultValue) {
|
|
3196
|
+
return Math.max(1, value?.length ?? defaultValue?.length ?? 1);
|
|
3197
|
+
}
|
|
3198
|
+
function hasAccessibleName(value) {
|
|
3199
|
+
return value !== void 0 && value.trim() !== "";
|
|
3200
|
+
}
|
|
3201
|
+
var Slider = React12.forwardRef(
|
|
3202
|
+
({
|
|
3203
|
+
className,
|
|
3204
|
+
value,
|
|
3205
|
+
defaultValue,
|
|
3206
|
+
thumbs,
|
|
3207
|
+
orientation = "horizontal",
|
|
3208
|
+
"aria-label": ariaLabel,
|
|
3209
|
+
"aria-labelledby": ariaLabelledBy,
|
|
3210
|
+
...props
|
|
3211
|
+
}, ref) => {
|
|
3212
|
+
const initialUncontrolledCount = React12.useRef(
|
|
3213
|
+
thumbCount(void 0, defaultValue)
|
|
3214
|
+
).current;
|
|
3215
|
+
const count = value?.length ?? initialUncontrolledCount;
|
|
3216
|
+
const isEmptyDefault = defaultValue !== void 0 && defaultValue.length === 0;
|
|
3217
|
+
const isEmptyControlled = value !== void 0 && value.length === 0;
|
|
3218
|
+
devWarnOnce(
|
|
3219
|
+
!isEmptyDefault && !isEmptyControlled,
|
|
3220
|
+
"Slider: `value`/`defaultValue` must hold one number per thumb, and an empty array holds none \u2014 the control has nothing to slide. An empty `defaultValue` falls back to `min`; an empty `value` renders nothing at all, because a controlled slider cannot be given a value without taking state the caller owns. Render nothing until the value is loaded rather than passing `[]`."
|
|
3221
|
+
);
|
|
3222
|
+
if (isEmptyControlled) return null;
|
|
3223
|
+
const isNamed = (index) => {
|
|
3224
|
+
const own = thumbs?.[index];
|
|
3225
|
+
if (hasAccessibleName(own?.["aria-label"])) return true;
|
|
3226
|
+
if (hasAccessibleName(own?.["aria-labelledby"])) return true;
|
|
3227
|
+
return count === 1 && (hasAccessibleName(ariaLabel) || hasAccessibleName(ariaLabelledBy));
|
|
3228
|
+
};
|
|
3229
|
+
devWarnOnce(
|
|
3230
|
+
Array.from({ length: count }).every((_, i) => isNamed(i)),
|
|
3231
|
+
"Slider: every thumb needs an accessible name. A single thumb may take it from the root's `aria-label`/`aria-labelledby`; a range needs one `thumbs` entry per thumb, because the root's name is not inherited and two thumbs sharing one name are announced identically."
|
|
3232
|
+
);
|
|
3233
|
+
const ariaFor = (index) => {
|
|
3234
|
+
const supplied = thumbs?.[index] ?? {};
|
|
3235
|
+
const ownLabel = hasAccessibleName(supplied["aria-label"]) ? supplied["aria-label"] : void 0;
|
|
3236
|
+
const ownLabelledBy = hasAccessibleName(supplied["aria-labelledby"]) ? supplied["aria-labelledby"] : void 0;
|
|
3237
|
+
if (count !== 1) {
|
|
3238
|
+
return {
|
|
3239
|
+
...supplied,
|
|
3240
|
+
"aria-label": ownLabel,
|
|
3241
|
+
"aria-labelledby": ownLabelledBy
|
|
3242
|
+
};
|
|
3243
|
+
}
|
|
3244
|
+
const namesItself = ownLabel !== void 0 || ownLabelledBy !== void 0;
|
|
3245
|
+
return {
|
|
3246
|
+
"aria-label": namesItself ? ownLabel : ariaLabel,
|
|
3247
|
+
"aria-labelledby": namesItself ? ownLabelledBy : ariaLabelledBy,
|
|
3248
|
+
"aria-valuetext": supplied["aria-valuetext"],
|
|
3249
|
+
"aria-describedby": supplied["aria-describedby"]
|
|
3250
|
+
};
|
|
3251
|
+
};
|
|
3252
|
+
const isVertical = orientation === "vertical";
|
|
3253
|
+
return /* @__PURE__ */ (0, import_jsx_runtime38.jsxs)(
|
|
3254
|
+
SliderPrimitive.Root,
|
|
3255
|
+
{
|
|
3256
|
+
ref,
|
|
3257
|
+
className: cn(
|
|
3258
|
+
"relative flex touch-none select-none items-center",
|
|
3259
|
+
// WCAG 2.5.8 wants a 24px target. Padding alone does not reach it: the
|
|
3260
|
+
// thumb is absolutely positioned, so the cross-axis size is the 6px
|
|
3261
|
+
// track plus the padding — 22px with `py-2`. An explicit minimum
|
|
3262
|
+
// states the target rather than leaving it to arithmetic that moves
|
|
3263
|
+
// whenever the track thickness does.
|
|
3264
|
+
isVertical ? (
|
|
3265
|
+
// A vertical slider needs a LENGTH, and it cannot inherit one:
|
|
3266
|
+
// `h-full` inside an auto-height parent resolves to zero, leaving
|
|
3267
|
+
// a control with no track to drag along. A concrete default is
|
|
3268
|
+
// usable everywhere and, being a plain utility, is replaced by a
|
|
3269
|
+
// caller's own `h-*` — including `h-full`, for the fill-the-parent
|
|
3270
|
+
// case this default gives up.
|
|
3271
|
+
"h-44 min-w-6 flex-col px-2"
|
|
3272
|
+
) : "min-h-6 w-full py-2",
|
|
3273
|
+
"data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
3274
|
+
className
|
|
3275
|
+
),
|
|
3276
|
+
orientation,
|
|
3277
|
+
value,
|
|
3278
|
+
defaultValue: isEmptyDefault ? void 0 : defaultValue,
|
|
3279
|
+
...props,
|
|
3280
|
+
children: [
|
|
3281
|
+
/* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3282
|
+
SliderPrimitive.Track,
|
|
3283
|
+
{
|
|
3284
|
+
className: cn(
|
|
3285
|
+
"bg-secondary relative grow overflow-hidden rounded-full",
|
|
3286
|
+
isVertical ? "h-full w-1.5" : "h-1.5 w-full"
|
|
3287
|
+
),
|
|
3288
|
+
children: /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3289
|
+
SliderPrimitive.Range,
|
|
3290
|
+
{
|
|
3291
|
+
className: cn(
|
|
3292
|
+
"bg-primary absolute",
|
|
3293
|
+
isVertical ? "w-full" : "h-full"
|
|
3294
|
+
)
|
|
3295
|
+
}
|
|
3296
|
+
)
|
|
3297
|
+
}
|
|
3298
|
+
),
|
|
3299
|
+
Array.from({ length: count }, (_, i) => /* @__PURE__ */ (0, import_jsx_runtime38.jsx)(
|
|
3300
|
+
SliderPrimitive.Thumb,
|
|
3301
|
+
{
|
|
3302
|
+
...ariaFor(i),
|
|
3303
|
+
className: cn(
|
|
3304
|
+
"border-primary bg-background block h-4 w-4 rounded-full border-2",
|
|
3305
|
+
"ring-offset-background transition-colors",
|
|
3306
|
+
"focus-visible:ring-ring focus-visible:outline-none focus-visible:ring-2",
|
|
3307
|
+
"focus-visible:ring-offset-2",
|
|
3308
|
+
"disabled:pointer-events-none disabled:opacity-50"
|
|
3309
|
+
)
|
|
3310
|
+
},
|
|
3311
|
+
i
|
|
3312
|
+
))
|
|
3313
|
+
]
|
|
3314
|
+
}
|
|
3315
|
+
);
|
|
3316
|
+
}
|
|
3317
|
+
);
|
|
3318
|
+
Slider.displayName = SliderPrimitive.Root.displayName;
|
|
3319
|
+
|
|
3320
|
+
// src/lib/shortcuts/react.tsx
|
|
3321
|
+
var React13 = __toESM(require("react"), 1);
|
|
3322
|
+
|
|
3323
|
+
// src/lib/shortcuts/key-spec.ts
|
|
3324
|
+
function normalizeKey(key) {
|
|
3325
|
+
return [...key].length === 1 ? key.toLowerCase() : key;
|
|
3326
|
+
}
|
|
3327
|
+
function shiftIsMeaningful(key) {
|
|
3328
|
+
if (key.length > 1) return true;
|
|
3329
|
+
if (key === " ") return true;
|
|
3330
|
+
return /[\p{L}\p{N}]/u.test(key);
|
|
3331
|
+
}
|
|
3332
|
+
function parseKeys(spec) {
|
|
3333
|
+
const steps = spec.trim().split(/\s+/).filter(Boolean);
|
|
3334
|
+
if (steps.length === 0) {
|
|
3335
|
+
throw new Error(`Shortcut spec is empty: ${JSON.stringify(spec)}`);
|
|
3336
|
+
}
|
|
3337
|
+
return steps.map((step) => parseChord(step, spec));
|
|
3338
|
+
}
|
|
3339
|
+
function parseChord(step, spec) {
|
|
3340
|
+
const trailingPlusIsKey = step.length > 2 && step.endsWith("++");
|
|
3341
|
+
const body = trailingPlusIsKey ? step.slice(0, -1) : step;
|
|
3342
|
+
const parts = step === "+" ? ["+"] : body.split("+").filter(Boolean);
|
|
3343
|
+
let mod = false;
|
|
3344
|
+
let ctrl = false;
|
|
3345
|
+
let meta = false;
|
|
3346
|
+
let alt = false;
|
|
3347
|
+
let shift = false;
|
|
3348
|
+
let key;
|
|
3349
|
+
for (const raw of parts) {
|
|
3350
|
+
switch (raw.toLowerCase()) {
|
|
3351
|
+
case "mod":
|
|
3352
|
+
mod = true;
|
|
3353
|
+
break;
|
|
3354
|
+
case "ctrl":
|
|
3355
|
+
case "control":
|
|
3356
|
+
ctrl = true;
|
|
3357
|
+
break;
|
|
3358
|
+
case "meta":
|
|
3359
|
+
case "cmd":
|
|
3360
|
+
case "command":
|
|
3361
|
+
meta = true;
|
|
3362
|
+
break;
|
|
3363
|
+
case "alt":
|
|
3364
|
+
case "option":
|
|
3365
|
+
alt = true;
|
|
3366
|
+
break;
|
|
3367
|
+
case "shift":
|
|
3368
|
+
shift = true;
|
|
3369
|
+
break;
|
|
3370
|
+
case "space":
|
|
3371
|
+
key = " ";
|
|
3372
|
+
break;
|
|
3373
|
+
default:
|
|
3374
|
+
if (key !== void 0) {
|
|
3375
|
+
throw new Error(
|
|
3376
|
+
`Shortcut step "${step}" names two keys ("${key}" and "${raw}") in ${JSON.stringify(spec)}`
|
|
3377
|
+
);
|
|
3378
|
+
}
|
|
3379
|
+
key = raw;
|
|
3380
|
+
}
|
|
3381
|
+
}
|
|
3382
|
+
if (trailingPlusIsKey) {
|
|
3383
|
+
if (key !== void 0) {
|
|
3384
|
+
throw new Error(
|
|
3385
|
+
`Shortcut step has more than one key: ${JSON.stringify(step)} in ${JSON.stringify(spec)}`
|
|
3386
|
+
);
|
|
3387
|
+
}
|
|
3388
|
+
key = "+";
|
|
3389
|
+
}
|
|
3390
|
+
if (key === void 0) {
|
|
3391
|
+
throw new Error(
|
|
3392
|
+
`Shortcut step "${step}" names modifiers but no key, in ${JSON.stringify(spec)}`
|
|
3393
|
+
);
|
|
3394
|
+
}
|
|
3395
|
+
return { key: normalizeKey(key), mod, ctrl, meta, alt, shift };
|
|
3396
|
+
}
|
|
3397
|
+
function chordMatches(chord, key, state, isApple) {
|
|
3398
|
+
if (normalizeKey(key) !== chord.key) return false;
|
|
3399
|
+
const wantsCtrl = chord.ctrl || chord.mod && !isApple;
|
|
3400
|
+
const wantsMeta = chord.meta || chord.mod && isApple;
|
|
3401
|
+
if (state.metaKey !== wantsMeta) return false;
|
|
3402
|
+
const altGraph = state.getModifierState?.("AltGraph") ?? false;
|
|
3403
|
+
const synthetic = altGraph && [...chord.key].length === 1 && !wantsCtrl && !chord.alt;
|
|
3404
|
+
if (!synthetic) {
|
|
3405
|
+
if (state.ctrlKey !== wantsCtrl) return false;
|
|
3406
|
+
if (state.altKey !== chord.alt) return false;
|
|
3407
|
+
}
|
|
3408
|
+
if (shiftIsMeaningful(chord.key) && state.shiftKey !== chord.shift)
|
|
3409
|
+
return false;
|
|
3410
|
+
return true;
|
|
3411
|
+
}
|
|
3412
|
+
function detectApplePlatform() {
|
|
3413
|
+
if (typeof navigator === "undefined") return false;
|
|
3414
|
+
const candidate = navigator;
|
|
3415
|
+
const platform = candidate.userAgentData?.platform ?? navigator.platform ?? "";
|
|
3416
|
+
return /mac|iphone|ipad|ipod/i.test(platform);
|
|
3417
|
+
}
|
|
3418
|
+
|
|
3419
|
+
// src/lib/shortcuts/manager.ts
|
|
3420
|
+
var DEFAULT_SEQUENCE_TIMEOUT_MS = 1e3;
|
|
3421
|
+
function signature(event) {
|
|
3422
|
+
return event.code || event.key;
|
|
3423
|
+
}
|
|
3424
|
+
function eventTarget(event) {
|
|
3425
|
+
const path = event.composedPath?.();
|
|
3426
|
+
return path && path.length > 0 ? path[0] ?? null : event.target;
|
|
3427
|
+
}
|
|
3428
|
+
function asElement(target) {
|
|
3429
|
+
if (target === null || typeof target !== "object") return null;
|
|
3430
|
+
const node = target;
|
|
3431
|
+
if (node.nodeType !== 1 || typeof node.tagName !== "string") return null;
|
|
3432
|
+
return target;
|
|
3433
|
+
}
|
|
3434
|
+
function inputType(element) {
|
|
3435
|
+
if (element.tagName !== "INPUT") return "";
|
|
3436
|
+
const value = element.type;
|
|
3437
|
+
return typeof value === "string" ? value.toLowerCase() : "";
|
|
3438
|
+
}
|
|
3439
|
+
function controlOwnsKey(target, event) {
|
|
3440
|
+
if (event.ctrlKey || event.metaKey || event.altKey) return false;
|
|
3441
|
+
const element = asElement(target);
|
|
3442
|
+
if (!element) return false;
|
|
3443
|
+
const tag = element.tagName;
|
|
3444
|
+
const type = inputType(element);
|
|
3445
|
+
if (tag === "BUTTON" || type === "button" || type === "submit" || type === "reset" || type === "image") {
|
|
3446
|
+
return event.key === " " || event.key === "Enter";
|
|
3447
|
+
}
|
|
3448
|
+
if (tag === "A" && element.getAttribute("href") !== null) {
|
|
3449
|
+
return event.key === "Enter";
|
|
3450
|
+
}
|
|
3451
|
+
if (tag === "SUMMARY") return event.key === " " || event.key === "Enter";
|
|
3452
|
+
if (type === "checkbox") return event.key === " ";
|
|
3453
|
+
if (type === "color") return event.key === " " || event.key === "Enter";
|
|
3454
|
+
if (type === "file") return event.key === " " || event.key === "Enter";
|
|
3455
|
+
if (type === "range") {
|
|
3456
|
+
return event.key.startsWith("Arrow") || RANGE_KEYS.has(event.key);
|
|
3457
|
+
}
|
|
3458
|
+
if (type === "radio") {
|
|
3459
|
+
return event.key === " " || event.key.startsWith("Arrow");
|
|
3460
|
+
}
|
|
3461
|
+
return false;
|
|
3462
|
+
}
|
|
3463
|
+
function isTypingTarget(target) {
|
|
3464
|
+
const element = asElement(target);
|
|
3465
|
+
if (!element) return false;
|
|
3466
|
+
if (element.isContentEditable) return true;
|
|
3467
|
+
const tag = element.tagName;
|
|
3468
|
+
if (tag === "TEXTAREA") return true;
|
|
3469
|
+
if (tag === "SELECT") return true;
|
|
3470
|
+
if (tag === "INPUT") {
|
|
3471
|
+
return !NON_TEXT_INPUT_TYPES.has(inputType(element));
|
|
3472
|
+
}
|
|
3473
|
+
const role = element.getAttribute("role");
|
|
3474
|
+
return role !== null && TYPE_AHEAD_ROLES.has(role);
|
|
3475
|
+
}
|
|
3476
|
+
var NON_TEXT_INPUT_TYPES = /* @__PURE__ */ new Set([
|
|
3477
|
+
"button",
|
|
3478
|
+
"checkbox",
|
|
3479
|
+
"color",
|
|
3480
|
+
"file",
|
|
3481
|
+
"hidden",
|
|
3482
|
+
"image",
|
|
3483
|
+
"radio",
|
|
3484
|
+
"range",
|
|
3485
|
+
"reset",
|
|
3486
|
+
"submit"
|
|
3487
|
+
]);
|
|
3488
|
+
function firesWhileTyping(prepared) {
|
|
3489
|
+
const explicit = prepared.binding.whenTyping;
|
|
3490
|
+
if (explicit !== void 0) return explicit;
|
|
3491
|
+
const first = prepared.keys[0];
|
|
3492
|
+
if (first === void 0) return false;
|
|
3493
|
+
return first.mod || first.ctrl || first.meta || first.alt || first.key === "Escape";
|
|
3494
|
+
}
|
|
3495
|
+
function createShortcutManager(options = {}) {
|
|
3496
|
+
const isApple = options.isApple ?? detectApplePlatform();
|
|
3497
|
+
const sequenceTimeoutMs = options.sequenceTimeoutMs ?? DEFAULT_SEQUENCE_TIMEOUT_MS;
|
|
3498
|
+
const now = options.now ?? (() => Date.now());
|
|
3499
|
+
const layers = /* @__PURE__ */ new Set();
|
|
3500
|
+
let nextSequence = 0;
|
|
3501
|
+
let pendingAt = null;
|
|
3502
|
+
let pendingLayer = null;
|
|
3503
|
+
const consumedPresses = /* @__PURE__ */ new Map();
|
|
3504
|
+
let pendingKey = null;
|
|
3505
|
+
function layerShape(bindings, options2) {
|
|
3506
|
+
const keys = bindings.map((b) => b.binding.keys).join("\0");
|
|
3507
|
+
return `${keys}${options2.depth}${options2.blocking === true}${options2.enabled !== false}`;
|
|
3508
|
+
}
|
|
3509
|
+
function blocking() {
|
|
3510
|
+
return ordered().some((layer) => layer.options.blocking === true);
|
|
3511
|
+
}
|
|
3512
|
+
function abandonSequence() {
|
|
3513
|
+
pendingAt = null;
|
|
3514
|
+
pressedEvents.length = 0;
|
|
3515
|
+
pendingLayer = null;
|
|
3516
|
+
pendingKey = null;
|
|
3517
|
+
}
|
|
3518
|
+
function prepare(bindings) {
|
|
3519
|
+
return bindings.map((binding) => ({
|
|
3520
|
+
binding,
|
|
3521
|
+
keys: parseKeys(binding.keys)
|
|
3522
|
+
}));
|
|
3523
|
+
}
|
|
3524
|
+
function ordered() {
|
|
3525
|
+
return [...layers].filter((layer) => layer.options.enabled !== false).sort(
|
|
3526
|
+
(a, b) => b.options.depth - a.options.depth || b.sequence - a.sequence
|
|
3527
|
+
);
|
|
3528
|
+
}
|
|
3529
|
+
function matchDepth(prepared, pressed) {
|
|
3530
|
+
if (pressed.length > prepared.keys.length) return "none";
|
|
3531
|
+
for (let i = 0; i < pressed.length; i++) {
|
|
3532
|
+
const chord = prepared.keys[i];
|
|
3533
|
+
const event = pressed[i];
|
|
3534
|
+
if (chord === void 0 || event === void 0) return "none";
|
|
3535
|
+
if (!chordMatches(chord, event.key, event, isApple)) return "none";
|
|
3536
|
+
}
|
|
3537
|
+
return pressed.length === prepared.keys.length ? "exact" : "prefix";
|
|
3538
|
+
}
|
|
3539
|
+
function fire(prepared, event, invoke) {
|
|
3540
|
+
if (prepared.binding.preventDefault !== false) event.preventDefault();
|
|
3541
|
+
if (invoke) prepared.binding.run(event);
|
|
3542
|
+
}
|
|
3543
|
+
function insertsText(event, typing) {
|
|
3544
|
+
if (event.key === "Tab")
|
|
3545
|
+
return !event.ctrlKey && !event.metaKey && !event.altKey;
|
|
3546
|
+
if (!typing) return false;
|
|
3547
|
+
const altGraph = event.getModifierState?.("AltGraph") ?? false;
|
|
3548
|
+
if (!altGraph && (event.ctrlKey || event.metaKey)) {
|
|
3549
|
+
const letter = event.key.length === 1 ? event.key.toLowerCase() : event.key;
|
|
3550
|
+
if (letter === "z" && event.shiftKey) return !event.altKey;
|
|
3551
|
+
if (letter === REDO_LETTER)
|
|
3552
|
+
return !isApple && !event.shiftKey && !event.altKey;
|
|
3553
|
+
if (EDITING_NAVIGATION.has(event.key)) return !event.altKey || isApple;
|
|
3554
|
+
if (event.shiftKey || event.altKey) return false;
|
|
3555
|
+
return EDITING_LETTERS.has(letter);
|
|
3556
|
+
}
|
|
3557
|
+
if (!altGraph && event.altKey) {
|
|
3558
|
+
if ((event.key === "ArrowDown" || event.key === "ArrowUp") && asElement(eventTarget(event))?.tagName === "SELECT") {
|
|
3559
|
+
return true;
|
|
3560
|
+
}
|
|
3561
|
+
if (!isApple) return false;
|
|
3562
|
+
if (EDITING_NAVIGATION.has(event.key)) return true;
|
|
3563
|
+
}
|
|
3564
|
+
if (event.key === "Dead" || event.key === "Process") return true;
|
|
3565
|
+
if ([...event.key].length === 1) return true;
|
|
3566
|
+
if (AMBIGUOUS_KEYS.has(event.key)) return targetOwnsAmbiguousKey(event);
|
|
3567
|
+
return FIELD_KEYS.has(event.key);
|
|
3568
|
+
}
|
|
3569
|
+
function offer(pressed, event, typing, invoke) {
|
|
3570
|
+
for (const layer of ordered()) {
|
|
3571
|
+
const mayMatch = pressed.length <= 1 || pendingLayer === null || layer === pendingLayer;
|
|
3572
|
+
if (mayMatch) {
|
|
3573
|
+
let prefixed = false;
|
|
3574
|
+
for (const prepared of layer.bindings) {
|
|
3575
|
+
if (typing && !firesWhileTyping(prepared)) continue;
|
|
3576
|
+
if (prepared.binding.when && !prepared.binding.when()) continue;
|
|
3577
|
+
const depth = matchDepth(prepared, pressed);
|
|
3578
|
+
if (depth === "exact") {
|
|
3579
|
+
fire(prepared, event, invoke);
|
|
3580
|
+
return "fired";
|
|
3581
|
+
}
|
|
3582
|
+
if (depth === "prefix") prefixed = true;
|
|
3583
|
+
}
|
|
3584
|
+
if (prefixed) {
|
|
3585
|
+
pendingLayer = layer;
|
|
3586
|
+
event.preventDefault();
|
|
3587
|
+
return "pending";
|
|
3588
|
+
}
|
|
3589
|
+
}
|
|
3590
|
+
if (layer.options.blocking) return "blocked";
|
|
3591
|
+
}
|
|
3592
|
+
return "none";
|
|
3593
|
+
}
|
|
3594
|
+
function warnOnPrefixConflicts(prepared, layerName) {
|
|
3595
|
+
const resolved = (chord) => {
|
|
3596
|
+
const ctrl = chord.ctrl || chord.mod && !isApple;
|
|
3597
|
+
const meta = chord.meta || chord.mod && isApple;
|
|
3598
|
+
const shift = shiftIsMeaningful(chord.key) ? chord.shift : false;
|
|
3599
|
+
return `${chord.key}\0${ctrl}${meta}${chord.alt}${shift}`;
|
|
3600
|
+
};
|
|
3601
|
+
const sameChord = (a, b) => resolved(a) === resolved(b);
|
|
3602
|
+
for (const short of prepared) {
|
|
3603
|
+
for (const long of prepared) {
|
|
3604
|
+
if (short === long || short.keys.length >= long.keys.length) continue;
|
|
3605
|
+
if (short.binding.when !== void 0) continue;
|
|
3606
|
+
if (!firesWhileTyping(short) && firesWhileTyping(long)) continue;
|
|
3607
|
+
if (short.keys.every((chord, i) => sameChord(chord, long.keys[i]))) {
|
|
3608
|
+
devWarnOnce(
|
|
3609
|
+
false,
|
|
3610
|
+
`shortcuts: in layer "${layerName}", "${short.binding.keys}" is a prefix of "${long.binding.keys}", so the longer one can never fire. Bind one or the other.`
|
|
3611
|
+
);
|
|
3612
|
+
}
|
|
3613
|
+
}
|
|
3614
|
+
}
|
|
3615
|
+
}
|
|
3616
|
+
const watchers = /* @__PURE__ */ new Set();
|
|
3617
|
+
let snapshot = null;
|
|
3618
|
+
function computeSnapshot() {
|
|
3619
|
+
return ordered().flatMap(
|
|
3620
|
+
(layer) => layer.bindings.map((prepared) => ({
|
|
3621
|
+
keys: prepared.binding.keys,
|
|
3622
|
+
description: prepared.binding.description,
|
|
3623
|
+
layer: layer.options.name
|
|
3624
|
+
}))
|
|
3625
|
+
);
|
|
3626
|
+
}
|
|
3627
|
+
function sameShortcuts(a, b) {
|
|
3628
|
+
return a.length === b.length && a.every(
|
|
3629
|
+
(entry, index) => entry.keys === b[index]?.keys && entry.description === b[index]?.description && entry.layer === b[index]?.layer
|
|
3630
|
+
);
|
|
3631
|
+
}
|
|
3632
|
+
function changed() {
|
|
3633
|
+
const previous = snapshot;
|
|
3634
|
+
snapshot = null;
|
|
3635
|
+
if (watchers.size === 0) return;
|
|
3636
|
+
const next = computeSnapshot();
|
|
3637
|
+
if (previous && sameShortcuts(previous, next)) {
|
|
3638
|
+
snapshot = previous;
|
|
3639
|
+
return;
|
|
3640
|
+
}
|
|
3641
|
+
snapshot = next;
|
|
3642
|
+
for (const watcher of watchers) watcher();
|
|
3643
|
+
}
|
|
3644
|
+
const pressedEvents = [];
|
|
3645
|
+
function runOffer(pressed, event, typing) {
|
|
3646
|
+
try {
|
|
3647
|
+
return offer(pressed, event, typing, true);
|
|
3648
|
+
} catch (error) {
|
|
3649
|
+
abandonSequence();
|
|
3650
|
+
consumedPresses.delete(signature(event));
|
|
3651
|
+
throw error;
|
|
3652
|
+
}
|
|
3653
|
+
}
|
|
3654
|
+
function handle(event) {
|
|
3655
|
+
if (typeof event.key !== "string") return false;
|
|
3656
|
+
if (event.defaultPrevented) {
|
|
3657
|
+
abandonSequence();
|
|
3658
|
+
return true;
|
|
3659
|
+
}
|
|
3660
|
+
if (event.isComposing) {
|
|
3661
|
+
abandonSequence();
|
|
3662
|
+
return true;
|
|
3663
|
+
}
|
|
3664
|
+
if (MODIFIER_KEYS.has(event.key)) return blocking();
|
|
3665
|
+
if (controlOwnsKey(eventTarget(event), event)) {
|
|
3666
|
+
abandonSequence();
|
|
3667
|
+
return true;
|
|
3668
|
+
}
|
|
3669
|
+
const typing = isTypingTarget(eventTarget(event));
|
|
3670
|
+
if (event.repeat) {
|
|
3671
|
+
if (pendingKey !== signature(event)) {
|
|
3672
|
+
abandonSequence();
|
|
3673
|
+
}
|
|
3674
|
+
const held = consumedPresses.get(signature(event));
|
|
3675
|
+
if (held) {
|
|
3676
|
+
if (held.prevented) {
|
|
3677
|
+
event.preventDefault();
|
|
3678
|
+
} else {
|
|
3679
|
+
const still = offer([event], event, typing, false);
|
|
3680
|
+
if (still !== "fired" && !insertsText(event, typing)) {
|
|
3681
|
+
event.preventDefault();
|
|
3682
|
+
}
|
|
3683
|
+
}
|
|
3684
|
+
return true;
|
|
3685
|
+
}
|
|
3686
|
+
const repeated = offer([event], event, typing, false);
|
|
3687
|
+
if (repeated === "blocked" && !insertsText(event, typing)) {
|
|
3688
|
+
event.preventDefault();
|
|
3689
|
+
}
|
|
3690
|
+
return repeated !== "none";
|
|
3691
|
+
}
|
|
3692
|
+
if (pendingAt !== null && now() - pendingAt > sequenceTimeoutMs) {
|
|
3693
|
+
abandonSequence();
|
|
3694
|
+
}
|
|
3695
|
+
pressedEvents.push(event);
|
|
3696
|
+
let outcome = runOffer(pressedEvents, event, typing);
|
|
3697
|
+
if (pressedEvents.length > 1 && (outcome === "none" || outcome === "blocked")) {
|
|
3698
|
+
abandonSequence();
|
|
3699
|
+
pressedEvents.push(event);
|
|
3700
|
+
outcome = runOffer(pressedEvents, event, typing);
|
|
3701
|
+
}
|
|
3702
|
+
if (outcome === "pending") {
|
|
3703
|
+
pendingAt = now();
|
|
3704
|
+
pendingKey = signature(event);
|
|
3705
|
+
consumedPresses.set(signature(event), {
|
|
3706
|
+
prevented: event.defaultPrevented
|
|
3707
|
+
});
|
|
3708
|
+
return true;
|
|
3709
|
+
}
|
|
3710
|
+
abandonSequence();
|
|
3711
|
+
if (outcome === "blocked") {
|
|
3712
|
+
if (!insertsText(event, typing)) event.preventDefault();
|
|
3713
|
+
}
|
|
3714
|
+
const consumed = outcome === "fired" || outcome === "blocked";
|
|
3715
|
+
if (consumed) {
|
|
3716
|
+
consumedPresses.set(signature(event), {
|
|
3717
|
+
prevented: event.defaultPrevented
|
|
3718
|
+
});
|
|
3719
|
+
} else {
|
|
3720
|
+
consumedPresses.delete(signature(event));
|
|
3721
|
+
}
|
|
3722
|
+
return consumed;
|
|
3723
|
+
}
|
|
3724
|
+
return {
|
|
3725
|
+
register(bindings, layerOptions) {
|
|
3726
|
+
const prepared = prepare(bindings);
|
|
3727
|
+
const layer = {
|
|
3728
|
+
options: layerOptions,
|
|
3729
|
+
bindings: prepared,
|
|
3730
|
+
sequence: nextSequence++,
|
|
3731
|
+
shape: layerShape(prepared, layerOptions)
|
|
3732
|
+
};
|
|
3733
|
+
warnOnPrefixConflicts(layer.bindings, layerOptions.name);
|
|
3734
|
+
layers.add(layer);
|
|
3735
|
+
changed();
|
|
3736
|
+
return {
|
|
3737
|
+
update(nextBindings, nextOptions) {
|
|
3738
|
+
layer.bindings = prepare(nextBindings);
|
|
3739
|
+
layer.options = nextOptions;
|
|
3740
|
+
warnOnPrefixConflicts(layer.bindings, nextOptions.name);
|
|
3741
|
+
const shape = layerShape(layer.bindings, nextOptions);
|
|
3742
|
+
const shapeChanged = shape !== layer.shape;
|
|
3743
|
+
layer.shape = shape;
|
|
3744
|
+
if (shapeChanged && pendingLayer === layer) abandonSequence();
|
|
3745
|
+
changed();
|
|
3746
|
+
},
|
|
3747
|
+
dispose() {
|
|
3748
|
+
layers.delete(layer);
|
|
3749
|
+
changed();
|
|
3750
|
+
if (pendingLayer === layer) abandonSequence();
|
|
3751
|
+
}
|
|
3752
|
+
};
|
|
3753
|
+
},
|
|
3754
|
+
handle,
|
|
3755
|
+
attach(target) {
|
|
3756
|
+
const listener = (event) => {
|
|
3757
|
+
try {
|
|
3758
|
+
if (handle(event)) event.stopPropagation();
|
|
3759
|
+
} catch (error) {
|
|
3760
|
+
event.stopPropagation();
|
|
3761
|
+
throw error;
|
|
3762
|
+
}
|
|
3763
|
+
};
|
|
3764
|
+
target.addEventListener("keydown", listener);
|
|
3765
|
+
return () => {
|
|
3766
|
+
target.removeEventListener("keydown", listener);
|
|
3767
|
+
abandonSequence();
|
|
3768
|
+
consumedPresses.clear();
|
|
3769
|
+
};
|
|
3770
|
+
},
|
|
3771
|
+
subscribe(onChange) {
|
|
3772
|
+
watchers.add(onChange);
|
|
3773
|
+
return () => {
|
|
3774
|
+
watchers.delete(onChange);
|
|
3775
|
+
};
|
|
3776
|
+
},
|
|
3777
|
+
activeBindings() {
|
|
3778
|
+
if (snapshot) return snapshot;
|
|
3779
|
+
snapshot = computeSnapshot();
|
|
3780
|
+
return snapshot;
|
|
3781
|
+
}
|
|
3782
|
+
};
|
|
3783
|
+
}
|
|
3784
|
+
var MODIFIER_KEYS = /* @__PURE__ */ new Set([
|
|
3785
|
+
"Control",
|
|
3786
|
+
"Meta",
|
|
3787
|
+
"Alt",
|
|
3788
|
+
"Shift",
|
|
3789
|
+
// A dedicated AltGraph key reports its own keydown before the character-producing one. Without
|
|
3790
|
+
// it here, pressing AltGraph mid-sequence abandons the sequence before the character that would
|
|
3791
|
+
// have completed it ever arrives.
|
|
3792
|
+
"AltGraph"
|
|
3793
|
+
]);
|
|
3794
|
+
var TYPE_AHEAD_ROLES = /* @__PURE__ */ new Set([
|
|
3795
|
+
"textbox",
|
|
3796
|
+
"combobox",
|
|
3797
|
+
"listbox",
|
|
3798
|
+
// The focused element inside an open listbox is the OPTION, and it is what the event reports;
|
|
3799
|
+
// the listbox itself is only its ancestor.
|
|
3800
|
+
"option",
|
|
3801
|
+
"menu",
|
|
3802
|
+
"menuitem",
|
|
3803
|
+
"menuitemcheckbox",
|
|
3804
|
+
"menuitemradio"
|
|
3805
|
+
]);
|
|
3806
|
+
var RANGE_KEYS = /* @__PURE__ */ new Set(["Home", "End", "PageUp", "PageDown"]);
|
|
3807
|
+
var EDITING_LETTERS = /* @__PURE__ */ new Set(["a", "c", "v", "x", "z"]);
|
|
3808
|
+
var REDO_LETTER = "y";
|
|
3809
|
+
var EDITING_NAVIGATION = /* @__PURE__ */ new Set([
|
|
3810
|
+
"Insert",
|
|
3811
|
+
"ArrowLeft",
|
|
3812
|
+
"ArrowRight",
|
|
3813
|
+
"ArrowUp",
|
|
3814
|
+
"ArrowDown",
|
|
3815
|
+
"Home",
|
|
3816
|
+
"End",
|
|
3817
|
+
"Backspace",
|
|
3818
|
+
"Delete"
|
|
3819
|
+
]);
|
|
3820
|
+
var AMBIGUOUS_KEYS = /* @__PURE__ */ new Set(["Enter", "PageUp", "PageDown"]);
|
|
3821
|
+
function targetOwnsAmbiguousKey(event) {
|
|
3822
|
+
const element = asElement(eventTarget(event));
|
|
3823
|
+
if (element === null) return false;
|
|
3824
|
+
const multiline = element.tagName === "TEXTAREA" || element.isContentEditable;
|
|
3825
|
+
if (event.key === "Enter") return multiline;
|
|
3826
|
+
return multiline || element.tagName === "SELECT";
|
|
3827
|
+
}
|
|
3828
|
+
var FIELD_KEYS = /* @__PURE__ */ new Set([
|
|
3829
|
+
"Backspace",
|
|
3830
|
+
"Delete",
|
|
3831
|
+
"ArrowUp",
|
|
3832
|
+
"ArrowDown",
|
|
3833
|
+
"ArrowLeft",
|
|
3834
|
+
"ArrowRight",
|
|
3835
|
+
"Home",
|
|
3836
|
+
"End",
|
|
3837
|
+
// Shift+Insert pastes and carries no ctrl or meta, so it arrives here rather than at the
|
|
3838
|
+
// chord branch where Ctrl+Insert is recognised.
|
|
3839
|
+
"Insert",
|
|
3840
|
+
// Tab moves focus, and a blocking layer must NOT take it. The documented case for blocking is
|
|
3841
|
+
// a modal, whose focus trap only calls `preventDefault()` at the first and last tabbable
|
|
3842
|
+
// element — ordinary moves between the controls inside it rely on the browser default.
|
|
3843
|
+
// Suppressing every Tab therefore pinned focus to one control in exactly the situation
|
|
3844
|
+
// blocking exists to serve. A layer that genuinely wants Tab binds it.
|
|
3845
|
+
"Tab"
|
|
3846
|
+
]);
|
|
3847
|
+
|
|
3848
|
+
// src/lib/shortcuts/react.tsx
|
|
3849
|
+
var import_jsx_runtime39 = require("react/jsx-runtime");
|
|
3850
|
+
var ShortcutContext = React13.createContext(null);
|
|
3851
|
+
var ownersByTarget = /* @__PURE__ */ new WeakMap();
|
|
3852
|
+
var useIsomorphicLayoutEffect = typeof document === "undefined" ? React13.useEffect : React13.useLayoutEffect;
|
|
3853
|
+
function optionsFingerprint(options) {
|
|
3854
|
+
return [
|
|
3855
|
+
options.isApple ?? "auto",
|
|
3856
|
+
options.sequenceTimeoutMs ?? "default",
|
|
3857
|
+
options.now ? "clock" : "no-clock"
|
|
3858
|
+
].join("\0");
|
|
3859
|
+
}
|
|
3860
|
+
function ShortcutProvider({
|
|
3861
|
+
children,
|
|
3862
|
+
target,
|
|
3863
|
+
...managerOptions
|
|
3864
|
+
}) {
|
|
3865
|
+
const parent = React13.useContext(ShortcutContext);
|
|
3866
|
+
const resolvedTarget = target === null ? null : target ?? (typeof document === "undefined" ? null : document);
|
|
3867
|
+
const nestedOnSameTarget = parent !== null && parent.target === resolvedTarget;
|
|
3868
|
+
const optionsRef = React13.useRef(managerOptions);
|
|
3869
|
+
const ownManagers = React13.useRef(/* @__PURE__ */ new WeakMap());
|
|
3870
|
+
const ownDetached = React13.useRef(null);
|
|
3871
|
+
const detached = React13.useMemo(() => {
|
|
3872
|
+
if (resolvedTarget === null) {
|
|
3873
|
+
ownDetached.current ??= createShortcutManager(optionsRef.current);
|
|
3874
|
+
return ownDetached.current;
|
|
3875
|
+
}
|
|
3876
|
+
const existing = ownManagers.current.get(resolvedTarget);
|
|
3877
|
+
if (existing) return existing;
|
|
3878
|
+
const created = createShortcutManager(optionsRef.current);
|
|
3879
|
+
ownManagers.current.set(resolvedTarget, created);
|
|
3880
|
+
return created;
|
|
3881
|
+
}, [resolvedTarget]);
|
|
3882
|
+
let owner = resolvedTarget === null ? null : ownersByTarget.get(resolvedTarget);
|
|
3883
|
+
const fingerprint = optionsFingerprint(optionsRef.current);
|
|
3884
|
+
if (resolvedTarget !== null && (!owner || owner.retired)) {
|
|
3885
|
+
owner = {
|
|
3886
|
+
manager: detached,
|
|
3887
|
+
providers: 0,
|
|
3888
|
+
retired: false,
|
|
3889
|
+
options: fingerprint
|
|
3890
|
+
};
|
|
3891
|
+
ownersByTarget.set(resolvedTarget, owner);
|
|
3892
|
+
}
|
|
3893
|
+
const adoptedDiffers = Boolean(owner) && owner?.options !== fingerprint || nestedOnSameTarget && parent !== null && parent.options !== fingerprint;
|
|
3894
|
+
devWarnOnce(
|
|
3895
|
+
!adoptedDiffers,
|
|
3896
|
+
"ShortcutProvider: another provider is already listening on this target with different options, so the ones passed here are being ignored. Managers are shared per target; give the providers matching options, or a target of their own."
|
|
3897
|
+
);
|
|
3898
|
+
const manager = nestedOnSameTarget && parent ? parent.manager : owner ? owner.manager : detached;
|
|
3899
|
+
useIsomorphicLayoutEffect(() => {
|
|
3900
|
+
if (resolvedTarget === null) return;
|
|
3901
|
+
let entry = ownersByTarget.get(resolvedTarget);
|
|
3902
|
+
if (!entry) {
|
|
3903
|
+
entry = {
|
|
3904
|
+
manager,
|
|
3905
|
+
providers: 0,
|
|
3906
|
+
retired: false,
|
|
3907
|
+
options: optionsFingerprint(optionsRef.current)
|
|
3908
|
+
};
|
|
3909
|
+
ownersByTarget.set(resolvedTarget, entry);
|
|
3910
|
+
}
|
|
3911
|
+
const owned = entry;
|
|
3912
|
+
owned.providers += 1;
|
|
3913
|
+
owned.retired = false;
|
|
3914
|
+
if (owned.providers === 1) {
|
|
3915
|
+
owned.detach = owned.manager.attach(resolvedTarget);
|
|
3916
|
+
}
|
|
3917
|
+
return () => {
|
|
3918
|
+
owned.providers -= 1;
|
|
3919
|
+
if (owned.providers === 0) {
|
|
3920
|
+
owned.detach?.();
|
|
3921
|
+
owned.detach = void 0;
|
|
3922
|
+
owned.retired = true;
|
|
3923
|
+
}
|
|
3924
|
+
};
|
|
3925
|
+
}, [resolvedTarget, manager]);
|
|
3926
|
+
const depth = nestedOnSameTarget && parent ? parent.depth : 0;
|
|
3927
|
+
const value = React13.useMemo(
|
|
3928
|
+
() => ({ manager, depth, target: resolvedTarget, options: fingerprint }),
|
|
3929
|
+
[manager, depth, resolvedTarget, fingerprint]
|
|
3930
|
+
);
|
|
3931
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShortcutContext.Provider, { value, children });
|
|
3932
|
+
}
|
|
3933
|
+
function ShortcutScope({
|
|
3934
|
+
children
|
|
3935
|
+
}) {
|
|
3936
|
+
const parent = React13.useContext(ShortcutContext);
|
|
3937
|
+
if (!parent) {
|
|
3938
|
+
throw new Error("ShortcutScope must be rendered inside a ShortcutProvider");
|
|
3939
|
+
}
|
|
3940
|
+
const value = React13.useMemo(
|
|
3941
|
+
() => ({
|
|
3942
|
+
manager: parent.manager,
|
|
3943
|
+
depth: parent.depth + 1,
|
|
3944
|
+
target: parent.target,
|
|
3945
|
+
// Inherited: a scope raises precedence, it does not build a manager, so the options in force
|
|
3946
|
+
// are still the ones the provider above it used.
|
|
3947
|
+
options: parent.options
|
|
3948
|
+
}),
|
|
3949
|
+
[parent.manager, parent.depth, parent.target, parent.options]
|
|
3950
|
+
);
|
|
3951
|
+
return /* @__PURE__ */ (0, import_jsx_runtime39.jsx)(ShortcutContext.Provider, { value, children });
|
|
3952
|
+
}
|
|
3953
|
+
function useShortcuts(bindings, options) {
|
|
3954
|
+
const context = React13.useContext(ShortcutContext);
|
|
3955
|
+
if (!context) {
|
|
3956
|
+
throw new Error("useShortcuts must be called inside a ShortcutProvider");
|
|
3957
|
+
}
|
|
3958
|
+
const { manager, depth } = context;
|
|
3959
|
+
const registration = React13.useRef(null);
|
|
3960
|
+
const latest = React13.useRef({ bindings, options });
|
|
3961
|
+
useIsomorphicLayoutEffect(() => {
|
|
3962
|
+
registration.current = manager.register([], {
|
|
3963
|
+
name: latest.current.options.name,
|
|
3964
|
+
depth
|
|
3965
|
+
});
|
|
3966
|
+
return () => {
|
|
3967
|
+
registration.current?.dispose();
|
|
3968
|
+
registration.current = null;
|
|
3969
|
+
};
|
|
3970
|
+
}, [manager, depth]);
|
|
3971
|
+
useIsomorphicLayoutEffect(() => {
|
|
3972
|
+
latest.current = { bindings, options };
|
|
3973
|
+
registration.current?.update(bindings, {
|
|
3974
|
+
name: options.name,
|
|
3975
|
+
depth,
|
|
3976
|
+
enabled: options.enabled,
|
|
3977
|
+
blocking: options.blocking
|
|
3978
|
+
});
|
|
3979
|
+
});
|
|
3980
|
+
}
|
|
3981
|
+
function useShortcutManager() {
|
|
3982
|
+
const context = React13.useContext(ShortcutContext);
|
|
3983
|
+
if (!context) {
|
|
3984
|
+
throw new Error(
|
|
3985
|
+
"useShortcutManager must be called inside a ShortcutProvider"
|
|
3986
|
+
);
|
|
3987
|
+
}
|
|
3988
|
+
return context.manager;
|
|
3989
|
+
}
|
|
3990
|
+
function useActiveShortcuts() {
|
|
3991
|
+
const manager = useShortcutManager();
|
|
3992
|
+
return React13.useSyncExternalStore(
|
|
3993
|
+
manager.subscribe,
|
|
3994
|
+
manager.activeBindings,
|
|
3995
|
+
manager.activeBindings
|
|
3996
|
+
);
|
|
3997
|
+
}
|
|
2812
3998
|
// Annotate the CommonJS export names for ESM import in node:
|
|
2813
3999
|
0 && (module.exports = {
|
|
2814
4000
|
Accordion,
|
|
@@ -2845,6 +4031,7 @@ TreeView.displayName = "TreeView";
|
|
|
2845
4031
|
Collapsible,
|
|
2846
4032
|
CollapsibleContent,
|
|
2847
4033
|
CollapsibleTrigger,
|
|
4034
|
+
ColorPicker,
|
|
2848
4035
|
Command,
|
|
2849
4036
|
CommandDialog,
|
|
2850
4037
|
CommandEmpty,
|
|
@@ -2929,7 +4116,10 @@ TreeView.displayName = "TreeView";
|
|
|
2929
4116
|
SheetPortal,
|
|
2930
4117
|
SheetTitle,
|
|
2931
4118
|
SheetTrigger,
|
|
4119
|
+
ShortcutProvider,
|
|
4120
|
+
ShortcutScope,
|
|
2932
4121
|
Skeleton,
|
|
4122
|
+
Slider,
|
|
2933
4123
|
Spinner,
|
|
2934
4124
|
Stack,
|
|
2935
4125
|
Stat,
|
|
@@ -2963,13 +4153,18 @@ TreeView.displayName = "TreeView";
|
|
|
2963
4153
|
badgeVariants,
|
|
2964
4154
|
buttonVariants,
|
|
2965
4155
|
cardVariants,
|
|
4156
|
+
createShortcutManager,
|
|
2966
4157
|
dialogContentVariants,
|
|
2967
4158
|
inputVariants,
|
|
4159
|
+
parseKeys,
|
|
2968
4160
|
progressVariants,
|
|
2969
4161
|
selectTriggerVariants,
|
|
2970
4162
|
sheetVariants,
|
|
2971
4163
|
spinnerVariants,
|
|
2972
4164
|
toast,
|
|
2973
|
-
|
|
4165
|
+
useActiveShortcuts,
|
|
4166
|
+
usePortalContainer,
|
|
4167
|
+
useShortcutManager,
|
|
4168
|
+
useShortcuts
|
|
2974
4169
|
});
|
|
2975
4170
|
//# sourceMappingURL=index.cjs.map
|