@bmsuisse/ui 0.7.0 → 0.11.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/index.d.ts +140 -1
- package/dist/index.js +669 -128
- package/dist/index.js.map +1 -1
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -625,7 +625,7 @@ DropdownMenuSubContent.displayName = "DropdownMenuSubContent";
|
|
|
625
625
|
import { cva as cva3 } from "class-variance-authority";
|
|
626
626
|
import * as DialogPrimitive2 from "@radix-ui/react-dialog";
|
|
627
627
|
import { X as X2 } from "lucide-react";
|
|
628
|
-
import { forwardRef as forwardRef15 } from "react";
|
|
628
|
+
import { forwardRef as forwardRef15, useEffect, useRef, useState } from "react";
|
|
629
629
|
import { jsx as jsx18, jsxs as jsxs5 } from "react/jsx-runtime";
|
|
630
630
|
var Sheet = DialogPrimitive2.Root;
|
|
631
631
|
var SheetTrigger = DialogPrimitive2.Trigger;
|
|
@@ -641,7 +641,7 @@ var SheetOverlay = forwardRef15(({ className, ...props }, ref) => /* @__PURE__ *
|
|
|
641
641
|
));
|
|
642
642
|
SheetOverlay.displayName = "SheetOverlay";
|
|
643
643
|
var sheetVariants = cva3(
|
|
644
|
-
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition-transform duration-300 ease-in-out",
|
|
644
|
+
"fixed z-50 gap-4 bg-background p-6 shadow-lg transition-[transform,box-shadow] duration-300 ease-in-out",
|
|
645
645
|
{
|
|
646
646
|
variants: {
|
|
647
647
|
side: {
|
|
@@ -656,17 +656,84 @@ var sheetVariants = cva3(
|
|
|
656
656
|
}
|
|
657
657
|
}
|
|
658
658
|
);
|
|
659
|
+
var enterFromTransform = {
|
|
660
|
+
top: "translateY(-100%)",
|
|
661
|
+
bottom: "translateY(100%)",
|
|
662
|
+
left: "translateX(-100%)",
|
|
663
|
+
right: "translateX(100%)"
|
|
664
|
+
};
|
|
659
665
|
var SheetContent = forwardRef15(
|
|
660
|
-
({ side = "right", className, children, ...props }, ref) =>
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
666
|
+
({ side = "right", className, children, resizable = false, style: styleProp, ...props }, ref) => {
|
|
667
|
+
const [entered, setEntered] = useState(false);
|
|
668
|
+
useEffect(() => {
|
|
669
|
+
const id = requestAnimationFrame(() => setEntered(true));
|
|
670
|
+
return () => cancelAnimationFrame(id);
|
|
671
|
+
}, []);
|
|
672
|
+
const [dragHeight, setDragHeight] = useState(null);
|
|
673
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
674
|
+
const dragStart = useRef(null);
|
|
675
|
+
const canResize = resizable && side === "bottom";
|
|
676
|
+
const onHandlePointerDown = (e) => {
|
|
677
|
+
const popup = e.currentTarget.closest('[data-slot="sheet-content"]');
|
|
678
|
+
if (!(popup instanceof HTMLElement)) return;
|
|
679
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
680
|
+
dragStart.current = { y: e.clientY, height: popup.getBoundingClientRect().height };
|
|
681
|
+
setIsDragging(true);
|
|
682
|
+
};
|
|
683
|
+
const onHandlePointerMove = (e) => {
|
|
684
|
+
if (!dragStart.current) return;
|
|
685
|
+
const delta = dragStart.current.y - e.clientY;
|
|
686
|
+
const next = dragStart.current.height + delta;
|
|
687
|
+
const min = window.innerHeight * 0.3;
|
|
688
|
+
const max = window.innerHeight * 0.95;
|
|
689
|
+
setDragHeight(Math.min(max, Math.max(min, next)));
|
|
690
|
+
};
|
|
691
|
+
const onHandlePointerUp = () => {
|
|
692
|
+
dragStart.current = null;
|
|
693
|
+
setIsDragging(false);
|
|
694
|
+
};
|
|
695
|
+
const style = entered ? { ...styleProp, ...canResize && dragHeight != null ? { height: dragHeight, maxHeight: dragHeight } : null } : { ...styleProp, transform: enterFromTransform[side ?? "right"] };
|
|
696
|
+
return /* @__PURE__ */ jsxs5(SheetPortal, { children: [
|
|
697
|
+
/* @__PURE__ */ jsx18(SheetOverlay, {}),
|
|
698
|
+
/* @__PURE__ */ jsxs5(
|
|
699
|
+
DialogPrimitive2.Content,
|
|
700
|
+
{
|
|
701
|
+
ref,
|
|
702
|
+
"data-slot": "sheet-content",
|
|
703
|
+
style,
|
|
704
|
+
className: cn(sheetVariants({ side }), isDragging && "shadow-2xl", className),
|
|
705
|
+
...props,
|
|
706
|
+
children: [
|
|
707
|
+
canResize && /* @__PURE__ */ jsx18(
|
|
708
|
+
"div",
|
|
709
|
+
{
|
|
710
|
+
"data-testid": "sheet-drag-handle",
|
|
711
|
+
className: "group -mb-2 flex shrink-0 cursor-grab touch-none justify-center py-2 active:cursor-grabbing",
|
|
712
|
+
onPointerDown: onHandlePointerDown,
|
|
713
|
+
onPointerMove: onHandlePointerMove,
|
|
714
|
+
onPointerUp: onHandlePointerUp,
|
|
715
|
+
onPointerCancel: onHandlePointerUp,
|
|
716
|
+
children: /* @__PURE__ */ jsx18(
|
|
717
|
+
"div",
|
|
718
|
+
{
|
|
719
|
+
className: cn(
|
|
720
|
+
"h-1 w-10 rounded-full bg-muted-foreground/30 transition-all duration-150 group-hover:w-14 group-hover:bg-muted-foreground/50",
|
|
721
|
+
isDragging && "w-14 bg-primary/60"
|
|
722
|
+
)
|
|
723
|
+
}
|
|
724
|
+
)
|
|
725
|
+
}
|
|
726
|
+
),
|
|
727
|
+
children,
|
|
728
|
+
/* @__PURE__ */ jsxs5(DialogPrimitive2.Close, { className: "absolute right-4 top-4 rounded-sm opacity-70 hover:opacity-100", children: [
|
|
729
|
+
/* @__PURE__ */ jsx18(X2, { className: "h-4 w-4" }),
|
|
730
|
+
/* @__PURE__ */ jsx18("span", { className: "sr-only", children: "Close" })
|
|
731
|
+
] })
|
|
732
|
+
]
|
|
733
|
+
}
|
|
734
|
+
)
|
|
735
|
+
] });
|
|
736
|
+
}
|
|
670
737
|
);
|
|
671
738
|
SheetContent.displayName = "SheetContent";
|
|
672
739
|
var SheetHeader = ({
|
|
@@ -730,7 +797,7 @@ var Modal = ({
|
|
|
730
797
|
] }) });
|
|
731
798
|
|
|
732
799
|
// src/patterns/modal/ConfirmDialog.tsx
|
|
733
|
-
import { useState } from "react";
|
|
800
|
+
import { useState as useState2 } from "react";
|
|
734
801
|
import { Fragment, jsx as jsx21, jsxs as jsxs7 } from "react/jsx-runtime";
|
|
735
802
|
var ConfirmDialog = ({
|
|
736
803
|
open,
|
|
@@ -744,7 +811,7 @@ var ConfirmDialog = ({
|
|
|
744
811
|
confirmTestId,
|
|
745
812
|
cancelTestId
|
|
746
813
|
}) => {
|
|
747
|
-
const [pending, setPending] =
|
|
814
|
+
const [pending, setPending] = useState2(false);
|
|
748
815
|
const handleCancel = () => {
|
|
749
816
|
onOpenChange(false);
|
|
750
817
|
};
|
|
@@ -785,7 +852,7 @@ var ConfirmDialog = ({
|
|
|
785
852
|
};
|
|
786
853
|
|
|
787
854
|
// src/patterns/modal/FormModal.tsx
|
|
788
|
-
import { useState as
|
|
855
|
+
import { useState as useState3 } from "react";
|
|
789
856
|
import { jsx as jsx22, jsxs as jsxs8 } from "react/jsx-runtime";
|
|
790
857
|
var FormModal = ({
|
|
791
858
|
open,
|
|
@@ -801,7 +868,7 @@ var FormModal = ({
|
|
|
801
868
|
submitTestId,
|
|
802
869
|
cancelTestId
|
|
803
870
|
}) => {
|
|
804
|
-
const [pending, setPending] =
|
|
871
|
+
const [pending, setPending] = useState3(false);
|
|
805
872
|
const handleSubmit = async (event) => {
|
|
806
873
|
event.preventDefault();
|
|
807
874
|
setPending(true);
|
|
@@ -837,9 +904,236 @@ var FormModal = ({
|
|
|
837
904
|
] }) }) });
|
|
838
905
|
};
|
|
839
906
|
|
|
907
|
+
// src/patterns/modal/ResponsivePanel.tsx
|
|
908
|
+
import { useRef as useRef2, useState as useState5 } from "react";
|
|
909
|
+
|
|
910
|
+
// src/lib/useMediaQuery.ts
|
|
911
|
+
import { useEffect as useEffect2, useState as useState4 } from "react";
|
|
912
|
+
function useMediaQuery(query) {
|
|
913
|
+
const [matches, setMatches] = useState4(
|
|
914
|
+
() => typeof window !== "undefined" ? window.matchMedia(query).matches : false
|
|
915
|
+
);
|
|
916
|
+
useEffect2(() => {
|
|
917
|
+
const mq = window.matchMedia(query);
|
|
918
|
+
setMatches(mq.matches);
|
|
919
|
+
const handler = (e) => setMatches(e.matches);
|
|
920
|
+
mq.addEventListener("change", handler);
|
|
921
|
+
return () => mq.removeEventListener("change", handler);
|
|
922
|
+
}, [query]);
|
|
923
|
+
return matches;
|
|
924
|
+
}
|
|
925
|
+
|
|
926
|
+
// src/patterns/modal/ResponsivePanel.tsx
|
|
927
|
+
import { jsx as jsx23, jsxs as jsxs9 } from "react/jsx-runtime";
|
|
928
|
+
var desktopSizeClasses = {
|
|
929
|
+
sm: "max-w-md",
|
|
930
|
+
md: "max-w-xl",
|
|
931
|
+
lg: "max-w-2xl",
|
|
932
|
+
xl: "max-w-4xl"
|
|
933
|
+
};
|
|
934
|
+
var drawerSizeClasses = {
|
|
935
|
+
sm: "max-h-[50vh]",
|
|
936
|
+
md: "max-h-[70vh]",
|
|
937
|
+
lg: "max-h-[90vh]",
|
|
938
|
+
xl: "max-h-[96vh]"
|
|
939
|
+
};
|
|
940
|
+
var MIN_PANEL_WIDTH = 320;
|
|
941
|
+
var MIN_PANEL_HEIGHT = 240;
|
|
942
|
+
var VIEWPORT_MARGIN = 16;
|
|
943
|
+
var cornerCursor = {
|
|
944
|
+
"top-left": "cursor-nwse-resize",
|
|
945
|
+
"bottom-right": "cursor-nwse-resize",
|
|
946
|
+
"top-right": "cursor-nesw-resize",
|
|
947
|
+
"bottom-left": "cursor-nesw-resize"
|
|
948
|
+
};
|
|
949
|
+
var cornerPosition = {
|
|
950
|
+
"top-left": "left-0 top-0",
|
|
951
|
+
"top-right": "right-0 top-0",
|
|
952
|
+
"bottom-left": "left-0 bottom-0",
|
|
953
|
+
"bottom-right": "right-0 bottom-0"
|
|
954
|
+
};
|
|
955
|
+
var cornerGripPosition = {
|
|
956
|
+
"top-left": "left-1 top-1 border-l-2 border-t-2",
|
|
957
|
+
"top-right": "right-1 top-1 border-r-2 border-t-2",
|
|
958
|
+
"bottom-left": "left-1 bottom-1 border-l-2 border-b-2",
|
|
959
|
+
"bottom-right": "right-1 bottom-1 border-r-2 border-b-2"
|
|
960
|
+
};
|
|
961
|
+
function useDesktopPanelGeometry() {
|
|
962
|
+
const [style, setStyle] = useState5(void 0);
|
|
963
|
+
const [isDragging, setIsDragging] = useState5(false);
|
|
964
|
+
const drag = useRef2(null);
|
|
965
|
+
const lastRect = useRef2(null);
|
|
966
|
+
const beginDrag = (mode, corner) => (e) => {
|
|
967
|
+
const content = e.currentTarget.closest('[data-slot="dialog-content"]');
|
|
968
|
+
if (!(content instanceof HTMLElement)) return;
|
|
969
|
+
e.currentTarget.setPointerCapture(e.pointerId);
|
|
970
|
+
const domRect = content.getBoundingClientRect();
|
|
971
|
+
const rect = lastRect.current ?? {
|
|
972
|
+
left: domRect.left,
|
|
973
|
+
top: domRect.top,
|
|
974
|
+
width: domRect.width,
|
|
975
|
+
height: domRect.height
|
|
976
|
+
};
|
|
977
|
+
drag.current = { mode, corner, x: e.clientX, y: e.clientY, rect };
|
|
978
|
+
setIsDragging(true);
|
|
979
|
+
};
|
|
980
|
+
const onPointerMove = (e) => {
|
|
981
|
+
const state = drag.current;
|
|
982
|
+
if (!state) return;
|
|
983
|
+
const deltaX = e.clientX - state.x;
|
|
984
|
+
const deltaY = e.clientY - state.y;
|
|
985
|
+
if (state.mode === "move") {
|
|
986
|
+
const maxLeft = window.innerWidth - VIEWPORT_MARGIN - state.rect.width;
|
|
987
|
+
const maxTop = window.innerHeight - VIEWPORT_MARGIN - state.rect.height;
|
|
988
|
+
const next2 = {
|
|
989
|
+
left: Math.min(maxLeft, Math.max(VIEWPORT_MARGIN, state.rect.left + deltaX)),
|
|
990
|
+
top: Math.min(maxTop, Math.max(VIEWPORT_MARGIN, state.rect.top + deltaY)),
|
|
991
|
+
width: state.rect.width,
|
|
992
|
+
height: state.rect.height
|
|
993
|
+
};
|
|
994
|
+
lastRect.current = next2;
|
|
995
|
+
setStyle({ position: "fixed", transform: "none", translate: "none", maxWidth: "none", maxHeight: "none", ...next2 });
|
|
996
|
+
return;
|
|
997
|
+
}
|
|
998
|
+
const corner = state.corner ?? "top-left";
|
|
999
|
+
const growsLeft = corner === "top-left" || corner === "bottom-left";
|
|
1000
|
+
const growsUp = corner === "top-left" || corner === "top-right";
|
|
1001
|
+
const right = state.rect.left + state.rect.width;
|
|
1002
|
+
const bottom = state.rect.top + state.rect.height;
|
|
1003
|
+
const maxWidth = window.innerWidth - VIEWPORT_MARGIN * 2;
|
|
1004
|
+
const maxHeight = window.innerHeight - VIEWPORT_MARGIN * 2;
|
|
1005
|
+
let width = Math.max(
|
|
1006
|
+
MIN_PANEL_WIDTH,
|
|
1007
|
+
Math.min(maxWidth, growsLeft ? state.rect.width - deltaX : state.rect.width + deltaX)
|
|
1008
|
+
);
|
|
1009
|
+
let height = Math.max(
|
|
1010
|
+
MIN_PANEL_HEIGHT,
|
|
1011
|
+
Math.min(maxHeight, growsUp ? state.rect.height - deltaY : state.rect.height + deltaY)
|
|
1012
|
+
);
|
|
1013
|
+
const left = growsLeft ? right - Math.min(width, right - VIEWPORT_MARGIN) : state.rect.left;
|
|
1014
|
+
width = growsLeft ? right - left : Math.min(width, window.innerWidth - VIEWPORT_MARGIN - state.rect.left);
|
|
1015
|
+
const top = growsUp ? bottom - Math.min(height, bottom - VIEWPORT_MARGIN) : state.rect.top;
|
|
1016
|
+
height = growsUp ? bottom - top : Math.min(height, window.innerHeight - VIEWPORT_MARGIN - state.rect.top);
|
|
1017
|
+
const next = { left, top, width, height };
|
|
1018
|
+
lastRect.current = next;
|
|
1019
|
+
setStyle({ position: "fixed", transform: "none", translate: "none", maxWidth: "none", maxHeight: "none", ...next });
|
|
1020
|
+
};
|
|
1021
|
+
const onPointerUp = () => {
|
|
1022
|
+
drag.current = null;
|
|
1023
|
+
setIsDragging(false);
|
|
1024
|
+
};
|
|
1025
|
+
return {
|
|
1026
|
+
style,
|
|
1027
|
+
isDragging,
|
|
1028
|
+
startMove: beginDrag("move"),
|
|
1029
|
+
startResize: (corner) => beginDrag("resize", corner),
|
|
1030
|
+
onPointerMove,
|
|
1031
|
+
onPointerUp
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
var ResponsivePanel = ({
|
|
1035
|
+
open,
|
|
1036
|
+
onOpenChange,
|
|
1037
|
+
title,
|
|
1038
|
+
description,
|
|
1039
|
+
children,
|
|
1040
|
+
footer,
|
|
1041
|
+
className,
|
|
1042
|
+
breakpoint = "(min-width: 1024px)",
|
|
1043
|
+
size = "lg",
|
|
1044
|
+
resizable = false,
|
|
1045
|
+
draggable = false,
|
|
1046
|
+
closeOnOutsideClick = true
|
|
1047
|
+
}) => {
|
|
1048
|
+
const isDesktop = useMediaQuery(breakpoint);
|
|
1049
|
+
const geometry = useDesktopPanelGeometry();
|
|
1050
|
+
const onInteractOutside = closeOnOutsideClick ? void 0 : (e) => e.preventDefault();
|
|
1051
|
+
if (isDesktop) {
|
|
1052
|
+
const corners = ["top-left", "top-right", "bottom-left", "bottom-right"];
|
|
1053
|
+
return /* @__PURE__ */ jsx23(Dialog, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
|
|
1054
|
+
DialogContent,
|
|
1055
|
+
{
|
|
1056
|
+
"data-slot": "dialog-content",
|
|
1057
|
+
onInteractOutside,
|
|
1058
|
+
style: resizable || draggable ? geometry.style : void 0,
|
|
1059
|
+
className: cn(
|
|
1060
|
+
"max-h-[85vh] overflow-y-auto transition-shadow duration-150 ease-out",
|
|
1061
|
+
geometry.isDragging && "shadow-2xl ring-2 ring-primary/30",
|
|
1062
|
+
desktopSizeClasses[size],
|
|
1063
|
+
className
|
|
1064
|
+
),
|
|
1065
|
+
children: [
|
|
1066
|
+
resizable && corners.map((corner) => /* @__PURE__ */ jsx23(
|
|
1067
|
+
"div",
|
|
1068
|
+
{
|
|
1069
|
+
"data-testid": `panel-resize-handle-${corner}`,
|
|
1070
|
+
className: cn(
|
|
1071
|
+
"group absolute h-4 w-4 touch-none",
|
|
1072
|
+
cornerPosition[corner],
|
|
1073
|
+
cornerCursor[corner]
|
|
1074
|
+
),
|
|
1075
|
+
onPointerDown: geometry.startResize(corner),
|
|
1076
|
+
onPointerMove: geometry.onPointerMove,
|
|
1077
|
+
onPointerUp: geometry.onPointerUp,
|
|
1078
|
+
onPointerCancel: geometry.onPointerUp,
|
|
1079
|
+
children: /* @__PURE__ */ jsx23(
|
|
1080
|
+
"div",
|
|
1081
|
+
{
|
|
1082
|
+
className: cn(
|
|
1083
|
+
"absolute h-2 w-2 scale-100 border-muted-foreground/40 transition-transform duration-150 group-hover:scale-125 group-hover:border-primary/60",
|
|
1084
|
+
cornerGripPosition[corner]
|
|
1085
|
+
)
|
|
1086
|
+
}
|
|
1087
|
+
)
|
|
1088
|
+
},
|
|
1089
|
+
corner
|
|
1090
|
+
)),
|
|
1091
|
+
/* @__PURE__ */ jsxs9(
|
|
1092
|
+
DialogHeader,
|
|
1093
|
+
{
|
|
1094
|
+
className: cn(draggable && "touch-none cursor-move select-none"),
|
|
1095
|
+
onPointerDown: draggable ? geometry.startMove : void 0,
|
|
1096
|
+
onPointerMove: draggable ? geometry.onPointerMove : void 0,
|
|
1097
|
+
onPointerUp: draggable ? geometry.onPointerUp : void 0,
|
|
1098
|
+
onPointerCancel: draggable ? geometry.onPointerUp : void 0,
|
|
1099
|
+
children: [
|
|
1100
|
+
/* @__PURE__ */ jsx23(DialogTitle, { children: title }),
|
|
1101
|
+
description ? /* @__PURE__ */ jsx23(DialogDescription, { children: description }) : null
|
|
1102
|
+
]
|
|
1103
|
+
}
|
|
1104
|
+
),
|
|
1105
|
+
children,
|
|
1106
|
+
footer ? /* @__PURE__ */ jsx23(DialogFooter, { children: footer }) : null
|
|
1107
|
+
]
|
|
1108
|
+
}
|
|
1109
|
+
) });
|
|
1110
|
+
}
|
|
1111
|
+
return /* @__PURE__ */ jsx23(Sheet, { open, onOpenChange, children: /* @__PURE__ */ jsxs9(
|
|
1112
|
+
SheetContent,
|
|
1113
|
+
{
|
|
1114
|
+
side: "bottom",
|
|
1115
|
+
resizable,
|
|
1116
|
+
onInteractOutside,
|
|
1117
|
+
className: cn(
|
|
1118
|
+
"flex flex-col overflow-y-auto rounded-t-2xl",
|
|
1119
|
+
drawerSizeClasses[size],
|
|
1120
|
+
className
|
|
1121
|
+
),
|
|
1122
|
+
children: [
|
|
1123
|
+
/* @__PURE__ */ jsxs9(SheetHeader, { children: [
|
|
1124
|
+
/* @__PURE__ */ jsx23(SheetTitle, { children: title }),
|
|
1125
|
+
description ? /* @__PURE__ */ jsx23(SheetDescription, { children: description }) : null
|
|
1126
|
+
] }),
|
|
1127
|
+
children,
|
|
1128
|
+
footer ? /* @__PURE__ */ jsx23(SheetFooter, { children: footer }) : null
|
|
1129
|
+
]
|
|
1130
|
+
}
|
|
1131
|
+
) });
|
|
1132
|
+
};
|
|
1133
|
+
|
|
840
1134
|
// src/patterns/form-field/FormField.tsx
|
|
841
1135
|
import { cloneElement, isValidElement, useId } from "react";
|
|
842
|
-
import { jsx as
|
|
1136
|
+
import { jsx as jsx24, jsxs as jsxs10 } from "react/jsx-runtime";
|
|
843
1137
|
function FormField({
|
|
844
1138
|
label,
|
|
845
1139
|
htmlFor,
|
|
@@ -864,23 +1158,24 @@ function FormField({
|
|
|
864
1158
|
...messageId ? { "aria-describedby": messageId } : {}
|
|
865
1159
|
}
|
|
866
1160
|
) : children;
|
|
867
|
-
return /* @__PURE__ */
|
|
868
|
-
/* @__PURE__ */
|
|
1161
|
+
return /* @__PURE__ */ jsxs10("div", { className: cn("flex flex-col gap-1.5", className), children: [
|
|
1162
|
+
/* @__PURE__ */ jsxs10(Label, { htmlFor: effectiveId, children: [
|
|
869
1163
|
label,
|
|
870
|
-
required ? /* @__PURE__ */
|
|
1164
|
+
required ? /* @__PURE__ */ jsxs10("span", { className: "text-destructive", "aria-hidden": "true", children: [
|
|
871
1165
|
" ",
|
|
872
1166
|
"*"
|
|
873
1167
|
] }) : null
|
|
874
1168
|
] }),
|
|
875
1169
|
child,
|
|
876
|
-
error ? /* @__PURE__ */
|
|
1170
|
+
error ? /* @__PURE__ */ jsx24("p", { id: messageId, className: "text-sm text-destructive", children: error }) : description ? /* @__PURE__ */ jsx24("p", { id: messageId, className: "text-sm text-muted-foreground", children: description }) : null
|
|
877
1171
|
] });
|
|
878
1172
|
}
|
|
879
1173
|
|
|
880
1174
|
// src/patterns/combobox/Combobox.tsx
|
|
881
1175
|
import { Check as Check4, ChevronsUpDown, X as X3 } from "lucide-react";
|
|
882
|
-
import { useEffect, useMemo, useRef, useState as
|
|
883
|
-
|
|
1176
|
+
import { useEffect as useEffect3, useMemo, useRef as useRef3, useState as useState6 } from "react";
|
|
1177
|
+
|
|
1178
|
+
// src/lib/optionGrouping.ts
|
|
884
1179
|
function groupMembers(options, group) {
|
|
885
1180
|
return options.filter((o) => o.group === group && !o.disabled).map((o) => o.value);
|
|
886
1181
|
}
|
|
@@ -890,6 +1185,24 @@ function groupCheckState(options, selectedValues, group) {
|
|
|
890
1185
|
if (selectedCount === 0) return "unchecked";
|
|
891
1186
|
return selectedCount === members.length ? "checked" : "indeterminate";
|
|
892
1187
|
}
|
|
1188
|
+
function buildRenderChunks(visibleOptions) {
|
|
1189
|
+
const chunks = [];
|
|
1190
|
+
visibleOptions.forEach((option, index) => {
|
|
1191
|
+
const row = { option, index };
|
|
1192
|
+
const last = chunks[chunks.length - 1];
|
|
1193
|
+
if (option.group && last?.kind === "group" && last.group === option.group) {
|
|
1194
|
+
last.rows.push(row);
|
|
1195
|
+
} else if (option.group) {
|
|
1196
|
+
chunks.push({ kind: "group", group: option.group, rows: [row] });
|
|
1197
|
+
} else {
|
|
1198
|
+
chunks.push({ kind: "single", row });
|
|
1199
|
+
}
|
|
1200
|
+
});
|
|
1201
|
+
return chunks;
|
|
1202
|
+
}
|
|
1203
|
+
|
|
1204
|
+
// src/patterns/combobox/Combobox.tsx
|
|
1205
|
+
import { jsx as jsx25, jsxs as jsxs11 } from "react/jsx-runtime";
|
|
893
1206
|
function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
894
1207
|
const selectedSet = new Set(selectedValues);
|
|
895
1208
|
const groupsPresent = Array.from(
|
|
@@ -909,21 +1222,6 @@ function resolveGroupTriggerLabel(options, selectedValues, groupLabel) {
|
|
|
909
1222
|
if (fullyCoveredGroups.length === 0 || hasLeftover) return null;
|
|
910
1223
|
return fullyCoveredGroups.map(groupLabel).join(", ");
|
|
911
1224
|
}
|
|
912
|
-
function buildRenderChunks(visibleOptions) {
|
|
913
|
-
const chunks = [];
|
|
914
|
-
visibleOptions.forEach((option, index) => {
|
|
915
|
-
const row = { option, index };
|
|
916
|
-
const last = chunks[chunks.length - 1];
|
|
917
|
-
if (option.group && last?.kind === "group" && last.group === option.group) {
|
|
918
|
-
last.rows.push(row);
|
|
919
|
-
} else if (option.group) {
|
|
920
|
-
chunks.push({ kind: "group", group: option.group, rows: [row] });
|
|
921
|
-
} else {
|
|
922
|
-
chunks.push({ kind: "single", row });
|
|
923
|
-
}
|
|
924
|
-
});
|
|
925
|
-
return chunks;
|
|
926
|
-
}
|
|
927
1225
|
function Combobox(props) {
|
|
928
1226
|
const {
|
|
929
1227
|
options,
|
|
@@ -942,10 +1240,10 @@ function Combobox(props) {
|
|
|
942
1240
|
selectedLabel
|
|
943
1241
|
} = props;
|
|
944
1242
|
const testId = props["data-testid"];
|
|
945
|
-
const [open, setOpen] =
|
|
946
|
-
const [search, setSearch] =
|
|
947
|
-
const [activeIndex, setActiveIndex] =
|
|
948
|
-
const inputRef =
|
|
1243
|
+
const [open, setOpen] = useState6(false);
|
|
1244
|
+
const [search, setSearch] = useState6("");
|
|
1245
|
+
const [activeIndex, setActiveIndex] = useState6(0);
|
|
1246
|
+
const inputRef = useRef3(null);
|
|
949
1247
|
const selectedValues = useMemo(
|
|
950
1248
|
() => props.multiple ? props.value : props.value ? [props.value] : [],
|
|
951
1249
|
[props.multiple, props.value]
|
|
@@ -960,7 +1258,7 @@ function Combobox(props) {
|
|
|
960
1258
|
if (!term) return options;
|
|
961
1259
|
return options.filter((option) => option.label.toLowerCase().includes(term));
|
|
962
1260
|
}, [options, search, onSearchChange]);
|
|
963
|
-
|
|
1261
|
+
useEffect3(() => {
|
|
964
1262
|
if (!open) return;
|
|
965
1263
|
setSearch("");
|
|
966
1264
|
onSearchChange?.("");
|
|
@@ -969,7 +1267,7 @@ function Combobox(props) {
|
|
|
969
1267
|
setActiveIndex(selectedIdx >= 0 ? selectedIdx : 0);
|
|
970
1268
|
inputRef.current?.focus();
|
|
971
1269
|
}, [open]);
|
|
972
|
-
|
|
1270
|
+
useEffect3(() => {
|
|
973
1271
|
setActiveIndex((i) => Math.min(i, Math.max(visibleOptions.length - 1, 0)));
|
|
974
1272
|
}, [visibleOptions.length]);
|
|
975
1273
|
function selectOption(option) {
|
|
@@ -1007,7 +1305,7 @@ function Combobox(props) {
|
|
|
1007
1305
|
function renderOption({ option, index }) {
|
|
1008
1306
|
const isSelected = selectedValues.includes(option.value);
|
|
1009
1307
|
const isActive = index === activeIndex;
|
|
1010
|
-
return /* @__PURE__ */
|
|
1308
|
+
return /* @__PURE__ */ jsxs11(
|
|
1011
1309
|
"button",
|
|
1012
1310
|
{
|
|
1013
1311
|
type: "button",
|
|
@@ -1023,7 +1321,7 @@ function Combobox(props) {
|
|
|
1023
1321
|
isActive && "bg-accent text-accent-foreground"
|
|
1024
1322
|
),
|
|
1025
1323
|
children: [
|
|
1026
|
-
props.multiple ? /* @__PURE__ */
|
|
1324
|
+
props.multiple ? /* @__PURE__ */ jsx25(
|
|
1027
1325
|
"input",
|
|
1028
1326
|
{
|
|
1029
1327
|
type: "checkbox",
|
|
@@ -1032,15 +1330,15 @@ function Combobox(props) {
|
|
|
1032
1330
|
tabIndex: -1,
|
|
1033
1331
|
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1034
1332
|
}
|
|
1035
|
-
) : /* @__PURE__ */
|
|
1036
|
-
/* @__PURE__ */
|
|
1333
|
+
) : /* @__PURE__ */ jsx25(Check4, { className: cn("h-4 w-4 shrink-0", isSelected ? "opacity-100" : "opacity-0") }),
|
|
1334
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: option.label })
|
|
1037
1335
|
]
|
|
1038
1336
|
},
|
|
1039
1337
|
option.value
|
|
1040
1338
|
);
|
|
1041
1339
|
}
|
|
1042
|
-
return /* @__PURE__ */
|
|
1043
|
-
/* @__PURE__ */
|
|
1340
|
+
return /* @__PURE__ */ jsxs11(Popover, { open, onOpenChange: setOpen, children: [
|
|
1341
|
+
/* @__PURE__ */ jsx25(PopoverTrigger, { asChild: true, children: /* @__PURE__ */ jsxs11(
|
|
1044
1342
|
Button,
|
|
1045
1343
|
{
|
|
1046
1344
|
id,
|
|
@@ -1056,9 +1354,9 @@ function Combobox(props) {
|
|
|
1056
1354
|
className
|
|
1057
1355
|
),
|
|
1058
1356
|
children: [
|
|
1059
|
-
/* @__PURE__ */
|
|
1060
|
-
/* @__PURE__ */
|
|
1061
|
-
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */
|
|
1357
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: triggerLabel }),
|
|
1358
|
+
/* @__PURE__ */ jsxs11("span", { className: "flex items-center gap-1", children: [
|
|
1359
|
+
clearable && !loading && selectedValues.length > 0 ? /* @__PURE__ */ jsx25(
|
|
1062
1360
|
"span",
|
|
1063
1361
|
{
|
|
1064
1362
|
role: "button",
|
|
@@ -1073,23 +1371,23 @@ function Combobox(props) {
|
|
|
1073
1371
|
props.onChange(null);
|
|
1074
1372
|
}
|
|
1075
1373
|
},
|
|
1076
|
-
children: /* @__PURE__ */
|
|
1374
|
+
children: /* @__PURE__ */ jsx25(X3, { className: "h-3.5 w-3.5" })
|
|
1077
1375
|
}
|
|
1078
1376
|
) : null,
|
|
1079
|
-
/* @__PURE__ */
|
|
1377
|
+
/* @__PURE__ */ jsx25(ChevronsUpDown, { className: "h-4 w-4 shrink-0 opacity-50", "aria-hidden": "true" })
|
|
1080
1378
|
] })
|
|
1081
1379
|
]
|
|
1082
1380
|
}
|
|
1083
1381
|
) }),
|
|
1084
|
-
/* @__PURE__ */
|
|
1382
|
+
/* @__PURE__ */ jsx25(
|
|
1085
1383
|
PopoverContent,
|
|
1086
1384
|
{
|
|
1087
1385
|
align: "start",
|
|
1088
1386
|
container,
|
|
1089
1387
|
className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
|
|
1090
1388
|
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1091
|
-
children: /* @__PURE__ */
|
|
1092
|
-
/* @__PURE__ */
|
|
1389
|
+
children: /* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-2", children: [
|
|
1390
|
+
/* @__PURE__ */ jsx25(
|
|
1093
1391
|
Input,
|
|
1094
1392
|
{
|
|
1095
1393
|
ref: inputRef,
|
|
@@ -1103,7 +1401,7 @@ function Combobox(props) {
|
|
|
1103
1401
|
"aria-label": searchPlaceholder
|
|
1104
1402
|
}
|
|
1105
1403
|
),
|
|
1106
|
-
/* @__PURE__ */
|
|
1404
|
+
/* @__PURE__ */ jsx25("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: renderChunks.length === 0 ? /* @__PURE__ */ jsx25("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : renderChunks.map((chunk) => {
|
|
1107
1405
|
if (chunk.kind === "single") return renderOption(chunk.row);
|
|
1108
1406
|
const headerState = groupCheckState(options, selectedValues, chunk.group);
|
|
1109
1407
|
return (
|
|
@@ -1114,14 +1412,14 @@ function Combobox(props) {
|
|
|
1114
1412
|
// lives on this wrapper's `pt-1`, not on the header itself -- a margin on
|
|
1115
1413
|
// the sticky element would travel with it once stuck, leaving a gap at the
|
|
1116
1414
|
// scrollport's top that the row behind it shows through.
|
|
1117
|
-
/* @__PURE__ */
|
|
1118
|
-
/* @__PURE__ */
|
|
1415
|
+
/* @__PURE__ */ jsxs11("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1416
|
+
/* @__PURE__ */ jsxs11(
|
|
1119
1417
|
"div",
|
|
1120
1418
|
{
|
|
1121
1419
|
"data-group-header": true,
|
|
1122
1420
|
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1123
1421
|
children: [
|
|
1124
|
-
props.multiple ? /* @__PURE__ */
|
|
1422
|
+
props.multiple ? /* @__PURE__ */ jsx25(
|
|
1125
1423
|
"input",
|
|
1126
1424
|
{
|
|
1127
1425
|
type: "checkbox",
|
|
@@ -1135,7 +1433,7 @@ function Combobox(props) {
|
|
|
1135
1433
|
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1136
1434
|
}
|
|
1137
1435
|
) : null,
|
|
1138
|
-
/* @__PURE__ */
|
|
1436
|
+
/* @__PURE__ */ jsx25("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1139
1437
|
]
|
|
1140
1438
|
}
|
|
1141
1439
|
),
|
|
@@ -1149,9 +1447,249 @@ function Combobox(props) {
|
|
|
1149
1447
|
] });
|
|
1150
1448
|
}
|
|
1151
1449
|
|
|
1450
|
+
// src/patterns/tag-combobox/TagCombobox.tsx
|
|
1451
|
+
import { X as X4 } from "lucide-react";
|
|
1452
|
+
import { useEffect as useEffect4, useMemo as useMemo2, useRef as useRef4, useState as useState7 } from "react";
|
|
1453
|
+
import { jsx as jsx26, jsxs as jsxs12 } from "react/jsx-runtime";
|
|
1454
|
+
function TagCombobox({
|
|
1455
|
+
options,
|
|
1456
|
+
value,
|
|
1457
|
+
onChange,
|
|
1458
|
+
placeholder = "Select\u2026",
|
|
1459
|
+
emptyMessage = "No matches.",
|
|
1460
|
+
disabled,
|
|
1461
|
+
loading = false,
|
|
1462
|
+
loadingMessage = "Loading\u2026",
|
|
1463
|
+
className,
|
|
1464
|
+
id,
|
|
1465
|
+
onSearchChange,
|
|
1466
|
+
container,
|
|
1467
|
+
groupLabels,
|
|
1468
|
+
...rest
|
|
1469
|
+
}) {
|
|
1470
|
+
const testId = rest["data-testid"];
|
|
1471
|
+
const [open, setOpen] = useState7(false);
|
|
1472
|
+
const [search, setSearch] = useState7("");
|
|
1473
|
+
const [activeIndex, setActiveIndex] = useState7(0);
|
|
1474
|
+
const inputRef = useRef4(null);
|
|
1475
|
+
const fieldRef = useRef4(null);
|
|
1476
|
+
const selectedOptions = useMemo2(
|
|
1477
|
+
() => value.map((v) => options.find((o) => o.value === v) ?? { value: v, label: v }),
|
|
1478
|
+
[options, value]
|
|
1479
|
+
);
|
|
1480
|
+
const visibleOptions = useMemo2(() => {
|
|
1481
|
+
if (onSearchChange) return options;
|
|
1482
|
+
const term = search.trim().toLowerCase();
|
|
1483
|
+
if (!term) return options;
|
|
1484
|
+
return options.filter((option) => option.label.toLowerCase().includes(term));
|
|
1485
|
+
}, [options, search, onSearchChange]);
|
|
1486
|
+
useEffect4(() => {
|
|
1487
|
+
setActiveIndex((i) => Math.min(Math.max(i, 0), Math.max(visibleOptions.length - 1, 0)));
|
|
1488
|
+
}, [visibleOptions.length]);
|
|
1489
|
+
const renderChunks = useMemo2(() => buildRenderChunks(visibleOptions), [visibleOptions]);
|
|
1490
|
+
useEffect4(() => {
|
|
1491
|
+
if (!open) return;
|
|
1492
|
+
setSearch("");
|
|
1493
|
+
onSearchChange?.("");
|
|
1494
|
+
const firstSelected = value[0];
|
|
1495
|
+
const selectedIdx = firstSelected ? options.findIndex((o) => o.value === firstSelected) : -1;
|
|
1496
|
+
setActiveIndex(selectedIdx >= 0 ? selectedIdx : 0);
|
|
1497
|
+
}, [open]);
|
|
1498
|
+
function updateSearch(next) {
|
|
1499
|
+
setSearch(next);
|
|
1500
|
+
onSearchChange?.(next);
|
|
1501
|
+
if (!open) setOpen(true);
|
|
1502
|
+
}
|
|
1503
|
+
function toggleOption(option) {
|
|
1504
|
+
if (option.disabled) return;
|
|
1505
|
+
const next = value.includes(option.value) ? value.filter((v) => v !== option.value) : [...value, option.value];
|
|
1506
|
+
onChange(next);
|
|
1507
|
+
updateSearch("");
|
|
1508
|
+
inputRef.current?.focus();
|
|
1509
|
+
}
|
|
1510
|
+
function removeValue(v) {
|
|
1511
|
+
if (options.find((o) => o.value === v)?.disabled) return;
|
|
1512
|
+
onChange(value.filter((existing) => existing !== v));
|
|
1513
|
+
}
|
|
1514
|
+
const groupLabel = (group) => groupLabels?.[group] ?? group;
|
|
1515
|
+
function toggleGroup(group) {
|
|
1516
|
+
const members = groupMembers(options, group);
|
|
1517
|
+
const next = groupCheckState(options, value, group) === "checked" ? value.filter((v) => !members.includes(v)) : Array.from(/* @__PURE__ */ new Set([...value, ...members]));
|
|
1518
|
+
onChange(next);
|
|
1519
|
+
inputRef.current?.focus();
|
|
1520
|
+
}
|
|
1521
|
+
function handleKeyDown(event) {
|
|
1522
|
+
if (event.key === "ArrowDown") {
|
|
1523
|
+
event.preventDefault();
|
|
1524
|
+
setOpen(true);
|
|
1525
|
+
setActiveIndex((i) => Math.min(i + 1, Math.max(visibleOptions.length - 1, 0)));
|
|
1526
|
+
} else if (event.key === "ArrowUp") {
|
|
1527
|
+
event.preventDefault();
|
|
1528
|
+
setActiveIndex((i) => Math.max(i - 1, 0));
|
|
1529
|
+
} else if (event.key === "Enter") {
|
|
1530
|
+
event.preventDefault();
|
|
1531
|
+
const option = visibleOptions[activeIndex];
|
|
1532
|
+
if (option) toggleOption(option);
|
|
1533
|
+
} else if (event.key === "Escape") {
|
|
1534
|
+
setOpen(false);
|
|
1535
|
+
} else if (event.key === "Backspace" && search === "") {
|
|
1536
|
+
const last = value[value.length - 1];
|
|
1537
|
+
if (last !== void 0) removeValue(last);
|
|
1538
|
+
}
|
|
1539
|
+
}
|
|
1540
|
+
function renderOption({ option, index }) {
|
|
1541
|
+
const isSelected = value.includes(option.value);
|
|
1542
|
+
const isActive = index === activeIndex;
|
|
1543
|
+
return /* @__PURE__ */ jsxs12(
|
|
1544
|
+
"button",
|
|
1545
|
+
{
|
|
1546
|
+
type: "button",
|
|
1547
|
+
role: "option",
|
|
1548
|
+
"aria-selected": isSelected,
|
|
1549
|
+
disabled: option.disabled,
|
|
1550
|
+
"data-testid": testId ? `${testId}-option` : void 0,
|
|
1551
|
+
"data-option-value": option.value,
|
|
1552
|
+
onMouseEnter: () => setActiveIndex(index),
|
|
1553
|
+
onClick: () => toggleOption(option),
|
|
1554
|
+
className: cn(
|
|
1555
|
+
"flex w-full items-center gap-2 rounded-sm px-2 py-1.5 text-left text-sm outline-none disabled:pointer-events-none disabled:opacity-50",
|
|
1556
|
+
isActive && "bg-accent text-accent-foreground"
|
|
1557
|
+
),
|
|
1558
|
+
children: [
|
|
1559
|
+
/* @__PURE__ */ jsx26(
|
|
1560
|
+
"input",
|
|
1561
|
+
{
|
|
1562
|
+
type: "checkbox",
|
|
1563
|
+
checked: isSelected,
|
|
1564
|
+
readOnly: true,
|
|
1565
|
+
tabIndex: -1,
|
|
1566
|
+
className: "pointer-events-none h-3.5 w-3.5 shrink-0 accent-primary"
|
|
1567
|
+
}
|
|
1568
|
+
),
|
|
1569
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate", children: option.label })
|
|
1570
|
+
]
|
|
1571
|
+
},
|
|
1572
|
+
option.value
|
|
1573
|
+
);
|
|
1574
|
+
}
|
|
1575
|
+
return /* @__PURE__ */ jsxs12(Popover, { open, onOpenChange: setOpen, children: [
|
|
1576
|
+
/* @__PURE__ */ jsx26(PopoverAnchor, { asChild: true, children: /* @__PURE__ */ jsxs12(
|
|
1577
|
+
"div",
|
|
1578
|
+
{
|
|
1579
|
+
ref: fieldRef,
|
|
1580
|
+
"data-testid": testId,
|
|
1581
|
+
role: "combobox",
|
|
1582
|
+
"aria-expanded": open,
|
|
1583
|
+
"aria-disabled": disabled,
|
|
1584
|
+
className: cn(
|
|
1585
|
+
"flex min-h-9 w-full flex-wrap items-center gap-1.5 rounded-md border border-input bg-transparent px-2 py-1.5 text-sm",
|
|
1586
|
+
"focus-within:outline-none focus-within:ring-2 focus-within:ring-ring",
|
|
1587
|
+
disabled && "cursor-not-allowed opacity-50",
|
|
1588
|
+
className
|
|
1589
|
+
),
|
|
1590
|
+
onClick: () => {
|
|
1591
|
+
if (!disabled) inputRef.current?.focus();
|
|
1592
|
+
},
|
|
1593
|
+
children: [
|
|
1594
|
+
selectedOptions.map((option) => /* @__PURE__ */ jsxs12(
|
|
1595
|
+
"span",
|
|
1596
|
+
{
|
|
1597
|
+
className: "inline-flex items-center gap-1 rounded-full bg-muted py-0.5 pr-1 pl-2.5 text-xs font-medium text-muted-foreground",
|
|
1598
|
+
children: [
|
|
1599
|
+
/* @__PURE__ */ jsx26("span", { className: "max-w-48 truncate", children: option.label }),
|
|
1600
|
+
!disabled && /* @__PURE__ */ jsx26(
|
|
1601
|
+
"button",
|
|
1602
|
+
{
|
|
1603
|
+
type: "button",
|
|
1604
|
+
"aria-label": `Remove ${option.label}`,
|
|
1605
|
+
className: "rounded-full p-0.5 hover:bg-accent hover:text-accent-foreground",
|
|
1606
|
+
onClick: (event) => {
|
|
1607
|
+
event.stopPropagation();
|
|
1608
|
+
removeValue(option.value);
|
|
1609
|
+
},
|
|
1610
|
+
children: /* @__PURE__ */ jsx26(X4, { className: "h-3 w-3" })
|
|
1611
|
+
}
|
|
1612
|
+
)
|
|
1613
|
+
]
|
|
1614
|
+
},
|
|
1615
|
+
option.value
|
|
1616
|
+
)),
|
|
1617
|
+
/* @__PURE__ */ jsx26(
|
|
1618
|
+
"input",
|
|
1619
|
+
{
|
|
1620
|
+
ref: inputRef,
|
|
1621
|
+
id,
|
|
1622
|
+
value: search,
|
|
1623
|
+
disabled,
|
|
1624
|
+
placeholder: selectedOptions.length === 0 ? placeholder : void 0,
|
|
1625
|
+
"aria-label": placeholder,
|
|
1626
|
+
className: "min-w-24 flex-1 bg-transparent outline-none placeholder:text-muted-foreground disabled:cursor-not-allowed",
|
|
1627
|
+
onFocus: () => setOpen(true),
|
|
1628
|
+
onChange: (event) => updateSearch(event.target.value),
|
|
1629
|
+
onKeyDown: handleKeyDown
|
|
1630
|
+
}
|
|
1631
|
+
)
|
|
1632
|
+
]
|
|
1633
|
+
}
|
|
1634
|
+
) }),
|
|
1635
|
+
/* @__PURE__ */ jsx26(
|
|
1636
|
+
PopoverContent,
|
|
1637
|
+
{
|
|
1638
|
+
align: "start",
|
|
1639
|
+
container,
|
|
1640
|
+
className: "w-[var(--radix-popover-trigger-width)] min-w-[10rem] p-2",
|
|
1641
|
+
onOpenAutoFocus: (event) => event.preventDefault(),
|
|
1642
|
+
onCloseAutoFocus: (event) => event.preventDefault(),
|
|
1643
|
+
onInteractOutside: (event) => {
|
|
1644
|
+
if (fieldRef.current?.contains(event.target)) event.preventDefault();
|
|
1645
|
+
},
|
|
1646
|
+
children: /* @__PURE__ */ jsx26("div", { role: "listbox", className: "flex max-h-60 flex-col gap-0.5 overflow-y-auto", children: loading ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: loadingMessage }) : renderChunks.length === 0 ? /* @__PURE__ */ jsx26("p", { className: "py-2 text-center text-sm text-muted-foreground", children: emptyMessage }) : renderChunks.map((chunk) => {
|
|
1647
|
+
if (chunk.kind === "single") return renderOption(chunk.row);
|
|
1648
|
+
const headerState = groupCheckState(options, value, chunk.group);
|
|
1649
|
+
return (
|
|
1650
|
+
// The header and every one of its group's rows share this wrapper -- its
|
|
1651
|
+
// bounds are the sticky header's containing block, so `sticky top-0` keeps
|
|
1652
|
+
// the header pinned for the group's whole scroll extent instead of just
|
|
1653
|
+
// past its first row. Spacing above a group lives on this wrapper's
|
|
1654
|
+
// `pt-1`/`first:pt-0`, not on the header itself -- see `Combobox`'s own
|
|
1655
|
+
// identical structure for why both of these matter.
|
|
1656
|
+
/* @__PURE__ */ jsxs12("div", { className: "flex flex-col gap-0.5 pt-1 first:pt-0", children: [
|
|
1657
|
+
/* @__PURE__ */ jsxs12(
|
|
1658
|
+
"div",
|
|
1659
|
+
{
|
|
1660
|
+
"data-group-header": true,
|
|
1661
|
+
className: "sticky top-0 z-10 flex items-center gap-2 rounded-sm bg-muted px-2 py-1.5 text-sm font-semibold text-foreground",
|
|
1662
|
+
children: [
|
|
1663
|
+
/* @__PURE__ */ jsx26(
|
|
1664
|
+
"input",
|
|
1665
|
+
{
|
|
1666
|
+
type: "checkbox",
|
|
1667
|
+
checked: headerState === "checked",
|
|
1668
|
+
ref: (el) => {
|
|
1669
|
+
if (el) el.indeterminate = headerState === "indeterminate";
|
|
1670
|
+
},
|
|
1671
|
+
onChange: () => toggleGroup(chunk.group),
|
|
1672
|
+
onClick: (event) => event.stopPropagation(),
|
|
1673
|
+
className: "h-3.5 w-3.5 shrink-0 accent-primary",
|
|
1674
|
+
"aria-label": `Select all of ${groupLabel(chunk.group)}`
|
|
1675
|
+
}
|
|
1676
|
+
),
|
|
1677
|
+
/* @__PURE__ */ jsx26("span", { className: "truncate", children: groupLabel(chunk.group) })
|
|
1678
|
+
]
|
|
1679
|
+
}
|
|
1680
|
+
),
|
|
1681
|
+
chunk.rows.map(renderOption)
|
|
1682
|
+
] }, `group-${chunk.group}`)
|
|
1683
|
+
);
|
|
1684
|
+
}) })
|
|
1685
|
+
}
|
|
1686
|
+
)
|
|
1687
|
+
] });
|
|
1688
|
+
}
|
|
1689
|
+
|
|
1152
1690
|
// src/patterns/alert-box/AlertBox.tsx
|
|
1153
1691
|
import { AlertCircle, AlertTriangle, CheckCircle2, Info } from "lucide-react";
|
|
1154
|
-
import { jsx as
|
|
1692
|
+
import { jsx as jsx27, jsxs as jsxs13 } from "react/jsx-runtime";
|
|
1155
1693
|
var variantStyles = {
|
|
1156
1694
|
error: "border-destructive bg-destructive/10 text-destructive",
|
|
1157
1695
|
warning: "border-amber-500 bg-amber-500/10 text-amber-700 dark:text-amber-300",
|
|
@@ -1159,24 +1697,24 @@ var variantStyles = {
|
|
|
1159
1697
|
success: "border-emerald-500 bg-emerald-500/10 text-emerald-700 dark:text-emerald-300"
|
|
1160
1698
|
};
|
|
1161
1699
|
var defaultIcons = {
|
|
1162
|
-
error: /* @__PURE__ */
|
|
1163
|
-
warning: /* @__PURE__ */
|
|
1164
|
-
info: /* @__PURE__ */
|
|
1165
|
-
success: /* @__PURE__ */
|
|
1700
|
+
error: /* @__PURE__ */ jsx27(AlertCircle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1701
|
+
warning: /* @__PURE__ */ jsx27(AlertTriangle, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1702
|
+
info: /* @__PURE__ */ jsx27(Info, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" }),
|
|
1703
|
+
success: /* @__PURE__ */ jsx27(CheckCircle2, { className: "h-4 w-4 shrink-0", "aria-hidden": "true" })
|
|
1166
1704
|
};
|
|
1167
1705
|
var AlertBox = ({ variant, title, children, className, icon }) => {
|
|
1168
1706
|
const resolvedIcon = icon === void 0 ? defaultIcons[variant] : icon;
|
|
1169
|
-
return /* @__PURE__ */
|
|
1170
|
-
resolvedIcon != null && /* @__PURE__ */
|
|
1171
|
-
/* @__PURE__ */
|
|
1172
|
-
title && /* @__PURE__ */
|
|
1173
|
-
/* @__PURE__ */
|
|
1707
|
+
return /* @__PURE__ */ jsx27("div", { className: cn("rounded-md border p-3 text-sm", variantStyles[variant], className), children: /* @__PURE__ */ jsxs13("div", { className: "flex flex-row items-start gap-2", children: [
|
|
1708
|
+
resolvedIcon != null && /* @__PURE__ */ jsx27("span", { className: "mt-0.5 shrink-0", children: resolvedIcon }),
|
|
1709
|
+
/* @__PURE__ */ jsxs13("div", { className: "flex flex-col gap-1", children: [
|
|
1710
|
+
title && /* @__PURE__ */ jsx27("p", { className: "font-bold", children: title }),
|
|
1711
|
+
/* @__PURE__ */ jsx27("div", { children })
|
|
1174
1712
|
] })
|
|
1175
1713
|
] }) });
|
|
1176
1714
|
};
|
|
1177
1715
|
|
|
1178
1716
|
// src/patterns/status-badge/StatusBadge.tsx
|
|
1179
|
-
import { jsx as
|
|
1717
|
+
import { jsx as jsx28 } from "react/jsx-runtime";
|
|
1180
1718
|
var TONE_CLASSES = {
|
|
1181
1719
|
success: "border-transparent bg-emerald-500/15 text-emerald-700 dark:bg-emerald-500/20 dark:text-emerald-300",
|
|
1182
1720
|
warning: "border-transparent bg-amber-500/15 text-amber-700 dark:bg-amber-500/20 dark:text-amber-300",
|
|
@@ -1223,11 +1761,11 @@ function resolveTone(status, tone, toneMap) {
|
|
|
1223
1761
|
var StatusBadge = ({ status, label, tone, toneMap, className, ...props }) => {
|
|
1224
1762
|
const resolvedTone = resolveTone(status, tone, toneMap);
|
|
1225
1763
|
const displayLabel = label ?? deriveLabel(status);
|
|
1226
|
-
return /* @__PURE__ */
|
|
1764
|
+
return /* @__PURE__ */ jsx28(Badge, { variant: "outline", className: cn(TONE_CLASSES[resolvedTone], className), ...props, children: displayLabel });
|
|
1227
1765
|
};
|
|
1228
1766
|
|
|
1229
1767
|
// src/patterns/button-group/ButtonGroup.tsx
|
|
1230
|
-
import { jsx as
|
|
1768
|
+
import { jsx as jsx29, jsxs as jsxs14 } from "react/jsx-runtime";
|
|
1231
1769
|
function ButtonGroup({
|
|
1232
1770
|
options,
|
|
1233
1771
|
value,
|
|
@@ -1237,10 +1775,10 @@ function ButtonGroup({
|
|
|
1237
1775
|
className,
|
|
1238
1776
|
...props
|
|
1239
1777
|
}) {
|
|
1240
|
-
return /* @__PURE__ */
|
|
1778
|
+
return /* @__PURE__ */ jsx29("div", { role: "group", "aria-label": props["aria-label"], className: cn("inline-flex", className), "data-testid": props["data-testid"], children: options.map((option, index) => {
|
|
1241
1779
|
const selected = option.value === value;
|
|
1242
1780
|
const Icon2 = option.icon;
|
|
1243
|
-
return /* @__PURE__ */
|
|
1781
|
+
return /* @__PURE__ */ jsxs14(
|
|
1244
1782
|
Button,
|
|
1245
1783
|
{
|
|
1246
1784
|
type: "button",
|
|
@@ -1258,7 +1796,7 @@ function ButtonGroup({
|
|
|
1258
1796
|
index > 0 && "-ml-px"
|
|
1259
1797
|
),
|
|
1260
1798
|
children: [
|
|
1261
|
-
Icon2 && /* @__PURE__ */
|
|
1799
|
+
Icon2 && /* @__PURE__ */ jsx29(Icon2, { className: "h-4 w-4" }),
|
|
1262
1800
|
option.label
|
|
1263
1801
|
]
|
|
1264
1802
|
},
|
|
@@ -1270,7 +1808,7 @@ function ButtonGroup({
|
|
|
1270
1808
|
// src/patterns/loading-spinner/LoadingSpinner.tsx
|
|
1271
1809
|
import { cva as cva4 } from "class-variance-authority";
|
|
1272
1810
|
import { Loader2 } from "lucide-react";
|
|
1273
|
-
import { jsx as
|
|
1811
|
+
import { jsx as jsx30, jsxs as jsxs15 } from "react/jsx-runtime";
|
|
1274
1812
|
var loadingSpinnerIconVariants = cva4("animate-spin text-muted-foreground", {
|
|
1275
1813
|
variants: {
|
|
1276
1814
|
size: {
|
|
@@ -1288,27 +1826,27 @@ var LoadingSpinner = ({
|
|
|
1288
1826
|
label,
|
|
1289
1827
|
className,
|
|
1290
1828
|
...props
|
|
1291
|
-
}) => /* @__PURE__ */
|
|
1292
|
-
/* @__PURE__ */
|
|
1293
|
-
label ? /* @__PURE__ */
|
|
1829
|
+
}) => /* @__PURE__ */ jsxs15("span", { role: "status", className: cn("inline-flex items-center gap-2", className), ...props, children: [
|
|
1830
|
+
/* @__PURE__ */ jsx30(Loader2, { className: cn(loadingSpinnerIconVariants({ size })), "aria-hidden": "true" }),
|
|
1831
|
+
label ? /* @__PURE__ */ jsx30("span", { children: label }) : null
|
|
1294
1832
|
] });
|
|
1295
1833
|
var LoadingOverlay = ({
|
|
1296
1834
|
size = "lg",
|
|
1297
1835
|
label,
|
|
1298
1836
|
className,
|
|
1299
1837
|
...props
|
|
1300
|
-
}) => /* @__PURE__ */
|
|
1838
|
+
}) => /* @__PURE__ */ jsx30(
|
|
1301
1839
|
"div",
|
|
1302
1840
|
{
|
|
1303
1841
|
className: cn("flex h-full min-h-32 w-full items-center justify-center", className),
|
|
1304
1842
|
...props,
|
|
1305
|
-
children: /* @__PURE__ */
|
|
1843
|
+
children: /* @__PURE__ */ jsx30(LoadingSpinner, { size, label })
|
|
1306
1844
|
}
|
|
1307
1845
|
);
|
|
1308
1846
|
|
|
1309
1847
|
// src/patterns/search-bar/SearchBar.tsx
|
|
1310
|
-
import { Search, X as
|
|
1311
|
-
import { jsx as
|
|
1848
|
+
import { Search, X as X5 } from "lucide-react";
|
|
1849
|
+
import { jsx as jsx31, jsxs as jsxs16 } from "react/jsx-runtime";
|
|
1312
1850
|
function SearchBar({
|
|
1313
1851
|
value,
|
|
1314
1852
|
onChange,
|
|
@@ -1320,9 +1858,9 @@ function SearchBar({
|
|
|
1320
1858
|
...props
|
|
1321
1859
|
}) {
|
|
1322
1860
|
const showClear = onClear !== false && value.length > 0;
|
|
1323
|
-
return /* @__PURE__ */
|
|
1324
|
-
/* @__PURE__ */
|
|
1325
|
-
/* @__PURE__ */
|
|
1861
|
+
return /* @__PURE__ */ jsxs16("div", { className: cn("relative", className), children: [
|
|
1862
|
+
/* @__PURE__ */ jsx31("span", { className: "pointer-events-none absolute top-1/2 left-4 -translate-y-1/2 text-muted-foreground", children: isLoading ? /* @__PURE__ */ jsx31(LoadingSpinner, { size: "sm" }) : /* @__PURE__ */ jsx31(Search, { className: "h-4 w-4", "aria-hidden": "true" }) }),
|
|
1863
|
+
/* @__PURE__ */ jsx31(
|
|
1326
1864
|
"input",
|
|
1327
1865
|
{
|
|
1328
1866
|
ref: inputRef,
|
|
@@ -1341,14 +1879,14 @@ function SearchBar({
|
|
|
1341
1879
|
...props
|
|
1342
1880
|
}
|
|
1343
1881
|
),
|
|
1344
|
-
showClear && /* @__PURE__ */
|
|
1882
|
+
showClear && /* @__PURE__ */ jsx31(
|
|
1345
1883
|
"button",
|
|
1346
1884
|
{
|
|
1347
1885
|
type: "button",
|
|
1348
1886
|
onClick: () => onClear ? onClear() : onChange(""),
|
|
1349
1887
|
"aria-label": clearLabel,
|
|
1350
1888
|
className: "absolute top-1/2 right-2 flex h-7 w-7 -translate-y-1/2 items-center justify-center rounded-full text-muted-foreground hover:bg-accent hover:text-foreground",
|
|
1351
|
-
children: /* @__PURE__ */
|
|
1889
|
+
children: /* @__PURE__ */ jsx31(X5, { className: "h-4 w-4", "aria-hidden": "true" })
|
|
1352
1890
|
}
|
|
1353
1891
|
)
|
|
1354
1892
|
] });
|
|
@@ -1363,7 +1901,7 @@ function useSidebarCollapsed() {
|
|
|
1363
1901
|
}
|
|
1364
1902
|
|
|
1365
1903
|
// src/patterns/sidebar/NavItem.tsx
|
|
1366
|
-
import { jsx as
|
|
1904
|
+
import { jsx as jsx32, jsxs as jsxs17 } from "react/jsx-runtime";
|
|
1367
1905
|
function NavItem({
|
|
1368
1906
|
as,
|
|
1369
1907
|
icon: Icon2,
|
|
@@ -1375,7 +1913,7 @@ function NavItem({
|
|
|
1375
1913
|
}) {
|
|
1376
1914
|
const collapsed = useSidebarCollapsed();
|
|
1377
1915
|
const Comp = as ?? "a";
|
|
1378
|
-
const row = /* @__PURE__ */
|
|
1916
|
+
const row = /* @__PURE__ */ jsxs17(
|
|
1379
1917
|
Comp,
|
|
1380
1918
|
{
|
|
1381
1919
|
className: cn(
|
|
@@ -1387,7 +1925,7 @@ function NavItem({
|
|
|
1387
1925
|
),
|
|
1388
1926
|
...rest,
|
|
1389
1927
|
children: [
|
|
1390
|
-
Icon2 && /* @__PURE__ */
|
|
1928
|
+
Icon2 && /* @__PURE__ */ jsx32(
|
|
1391
1929
|
Icon2,
|
|
1392
1930
|
{
|
|
1393
1931
|
className: cn("h-[18px] w-[18px] shrink-0", active ? "text-nav-primary" : "text-muted-foreground"),
|
|
@@ -1395,7 +1933,7 @@ function NavItem({
|
|
|
1395
1933
|
"aria-hidden": true
|
|
1396
1934
|
}
|
|
1397
1935
|
),
|
|
1398
|
-
/* @__PURE__ */
|
|
1936
|
+
/* @__PURE__ */ jsx32(
|
|
1399
1937
|
"span",
|
|
1400
1938
|
{
|
|
1401
1939
|
className: cn(
|
|
@@ -1410,9 +1948,9 @@ function NavItem({
|
|
|
1410
1948
|
}
|
|
1411
1949
|
);
|
|
1412
1950
|
if (!collapsed) return row;
|
|
1413
|
-
return /* @__PURE__ */
|
|
1414
|
-
/* @__PURE__ */
|
|
1415
|
-
/* @__PURE__ */
|
|
1951
|
+
return /* @__PURE__ */ jsxs17(Tooltip, { children: [
|
|
1952
|
+
/* @__PURE__ */ jsx32(TooltipTrigger, { asChild: true, children: row }),
|
|
1953
|
+
/* @__PURE__ */ jsxs17(TooltipContent, { side: "right", className: "flex items-center gap-1.5", children: [
|
|
1416
1954
|
label,
|
|
1417
1955
|
badge
|
|
1418
1956
|
] })
|
|
@@ -1421,8 +1959,8 @@ function NavItem({
|
|
|
1421
1959
|
|
|
1422
1960
|
// src/patterns/sidebar/NavGroup.tsx
|
|
1423
1961
|
import { ChevronDown as ChevronDown2 } from "lucide-react";
|
|
1424
|
-
import { useState as
|
|
1425
|
-
import { jsx as
|
|
1962
|
+
import { useState as useState8 } from "react";
|
|
1963
|
+
import { jsx as jsx33, jsxs as jsxs18 } from "react/jsx-runtime";
|
|
1426
1964
|
function NavGroup({
|
|
1427
1965
|
label,
|
|
1428
1966
|
defaultCollapsed = false,
|
|
@@ -1431,15 +1969,15 @@ function NavGroup({
|
|
|
1431
1969
|
children,
|
|
1432
1970
|
className
|
|
1433
1971
|
}) {
|
|
1434
|
-
const [internalCollapsed, setInternalCollapsed] =
|
|
1972
|
+
const [internalCollapsed, setInternalCollapsed] = useState8(defaultCollapsed);
|
|
1435
1973
|
const groupCollapsed = collapsed ?? internalCollapsed;
|
|
1436
1974
|
const railCollapsed = useSidebarCollapsed();
|
|
1437
1975
|
const setGroupCollapsed = (next) => {
|
|
1438
1976
|
onCollapsedChange?.(next);
|
|
1439
1977
|
if (collapsed === void 0) setInternalCollapsed(next);
|
|
1440
1978
|
};
|
|
1441
|
-
return /* @__PURE__ */
|
|
1442
|
-
label && (railCollapsed ? /* @__PURE__ */
|
|
1979
|
+
return /* @__PURE__ */ jsxs18("div", { className: cn("flex flex-col gap-0.5", className), children: [
|
|
1980
|
+
label && (railCollapsed ? /* @__PURE__ */ jsx33("div", { className: "mx-2 my-1 h-px bg-border/50", "aria-hidden": true }) : /* @__PURE__ */ jsxs18(
|
|
1443
1981
|
"button",
|
|
1444
1982
|
{
|
|
1445
1983
|
type: "button",
|
|
@@ -1447,8 +1985,8 @@ function NavGroup({
|
|
|
1447
1985
|
"aria-expanded": !groupCollapsed,
|
|
1448
1986
|
className: "group flex w-full items-center justify-between px-2.5 py-0.5 text-[11px] font-medium text-muted-foreground/80 hover:text-foreground",
|
|
1449
1987
|
children: [
|
|
1450
|
-
/* @__PURE__ */
|
|
1451
|
-
/* @__PURE__ */
|
|
1988
|
+
/* @__PURE__ */ jsx33("span", { children: label }),
|
|
1989
|
+
/* @__PURE__ */ jsx33(
|
|
1452
1990
|
ChevronDown2,
|
|
1453
1991
|
{
|
|
1454
1992
|
className: cn(
|
|
@@ -1461,7 +1999,7 @@ function NavGroup({
|
|
|
1461
1999
|
]
|
|
1462
2000
|
}
|
|
1463
2001
|
)),
|
|
1464
|
-
!groupCollapsed && /* @__PURE__ */
|
|
2002
|
+
!groupCollapsed && /* @__PURE__ */ jsx33("div", { className: "flex flex-col gap-0.5", children })
|
|
1465
2003
|
] });
|
|
1466
2004
|
}
|
|
1467
2005
|
|
|
@@ -1469,11 +2007,11 @@ function NavGroup({
|
|
|
1469
2007
|
import { PanelLeftClose, PanelLeftOpen } from "lucide-react";
|
|
1470
2008
|
import {
|
|
1471
2009
|
useCallback,
|
|
1472
|
-
useEffect as
|
|
1473
|
-
useRef as
|
|
1474
|
-
useState as
|
|
2010
|
+
useEffect as useEffect5,
|
|
2011
|
+
useRef as useRef5,
|
|
2012
|
+
useState as useState9
|
|
1475
2013
|
} from "react";
|
|
1476
|
-
import { jsx as
|
|
2014
|
+
import { jsx as jsx34, jsxs as jsxs19 } from "react/jsx-runtime";
|
|
1477
2015
|
function resolve(value, collapsed) {
|
|
1478
2016
|
return typeof value === "function" ? value(collapsed) : value;
|
|
1479
2017
|
}
|
|
@@ -1492,9 +2030,9 @@ function Sidebar({
|
|
|
1492
2030
|
children,
|
|
1493
2031
|
className
|
|
1494
2032
|
}) {
|
|
1495
|
-
const [internalWidth, setInternalWidth] =
|
|
2033
|
+
const [internalWidth, setInternalWidth] = useState9(defaultWidth);
|
|
1496
2034
|
const currentWidth = width ?? internalWidth;
|
|
1497
|
-
const [isDragging, setIsDragging] =
|
|
2035
|
+
const [isDragging, setIsDragging] = useState9(false);
|
|
1498
2036
|
const setWidth = useCallback(
|
|
1499
2037
|
(next) => {
|
|
1500
2038
|
const clamped = Math.min(maxWidth, Math.max(minWidth, next));
|
|
@@ -1503,7 +2041,7 @@ function Sidebar({
|
|
|
1503
2041
|
},
|
|
1504
2042
|
[maxWidth, minWidth, onWidthChange, width]
|
|
1505
2043
|
);
|
|
1506
|
-
const dragStartRef =
|
|
2044
|
+
const dragStartRef = useRef5(null);
|
|
1507
2045
|
const handlePointerDown = (event) => {
|
|
1508
2046
|
event.currentTarget.setPointerCapture(event.pointerId);
|
|
1509
2047
|
dragStartRef.current = { x: event.clientX, width: currentWidth };
|
|
@@ -1520,7 +2058,7 @@ function Sidebar({
|
|
|
1520
2058
|
dragStartRef.current = null;
|
|
1521
2059
|
setIsDragging(false);
|
|
1522
2060
|
};
|
|
1523
|
-
return /* @__PURE__ */
|
|
2061
|
+
return /* @__PURE__ */ jsx34(SidebarContextProvider, { value: { collapsed }, children: /* @__PURE__ */ jsx34(TooltipProvider, { delayDuration: 200, children: /* @__PURE__ */ jsxs19(
|
|
1524
2062
|
"aside",
|
|
1525
2063
|
{
|
|
1526
2064
|
style: { width: collapsed ? railWidth : currentWidth },
|
|
@@ -1530,22 +2068,22 @@ function Sidebar({
|
|
|
1530
2068
|
className
|
|
1531
2069
|
),
|
|
1532
2070
|
children: [
|
|
1533
|
-
(header || onCollapsedChange) && /* @__PURE__ */
|
|
1534
|
-
header && /* @__PURE__ */
|
|
1535
|
-
onCollapsedChange && /* @__PURE__ */
|
|
2071
|
+
(header || onCollapsedChange) && /* @__PURE__ */ jsxs19("div", { className: "flex h-16 items-center gap-2 border-b border-border px-3", children: [
|
|
2072
|
+
header && /* @__PURE__ */ jsx34("div", { className: "min-w-0 flex-1", children: resolve(header, collapsed) }),
|
|
2073
|
+
onCollapsedChange && /* @__PURE__ */ jsx34(
|
|
1536
2074
|
"button",
|
|
1537
2075
|
{
|
|
1538
2076
|
type: "button",
|
|
1539
2077
|
onClick: () => onCollapsedChange(!collapsed),
|
|
1540
2078
|
"aria-label": collapsed ? "Expand sidebar" : "Collapse sidebar",
|
|
1541
2079
|
className: "flex h-7 w-7 shrink-0 items-center justify-center rounded-md text-muted-foreground hover:bg-muted hover:text-foreground",
|
|
1542
|
-
children: collapsed ? /* @__PURE__ */
|
|
2080
|
+
children: collapsed ? /* @__PURE__ */ jsx34(PanelLeftOpen, { className: "h-4 w-4", "aria-hidden": true }) : /* @__PURE__ */ jsx34(PanelLeftClose, { className: "h-4 w-4", "aria-hidden": true })
|
|
1543
2081
|
}
|
|
1544
2082
|
)
|
|
1545
2083
|
] }),
|
|
1546
|
-
/* @__PURE__ */
|
|
1547
|
-
footer && /* @__PURE__ */
|
|
1548
|
-
resizable && !collapsed && /* @__PURE__ */
|
|
2084
|
+
/* @__PURE__ */ jsx34(SidebarNav, { children }),
|
|
2085
|
+
footer && /* @__PURE__ */ jsx34("div", { className: "border-t border-border/50 px-2 py-2", children: resolve(footer, collapsed) }),
|
|
2086
|
+
resizable && !collapsed && /* @__PURE__ */ jsx34(
|
|
1549
2087
|
"div",
|
|
1550
2088
|
{
|
|
1551
2089
|
onPointerDown: handlePointerDown,
|
|
@@ -1560,9 +2098,9 @@ function Sidebar({
|
|
|
1560
2098
|
) }) });
|
|
1561
2099
|
}
|
|
1562
2100
|
function SidebarNav({ children, className, ...rest }) {
|
|
1563
|
-
const ref =
|
|
1564
|
-
const [showFade, setShowFade] =
|
|
1565
|
-
|
|
2101
|
+
const ref = useRef5(null);
|
|
2102
|
+
const [showFade, setShowFade] = useState9(false);
|
|
2103
|
+
useEffect5(() => {
|
|
1566
2104
|
const el = ref.current;
|
|
1567
2105
|
if (!el) return;
|
|
1568
2106
|
const update = () => setShowFade(el.scrollHeight - el.scrollTop - el.clientHeight > 1);
|
|
@@ -1575,8 +2113,8 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
1575
2113
|
observer.disconnect();
|
|
1576
2114
|
};
|
|
1577
2115
|
}, []);
|
|
1578
|
-
return /* @__PURE__ */
|
|
1579
|
-
/* @__PURE__ */
|
|
2116
|
+
return /* @__PURE__ */ jsxs19("div", { className: "relative flex min-h-0 flex-1 flex-col", children: [
|
|
2117
|
+
/* @__PURE__ */ jsx34(
|
|
1580
2118
|
"div",
|
|
1581
2119
|
{
|
|
1582
2120
|
ref,
|
|
@@ -1585,7 +2123,7 @@ function SidebarNav({ children, className, ...rest }) {
|
|
|
1585
2123
|
children
|
|
1586
2124
|
}
|
|
1587
2125
|
),
|
|
1588
|
-
showFade && /* @__PURE__ */
|
|
2126
|
+
showFade && /* @__PURE__ */ jsx34(
|
|
1589
2127
|
"div",
|
|
1590
2128
|
{
|
|
1591
2129
|
className: "pointer-events-none absolute right-0 bottom-0 left-0 h-6 bg-gradient-to-t from-sidebar to-transparent",
|
|
@@ -1643,6 +2181,7 @@ export {
|
|
|
1643
2181
|
PopoverAnchor,
|
|
1644
2182
|
PopoverContent,
|
|
1645
2183
|
PopoverTrigger,
|
|
2184
|
+
ResponsivePanel,
|
|
1646
2185
|
ScrollArea,
|
|
1647
2186
|
ScrollBar,
|
|
1648
2187
|
SearchBar,
|
|
@@ -1679,6 +2218,7 @@ export {
|
|
|
1679
2218
|
TabsContent,
|
|
1680
2219
|
TabsList,
|
|
1681
2220
|
TabsTrigger,
|
|
2221
|
+
TagCombobox,
|
|
1682
2222
|
Textarea,
|
|
1683
2223
|
Tooltip,
|
|
1684
2224
|
TooltipContent,
|
|
@@ -1689,6 +2229,7 @@ export {
|
|
|
1689
2229
|
cn,
|
|
1690
2230
|
loadingSpinnerIconVariants,
|
|
1691
2231
|
sheetVariants,
|
|
2232
|
+
useMediaQuery,
|
|
1692
2233
|
useSidebarCollapsed
|
|
1693
2234
|
};
|
|
1694
2235
|
//# sourceMappingURL=index.js.map
|