@aircall/blocks 0.2.1
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/globals.css +2 -0
- package/dist/index.d.ts +350 -0
- package/dist/index.js +525 -0
- package/package.json +86 -0
package/dist/index.js
ADDED
|
@@ -0,0 +1,525 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { createContext, useCallback, useContext, useEffect, useRef, useState } from "react";
|
|
3
|
+
import { Badge, Button, CounterBadge, DropdownMenu, DropdownMenuAddon, DropdownMenuContent, DropdownMenuGroup, DropdownMenuItem, DropdownMenuLabel, DropdownMenuSeparator, DropdownMenuTrigger, SidebarProvider, TabsList, Tooltip, TooltipContent, TooltipTrigger, cn, useSidebar } from "@aircall/ds";
|
|
4
|
+
import { Check, ChevronLeft, ChevronRight, Copy, PanelLeftClose, PanelLeftOpen } from "@aircall/react-icons";
|
|
5
|
+
import { useCallbackRef, useIsMounted, useUnmount } from "@aircall/hooks";
|
|
6
|
+
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
7
|
+
import { mergeProps } from "@base-ui/react/merge-props";
|
|
8
|
+
import { useRender } from "@base-ui/react/use-render";
|
|
9
|
+
|
|
10
|
+
//#region src/hooks/use-copy-to-clipboard.ts
|
|
11
|
+
function useCopyToClipboard(value, { timeout = 5e3, onCopied } = {}) {
|
|
12
|
+
const [copied, setCopied] = useState(false);
|
|
13
|
+
const timerRef = useRef();
|
|
14
|
+
const getIsMounted = useIsMounted();
|
|
15
|
+
const onCopiedRef = useCallbackRef(onCopied);
|
|
16
|
+
useUnmount(() => clearTimeout(timerRef.current));
|
|
17
|
+
useEffect(() => {
|
|
18
|
+
clearTimeout(timerRef.current);
|
|
19
|
+
setCopied(false);
|
|
20
|
+
}, [value]);
|
|
21
|
+
return {
|
|
22
|
+
copy: useCallback(async () => {
|
|
23
|
+
try {
|
|
24
|
+
await navigator.clipboard.writeText(value);
|
|
25
|
+
if (!getIsMounted()) return;
|
|
26
|
+
setCopied(true);
|
|
27
|
+
onCopiedRef();
|
|
28
|
+
clearTimeout(timerRef.current);
|
|
29
|
+
timerRef.current = setTimeout(() => {
|
|
30
|
+
if (getIsMounted()) setCopied(false);
|
|
31
|
+
}, timeout);
|
|
32
|
+
} catch {}
|
|
33
|
+
}, [
|
|
34
|
+
value,
|
|
35
|
+
timeout,
|
|
36
|
+
getIsMounted,
|
|
37
|
+
onCopiedRef
|
|
38
|
+
]),
|
|
39
|
+
copied
|
|
40
|
+
};
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
//#endregion
|
|
44
|
+
//#region src/components/copy-button.tsx
|
|
45
|
+
const CopyButtonContext = createContext(null);
|
|
46
|
+
function useCopyButtonContext() {
|
|
47
|
+
const ctx = useContext(CopyButtonContext);
|
|
48
|
+
if (ctx === null) throw new Error("CopyButtonIcon and CopyButtonLabel must be rendered inside a CopyButton.");
|
|
49
|
+
return ctx;
|
|
50
|
+
}
|
|
51
|
+
function CopyButton({ value, timeout, onCopied, variant = "outline", children, ...buttonProps }) {
|
|
52
|
+
const { copy, copied } = useCopyToClipboard(value, {
|
|
53
|
+
timeout,
|
|
54
|
+
onCopied
|
|
55
|
+
});
|
|
56
|
+
return /* @__PURE__ */ jsx(CopyButtonContext.Provider, {
|
|
57
|
+
value: { copied },
|
|
58
|
+
children: /* @__PURE__ */ jsx(Button, {
|
|
59
|
+
onClick: () => {
|
|
60
|
+
copy();
|
|
61
|
+
},
|
|
62
|
+
variant,
|
|
63
|
+
...buttonProps,
|
|
64
|
+
children
|
|
65
|
+
})
|
|
66
|
+
});
|
|
67
|
+
}
|
|
68
|
+
function CopyButtonIcon({ className }) {
|
|
69
|
+
const { copied } = useCopyButtonContext();
|
|
70
|
+
return /* @__PURE__ */ jsx(copied ? Check : Copy, { className });
|
|
71
|
+
}
|
|
72
|
+
function CopyButtonLabel({ label = "Copy", copiedLabel = "Copied", className }) {
|
|
73
|
+
const { copied } = useCopyButtonContext();
|
|
74
|
+
return /* @__PURE__ */ jsx("span", {
|
|
75
|
+
className: cn(className),
|
|
76
|
+
children: copied ? copiedLabel : label
|
|
77
|
+
});
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
//#endregion
|
|
81
|
+
//#region src/components/dashboard-page.tsx
|
|
82
|
+
function DashboardPage({ className, ...props }) {
|
|
83
|
+
return /* @__PURE__ */ jsx("div", {
|
|
84
|
+
"data-slot": "dashboard-page",
|
|
85
|
+
className: cn("flex flex-col h-screen bg-page-background p-2 gap-2", className),
|
|
86
|
+
...props
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
function DashboardPageBanner({ className, ...props }) {
|
|
90
|
+
return /* @__PURE__ */ jsx("div", {
|
|
91
|
+
"data-slot": "dashboard-page-banner",
|
|
92
|
+
className: cn("-mt-2 -mx-2", className),
|
|
93
|
+
...props
|
|
94
|
+
});
|
|
95
|
+
}
|
|
96
|
+
function DashboardPageContent({ className, ...props }) {
|
|
97
|
+
return /* @__PURE__ */ jsx("div", {
|
|
98
|
+
"data-slot": "dashboard-page-content",
|
|
99
|
+
className: cn("flex-1 min-w-0 flex flex-col bg-background rounded-xl border border-border overflow-hidden", "[&:has([data-slot=dph-tabs])>[data-slot=dph]]:border-b-0", className),
|
|
100
|
+
...props
|
|
101
|
+
});
|
|
102
|
+
}
|
|
103
|
+
function DashboardPageMain({ className, ...props }) {
|
|
104
|
+
return /* @__PURE__ */ jsx("main", {
|
|
105
|
+
"data-slot": "dashboard-page-main",
|
|
106
|
+
className: cn("flex-1 min-h-0 overflow-auto", className),
|
|
107
|
+
...props
|
|
108
|
+
});
|
|
109
|
+
}
|
|
110
|
+
function DashboardPageTabs({ className, ...props }) {
|
|
111
|
+
return /* @__PURE__ */ jsx(TabsList, {
|
|
112
|
+
...props,
|
|
113
|
+
"data-slot": "dph-tabs",
|
|
114
|
+
className: cn("flex w-full justify-start gap-0 px-4 border-b border-border rounded-none bg-transparent", "[&>[data-slot=tabs-trigger]]:flex-none", "[&>[data-slot=tabs-trigger]]:rounded-none", "[&>[data-slot=tabs-trigger]]:after:hidden", "[&>[data-slot=tabs-trigger]]:border-b-2", "[&>[data-slot=tabs-trigger]]:border-b-transparent", "[&>[data-slot=tabs-trigger]]:-mb-px", "[&>[data-slot=tabs-trigger][data-active]]:border-b-foreground", className)
|
|
115
|
+
});
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
//#endregion
|
|
119
|
+
//#region src/components/dashboard-page-header.tsx
|
|
120
|
+
function DashboardPageHeader({ className, children, ...props }) {
|
|
121
|
+
return /* @__PURE__ */ jsx("div", {
|
|
122
|
+
"data-slot": "dph",
|
|
123
|
+
className: cn("@container bg-background border-b border-border w-full", className),
|
|
124
|
+
...props,
|
|
125
|
+
children: /* @__PURE__ */ jsx("div", {
|
|
126
|
+
"data-slot": "dph-grid",
|
|
127
|
+
className: "group/dph pl-4 pr-6 py-3",
|
|
128
|
+
children
|
|
129
|
+
})
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
function DashboardPageHeaderNav({ className, children, ...props }) {
|
|
133
|
+
return /* @__PURE__ */ jsx("div", {
|
|
134
|
+
"data-slot": "dph-nav",
|
|
135
|
+
className: cn("[grid-area:nav] flex items-center gap-1", className),
|
|
136
|
+
...props,
|
|
137
|
+
children
|
|
138
|
+
});
|
|
139
|
+
}
|
|
140
|
+
function DashboardPageHeaderNavBack({ children, ...props }) {
|
|
141
|
+
return /* @__PURE__ */ jsxs(Button, {
|
|
142
|
+
variant: "ghost",
|
|
143
|
+
size: "sm",
|
|
144
|
+
nativeButton: false,
|
|
145
|
+
...props,
|
|
146
|
+
children: [/* @__PURE__ */ jsx(ChevronLeft, {}), children]
|
|
147
|
+
});
|
|
148
|
+
}
|
|
149
|
+
function DashboardPageHeaderPrefix({ className, children, ...props }) {
|
|
150
|
+
return /* @__PURE__ */ jsx("div", {
|
|
151
|
+
"data-slot": "dph-prefix",
|
|
152
|
+
className: cn("[grid-area:prefix] flex shrink-0 pr-2 pt-2 items-center justify-center self-start", "group-has-[[data-slot=dph-nav]]/dph:pl-2", className),
|
|
153
|
+
...props,
|
|
154
|
+
children
|
|
155
|
+
});
|
|
156
|
+
}
|
|
157
|
+
function DashboardPageHeaderTitleGroup({ className, children, ...props }) {
|
|
158
|
+
return /* @__PURE__ */ jsx("div", {
|
|
159
|
+
"data-slot": "dph-title-group",
|
|
160
|
+
className: cn("[grid-area:info] flex min-w-0 flex-col self-center", "group-has-[[data-slot=dph-nav]]/dph:pl-2", "group-has-[[data-slot=dph-prefix]]/dph:pl-0", className),
|
|
161
|
+
...props,
|
|
162
|
+
children
|
|
163
|
+
});
|
|
164
|
+
}
|
|
165
|
+
function DashboardPageHeaderTitle({ size = "lg", className, children, ...props }) {
|
|
166
|
+
return /* @__PURE__ */ jsx("h1", {
|
|
167
|
+
"data-slot": "dph-title",
|
|
168
|
+
className: cn("[grid-area:info] self-center group-has-[[data-slot=dph-nav]]/dph:pl-2 group-has-[[data-slot=dph-prefix]]/dph:pl-0", "[[data-slot=dph-title-group]_&]:p-0", "[[data-slot=dph-title-group]_&]:self-auto", "truncate text-foreground", size === "lg" ? "text-3xl font-bold" : "text-xl font-bold", "leading-10", className),
|
|
169
|
+
...props,
|
|
170
|
+
children
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
function DashboardPageHeaderSubtitle({ className, children, ...props }) {
|
|
174
|
+
return /* @__PURE__ */ jsx("div", {
|
|
175
|
+
"data-slot": "dph-subtitle",
|
|
176
|
+
className: cn("flex flex-col items-start gap-0.5 max-w-xl", className),
|
|
177
|
+
...props,
|
|
178
|
+
children
|
|
179
|
+
});
|
|
180
|
+
}
|
|
181
|
+
function DashboardPageHeaderDescription({ className, children, ...props }) {
|
|
182
|
+
return /* @__PURE__ */ jsx("div", {
|
|
183
|
+
"data-slot": "dph-description",
|
|
184
|
+
className: cn("text-sm text-muted-foreground line-clamp-3 -mt-1", className),
|
|
185
|
+
...props,
|
|
186
|
+
children
|
|
187
|
+
});
|
|
188
|
+
}
|
|
189
|
+
function DashboardPageHeaderActions({ className, children, ...props }) {
|
|
190
|
+
return /* @__PURE__ */ jsx("div", {
|
|
191
|
+
"data-slot": "dph-actions",
|
|
192
|
+
className: cn("[grid-area:actions] flex items-center gap-2 self-start pl-2", className),
|
|
193
|
+
...props,
|
|
194
|
+
children
|
|
195
|
+
});
|
|
196
|
+
}
|
|
197
|
+
const DashboardPageHeaderAction = React.forwardRef(({ size = "lg", ...props }, ref) => /* @__PURE__ */ jsx(Button, {
|
|
198
|
+
ref,
|
|
199
|
+
size,
|
|
200
|
+
...props
|
|
201
|
+
}));
|
|
202
|
+
DashboardPageHeaderAction.displayName = "DashboardPageHeaderAction";
|
|
203
|
+
|
|
204
|
+
//#endregion
|
|
205
|
+
//#region src/components/product-badges.tsx
|
|
206
|
+
function badgeBg(color) {
|
|
207
|
+
return { backgroundImage: [`linear-gradient(90deg, var(--badge-overlay) 0%, var(--badge-overlay) 100%)`, `linear-gradient(90deg, ${color} 0%, ${color} 100%)`].join(", ") };
|
|
208
|
+
}
|
|
209
|
+
function createGradientBadge(slot, color, label) {
|
|
210
|
+
function GradientBadge({ className, ...props }) {
|
|
211
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
212
|
+
"data-slot": slot,
|
|
213
|
+
className: cn("border-transparent text-foreground", className),
|
|
214
|
+
style: badgeBg(color),
|
|
215
|
+
...props,
|
|
216
|
+
children: label
|
|
217
|
+
});
|
|
218
|
+
}
|
|
219
|
+
GradientBadge.displayName = label.replace(/\s+/g, "") + "Badge";
|
|
220
|
+
return GradientBadge;
|
|
221
|
+
}
|
|
222
|
+
/** Blue pill — feature in beta, not yet GA */
|
|
223
|
+
const BetaBadge = createGradientBadge("badge-beta", "var(--color-blue-300)", "Beta");
|
|
224
|
+
/** Green pill — recently launched feature */
|
|
225
|
+
const NewBadge = createGradientBadge("badge-new", "var(--color-green-300)", "New");
|
|
226
|
+
/** Amber pill — feature being phased out */
|
|
227
|
+
const DeprecatedBadge = createGradientBadge("badge-deprecated", "var(--color-yellow-300)", "Deprecated");
|
|
228
|
+
/** Blue pill — user is in a trial period (same blue family as Beta) */
|
|
229
|
+
const TrialBadge = createGradientBadge("badge-trial", "var(--color-blue-300)", "Trial");
|
|
230
|
+
/** Green pill — Professional tier (no gradient; uses default Badge variant) */
|
|
231
|
+
function ProfessionalBadge({ className, ...props }) {
|
|
232
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
233
|
+
"data-slot": "badge-professional",
|
|
234
|
+
className,
|
|
235
|
+
variant: "default",
|
|
236
|
+
...props,
|
|
237
|
+
children: "Professional"
|
|
238
|
+
});
|
|
239
|
+
}
|
|
240
|
+
const ROLE_CONFIG = {
|
|
241
|
+
agent: {
|
|
242
|
+
color: "var(--color-yellow-400)",
|
|
243
|
+
label: "Agent"
|
|
244
|
+
},
|
|
245
|
+
supervisor: {
|
|
246
|
+
color: "var(--color-blue-500)",
|
|
247
|
+
label: "Supervisor"
|
|
248
|
+
},
|
|
249
|
+
admin: {
|
|
250
|
+
color: "var(--color-purple-500)",
|
|
251
|
+
label: "Admin"
|
|
252
|
+
},
|
|
253
|
+
owner: {
|
|
254
|
+
color: "var(--color-green-400)",
|
|
255
|
+
label: "Owner"
|
|
256
|
+
}
|
|
257
|
+
};
|
|
258
|
+
function RoleBadge({ role, className, ...props }) {
|
|
259
|
+
const { color, label } = ROLE_CONFIG[role];
|
|
260
|
+
return /* @__PURE__ */ jsx(Badge, {
|
|
261
|
+
"data-slot": `badge-${role}`,
|
|
262
|
+
className: cn("border-transparent text-foreground", className),
|
|
263
|
+
style: badgeBg(color),
|
|
264
|
+
...props,
|
|
265
|
+
children: label
|
|
266
|
+
});
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
//#endregion
|
|
270
|
+
//#region src/components/dashboard-sidebar.tsx
|
|
271
|
+
function DashboardSidebarProvider({ className, style, ...props }) {
|
|
272
|
+
return /* @__PURE__ */ jsx(SidebarProvider, {
|
|
273
|
+
className: cn("!min-h-0 !w-auto flex flex-1 min-h-0 gap-2", "[&>[data-slot=tabs]]:flex-1 [&>[data-slot=tabs]]:min-w-0 [&>[data-slot=tabs]]:min-h-0 [&>[data-slot=tabs]]:flex [&>[data-slot=tabs]]:flex-col", className),
|
|
274
|
+
style: {
|
|
275
|
+
"--sidebar-width": "14rem",
|
|
276
|
+
"--sidebar-width-icon": "3rem",
|
|
277
|
+
...style
|
|
278
|
+
},
|
|
279
|
+
...props
|
|
280
|
+
});
|
|
281
|
+
}
|
|
282
|
+
function DashboardSidebar({ className, children, ...props }) {
|
|
283
|
+
const { state } = useSidebar();
|
|
284
|
+
return /* @__PURE__ */ jsx("div", {
|
|
285
|
+
"data-slot": "dashboard-sidebar",
|
|
286
|
+
"data-state": state,
|
|
287
|
+
"data-collapsible": state === "collapsed" ? "icon" : "",
|
|
288
|
+
className: cn("group/dbs flex flex-col h-full overflow-hidden", "w-(--sidebar-width) transition-[width] duration-200 ease-linear", "data-[collapsible=icon]:w-(--sidebar-width-icon)", className),
|
|
289
|
+
...props,
|
|
290
|
+
children
|
|
291
|
+
});
|
|
292
|
+
}
|
|
293
|
+
function DashboardSidebarHeader({ className, children, ...props }) {
|
|
294
|
+
return /* @__PURE__ */ jsx("div", {
|
|
295
|
+
"data-slot": "dashboard-sidebar-header",
|
|
296
|
+
className: cn("flex items-start gap-0 p-1 rounded-md shrink-0 justify-end w-full", "group-data-[collapsible=icon]/dbs:justify-center", className),
|
|
297
|
+
...props,
|
|
298
|
+
children
|
|
299
|
+
});
|
|
300
|
+
}
|
|
301
|
+
function DashboardSidebarTrigger({ className, onClick, ...props }) {
|
|
302
|
+
const { toggleSidebar, state } = useSidebar();
|
|
303
|
+
return /* @__PURE__ */ jsx(Button, {
|
|
304
|
+
"data-slot": "dashboard-sidebar-trigger",
|
|
305
|
+
variant: "ghost",
|
|
306
|
+
size: "icon",
|
|
307
|
+
className,
|
|
308
|
+
onClick: (event) => {
|
|
309
|
+
onClick?.(event);
|
|
310
|
+
toggleSidebar();
|
|
311
|
+
},
|
|
312
|
+
"aria-label": state === "expanded" ? "Collapse sidebar" : "Expand sidebar",
|
|
313
|
+
...props,
|
|
314
|
+
children: state === "expanded" ? /* @__PURE__ */ jsx(PanelLeftClose, { className: "size-4" }) : /* @__PURE__ */ jsx(PanelLeftOpen, { className: "size-4" })
|
|
315
|
+
});
|
|
316
|
+
}
|
|
317
|
+
function DashboardSidebarContent({ className, ...props }) {
|
|
318
|
+
return /* @__PURE__ */ jsx("div", {
|
|
319
|
+
"data-slot": "dashboard-sidebar-content",
|
|
320
|
+
className: cn("flex flex-1 flex-col overflow-y-auto overflow-x-hidden min-h-0", "[scrollbar-width:none] [&::-webkit-scrollbar]:hidden", className),
|
|
321
|
+
...props
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
function DashboardSidebarFooter({ className, ...props }) {
|
|
325
|
+
return /* @__PURE__ */ jsx("div", {
|
|
326
|
+
"data-slot": "dashboard-sidebar-footer",
|
|
327
|
+
className: cn("bg-background/80 dark:bg-accent/50 rounded-xl p-1 flex flex-col shrink-0", "items-start w-full", "group-data-[collapsible=icon]/dbs:items-center group-data-[collapsible=icon]/dbs:w-auto", className),
|
|
328
|
+
...props
|
|
329
|
+
});
|
|
330
|
+
}
|
|
331
|
+
function DashboardSidebarGroup({ className, ...props }) {
|
|
332
|
+
return /* @__PURE__ */ jsx("div", {
|
|
333
|
+
"data-slot": "dashboard-sidebar-group",
|
|
334
|
+
className: cn("flex flex-col gap-1 items-start p-1 w-full", className),
|
|
335
|
+
...props
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
function DashboardSidebarGroupLabel({ className, render, ...props }) {
|
|
339
|
+
return useRender({
|
|
340
|
+
defaultTagName: "div",
|
|
341
|
+
props: mergeProps({ className: cn("truncate text-sm font-medium text-navbar-foreground opacity-90 px-2 py-1.5 rounded-md w-full", "group-data-[collapsible=icon]/dbs:hidden", className) }, props),
|
|
342
|
+
render,
|
|
343
|
+
state: { slot: "dashboard-sidebar-group-label" }
|
|
344
|
+
});
|
|
345
|
+
}
|
|
346
|
+
function DashboardSidebarMenu({ className, ...props }) {
|
|
347
|
+
return /* @__PURE__ */ jsx("ul", {
|
|
348
|
+
"data-slot": "dashboard-sidebar-menu",
|
|
349
|
+
className: cn("flex w-full min-w-0 flex-col gap-1", className),
|
|
350
|
+
...props
|
|
351
|
+
});
|
|
352
|
+
}
|
|
353
|
+
function DashboardSidebarMenuItem({ className, ...props }) {
|
|
354
|
+
return /* @__PURE__ */ jsx("li", {
|
|
355
|
+
"data-slot": "dashboard-sidebar-menu-item",
|
|
356
|
+
className: cn("relative list-none", className),
|
|
357
|
+
...props
|
|
358
|
+
});
|
|
359
|
+
}
|
|
360
|
+
const DashboardSidebarMenuButton = React.forwardRef(function DashboardSidebarMenuButton({ className, render, isActive = false, disabled = false, icon, action, children, ...props }, ref) {
|
|
361
|
+
const buttonContent = /* @__PURE__ */ jsxs(Fragment, { children: [
|
|
362
|
+
icon && /* @__PURE__ */ jsx("span", {
|
|
363
|
+
className: cn("size-6 rounded-full bg-navbar-icon-bg text-navbar-foreground flex items-center justify-center shrink-0 overflow-hidden", "[&>svg]:size-4 [&>svg]:shrink-0"),
|
|
364
|
+
children: icon
|
|
365
|
+
}),
|
|
366
|
+
/* @__PURE__ */ jsx("span", {
|
|
367
|
+
className: "flex-1 min-w-0 flex items-center gap-2 truncate",
|
|
368
|
+
children
|
|
369
|
+
}),
|
|
370
|
+
action && /* @__PURE__ */ jsx("span", {
|
|
371
|
+
className: "flex shrink-0 items-center justify-center size-4 [&>svg]:size-4 [&>svg]:shrink-0 group-data-[collapsible=icon]/dbs:hidden",
|
|
372
|
+
children: action
|
|
373
|
+
})
|
|
374
|
+
] });
|
|
375
|
+
return useRender({
|
|
376
|
+
defaultTagName: "button",
|
|
377
|
+
props: mergeProps({
|
|
378
|
+
ref,
|
|
379
|
+
type: "button",
|
|
380
|
+
"data-active": isActive,
|
|
381
|
+
className: cn("peer/dbs-button relative flex w-full items-center gap-2 h-10 p-2 rounded-full overflow-hidden", "text-sm font-medium transition-[width,padding,color,background-color] duration-200 ease-linear", disabled ? "cursor-default" : "cursor-pointer hover:bg-sidebar-accent", "focus-visible:outline-none focus-visible:ring-3 focus-visible:ring-ring/50", disabled ? "text-muted-foreground" : "text-navbar-foreground", disabled && "opacity-60", isActive && "bg-navbar-accent text-navbar-accent-foreground hover:bg-navbar-accent", "aria-expanded:bg-navbar-accent aria-expanded:text-navbar-accent-foreground aria-expanded:hover:bg-navbar-accent", className),
|
|
382
|
+
children: buttonContent
|
|
383
|
+
}, props),
|
|
384
|
+
render,
|
|
385
|
+
state: {
|
|
386
|
+
slot: "dashboard-sidebar-menu-button",
|
|
387
|
+
active: isActive
|
|
388
|
+
}
|
|
389
|
+
});
|
|
390
|
+
});
|
|
391
|
+
DashboardSidebarMenuButton.displayName = "DashboardSidebarMenuButton";
|
|
392
|
+
function DashboardSidebarMenuAddon({ className, ...props }) {
|
|
393
|
+
return /* @__PURE__ */ jsx("div", {
|
|
394
|
+
"data-slot": "dashboard-sidebar-menu-addon",
|
|
395
|
+
className: cn("ml-auto shrink-0 inline-flex items-center", className),
|
|
396
|
+
...props
|
|
397
|
+
});
|
|
398
|
+
}
|
|
399
|
+
function DashboardSidebarMenuBadge({ className, ...props }) {
|
|
400
|
+
return /* @__PURE__ */ jsx(CounterBadge, {
|
|
401
|
+
"data-slot": "dashboard-sidebar-menu-badge",
|
|
402
|
+
className: cn("pointer-events-none absolute select-none", "right-2 top-1/2 -translate-y-1/2", "group-data-[collapsible=icon]/dbs:right-[-2px] group-data-[collapsible=icon]/dbs:top-[-3.5px] group-data-[collapsible=icon]/dbs:translate-y-0", "peer-data-[active=true]/dbs-button:bg-navbar-icon-bg peer-data-[active=true]/dbs-button:text-navbar-foreground", className),
|
|
403
|
+
...props
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
function DashboardSidebarSubmenu({ className, ...props }) {
|
|
407
|
+
return /* @__PURE__ */ jsx("div", {
|
|
408
|
+
"data-slot": "dashboard-sidebar-submenu",
|
|
409
|
+
className: cn("bg-popover border border-border shadow-md rounded-md p-1", "flex flex-col w-[238px]", className),
|
|
410
|
+
...props
|
|
411
|
+
});
|
|
412
|
+
}
|
|
413
|
+
function DashboardSidebarSubmenuItem({ className, render, isActive = false, ...props }) {
|
|
414
|
+
return useRender({
|
|
415
|
+
defaultTagName: "div",
|
|
416
|
+
props: mergeProps({ className: cn("flex items-center gap-2 p-2 rounded-md cursor-pointer", "text-sm text-foreground leading-snug", "hover:bg-accent transition-colors", isActive && "bg-accent font-medium", className) }, props),
|
|
417
|
+
render,
|
|
418
|
+
state: {
|
|
419
|
+
slot: "dashboard-sidebar-submenu-item",
|
|
420
|
+
active: isActive
|
|
421
|
+
}
|
|
422
|
+
});
|
|
423
|
+
}
|
|
424
|
+
function DashboardSidebarSubmenuSeparator({ className }) {
|
|
425
|
+
return /* @__PURE__ */ jsx("div", {
|
|
426
|
+
"data-slot": "dashboard-sidebar-submenu-separator",
|
|
427
|
+
className: cn("h-px bg-border -mx-1 my-1", className)
|
|
428
|
+
});
|
|
429
|
+
}
|
|
430
|
+
|
|
431
|
+
//#endregion
|
|
432
|
+
//#region src/components/dashboard-sidebar-nav.tsx
|
|
433
|
+
function SubmenuItems({ items, renderLink }) {
|
|
434
|
+
const sections = [];
|
|
435
|
+
let current = [];
|
|
436
|
+
let currentLabel;
|
|
437
|
+
const flush = () => {
|
|
438
|
+
if (current.length > 0) sections.push({
|
|
439
|
+
kind: "group",
|
|
440
|
+
label: currentLabel,
|
|
441
|
+
links: current
|
|
442
|
+
});
|
|
443
|
+
current = [];
|
|
444
|
+
currentLabel = void 0;
|
|
445
|
+
};
|
|
446
|
+
for (const item of items) if (item.kind === "separator") {
|
|
447
|
+
flush();
|
|
448
|
+
sections.push({ kind: "separator" });
|
|
449
|
+
} else if (item.kind === "label") {
|
|
450
|
+
flush();
|
|
451
|
+
currentLabel = item.label;
|
|
452
|
+
} else current.push(item);
|
|
453
|
+
flush();
|
|
454
|
+
const renderLink_ = (item, key) => {
|
|
455
|
+
const linkEl = item.href ? renderLink ? renderLink(item.href) : /* @__PURE__ */ jsx("a", { href: item.href }) : void 0;
|
|
456
|
+
return /* @__PURE__ */ jsxs(DropdownMenuItem, {
|
|
457
|
+
"data-active": item.isActive || void 0,
|
|
458
|
+
render: linkEl,
|
|
459
|
+
children: [item.label, item.badge && /* @__PURE__ */ jsx(DropdownMenuAddon, { children: item.badge })]
|
|
460
|
+
}, key);
|
|
461
|
+
};
|
|
462
|
+
return /* @__PURE__ */ jsx(Fragment, { children: sections.map((section, si) => {
|
|
463
|
+
if (section.kind === "separator") return /* @__PURE__ */ jsx(DropdownMenuSeparator, {}, si);
|
|
464
|
+
if (section.label !== void 0) return /* @__PURE__ */ jsxs(DropdownMenuGroup, { children: [/* @__PURE__ */ jsx(DropdownMenuLabel, { children: section.label }), section.links.map((item, ii) => renderLink_(item, `${si}-${ii}`))] }, si);
|
|
465
|
+
return section.links.map((item, ii) => renderLink_(item, `${si}-${ii}`));
|
|
466
|
+
}) });
|
|
467
|
+
}
|
|
468
|
+
function NavItem({ item, renderLink }) {
|
|
469
|
+
const trailing = item.badge ? /* @__PURE__ */ jsx(DashboardSidebarMenuAddon, { children: item.badge }) : null;
|
|
470
|
+
if (item.kind === "submenu" && !item.disabled) {
|
|
471
|
+
const chevron = item.action !== void 0 ? item.action : /* @__PURE__ */ jsx(ChevronRight, {});
|
|
472
|
+
return /* @__PURE__ */ jsx(DashboardSidebarMenuItem, { children: /* @__PURE__ */ jsxs(DropdownMenu, { children: [/* @__PURE__ */ jsxs(DropdownMenuTrigger, {
|
|
473
|
+
render: /* @__PURE__ */ jsx(DashboardSidebarMenuButton, {
|
|
474
|
+
icon: item.icon,
|
|
475
|
+
isActive: item.isActive,
|
|
476
|
+
action: chevron
|
|
477
|
+
}),
|
|
478
|
+
children: [item.label, trailing]
|
|
479
|
+
}), /* @__PURE__ */ jsx(DropdownMenuContent, {
|
|
480
|
+
side: "right",
|
|
481
|
+
sideOffset: 8,
|
|
482
|
+
align: "start",
|
|
483
|
+
className: "w-56",
|
|
484
|
+
children: /* @__PURE__ */ jsx(SubmenuItems, {
|
|
485
|
+
items: item.items,
|
|
486
|
+
renderLink
|
|
487
|
+
})
|
|
488
|
+
})] }) });
|
|
489
|
+
}
|
|
490
|
+
const action = item.kind === "submenu" ? item.action ?? null : item.action;
|
|
491
|
+
const hasCount = (item.kind === "link" || item.kind === "action") && item.count !== void 0;
|
|
492
|
+
const buttonEl = item.kind === "link" ? /* @__PURE__ */ jsx(DashboardSidebarMenuButton, {
|
|
493
|
+
icon: item.icon,
|
|
494
|
+
isActive: item.isActive,
|
|
495
|
+
disabled: item.disabled,
|
|
496
|
+
action,
|
|
497
|
+
render: item.disabled ? void 0 : renderLink ? renderLink(item.href) : /* @__PURE__ */ jsx("a", { href: item.href })
|
|
498
|
+
}) : /* @__PURE__ */ jsx(DashboardSidebarMenuButton, {
|
|
499
|
+
icon: item.icon,
|
|
500
|
+
isActive: item.isActive,
|
|
501
|
+
disabled: item.disabled,
|
|
502
|
+
action,
|
|
503
|
+
onClick: item.kind === "action" ? item.onClick : void 0
|
|
504
|
+
});
|
|
505
|
+
const countBadge = hasCount ? /* @__PURE__ */ jsx(DashboardSidebarMenuBadge, { children: item.count }) : null;
|
|
506
|
+
if (item.tooltip) return /* @__PURE__ */ jsxs(DashboardSidebarMenuItem, { children: [/* @__PURE__ */ jsxs(Tooltip, { children: [/* @__PURE__ */ jsxs(TooltipTrigger, {
|
|
507
|
+
render: buttonEl,
|
|
508
|
+
children: [item.label, trailing]
|
|
509
|
+
}), /* @__PURE__ */ jsx(TooltipContent, {
|
|
510
|
+
side: "right",
|
|
511
|
+
sideOffset: 8,
|
|
512
|
+
className: "max-w-64",
|
|
513
|
+
children: item.tooltip
|
|
514
|
+
})] }), countBadge] });
|
|
515
|
+
return /* @__PURE__ */ jsxs(DashboardSidebarMenuItem, { children: [React.cloneElement(buttonEl, {}, item.label, trailing), countBadge] });
|
|
516
|
+
}
|
|
517
|
+
function DashboardSidebarNav({ groups, renderLink }) {
|
|
518
|
+
return /* @__PURE__ */ jsx(Fragment, { children: groups.map((group) => /* @__PURE__ */ jsxs(DashboardSidebarGroup, { children: [group.label && /* @__PURE__ */ jsx(DashboardSidebarGroupLabel, { children: group.label }), /* @__PURE__ */ jsx(DashboardSidebarMenu, { children: group.items.map((item) => /* @__PURE__ */ jsx(NavItem, {
|
|
519
|
+
item,
|
|
520
|
+
renderLink
|
|
521
|
+
}, item.id)) })] }, group.id)) });
|
|
522
|
+
}
|
|
523
|
+
|
|
524
|
+
//#endregion
|
|
525
|
+
export { BetaBadge, CopyButton, CopyButtonIcon, CopyButtonLabel, DashboardPage, DashboardPageBanner, DashboardPageContent, DashboardPageHeader, DashboardPageHeaderAction, DashboardPageHeaderActions, DashboardPageHeaderDescription, DashboardPageHeaderNav, DashboardPageHeaderNavBack, DashboardPageHeaderPrefix, DashboardPageHeaderSubtitle, DashboardPageHeaderTitle, DashboardPageHeaderTitleGroup, DashboardPageMain, DashboardPageTabs, DashboardSidebar, DashboardSidebarContent, DashboardSidebarFooter, DashboardSidebarGroup, DashboardSidebarGroupLabel, DashboardSidebarHeader, DashboardSidebarMenu, DashboardSidebarMenuBadge, DashboardSidebarMenuButton, DashboardSidebarMenuItem, DashboardSidebarNav, DashboardSidebarProvider, DashboardSidebarSubmenu, DashboardSidebarSubmenuItem, DashboardSidebarSubmenuSeparator, DashboardSidebarTrigger, DeprecatedBadge, NewBadge, ProfessionalBadge, RoleBadge, TrialBadge, useCopyToClipboard, useSidebar as useDashboardSidebar };
|
package/package.json
ADDED
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@aircall/blocks",
|
|
3
|
+
"version": "0.2.1",
|
|
4
|
+
"type": "module",
|
|
5
|
+
"private": false,
|
|
6
|
+
"sideEffects": [
|
|
7
|
+
"**/*.css"
|
|
8
|
+
],
|
|
9
|
+
"description": "Aircall Blocks — higher-level UI compositions built on @aircall/ds",
|
|
10
|
+
"author": "Aircall",
|
|
11
|
+
"license": "ISC",
|
|
12
|
+
"publishConfig": {
|
|
13
|
+
"access": "public"
|
|
14
|
+
},
|
|
15
|
+
"files": [
|
|
16
|
+
"dist",
|
|
17
|
+
"README.md",
|
|
18
|
+
"package.json"
|
|
19
|
+
],
|
|
20
|
+
"exports": {
|
|
21
|
+
".": {
|
|
22
|
+
"types": "./dist/index.d.ts",
|
|
23
|
+
"default": "./dist/index.js"
|
|
24
|
+
},
|
|
25
|
+
"./globals.css": "./dist/globals.css"
|
|
26
|
+
},
|
|
27
|
+
"peerDependencies": {
|
|
28
|
+
"@aircall/ds": "*",
|
|
29
|
+
"@aircall/hooks": "*",
|
|
30
|
+
"@aircall/react-icons": "*",
|
|
31
|
+
"@base-ui/react": "*",
|
|
32
|
+
"react": "^18.0.0 || ^19.0.0",
|
|
33
|
+
"react-dom": "^18.0.0 || ^19.0.0"
|
|
34
|
+
},
|
|
35
|
+
"dependencies": {},
|
|
36
|
+
"devDependencies": {
|
|
37
|
+
"@aircall/react-icons": "*",
|
|
38
|
+
"@base-ui/react": "1.3.0",
|
|
39
|
+
"@aircall/tsconfig": "1.4.3",
|
|
40
|
+
"@chromatic-com/storybook": "5.0.2",
|
|
41
|
+
"@faker-js/faker": "10.2.0",
|
|
42
|
+
"@storybook/addon-a11y": "10.3.3",
|
|
43
|
+
"@storybook/addon-designs": "^11.1.3",
|
|
44
|
+
"@storybook/addon-docs": "10.3.3",
|
|
45
|
+
"@storybook/addon-mcp": "0.4.2",
|
|
46
|
+
"@storybook/addon-themes": "10.3.3",
|
|
47
|
+
"@storybook/addon-vitest": "10.3.3",
|
|
48
|
+
"@storybook/react-vite": "10.3.3",
|
|
49
|
+
"@tailwindcss/cli": "4.1.18",
|
|
50
|
+
"@tailwindcss/postcss": "4.1.18",
|
|
51
|
+
"@types/node": "24",
|
|
52
|
+
"@types/react": "19",
|
|
53
|
+
"@types/react-dom": "19",
|
|
54
|
+
"@vitest/browser-playwright": "4.0.17",
|
|
55
|
+
"@vitest/coverage-v8": "4.0.17",
|
|
56
|
+
"chromatic": "13.3.5",
|
|
57
|
+
"playwright": "1.57.0",
|
|
58
|
+
"react": "19.2.3",
|
|
59
|
+
"react-compiler-runtime": "1.0.0",
|
|
60
|
+
"react-dom": "19.2.3",
|
|
61
|
+
"storybook": "10.3.3",
|
|
62
|
+
"storybook-addon-pseudo-states": "10.3.3",
|
|
63
|
+
"tailwindcss": "4.1.18",
|
|
64
|
+
"tsdown": "0.20.0-beta.3",
|
|
65
|
+
"typescript": "5.9.3",
|
|
66
|
+
"vite": "7.3.1",
|
|
67
|
+
"vitest": "4.0.17",
|
|
68
|
+
"@aircall/ds": "0.9.0",
|
|
69
|
+
"@aircall/hooks": "0.5.1"
|
|
70
|
+
},
|
|
71
|
+
"scripts": {
|
|
72
|
+
"clean": "rm -rf .turbo && rm -rf node_modules && rm -rf dist",
|
|
73
|
+
"build:js": "tsdown --config ./tsdown.config.ts",
|
|
74
|
+
"build:css": "tailwindcss -i ./src/styles/globals.css -o ./dist/globals.css --minify",
|
|
75
|
+
"build:package": "pnpm run build:js && pnpm run build:css",
|
|
76
|
+
"lint": "eslint . --max-warnings 0",
|
|
77
|
+
"sb:dev": "storybook dev -p 6009",
|
|
78
|
+
"sb:build": "storybook build",
|
|
79
|
+
"sb:build:chromatic": "storybook build --test --stats-json",
|
|
80
|
+
"sb:test": "vitest --project=storybook",
|
|
81
|
+
"chromatic": "chromatic --storybook-build-dir=./storybook-static"
|
|
82
|
+
},
|
|
83
|
+
"types": "./dist/index.d.ts",
|
|
84
|
+
"module": "./dist/index.js",
|
|
85
|
+
"main": "./dist/index.js"
|
|
86
|
+
}
|