@sentry/junior-dashboard 0.264.0 → 0.266.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/LICENSE +201 -0
- package/README.md +2 -5
- package/dist/app.js +119 -35
- package/dist/client.js +145 -136
- package/dist/index.d.ts +0 -1
- package/dist/index.js +119 -35
- package/dist/tailwind.css +1 -1
- package/package.json +5 -4
- package/src/api/schema.ts +0 -1
- package/src/app.ts +5 -20
- package/src/client/App.tsx +29 -31
- package/src/client/api.ts +7 -0
- package/src/client/components/ActorAvatar.tsx +10 -22
- package/src/client/components/BottomSheet.tsx +12 -2
- package/src/client/components/ConnectionBanner.tsx +4 -3
- package/src/client/components/CopyButton.tsx +82 -0
- package/src/client/components/DetailsPanel.tsx +133 -0
- package/src/client/components/Drawer.tsx +15 -24
- package/src/client/components/EmptyTelemetry.tsx +5 -3
- package/src/client/components/FilterBar.tsx +16 -31
- package/src/client/components/ImageAttachment.tsx +49 -63
- package/src/client/components/InlineError.tsx +8 -4
- package/src/client/components/Metric.tsx +33 -23
- package/src/client/components/Notice.tsx +19 -26
- package/src/client/components/PageContentSkeleton.tsx +1 -1
- package/src/client/components/Pagination.tsx +27 -27
- package/src/client/components/ParticipantAvatarStack.tsx +43 -39
- package/src/client/components/ProfileMenu.tsx +27 -93
- package/src/client/components/README.md +103 -78
- package/src/client/components/SearchInput.tsx +51 -41
- package/src/client/components/SegmentedNav.tsx +1 -1
- package/src/client/components/ShimmerText.tsx +2 -6
- package/src/client/components/ThemeSwitcher.tsx +19 -15
- package/src/client/components/VersionDriftBanner.tsx +9 -7
- package/src/client/components/charts/ActivityChart.tsx +173 -288
- package/src/client/components/charts/CacheWrites.tsx +59 -37
- package/src/client/components/charts/InputCacheChart.tsx +137 -131
- package/src/client/components/charts/README.md +4 -4
- package/src/client/components/charts/SystemMetricCharts.tsx +68 -95
- package/src/client/components/charts/WorkspaceUsageChart.tsx +42 -62
- package/src/client/components/controls/DirectorySortSelect.tsx +3 -3
- package/src/client/components/controls/TimeRangeSelector.tsx +10 -15
- package/src/client/components/layout/DashboardHeader.tsx +44 -90
- package/src/client/components/layout/SecondaryNavigation.tsx +2 -1
- package/src/client/components/layout/VisualViewportShell.tsx +2 -2
- package/src/client/components/ui/accordion.tsx +77 -0
- package/src/client/components/ui/alert-dialog.tsx +89 -0
- package/src/client/components/ui/alert.tsx +64 -0
- package/src/client/components/ui/avatar.tsx +53 -0
- package/src/client/components/ui/button.tsx +86 -0
- package/src/client/components/ui/chart.tsx +74 -0
- package/src/client/components/ui/checkbox.tsx +26 -0
- package/src/client/components/ui/collapsible.tsx +22 -0
- package/src/client/components/ui/dialog.tsx +66 -0
- package/src/client/components/ui/empty.tsx +32 -0
- package/src/client/components/ui/field.tsx +49 -0
- package/src/client/components/ui/input-group.tsx +96 -0
- package/src/client/components/ui/input.tsx +35 -0
- package/src/client/components/ui/native-select.tsx +57 -0
- package/src/client/components/ui/pagination.tsx +60 -0
- package/src/client/components/ui/popover.tsx +67 -0
- package/src/client/components/ui/radio-group.tsx +32 -0
- package/src/client/components/ui/sheet.tsx +13 -3
- package/src/client/components/ui/skeleton.tsx +19 -0
- package/src/client/components/ui/spinner.tsx +21 -0
- package/src/client/components/ui/table.tsx +101 -0
- package/src/client/components/ui/tabs.tsx +75 -0
- package/src/client/components/ui/textarea.tsx +17 -0
- package/src/client/components/ui/toast.tsx +215 -0
- package/src/client/components/ui/toggle-group.tsx +36 -0
- package/src/client/components/ui/toggle.tsx +37 -0
- package/src/client/components/ui/tooltip.tsx +168 -0
- package/src/client/components/ui/trace.tsx +96 -0
- package/src/client/conversations/ChatLayout.tsx +7 -3
- package/src/client/conversations/ConversationBrief.tsx +17 -6
- package/src/client/conversations/ConversationChildAgents.tsx +5 -5
- package/src/client/conversations/ConversationComposer.tsx +26 -12
- package/src/client/conversations/ConversationDetails.tsx +147 -62
- package/src/client/conversations/ConversationEventDetails.tsx +141 -0
- package/src/client/conversations/ConversationEventLog.tsx +150 -129
- package/src/client/conversations/ConversationHeader.tsx +13 -7
- package/src/client/conversations/ConversationHeaderActions.tsx +97 -93
- package/src/client/conversations/ConversationHomeList.tsx +1 -1
- package/src/client/conversations/ConversationLinkedWork.tsx +118 -0
- package/src/client/conversations/ConversationMeta.tsx +55 -88
- package/src/client/conversations/ConversationPage.tsx +220 -101
- package/src/client/conversations/ConversationSidebar.tsx +49 -29
- package/src/client/conversations/ConversationUsageBreakdown.tsx +108 -65
- package/src/client/conversations/ConversationWatches.tsx +173 -0
- package/src/client/conversations/ConversationWorkspace.tsx +12 -22
- package/src/client/conversations/ConversationWorkspaceLoading.tsx +1 -1
- package/src/client/conversations/DetailsList.tsx +139 -0
- package/src/client/conversations/EventDetails.tsx +42 -8
- package/src/client/conversations/EventUsage.tsx +99 -74
- package/src/client/conversations/ForkConversationButton.tsx +27 -16
- package/src/client/conversations/ObjectCard.tsx +20 -8
- package/src/client/conversations/PendingMailboxStack.tsx +52 -38
- package/src/client/conversations/README.md +89 -13
- package/src/client/conversations/ShareConversationButton.tsx +28 -17
- package/src/client/conversations/SubagentTranscriptDrawer.tsx +21 -15
- package/src/client/conversations/ToolFrame.tsx +36 -52
- package/src/client/conversations/TranscriptActivityGroup.tsx +13 -4
- package/src/client/conversations/TranscriptAutomationTriggerView.tsx +5 -4
- package/src/client/conversations/TranscriptContextEventView.tsx +25 -11
- package/src/client/conversations/TranscriptEventView.tsx +35 -16
- package/src/client/conversations/TranscriptHeadingRow.tsx +7 -7
- package/src/client/conversations/TranscriptLoading.tsx +1 -1
- package/src/client/conversations/TranscriptMarkdown.tsx +25 -20
- package/src/client/conversations/TranscriptMessageContextView.tsx +35 -18
- package/src/client/conversations/TranscriptMessageView.tsx +6 -1
- package/src/client/conversations/TranscriptReasoningView.tsx +54 -68
- package/src/client/conversations/TranscriptStructuredEventView.tsx +25 -15
- package/src/client/conversations/TranscriptSummary.tsx +34 -2
- package/src/client/conversations/TranscriptTimestamp.tsx +9 -7
- package/src/client/conversations/TranscriptToolView.tsx +1 -1
- package/src/client/conversations/TranscriptTurnContextView.tsx +144 -65
- package/src/client/conversations/TranscriptView.tsx +63 -27
- package/src/client/conversations/eventLog.ts +58 -0
- package/src/client/conversations/eventTime.ts +60 -0
- package/src/client/conversations/queries.ts +29 -0
- package/src/client/conversations/transcriptBottomPinning.ts +34 -0
- package/src/client/conversations/transcriptMentions.tsx +18 -5
- package/src/client/format.ts +5 -0
- package/src/client/pages/ConnectionsPage.tsx +2 -2
- package/src/client/pages/PersonalTokensPage.tsx +4 -4
- package/src/client/pages/SettingsPage.tsx +9 -10
- package/src/client/pages/admin/ServiceAccountsSection.tsx +2 -2
- package/src/client/pages/automations/AutomationCostChart.tsx +50 -71
- package/src/client/pages/automations/AutomationDeleteDialog.tsx +87 -0
- package/src/client/pages/automations/AutomationDetails.tsx +193 -0
- package/src/client/pages/automations/AutomationEditPage.tsx +7 -8
- package/src/client/pages/automations/AutomationEditor.tsx +41 -29
- package/src/client/pages/automations/AutomationEditorLoading.tsx +1 -1
- package/src/client/pages/automations/AutomationEventFields.tsx +202 -119
- package/src/client/pages/automations/AutomationExecutionChart.tsx +51 -64
- package/src/client/pages/automations/AutomationExecutionStatusChart.tsx +47 -70
- package/src/client/pages/automations/AutomationExecutionsPage.tsx +150 -126
- package/src/client/pages/automations/AutomationFilters.tsx +189 -74
- package/src/client/pages/automations/AutomationLifecycle.tsx +109 -78
- package/src/client/pages/automations/AutomationOutcomeFields.tsx +26 -24
- package/src/client/pages/automations/AutomationRecentRuns.tsx +143 -0
- package/src/client/pages/automations/AutomationRow.tsx +12 -138
- package/src/client/pages/automations/AutomationRunDetails.tsx +307 -0
- package/src/client/pages/automations/AutomationRunsPage.tsx +119 -94
- package/src/client/pages/automations/AutomationScheduleFields.tsx +51 -51
- package/src/client/pages/automations/AutomationStatus.tsx +201 -0
- package/src/client/pages/automations/AutomationVersionsPage.tsx +65 -28
- package/src/client/pages/automations/AutomationsPage.tsx +80 -68
- package/src/client/pages/automations/AutomationsPageLayout.tsx +46 -14
- package/src/client/pages/automations/README.md +57 -0
- package/src/client/pages/automations/TriggerFilters.tsx +22 -0
- package/src/client/pages/automations/automationFormat.ts +37 -23
- package/src/client/pages/code/CodeActivityChart.tsx +59 -101
- package/src/client/pages/code/CodePage.tsx +52 -55
- package/src/client/pages/code/RepositoryPage.tsx +6 -8
- package/src/client/pages/locations/LocationActivityChart.tsx +53 -70
- package/src/client/pages/locations/LocationDirectoryActivityChart.tsx +60 -80
- package/src/client/pages/memory/MemoryCostChart.tsx +79 -103
- package/src/client/pages/memory/MemoryDetails.tsx +218 -0
- package/src/client/pages/memory/MemoryPage.tsx +168 -112
- package/src/client/pages/memory/MemoryPageLayout.tsx +33 -9
- package/src/client/pages/memory/MemoryTimeline.tsx +46 -69
- package/src/client/pages/memory/memoryRecord.ts +1 -1
- package/src/client/pages/people/ContributionGrid.tsx +23 -16
- package/src/client/pages/people/PeopleActivityChart.tsx +68 -107
- package/src/client/pages/people/ProfileCodeChanges.tsx +5 -5
- package/src/client/pages/system/BaseSandboxCard.tsx +1 -1
- package/src/client/pages/system/ConversationActivityChart.tsx +49 -67
- package/src/client/pages/system/GuardianActivity.tsx +1 -2
- package/src/client/pages/system/PluginBarChart.tsx +106 -87
- package/src/client/pages/system/PluginReportRecordSet.tsx +25 -24
- package/src/client/pages/system/README.md +23 -0
- package/src/client/pages/system/SystemNavigation.tsx +5 -1
- package/src/client/pages/system/WorkspaceDeleteDialog.tsx +111 -0
- package/src/client/pages/system/WorkspaceDetails.tsx +200 -43
- package/src/client/pages/system/WorkspaceEditor.tsx +59 -61
- package/src/client/pages/system/WorkspaceFormPage.tsx +23 -47
- package/src/client/pages/system/WorkspaceList.tsx +20 -30
- package/src/client/pages/system/WorkspacesPage.tsx +117 -119
- package/src/client/pages/system/sandboxSize.ts +8 -0
- package/src/client/pages/system/workspaceQueries.ts +53 -0
- package/src/client/pages/user/PluginUserPage.tsx +51 -2
- package/src/client/pages/user/pluginUserPageData.ts +1 -6
- package/src/client/styles.ts +4 -0
- package/src/client.tsx +7 -1
- package/src/mock-reporting/automation-collection.ts +4 -2
- package/src/mock-reporting/fixtures.ts +23 -7
- package/src/mock-reporting/routes.ts +12 -0
- package/src/mock-reporting/watches.ts +94 -0
- package/src/shell.ts +4 -9
- package/src/tailwind.css +0 -10
- package/src/client/components/Button.tsx +0 -118
- package/src/client/components/Field.tsx +0 -53
- package/src/client/components/SegmentedTabs.tsx +0 -119
- package/src/client/components/Select.tsx +0 -52
- package/src/client/components/Skeleton.tsx +0 -17
- package/src/client/components/TextInput.tsx +0 -58
- package/src/client/components/Tooltip.tsx +0 -216
- package/src/client/components/charts/useChartLayout.ts +0 -27
- package/src/client/components/ui/sheet.LICENSE +0 -21
- package/src/client/conversations/ConversationDetailsFrame.tsx +0 -119
- package/src/client/conversations/CopyMarkdownButton.tsx +0 -65
- package/src/client/pages/automations/AutomationDetailsDrawer.tsx +0 -219
- package/src/client/pages/dev/ChartFixtures.tsx +0 -325
- package/src/client/pages/dev/ComponentsPage.tsx +0 -899
- package/src/client/pages/dev/ConversationFixture.tsx +0 -231
- package/src/client/pages/dev/ObjectFixtures.tsx +0 -138
- package/src/client/pages/dev/SegmentedFixtures.tsx +0 -59
- package/src/client/pages/dev/SelectFixtures.tsx +0 -62
- package/src/client/pages/dev/TranscriptActivityFixtures.tsx +0 -206
- package/src/client/pages/memory/MemoryDetailsDrawer.tsx +0 -167
- package/src/client/pages/system/SandboxSizeSummary.tsx +0 -47
- package/src/client/pages/system/SnapshotSummary.tsx +0 -78
|
@@ -0,0 +1,67 @@
|
|
|
1
|
+
import { Popover as PopoverPrimitive } from "@base-ui/react/popover";
|
|
2
|
+
import { cn } from "@dashboard/lib/utils";
|
|
3
|
+
|
|
4
|
+
/** Own an anchored popover's state and dismissal. */
|
|
5
|
+
export function Popover(props: PopoverPrimitive.Root.Props) {
|
|
6
|
+
return <PopoverPrimitive.Root {...props} />;
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
/** Open a popover from the owning control. */
|
|
10
|
+
export function PopoverTrigger(props: PopoverPrimitive.Trigger.Props) {
|
|
11
|
+
return <PopoverPrimitive.Trigger data-slot="popover-trigger" {...props} />;
|
|
12
|
+
}
|
|
13
|
+
|
|
14
|
+
/** Position popover content within the viewport and share its themed surface. */
|
|
15
|
+
export function PopoverContent({
|
|
16
|
+
className,
|
|
17
|
+
align = "center",
|
|
18
|
+
side = "bottom",
|
|
19
|
+
...props
|
|
20
|
+
}: PopoverPrimitive.Popup.Props &
|
|
21
|
+
Pick<PopoverPrimitive.Positioner.Props, "align" | "side">) {
|
|
22
|
+
return (
|
|
23
|
+
<PopoverPrimitive.Portal>
|
|
24
|
+
<PopoverPrimitive.Positioner
|
|
25
|
+
align={align}
|
|
26
|
+
side={side}
|
|
27
|
+
sideOffset={8}
|
|
28
|
+
className="isolate z-50"
|
|
29
|
+
>
|
|
30
|
+
<PopoverPrimitive.Popup
|
|
31
|
+
data-slot="popover-content"
|
|
32
|
+
className={cn(
|
|
33
|
+
"w-80 max-w-[calc(100vw-2rem)] max-h-(--available-height) origin-(--transform-origin) overflow-y-auto rounded-lg border border-dashboard-border-emphasis bg-dashboard-surface-raised p-4 text-sm text-dashboard-text shadow-lg outline-none focus-visible:outline-2 focus-visible:outline-dashboard-focus transition-[opacity,scale] duration-180 motion-reduce:transition-none data-ending-style:opacity-0 data-starting-style:opacity-0 data-ending-style:scale-95 data-starting-style:scale-95",
|
|
34
|
+
className,
|
|
35
|
+
)}
|
|
36
|
+
{...props}
|
|
37
|
+
/>
|
|
38
|
+
</PopoverPrimitive.Positioner>
|
|
39
|
+
</PopoverPrimitive.Portal>
|
|
40
|
+
);
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/** Name a popover through its visible heading. */
|
|
44
|
+
export function PopoverTitle({
|
|
45
|
+
className,
|
|
46
|
+
...props
|
|
47
|
+
}: PopoverPrimitive.Title.Props) {
|
|
48
|
+
return (
|
|
49
|
+
<PopoverPrimitive.Title
|
|
50
|
+
className={cn("m-0 text-sm font-medium", className)}
|
|
51
|
+
{...props}
|
|
52
|
+
/>
|
|
53
|
+
);
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
/** Explain a popover without duplicating its heading. */
|
|
57
|
+
export function PopoverDescription({
|
|
58
|
+
className,
|
|
59
|
+
...props
|
|
60
|
+
}: PopoverPrimitive.Description.Props) {
|
|
61
|
+
return (
|
|
62
|
+
<PopoverPrimitive.Description
|
|
63
|
+
className={cn("m-0 leading-relaxed text-dashboard-text-muted", className)}
|
|
64
|
+
{...props}
|
|
65
|
+
/>
|
|
66
|
+
);
|
|
67
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { Radio as RadioPrimitive } from "@base-ui/react/radio";
|
|
2
|
+
import { RadioGroup as RadioGroupPrimitive } from "@base-ui/react/radio-group";
|
|
3
|
+
import { cn } from "@dashboard/lib/utils";
|
|
4
|
+
|
|
5
|
+
/** Own the selected value and arrow-key focus for a set of radios. */
|
|
6
|
+
function RadioGroup<Value>(props: RadioGroupPrimitive.Props<Value>) {
|
|
7
|
+
return <RadioGroupPrimitive data-slot="radio-group" {...props} />;
|
|
8
|
+
}
|
|
9
|
+
|
|
10
|
+
/** Render one Base UI radio with the dashboard control surface. */
|
|
11
|
+
function RadioGroupItem({
|
|
12
|
+
className,
|
|
13
|
+
...props
|
|
14
|
+
}: Omit<RadioPrimitive.Root.Props, "className"> & { className?: string }) {
|
|
15
|
+
return (
|
|
16
|
+
<RadioPrimitive.Root
|
|
17
|
+
data-slot="radio-group-item"
|
|
18
|
+
className={cn(
|
|
19
|
+
"grid size-4 shrink-0 cursor-pointer place-items-center rounded-full border border-dashboard-border-interactive bg-dashboard-control transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus data-checked:border-dashboard-focus data-checked:bg-dashboard-focus data-disabled:cursor-not-allowed data-disabled:opacity-50",
|
|
20
|
+
className,
|
|
21
|
+
)}
|
|
22
|
+
{...props}
|
|
23
|
+
>
|
|
24
|
+
<RadioPrimitive.Indicator
|
|
25
|
+
data-slot="radio-group-indicator"
|
|
26
|
+
className="size-1.5 rounded-full bg-dashboard-text-inverse"
|
|
27
|
+
/>
|
|
28
|
+
</RadioPrimitive.Root>
|
|
29
|
+
);
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
export { RadioGroup, RadioGroupItem };
|
|
@@ -1,9 +1,8 @@
|
|
|
1
|
-
// Adapted from shadcn/ui Base UI Sheet. See sheet.LICENSE.
|
|
2
1
|
import * as React from "react";
|
|
3
2
|
import { Dialog as SheetPrimitive } from "@base-ui/react/dialog";
|
|
4
3
|
import { cn } from "@dashboard/lib/utils";
|
|
5
4
|
|
|
6
|
-
import { Button } from "
|
|
5
|
+
import { Button } from "./button";
|
|
7
6
|
import { XIcon } from "lucide-react";
|
|
8
7
|
|
|
9
8
|
/** Own the open state and modal focus rules for a shadcn sheet. */
|
|
@@ -44,10 +43,12 @@ function SheetContent({
|
|
|
44
43
|
children,
|
|
45
44
|
side = "right",
|
|
46
45
|
showCloseButton = true,
|
|
46
|
+
closeButtonProps,
|
|
47
47
|
...props
|
|
48
48
|
}: SheetPrimitive.Popup.Props & {
|
|
49
49
|
side?: "top" | "right" | "bottom" | "left";
|
|
50
50
|
showCloseButton?: boolean;
|
|
51
|
+
closeButtonProps?: React.ComponentProps<typeof Button>;
|
|
51
52
|
}) {
|
|
52
53
|
// Keep portal order tied to the app tree when a resize mounts another sheet.
|
|
53
54
|
const portalRef = React.useRef<HTMLDivElement>(null);
|
|
@@ -70,7 +71,16 @@ function SheetContent({
|
|
|
70
71
|
{showCloseButton && (
|
|
71
72
|
<SheetPrimitive.Close
|
|
72
73
|
data-slot="sheet-close"
|
|
73
|
-
render={
|
|
74
|
+
render={
|
|
75
|
+
<Button
|
|
76
|
+
{...closeButtonProps}
|
|
77
|
+
className={cn(
|
|
78
|
+
"absolute top-3 right-3",
|
|
79
|
+
closeButtonProps?.className,
|
|
80
|
+
)}
|
|
81
|
+
size="icon"
|
|
82
|
+
/>
|
|
83
|
+
}
|
|
74
84
|
>
|
|
75
85
|
<XIcon aria-hidden="true" size={15} strokeWidth={2.25} />
|
|
76
86
|
<span className="sr-only">Close</span>
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
import { cn } from "@dashboard/lib/utils";
|
|
3
|
+
|
|
4
|
+
/** Hide one content placeholder from assistive technology. */
|
|
5
|
+
function Skeleton({ className, ...props }: ComponentProps<"div">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
aria-hidden="true"
|
|
9
|
+
data-slot="skeleton"
|
|
10
|
+
className={cn(
|
|
11
|
+
"animate-pulse rounded-md bg-dashboard-fill-soft motion-reduce:animate-none",
|
|
12
|
+
className,
|
|
13
|
+
)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
);
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
export { Skeleton };
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
import { Loader2Icon } from "lucide-react";
|
|
3
|
+
import { cn } from "@dashboard/lib/utils";
|
|
4
|
+
|
|
5
|
+
/** Render a loading spinner. It stops spinning when the user prefers reduced motion. */
|
|
6
|
+
function Spinner({ className, ...props }: ComponentProps<"svg">) {
|
|
7
|
+
return (
|
|
8
|
+
<Loader2Icon
|
|
9
|
+
data-slot="spinner"
|
|
10
|
+
role="status"
|
|
11
|
+
aria-label="Loading"
|
|
12
|
+
className={cn(
|
|
13
|
+
"size-4 animate-spin motion-reduce:animate-none",
|
|
14
|
+
className,
|
|
15
|
+
)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export { Spinner };
|
|
@@ -0,0 +1,101 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
import { cn } from "@dashboard/lib/utils";
|
|
3
|
+
|
|
4
|
+
/** Render a data table that scrolls sideways when it is wider than its box. */
|
|
5
|
+
function Table({ className, ...props }: ComponentProps<"table">) {
|
|
6
|
+
return (
|
|
7
|
+
<div
|
|
8
|
+
data-slot="table-container"
|
|
9
|
+
className="relative w-full overflow-x-auto"
|
|
10
|
+
>
|
|
11
|
+
<table
|
|
12
|
+
data-slot="table"
|
|
13
|
+
className={cn("w-full border-collapse text-left text-sm", className)}
|
|
14
|
+
{...props}
|
|
15
|
+
/>
|
|
16
|
+
</div>
|
|
17
|
+
);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
function TableHeader({ className, ...props }: ComponentProps<"thead">) {
|
|
21
|
+
return (
|
|
22
|
+
<thead
|
|
23
|
+
data-slot="table-header"
|
|
24
|
+
className={cn("[&_tr]:hover:bg-transparent", className)}
|
|
25
|
+
{...props}
|
|
26
|
+
/>
|
|
27
|
+
);
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
function TableBody({ className, ...props }: ComponentProps<"tbody">) {
|
|
31
|
+
return (
|
|
32
|
+
<tbody
|
|
33
|
+
data-slot="table-body"
|
|
34
|
+
className={cn("[&_tr:last-child]:border-0", className)}
|
|
35
|
+
{...props}
|
|
36
|
+
/>
|
|
37
|
+
);
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
function TableFooter({ className, ...props }: ComponentProps<"tfoot">) {
|
|
41
|
+
return (
|
|
42
|
+
<tfoot
|
|
43
|
+
data-slot="table-footer"
|
|
44
|
+
className={cn(
|
|
45
|
+
"border-t border-dashboard-border font-medium [&>tr]:last:border-b-0",
|
|
46
|
+
className,
|
|
47
|
+
)}
|
|
48
|
+
{...props}
|
|
49
|
+
/>
|
|
50
|
+
);
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
function TableRow({ className, ...props }: ComponentProps<"tr">) {
|
|
54
|
+
return (
|
|
55
|
+
<tr
|
|
56
|
+
data-slot="table-row"
|
|
57
|
+
className={cn(
|
|
58
|
+
"border-b border-dashboard-border-subtle transition-colors hover:bg-dashboard-fill-soft has-aria-expanded:bg-dashboard-fill-soft data-[state=selected]:bg-dashboard-surface-active",
|
|
59
|
+
className,
|
|
60
|
+
)}
|
|
61
|
+
{...props}
|
|
62
|
+
/>
|
|
63
|
+
);
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/** Render a column header in the dashboard label style. */
|
|
67
|
+
function TableHead({ className, ...props }: ComponentProps<"th">) {
|
|
68
|
+
return (
|
|
69
|
+
<th
|
|
70
|
+
data-slot="table-head"
|
|
71
|
+
className={cn(
|
|
72
|
+
"px-2 py-2.5 text-left align-middle font-sans text-xs font-medium whitespace-nowrap text-dashboard-text-muted [&:has([role=checkbox])]:pr-0",
|
|
73
|
+
className,
|
|
74
|
+
)}
|
|
75
|
+
{...props}
|
|
76
|
+
/>
|
|
77
|
+
);
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
function TableCell({ className, ...props }: ComponentProps<"td">) {
|
|
81
|
+
return (
|
|
82
|
+
<td
|
|
83
|
+
data-slot="table-cell"
|
|
84
|
+
className={cn(
|
|
85
|
+
"p-2 align-middle whitespace-nowrap text-dashboard-text [&:has([role=checkbox])]:pr-0",
|
|
86
|
+
className,
|
|
87
|
+
)}
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
export {
|
|
94
|
+
Table,
|
|
95
|
+
TableHeader,
|
|
96
|
+
TableBody,
|
|
97
|
+
TableFooter,
|
|
98
|
+
TableHead,
|
|
99
|
+
TableRow,
|
|
100
|
+
TableCell,
|
|
101
|
+
};
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
import { Tabs as TabsPrimitive } from "@base-ui/react/tabs";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@dashboard/lib/utils";
|
|
4
|
+
|
|
5
|
+
/** Own the selected panel and link each tab to its panel. */
|
|
6
|
+
function Tabs({ className, ...props }: TabsPrimitive.Root.Props) {
|
|
7
|
+
return (
|
|
8
|
+
<TabsPrimitive.Root
|
|
9
|
+
data-slot="tabs"
|
|
10
|
+
className={cn("grid min-w-0 gap-4", className)}
|
|
11
|
+
{...props}
|
|
12
|
+
/>
|
|
13
|
+
);
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
const tabsListVariants = cva("group/tabs-list flex min-w-0", {
|
|
17
|
+
variants: {
|
|
18
|
+
variant: {
|
|
19
|
+
/** A pill track. Tabs share the row equally on wide screens. */
|
|
20
|
+
default:
|
|
21
|
+
"gap-1 rounded-lg border border-dashboard-border bg-dashboard-surface-panel p-1",
|
|
22
|
+
/** Underlined tabs on a bottom border, for collection filters. */
|
|
23
|
+
line: "gap-1 border-b border-dashboard-border-subtle",
|
|
24
|
+
},
|
|
25
|
+
},
|
|
26
|
+
defaultVariants: { variant: "default" },
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
/** Hold the tabs. `variant` sets the look of the row and its tabs. */
|
|
30
|
+
function TabsList({
|
|
31
|
+
className,
|
|
32
|
+
variant = "default",
|
|
33
|
+
...props
|
|
34
|
+
}: TabsPrimitive.List.Props & VariantProps<typeof tabsListVariants>) {
|
|
35
|
+
return (
|
|
36
|
+
<TabsPrimitive.List
|
|
37
|
+
data-slot="tabs-list"
|
|
38
|
+
data-variant={variant}
|
|
39
|
+
className={cn(tabsListVariants({ variant }), className)}
|
|
40
|
+
{...props}
|
|
41
|
+
/>
|
|
42
|
+
);
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/** Select one panel. The active tab uses the strong fill or the accent line. */
|
|
46
|
+
function TabsTrigger({ className, ...props }: TabsPrimitive.Tab.Props) {
|
|
47
|
+
return (
|
|
48
|
+
<TabsPrimitive.Tab
|
|
49
|
+
data-slot="tabs-trigger"
|
|
50
|
+
className={cn(
|
|
51
|
+
"inline-flex min-w-0 cursor-pointer items-center gap-1.5 border-0 bg-transparent font-sans text-xs font-medium text-dashboard-text-muted transition-colors hover:text-dashboard-text focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus",
|
|
52
|
+
"group-data-[variant=default]/tabs-list:min-h-9 group-data-[variant=default]/tabs-list:flex-auto group-data-[variant=default]/tabs-list:justify-center group-data-[variant=default]/tabs-list:rounded-md group-data-[variant=default]/tabs-list:px-1.5 group-data-[variant=default]/tabs-list:py-2 group-data-[variant=default]/tabs-list:hover:bg-dashboard-fill-faint group-data-[variant=default]/tabs-list:data-active:bg-dashboard-fill-strong group-data-[variant=default]/tabs-list:data-active:text-dashboard-text group-data-[variant=default]/tabs-list:sm:flex-1",
|
|
53
|
+
"group-data-[variant=line]/tabs-list:relative group-data-[variant=line]/tabs-list:gap-2 group-data-[variant=line]/tabs-list:px-3 group-data-[variant=line]/tabs-list:py-2.5 group-data-[variant=line]/tabs-list:after:absolute group-data-[variant=line]/tabs-list:after:inset-x-2 group-data-[variant=line]/tabs-list:after:bottom-0 group-data-[variant=line]/tabs-list:after:h-px group-data-[variant=line]/tabs-list:data-active:text-dashboard-accent-text group-data-[variant=line]/tabs-list:data-active:after:bg-dashboard-accent",
|
|
54
|
+
className,
|
|
55
|
+
)}
|
|
56
|
+
{...props}
|
|
57
|
+
/>
|
|
58
|
+
);
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
/** Show the content for the selected tab. */
|
|
62
|
+
function TabsContent({ className, ...props }: TabsPrimitive.Panel.Props) {
|
|
63
|
+
return (
|
|
64
|
+
<TabsPrimitive.Panel
|
|
65
|
+
data-slot="tabs-content"
|
|
66
|
+
className={cn(
|
|
67
|
+
"min-w-0 rounded-md focus-visible:outline focus-visible:outline-2 focus-visible:outline-dashboard-focus",
|
|
68
|
+
className,
|
|
69
|
+
)}
|
|
70
|
+
{...props}
|
|
71
|
+
/>
|
|
72
|
+
);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
export { Tabs, TabsList, TabsTrigger, TabsContent };
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
import type { ComponentProps } from "react";
|
|
2
|
+
import { cn } from "@dashboard/lib/utils";
|
|
3
|
+
|
|
4
|
+
import { textControlClass } from "./input";
|
|
5
|
+
|
|
6
|
+
/** Render a text area with the dashboard text control surface. */
|
|
7
|
+
function Textarea({ className, ...props }: ComponentProps<"textarea">) {
|
|
8
|
+
return (
|
|
9
|
+
<textarea
|
|
10
|
+
data-slot="textarea"
|
|
11
|
+
className={cn(textControlClass, "min-h-36", className)}
|
|
12
|
+
{...props}
|
|
13
|
+
/>
|
|
14
|
+
);
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export { Textarea };
|
|
@@ -0,0 +1,215 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { Toast as ToastPrimitive } from "@base-ui/react/toast";
|
|
3
|
+
import { cn } from "@dashboard/lib/utils";
|
|
4
|
+
|
|
5
|
+
import { Button } from "./button";
|
|
6
|
+
import {
|
|
7
|
+
XIcon,
|
|
8
|
+
CircleCheckIcon,
|
|
9
|
+
InfoIcon,
|
|
10
|
+
TriangleAlertIcon,
|
|
11
|
+
OctagonXIcon,
|
|
12
|
+
Loader2Icon,
|
|
13
|
+
} from "lucide-react";
|
|
14
|
+
|
|
15
|
+
function ToastProvider({ ...props }: ToastPrimitive.Provider.Props) {
|
|
16
|
+
return <ToastPrimitive.Provider {...props} />;
|
|
17
|
+
}
|
|
18
|
+
|
|
19
|
+
function ToastPortal({ ...props }: ToastPrimitive.Portal.Props) {
|
|
20
|
+
return <ToastPrimitive.Portal data-slot="toast-portal" {...props} />;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
function ToastViewport({ className, ...props }: ToastPrimitive.Viewport.Props) {
|
|
24
|
+
return (
|
|
25
|
+
<ToastPrimitive.Viewport
|
|
26
|
+
data-slot="toast-viewport"
|
|
27
|
+
className={cn(
|
|
28
|
+
"pointer-events-none fixed inset-x-4 bottom-[max(5.25rem,env(safe-area-inset-bottom))] z-[60] mx-auto w-auto max-w-sm outline-none sm:right-4 sm:left-auto sm:mx-0 sm:w-full",
|
|
29
|
+
className,
|
|
30
|
+
)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
function Toast({ className, ...props }: ToastPrimitive.Root.Props) {
|
|
37
|
+
return (
|
|
38
|
+
<ToastPrimitive.Root
|
|
39
|
+
data-slot="toast"
|
|
40
|
+
className={cn(
|
|
41
|
+
"group/toast pointer-events-auto absolute right-0 bottom-0 z-[calc(1000-var(--toast-index))] w-full origin-bottom rounded-lg border border-dashboard-border-emphasis bg-dashboard-surface-raised text-dashboard-text shadow-lg will-change-transform outline-none select-none focus-visible:outline-2 focus-visible:outline-dashboard-focus",
|
|
42
|
+
"[--gap:0.75rem] [--height:var(--toast-frontmost-height,var(--toast-height))] [--offset-y:calc(var(--toast-offset-y)*-1+calc(var(--toast-index)*var(--gap)*-1)+var(--toast-swipe-movement-y))] [--peek:0.75rem] [--scale:calc(max(0,1-(var(--toast-index)*0.1)))] [--shrink:calc(1-var(--scale))]",
|
|
43
|
+
"h-(--height) [transform:translateX(var(--toast-swipe-movement-x))_translateY(calc(var(--toast-swipe-movement-y)-(var(--toast-index)*var(--peek))-(var(--shrink)*var(--height))))_scale(var(--scale))] [transition:transform_180ms_ease-out,opacity_180ms,height_180ms] motion-reduce:transition-none",
|
|
44
|
+
"after:absolute after:top-full after:left-0 after:h-[calc(var(--gap)+1px)] after:w-full after:content-['']",
|
|
45
|
+
"data-expanded:h-(--toast-height) data-expanded:[transform:translateX(var(--toast-swipe-movement-x))_translateY(var(--offset-y))]",
|
|
46
|
+
"data-limited:opacity-0 data-starting-style:[transform:translateY(150%)]",
|
|
47
|
+
"[&[data-ending-style]:not([data-limited]):not([data-swipe-direction])]:[transform:translateY(150%)]",
|
|
48
|
+
"data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
49
|
+
"data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
50
|
+
"data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
51
|
+
"data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
52
|
+
"data-expanded:data-ending-style:data-[swipe-direction=down]:[transform:translateY(calc(var(--toast-swipe-movement-y)+150%))]",
|
|
53
|
+
"data-expanded:data-ending-style:data-[swipe-direction=left]:[transform:translateX(calc(var(--toast-swipe-movement-x)-150%))_translateY(var(--offset-y))]",
|
|
54
|
+
"data-expanded:data-ending-style:data-[swipe-direction=right]:[transform:translateX(calc(var(--toast-swipe-movement-x)+150%))_translateY(var(--offset-y))]",
|
|
55
|
+
"data-expanded:data-ending-style:data-[swipe-direction=up]:[transform:translateY(calc(var(--toast-swipe-movement-y)-150%))]",
|
|
56
|
+
className,
|
|
57
|
+
)}
|
|
58
|
+
{...props}
|
|
59
|
+
/>
|
|
60
|
+
);
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
function ToastContent({ className, ...props }: ToastPrimitive.Content.Props) {
|
|
64
|
+
return (
|
|
65
|
+
<ToastPrimitive.Content
|
|
66
|
+
data-slot="toast-content"
|
|
67
|
+
className={cn(
|
|
68
|
+
"flex h-full items-center gap-3 overflow-hidden p-4 transition-opacity duration-180 motion-reduce:transition-none ease-[cubic-bezier(0.22,1,0.36,1)] data-behind:opacity-0 data-expanded:opacity-100",
|
|
69
|
+
className,
|
|
70
|
+
)}
|
|
71
|
+
{...props}
|
|
72
|
+
/>
|
|
73
|
+
);
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
function ToastTitle({ className, ...props }: ToastPrimitive.Title.Props) {
|
|
77
|
+
return (
|
|
78
|
+
<ToastPrimitive.Title
|
|
79
|
+
data-slot="toast-title"
|
|
80
|
+
className={cn("break-words text-sm font-medium", className)}
|
|
81
|
+
{...props}
|
|
82
|
+
/>
|
|
83
|
+
);
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
function ToastDescription({
|
|
87
|
+
className,
|
|
88
|
+
...props
|
|
89
|
+
}: ToastPrimitive.Description.Props) {
|
|
90
|
+
return (
|
|
91
|
+
<ToastPrimitive.Description
|
|
92
|
+
data-slot="toast-description"
|
|
93
|
+
className={cn("break-words text-sm text-dashboard-text-muted", className)}
|
|
94
|
+
{...props}
|
|
95
|
+
/>
|
|
96
|
+
);
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
function ToastAction({
|
|
100
|
+
className,
|
|
101
|
+
render = <Button />,
|
|
102
|
+
...props
|
|
103
|
+
}: ToastPrimitive.Action.Props) {
|
|
104
|
+
return (
|
|
105
|
+
<ToastPrimitive.Action
|
|
106
|
+
data-slot="toast-action"
|
|
107
|
+
render={render}
|
|
108
|
+
className={cn("shrink-0", className)}
|
|
109
|
+
{...props}
|
|
110
|
+
/>
|
|
111
|
+
);
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function ToastClose({
|
|
115
|
+
className,
|
|
116
|
+
children,
|
|
117
|
+
render = <Button size="icon" />,
|
|
118
|
+
...props
|
|
119
|
+
}: ToastPrimitive.Close.Props) {
|
|
120
|
+
return (
|
|
121
|
+
<ToastPrimitive.Close
|
|
122
|
+
data-slot="toast-close"
|
|
123
|
+
aria-label="Close toast"
|
|
124
|
+
render={render}
|
|
125
|
+
className={cn(
|
|
126
|
+
"relative shrink-0 text-dashboard-text-muted after:absolute after:-inset-2 after:content-[''] hover:text-dashboard-text",
|
|
127
|
+
className,
|
|
128
|
+
)}
|
|
129
|
+
{...props}
|
|
130
|
+
>
|
|
131
|
+
{children ?? <XIcon aria-hidden="true" />}
|
|
132
|
+
</ToastPrimitive.Close>
|
|
133
|
+
);
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
function ToastIcon({ type }: { type: string | undefined }) {
|
|
137
|
+
let icon: React.ReactNode = null;
|
|
138
|
+
|
|
139
|
+
if (type === "success") {
|
|
140
|
+
icon = <CircleCheckIcon aria-hidden="true" />;
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
if (type === "info") {
|
|
144
|
+
icon = <InfoIcon aria-hidden="true" />;
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
if (type === "warning") {
|
|
148
|
+
icon = <TriangleAlertIcon aria-hidden="true" />;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
if (type === "error") {
|
|
152
|
+
icon = <OctagonXIcon className="text-rose-300" aria-hidden="true" />;
|
|
153
|
+
}
|
|
154
|
+
|
|
155
|
+
if (type === "loading") {
|
|
156
|
+
icon = (
|
|
157
|
+
<Loader2Icon
|
|
158
|
+
className="animate-spin motion-reduce:animate-none"
|
|
159
|
+
aria-hidden="true"
|
|
160
|
+
/>
|
|
161
|
+
);
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
if (!icon) {
|
|
165
|
+
return null;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
return (
|
|
169
|
+
<span
|
|
170
|
+
data-slot="toast-icon"
|
|
171
|
+
className="shrink-0 [&_svg]:pointer-events-none [&_svg:not([class*='size-'])]:size-4"
|
|
172
|
+
>
|
|
173
|
+
{icon}
|
|
174
|
+
</span>
|
|
175
|
+
);
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
function ToastList() {
|
|
179
|
+
const { toasts } = ToastPrimitive.useToastManager();
|
|
180
|
+
|
|
181
|
+
return toasts.map((toastItem) => (
|
|
182
|
+
<Toast key={toastItem.id} toast={toastItem}>
|
|
183
|
+
<ToastContent>
|
|
184
|
+
<ToastIcon type={toastItem.type} />
|
|
185
|
+
<div className="flex min-w-0 flex-1 flex-col gap-1">
|
|
186
|
+
<ToastTitle />
|
|
187
|
+
<ToastDescription />
|
|
188
|
+
</div>
|
|
189
|
+
{toastItem.actionProps ? (
|
|
190
|
+
<ToastAction {...toastItem.actionProps} />
|
|
191
|
+
) : null}
|
|
192
|
+
<ToastClose />
|
|
193
|
+
</ToastContent>
|
|
194
|
+
</Toast>
|
|
195
|
+
));
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
/** Own notification state for one mounted dashboard and render its toast stack. */
|
|
199
|
+
function Toaster({ children, ...props }: ToastPrimitive.Provider.Props) {
|
|
200
|
+
return (
|
|
201
|
+
<ToastProvider limit={3} timeout={4000} {...props}>
|
|
202
|
+
{children}
|
|
203
|
+
<ToastPortal>
|
|
204
|
+
<ToastViewport>
|
|
205
|
+
<ToastList />
|
|
206
|
+
</ToastViewport>
|
|
207
|
+
</ToastPortal>
|
|
208
|
+
</ToastProvider>
|
|
209
|
+
);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
/** Show short notifications through the nearest mounted Toaster. */
|
|
213
|
+
const useToastManager = ToastPrimitive.useToastManager;
|
|
214
|
+
|
|
215
|
+
export { Toaster, useToastManager };
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
|
|
2
|
+
import { ToggleGroup as ToggleGroupPrimitive } from "@base-ui/react/toggle-group";
|
|
3
|
+
import type { VariantProps } from "class-variance-authority";
|
|
4
|
+
import { cn } from "@dashboard/lib/utils";
|
|
5
|
+
import { toggleVariants } from "./toggle";
|
|
6
|
+
|
|
7
|
+
/** Own the pressed values and arrow-key focus across a row of items. */
|
|
8
|
+
function ToggleGroup<Value extends string>({
|
|
9
|
+
className,
|
|
10
|
+
...props
|
|
11
|
+
}: ToggleGroupPrimitive.Props<Value>) {
|
|
12
|
+
return (
|
|
13
|
+
<ToggleGroupPrimitive
|
|
14
|
+
data-slot="toggle-group"
|
|
15
|
+
className={cn("flex items-center gap-1", className)}
|
|
16
|
+
{...props}
|
|
17
|
+
/>
|
|
18
|
+
);
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
/** Render one item with a `ui/toggle` look. */
|
|
22
|
+
function ToggleGroupItem<Value extends string>({
|
|
23
|
+
className,
|
|
24
|
+
variant,
|
|
25
|
+
...props
|
|
26
|
+
}: TogglePrimitive.Props<Value> & VariantProps<typeof toggleVariants>) {
|
|
27
|
+
return (
|
|
28
|
+
<TogglePrimitive
|
|
29
|
+
data-slot="toggle-group-item"
|
|
30
|
+
className={cn(toggleVariants({ variant }), className)}
|
|
31
|
+
{...props}
|
|
32
|
+
/>
|
|
33
|
+
);
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
export { ToggleGroup, ToggleGroupItem };
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
import { Toggle as TogglePrimitive } from "@base-ui/react/toggle";
|
|
2
|
+
import { cva, type VariantProps } from "class-variance-authority";
|
|
3
|
+
import { cn } from "@dashboard/lib/utils";
|
|
4
|
+
|
|
5
|
+
/** Pressed-state looks shared by `Toggle` and `ToggleGroupItem`. */
|
|
6
|
+
const toggleVariants = cva(
|
|
7
|
+
"inline-flex cursor-pointer items-center justify-center font-sans text-xs font-medium transition-colors focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-dashboard-focus",
|
|
8
|
+
{
|
|
9
|
+
variants: {
|
|
10
|
+
variant: {
|
|
11
|
+
/** Quiet when off. The pressed state uses the active surface. */
|
|
12
|
+
default:
|
|
13
|
+
"h-7 min-w-10 rounded-md border border-transparent bg-transparent px-2 text-dashboard-text-muted hover:bg-dashboard-fill-hover hover:text-dashboard-text data-pressed:border-dashboard-border-interactive data-pressed:bg-dashboard-surface-active data-pressed:text-dashboard-text",
|
|
14
|
+
/** A bordered filter chip. The pressed state fills solid. */
|
|
15
|
+
pill: "rounded-md border border-dashboard-border-strong bg-dashboard-surface-raised px-2 py-1 leading-tight text-dashboard-text-muted hover:border-dashboard-border-interactive hover:bg-dashboard-surface-hover hover:text-dashboard-text data-pressed:border-dashboard-border-heavy data-pressed:bg-dashboard-text-solid data-pressed:text-dashboard-text-inverse",
|
|
16
|
+
},
|
|
17
|
+
},
|
|
18
|
+
defaultVariants: { variant: "default" },
|
|
19
|
+
},
|
|
20
|
+
);
|
|
21
|
+
|
|
22
|
+
/** Turn one setting on or off. */
|
|
23
|
+
function Toggle<Value extends string>({
|
|
24
|
+
className,
|
|
25
|
+
variant,
|
|
26
|
+
...props
|
|
27
|
+
}: TogglePrimitive.Props<Value> & VariantProps<typeof toggleVariants>) {
|
|
28
|
+
return (
|
|
29
|
+
<TogglePrimitive
|
|
30
|
+
data-slot="toggle"
|
|
31
|
+
className={cn(toggleVariants({ variant }), className)}
|
|
32
|
+
{...props}
|
|
33
|
+
/>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
export { Toggle, toggleVariants };
|