@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.
Files changed (142) hide show
  1. package/.claude/ralph-loop.local.md +9 -0
  2. package/README.md +172 -0
  3. package/bin/init.js +269 -0
  4. package/bun.lock +401 -0
  5. package/components.json +28 -0
  6. package/package.json +74 -0
  7. package/scripts/publish-npm.sh +202 -0
  8. package/src/AppShell.tsx +847 -0
  9. package/src/components/PageHeader.tsx +160 -0
  10. package/src/components/data-table/README.md +447 -0
  11. package/src/components/data-table/data-table-preferences.tsx +184 -0
  12. package/src/components/data-table/data-table-toolbar.tsx +118 -0
  13. package/src/components/data-table/data-table.tsx +37 -0
  14. package/src/components/data-table/index.ts +32 -0
  15. package/src/components/global-header/AllServicesButton.tsx +127 -0
  16. package/src/components/global-header/CategoriesButton.tsx +120 -0
  17. package/src/components/global-header/GlobalHeader.tsx +59 -0
  18. package/src/components/global-header/GlobalHeaderSearch.tsx +57 -0
  19. package/src/components/global-header/HeaderUtilities.tsx +243 -0
  20. package/src/components/global-header/ServicesMenu.tsx +246 -0
  21. package/src/components/layout/AppBreadcrumb.tsx +70 -0
  22. package/src/components/layout/AppFlashbar.tsx +95 -0
  23. package/src/components/layout/AppLayout.tsx +271 -0
  24. package/src/components/layout/AppNavigation.tsx +313 -0
  25. package/src/components/layout/AppSidebar.tsx +229 -0
  26. package/src/components/patterns/index.ts +14 -0
  27. package/src/components/patterns/p-alert-5.tsx +19 -0
  28. package/src/components/patterns/p-autocomplete-5.tsx +89 -0
  29. package/src/components/patterns/p-breadcrumb-1.tsx +28 -0
  30. package/src/components/patterns/p-button-42.tsx +37 -0
  31. package/src/components/patterns/p-button-51.tsx +14 -0
  32. package/src/components/patterns/p-button-6.tsx +5 -0
  33. package/src/components/patterns/p-calendar-1.tsx +18 -0
  34. package/src/components/patterns/p-card-1.tsx +33 -0
  35. package/src/components/patterns/p-card-2.tsx +26 -0
  36. package/src/components/patterns/p-card-5.tsx +31 -0
  37. package/src/components/patterns/p-collapsible-7.tsx +121 -0
  38. package/src/components/patterns/p-command-6.tsx +113 -0
  39. package/src/components/patterns/p-dialog-1.tsx +56 -0
  40. package/src/components/patterns/p-dropdown-menu-1.tsx +38 -0
  41. package/src/components/patterns/p-dropdown-menu-11.tsx +122 -0
  42. package/src/components/patterns/p-dropdown-menu-14.tsx +165 -0
  43. package/src/components/patterns/p-dropdown-menu-9.tsx +108 -0
  44. package/src/components/patterns/p-empty-2.tsx +34 -0
  45. package/src/components/patterns/p-file-upload-1.tsx +72 -0
  46. package/src/components/patterns/p-filters-1.tsx +666 -0
  47. package/src/components/patterns/p-frame-2.tsx +26 -0
  48. package/src/components/patterns/p-tabs-2.tsx +129 -0
  49. package/src/components/reui/alert.tsx +92 -0
  50. package/src/components/reui/autocomplete.tsx +343 -0
  51. package/src/components/reui/badge.tsx +87 -0
  52. package/src/components/reui/data-grid/data-grid-column-filter.tsx +165 -0
  53. package/src/components/reui/data-grid/data-grid-column-header.tsx +339 -0
  54. package/src/components/reui/data-grid/data-grid-column-visibility.tsx +55 -0
  55. package/src/components/reui/data-grid/data-grid-pagination.tsx +224 -0
  56. package/src/components/reui/data-grid/data-grid-table-dnd-rows.tsx +260 -0
  57. package/src/components/reui/data-grid/data-grid-table-dnd.tsx +253 -0
  58. package/src/components/reui/data-grid/data-grid-table.tsx +639 -0
  59. package/src/components/reui/data-grid/data-grid.tsx +209 -0
  60. package/src/components/reui/date-selector.tsx +1330 -0
  61. package/src/components/reui/filters.tsx +1869 -0
  62. package/src/components/reui/frame.tsx +134 -0
  63. package/src/components/reui/index.ts +17 -0
  64. package/src/components/reui/timeline.tsx +219 -0
  65. package/src/components/search/Autocomplete.tsx +183 -0
  66. package/src/components/search/AutocompleteClient.tsx +293 -0
  67. package/src/components/search/GlobalSearch.tsx +187 -0
  68. package/src/components/section-drawer/deal-drawer-content.tsx +891 -0
  69. package/src/components/section-drawer/index.ts +19 -0
  70. package/src/components/section-drawer/section-drawer.css +665 -0
  71. package/src/components/section-drawer/section-drawer.tsx +467 -0
  72. package/src/components/sectioned-list-board/README.md +78 -0
  73. package/src/components/sectioned-list-board/board-card-content.tsx +340 -0
  74. package/src/components/sectioned-list-board/date-range-filter.tsx +249 -0
  75. package/src/components/sectioned-list-board/index.ts +19 -0
  76. package/src/components/sectioned-list-board/sectioned-list-board.css +564 -0
  77. package/src/components/sectioned-list-board/sectioned-list-board.tsx +731 -0
  78. package/src/components/sectioned-list-board/sortable-card.tsx +314 -0
  79. package/src/components/sectioned-list-board/sortable-section.tsx +319 -0
  80. package/src/components/sectioned-list-board/types.ts +216 -0
  81. package/src/components/sectioned-list-table/README.md +80 -0
  82. package/src/components/sectioned-list-table/index.ts +14 -0
  83. package/src/components/sectioned-list-table/sectioned-list-table.css +534 -0
  84. package/src/components/sectioned-list-table/sectioned-list-table.tsx +740 -0
  85. package/src/components/sectioned-list-table/sortable-column-header.tsx +120 -0
  86. package/src/components/sectioned-list-table/sortable-row.tsx +420 -0
  87. package/src/components/sectioned-list-table/sortable-section.tsx +251 -0
  88. package/src/components/sectioned-list-table/table-cell-content.tsx +129 -0
  89. package/src/components/sectioned-list-table/types.ts +120 -0
  90. package/src/components/sectioned-list-table/use-column-preferences.ts +103 -0
  91. package/src/components/ui/actions-dropdown.tsx +109 -0
  92. package/src/components/ui/assignee-selector.tsx +209 -0
  93. package/src/components/ui/avatar.tsx +107 -0
  94. package/src/components/ui/breadcrumb.tsx +109 -0
  95. package/src/components/ui/button-group.tsx +83 -0
  96. package/src/components/ui/button.tsx +64 -0
  97. package/src/components/ui/calendar.tsx +220 -0
  98. package/src/components/ui/card.tsx +92 -0
  99. package/src/components/ui/chart.tsx +376 -0
  100. package/src/components/ui/checkbox.tsx +30 -0
  101. package/src/components/ui/collapsible.tsx +33 -0
  102. package/src/components/ui/command.tsx +182 -0
  103. package/src/components/ui/context-menu.tsx +250 -0
  104. package/src/components/ui/create-button-group.tsx +128 -0
  105. package/src/components/ui/dialog.tsx +156 -0
  106. package/src/components/ui/drawer.tsx +133 -0
  107. package/src/components/ui/dropdown-menu.tsx +255 -0
  108. package/src/components/ui/empty.tsx +104 -0
  109. package/src/components/ui/field.tsx +248 -0
  110. package/src/components/ui/form.tsx +165 -0
  111. package/src/components/ui/index.ts +37 -0
  112. package/src/components/ui/input-group.tsx +168 -0
  113. package/src/components/ui/input.tsx +21 -0
  114. package/src/components/ui/kbd.tsx +28 -0
  115. package/src/components/ui/label.tsx +22 -0
  116. package/src/components/ui/navigation-menu.tsx +168 -0
  117. package/src/components/ui/page-header.tsx +80 -0
  118. package/src/components/ui/popover.tsx +87 -0
  119. package/src/components/ui/scroll-area.tsx +56 -0
  120. package/src/components/ui/select.tsx +190 -0
  121. package/src/components/ui/separator.tsx +26 -0
  122. package/src/components/ui/sheet.tsx +141 -0
  123. package/src/components/ui/sidebar.tsx +726 -0
  124. package/src/components/ui/skeleton.tsx +13 -0
  125. package/src/components/ui/sonner.tsx +38 -0
  126. package/src/components/ui/switch.tsx +33 -0
  127. package/src/components/ui/tabs.tsx +91 -0
  128. package/src/components/ui/textarea.tsx +18 -0
  129. package/src/components/ui/toggle-group.tsx +83 -0
  130. package/src/components/ui/toggle.tsx +45 -0
  131. package/src/components/ui/tooltip.tsx +57 -0
  132. package/src/hooks/use-copy-to-clipboard.ts +37 -0
  133. package/src/hooks/use-file-upload.ts +415 -0
  134. package/src/hooks/use-mobile.ts +19 -0
  135. package/src/index.ts +95 -0
  136. package/src/lib/utils.ts +6 -0
  137. package/src/styles.css +1859 -0
  138. package/src/urls.ts +83 -0
  139. package/src/vite.d.ts +22 -0
  140. package/src/vite.js +241 -0
  141. package/tsconfig.base.json +18 -0
  142. 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
+ };