@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/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
+ }