@mostrom/app-shell 0.1.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/.claude/ralph-loop.local.md +9 -0
- package/README.md +172 -0
- package/bin/init.js +269 -0
- package/bun.lock +401 -0
- package/components.json +28 -0
- package/package.json +74 -0
- package/scripts/publish-npm.sh +202 -0
- package/src/AppShell.tsx +847 -0
- package/src/components/PageHeader.tsx +160 -0
- package/src/components/data-table/README.md +447 -0
- package/src/components/data-table/data-table-preferences.tsx +184 -0
- package/src/components/data-table/data-table-toolbar.tsx +118 -0
- package/src/components/data-table/data-table.tsx +37 -0
- package/src/components/data-table/index.ts +32 -0
- package/src/components/global-header/AllServicesButton.tsx +127 -0
- package/src/components/global-header/CategoriesButton.tsx +120 -0
- package/src/components/global-header/GlobalHeader.tsx +59 -0
- package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
- package/src/components/global-header/HeaderUtilities.tsx +243 -0
- package/src/components/global-header/ServicesMenu.tsx +246 -0
- package/src/components/layout/AppBreadcrumb.tsx +70 -0
- package/src/components/layout/AppFlashbar.tsx +95 -0
- package/src/components/layout/AppLayout.tsx +271 -0
- package/src/components/layout/AppNavigation.tsx +313 -0
- package/src/components/layout/AppSidebar.tsx +229 -0
- package/src/components/patterns/index.ts +14 -0
- package/src/components/patterns/p-alert-5.tsx +19 -0
- package/src/components/patterns/p-autocomplete-5.tsx +89 -0
- package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
- package/src/components/patterns/p-button-42.tsx +37 -0
- package/src/components/patterns/p-button-51.tsx +14 -0
- package/src/components/patterns/p-button-6.tsx +5 -0
- package/src/components/patterns/p-calendar-1.tsx +18 -0
- package/src/components/patterns/p-card-1.tsx +33 -0
- package/src/components/patterns/p-card-2.tsx +26 -0
- package/src/components/patterns/p-card-5.tsx +31 -0
- package/src/components/patterns/p-collapsible-7.tsx +121 -0
- package/src/components/patterns/p-command-6.tsx +113 -0
- package/src/components/patterns/p-dialog-1.tsx +56 -0
- package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
- package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
- package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
- package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
- package/src/components/patterns/p-empty-2.tsx +34 -0
- package/src/components/patterns/p-file-upload-1.tsx +72 -0
- package/src/components/patterns/p-filters-1.tsx +666 -0
- package/src/components/patterns/p-frame-2.tsx +26 -0
- package/src/components/patterns/p-tabs-2.tsx +129 -0
- package/src/components/reui/alert.tsx +92 -0
- package/src/components/reui/autocomplete.tsx +343 -0
- package/src/components/reui/badge.tsx +87 -0
- package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
- package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
- package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
- package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
- package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
- package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
- package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
- package/src/components/reui/data-grid/data-grid.tsx +209 -0
- package/src/components/reui/date-selector.tsx +1330 -0
- package/src/components/reui/filters.tsx +1869 -0
- package/src/components/reui/frame.tsx +134 -0
- package/src/components/reui/index.ts +17 -0
- package/src/components/reui/timeline.tsx +219 -0
- package/src/components/search/Autocomplete.tsx +183 -0
- package/src/components/search/AutocompleteClient.tsx +293 -0
- package/src/components/search/GlobalSearch.tsx +187 -0
- package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
- package/src/components/section-drawer/index.ts +19 -0
- package/src/components/section-drawer/section-drawer.css +665 -0
- package/src/components/section-drawer/section-drawer.tsx +467 -0
- package/src/components/sectioned-list-board/README.md +78 -0
- package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
- package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
- package/src/components/sectioned-list-board/index.ts +19 -0
- package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
- package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
- package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
- package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
- package/src/components/sectioned-list-board/types.ts +216 -0
- package/src/components/sectioned-list-table/README.md +80 -0
- package/src/components/sectioned-list-table/index.ts +14 -0
- package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
- package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
- package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
- package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
- package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
- package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
- package/src/components/sectioned-list-table/types.ts +120 -0
- package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
- package/src/components/ui/actions-dropdown.tsx +109 -0
- package/src/components/ui/assignee-selector.tsx +209 -0
- package/src/components/ui/avatar.tsx +107 -0
- package/src/components/ui/breadcrumb.tsx +109 -0
- package/src/components/ui/button-group.tsx +83 -0
- package/src/components/ui/button.tsx +64 -0
- package/src/components/ui/calendar.tsx +220 -0
- package/src/components/ui/card.tsx +92 -0
- package/src/components/ui/chart.tsx +376 -0
- package/src/components/ui/checkbox.tsx +30 -0
- package/src/components/ui/collapsible.tsx +33 -0
- package/src/components/ui/command.tsx +182 -0
- package/src/components/ui/context-menu.tsx +250 -0
- package/src/components/ui/create-button-group.tsx +128 -0
- package/src/components/ui/dialog.tsx +156 -0
- package/src/components/ui/drawer.tsx +133 -0
- package/src/components/ui/dropdown-menu.tsx +255 -0
- package/src/components/ui/empty.tsx +104 -0
- package/src/components/ui/field.tsx +248 -0
- package/src/components/ui/form.tsx +165 -0
- package/src/components/ui/index.ts +37 -0
- package/src/components/ui/input-group.tsx +168 -0
- package/src/components/ui/input.tsx +21 -0
- package/src/components/ui/kbd.tsx +28 -0
- package/src/components/ui/label.tsx +22 -0
- package/src/components/ui/navigation-menu.tsx +168 -0
- package/src/components/ui/page-header.tsx +80 -0
- package/src/components/ui/popover.tsx +87 -0
- package/src/components/ui/scroll-area.tsx +56 -0
- package/src/components/ui/select.tsx +190 -0
- package/src/components/ui/separator.tsx +26 -0
- package/src/components/ui/sheet.tsx +141 -0
- package/src/components/ui/sidebar.tsx +726 -0
- package/src/components/ui/skeleton.tsx +13 -0
- package/src/components/ui/sonner.tsx +38 -0
- package/src/components/ui/switch.tsx +33 -0
- package/src/components/ui/tabs.tsx +91 -0
- package/src/components/ui/textarea.tsx +18 -0
- package/src/components/ui/toggle-group.tsx +83 -0
- package/src/components/ui/toggle.tsx +45 -0
- package/src/components/ui/tooltip.tsx +57 -0
- package/src/hooks/use-copy-to-clipboard.ts +37 -0
- package/src/hooks/use-file-upload.ts +415 -0
- package/src/hooks/use-mobile.ts +19 -0
- package/src/index.ts +95 -0
- package/src/lib/utils.ts +6 -0
- package/src/styles.css +1859 -0
- package/src/urls.ts +83 -0
- package/src/vite.d.ts +22 -0
- package/src/vite.js +241 -0
- package/tsconfig.base.json +18 -0
- package/tsconfig.json +24 -0
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { cva, type VariantProps } from "class-variance-authority"
|
|
2
|
+
|
|
3
|
+
import { cn } from "@/lib/utils"
|
|
4
|
+
|
|
5
|
+
const frameVariants = cva(
|
|
6
|
+
"relative flex flex-col bg-muted/50 gap-0.75 p-0.75 rounded-(--frame-radius) [--frame-radius:var(--radius-lg)]",
|
|
7
|
+
{
|
|
8
|
+
variants: {
|
|
9
|
+
variant: {
|
|
10
|
+
default: "border border-border/50 bg-clip-padding",
|
|
11
|
+
ghost: "",
|
|
12
|
+
},
|
|
13
|
+
spacing: {
|
|
14
|
+
xs: "[&_[data-slot=frame-panel]]:p-2 [&_[data-slot=frame-panel-header]]:px-2 [&_[data-slot=frame-panel-header]]:py-1 [&_[data-slot=frame-panel-footer]]:px-2 [&_[data-slot=frame-panel-footer]]:py-1",
|
|
15
|
+
sm: "[&_[data-slot=frame-panel]]:p-3 [&_[data-slot=frame-panel-header]]:px-3 [&_[data-slot=frame-panel-header]]:py-2 [&_[data-slot=frame-panel-footer]]:px-3 [&_[data-slot=frame-panel-footer]]:py-2",
|
|
16
|
+
default:
|
|
17
|
+
"[&_[data-slot=frame-panel]]:p-4 [&_[data-slot=frame-panel-header]]:px-4 [&_[data-slot=frame-panel-header]]:py-3 [&_[data-slot=frame-panel-footer]]:px-4 [&_[data-slot=frame-panel-footer]]:py-3",
|
|
18
|
+
lg: "[&_[data-slot=frame-panel]]:p-5 [&_[data-slot=frame-panel-header]]:px-5 [&_[data-slot=frame-panel-header]]:py-4 [&_[data-slot=frame-panel-footer]]:px-5 [&_[data-slot=frame-panel-footer]]:py-4",
|
|
19
|
+
},
|
|
20
|
+
stacked: {
|
|
21
|
+
true: [
|
|
22
|
+
"gap-0 *:has-[+[data-slot=frame-panel]]:rounded-b-none",
|
|
23
|
+
"*:has-[+[data-slot=frame-panel]]:before:hidden",
|
|
24
|
+
"dark:*:has-[+[data-slot=frame-panel]]:before:block",
|
|
25
|
+
"*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:rounded-t-none",
|
|
26
|
+
"*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:border-t-0",
|
|
27
|
+
"dark:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:before:hidden",
|
|
28
|
+
],
|
|
29
|
+
false: [
|
|
30
|
+
"data-[spacing=sm]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-0.5",
|
|
31
|
+
"data-[spacing=default]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-1",
|
|
32
|
+
"data-[spacing=lg]:*:[[data-slot=frame-panel]+[data-slot=frame-panel]]:mt-2",
|
|
33
|
+
],
|
|
34
|
+
},
|
|
35
|
+
dense: {
|
|
36
|
+
true: "p-0 border-border [&_[data-slot=frame-panel]]:-mx-px [&_[data-slot=frame-panel]]:before:hidden [&_[data-slot=frame-panel]:last-child]:-mb-px",
|
|
37
|
+
false: "",
|
|
38
|
+
},
|
|
39
|
+
},
|
|
40
|
+
defaultVariants: {
|
|
41
|
+
variant: "default",
|
|
42
|
+
spacing: "default",
|
|
43
|
+
stacked: false,
|
|
44
|
+
dense: false,
|
|
45
|
+
},
|
|
46
|
+
}
|
|
47
|
+
)
|
|
48
|
+
|
|
49
|
+
function Frame({
|
|
50
|
+
className,
|
|
51
|
+
variant,
|
|
52
|
+
spacing,
|
|
53
|
+
stacked,
|
|
54
|
+
dense,
|
|
55
|
+
...props
|
|
56
|
+
}: React.ComponentProps<"div"> & VariantProps<typeof frameVariants>) {
|
|
57
|
+
return (
|
|
58
|
+
<div
|
|
59
|
+
className={cn(
|
|
60
|
+
frameVariants({ variant, spacing, stacked, dense }),
|
|
61
|
+
className
|
|
62
|
+
)}
|
|
63
|
+
data-slot="frame"
|
|
64
|
+
data-spacing={spacing}
|
|
65
|
+
{...props}
|
|
66
|
+
/>
|
|
67
|
+
)
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
function FramePanel({ className, ...props }: React.ComponentProps<"div">) {
|
|
71
|
+
return (
|
|
72
|
+
<div
|
|
73
|
+
className={cn(
|
|
74
|
+
"bg-background relative rounded-(--frame-radius) border bg-clip-padding shadow-xs before:pointer-events-none before:absolute before:inset-0 before:rounded-[calc(var(--frame-radius)-1px)] before:shadow-black/5 dark:bg-clip-border dark:before:shadow-white/5",
|
|
75
|
+
className
|
|
76
|
+
)}
|
|
77
|
+
data-slot="frame-panel"
|
|
78
|
+
{...props}
|
|
79
|
+
/>
|
|
80
|
+
)
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
function FrameHeader({ className, ...props }: React.ComponentProps<"header">) {
|
|
84
|
+
return (
|
|
85
|
+
<header
|
|
86
|
+
className={cn("flex flex-col", className)}
|
|
87
|
+
data-slot="frame-panel-header"
|
|
88
|
+
{...props}
|
|
89
|
+
/>
|
|
90
|
+
)
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
function FrameTitle({ className, ...props }: React.ComponentProps<"div">) {
|
|
94
|
+
return (
|
|
95
|
+
<div
|
|
96
|
+
className={cn("text-sm font-semibold", className)}
|
|
97
|
+
data-slot="frame-panel-title"
|
|
98
|
+
{...props}
|
|
99
|
+
/>
|
|
100
|
+
)
|
|
101
|
+
}
|
|
102
|
+
|
|
103
|
+
function FrameDescription({
|
|
104
|
+
className,
|
|
105
|
+
...props
|
|
106
|
+
}: React.ComponentProps<"div">) {
|
|
107
|
+
return (
|
|
108
|
+
<div
|
|
109
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
110
|
+
data-slot="frame-panel-description"
|
|
111
|
+
{...props}
|
|
112
|
+
/>
|
|
113
|
+
)
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
function FrameFooter({ className, ...props }: React.ComponentProps<"footer">) {
|
|
117
|
+
return (
|
|
118
|
+
<footer
|
|
119
|
+
className={cn("flex flex-col gap-1", className)}
|
|
120
|
+
data-slot="frame-panel-footer"
|
|
121
|
+
{...props}
|
|
122
|
+
/>
|
|
123
|
+
)
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export {
|
|
127
|
+
Frame,
|
|
128
|
+
FramePanel,
|
|
129
|
+
FrameHeader,
|
|
130
|
+
FrameTitle,
|
|
131
|
+
FrameDescription,
|
|
132
|
+
FrameFooter,
|
|
133
|
+
frameVariants,
|
|
134
|
+
}
|
|
@@ -0,0 +1,17 @@
|
|
|
1
|
+
// ReUI Components
|
|
2
|
+
export * from "./alert";
|
|
3
|
+
export * from "./autocomplete";
|
|
4
|
+
export * from "./badge";
|
|
5
|
+
export * from "./date-selector";
|
|
6
|
+
export * from "./filters";
|
|
7
|
+
export * from "./frame";
|
|
8
|
+
|
|
9
|
+
// Data Grid (has subdirectory)
|
|
10
|
+
export * from "./data-grid/data-grid";
|
|
11
|
+
export * from "./data-grid/data-grid-column-filter";
|
|
12
|
+
export * from "./data-grid/data-grid-column-header";
|
|
13
|
+
export * from "./data-grid/data-grid-column-visibility";
|
|
14
|
+
export * from "./data-grid/data-grid-pagination";
|
|
15
|
+
export * from "./data-grid/data-grid-table";
|
|
16
|
+
export * from "./data-grid/data-grid-table-dnd";
|
|
17
|
+
export * from "./data-grid/data-grid-table-dnd-rows";
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
import {
|
|
2
|
+
createContext,
|
|
3
|
+
HTMLAttributes,
|
|
4
|
+
useCallback,
|
|
5
|
+
useContext,
|
|
6
|
+
useState,
|
|
7
|
+
} from "react"
|
|
8
|
+
import { Slot } from "radix-ui"
|
|
9
|
+
|
|
10
|
+
import { cn } from "@/lib/utils"
|
|
11
|
+
|
|
12
|
+
// Types
|
|
13
|
+
type TimelineContextValue = {
|
|
14
|
+
activeStep: number
|
|
15
|
+
setActiveStep: (step: number) => void
|
|
16
|
+
}
|
|
17
|
+
|
|
18
|
+
// Context
|
|
19
|
+
const TimelineContext = createContext<TimelineContextValue | undefined>(
|
|
20
|
+
undefined
|
|
21
|
+
)
|
|
22
|
+
|
|
23
|
+
const useTimeline = () => {
|
|
24
|
+
const context = useContext(TimelineContext)
|
|
25
|
+
if (!context) {
|
|
26
|
+
throw new Error("useTimeline must be used within a Timeline")
|
|
27
|
+
}
|
|
28
|
+
return context
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
// Components
|
|
32
|
+
interface TimelineProps extends HTMLAttributes<HTMLDivElement> {
|
|
33
|
+
defaultValue?: number
|
|
34
|
+
value?: number
|
|
35
|
+
onValueChange?: (value: number) => void
|
|
36
|
+
orientation?: "horizontal" | "vertical"
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
function Timeline({
|
|
40
|
+
defaultValue = 1,
|
|
41
|
+
value,
|
|
42
|
+
onValueChange,
|
|
43
|
+
orientation = "vertical",
|
|
44
|
+
className,
|
|
45
|
+
children,
|
|
46
|
+
...props
|
|
47
|
+
}: TimelineProps) {
|
|
48
|
+
const [activeStep, setInternalStep] = useState(defaultValue)
|
|
49
|
+
|
|
50
|
+
const setActiveStep = useCallback(
|
|
51
|
+
(step: number) => {
|
|
52
|
+
if (value === undefined) {
|
|
53
|
+
setInternalStep(step)
|
|
54
|
+
}
|
|
55
|
+
onValueChange?.(step)
|
|
56
|
+
},
|
|
57
|
+
[value, onValueChange]
|
|
58
|
+
)
|
|
59
|
+
|
|
60
|
+
const currentStep = value ?? activeStep
|
|
61
|
+
|
|
62
|
+
return (
|
|
63
|
+
<TimelineContext.Provider
|
|
64
|
+
value={{ activeStep: currentStep, setActiveStep }}
|
|
65
|
+
>
|
|
66
|
+
<div
|
|
67
|
+
className={cn(
|
|
68
|
+
"group/timeline flex data-[orientation=horizontal]:w-full data-[orientation=horizontal]:flex-row data-[orientation=vertical]:flex-col",
|
|
69
|
+
className
|
|
70
|
+
)}
|
|
71
|
+
data-orientation={orientation}
|
|
72
|
+
data-slot="timeline"
|
|
73
|
+
{...props}
|
|
74
|
+
>
|
|
75
|
+
{children}
|
|
76
|
+
</div>
|
|
77
|
+
</TimelineContext.Provider>
|
|
78
|
+
)
|
|
79
|
+
}
|
|
80
|
+
|
|
81
|
+
// TimelineContent
|
|
82
|
+
function TimelineContent({
|
|
83
|
+
className,
|
|
84
|
+
...props
|
|
85
|
+
}: HTMLAttributes<HTMLDivElement>) {
|
|
86
|
+
return (
|
|
87
|
+
<div
|
|
88
|
+
className={cn("text-muted-foreground text-sm", className)}
|
|
89
|
+
data-slot="timeline-content"
|
|
90
|
+
{...props}
|
|
91
|
+
/>
|
|
92
|
+
)
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
// TimelineDate
|
|
96
|
+
interface TimelineDateProps extends HTMLAttributes<HTMLTimeElement> {
|
|
97
|
+
asChild?: boolean
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
function TimelineDate({
|
|
101
|
+
asChild = false,
|
|
102
|
+
className,
|
|
103
|
+
...props
|
|
104
|
+
}: TimelineDateProps) {
|
|
105
|
+
const Comp = asChild ? Slot.Root : "time"
|
|
106
|
+
|
|
107
|
+
return (
|
|
108
|
+
<Comp
|
|
109
|
+
className={cn(
|
|
110
|
+
"text-muted-foreground mb-1 block text-xs font-medium group-data-[orientation=vertical]/timeline:max-sm:h-4",
|
|
111
|
+
className
|
|
112
|
+
)}
|
|
113
|
+
data-slot="timeline-date"
|
|
114
|
+
{...props}
|
|
115
|
+
/>
|
|
116
|
+
)
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
// TimelineHeader
|
|
120
|
+
function TimelineHeader({
|
|
121
|
+
className,
|
|
122
|
+
...props
|
|
123
|
+
}: HTMLAttributes<HTMLDivElement>) {
|
|
124
|
+
return (
|
|
125
|
+
<div className={cn(className)} data-slot="timeline-header" {...props} />
|
|
126
|
+
)
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
// TimelineIndicator
|
|
130
|
+
interface TimelineIndicatorProps extends HTMLAttributes<HTMLDivElement> {
|
|
131
|
+
asChild?: boolean
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
function TimelineIndicator({
|
|
135
|
+
asChild = false,
|
|
136
|
+
className,
|
|
137
|
+
children,
|
|
138
|
+
...props
|
|
139
|
+
}: TimelineIndicatorProps) {
|
|
140
|
+
const Comp = asChild ? Slot.Root : "div"
|
|
141
|
+
|
|
142
|
+
return (
|
|
143
|
+
<Comp
|
|
144
|
+
aria-hidden="true"
|
|
145
|
+
className={cn(
|
|
146
|
+
"border-primary/20 group-data-completed/timeline-item:border-primary absolute size-4 rounded-full border-2 group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:left-0 group-data-[orientation=horizontal]/timeline:-translate-y-1/2 group-data-[orientation=vertical]/timeline:top-0 group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:-translate-x-1/2",
|
|
147
|
+
className
|
|
148
|
+
)}
|
|
149
|
+
data-slot="timeline-indicator"
|
|
150
|
+
{...props}
|
|
151
|
+
>
|
|
152
|
+
{children}
|
|
153
|
+
</Comp>
|
|
154
|
+
)
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
// TimelineItem
|
|
158
|
+
interface TimelineItemProps extends HTMLAttributes<HTMLDivElement> {
|
|
159
|
+
step: number
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function TimelineItem({ step, className, ...props }: TimelineItemProps) {
|
|
163
|
+
const { activeStep } = useTimeline()
|
|
164
|
+
|
|
165
|
+
return (
|
|
166
|
+
<div
|
|
167
|
+
className={cn(
|
|
168
|
+
"group/timeline-item has-[+[data-completed]]:**:data-[slot=timeline-separator]:bg-primary relative flex flex-1 flex-col gap-0.5 group-data-[orientation=horizontal]/timeline:mt-8 group-data-[orientation=horizontal]/timeline:not-last:pe-8 group-data-[orientation=vertical]/timeline:ms-8 group-data-[orientation=vertical]/timeline:not-last:pb-6",
|
|
169
|
+
className
|
|
170
|
+
)}
|
|
171
|
+
data-completed={step <= activeStep || undefined}
|
|
172
|
+
data-slot="timeline-item"
|
|
173
|
+
{...props}
|
|
174
|
+
/>
|
|
175
|
+
)
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
// TimelineSeparator
|
|
179
|
+
function TimelineSeparator({
|
|
180
|
+
className,
|
|
181
|
+
...props
|
|
182
|
+
}: HTMLAttributes<HTMLDivElement>) {
|
|
183
|
+
return (
|
|
184
|
+
<div
|
|
185
|
+
aria-hidden="true"
|
|
186
|
+
className={cn(
|
|
187
|
+
"bg-primary/10 absolute self-start group-last/timeline-item:hidden group-data-[orientation=horizontal]/timeline:-top-6 group-data-[orientation=horizontal]/timeline:h-0.5 group-data-[orientation=horizontal]/timeline:w-[calc(100%-1rem-0.25rem)] group-data-[orientation=horizontal]/timeline:translate-x-4.5 group-data-[orientation=horizontal]/timeline:-translate-y-1/2 group-data-[orientation=vertical]/timeline:-left-6 group-data-[orientation=vertical]/timeline:h-[calc(100%-1rem-0.25rem)] group-data-[orientation=vertical]/timeline:w-0.5 group-data-[orientation=vertical]/timeline:-translate-x-1/2 group-data-[orientation=vertical]/timeline:translate-y-4.5",
|
|
188
|
+
className
|
|
189
|
+
)}
|
|
190
|
+
data-slot="timeline-separator"
|
|
191
|
+
{...props}
|
|
192
|
+
/>
|
|
193
|
+
)
|
|
194
|
+
}
|
|
195
|
+
|
|
196
|
+
// TimelineTitle
|
|
197
|
+
function TimelineTitle({
|
|
198
|
+
className,
|
|
199
|
+
...props
|
|
200
|
+
}: HTMLAttributes<HTMLHeadingElement>) {
|
|
201
|
+
return (
|
|
202
|
+
<h3
|
|
203
|
+
className={cn("text-sm font-medium", className)}
|
|
204
|
+
data-slot="timeline-title"
|
|
205
|
+
{...props}
|
|
206
|
+
/>
|
|
207
|
+
)
|
|
208
|
+
}
|
|
209
|
+
|
|
210
|
+
export {
|
|
211
|
+
Timeline,
|
|
212
|
+
TimelineContent,
|
|
213
|
+
TimelineDate,
|
|
214
|
+
TimelineHeader,
|
|
215
|
+
TimelineIndicator,
|
|
216
|
+
TimelineItem,
|
|
217
|
+
TimelineSeparator,
|
|
218
|
+
TimelineTitle,
|
|
219
|
+
}
|
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
"use client";
|
|
2
|
+
|
|
3
|
+
import React, { useState, useEffect } from "react";
|
|
4
|
+
|
|
5
|
+
// Types for the autocomplete components
|
|
6
|
+
export interface AutocompleteContentProps {
|
|
7
|
+
className?: string;
|
|
8
|
+
children?: React.ReactNode;
|
|
9
|
+
align?: "start" | "center" | "end";
|
|
10
|
+
sideOffset?: number;
|
|
11
|
+
alignOffset?: number;
|
|
12
|
+
side?: "top" | "bottom" | "left" | "right";
|
|
13
|
+
anchor?: React.RefObject<HTMLElement | null> | HTMLElement | null;
|
|
14
|
+
showBackdrop?: boolean;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
// Re-export everything from AutocompleteClient but wrap with client-only check
|
|
18
|
+
// This file handles the SSR boundary
|
|
19
|
+
|
|
20
|
+
let clientModule: typeof import("./AutocompleteClient") | null = null;
|
|
21
|
+
let clientModulePromise: Promise<typeof import("./AutocompleteClient")> | null = null;
|
|
22
|
+
|
|
23
|
+
function getClientModule() {
|
|
24
|
+
if (typeof window === "undefined") {
|
|
25
|
+
return null;
|
|
26
|
+
}
|
|
27
|
+
if (clientModule) {
|
|
28
|
+
return clientModule;
|
|
29
|
+
}
|
|
30
|
+
if (!clientModulePromise) {
|
|
31
|
+
clientModulePromise = import("./AutocompleteClient").then((mod) => {
|
|
32
|
+
clientModule = mod;
|
|
33
|
+
return mod;
|
|
34
|
+
}).catch((err) => {
|
|
35
|
+
console.error("[Autocomplete] Failed to load AutocompleteClient:", err);
|
|
36
|
+
// Reset promise so it can be retried
|
|
37
|
+
clientModulePromise = null;
|
|
38
|
+
throw err;
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
return null;
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
// Hook to use the client module with re-render on load
|
|
45
|
+
function useClientModule() {
|
|
46
|
+
const [mod, setMod] = useState(() => getClientModule());
|
|
47
|
+
const [error, setError] = useState<Error | null>(null);
|
|
48
|
+
|
|
49
|
+
useEffect(() => {
|
|
50
|
+
if (mod || error) return;
|
|
51
|
+
|
|
52
|
+
if (clientModulePromise) {
|
|
53
|
+
clientModulePromise
|
|
54
|
+
.then((loaded) => {
|
|
55
|
+
setMod(loaded);
|
|
56
|
+
})
|
|
57
|
+
.catch((err) => {
|
|
58
|
+
setError(err);
|
|
59
|
+
});
|
|
60
|
+
}
|
|
61
|
+
}, [mod, error]);
|
|
62
|
+
|
|
63
|
+
return mod;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// SSR fallback input
|
|
67
|
+
// suppressHydrationWarning is needed because browser extensions (Dashlane, 1Password, etc.)
|
|
68
|
+
// inject data attributes before React hydrates, causing hydration mismatches
|
|
69
|
+
function FallbackInput() {
|
|
70
|
+
return (
|
|
71
|
+
<div className="app-shell-search-input-container" suppressHydrationWarning>
|
|
72
|
+
<input
|
|
73
|
+
type="text"
|
|
74
|
+
className="app-shell-search-input"
|
|
75
|
+
placeholder="Search"
|
|
76
|
+
disabled
|
|
77
|
+
suppressHydrationWarning
|
|
78
|
+
/>
|
|
79
|
+
</div>
|
|
80
|
+
);
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
// Main Autocomplete wrapper
|
|
84
|
+
function Autocomplete(props: Record<string, unknown>) {
|
|
85
|
+
const mod = useClientModule();
|
|
86
|
+
if (!mod) return <FallbackInput />;
|
|
87
|
+
return <mod.AutocompleteRoot {...props} />;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
function AutocompleteValue(props: Record<string, unknown>) {
|
|
91
|
+
const mod = useClientModule();
|
|
92
|
+
if (!mod) return null;
|
|
93
|
+
return <mod.AutocompleteValue {...props} />;
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
function AutocompleteInput(props: Record<string, unknown>) {
|
|
97
|
+
const mod = useClientModule();
|
|
98
|
+
if (!mod) return null;
|
|
99
|
+
return <mod.AutocompleteInput {...props} />;
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
function AutocompletePortal(props: Record<string, unknown>) {
|
|
103
|
+
const mod = useClientModule();
|
|
104
|
+
if (!mod) return null;
|
|
105
|
+
return <mod.AutocompletePortal {...props} />;
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
function AutocompleteBackdrop(props: Record<string, unknown>) {
|
|
109
|
+
const mod = useClientModule();
|
|
110
|
+
if (!mod) return null;
|
|
111
|
+
return <mod.AutocompleteBackdrop {...props} />;
|
|
112
|
+
}
|
|
113
|
+
|
|
114
|
+
function AutocompletePositioner(props: Record<string, unknown>) {
|
|
115
|
+
const mod = useClientModule();
|
|
116
|
+
if (!mod) return null;
|
|
117
|
+
return <mod.AutocompletePositioner {...props} />;
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
function AutocompleteContent(props: AutocompleteContentProps) {
|
|
121
|
+
const mod = useClientModule();
|
|
122
|
+
if (!mod) return null;
|
|
123
|
+
return <mod.AutocompleteContent {...props} />;
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
function AutocompleteList(props: Record<string, unknown>) {
|
|
127
|
+
const mod = useClientModule();
|
|
128
|
+
if (!mod) return null;
|
|
129
|
+
return <mod.AutocompleteList {...props} />;
|
|
130
|
+
}
|
|
131
|
+
|
|
132
|
+
function AutocompleteItem(props: Record<string, unknown>) {
|
|
133
|
+
const mod = useClientModule();
|
|
134
|
+
if (!mod) return null;
|
|
135
|
+
return <mod.AutocompleteItem {...props} />;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function AutocompleteGroup(props: Record<string, unknown>) {
|
|
139
|
+
const mod = useClientModule();
|
|
140
|
+
if (!mod) return null;
|
|
141
|
+
return <mod.AutocompleteGroup {...props} />;
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
function AutocompleteGroupLabel(props: Record<string, unknown>) {
|
|
145
|
+
const mod = useClientModule();
|
|
146
|
+
if (!mod) return null;
|
|
147
|
+
return <mod.AutocompleteGroupLabel {...props} />;
|
|
148
|
+
}
|
|
149
|
+
|
|
150
|
+
function AutocompleteEmpty(props: Record<string, unknown>) {
|
|
151
|
+
const mod = useClientModule();
|
|
152
|
+
if (!mod) return null;
|
|
153
|
+
return <mod.AutocompleteEmpty {...props} />;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
function AutocompleteClear(props: Record<string, unknown>) {
|
|
157
|
+
const mod = useClientModule();
|
|
158
|
+
if (!mod) return null;
|
|
159
|
+
return <mod.AutocompleteClear {...props} />;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
function AutocompleteTrigger(props: Record<string, unknown>) {
|
|
163
|
+
const mod = useClientModule();
|
|
164
|
+
if (!mod) return null;
|
|
165
|
+
return <mod.AutocompleteTrigger {...props} />;
|
|
166
|
+
}
|
|
167
|
+
|
|
168
|
+
export {
|
|
169
|
+
Autocomplete,
|
|
170
|
+
AutocompleteValue,
|
|
171
|
+
AutocompleteInput,
|
|
172
|
+
AutocompletePortal,
|
|
173
|
+
AutocompleteBackdrop,
|
|
174
|
+
AutocompletePositioner,
|
|
175
|
+
AutocompleteContent,
|
|
176
|
+
AutocompleteList,
|
|
177
|
+
AutocompleteItem,
|
|
178
|
+
AutocompleteGroup,
|
|
179
|
+
AutocompleteGroupLabel,
|
|
180
|
+
AutocompleteEmpty,
|
|
181
|
+
AutocompleteClear,
|
|
182
|
+
AutocompleteTrigger,
|
|
183
|
+
};
|