@classic-homes/theme-react 0.1.49 → 0.1.51
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.css +141 -0
- package/dist/index.d.mts +118 -4
- package/dist/index.d.ts +118 -4
- package/dist/index.js +911 -21
- package/dist/index.mjs +927 -20
- package/package.json +5 -3
package/dist/index.mjs
CHANGED
|
@@ -122,6 +122,7 @@ var Label = React4.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
122
122
|
Label.displayName = LabelPrimitive.Root.displayName;
|
|
123
123
|
|
|
124
124
|
// src/components/Badge.tsx
|
|
125
|
+
import * as React5 from "react";
|
|
125
126
|
import { cva as cva3 } from "class-variance-authority";
|
|
126
127
|
import { jsx as jsx5 } from "react/jsx-runtime";
|
|
127
128
|
var badgeVariants = cva3(
|
|
@@ -140,15 +141,26 @@ var badgeVariants = cva3(
|
|
|
140
141
|
}
|
|
141
142
|
}
|
|
142
143
|
);
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
144
|
+
var Badge = React5.forwardRef(
|
|
145
|
+
({ className, variant, ...props }, ref) => {
|
|
146
|
+
return /* @__PURE__ */ jsx5(
|
|
147
|
+
"div",
|
|
148
|
+
{
|
|
149
|
+
ref,
|
|
150
|
+
role: "status",
|
|
151
|
+
className: cn(badgeVariants({ variant }), className),
|
|
152
|
+
...props
|
|
153
|
+
}
|
|
154
|
+
);
|
|
155
|
+
}
|
|
156
|
+
);
|
|
157
|
+
Badge.displayName = "Badge";
|
|
146
158
|
|
|
147
159
|
// src/components/Separator.tsx
|
|
148
|
-
import * as
|
|
160
|
+
import * as React6 from "react";
|
|
149
161
|
import * as SeparatorPrimitive from "@radix-ui/react-separator";
|
|
150
162
|
import { jsx as jsx6 } from "react/jsx-runtime";
|
|
151
|
-
var Separator =
|
|
163
|
+
var Separator = React6.forwardRef(({ className, orientation = "horizontal", decorative = true, ...props }, ref) => /* @__PURE__ */ jsx6(
|
|
152
164
|
SeparatorPrimitive.Root,
|
|
153
165
|
{
|
|
154
166
|
ref,
|
|
@@ -165,10 +177,10 @@ var Separator = React5.forwardRef(({ className, orientation = "horizontal", deco
|
|
|
165
177
|
Separator.displayName = SeparatorPrimitive.Root.displayName;
|
|
166
178
|
|
|
167
179
|
// src/components/Switch.tsx
|
|
168
|
-
import * as
|
|
180
|
+
import * as React7 from "react";
|
|
169
181
|
import * as SwitchPrimitives from "@radix-ui/react-switch";
|
|
170
182
|
import { jsx as jsx7 } from "react/jsx-runtime";
|
|
171
|
-
var Switch =
|
|
183
|
+
var Switch = React7.forwardRef(
|
|
172
184
|
({ className, size = "default", ...props }, ref) => /* @__PURE__ */ jsx7(
|
|
173
185
|
SwitchPrimitives.Root,
|
|
174
186
|
{
|
|
@@ -196,10 +208,10 @@ var Switch = React6.forwardRef(
|
|
|
196
208
|
Switch.displayName = SwitchPrimitives.Root.displayName;
|
|
197
209
|
|
|
198
210
|
// src/components/Avatar.tsx
|
|
199
|
-
import * as
|
|
211
|
+
import * as React8 from "react";
|
|
200
212
|
import * as AvatarPrimitive from "@radix-ui/react-avatar";
|
|
201
213
|
import { jsx as jsx8 } from "react/jsx-runtime";
|
|
202
|
-
var Avatar =
|
|
214
|
+
var Avatar = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
203
215
|
AvatarPrimitive.Root,
|
|
204
216
|
{
|
|
205
217
|
ref,
|
|
@@ -208,16 +220,16 @@ var Avatar = React7.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */
|
|
|
208
220
|
}
|
|
209
221
|
));
|
|
210
222
|
Avatar.displayName = AvatarPrimitive.Root.displayName;
|
|
211
|
-
var AvatarImage =
|
|
223
|
+
var AvatarImage = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
212
224
|
AvatarPrimitive.Image,
|
|
213
225
|
{
|
|
214
226
|
ref,
|
|
215
|
-
className: cn("aspect-square h-full w-full", className),
|
|
227
|
+
className: cn("aspect-square h-full w-full object-cover", className),
|
|
216
228
|
...props
|
|
217
229
|
}
|
|
218
230
|
));
|
|
219
231
|
AvatarImage.displayName = AvatarPrimitive.Image.displayName;
|
|
220
|
-
var AvatarFallback =
|
|
232
|
+
var AvatarFallback = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx8(
|
|
221
233
|
AvatarPrimitive.Fallback,
|
|
222
234
|
{
|
|
223
235
|
ref,
|
|
@@ -231,14 +243,14 @@ var AvatarFallback = React7.forwardRef(({ className, ...props }, ref) => /* @__P
|
|
|
231
243
|
AvatarFallback.displayName = AvatarPrimitive.Fallback.displayName;
|
|
232
244
|
|
|
233
245
|
// src/components/Dialog.tsx
|
|
234
|
-
import * as
|
|
246
|
+
import * as React9 from "react";
|
|
235
247
|
import * as DialogPrimitive from "@radix-ui/react-dialog";
|
|
236
248
|
import { jsx as jsx9, jsxs } from "react/jsx-runtime";
|
|
237
249
|
var Dialog = DialogPrimitive.Root;
|
|
238
250
|
var DialogTrigger = DialogPrimitive.Trigger;
|
|
239
251
|
var DialogPortal = DialogPrimitive.Portal;
|
|
240
252
|
var DialogClose = DialogPrimitive.Close;
|
|
241
|
-
var DialogOverlay =
|
|
253
|
+
var DialogOverlay = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
242
254
|
DialogPrimitive.Overlay,
|
|
243
255
|
{
|
|
244
256
|
ref,
|
|
@@ -250,7 +262,7 @@ var DialogOverlay = React8.forwardRef(({ className, ...props }, ref) => /* @__PU
|
|
|
250
262
|
}
|
|
251
263
|
));
|
|
252
264
|
DialogOverlay.displayName = DialogPrimitive.Overlay.displayName;
|
|
253
|
-
var DialogContent =
|
|
265
|
+
var DialogContent = React9.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(DialogPortal, { children: [
|
|
254
266
|
/* @__PURE__ */ jsx9(DialogOverlay, {}),
|
|
255
267
|
/* @__PURE__ */ jsx9(
|
|
256
268
|
DialogPrimitive.Content,
|
|
@@ -276,7 +288,7 @@ var DialogFooter = ({ className, ...props }) => /* @__PURE__ */ jsx9(
|
|
|
276
288
|
}
|
|
277
289
|
);
|
|
278
290
|
DialogFooter.displayName = "DialogFooter";
|
|
279
|
-
var DialogTitle =
|
|
291
|
+
var DialogTitle = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
280
292
|
DialogPrimitive.Title,
|
|
281
293
|
{
|
|
282
294
|
ref,
|
|
@@ -285,7 +297,7 @@ var DialogTitle = React8.forwardRef(({ className, ...props }, ref) => /* @__PURE
|
|
|
285
297
|
}
|
|
286
298
|
));
|
|
287
299
|
DialogTitle.displayName = DialogPrimitive.Title.displayName;
|
|
288
|
-
var DialogDescription =
|
|
300
|
+
var DialogDescription = React9.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx9(
|
|
289
301
|
DialogPrimitive.Description,
|
|
290
302
|
{
|
|
291
303
|
ref,
|
|
@@ -296,7 +308,7 @@ var DialogDescription = React8.forwardRef(({ className, ...props }, ref) => /* @
|
|
|
296
308
|
DialogDescription.displayName = DialogPrimitive.Description.displayName;
|
|
297
309
|
|
|
298
310
|
// src/components/PageHeader.tsx
|
|
299
|
-
import * as
|
|
311
|
+
import * as React10 from "react";
|
|
300
312
|
import { cva as cva4 } from "class-variance-authority";
|
|
301
313
|
import { jsx as jsx10, jsxs as jsxs2 } from "react/jsx-runtime";
|
|
302
314
|
var pageHeaderContainerVariants = cva4("", {
|
|
@@ -347,7 +359,7 @@ var pageHeaderActionsVariants = cva4("flex gap-4", {
|
|
|
347
359
|
variant: "default"
|
|
348
360
|
}
|
|
349
361
|
});
|
|
350
|
-
var PageHeader =
|
|
362
|
+
var PageHeader = React10.forwardRef(
|
|
351
363
|
({ className, variant, title, subtitle, actions, ...props }, ref) => /* @__PURE__ */ jsxs2(
|
|
352
364
|
"header",
|
|
353
365
|
{
|
|
@@ -363,6 +375,895 @@ var PageHeader = React9.forwardRef(
|
|
|
363
375
|
)
|
|
364
376
|
);
|
|
365
377
|
PageHeader.displayName = "PageHeader";
|
|
378
|
+
|
|
379
|
+
// src/components/DataPanel.tsx
|
|
380
|
+
import * as React11 from "react";
|
|
381
|
+
import * as DropdownMenuPrimitive from "@radix-ui/react-dropdown-menu";
|
|
382
|
+
import {
|
|
383
|
+
DEFAULT_PANEL_STATE,
|
|
384
|
+
DEFAULT_CONSTRAINTS,
|
|
385
|
+
loadPanelState,
|
|
386
|
+
savePanelState,
|
|
387
|
+
getPointerPosition,
|
|
388
|
+
calculateDragPosition,
|
|
389
|
+
calculateResize,
|
|
390
|
+
getResizeCursor,
|
|
391
|
+
getPinnedResizeHandle,
|
|
392
|
+
constrainSize,
|
|
393
|
+
constrainPosition,
|
|
394
|
+
constrainPinnedWidth,
|
|
395
|
+
constrainPinnedHeight,
|
|
396
|
+
detectEdgeSnap,
|
|
397
|
+
getPinnedPositionStyles,
|
|
398
|
+
getDetachedPositionStyles,
|
|
399
|
+
getInitialDetachedPosition,
|
|
400
|
+
isHorizontalEdge,
|
|
401
|
+
calculateDetachDistance,
|
|
402
|
+
calculatePullOffset,
|
|
403
|
+
calculateDetachedPositionFromPinned,
|
|
404
|
+
getPinnedPullTransform
|
|
405
|
+
} from "@classic-homes/data-panel-core";
|
|
406
|
+
import { Fragment, jsx as jsx11, jsxs as jsxs3 } from "react/jsx-runtime";
|
|
407
|
+
function useDataPanel(options = {}) {
|
|
408
|
+
const {
|
|
409
|
+
persistKey,
|
|
410
|
+
initialMode = DEFAULT_PANEL_STATE.mode,
|
|
411
|
+
initialEdge = DEFAULT_PANEL_STATE.edge,
|
|
412
|
+
initialExpanded = DEFAULT_PANEL_STATE.isExpanded,
|
|
413
|
+
constraints = {}
|
|
414
|
+
} = options;
|
|
415
|
+
const resolvedConstraints = { ...DEFAULT_CONSTRAINTS, ...constraints };
|
|
416
|
+
const [persistedState] = React11.useState(() => {
|
|
417
|
+
if (persistKey && typeof window !== "undefined") {
|
|
418
|
+
return loadPanelState(persistKey);
|
|
419
|
+
}
|
|
420
|
+
return null;
|
|
421
|
+
});
|
|
422
|
+
const [mode, setMode] = React11.useState(persistedState?.mode ?? initialMode);
|
|
423
|
+
const [edge, setEdge] = React11.useState(persistedState?.edge ?? initialEdge);
|
|
424
|
+
const [isExpanded, setIsExpanded] = React11.useState(
|
|
425
|
+
persistedState?.isExpanded ?? initialExpanded
|
|
426
|
+
);
|
|
427
|
+
const [detachedPosition, setDetachedPosition] = React11.useState(
|
|
428
|
+
persistedState?.detachedPosition ?? DEFAULT_PANEL_STATE.detachedPosition
|
|
429
|
+
);
|
|
430
|
+
const [detachedSize, setDetachedSize] = React11.useState(
|
|
431
|
+
persistedState?.detachedSize ?? DEFAULT_PANEL_STATE.detachedSize
|
|
432
|
+
);
|
|
433
|
+
const [pinnedSize, setPinnedSize] = React11.useState(
|
|
434
|
+
persistedState?.pinnedSize ?? DEFAULT_PANEL_STATE.pinnedSize
|
|
435
|
+
);
|
|
436
|
+
const saveTimeoutRef = React11.useRef(null);
|
|
437
|
+
const debouncedSave = React11.useCallback(() => {
|
|
438
|
+
if (!persistKey) return;
|
|
439
|
+
if (saveTimeoutRef.current) {
|
|
440
|
+
clearTimeout(saveTimeoutRef.current);
|
|
441
|
+
}
|
|
442
|
+
saveTimeoutRef.current = setTimeout(() => {
|
|
443
|
+
savePanelState(persistKey, {
|
|
444
|
+
mode,
|
|
445
|
+
variant: "full",
|
|
446
|
+
edge,
|
|
447
|
+
isExpanded,
|
|
448
|
+
detachedPosition,
|
|
449
|
+
detachedSize,
|
|
450
|
+
pinnedSize,
|
|
451
|
+
cardSnapIndex: 0
|
|
452
|
+
});
|
|
453
|
+
}, 300);
|
|
454
|
+
}, [persistKey, mode, edge, isExpanded, detachedPosition, detachedSize, pinnedSize]);
|
|
455
|
+
const handleSetMode = React11.useCallback(
|
|
456
|
+
(newMode) => {
|
|
457
|
+
setMode(newMode);
|
|
458
|
+
if (newMode === "detached" && typeof window !== "undefined" && detachedPosition.x === DEFAULT_PANEL_STATE.detachedPosition.x && detachedPosition.y === DEFAULT_PANEL_STATE.detachedPosition.y) {
|
|
459
|
+
setDetachedPosition(
|
|
460
|
+
getInitialDetachedPosition(detachedSize, window.innerWidth, window.innerHeight)
|
|
461
|
+
);
|
|
462
|
+
}
|
|
463
|
+
},
|
|
464
|
+
[detachedPosition, detachedSize]
|
|
465
|
+
);
|
|
466
|
+
const handleSetDetachedPosition = React11.useCallback(
|
|
467
|
+
(position) => {
|
|
468
|
+
if (typeof window === "undefined") {
|
|
469
|
+
setDetachedPosition(position);
|
|
470
|
+
return;
|
|
471
|
+
}
|
|
472
|
+
setDetachedPosition(
|
|
473
|
+
constrainPosition(position, detachedSize, window.innerWidth, window.innerHeight)
|
|
474
|
+
);
|
|
475
|
+
},
|
|
476
|
+
[detachedSize]
|
|
477
|
+
);
|
|
478
|
+
const handleSetDetachedSize = React11.useCallback(
|
|
479
|
+
(size) => {
|
|
480
|
+
setDetachedSize(constrainSize(size, resolvedConstraints));
|
|
481
|
+
},
|
|
482
|
+
[resolvedConstraints]
|
|
483
|
+
);
|
|
484
|
+
const handleSetPinnedSize = React11.useCallback(
|
|
485
|
+
(size) => {
|
|
486
|
+
if (typeof window === "undefined") {
|
|
487
|
+
setPinnedSize(size);
|
|
488
|
+
return;
|
|
489
|
+
}
|
|
490
|
+
if (isHorizontalEdge(edge)) {
|
|
491
|
+
setPinnedSize(constrainPinnedWidth(size, resolvedConstraints, window.innerWidth));
|
|
492
|
+
} else {
|
|
493
|
+
setPinnedSize(constrainPinnedHeight(size, resolvedConstraints, window.innerHeight));
|
|
494
|
+
}
|
|
495
|
+
},
|
|
496
|
+
[edge, resolvedConstraints]
|
|
497
|
+
);
|
|
498
|
+
React11.useEffect(() => {
|
|
499
|
+
debouncedSave();
|
|
500
|
+
}, [debouncedSave]);
|
|
501
|
+
return {
|
|
502
|
+
mode,
|
|
503
|
+
setMode: handleSetMode,
|
|
504
|
+
edge,
|
|
505
|
+
setEdge,
|
|
506
|
+
isExpanded,
|
|
507
|
+
setIsExpanded,
|
|
508
|
+
detachedPosition,
|
|
509
|
+
setDetachedPosition: handleSetDetachedPosition,
|
|
510
|
+
detachedSize,
|
|
511
|
+
setDetachedSize: handleSetDetachedSize,
|
|
512
|
+
pinnedSize,
|
|
513
|
+
setPinnedSize: handleSetPinnedSize
|
|
514
|
+
};
|
|
515
|
+
}
|
|
516
|
+
var ChevronUpIcon = () => /* @__PURE__ */ jsx11(
|
|
517
|
+
"svg",
|
|
518
|
+
{
|
|
519
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
520
|
+
width: "16",
|
|
521
|
+
height: "16",
|
|
522
|
+
viewBox: "0 0 24 24",
|
|
523
|
+
fill: "none",
|
|
524
|
+
stroke: "currentColor",
|
|
525
|
+
strokeWidth: "2",
|
|
526
|
+
strokeLinecap: "round",
|
|
527
|
+
strokeLinejoin: "round",
|
|
528
|
+
children: /* @__PURE__ */ jsx11("path", { d: "m18 15-6-6-6 6" })
|
|
529
|
+
}
|
|
530
|
+
);
|
|
531
|
+
var MoreVerticalIcon = () => /* @__PURE__ */ jsxs3(
|
|
532
|
+
"svg",
|
|
533
|
+
{
|
|
534
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
535
|
+
width: "16",
|
|
536
|
+
height: "16",
|
|
537
|
+
viewBox: "0 0 24 24",
|
|
538
|
+
fill: "none",
|
|
539
|
+
stroke: "currentColor",
|
|
540
|
+
strokeWidth: "2",
|
|
541
|
+
strokeLinecap: "round",
|
|
542
|
+
strokeLinejoin: "round",
|
|
543
|
+
children: [
|
|
544
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "12", r: "1" }),
|
|
545
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "5", r: "1" }),
|
|
546
|
+
/* @__PURE__ */ jsx11("circle", { cx: "12", cy: "19", r: "1" })
|
|
547
|
+
]
|
|
548
|
+
}
|
|
549
|
+
);
|
|
550
|
+
var CloseIcon = () => /* @__PURE__ */ jsxs3(
|
|
551
|
+
"svg",
|
|
552
|
+
{
|
|
553
|
+
xmlns: "http://www.w3.org/2000/svg",
|
|
554
|
+
width: "16",
|
|
555
|
+
height: "16",
|
|
556
|
+
viewBox: "0 0 24 24",
|
|
557
|
+
fill: "none",
|
|
558
|
+
stroke: "currentColor",
|
|
559
|
+
strokeWidth: "2",
|
|
560
|
+
strokeLinecap: "round",
|
|
561
|
+
strokeLinejoin: "round",
|
|
562
|
+
children: [
|
|
563
|
+
/* @__PURE__ */ jsx11("path", { d: "M18 6 6 18" }),
|
|
564
|
+
/* @__PURE__ */ jsx11("path", { d: "m6 6 12 12" })
|
|
565
|
+
]
|
|
566
|
+
}
|
|
567
|
+
);
|
|
568
|
+
var edgeLabels = {
|
|
569
|
+
left: "Pin to left",
|
|
570
|
+
right: "Pin to right",
|
|
571
|
+
top: "Pin to top",
|
|
572
|
+
bottom: "Pin to bottom"
|
|
573
|
+
};
|
|
574
|
+
var DataPanelHeader = React11.forwardRef(
|
|
575
|
+
({
|
|
576
|
+
title,
|
|
577
|
+
subtitle,
|
|
578
|
+
mode,
|
|
579
|
+
edge,
|
|
580
|
+
isExpanded,
|
|
581
|
+
onModeChange,
|
|
582
|
+
onEdgeChange,
|
|
583
|
+
onExpandedChange,
|
|
584
|
+
onClose,
|
|
585
|
+
disableClose = false,
|
|
586
|
+
disableModeSwitch = false,
|
|
587
|
+
headerContent,
|
|
588
|
+
headerActions,
|
|
589
|
+
className,
|
|
590
|
+
draggable = false
|
|
591
|
+
}, ref) => {
|
|
592
|
+
return /* @__PURE__ */ jsxs3(
|
|
593
|
+
"div",
|
|
594
|
+
{
|
|
595
|
+
ref,
|
|
596
|
+
className: cn(
|
|
597
|
+
"flex items-center justify-between gap-2 border-b border-border bg-muted/30 px-4 py-3",
|
|
598
|
+
draggable && "cursor-grab active:cursor-grabbing",
|
|
599
|
+
className
|
|
600
|
+
),
|
|
601
|
+
"data-panel-header": true,
|
|
602
|
+
children: [
|
|
603
|
+
/* @__PURE__ */ jsx11("div", { className: "flex flex-col min-w-0 flex-1", children: headerContent ? headerContent : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
604
|
+
title && /* @__PURE__ */ jsx11("h3", { className: "text-sm font-semibold leading-none truncate", children: title }),
|
|
605
|
+
subtitle && /* @__PURE__ */ jsx11("p", { className: "text-xs text-muted-foreground mt-1 truncate", children: subtitle })
|
|
606
|
+
] }) }),
|
|
607
|
+
/* @__PURE__ */ jsxs3("div", { className: "flex items-center gap-1 shrink-0", children: [
|
|
608
|
+
headerActions,
|
|
609
|
+
/* @__PURE__ */ jsx11(
|
|
610
|
+
"button",
|
|
611
|
+
{
|
|
612
|
+
type: "button",
|
|
613
|
+
className: "flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
614
|
+
onClick: () => onExpandedChange?.(!isExpanded),
|
|
615
|
+
"aria-label": isExpanded ? "Collapse panel" : "Expand panel",
|
|
616
|
+
"aria-expanded": isExpanded,
|
|
617
|
+
children: /* @__PURE__ */ jsx11("span", { className: cn("transition-transform", !isExpanded && "rotate-180"), children: /* @__PURE__ */ jsx11(ChevronUpIcon, {}) })
|
|
618
|
+
}
|
|
619
|
+
),
|
|
620
|
+
!disableModeSwitch && /* @__PURE__ */ jsxs3(DropdownMenuPrimitive.Root, { children: [
|
|
621
|
+
/* @__PURE__ */ jsx11(DropdownMenuPrimitive.Trigger, { asChild: true, children: /* @__PURE__ */ jsx11(
|
|
622
|
+
"button",
|
|
623
|
+
{
|
|
624
|
+
type: "button",
|
|
625
|
+
className: "flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
626
|
+
"aria-label": "Panel options",
|
|
627
|
+
children: /* @__PURE__ */ jsx11(MoreVerticalIcon, {})
|
|
628
|
+
}
|
|
629
|
+
) }),
|
|
630
|
+
/* @__PURE__ */ jsx11(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsxs3(
|
|
631
|
+
DropdownMenuPrimitive.Content,
|
|
632
|
+
{
|
|
633
|
+
className: "z-50 min-w-[8rem] overflow-hidden rounded-md border border-border bg-popover p-1 text-popover-foreground shadow-md animate-in fade-in-0 zoom-in-95",
|
|
634
|
+
sideOffset: 4,
|
|
635
|
+
align: "end",
|
|
636
|
+
children: [
|
|
637
|
+
/* @__PURE__ */ jsx11(DropdownMenuPrimitive.Label, { className: "px-2 py-1.5 text-sm font-semibold", children: "Panel Position" }),
|
|
638
|
+
["left", "right", "top", "bottom"].map((e) => /* @__PURE__ */ jsxs3(
|
|
639
|
+
DropdownMenuPrimitive.Item,
|
|
640
|
+
{
|
|
641
|
+
className: "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
642
|
+
onSelect: () => {
|
|
643
|
+
onModeChange?.("pinned");
|
|
644
|
+
onEdgeChange?.(e);
|
|
645
|
+
},
|
|
646
|
+
children: [
|
|
647
|
+
edgeLabels[e],
|
|
648
|
+
mode === "pinned" && edge === e && /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs", children: "\u2713" })
|
|
649
|
+
]
|
|
650
|
+
},
|
|
651
|
+
e
|
|
652
|
+
)),
|
|
653
|
+
/* @__PURE__ */ jsx11(DropdownMenuPrimitive.Separator, { className: "-mx-1 my-1 h-px bg-muted" }),
|
|
654
|
+
/* @__PURE__ */ jsxs3(
|
|
655
|
+
DropdownMenuPrimitive.Item,
|
|
656
|
+
{
|
|
657
|
+
className: "relative flex cursor-pointer select-none items-center rounded-sm px-2 py-1.5 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground",
|
|
658
|
+
onSelect: () => onModeChange?.("detached"),
|
|
659
|
+
children: [
|
|
660
|
+
"Detach",
|
|
661
|
+
mode === "detached" && /* @__PURE__ */ jsx11("span", { className: "ml-auto text-xs", children: "\u2713" })
|
|
662
|
+
]
|
|
663
|
+
}
|
|
664
|
+
)
|
|
665
|
+
]
|
|
666
|
+
}
|
|
667
|
+
) })
|
|
668
|
+
] }),
|
|
669
|
+
!disableClose && /* @__PURE__ */ jsx11(
|
|
670
|
+
"button",
|
|
671
|
+
{
|
|
672
|
+
type: "button",
|
|
673
|
+
className: "flex h-8 w-8 items-center justify-center rounded-md bg-transparent text-muted-foreground transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
674
|
+
onClick: onClose,
|
|
675
|
+
"aria-label": "Close panel",
|
|
676
|
+
children: /* @__PURE__ */ jsx11(CloseIcon, {})
|
|
677
|
+
}
|
|
678
|
+
)
|
|
679
|
+
] })
|
|
680
|
+
]
|
|
681
|
+
}
|
|
682
|
+
);
|
|
683
|
+
}
|
|
684
|
+
);
|
|
685
|
+
DataPanelHeader.displayName = "DataPanelHeader";
|
|
686
|
+
var DataPanelContent = React11.forwardRef(
|
|
687
|
+
({ children, className }, ref) => {
|
|
688
|
+
return /* @__PURE__ */ jsx11("div", { ref, className: cn("flex-1 overflow-auto p-4", className), children });
|
|
689
|
+
}
|
|
690
|
+
);
|
|
691
|
+
DataPanelContent.displayName = "DataPanelContent";
|
|
692
|
+
var DataPanelFooter = React11.forwardRef(
|
|
693
|
+
({ actions = [], footerContent, footerActions, className }, ref) => {
|
|
694
|
+
if (!footerContent && actions.length === 0 && !footerActions) {
|
|
695
|
+
return null;
|
|
696
|
+
}
|
|
697
|
+
const mapVariant = (variant) => {
|
|
698
|
+
if (variant === "primary") return "default";
|
|
699
|
+
return variant ?? "default";
|
|
700
|
+
};
|
|
701
|
+
return /* @__PURE__ */ jsx11(
|
|
702
|
+
"div",
|
|
703
|
+
{
|
|
704
|
+
ref,
|
|
705
|
+
className: cn(
|
|
706
|
+
"flex items-center justify-end gap-2 border-t border-border bg-muted/30 px-4 py-3",
|
|
707
|
+
className
|
|
708
|
+
),
|
|
709
|
+
children: footerContent ? footerContent : /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
710
|
+
footerActions,
|
|
711
|
+
actions.map((action, index) => /* @__PURE__ */ jsx11(
|
|
712
|
+
Button,
|
|
713
|
+
{
|
|
714
|
+
variant: mapVariant(action.variant),
|
|
715
|
+
size: "sm",
|
|
716
|
+
disabled: action.disabled,
|
|
717
|
+
onClick: action.onClick,
|
|
718
|
+
children: action.label
|
|
719
|
+
},
|
|
720
|
+
index
|
|
721
|
+
))
|
|
722
|
+
] })
|
|
723
|
+
}
|
|
724
|
+
);
|
|
725
|
+
}
|
|
726
|
+
);
|
|
727
|
+
DataPanelFooter.displayName = "DataPanelFooter";
|
|
728
|
+
var DataPanelTab = React11.forwardRef(
|
|
729
|
+
({ title = "Panel", edge, onClick, className }, ref) => {
|
|
730
|
+
const positionClasses = {
|
|
731
|
+
left: "left-0 top-1/2 -translate-y-1/2 rounded-r-md border-l-0",
|
|
732
|
+
right: "right-0 top-1/2 -translate-y-1/2 rounded-l-md border-r-0",
|
|
733
|
+
top: "top-0 left-1/2 -translate-x-1/2 rounded-b-md border-t-0",
|
|
734
|
+
bottom: "bottom-0 left-1/2 -translate-x-1/2 rounded-t-md border-b-0"
|
|
735
|
+
}[edge];
|
|
736
|
+
const isVertical = edge === "left" || edge === "right";
|
|
737
|
+
const iconRotation = {
|
|
738
|
+
left: "rotate-90",
|
|
739
|
+
right: "-rotate-90",
|
|
740
|
+
top: "rotate-180",
|
|
741
|
+
bottom: ""
|
|
742
|
+
}[edge];
|
|
743
|
+
return /* @__PURE__ */ jsxs3(
|
|
744
|
+
"button",
|
|
745
|
+
{
|
|
746
|
+
ref,
|
|
747
|
+
type: "button",
|
|
748
|
+
className: cn(
|
|
749
|
+
"fixed z-40 flex items-center justify-center gap-2 border border-border bg-background px-3 py-2 text-sm font-medium shadow-md transition-colors hover:bg-accent hover:text-accent-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring",
|
|
750
|
+
positionClasses,
|
|
751
|
+
isVertical && "[writing-mode:vertical-rl]",
|
|
752
|
+
className
|
|
753
|
+
),
|
|
754
|
+
onClick,
|
|
755
|
+
"aria-label": `Expand ${title} panel`,
|
|
756
|
+
children: [
|
|
757
|
+
/* @__PURE__ */ jsx11("span", { className: cn("shrink-0", iconRotation), children: /* @__PURE__ */ jsx11(ChevronUpIcon, {}) }),
|
|
758
|
+
/* @__PURE__ */ jsx11("span", { className: cn(isVertical && "rotate-180"), children: title })
|
|
759
|
+
]
|
|
760
|
+
}
|
|
761
|
+
);
|
|
762
|
+
}
|
|
763
|
+
);
|
|
764
|
+
DataPanelTab.displayName = "DataPanelTab";
|
|
765
|
+
var DataPanel = React11.forwardRef(
|
|
766
|
+
({
|
|
767
|
+
open: controlledOpen,
|
|
768
|
+
onOpenChange,
|
|
769
|
+
mode: controlledMode,
|
|
770
|
+
onModeChange,
|
|
771
|
+
edge: controlledEdge,
|
|
772
|
+
onEdgeChange,
|
|
773
|
+
expanded: controlledExpanded,
|
|
774
|
+
onExpandedChange,
|
|
775
|
+
title,
|
|
776
|
+
subtitle,
|
|
777
|
+
children,
|
|
778
|
+
actions = [],
|
|
779
|
+
constraints = {},
|
|
780
|
+
disableClose = false,
|
|
781
|
+
disableResize = false,
|
|
782
|
+
disableDrag = false,
|
|
783
|
+
disableModeSwitch = false,
|
|
784
|
+
persistKey,
|
|
785
|
+
snapThreshold = 20,
|
|
786
|
+
detachThreshold = 40,
|
|
787
|
+
headerContent,
|
|
788
|
+
headerActions,
|
|
789
|
+
footerContent,
|
|
790
|
+
footerActions,
|
|
791
|
+
className
|
|
792
|
+
}, ref) => {
|
|
793
|
+
const resolvedConstraints = { ...DEFAULT_CONSTRAINTS, ...constraints };
|
|
794
|
+
const [internalOpen, setInternalOpen] = React11.useState(true);
|
|
795
|
+
const [internalMode, setInternalMode] = React11.useState(DEFAULT_PANEL_STATE.mode);
|
|
796
|
+
const [internalEdge, setInternalEdge] = React11.useState(DEFAULT_PANEL_STATE.edge);
|
|
797
|
+
const [internalExpanded, setInternalExpanded] = React11.useState(
|
|
798
|
+
DEFAULT_PANEL_STATE.isExpanded
|
|
799
|
+
);
|
|
800
|
+
const [detachedPosition, setDetachedPosition] = React11.useState(
|
|
801
|
+
DEFAULT_PANEL_STATE.detachedPosition
|
|
802
|
+
);
|
|
803
|
+
const [detachedSize, setDetachedSize] = React11.useState(DEFAULT_PANEL_STATE.detachedSize);
|
|
804
|
+
const [pinnedSize, setPinnedSize] = React11.useState(DEFAULT_PANEL_STATE.pinnedSize);
|
|
805
|
+
const [isDragging, setIsDragging] = React11.useState(false);
|
|
806
|
+
const [isResizing, setIsResizing] = React11.useState(false);
|
|
807
|
+
const [snapPreview, setSnapPreview] = React11.useState(null);
|
|
808
|
+
const [isPinnedDragging, setIsPinnedDragging] = React11.useState(false);
|
|
809
|
+
const [pullOffset, setPullOffset] = React11.useState(0);
|
|
810
|
+
const dragStateRef = React11.useRef({
|
|
811
|
+
startPosition: { x: 0, y: 0 },
|
|
812
|
+
startPanelPosition: { x: 0, y: 0 }
|
|
813
|
+
});
|
|
814
|
+
const pinnedDragStateRef = React11.useRef({
|
|
815
|
+
startPosition: { x: 0, y: 0 },
|
|
816
|
+
hasDetached: false
|
|
817
|
+
});
|
|
818
|
+
const resizeStateRef = React11.useRef({
|
|
819
|
+
handle: null,
|
|
820
|
+
startPosition: { x: 0, y: 0 },
|
|
821
|
+
startSize: { width: 0, height: 0 },
|
|
822
|
+
startPanelPosition: { x: 0, y: 0 }
|
|
823
|
+
});
|
|
824
|
+
const open = controlledOpen ?? internalOpen;
|
|
825
|
+
const mode = controlledMode ?? internalMode;
|
|
826
|
+
const edge = controlledEdge ?? internalEdge;
|
|
827
|
+
const isExpanded = controlledExpanded ?? internalExpanded;
|
|
828
|
+
const saveTimeoutRef = React11.useRef(null);
|
|
829
|
+
const debouncedSave = React11.useCallback(() => {
|
|
830
|
+
if (!persistKey) return;
|
|
831
|
+
if (saveTimeoutRef.current) {
|
|
832
|
+
clearTimeout(saveTimeoutRef.current);
|
|
833
|
+
}
|
|
834
|
+
saveTimeoutRef.current = setTimeout(() => {
|
|
835
|
+
savePanelState(persistKey, {
|
|
836
|
+
mode: internalMode,
|
|
837
|
+
variant: "full",
|
|
838
|
+
edge: internalEdge,
|
|
839
|
+
isExpanded: internalExpanded,
|
|
840
|
+
detachedPosition,
|
|
841
|
+
detachedSize,
|
|
842
|
+
pinnedSize,
|
|
843
|
+
cardSnapIndex: 0
|
|
844
|
+
});
|
|
845
|
+
}, 300);
|
|
846
|
+
}, [
|
|
847
|
+
persistKey,
|
|
848
|
+
internalMode,
|
|
849
|
+
internalEdge,
|
|
850
|
+
internalExpanded,
|
|
851
|
+
detachedPosition,
|
|
852
|
+
detachedSize,
|
|
853
|
+
pinnedSize
|
|
854
|
+
]);
|
|
855
|
+
React11.useEffect(() => {
|
|
856
|
+
if (!persistKey) return;
|
|
857
|
+
const persisted = loadPanelState(persistKey);
|
|
858
|
+
if (persisted) {
|
|
859
|
+
if (controlledMode === void 0 && persisted.mode) setInternalMode(persisted.mode);
|
|
860
|
+
if (controlledEdge === void 0 && persisted.edge) setInternalEdge(persisted.edge);
|
|
861
|
+
if (controlledExpanded === void 0 && persisted.isExpanded !== void 0)
|
|
862
|
+
setInternalExpanded(persisted.isExpanded);
|
|
863
|
+
if (persisted.detachedPosition) setDetachedPosition(persisted.detachedPosition);
|
|
864
|
+
if (persisted.detachedSize) setDetachedSize(persisted.detachedSize);
|
|
865
|
+
if (persisted.pinnedSize) setPinnedSize(persisted.pinnedSize);
|
|
866
|
+
}
|
|
867
|
+
}, [persistKey, controlledMode, controlledEdge, controlledExpanded]);
|
|
868
|
+
React11.useEffect(() => {
|
|
869
|
+
if (mode === "detached" && detachedPosition.x === DEFAULT_PANEL_STATE.detachedPosition.x && detachedPosition.y === DEFAULT_PANEL_STATE.detachedPosition.y) {
|
|
870
|
+
setDetachedPosition(
|
|
871
|
+
getInitialDetachedPosition(detachedSize, window.innerWidth, window.innerHeight)
|
|
872
|
+
);
|
|
873
|
+
}
|
|
874
|
+
}, [mode, detachedPosition, detachedSize]);
|
|
875
|
+
const handleOpenChange = (newOpen) => {
|
|
876
|
+
if (controlledOpen === void 0) {
|
|
877
|
+
setInternalOpen(newOpen);
|
|
878
|
+
}
|
|
879
|
+
onOpenChange?.(newOpen);
|
|
880
|
+
};
|
|
881
|
+
const handleModeChange = (newMode) => {
|
|
882
|
+
if (controlledMode === void 0) {
|
|
883
|
+
setInternalMode(newMode);
|
|
884
|
+
}
|
|
885
|
+
onModeChange?.(newMode);
|
|
886
|
+
debouncedSave();
|
|
887
|
+
};
|
|
888
|
+
const handleEdgeChange = (newEdge) => {
|
|
889
|
+
if (controlledEdge === void 0) {
|
|
890
|
+
setInternalEdge(newEdge);
|
|
891
|
+
}
|
|
892
|
+
onEdgeChange?.(newEdge);
|
|
893
|
+
debouncedSave();
|
|
894
|
+
};
|
|
895
|
+
const handleExpandedChange = (newExpanded) => {
|
|
896
|
+
if (controlledExpanded === void 0) {
|
|
897
|
+
setInternalExpanded(newExpanded);
|
|
898
|
+
}
|
|
899
|
+
onExpandedChange?.(newExpanded);
|
|
900
|
+
debouncedSave();
|
|
901
|
+
};
|
|
902
|
+
const handleDragStart = (e) => {
|
|
903
|
+
if (disableDrag) return;
|
|
904
|
+
const target = e.target;
|
|
905
|
+
if (!target.closest("[data-panel-header]")) return;
|
|
906
|
+
const pos = getPointerPosition(e.nativeEvent);
|
|
907
|
+
if (mode === "detached") {
|
|
908
|
+
setIsDragging(true);
|
|
909
|
+
dragStateRef.current = {
|
|
910
|
+
startPosition: pos,
|
|
911
|
+
startPanelPosition: { ...detachedPosition }
|
|
912
|
+
};
|
|
913
|
+
document.body.style.cursor = "grabbing";
|
|
914
|
+
document.body.style.userSelect = "none";
|
|
915
|
+
} else if (mode === "pinned" && detachThreshold > 0) {
|
|
916
|
+
setIsPinnedDragging(true);
|
|
917
|
+
pinnedDragStateRef.current = {
|
|
918
|
+
startPosition: pos,
|
|
919
|
+
hasDetached: false
|
|
920
|
+
};
|
|
921
|
+
setPullOffset(0);
|
|
922
|
+
document.body.style.cursor = "grab";
|
|
923
|
+
document.body.style.userSelect = "none";
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
const handleDragMove = React11.useCallback(
|
|
927
|
+
(e) => {
|
|
928
|
+
if (!isDragging) return;
|
|
929
|
+
const currentPos = getPointerPosition(e);
|
|
930
|
+
const newPosition = calculateDragPosition(
|
|
931
|
+
{
|
|
932
|
+
isDragging: true,
|
|
933
|
+
startPosition: dragStateRef.current.startPosition,
|
|
934
|
+
startPanelPosition: dragStateRef.current.startPanelPosition,
|
|
935
|
+
currentPosition: currentPos
|
|
936
|
+
},
|
|
937
|
+
currentPos
|
|
938
|
+
);
|
|
939
|
+
const snap = detectEdgeSnap(
|
|
940
|
+
newPosition,
|
|
941
|
+
detachedSize,
|
|
942
|
+
window.innerWidth,
|
|
943
|
+
window.innerHeight,
|
|
944
|
+
snapThreshold
|
|
945
|
+
);
|
|
946
|
+
setSnapPreview(snap.shouldSnap ? snap.edge : null);
|
|
947
|
+
setDetachedPosition(
|
|
948
|
+
constrainPosition(newPosition, detachedSize, window.innerWidth, window.innerHeight)
|
|
949
|
+
);
|
|
950
|
+
},
|
|
951
|
+
[isDragging, detachedSize, snapThreshold]
|
|
952
|
+
);
|
|
953
|
+
const handleDragEnd = React11.useCallback(() => {
|
|
954
|
+
if (!isDragging) return;
|
|
955
|
+
setIsDragging(false);
|
|
956
|
+
document.body.style.cursor = "";
|
|
957
|
+
document.body.style.userSelect = "";
|
|
958
|
+
if (snapPreview) {
|
|
959
|
+
handleModeChange("pinned");
|
|
960
|
+
handleEdgeChange(snapPreview);
|
|
961
|
+
setSnapPreview(null);
|
|
962
|
+
} else {
|
|
963
|
+
debouncedSave();
|
|
964
|
+
}
|
|
965
|
+
}, [isDragging, snapPreview, handleModeChange, handleEdgeChange, debouncedSave]);
|
|
966
|
+
const handlePinnedDragMove = React11.useCallback(
|
|
967
|
+
(e) => {
|
|
968
|
+
if (!isPinnedDragging) return;
|
|
969
|
+
const currentPos = getPointerPosition(e);
|
|
970
|
+
const distance = calculateDetachDistance(
|
|
971
|
+
pinnedDragStateRef.current.startPosition,
|
|
972
|
+
currentPos,
|
|
973
|
+
edge
|
|
974
|
+
);
|
|
975
|
+
if (distance >= detachThreshold && !pinnedDragStateRef.current.hasDetached) {
|
|
976
|
+
pinnedDragStateRef.current.hasDetached = true;
|
|
977
|
+
setIsPinnedDragging(false);
|
|
978
|
+
setPullOffset(0);
|
|
979
|
+
const newPosition = calculateDetachedPositionFromPinned(
|
|
980
|
+
currentPos,
|
|
981
|
+
pinnedSize,
|
|
982
|
+
detachedSize,
|
|
983
|
+
edge,
|
|
984
|
+
window.innerWidth,
|
|
985
|
+
window.innerHeight
|
|
986
|
+
);
|
|
987
|
+
setDetachedPosition(newPosition);
|
|
988
|
+
handleModeChange("detached");
|
|
989
|
+
setIsDragging(true);
|
|
990
|
+
dragStateRef.current = {
|
|
991
|
+
startPosition: currentPos,
|
|
992
|
+
startPanelPosition: newPosition
|
|
993
|
+
};
|
|
994
|
+
document.body.style.cursor = "grabbing";
|
|
995
|
+
} else if (!pinnedDragStateRef.current.hasDetached) {
|
|
996
|
+
const pull = calculatePullOffset(distance, detachThreshold);
|
|
997
|
+
setPullOffset(pull);
|
|
998
|
+
}
|
|
999
|
+
},
|
|
1000
|
+
[isPinnedDragging, edge, detachThreshold, pinnedSize, detachedSize, handleModeChange]
|
|
1001
|
+
);
|
|
1002
|
+
const handlePinnedDragEnd = React11.useCallback(() => {
|
|
1003
|
+
if (!isPinnedDragging) return;
|
|
1004
|
+
setIsPinnedDragging(false);
|
|
1005
|
+
setPullOffset(0);
|
|
1006
|
+
document.body.style.cursor = "";
|
|
1007
|
+
document.body.style.userSelect = "";
|
|
1008
|
+
}, [isPinnedDragging]);
|
|
1009
|
+
const handleResizeStart = (e, handle) => {
|
|
1010
|
+
if (disableResize) return;
|
|
1011
|
+
e.preventDefault();
|
|
1012
|
+
e.stopPropagation();
|
|
1013
|
+
setIsResizing(true);
|
|
1014
|
+
const pos = getPointerPosition(e.nativeEvent);
|
|
1015
|
+
resizeStateRef.current = {
|
|
1016
|
+
handle,
|
|
1017
|
+
startPosition: pos,
|
|
1018
|
+
startSize: mode === "detached" ? { ...detachedSize } : { width: pinnedSize, height: pinnedSize },
|
|
1019
|
+
startPanelPosition: { ...detachedPosition }
|
|
1020
|
+
};
|
|
1021
|
+
document.body.style.cursor = getResizeCursor(handle);
|
|
1022
|
+
document.body.style.userSelect = "none";
|
|
1023
|
+
};
|
|
1024
|
+
const handleResizeMove = React11.useCallback(
|
|
1025
|
+
(e) => {
|
|
1026
|
+
if (!isResizing || !resizeStateRef.current.handle) return;
|
|
1027
|
+
const currentPos = getPointerPosition(e);
|
|
1028
|
+
const { handle, startPosition, startSize, startPanelPosition } = resizeStateRef.current;
|
|
1029
|
+
if (mode === "detached") {
|
|
1030
|
+
const result = calculateResize(
|
|
1031
|
+
{
|
|
1032
|
+
isResizing: true,
|
|
1033
|
+
handle,
|
|
1034
|
+
startPosition,
|
|
1035
|
+
startSize,
|
|
1036
|
+
startPanelPosition
|
|
1037
|
+
},
|
|
1038
|
+
currentPos
|
|
1039
|
+
);
|
|
1040
|
+
const constrainedSize = constrainSize(result.size, resolvedConstraints);
|
|
1041
|
+
const constrainedPosition = constrainPosition(
|
|
1042
|
+
result.position,
|
|
1043
|
+
constrainedSize,
|
|
1044
|
+
window.innerWidth,
|
|
1045
|
+
window.innerHeight
|
|
1046
|
+
);
|
|
1047
|
+
setDetachedSize(constrainedSize);
|
|
1048
|
+
setDetachedPosition(constrainedPosition);
|
|
1049
|
+
} else {
|
|
1050
|
+
const delta = isHorizontalEdge(edge) ? currentPos.x - startPosition.x : currentPos.y - startPosition.y;
|
|
1051
|
+
const direction = edge === "left" || edge === "top" ? 1 : -1;
|
|
1052
|
+
const newSize = startSize.width + delta * direction;
|
|
1053
|
+
if (isHorizontalEdge(edge)) {
|
|
1054
|
+
setPinnedSize(constrainPinnedWidth(newSize, resolvedConstraints, window.innerWidth));
|
|
1055
|
+
} else {
|
|
1056
|
+
setPinnedSize(constrainPinnedHeight(newSize, resolvedConstraints, window.innerHeight));
|
|
1057
|
+
}
|
|
1058
|
+
}
|
|
1059
|
+
},
|
|
1060
|
+
[isResizing, mode, edge, resolvedConstraints]
|
|
1061
|
+
);
|
|
1062
|
+
const handleResizeEnd = React11.useCallback(() => {
|
|
1063
|
+
if (!isResizing) return;
|
|
1064
|
+
setIsResizing(false);
|
|
1065
|
+
resizeStateRef.current.handle = null;
|
|
1066
|
+
document.body.style.cursor = "";
|
|
1067
|
+
document.body.style.userSelect = "";
|
|
1068
|
+
debouncedSave();
|
|
1069
|
+
}, [isResizing, debouncedSave]);
|
|
1070
|
+
React11.useEffect(() => {
|
|
1071
|
+
if (isDragging) {
|
|
1072
|
+
window.addEventListener("pointermove", handleDragMove);
|
|
1073
|
+
window.addEventListener("pointerup", handleDragEnd);
|
|
1074
|
+
return () => {
|
|
1075
|
+
window.removeEventListener("pointermove", handleDragMove);
|
|
1076
|
+
window.removeEventListener("pointerup", handleDragEnd);
|
|
1077
|
+
};
|
|
1078
|
+
}
|
|
1079
|
+
}, [isDragging, handleDragMove, handleDragEnd]);
|
|
1080
|
+
React11.useEffect(() => {
|
|
1081
|
+
if (isPinnedDragging) {
|
|
1082
|
+
window.addEventListener("pointermove", handlePinnedDragMove);
|
|
1083
|
+
window.addEventListener("pointerup", handlePinnedDragEnd);
|
|
1084
|
+
return () => {
|
|
1085
|
+
window.removeEventListener("pointermove", handlePinnedDragMove);
|
|
1086
|
+
window.removeEventListener("pointerup", handlePinnedDragEnd);
|
|
1087
|
+
};
|
|
1088
|
+
}
|
|
1089
|
+
}, [isPinnedDragging, handlePinnedDragMove, handlePinnedDragEnd]);
|
|
1090
|
+
React11.useEffect(() => {
|
|
1091
|
+
if (isResizing) {
|
|
1092
|
+
window.addEventListener("pointermove", handleResizeMove);
|
|
1093
|
+
window.addEventListener("pointerup", handleResizeEnd);
|
|
1094
|
+
return () => {
|
|
1095
|
+
window.removeEventListener("pointermove", handleResizeMove);
|
|
1096
|
+
window.removeEventListener("pointerup", handleResizeEnd);
|
|
1097
|
+
};
|
|
1098
|
+
}
|
|
1099
|
+
}, [isResizing, handleResizeMove, handleResizeEnd]);
|
|
1100
|
+
const panelStyles = React11.useMemo(() => {
|
|
1101
|
+
if (mode === "pinned") {
|
|
1102
|
+
const baseStyles = getPinnedPositionStyles(edge, pinnedSize);
|
|
1103
|
+
if (pullOffset > 0) {
|
|
1104
|
+
const transform = getPinnedPullTransform(pullOffset, edge);
|
|
1105
|
+
return { ...baseStyles, transform };
|
|
1106
|
+
}
|
|
1107
|
+
return baseStyles;
|
|
1108
|
+
}
|
|
1109
|
+
return getDetachedPositionStyles(detachedPosition, detachedSize);
|
|
1110
|
+
}, [mode, edge, pinnedSize, detachedPosition, detachedSize, pullOffset]);
|
|
1111
|
+
const pinnedClasses = {
|
|
1112
|
+
left: "left-0 top-0 bottom-0 border-r",
|
|
1113
|
+
right: "right-0 top-0 bottom-0 border-l",
|
|
1114
|
+
top: "top-0 left-0 right-0 border-b",
|
|
1115
|
+
bottom: "bottom-0 left-0 right-0 border-t"
|
|
1116
|
+
}[edge];
|
|
1117
|
+
const animationClass = mode === "pinned" ? {
|
|
1118
|
+
left: "animate-slide-right",
|
|
1119
|
+
right: "animate-slide-left",
|
|
1120
|
+
top: "animate-slide-down",
|
|
1121
|
+
bottom: "animate-slide-up"
|
|
1122
|
+
}[edge] : "animate-scale-in";
|
|
1123
|
+
if (!open) return null;
|
|
1124
|
+
if (!isExpanded && mode === "pinned") {
|
|
1125
|
+
return /* @__PURE__ */ jsx11(DataPanelTab, { title, edge, onClick: () => handleExpandedChange(true) });
|
|
1126
|
+
}
|
|
1127
|
+
return /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1128
|
+
snapPreview && /* @__PURE__ */ jsx11(
|
|
1129
|
+
"div",
|
|
1130
|
+
{
|
|
1131
|
+
className: cn(
|
|
1132
|
+
"fixed z-40 bg-primary/20 border-2 border-primary border-dashed transition-all",
|
|
1133
|
+
snapPreview === "left" && "left-0 top-0 bottom-0 w-80",
|
|
1134
|
+
snapPreview === "right" && "right-0 top-0 bottom-0 w-80",
|
|
1135
|
+
snapPreview === "top" && "top-0 left-0 right-0 h-64",
|
|
1136
|
+
snapPreview === "bottom" && "bottom-0 left-0 right-0 h-64"
|
|
1137
|
+
)
|
|
1138
|
+
}
|
|
1139
|
+
),
|
|
1140
|
+
/* @__PURE__ */ jsxs3(
|
|
1141
|
+
"div",
|
|
1142
|
+
{
|
|
1143
|
+
ref,
|
|
1144
|
+
className: cn(
|
|
1145
|
+
"fixed z-50 flex flex-col bg-background border border-border shadow-lg overflow-hidden",
|
|
1146
|
+
mode === "pinned" && pinnedClasses,
|
|
1147
|
+
mode === "detached" && "rounded-lg",
|
|
1148
|
+
isPinnedDragging && pullOffset > 0 && "shadow-2xl",
|
|
1149
|
+
animationClass,
|
|
1150
|
+
className
|
|
1151
|
+
),
|
|
1152
|
+
style: panelStyles,
|
|
1153
|
+
role: mode === "detached" ? "dialog" : void 0,
|
|
1154
|
+
"aria-modal": mode === "detached" ? "true" : void 0,
|
|
1155
|
+
"aria-labelledby": title ? "data-panel-title" : void 0,
|
|
1156
|
+
onPointerDown: handleDragStart,
|
|
1157
|
+
children: [
|
|
1158
|
+
/* @__PURE__ */ jsx11(
|
|
1159
|
+
DataPanelHeader,
|
|
1160
|
+
{
|
|
1161
|
+
title,
|
|
1162
|
+
subtitle,
|
|
1163
|
+
mode,
|
|
1164
|
+
edge,
|
|
1165
|
+
isExpanded,
|
|
1166
|
+
onModeChange: handleModeChange,
|
|
1167
|
+
onEdgeChange: handleEdgeChange,
|
|
1168
|
+
onExpandedChange: handleExpandedChange,
|
|
1169
|
+
onClose: () => handleOpenChange(false),
|
|
1170
|
+
disableClose,
|
|
1171
|
+
disableModeSwitch,
|
|
1172
|
+
headerContent,
|
|
1173
|
+
headerActions,
|
|
1174
|
+
draggable: !disableDrag && (mode === "detached" || mode === "pinned" && detachThreshold > 0)
|
|
1175
|
+
}
|
|
1176
|
+
),
|
|
1177
|
+
/* @__PURE__ */ jsx11(DataPanelContent, { children }),
|
|
1178
|
+
/* @__PURE__ */ jsx11(
|
|
1179
|
+
DataPanelFooter,
|
|
1180
|
+
{
|
|
1181
|
+
actions,
|
|
1182
|
+
footerContent,
|
|
1183
|
+
footerActions
|
|
1184
|
+
}
|
|
1185
|
+
),
|
|
1186
|
+
!disableResize && /* @__PURE__ */ jsx11(Fragment, { children: mode === "detached" ? /* @__PURE__ */ jsxs3(Fragment, { children: [
|
|
1187
|
+
/* @__PURE__ */ jsx11(
|
|
1188
|
+
"div",
|
|
1189
|
+
{
|
|
1190
|
+
className: "resize-handle resize-handle--horizontal resize-handle--top",
|
|
1191
|
+
onPointerDown: (e) => handleResizeStart(e, "top")
|
|
1192
|
+
}
|
|
1193
|
+
),
|
|
1194
|
+
/* @__PURE__ */ jsx11(
|
|
1195
|
+
"div",
|
|
1196
|
+
{
|
|
1197
|
+
className: "resize-handle resize-handle--horizontal resize-handle--bottom",
|
|
1198
|
+
onPointerDown: (e) => handleResizeStart(e, "bottom")
|
|
1199
|
+
}
|
|
1200
|
+
),
|
|
1201
|
+
/* @__PURE__ */ jsx11(
|
|
1202
|
+
"div",
|
|
1203
|
+
{
|
|
1204
|
+
className: "resize-handle resize-handle--vertical resize-handle--left",
|
|
1205
|
+
onPointerDown: (e) => handleResizeStart(e, "left")
|
|
1206
|
+
}
|
|
1207
|
+
),
|
|
1208
|
+
/* @__PURE__ */ jsx11(
|
|
1209
|
+
"div",
|
|
1210
|
+
{
|
|
1211
|
+
className: "resize-handle resize-handle--vertical resize-handle--right",
|
|
1212
|
+
onPointerDown: (e) => handleResizeStart(e, "right")
|
|
1213
|
+
}
|
|
1214
|
+
),
|
|
1215
|
+
/* @__PURE__ */ jsx11(
|
|
1216
|
+
"div",
|
|
1217
|
+
{
|
|
1218
|
+
className: "resize-handle resize-handle--corner resize-handle--top-left",
|
|
1219
|
+
style: { "--handle-radius": "6px" },
|
|
1220
|
+
onPointerDown: (e) => handleResizeStart(e, "top-left")
|
|
1221
|
+
}
|
|
1222
|
+
),
|
|
1223
|
+
/* @__PURE__ */ jsx11(
|
|
1224
|
+
"div",
|
|
1225
|
+
{
|
|
1226
|
+
className: "resize-handle resize-handle--corner resize-handle--top-right",
|
|
1227
|
+
style: { "--handle-radius": "6px" },
|
|
1228
|
+
onPointerDown: (e) => handleResizeStart(e, "top-right")
|
|
1229
|
+
}
|
|
1230
|
+
),
|
|
1231
|
+
/* @__PURE__ */ jsx11(
|
|
1232
|
+
"div",
|
|
1233
|
+
{
|
|
1234
|
+
className: "resize-handle resize-handle--corner resize-handle--bottom-left",
|
|
1235
|
+
style: { "--handle-radius": "6px" },
|
|
1236
|
+
onPointerDown: (e) => handleResizeStart(e, "bottom-left")
|
|
1237
|
+
}
|
|
1238
|
+
),
|
|
1239
|
+
/* @__PURE__ */ jsx11(
|
|
1240
|
+
"div",
|
|
1241
|
+
{
|
|
1242
|
+
className: "resize-handle resize-handle--corner resize-handle--bottom-right",
|
|
1243
|
+
style: { "--handle-radius": "6px" },
|
|
1244
|
+
onPointerDown: (e) => handleResizeStart(e, "bottom-right")
|
|
1245
|
+
}
|
|
1246
|
+
)
|
|
1247
|
+
] }) : /* @__PURE__ */ jsx11(
|
|
1248
|
+
"div",
|
|
1249
|
+
{
|
|
1250
|
+
className: cn(
|
|
1251
|
+
"resize-handle",
|
|
1252
|
+
edge === "left" && "resize-handle--vertical resize-handle--right",
|
|
1253
|
+
edge === "right" && "resize-handle--vertical resize-handle--left",
|
|
1254
|
+
edge === "top" && "resize-handle--horizontal resize-handle--bottom",
|
|
1255
|
+
edge === "bottom" && "resize-handle--horizontal resize-handle--top"
|
|
1256
|
+
),
|
|
1257
|
+
onPointerDown: (e) => handleResizeStart(e, getPinnedResizeHandle(edge))
|
|
1258
|
+
}
|
|
1259
|
+
) })
|
|
1260
|
+
]
|
|
1261
|
+
}
|
|
1262
|
+
)
|
|
1263
|
+
] });
|
|
1264
|
+
}
|
|
1265
|
+
);
|
|
1266
|
+
DataPanel.displayName = "DataPanel";
|
|
366
1267
|
export {
|
|
367
1268
|
Avatar,
|
|
368
1269
|
AvatarFallback,
|
|
@@ -375,6 +1276,11 @@ export {
|
|
|
375
1276
|
CardFooter,
|
|
376
1277
|
CardHeader,
|
|
377
1278
|
CardTitle,
|
|
1279
|
+
DataPanel,
|
|
1280
|
+
DataPanelContent,
|
|
1281
|
+
DataPanelFooter,
|
|
1282
|
+
DataPanelHeader,
|
|
1283
|
+
DataPanelTab,
|
|
378
1284
|
Dialog,
|
|
379
1285
|
DialogClose,
|
|
380
1286
|
DialogContent,
|
|
@@ -396,5 +1302,6 @@ export {
|
|
|
396
1302
|
pageHeaderActionsVariants,
|
|
397
1303
|
pageHeaderContainerVariants,
|
|
398
1304
|
pageHeaderSubtitleVariants,
|
|
399
|
-
pageHeaderTitleVariants
|
|
1305
|
+
pageHeaderTitleVariants,
|
|
1306
|
+
useDataPanel
|
|
400
1307
|
};
|