@arbidocs/react 0.3.185 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-YJAYST7S.js → chunk-3CLGHHME.js} +880 -240
- package/dist/chunk-3CLGHHME.js.map +1 -0
- package/dist/chunk-6ALMBER7.js +176 -0
- package/dist/chunk-6ALMBER7.js.map +1 -0
- package/dist/chunk-BXP6S34V.cjs +284 -0
- package/dist/chunk-BXP6S34V.cjs.map +1 -0
- package/dist/chunk-EJ2BMHYK.js +272 -0
- package/dist/chunk-EJ2BMHYK.js.map +1 -0
- package/dist/{chunk-IZPXHVPJ.cjs → chunk-OU2QLIKS.cjs} +969 -344
- package/dist/chunk-OU2QLIKS.cjs.map +1 -0
- package/dist/chunk-RZQACYNQ.cjs +218 -0
- package/dist/chunk-RZQACYNQ.cjs.map +1 -0
- package/dist/{index-4WwLZsQO.d.cts → index-CcbVSNSP.d.cts} +40 -2
- package/dist/{index-4WwLZsQO.d.ts → index-CcbVSNSP.d.ts} +40 -2
- package/dist/index.cjs +589 -1070
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +11 -155
- package/dist/index.d.ts +11 -155
- package/dist/index.js +223 -725
- package/dist/index.js.map +1 -1
- package/dist/shell/index.cjs +13 -4
- package/dist/shell/index.d.cts +1 -1
- package/dist/shell/index.d.ts +1 -1
- package/dist/shell/index.js +2 -1
- package/dist/ui/index.cjs +211 -190
- package/dist/ui/index.d.cts +204 -3
- package/dist/ui/index.d.ts +204 -3
- package/dist/ui/index.js +2 -1
- package/dist/viewers/index.cjs +327 -76
- package/dist/viewers/index.cjs.map +1 -1
- package/dist/viewers/index.d.cts +90 -4
- package/dist/viewers/index.d.ts +90 -4
- package/dist/viewers/index.js +316 -78
- package/dist/viewers/index.js.map +1 -1
- package/package.json +4 -3
- package/dist/chunk-3MLQUKT4.cjs +0 -97
- package/dist/chunk-3MLQUKT4.cjs.map +0 -1
- package/dist/chunk-IZPXHVPJ.cjs.map +0 -1
- package/dist/chunk-J3XEKOV5.js +0 -87
- package/dist/chunk-J3XEKOV5.js.map +0 -1
- package/dist/chunk-YJAYST7S.js.map +0 -1
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
import { clsx } from 'clsx';
|
|
2
|
+
import { twMerge } from 'tailwind-merge';
|
|
3
|
+
import * as React2 from 'react';
|
|
4
|
+
import * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area';
|
|
5
|
+
import { jsxs, jsx } from 'react/jsx-runtime';
|
|
6
|
+
import * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu';
|
|
7
|
+
import { ChevronRight, Check, Circle } from 'lucide-react';
|
|
8
|
+
|
|
9
|
+
// src/lib/utils.ts
|
|
10
|
+
function cn(...inputs) {
|
|
11
|
+
return twMerge(clsx(inputs));
|
|
12
|
+
}
|
|
13
|
+
var ScrollArea = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
14
|
+
ScrollAreaPrimitive.Root,
|
|
15
|
+
{
|
|
16
|
+
ref,
|
|
17
|
+
className: cn("relative overflow-hidden", className),
|
|
18
|
+
...props,
|
|
19
|
+
children: [
|
|
20
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Viewport, { className: "h-full w-full rounded-[inherit]", children }),
|
|
21
|
+
/* @__PURE__ */ jsx(ScrollBar, {}),
|
|
22
|
+
/* @__PURE__ */ jsx(ScrollAreaPrimitive.Corner, {})
|
|
23
|
+
]
|
|
24
|
+
}
|
|
25
|
+
));
|
|
26
|
+
ScrollArea.displayName = ScrollAreaPrimitive.Root.displayName;
|
|
27
|
+
var ScrollBar = React2.forwardRef(({ className, orientation = "vertical", ...props }, ref) => /* @__PURE__ */ jsx(
|
|
28
|
+
ScrollAreaPrimitive.ScrollAreaScrollbar,
|
|
29
|
+
{
|
|
30
|
+
ref,
|
|
31
|
+
orientation,
|
|
32
|
+
className: cn(
|
|
33
|
+
"flex touch-none select-none transition-colors",
|
|
34
|
+
orientation === "vertical" && "h-full w-2.5 border-l border-l-transparent p-[1px]",
|
|
35
|
+
orientation === "horizontal" && "h-2.5 flex-col border-t border-t-transparent p-[1px]",
|
|
36
|
+
className
|
|
37
|
+
),
|
|
38
|
+
...props,
|
|
39
|
+
children: /* @__PURE__ */ jsx(ScrollAreaPrimitive.ScrollAreaThumb, { className: "relative flex-1 rounded-full bg-border" })
|
|
40
|
+
}
|
|
41
|
+
));
|
|
42
|
+
ScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName;
|
|
43
|
+
|
|
44
|
+
// src/lib/collision-padding.ts
|
|
45
|
+
function getCollisionPadding() {
|
|
46
|
+
const base = 8;
|
|
47
|
+
if (typeof window === "undefined") return { top: base, right: base, bottom: base, left: base };
|
|
48
|
+
const hidden = Math.max(
|
|
49
|
+
0,
|
|
50
|
+
(document.documentElement?.clientHeight ?? window.innerHeight) - window.innerHeight
|
|
51
|
+
);
|
|
52
|
+
return { top: base, right: base, bottom: base + hidden, left: base };
|
|
53
|
+
}
|
|
54
|
+
var DropdownMenu = DropdownMenuPrimitive.Root;
|
|
55
|
+
var DropdownMenuTrigger = DropdownMenuPrimitive.Trigger;
|
|
56
|
+
var DropdownMenuGroup = DropdownMenuPrimitive.Group;
|
|
57
|
+
var DropdownMenuPortal = DropdownMenuPrimitive.Portal;
|
|
58
|
+
var DropdownMenuSub = DropdownMenuPrimitive.Sub;
|
|
59
|
+
var DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup;
|
|
60
|
+
var DropdownMenuSubTrigger = React2.forwardRef(({ className, inset, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
61
|
+
DropdownMenuPrimitive.SubTrigger,
|
|
62
|
+
{
|
|
63
|
+
ref,
|
|
64
|
+
className: cn(
|
|
65
|
+
"flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-11 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0",
|
|
66
|
+
inset && "pl-8",
|
|
67
|
+
className
|
|
68
|
+
),
|
|
69
|
+
...props,
|
|
70
|
+
children: [
|
|
71
|
+
children,
|
|
72
|
+
/* @__PURE__ */ jsx(ChevronRight, { className: "ml-auto" })
|
|
73
|
+
]
|
|
74
|
+
}
|
|
75
|
+
));
|
|
76
|
+
DropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName;
|
|
77
|
+
var DropdownMenuSubContent = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
78
|
+
DropdownMenuPrimitive.SubContent,
|
|
79
|
+
{
|
|
80
|
+
ref,
|
|
81
|
+
collisionPadding: getCollisionPadding(),
|
|
82
|
+
className: cn(
|
|
83
|
+
"z-50 min-w-[8rem] max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
84
|
+
className
|
|
85
|
+
),
|
|
86
|
+
...props
|
|
87
|
+
}
|
|
88
|
+
));
|
|
89
|
+
DropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName;
|
|
90
|
+
var DropdownMenuContent = React2.forwardRef(({ className, sideOffset = 4, ...props }, ref) => /* @__PURE__ */ jsx(DropdownMenuPrimitive.Portal, { children: /* @__PURE__ */ jsx(
|
|
91
|
+
DropdownMenuPrimitive.Content,
|
|
92
|
+
{
|
|
93
|
+
ref,
|
|
94
|
+
sideOffset,
|
|
95
|
+
collisionPadding: getCollisionPadding(),
|
|
96
|
+
className: cn(
|
|
97
|
+
"z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] max-w-[calc(100vw-1rem)] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg",
|
|
98
|
+
"data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]",
|
|
99
|
+
className
|
|
100
|
+
),
|
|
101
|
+
...props
|
|
102
|
+
}
|
|
103
|
+
) }));
|
|
104
|
+
DropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName;
|
|
105
|
+
var DropdownMenuItem = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
106
|
+
DropdownMenuPrimitive.Item,
|
|
107
|
+
{
|
|
108
|
+
ref,
|
|
109
|
+
className: cn(
|
|
110
|
+
"relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0",
|
|
111
|
+
inset && "pl-8",
|
|
112
|
+
className
|
|
113
|
+
),
|
|
114
|
+
...props
|
|
115
|
+
}
|
|
116
|
+
));
|
|
117
|
+
DropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName;
|
|
118
|
+
var DropdownMenuCheckboxItem = React2.forwardRef(({ className, children, checked, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
119
|
+
DropdownMenuPrimitive.CheckboxItem,
|
|
120
|
+
{
|
|
121
|
+
ref,
|
|
122
|
+
className: cn(
|
|
123
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
124
|
+
className
|
|
125
|
+
),
|
|
126
|
+
checked,
|
|
127
|
+
...props,
|
|
128
|
+
children: [
|
|
129
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-4 w-4 items-center justify-center rounded border border-border bg-popover", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Check, { className: "h-3.5 w-3.5 text-primary" }) }) }),
|
|
130
|
+
children
|
|
131
|
+
]
|
|
132
|
+
}
|
|
133
|
+
));
|
|
134
|
+
DropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName;
|
|
135
|
+
var DropdownMenuRadioItem = React2.forwardRef(({ className, children, ...props }, ref) => /* @__PURE__ */ jsxs(
|
|
136
|
+
DropdownMenuPrimitive.RadioItem,
|
|
137
|
+
{
|
|
138
|
+
ref,
|
|
139
|
+
className: cn(
|
|
140
|
+
"relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50",
|
|
141
|
+
className
|
|
142
|
+
),
|
|
143
|
+
...props,
|
|
144
|
+
children: [
|
|
145
|
+
/* @__PURE__ */ jsx("span", { className: "absolute left-2 flex h-3.5 w-3.5 items-center justify-center", children: /* @__PURE__ */ jsx(DropdownMenuPrimitive.ItemIndicator, { children: /* @__PURE__ */ jsx(Circle, { className: "h-2 w-2 fill-current" }) }) }),
|
|
146
|
+
children
|
|
147
|
+
]
|
|
148
|
+
}
|
|
149
|
+
));
|
|
150
|
+
DropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName;
|
|
151
|
+
var DropdownMenuLabel = React2.forwardRef(({ className, inset, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
152
|
+
DropdownMenuPrimitive.Label,
|
|
153
|
+
{
|
|
154
|
+
ref,
|
|
155
|
+
className: cn("px-2 py-1.5 text-sm font-semibold", inset && "pl-8", className),
|
|
156
|
+
...props
|
|
157
|
+
}
|
|
158
|
+
));
|
|
159
|
+
DropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName;
|
|
160
|
+
var DropdownMenuSeparator = React2.forwardRef(({ className, ...props }, ref) => /* @__PURE__ */ jsx(
|
|
161
|
+
DropdownMenuPrimitive.Separator,
|
|
162
|
+
{
|
|
163
|
+
ref,
|
|
164
|
+
className: cn("-mx-1 my-1 h-px bg-muted", className),
|
|
165
|
+
...props
|
|
166
|
+
}
|
|
167
|
+
));
|
|
168
|
+
DropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName;
|
|
169
|
+
var DropdownMenuShortcut = ({ className, ...props }) => {
|
|
170
|
+
return /* @__PURE__ */ jsx("span", { className: cn("ml-auto text-sm tracking-widest opacity-60", className), ...props });
|
|
171
|
+
};
|
|
172
|
+
DropdownMenuShortcut.displayName = "DropdownMenuShortcut";
|
|
173
|
+
|
|
174
|
+
export { DropdownMenu, DropdownMenuCheckboxItem, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuPortal, DropdownMenuRadioGroup, DropdownMenuRadioItem, DropdownMenuSeparator, DropdownMenuShortcut, DropdownMenuSub, DropdownMenuSubContent, DropdownMenuSubTrigger, DropdownMenuTrigger, ScrollArea, ScrollBar, cn, getCollisionPadding };
|
|
175
|
+
//# sourceMappingURL=chunk-6ALMBER7.js.map
|
|
176
|
+
//# sourceMappingURL=chunk-6ALMBER7.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/lib/utils.ts","../src/ui/scroll-area.tsx","../src/lib/collision-padding.ts","../src/ui/dropdown-menu.tsx"],"names":["React","jsxs","jsx"],"mappings":"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","file":"chunk-6ALMBER7.js","sourcesContent":["/**\n * Core utility: `cn()` — merge class names with Tailwind-aware conflict\n * resolution (clsx + tailwind-merge). Copied faithfully from the ARBI app so the\n * promoted \"lego\" components style identically in a consumer app.\n */\n\nimport { clsx, type ClassValue } from 'clsx'\nimport { twMerge } from 'tailwind-merge'\n\nexport function cn(...inputs: ClassValue[]) {\n return twMerge(clsx(inputs))\n}\n","import * as React from 'react'\nimport * as ScrollAreaPrimitive from '@radix-ui/react-scroll-area'\n\nimport { cn } from '../lib/utils'\n\nconst ScrollArea = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.Root>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.Root>\n>(({ className, children, ...props }, ref) => (\n <ScrollAreaPrimitive.Root\n ref={ref}\n className={cn('relative overflow-hidden', className)}\n {...props}\n >\n <ScrollAreaPrimitive.Viewport className=\"h-full w-full rounded-[inherit]\">\n {children}\n </ScrollAreaPrimitive.Viewport>\n <ScrollBar />\n <ScrollAreaPrimitive.Corner />\n </ScrollAreaPrimitive.Root>\n))\nScrollArea.displayName = ScrollAreaPrimitive.Root.displayName\n\nconst ScrollBar = React.forwardRef<\n React.ElementRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>,\n React.ComponentPropsWithoutRef<typeof ScrollAreaPrimitive.ScrollAreaScrollbar>\n>(({ className, orientation = 'vertical', ...props }, ref) => (\n <ScrollAreaPrimitive.ScrollAreaScrollbar\n ref={ref}\n orientation={orientation}\n className={cn(\n 'flex touch-none select-none transition-colors',\n orientation === 'vertical' && 'h-full w-2.5 border-l border-l-transparent p-[1px]',\n orientation === 'horizontal' && 'h-2.5 flex-col border-t border-t-transparent p-[1px]',\n className\n )}\n {...props}\n >\n <ScrollAreaPrimitive.ScrollAreaThumb className=\"relative flex-1 rounded-full bg-border\" />\n </ScrollAreaPrimitive.ScrollAreaScrollbar>\n))\nScrollBar.displayName = ScrollAreaPrimitive.ScrollAreaScrollbar.displayName\n\nexport { ScrollArea, ScrollBar }\n","/**\n * Collision padding for Radix poppers (menus, popovers, selects, hover cards).\n *\n * 8px on every side, plus, at the bottom, the amount by which the layout viewport is taller than\n * the visible one. Mobile Safari (and its landscape emulation) reports a layout viewport that is\n * larger than `innerHeight` while the toolbars are showing, so Radix would otherwise place a menu\n * under the bottom bar.\n */\nexport function getCollisionPadding(): {\n top: number\n right: number\n bottom: number\n left: number\n} {\n const base = 8\n if (typeof window === 'undefined') return { top: base, right: base, bottom: base, left: base }\n const hidden = Math.max(\n 0,\n (document.documentElement?.clientHeight ?? window.innerHeight) - window.innerHeight\n )\n return { top: base, right: base, bottom: base + hidden, left: base }\n}\n","'use client'\n\nimport * as React from 'react'\nimport * as DropdownMenuPrimitive from '@radix-ui/react-dropdown-menu'\nimport { Check, ChevronRight, Circle } from 'lucide-react'\n\nimport { cn } from '../lib/utils'\nimport { getCollisionPadding } from '../lib/collision-padding'\n\nconst DropdownMenu = DropdownMenuPrimitive.Root\n\nconst DropdownMenuTrigger = DropdownMenuPrimitive.Trigger\n\nconst DropdownMenuGroup = DropdownMenuPrimitive.Group\n\nconst DropdownMenuPortal = DropdownMenuPrimitive.Portal\n\nconst DropdownMenuSub = DropdownMenuPrimitive.Sub\n\nconst DropdownMenuRadioGroup = DropdownMenuPrimitive.RadioGroup\n\nconst DropdownMenuSubTrigger = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubTrigger>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubTrigger> & {\n inset?: boolean\n }\n>(({ className, inset, children, ...props }, ref) => (\n <DropdownMenuPrimitive.SubTrigger\n ref={ref}\n className={cn(\n 'flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-11 text-sm outline-none focus:bg-accent data-[state=open]:bg-accent [&_svg]:pointer-events-none [&_svg]:size-4 [&_svg]:shrink-0',\n inset && 'pl-8',\n className\n )}\n {...props}\n >\n {children}\n <ChevronRight className=\"ml-auto\" />\n </DropdownMenuPrimitive.SubTrigger>\n))\nDropdownMenuSubTrigger.displayName = DropdownMenuPrimitive.SubTrigger.displayName\n\nconst DropdownMenuSubContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.SubContent>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.SubContent>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.SubContent\n ref={ref}\n collisionPadding={getCollisionPadding()}\n className={cn(\n 'z-50 min-w-[8rem] max-w-[calc(100vw-1rem)] max-h-[var(--radix-dropdown-menu-content-available-height)] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',\n className\n )}\n {...props}\n />\n))\nDropdownMenuSubContent.displayName = DropdownMenuPrimitive.SubContent.displayName\n\nconst DropdownMenuContent = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Content>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Content>\n>(({ className, sideOffset = 4, ...props }, ref) => (\n <DropdownMenuPrimitive.Portal>\n <DropdownMenuPrimitive.Content\n ref={ref}\n sideOffset={sideOffset}\n collisionPadding={getCollisionPadding()}\n className={cn(\n 'z-50 max-h-[var(--radix-dropdown-menu-content-available-height)] min-w-[8rem] max-w-[calc(100vw-1rem)] overflow-y-auto overflow-x-hidden rounded-md border bg-popover p-1 text-popover-foreground shadow-lg',\n 'data-[state=open]:animate-in data-[state=closed]:animate-out data-[state=closed]:fade-out-0 data-[state=open]:fade-in-0 data-[state=closed]:zoom-out-95 data-[state=open]:zoom-in-95 data-[side=bottom]:slide-in-from-top-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 origin-[--radix-dropdown-menu-content-transform-origin]',\n className\n )}\n {...props}\n />\n </DropdownMenuPrimitive.Portal>\n))\nDropdownMenuContent.displayName = DropdownMenuPrimitive.Content.displayName\n\nconst DropdownMenuItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Item>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Item> & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Item\n ref={ref}\n className={cn(\n 'relative flex cursor-default select-none items-center gap-2 rounded-sm px-2 py-1.5 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50 [&>svg]:size-4 [&>svg]:shrink-0',\n inset && 'pl-8',\n className\n )}\n {...props}\n />\n))\nDropdownMenuItem.displayName = DropdownMenuPrimitive.Item.displayName\n\nconst DropdownMenuCheckboxItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.CheckboxItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.CheckboxItem>\n>(({ className, children, checked, ...props }, ref) => (\n <DropdownMenuPrimitive.CheckboxItem\n ref={ref}\n className={cn(\n 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n className\n )}\n checked={checked}\n {...props}\n >\n <span className=\"absolute left-2 flex h-4 w-4 items-center justify-center rounded border border-border bg-popover\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Check className=\"h-3.5 w-3.5 text-primary\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.CheckboxItem>\n))\nDropdownMenuCheckboxItem.displayName = DropdownMenuPrimitive.CheckboxItem.displayName\n\nconst DropdownMenuRadioItem = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.RadioItem>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.RadioItem>\n>(({ className, children, ...props }, ref) => (\n <DropdownMenuPrimitive.RadioItem\n ref={ref}\n className={cn(\n 'relative flex cursor-default select-none items-center rounded-sm py-1.5 pl-8 pr-2 pointer-coarse:min-h-11 text-sm outline-none transition-colors focus:bg-accent focus:text-accent-foreground data-[disabled]:pointer-events-none data-[disabled]:opacity-50',\n className\n )}\n {...props}\n >\n <span className=\"absolute left-2 flex h-3.5 w-3.5 items-center justify-center\">\n <DropdownMenuPrimitive.ItemIndicator>\n <Circle className=\"h-2 w-2 fill-current\" />\n </DropdownMenuPrimitive.ItemIndicator>\n </span>\n {children}\n </DropdownMenuPrimitive.RadioItem>\n))\nDropdownMenuRadioItem.displayName = DropdownMenuPrimitive.RadioItem.displayName\n\nconst DropdownMenuLabel = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Label>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Label> & {\n inset?: boolean\n }\n>(({ className, inset, ...props }, ref) => (\n <DropdownMenuPrimitive.Label\n ref={ref}\n className={cn('px-2 py-1.5 text-sm font-semibold', inset && 'pl-8', className)}\n {...props}\n />\n))\nDropdownMenuLabel.displayName = DropdownMenuPrimitive.Label.displayName\n\nconst DropdownMenuSeparator = React.forwardRef<\n React.ElementRef<typeof DropdownMenuPrimitive.Separator>,\n React.ComponentPropsWithoutRef<typeof DropdownMenuPrimitive.Separator>\n>(({ className, ...props }, ref) => (\n <DropdownMenuPrimitive.Separator\n ref={ref}\n className={cn('-mx-1 my-1 h-px bg-muted', className)}\n {...props}\n />\n))\nDropdownMenuSeparator.displayName = DropdownMenuPrimitive.Separator.displayName\n\nconst DropdownMenuShortcut = ({ className, ...props }: React.HTMLAttributes<HTMLSpanElement>) => {\n return <span className={cn('ml-auto text-sm tracking-widest opacity-60', className)} {...props} />\n}\nDropdownMenuShortcut.displayName = 'DropdownMenuShortcut'\n\nexport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n DropdownMenuCheckboxItem,\n DropdownMenuRadioItem,\n DropdownMenuLabel,\n DropdownMenuSeparator,\n DropdownMenuShortcut,\n DropdownMenuGroup,\n DropdownMenuPortal,\n DropdownMenuSub,\n DropdownMenuSubContent,\n DropdownMenuSubTrigger,\n DropdownMenuRadioGroup,\n}\n"]}
|
|
@@ -0,0 +1,284 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var chunkRZQACYNQ_cjs = require('./chunk-RZQACYNQ.cjs');
|
|
4
|
+
var sonner = require('sonner');
|
|
5
|
+
var zustand = require('zustand');
|
|
6
|
+
var lucideReact = require('lucide-react');
|
|
7
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
8
|
+
|
|
9
|
+
var seq = 0;
|
|
10
|
+
var uid = () => `n${Date.now().toString(36)}${(seq++).toString(36)}`;
|
|
11
|
+
var useNotify = zustand.create((set) => ({
|
|
12
|
+
notifications: [],
|
|
13
|
+
markRead: (id) => set((s) => ({
|
|
14
|
+
notifications: s.notifications.map((n) => n.id === id ? { ...n, read: true } : n)
|
|
15
|
+
})),
|
|
16
|
+
markAllRead: () => set((s) => ({ notifications: s.notifications.map((n) => ({ ...n, read: true })) })),
|
|
17
|
+
removeNotification: (id) => set((s) => ({ notifications: s.notifications.filter((n) => n.id !== id) })),
|
|
18
|
+
clearNotifications: () => set({ notifications: [] }),
|
|
19
|
+
seed: (items) => set({ notifications: items }),
|
|
20
|
+
record: (note) => set((s) => ({ notifications: [note, ...s.notifications].slice(0, 50) })),
|
|
21
|
+
syncSource: (source, items) => set((s) => ({
|
|
22
|
+
notifications: [
|
|
23
|
+
...items.map((n) => ({ ...n, source })),
|
|
24
|
+
...s.notifications.filter((n) => n.source !== source)
|
|
25
|
+
].sort((a, b) => b.ts - a.ts).slice(0, 100)
|
|
26
|
+
}))
|
|
27
|
+
}));
|
|
28
|
+
function make(variant) {
|
|
29
|
+
return (title, opts = {}) => {
|
|
30
|
+
sonner.toast[variant](title, {
|
|
31
|
+
description: opts.description,
|
|
32
|
+
duration: opts.duration,
|
|
33
|
+
action: opts.action,
|
|
34
|
+
icon: opts.icon
|
|
35
|
+
});
|
|
36
|
+
const id = uid();
|
|
37
|
+
if (opts.record !== false) {
|
|
38
|
+
useNotify.getState().record({
|
|
39
|
+
id,
|
|
40
|
+
title,
|
|
41
|
+
description: opts.description,
|
|
42
|
+
variant,
|
|
43
|
+
ts: Date.now(),
|
|
44
|
+
read: false,
|
|
45
|
+
href: opts.href,
|
|
46
|
+
ambient: opts.ambient
|
|
47
|
+
});
|
|
48
|
+
}
|
|
49
|
+
return id;
|
|
50
|
+
};
|
|
51
|
+
}
|
|
52
|
+
var notify = {
|
|
53
|
+
success: make("success"),
|
|
54
|
+
error: make("error"),
|
|
55
|
+
info: make("info"),
|
|
56
|
+
warning: make("warning")
|
|
57
|
+
};
|
|
58
|
+
function seedNotifications(items) {
|
|
59
|
+
useNotify.getState().seed(items);
|
|
60
|
+
}
|
|
61
|
+
var selectUnread = (s) => s.notifications.filter((n) => !n.read && !n.ambient).length;
|
|
62
|
+
var DEFAULT_NOTIFY_TONE = {
|
|
63
|
+
success: "text-green-600",
|
|
64
|
+
error: "text-destructive",
|
|
65
|
+
warning: "text-amber-500",
|
|
66
|
+
info: "text-blue-500"
|
|
67
|
+
};
|
|
68
|
+
|
|
69
|
+
// src/lib/format-relative-time.ts
|
|
70
|
+
var RELATIVE = new Intl.RelativeTimeFormat(void 0, { numeric: "auto" });
|
|
71
|
+
var DIVISIONS = [
|
|
72
|
+
{ amount: 60, unit: "seconds" },
|
|
73
|
+
{ amount: 60, unit: "minutes" },
|
|
74
|
+
{ amount: 24, unit: "hours" },
|
|
75
|
+
{ amount: 7, unit: "days" },
|
|
76
|
+
{ amount: 4.34524, unit: "weeks" },
|
|
77
|
+
{ amount: 12, unit: "months" },
|
|
78
|
+
{ amount: Number.POSITIVE_INFINITY, unit: "years" }
|
|
79
|
+
];
|
|
80
|
+
function formatRelativeTime(iso) {
|
|
81
|
+
const then = Date.parse(iso);
|
|
82
|
+
if (Number.isNaN(then)) return "";
|
|
83
|
+
let duration = (then - Date.now()) / 1e3;
|
|
84
|
+
if (Math.abs(duration) < 45) return "just now";
|
|
85
|
+
for (const division of DIVISIONS) {
|
|
86
|
+
if (Math.abs(duration) < division.amount) {
|
|
87
|
+
return RELATIVE.format(Math.round(duration), division.unit);
|
|
88
|
+
}
|
|
89
|
+
duration /= division.amount;
|
|
90
|
+
}
|
|
91
|
+
return "";
|
|
92
|
+
}
|
|
93
|
+
var GLYPH = {
|
|
94
|
+
success: lucideReact.CheckCircle2,
|
|
95
|
+
error: lucideReact.XCircle,
|
|
96
|
+
warning: lucideReact.AlertTriangle,
|
|
97
|
+
info: lucideReact.Info
|
|
98
|
+
};
|
|
99
|
+
function NotificationBell({
|
|
100
|
+
renderTrigger,
|
|
101
|
+
onNavigate,
|
|
102
|
+
onMarkRead,
|
|
103
|
+
onMarkAllRead,
|
|
104
|
+
muted = false,
|
|
105
|
+
onToggleMute,
|
|
106
|
+
toneClassNames,
|
|
107
|
+
emptyText = "You're all caught up.",
|
|
108
|
+
onViewAll,
|
|
109
|
+
viewAllLabel = "View all",
|
|
110
|
+
testId = "notifications",
|
|
111
|
+
side = "bottom",
|
|
112
|
+
align = "end"
|
|
113
|
+
}) {
|
|
114
|
+
const notifications = useNotify((s) => s.notifications);
|
|
115
|
+
const unread = useNotify(selectUnread);
|
|
116
|
+
const markAllRead = useNotify((s) => s.markAllRead);
|
|
117
|
+
const markRead = useNotify((s) => s.markRead);
|
|
118
|
+
const tone = (v) => toneClassNames?.[v] ?? DEFAULT_NOTIFY_TONE[v];
|
|
119
|
+
const visible = notifications.filter((n) => !n.ambient);
|
|
120
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(chunkRZQACYNQ_cjs.DropdownMenu, { children: [
|
|
121
|
+
/* @__PURE__ */ jsxRuntime.jsx(chunkRZQACYNQ_cjs.DropdownMenuTrigger, { asChild: true, children: renderTrigger ? renderTrigger({
|
|
122
|
+
unread,
|
|
123
|
+
ariaLabel: `Notifications${unread ? `, ${unread} unread` : ""}`,
|
|
124
|
+
testId
|
|
125
|
+
}) : /* @__PURE__ */ jsxRuntime.jsxs(
|
|
126
|
+
"button",
|
|
127
|
+
{
|
|
128
|
+
className: "relative flex size-9 pointer-coarse:size-11 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground",
|
|
129
|
+
"data-testid": testId,
|
|
130
|
+
"aria-label": `Notifications${unread ? `, ${unread} unread` : ""}`,
|
|
131
|
+
title: "Notifications",
|
|
132
|
+
children: [
|
|
133
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.Bell, { className: "size-5" }),
|
|
134
|
+
unread > 0 && /* @__PURE__ */ jsxRuntime.jsx(
|
|
135
|
+
"span",
|
|
136
|
+
{
|
|
137
|
+
className: "absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-xs font-semibold leading-4 text-primary-foreground",
|
|
138
|
+
"data-testid": `${testId}-badge`,
|
|
139
|
+
children: unread > 9 ? "9+" : unread
|
|
140
|
+
}
|
|
141
|
+
)
|
|
142
|
+
]
|
|
143
|
+
}
|
|
144
|
+
) }),
|
|
145
|
+
/* @__PURE__ */ jsxRuntime.jsxs(
|
|
146
|
+
chunkRZQACYNQ_cjs.DropdownMenuContent,
|
|
147
|
+
{
|
|
148
|
+
align,
|
|
149
|
+
side,
|
|
150
|
+
sideOffset: 8,
|
|
151
|
+
collisionPadding: 12,
|
|
152
|
+
className: "w-[360px] rounded-lg border-border/60 p-0 shadow-lg",
|
|
153
|
+
"data-testid": `${testId}-panel`,
|
|
154
|
+
"data-topbar-popover": "",
|
|
155
|
+
children: [
|
|
156
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center justify-between gap-3 border-b border-border px-3.5 py-2.5 pointer-coarse:py-0", children: [
|
|
157
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-semibold text-foreground", children: "Notifications" }),
|
|
158
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-3 pointer-coarse:gap-1", children: [
|
|
159
|
+
unread > 0 && /* @__PURE__ */ jsxRuntime.jsxs(
|
|
160
|
+
"button",
|
|
161
|
+
{
|
|
162
|
+
onClick: () => {
|
|
163
|
+
markAllRead();
|
|
164
|
+
onMarkAllRead?.();
|
|
165
|
+
},
|
|
166
|
+
className: "flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:opacity-80 pointer-coarse:min-h-11 pointer-coarse:px-2",
|
|
167
|
+
"data-testid": `${testId}-mark-all`,
|
|
168
|
+
children: [
|
|
169
|
+
/* @__PURE__ */ jsxRuntime.jsx(lucideReact.CheckCheck, { className: "size-3.5" }),
|
|
170
|
+
" Mark all read"
|
|
171
|
+
]
|
|
172
|
+
}
|
|
173
|
+
),
|
|
174
|
+
onToggleMute && /* @__PURE__ */ jsxRuntime.jsx(
|
|
175
|
+
"button",
|
|
176
|
+
{
|
|
177
|
+
onClick: onToggleMute,
|
|
178
|
+
"aria-pressed": muted,
|
|
179
|
+
title: muted ? "Unmute notifications" : "Mute notifications",
|
|
180
|
+
"aria-label": muted ? "Unmute notifications" : "Mute notifications",
|
|
181
|
+
className: chunkRZQACYNQ_cjs.cn(
|
|
182
|
+
"flex size-6 items-center justify-center rounded-md transition-colors hover:bg-accent pointer-coarse:size-11",
|
|
183
|
+
muted ? "text-foreground" : "text-muted-foreground hover:text-foreground"
|
|
184
|
+
),
|
|
185
|
+
"data-testid": `${testId}-mute`,
|
|
186
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.BellOff, { className: "size-3.5" })
|
|
187
|
+
}
|
|
188
|
+
),
|
|
189
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
190
|
+
chunkRZQACYNQ_cjs.DropdownMenuItem,
|
|
191
|
+
{
|
|
192
|
+
className: "flex size-6 items-center justify-center rounded-md p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus:bg-accent pointer-coarse:size-11",
|
|
193
|
+
"aria-label": "Close notifications",
|
|
194
|
+
"data-testid": `${testId}-close`,
|
|
195
|
+
children: /* @__PURE__ */ jsxRuntime.jsx(lucideReact.X, { className: "size-3.5" })
|
|
196
|
+
}
|
|
197
|
+
)
|
|
198
|
+
] })
|
|
199
|
+
] }),
|
|
200
|
+
muted && /* @__PURE__ */ jsxRuntime.jsx(
|
|
201
|
+
"p",
|
|
202
|
+
{
|
|
203
|
+
className: "border-b border-border/60 bg-amber-500/10 px-3.5 py-1.5 text-xs text-muted-foreground",
|
|
204
|
+
"data-testid": `${testId}-muted-note`,
|
|
205
|
+
children: "Toasts are muted \u2014 everything still lands here."
|
|
206
|
+
}
|
|
207
|
+
),
|
|
208
|
+
visible.length === 0 ? /* @__PURE__ */ jsxRuntime.jsx("div", { className: "px-3.5 py-10 text-center text-sm text-muted-foreground", children: emptyText }) : /* @__PURE__ */ jsxRuntime.jsx(chunkRZQACYNQ_cjs.ScrollArea, { className: "max-h-[380px]", children: /* @__PURE__ */ jsxRuntime.jsx("ul", { "data-testid": `${testId}-list`, children: visible.map((n) => {
|
|
209
|
+
const Glyph = GLYPH[n.variant];
|
|
210
|
+
return /* @__PURE__ */ jsxRuntime.jsx("li", { children: /* @__PURE__ */ jsxRuntime.jsxs(
|
|
211
|
+
"button",
|
|
212
|
+
{
|
|
213
|
+
onClick: () => {
|
|
214
|
+
markRead(n.id);
|
|
215
|
+
if (!n.read) onMarkRead?.(n.id);
|
|
216
|
+
if (n.onSelect) n.onSelect();
|
|
217
|
+
else if (n.href) onNavigate?.(n.href);
|
|
218
|
+
},
|
|
219
|
+
className: chunkRZQACYNQ_cjs.cn(
|
|
220
|
+
"flex w-full items-start gap-3 border-b border-border/60 px-3.5 py-3 text-left transition-colors hover:bg-accent",
|
|
221
|
+
!n.read && "bg-accent/40"
|
|
222
|
+
),
|
|
223
|
+
"data-testid": `${testId}-item`,
|
|
224
|
+
children: [
|
|
225
|
+
/* @__PURE__ */ jsxRuntime.jsx(Glyph, { className: chunkRZQACYNQ_cjs.cn("mt-0.5 size-4 shrink-0", tone(n.variant)) }),
|
|
226
|
+
/* @__PURE__ */ jsxRuntime.jsxs("div", { className: "min-w-0 flex-1", children: [
|
|
227
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "text-sm font-medium leading-snug text-foreground", children: n.title }),
|
|
228
|
+
n.description && /* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-0.5 line-clamp-2 text-xs leading-relaxed text-muted-foreground", children: n.description }),
|
|
229
|
+
/* @__PURE__ */ jsxRuntime.jsx("p", { className: "mt-1 text-xs text-muted-foreground/80", children: formatRelativeTime(new Date(n.ts).toISOString()) })
|
|
230
|
+
] }),
|
|
231
|
+
!n.read && /* @__PURE__ */ jsxRuntime.jsx("span", { className: "mt-1.5 size-2 shrink-0 rounded-full bg-primary" })
|
|
232
|
+
]
|
|
233
|
+
}
|
|
234
|
+
) }, n.id);
|
|
235
|
+
}) }) }),
|
|
236
|
+
onViewAll && // A real menu item, not a bare button: selecting it must CLOSE the
|
|
237
|
+
// dropdown on its way to the full surface.
|
|
238
|
+
/* @__PURE__ */ jsxRuntime.jsx(
|
|
239
|
+
chunkRZQACYNQ_cjs.DropdownMenuItem,
|
|
240
|
+
{
|
|
241
|
+
onClick: onViewAll,
|
|
242
|
+
className: "justify-center rounded-none border-t border-border px-3.5 py-2.5 text-xs font-medium text-primary focus:text-primary pointer-coarse:min-h-11",
|
|
243
|
+
"data-testid": `${testId}-view-all`,
|
|
244
|
+
children: viewAllLabel
|
|
245
|
+
}
|
|
246
|
+
)
|
|
247
|
+
]
|
|
248
|
+
}
|
|
249
|
+
)
|
|
250
|
+
] });
|
|
251
|
+
}
|
|
252
|
+
var DEFAULT_TOAST_OPTIONS = {
|
|
253
|
+
classNames: {
|
|
254
|
+
toast: "group toast group-[.toaster]:bg-card group-[.toaster]:text-foreground group-[.toaster]:border group-[.toaster]:border-border group-[.toaster]:shadow-lg group-[.toaster]:rounded-lg",
|
|
255
|
+
description: "group-[.toast]:text-muted-foreground",
|
|
256
|
+
actionButton: "group-[.toast]:bg-primary group-[.toast]:text-primary-foreground",
|
|
257
|
+
cancelButton: "group-[.toast]:bg-muted group-[.toast]:text-muted-foreground"
|
|
258
|
+
}
|
|
259
|
+
};
|
|
260
|
+
function Toaster({ toastOptions, ...props }) {
|
|
261
|
+
return /* @__PURE__ */ jsxRuntime.jsx(
|
|
262
|
+
sonner.Toaster,
|
|
263
|
+
{
|
|
264
|
+
className: "toaster group",
|
|
265
|
+
toastOptions: toastOptions ?? DEFAULT_TOAST_OPTIONS,
|
|
266
|
+
...props
|
|
267
|
+
}
|
|
268
|
+
);
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
Object.defineProperty(exports, "toast", {
|
|
272
|
+
enumerable: true,
|
|
273
|
+
get: function () { return sonner.toast; }
|
|
274
|
+
});
|
|
275
|
+
exports.DEFAULT_NOTIFY_TONE = DEFAULT_NOTIFY_TONE;
|
|
276
|
+
exports.NotificationBell = NotificationBell;
|
|
277
|
+
exports.Toaster = Toaster;
|
|
278
|
+
exports.formatRelativeTime = formatRelativeTime;
|
|
279
|
+
exports.notify = notify;
|
|
280
|
+
exports.seedNotifications = seedNotifications;
|
|
281
|
+
exports.selectUnread = selectUnread;
|
|
282
|
+
exports.useNotify = useNotify;
|
|
283
|
+
//# sourceMappingURL=chunk-BXP6S34V.cjs.map
|
|
284
|
+
//# sourceMappingURL=chunk-BXP6S34V.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/notifications/notify-store.ts","../src/lib/format-relative-time.ts","../src/notifications/NotificationBell.tsx","../src/notifications/Toaster.tsx"],"names":["create","toast","CheckCircle2","XCircle","AlertTriangle","Info","DropdownMenu","jsx","DropdownMenuTrigger","jsxs","Bell","DropdownMenuContent","CheckCheck","cn","BellOff","DropdownMenuItem","X","ScrollArea","Sonner"],"mappings":"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","file":"chunk-BXP6S34V.cjs","sourcesContent":["/**\n * App notification store — the one shared toast + notification-centre. A single\n * `notify.*` call pops a transient toast (rendered by <Toaster/>, on sonner) and,\n * unless `{ record: false }`, records a persistent entry for the topbar bell.\n *\n * notify.success('Saved') // toast + centre entry\n * notify.error('Upload failed', { record: false }) // toast only\n *\n * sonner owns the transient toast lifecycle (stacking, timing, dismissal); this\n * store owns only the persistent centre history (what the <NotificationBell/>\n * shows). Brand-neutral: no seed data (inject via `seedNotifications`), no app\n * imports. For full sonner power (custom content, `toast.dismiss`, …) the raw\n * `toast` is re-exported.\n */\nimport { type ReactNode } from 'react'\n// This module IS the sanctioned toast engine home — the app-level rule that\n// steers callers to sendNotification()/notify() doesn't apply here.\n// eslint-disable-next-line no-restricted-imports\nimport { toast } from 'sonner'\nimport { create } from 'zustand'\n\nexport { toast }\n\nexport type NotifyVariant = 'success' | 'error' | 'info' | 'warning'\n\nexport interface AppNotification {\n id: string\n title: string\n description?: string\n variant: NotifyVariant\n /** Epoch ms the entry was recorded. */\n ts: number\n read: boolean\n /** Optional in-app link the entry deep-links to (passed to the bell's onNavigate). */\n href?: string\n /** Optional click action — wins over `href` when both are present. Session\n * history only; entries are never serialized, so a closure is safe here. */\n onSelect?: () => void\n /** Routine session activity (\"Opening workspace…\", \"Saved\") — kept for the\n * log but not something that demands attention: excluded from the unread\n * badge and from the bell's default list (shown under \"All activity\"). */\n ambient?: boolean\n /** Where the entry comes from. Session entries (`record`) have none; a\n * backend bridge stamps its own (e.g. 'server') so {@link syncSource} can\n * replace exactly its slice on every refetch without touching the rest. */\n source?: string\n}\n\nexport interface NotifyOptions {\n description?: string\n /** Also record a persistent notification-centre entry (default true). */\n record?: boolean\n href?: string\n duration?: number\n /** A toast action button (sonner). */\n action?: { label: ReactNode; onClick: () => void }\n /** Override the toast glyph (sonner). */\n icon?: ReactNode\n /** Record as routine session activity (see {@link AppNotification.ambient}). */\n ambient?: boolean\n}\n\ninterface NotifyState {\n notifications: AppNotification[]\n markRead: (id: string) => void\n markAllRead: () => void\n removeNotification: (id: string) => void\n clearNotifications: () => void\n /** Replace the centre's history (e.g. seed a demo/initial set). */\n seed: (items: AppNotification[]) => void\n /** Record a centre entry (used by `notify.*`; sonner renders the toast). */\n record: (note: AppNotification) => void\n /** Reconcile one source's slice with fresh truth (e.g. a backend list on\n * every refetch): entries stamped with `source` are replaced wholesale —\n * including their read state, which the source owns — while entries from\n * other sources (session activity) stay untouched. Newest first. */\n syncSource: (source: string, items: AppNotification[]) => void\n}\n\nlet seq = 0\nconst uid = () => `n${Date.now().toString(36)}${(seq++).toString(36)}`\n\nexport const useNotify = create<NotifyState>((set) => ({\n notifications: [],\n markRead: (id) =>\n set((s) => ({\n notifications: s.notifications.map((n) => (n.id === id ? { ...n, read: true } : n)),\n })),\n markAllRead: () =>\n set((s) => ({ notifications: s.notifications.map((n) => ({ ...n, read: true })) })),\n removeNotification: (id) =>\n set((s) => ({ notifications: s.notifications.filter((n) => n.id !== id) })),\n clearNotifications: () => set({ notifications: [] }),\n seed: (items) => set({ notifications: items }),\n record: (note) => set((s) => ({ notifications: [note, ...s.notifications].slice(0, 50) })),\n syncSource: (source, items) =>\n set((s) => ({\n notifications: [\n ...items.map((n) => ({ ...n, source })),\n ...s.notifications.filter((n) => n.source !== source),\n ]\n .sort((a, b) => b.ts - a.ts)\n .slice(0, 100),\n })),\n}))\n\n/** Ergonomic helper — `notify.success('Saved')` — toast (sonner) + optional centre entry. */\nfunction make(variant: NotifyVariant) {\n return (title: string, opts: NotifyOptions = {}): string => {\n toast[variant](title, {\n description: opts.description,\n duration: opts.duration,\n action: opts.action,\n icon: opts.icon,\n })\n const id = uid()\n if (opts.record !== false) {\n useNotify.getState().record({\n id,\n title,\n description: opts.description,\n variant,\n ts: Date.now(),\n read: false,\n href: opts.href,\n ambient: opts.ambient,\n })\n }\n return id\n }\n}\nexport const notify = {\n success: make('success'),\n error: make('error'),\n info: make('info'),\n warning: make('warning'),\n}\n\n/** Replace the notification centre's history — inject a seed/initial set. */\nexport function seedNotifications(items: AppNotification[]): void {\n useNotify.getState().seed(items)\n}\n\n/** Unread count selector for the topbar bell — ambient session activity never\n * demands attention, so it never counts. */\nexport const selectUnread = (s: NotifyState): number =>\n s.notifications.filter((n) => !n.read && !n.ambient).length\n\n/** Default glyph colour per variant (raw palette, override via `toneClassNames`). */\nexport const DEFAULT_NOTIFY_TONE: Record<NotifyVariant, string> = {\n success: 'text-green-600',\n error: 'text-destructive',\n warning: 'text-amber-500',\n info: 'text-blue-500',\n}\n","/** Relative-time formatting shared by the notification bell and the DM kit (no type imports, so the\n * SDK-free `/shell` entry can use it). */\n\nconst RELATIVE = new Intl.RelativeTimeFormat(undefined, { numeric: 'auto' })\nconst DIVISIONS: Array<{ amount: number; unit: Intl.RelativeTimeFormatUnit }> = [\n { amount: 60, unit: 'seconds' },\n { amount: 60, unit: 'minutes' },\n { amount: 24, unit: 'hours' },\n { amount: 7, unit: 'days' },\n { amount: 4.34524, unit: 'weeks' },\n { amount: 12, unit: 'months' },\n { amount: Number.POSITIVE_INFINITY, unit: 'years' },\n]\n\n/** Compact relative time, e.g. \"2 hours ago\", \"just now\". */\nexport function formatRelativeTime(iso: string): string {\n const then = Date.parse(iso)\n if (Number.isNaN(then)) return ''\n let duration = (then - Date.now()) / 1000\n if (Math.abs(duration) < 45) return 'just now'\n for (const division of DIVISIONS) {\n if (Math.abs(duration) < division.amount) {\n return RELATIVE.format(Math.round(duration), division.unit)\n }\n duration /= division.amount\n }\n return ''\n}\n","/**\n * NotificationBell — the topbar notification centre: a bell with an unread badge\n * that opens a dropdown of recorded entries (newest first) with an unread dot,\n * relative time and optional deep-link. \"Mark all read\" clears the badge. Backed\n * by the shared {@link useNotify} store (the same one the <Toaster/> renders).\n *\n * Firm-agnostic: navigation is a callback (`onNavigate`), styling tracks semantic\n * tokens, and per-variant glyph colours are overridable via `toneClassNames`.\n */\nimport type { ReactElement } from 'react'\nimport {\n Bell,\n BellOff,\n CheckCheck,\n CheckCircle2,\n AlertTriangle,\n XCircle,\n Info,\n X,\n} from 'lucide-react'\nimport {\n DropdownMenu,\n DropdownMenuTrigger,\n DropdownMenuContent,\n DropdownMenuItem,\n} from '../ui/dropdown-menu'\nimport { ScrollArea } from '../ui/scroll-area'\nimport { cn } from '../lib/utils'\nimport { formatRelativeTime } from '../lib/format-relative-time'\nimport { useNotify, selectUnread, DEFAULT_NOTIFY_TONE, type NotifyVariant } from './notify-store'\n\nconst GLYPH: Record<NotifyVariant, typeof Info> = {\n success: CheckCircle2,\n error: XCircle,\n warning: AlertTriangle,\n info: Info,\n}\n\n/** What a custom bell trigger needs to render itself. */\nexport interface NotificationBellTriggerState {\n /** Unread count (0 when none). */\n unread: number\n /** Accessible name, including the count: \"Notifications, 2 unread\". */\n ariaLabel: string\n testId: string\n}\n\nexport interface NotificationBellProps {\n /**\n * Replaces the default icon button with the host's own trigger (for example a labelled sidebar\n * row). It must forward its ref and rest props: it is mounted as the dropdown trigger.\n */\n renderTrigger?: (state: NotificationBellTriggerState) => ReactElement\n /** Called with an entry's `href` when it's clicked (e.g. router navigate). */\n onNavigate?: (href: string) => void\n /** Called after an entry is locally marked read — the hook for a backend\n * bridge to propagate read state upstream (filter by the ids it owns). */\n onMarkRead?: (id: string) => void\n /** Called after \"Mark all read\" — same upstream-propagation hook. */\n onMarkAllRead?: () => void\n /** Toast muting — the bell is where notification behaviour is controlled, so\n * the mute switch lives in its header. Omit `onToggleMute` to hide it. */\n muted?: boolean\n onToggleMute?: () => void\n /** Override per-variant glyph colours (e.g. brand emerald for success). */\n toneClassNames?: Partial<Record<NotifyVariant, string>>\n emptyText?: string\n /** Footer link to the app's full notification surface. Omit to hide. */\n onViewAll?: () => void\n viewAllLabel?: string\n testId?: string\n /** Where the panel opens relative to the bell (a sidebar-footer bell opens upward). */\n side?: 'top' | 'right' | 'bottom' | 'left'\n align?: 'start' | 'center' | 'end'\n}\n\nexport function NotificationBell({\n renderTrigger,\n onNavigate,\n onMarkRead,\n onMarkAllRead,\n muted = false,\n onToggleMute,\n toneClassNames,\n emptyText = \"You're all caught up.\",\n onViewAll,\n viewAllLabel = 'View all',\n testId = 'notifications',\n side = 'bottom',\n align = 'end',\n}: NotificationBellProps) {\n const notifications = useNotify((s) => s.notifications)\n const unread = useNotify(selectUnread)\n const markAllRead = useNotify((s) => s.markAllRead)\n const markRead = useNotify((s) => s.markRead)\n const tone = (v: NotifyVariant) => toneClassNames?.[v] ?? DEFAULT_NOTIFY_TONE[v]\n\n // Ambient entries (routine session activity — \"Opening workspace…\") are kept\n // for the log but never surfaced in the bell panel: they never badge and are\n // always filtered out here.\n const visible = notifications.filter((n) => !n.ambient)\n\n return (\n <DropdownMenu>\n <DropdownMenuTrigger asChild>\n {renderTrigger ? (\n renderTrigger({\n unread,\n ariaLabel: `Notifications${unread ? `, ${unread} unread` : ''}`,\n testId,\n })\n ) : (\n <button\n className=\"relative flex size-9 pointer-coarse:size-11 items-center justify-center rounded-md text-muted-foreground transition-colors hover:bg-accent hover:text-foreground\"\n data-testid={testId}\n aria-label={`Notifications${unread ? `, ${unread} unread` : ''}`}\n title=\"Notifications\"\n >\n <Bell className=\"size-5\" />\n {unread > 0 && (\n <span\n className=\"absolute -right-0.5 -top-0.5 flex min-w-4 items-center justify-center rounded-full bg-primary px-1 text-xs font-semibold leading-4 text-primary-foreground\"\n data-testid={`${testId}-badge`}\n >\n {unread > 9 ? '9+' : unread}\n </span>\n )}\n </button>\n )}\n </DropdownMenuTrigger>\n <DropdownMenuContent\n align={align}\n side={side}\n sideOffset={8}\n collisionPadding={12}\n className=\"w-[360px] rounded-lg border-border/60 p-0 shadow-lg\"\n data-testid={`${testId}-panel`}\n data-topbar-popover=\"\"\n >\n <div className=\"flex items-center justify-between gap-3 border-b border-border px-3.5 py-2.5 pointer-coarse:py-0\">\n <p className=\"text-sm font-semibold text-foreground\">Notifications</p>\n <div className=\"flex items-center gap-3 pointer-coarse:gap-1\">\n {unread > 0 && (\n <button\n onClick={() => {\n markAllRead()\n onMarkAllRead?.()\n }}\n className=\"flex items-center gap-1 text-xs font-medium text-primary transition-colors hover:opacity-80 pointer-coarse:min-h-11 pointer-coarse:px-2\"\n data-testid={`${testId}-mark-all`}\n >\n <CheckCheck className=\"size-3.5\" /> Mark all read\n </button>\n )}\n {onToggleMute && (\n <button\n onClick={onToggleMute}\n aria-pressed={muted}\n title={muted ? 'Unmute notifications' : 'Mute notifications'}\n aria-label={muted ? 'Unmute notifications' : 'Mute notifications'}\n className={cn(\n 'flex size-6 items-center justify-center rounded-md transition-colors hover:bg-accent pointer-coarse:size-11',\n muted ? 'text-foreground' : 'text-muted-foreground hover:text-foreground'\n )}\n data-testid={`${testId}-mute`}\n >\n <BellOff className=\"size-3.5\" />\n </button>\n )}\n {/* Explicit close — the dropdown otherwise only dismisses via\n tap-outside/Escape, which isn't discoverable on touch and (at\n mobile width, see blocks.css) the panel covers most\n of the screen so there's nothing to tap outside of. Radix\n closes a DropdownMenu on any item select by default, so a\n plain item with no other effect doubles as a close button. */}\n <DropdownMenuItem\n className=\"flex size-6 items-center justify-center rounded-md p-0 text-muted-foreground transition-colors hover:bg-accent hover:text-foreground focus:bg-accent pointer-coarse:size-11\"\n aria-label=\"Close notifications\"\n data-testid={`${testId}-close`}\n >\n <X className=\"size-3.5\" />\n </DropdownMenuItem>\n </div>\n </div>\n {muted && (\n <p\n className=\"border-b border-border/60 bg-amber-500/10 px-3.5 py-1.5 text-xs text-muted-foreground\"\n data-testid={`${testId}-muted-note`}\n >\n Toasts are muted — everything still lands here.\n </p>\n )}\n\n {visible.length === 0 ? (\n <div className=\"px-3.5 py-10 text-center text-sm text-muted-foreground\">{emptyText}</div>\n ) : (\n <ScrollArea className=\"max-h-[380px]\">\n <ul data-testid={`${testId}-list`}>\n {visible.map((n) => {\n const Glyph = GLYPH[n.variant]\n return (\n <li key={n.id}>\n <button\n onClick={() => {\n markRead(n.id)\n if (!n.read) onMarkRead?.(n.id)\n // The specific action wins over the generic deep link.\n if (n.onSelect) n.onSelect()\n else if (n.href) onNavigate?.(n.href)\n }}\n className={cn(\n 'flex w-full items-start gap-3 border-b border-border/60 px-3.5 py-3 text-left transition-colors hover:bg-accent',\n !n.read && 'bg-accent/40'\n )}\n data-testid={`${testId}-item`}\n >\n <Glyph className={cn('mt-0.5 size-4 shrink-0', tone(n.variant))} />\n <div className=\"min-w-0 flex-1\">\n <p className=\"text-sm font-medium leading-snug text-foreground\">\n {n.title}\n </p>\n {n.description && (\n <p className=\"mt-0.5 line-clamp-2 text-xs leading-relaxed text-muted-foreground\">\n {n.description}\n </p>\n )}\n <p className=\"mt-1 text-xs text-muted-foreground/80\">\n {formatRelativeTime(new Date(n.ts).toISOString())}\n </p>\n </div>\n {!n.read && (\n <span className=\"mt-1.5 size-2 shrink-0 rounded-full bg-primary\" />\n )}\n </button>\n </li>\n )\n })}\n </ul>\n </ScrollArea>\n )}\n {onViewAll && (\n // A real menu item, not a bare button: selecting it must CLOSE the\n // dropdown on its way to the full surface.\n <DropdownMenuItem\n onClick={onViewAll}\n className=\"justify-center rounded-none border-t border-border px-3.5 py-2.5 text-xs font-medium text-primary focus:text-primary pointer-coarse:min-h-11\"\n data-testid={`${testId}-view-all`}\n >\n {viewAllLabel}\n </DropdownMenuItem>\n )}\n </DropdownMenuContent>\n </DropdownMenu>\n )\n}\n","/**\n * Toaster — the one shared toast surface, built on sonner (the mature engine:\n * stacking, swipe-to-dismiss, a11y, timing). Mount once near the app root; any\n * code then calls `notify.*(...)` (or the re-exported `toast`) from anywhere.\n *\n * Firm-agnostic: every sonner `ToasterProps` passes through, so a consumer sets\n * its own `position` / `theme` / `toastOptions`. The defaults give a neutral\n * shadcn-token card that both apps can override.\n */\nimport { Toaster as Sonner, type ToasterProps } from 'sonner'\n\nexport type { ToasterProps }\n\nconst DEFAULT_TOAST_OPTIONS: ToasterProps['toastOptions'] = {\n classNames: {\n toast:\n 'group toast group-[.toaster]:bg-card group-[.toaster]:text-foreground group-[.toaster]:border group-[.toaster]:border-border group-[.toaster]:shadow-lg group-[.toaster]:rounded-lg',\n description: 'group-[.toast]:text-muted-foreground',\n actionButton: 'group-[.toast]:bg-primary group-[.toast]:text-primary-foreground',\n cancelButton: 'group-[.toast]:bg-muted group-[.toast]:text-muted-foreground',\n },\n}\n\nexport function Toaster({ toastOptions, ...props }: ToasterProps) {\n return (\n <Sonner\n className=\"toaster group\"\n toastOptions={toastOptions ?? DEFAULT_TOAST_OPTIONS}\n {...props}\n />\n )\n}\n"]}
|