create-freakpunch 0.0.0-stage → 0.2.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 (216) hide show
  1. package/README.md +24 -3
  2. package/bin/create-freakpunch.mjs +227 -0
  3. package/files/README.md +19 -0
  4. package/files/app.config.ts +50 -0
  5. package/files/locales.ts +10 -0
  6. package/files/registry.ts +17 -0
  7. package/package.json +23 -6
  8. package/template/.dockerignore +17 -0
  9. package/template/.oxlintrc.json +9 -0
  10. package/template/AGENTS.md +6 -0
  11. package/template/BACKEND.md +108 -0
  12. package/template/CLAUDE.md +297 -0
  13. package/template/Dockerfile +19 -0
  14. package/template/_gitignore +26 -0
  15. package/template/components.json +27 -0
  16. package/template/design/COMPONENTS.md +145 -0
  17. package/template/design/DESIGN.md +212 -0
  18. package/template/design/PATTERNS.md +110 -0
  19. package/template/index.html +25 -0
  20. package/template/nginx.conf +32 -0
  21. package/template/package-lock.json +6484 -0
  22. package/template/package.json +51 -0
  23. package/template/public/favicon.svg +1 -0
  24. package/template/public/robots.txt +2 -0
  25. package/template/src/app.config.ts +50 -0
  26. package/template/src/app.roles.ts +24 -0
  27. package/template/src/components/ui/alert-dialog.tsx +181 -0
  28. package/template/src/components/ui/avatar.tsx +116 -0
  29. package/template/src/components/ui/badge.tsx +57 -0
  30. package/template/src/components/ui/breadcrumb.tsx +130 -0
  31. package/template/src/components/ui/button.tsx +58 -0
  32. package/template/src/components/ui/calendar.tsx +277 -0
  33. package/template/src/components/ui/card.tsx +99 -0
  34. package/template/src/components/ui/chart.tsx +370 -0
  35. package/template/src/components/ui/checkbox.tsx +28 -0
  36. package/template/src/components/ui/combobox.tsx +329 -0
  37. package/template/src/components/ui/command.tsx +191 -0
  38. package/template/src/components/ui/dialog.tsx +162 -0
  39. package/template/src/components/ui/dropdown-menu.tsx +271 -0
  40. package/template/src/components/ui/empty.tsx +103 -0
  41. package/template/src/components/ui/field.tsx +236 -0
  42. package/template/src/components/ui/input-group.tsx +263 -0
  43. package/template/src/components/ui/input.tsx +45 -0
  44. package/template/src/components/ui/kbd.tsx +31 -0
  45. package/template/src/components/ui/label.tsx +17 -0
  46. package/template/src/components/ui/pagination.tsx +134 -0
  47. package/template/src/components/ui/popover.tsx +87 -0
  48. package/template/src/components/ui/radio-group.tsx +35 -0
  49. package/template/src/components/ui/select.tsx +204 -0
  50. package/template/src/components/ui/separator.tsx +25 -0
  51. package/template/src/components/ui/sheet.tsx +139 -0
  52. package/template/src/components/ui/sidebar.tsx +741 -0
  53. package/template/src/components/ui/skeleton.tsx +13 -0
  54. package/template/src/components/ui/sonner.tsx +78 -0
  55. package/template/src/components/ui/spinner.tsx +42 -0
  56. package/template/src/components/ui/switch.tsx +29 -0
  57. package/template/src/components/ui/table.tsx +121 -0
  58. package/template/src/components/ui/tabs.tsx +191 -0
  59. package/template/src/components/ui/textarea.tsx +17 -0
  60. package/template/src/components/ui/toggle-group.tsx +94 -0
  61. package/template/src/components/ui/toggle.tsx +48 -0
  62. package/template/src/components/ui/tooltip.tsx +91 -0
  63. package/template/src/core/api/demo.ts +8 -0
  64. package/template/src/core/api/http.ts +76 -0
  65. package/template/src/core/api/mock.ts +88 -0
  66. package/template/src/core/api/rest.ts +67 -0
  67. package/template/src/core/api/types.ts +21 -0
  68. package/template/src/core/audit/audit.service.ts +94 -0
  69. package/template/src/core/audit/hooks.ts +19 -0
  70. package/template/src/core/auth/auth-store.ts +52 -0
  71. package/template/src/core/auth/auth.service.ts +86 -0
  72. package/template/src/core/auth/can.tsx +39 -0
  73. package/template/src/core/auth/dev-role-switcher.tsx +64 -0
  74. package/template/src/core/auth/local-credentials.ts +46 -0
  75. package/template/src/core/auth/permissions.ts +115 -0
  76. package/template/src/core/auth/require-auth.tsx +16 -0
  77. package/template/src/core/auth/require-permission.tsx +24 -0
  78. package/template/src/core/auth/roles.ts +9 -0
  79. package/template/src/core/auth/types.ts +20 -0
  80. package/template/src/core/auth/use-auth.ts +19 -0
  81. package/template/src/core/company/company.service.ts +94 -0
  82. package/template/src/core/company/hooks.ts +40 -0
  83. package/template/src/core/company/password-policy.ts +40 -0
  84. package/template/src/core/company/types.ts +46 -0
  85. package/template/src/core/company/use-app-name.ts +7 -0
  86. package/template/src/core/components/button-link.tsx +14 -0
  87. package/template/src/core/components/confirm-dialog.tsx +116 -0
  88. package/template/src/core/components/data-table/data-table.tsx +297 -0
  89. package/template/src/core/components/data-table/features.ts +24 -0
  90. package/template/src/core/components/data-table/table-header-cell.tsx +13 -0
  91. package/template/src/core/components/data-table/toolbar.tsx +100 -0
  92. package/template/src/core/components/data-table/use-list-url-state.ts +108 -0
  93. package/template/src/core/components/date-picker.tsx +71 -0
  94. package/template/src/core/components/file-attachments.tsx +84 -0
  95. package/template/src/core/components/form-error.tsx +9 -0
  96. package/template/src/core/components/form-page.tsx +93 -0
  97. package/template/src/core/components/image-upload.tsx +86 -0
  98. package/template/src/core/components/page.tsx +75 -0
  99. package/template/src/core/components/phone-input.tsx +58 -0
  100. package/template/src/core/components/search-select.tsx +57 -0
  101. package/template/src/core/components/segmented.tsx +41 -0
  102. package/template/src/core/components/states.tsx +97 -0
  103. package/template/src/core/components/status-badge.tsx +26 -0
  104. package/template/src/core/components/user-avatar.tsx +23 -0
  105. package/template/src/core/config/runtime.ts +16 -0
  106. package/template/src/core/config/types.ts +37 -0
  107. package/template/src/core/i18n/i18n.ts +69 -0
  108. package/template/src/core/i18n/i18next.d.ts +12 -0
  109. package/template/src/core/i18n/keys.ts +37 -0
  110. package/template/src/core/i18n/language-switcher.tsx +45 -0
  111. package/template/src/core/i18n/types.ts +17 -0
  112. package/template/src/core/i18n/use-format.ts +31 -0
  113. package/template/src/core/layout/app-shell.tsx +50 -0
  114. package/template/src/core/layout/app-sidebar.tsx +285 -0
  115. package/template/src/core/layout/command-menu.tsx +117 -0
  116. package/template/src/core/layout/top-bar.tsx +100 -0
  117. package/template/src/core/lib/csv.ts +27 -0
  118. package/template/src/core/lib/format.ts +87 -0
  119. package/template/src/core/lib/phone.ts +50 -0
  120. package/template/src/core/lib/storage.ts +27 -0
  121. package/template/src/core/modules/lazy-page.ts +9 -0
  122. package/template/src/core/modules/navigation.ts +39 -0
  123. package/template/src/core/modules/types.ts +55 -0
  124. package/template/src/core/notifications/hooks.ts +84 -0
  125. package/template/src/core/notifications/notification-center.tsx +251 -0
  126. package/template/src/core/notifications/notifications.service.ts +132 -0
  127. package/template/src/core/notifications/types.ts +70 -0
  128. package/template/src/core/preferences/preferences-store.ts +70 -0
  129. package/template/src/core/setup/setup-gate.tsx +20 -0
  130. package/template/src/core/setup/setup.service.ts +101 -0
  131. package/template/src/core/theme/brand-context.tsx +53 -0
  132. package/template/src/core/theme/brand.ts +29 -0
  133. package/template/src/core/theme/theme-context.tsx +62 -0
  134. package/template/src/core/theme/theme-options.ts +8 -0
  135. package/template/src/core/theme/theme-toggle.tsx +45 -0
  136. package/template/src/core/users/hooks.ts +51 -0
  137. package/template/src/core/users/types.ts +37 -0
  138. package/template/src/core/users/users.service.ts +167 -0
  139. package/template/src/hooks/use-mobile.ts +19 -0
  140. package/template/src/index.css +321 -0
  141. package/template/src/lib/utils.ts +1 -0
  142. package/template/src/locales/en/account.ts +180 -0
  143. package/template/src/locales/en/auth.ts +18 -0
  144. package/template/src/locales/en/common.ts +151 -0
  145. package/template/src/locales/en/company.ts +380 -0
  146. package/template/src/locales/en/dashboard.ts +46 -0
  147. package/template/src/locales/en/errors.ts +18 -0
  148. package/template/src/locales/en/notifications.ts +43 -0
  149. package/template/src/locales/en/settings.ts +25 -0
  150. package/template/src/locales/en/setup.ts +45 -0
  151. package/template/src/locales/index.ts +58 -0
  152. package/template/src/locales/tr/account.ts +184 -0
  153. package/template/src/locales/tr/auth.ts +22 -0
  154. package/template/src/locales/tr/common.ts +154 -0
  155. package/template/src/locales/tr/company.ts +384 -0
  156. package/template/src/locales/tr/dashboard.ts +50 -0
  157. package/template/src/locales/tr/errors.ts +22 -0
  158. package/template/src/locales/tr/notifications.ts +47 -0
  159. package/template/src/locales/tr/settings.ts +29 -0
  160. package/template/src/locales/tr/setup.ts +49 -0
  161. package/template/src/main.tsx +16 -0
  162. package/template/src/modules/locales.ts +10 -0
  163. package/template/src/modules/registry.ts +17 -0
  164. package/template/src/pages/dashboard/dashboard-page.tsx +55 -0
  165. package/template/src/pages/dashboard/quick-actions.tsx +37 -0
  166. package/template/src/pages/login-page.tsx +152 -0
  167. package/template/src/pages/no-access-page.tsx +11 -0
  168. package/template/src/pages/not-found-page.tsx +61 -0
  169. package/template/src/pages/settings/account/account.service.ts +128 -0
  170. package/template/src/pages/settings/account/components/mock-qr.tsx +50 -0
  171. package/template/src/pages/settings/account/components/password-strength.tsx +38 -0
  172. package/template/src/pages/settings/account/components/two-factor-dialog.tsx +140 -0
  173. package/template/src/pages/settings/account/hooks.ts +73 -0
  174. package/template/src/pages/settings/account/notifications-page.tsx +103 -0
  175. package/template/src/pages/settings/account/preferences-page.tsx +157 -0
  176. package/template/src/pages/settings/account/profile-page.tsx +242 -0
  177. package/template/src/pages/settings/account/security-page.tsx +364 -0
  178. package/template/src/pages/settings/account/types.ts +37 -0
  179. package/template/src/pages/settings/company/audit-log-page.tsx +126 -0
  180. package/template/src/pages/settings/company/branding-page.tsx +278 -0
  181. package/template/src/pages/settings/company/components/invite-dialog.tsx +106 -0
  182. package/template/src/pages/settings/company/components/read-only-notice.tsx +13 -0
  183. package/template/src/pages/settings/company/danger-zone-page.tsx +127 -0
  184. package/template/src/pages/settings/company/general-page.tsx +234 -0
  185. package/template/src/pages/settings/company/integrations/api-keys-panel.tsx +170 -0
  186. package/template/src/pages/settings/company/integrations/hooks.ts +37 -0
  187. package/template/src/pages/settings/company/integrations/integrations-page.tsx +57 -0
  188. package/template/src/pages/settings/company/integrations/integrations.service.ts +153 -0
  189. package/template/src/pages/settings/company/integrations/settings-forms.tsx +299 -0
  190. package/template/src/pages/settings/company/integrations/webhooks-panel.tsx +179 -0
  191. package/template/src/pages/settings/company/modules-page.tsx +65 -0
  192. package/template/src/pages/settings/company/roles-page.tsx +116 -0
  193. package/template/src/pages/settings/company/security-policies-page.tsx +164 -0
  194. package/template/src/pages/settings/company/users-page.tsx +262 -0
  195. package/template/src/pages/settings/settings-layout.tsx +57 -0
  196. package/template/src/pages/settings/settings-nav.ts +69 -0
  197. package/template/src/pages/settings/settings-routes.tsx +49 -0
  198. package/template/src/pages/setup-page.tsx +387 -0
  199. package/template/src/providers.tsx +68 -0
  200. package/template/src/router.tsx +59 -0
  201. package/template/tools/check-design.mjs +73 -0
  202. package/template/tools/new-module.mjs +90 -0
  203. package/template/tools/templates/module/api/__id__.mock.ts.tpl +17 -0
  204. package/template/tools/templates/module/api/__id__.service.ts.tpl +51 -0
  205. package/template/tools/templates/module/hooks.ts.tpl +57 -0
  206. package/template/tools/templates/module/index.ts.tpl +44 -0
  207. package/template/tools/templates/module/locales/en.ts.tpl +51 -0
  208. package/template/tools/templates/module/locales/tr.ts.tpl +55 -0
  209. package/template/tools/templates/module/pages/__id__-detail-page.tsx.tpl +103 -0
  210. package/template/tools/templates/module/pages/__id__-form-page.tsx.tpl +121 -0
  211. package/template/tools/templates/module/pages/__id__-list-page.tsx.tpl +234 -0
  212. package/template/tools/templates/module/types.ts.tpl +32 -0
  213. package/template/tsconfig.app.json +32 -0
  214. package/template/tsconfig.json +12 -0
  215. package/template/tsconfig.node.json +23 -0
  216. package/template/vite.config.ts +47 -0
@@ -0,0 +1,329 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Combobox as ComboboxPrimitive } from "@base-ui/react"
5
+ import { cn } from "@/lib/utils"
6
+
7
+ import { Button } from "@/components/ui/button"
8
+ import {
9
+ InputGroup,
10
+ InputGroupAddon,
11
+ InputGroupButton,
12
+ InputGroupInput,
13
+ } from "@/components/ui/input-group"
14
+ import { ChevronDownIcon, XIcon, CheckIcon } from "lucide-react"
15
+
16
+ const Combobox = ComboboxPrimitive.Root
17
+
18
+ function ComboboxValue({ ...props }: ComboboxPrimitive.Value.Props) {
19
+ return <ComboboxPrimitive.Value data-slot="combobox-value" {...props} />
20
+ }
21
+
22
+ function ComboboxTrigger({
23
+ className,
24
+ children,
25
+ ...props
26
+ }: ComboboxPrimitive.Trigger.Props) {
27
+ return (
28
+ <ComboboxPrimitive.Trigger
29
+ data-slot="combobox-trigger"
30
+ aria-label={children == null ? "Show options" : undefined}
31
+ className={cn("[&_svg:not([class*='size-'])]:size-4", className)}
32
+ {...props}
33
+ >
34
+ {children}
35
+ <ChevronDownIcon className="pointer-events-none size-4 text-muted-foreground" />
36
+ </ComboboxPrimitive.Trigger>
37
+ )
38
+ }
39
+
40
+ function ComboboxClear({ className, ...props }: ComboboxPrimitive.Clear.Props) {
41
+ return (
42
+ <ComboboxPrimitive.Clear
43
+ data-slot="combobox-clear"
44
+ aria-label="Clear"
45
+ render={<InputGroupButton variant="ghost" size="icon-xs" />}
46
+ className={className}
47
+ {...props}
48
+ >
49
+ <XIcon className="pointer-events-none" />
50
+ </ComboboxPrimitive.Clear>
51
+ )
52
+ }
53
+
54
+ function ComboboxInput({
55
+ className,
56
+ children,
57
+ disabled = false,
58
+ showTrigger = true,
59
+ showClear = false,
60
+ size,
61
+ ...props
62
+ }: Omit<ComboboxPrimitive.Input.Props, "size"> & {
63
+ showTrigger?: boolean
64
+ showClear?: boolean
65
+ size?: number | "default" | "lg" | "xl"
66
+ }) {
67
+ return (
68
+ <InputGroup
69
+ size={typeof size === "string" ? size : "default"}
70
+ className={cn("w-auto", className)}
71
+ >
72
+ <ComboboxPrimitive.Input
73
+ render={
74
+ <InputGroupInput
75
+ disabled={disabled}
76
+ size={typeof size === "number" ? size : undefined}
77
+ />
78
+ }
79
+ {...props}
80
+ />
81
+ <InputGroupAddon align="inline-end">
82
+ {showTrigger && (
83
+ <InputGroupButton
84
+ size="icon-xs"
85
+ variant="ghost"
86
+ render={<ComboboxTrigger />}
87
+ data-slot="input-group-button"
88
+ className="text-muted-foreground group-has-data-[slot=combobox-clear]/input-group:hidden hover:text-foreground data-pressed:bg-transparent"
89
+ disabled={disabled}
90
+ />
91
+ )}
92
+ {showClear && <ComboboxClear disabled={disabled} />}
93
+ </InputGroupAddon>
94
+ {children}
95
+ </InputGroup>
96
+ )
97
+ }
98
+
99
+ function ComboboxContent({
100
+ className,
101
+ side = "bottom",
102
+ sideOffset = 6,
103
+ align = "start",
104
+ alignOffset = 0,
105
+ anchor,
106
+ ...props
107
+ }: ComboboxPrimitive.Popup.Props &
108
+ Pick<
109
+ ComboboxPrimitive.Positioner.Props,
110
+ "side" | "align" | "sideOffset" | "alignOffset" | "anchor"
111
+ >) {
112
+ return (
113
+ <ComboboxPrimitive.Portal>
114
+ <ComboboxPrimitive.Positioner
115
+ side={side}
116
+ sideOffset={sideOffset}
117
+ align={align}
118
+ alignOffset={alignOffset}
119
+ anchor={anchor}
120
+ className="isolate z-50"
121
+ >
122
+ <ComboboxPrimitive.Popup
123
+ data-slot="combobox-content"
124
+ data-chips={!!anchor}
125
+ className={cn(
126
+ "group/combobox-content relative max-h-(--available-height) w-(--anchor-width) max-w-(--available-width) min-w-[calc(var(--anchor-width)+--spacing(7))] origin-(--transform-origin) overflow-hidden rounded-14 bg-popover text-popover-foreground shadow-(--shadow-popover) ring-1 ring-border duration-100 has-[[data-slot=input-group]]:min-w-(--anchor-width) data-[chips=true]:min-w-(--anchor-width) data-[side=bottom]:slide-in-from-top-2 data-[side=inline-end]:slide-in-from-left-2 data-[side=inline-start]:slide-in-from-right-2 data-[side=left]:slide-in-from-right-2 data-[side=right]:slide-in-from-left-2 data-[side=top]:slide-in-from-bottom-2 *:data-[slot=input-group]:m-1 *:data-[slot=input-group]:mb-0 *:data-[slot=input-group]:h-8 data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
127
+ className
128
+ )}
129
+ {...props}
130
+ />
131
+ </ComboboxPrimitive.Positioner>
132
+ </ComboboxPrimitive.Portal>
133
+ )
134
+ }
135
+
136
+ function ComboboxList({ className, ...props }: ComboboxPrimitive.List.Props) {
137
+ return (
138
+ <ComboboxPrimitive.List
139
+ data-slot="combobox-list"
140
+ className={cn(
141
+ "no-scrollbar max-h-[min(--spacing(66),calc(var(--available-height)---spacing(9)))] scroll-py-1 overflow-y-auto overscroll-contain p-1 data-empty:p-0",
142
+ className
143
+ )}
144
+ {...props}
145
+ />
146
+ )
147
+ }
148
+
149
+ function ComboboxItem({
150
+ className,
151
+ children,
152
+ ...props
153
+ }: ComboboxPrimitive.Item.Props) {
154
+ return (
155
+ <ComboboxPrimitive.Item
156
+ data-slot="combobox-item"
157
+ className={cn(
158
+ "relative flex w-full cursor-default items-center gap-2 rounded-lg py-1.5 pr-8 pl-2 text-sm outline-hidden select-none data-highlighted:bg-accent data-highlighted:text-accent-foreground not-data-[variant=destructive]:data-highlighted:**:text-accent-foreground data-disabled:pointer-events-none data-disabled:opacity-50 [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4",
159
+ className
160
+ )}
161
+ {...props}
162
+ >
163
+ {children}
164
+ <ComboboxPrimitive.ItemIndicator
165
+ render={
166
+ <span className="pointer-events-none absolute right-2 flex size-4 items-center justify-center" />
167
+ }
168
+ >
169
+ <CheckIcon className="pointer-events-none" />
170
+ </ComboboxPrimitive.ItemIndicator>
171
+ </ComboboxPrimitive.Item>
172
+ )
173
+ }
174
+
175
+ function ComboboxGroup({ className, ...props }: ComboboxPrimitive.Group.Props) {
176
+ return (
177
+ <ComboboxPrimitive.Group
178
+ data-slot="combobox-group"
179
+ className={className}
180
+ {...props}
181
+ />
182
+ )
183
+ }
184
+
185
+ function ComboboxLabel({
186
+ className,
187
+ ...props
188
+ }: ComboboxPrimitive.GroupLabel.Props) {
189
+ return (
190
+ <ComboboxPrimitive.GroupLabel
191
+ data-slot="combobox-label"
192
+ className={cn("px-2 py-1 text-2xs text-muted-foreground", className)}
193
+ {...props}
194
+ />
195
+ )
196
+ }
197
+
198
+ function ComboboxCollection({ ...props }: ComboboxPrimitive.Collection.Props) {
199
+ return (
200
+ <ComboboxPrimitive.Collection data-slot="combobox-collection" {...props} />
201
+ )
202
+ }
203
+
204
+ function ComboboxEmpty({ className, ...props }: ComboboxPrimitive.Empty.Props) {
205
+ return (
206
+ <ComboboxPrimitive.Empty
207
+ data-slot="combobox-empty"
208
+ className={cn(
209
+ "hidden w-full justify-center py-2 text-center text-sm text-muted-foreground group-data-empty/combobox-content:flex",
210
+ className
211
+ )}
212
+ {...props}
213
+ />
214
+ )
215
+ }
216
+
217
+ function ComboboxSeparator({
218
+ className,
219
+ ...props
220
+ }: ComboboxPrimitive.Separator.Props) {
221
+ return (
222
+ <ComboboxPrimitive.Separator
223
+ data-slot="combobox-separator"
224
+ className={cn("-mx-1 mt-1 mb-[3px] h-px bg-border", className)}
225
+ {...props}
226
+ />
227
+ )
228
+ }
229
+
230
+ function ComboboxChips({
231
+ className,
232
+ children,
233
+ showTrigger = true,
234
+ ...props
235
+ }: React.ComponentPropsWithRef<typeof ComboboxPrimitive.Chips> &
236
+ ComboboxPrimitive.Chips.Props & {
237
+ showTrigger?: boolean
238
+ }) {
239
+ return (
240
+ <ComboboxPrimitive.Chips
241
+ data-slot="combobox-chips"
242
+ className={cn(
243
+ "relative flex min-h-8 flex-wrap items-center gap-1 rounded-lg bg-field px-2.5 py-1.5 text-xs outline-1 -outline-offset-1 outline-field-border transition-[color,box-shadow,outline-color] duration-200 outline-solid has-aria-invalid:ring-2 has-aria-invalid:ring-destructive/20 has-aria-invalid:outline-destructive has-[:focus-visible]:ring-2 has-[:focus-visible]:ring-ring/30 has-[:focus-visible]:outline-ring dark:has-aria-invalid:ring-destructive/40 dark:has-aria-invalid:outline-destructive/50",
244
+ showTrigger && "pr-9.5",
245
+ className
246
+ )}
247
+ {...props}
248
+ >
249
+ {children}
250
+ {showTrigger && (
251
+ <ComboboxTrigger
252
+ render={<Button variant="ghost" size="icon-xs" />}
253
+ className="absolute top-1/2 right-1 -translate-y-1/2 data-pressed:bg-transparent"
254
+ />
255
+ )}
256
+ </ComboboxPrimitive.Chips>
257
+ )
258
+ }
259
+
260
+ function ComboboxChip({
261
+ className,
262
+ children,
263
+ showRemove = true,
264
+ ...props
265
+ }: ComboboxPrimitive.Chip.Props & {
266
+ showRemove?: boolean
267
+ }) {
268
+ return (
269
+ <ComboboxPrimitive.Chip
270
+ data-slot="combobox-chip"
271
+ className={cn(
272
+ "flex h-5 w-fit items-center justify-center gap-1 rounded-full bg-control px-2 text-2xs font-medium whitespace-nowrap text-foreground shadow-xs outline-1 outline-control-border outline-solid has-disabled:pointer-events-none has-disabled:cursor-not-allowed has-disabled:opacity-50 has-data-[slot=combobox-chip-remove]:pr-0.5",
273
+ className
274
+ )}
275
+ {...props}
276
+ >
277
+ {children}
278
+ {showRemove && (
279
+ <ComboboxPrimitive.ChipRemove
280
+ render={<Button variant="ghost" size="icon-xs" />}
281
+ className="size-4.5 opacity-50 hover:opacity-100 aria-disabled:pointer-events-none"
282
+ data-slot="combobox-chip-remove"
283
+ aria-label="Remove"
284
+ >
285
+ <XIcon className="pointer-events-none size-3" />
286
+ </ComboboxPrimitive.ChipRemove>
287
+ )}
288
+ </ComboboxPrimitive.Chip>
289
+ )
290
+ }
291
+
292
+ function ComboboxChipsInput({
293
+ className,
294
+ ...props
295
+ }: ComboboxPrimitive.Input.Props) {
296
+ return (
297
+ <ComboboxPrimitive.Input
298
+ data-slot="combobox-chip-input"
299
+ className={cn(
300
+ "min-w-16 flex-1 text-xs outline-none [[data-slot=combobox-chip]~&]:pl-1",
301
+ className
302
+ )}
303
+ {...props}
304
+ />
305
+ )
306
+ }
307
+
308
+ function useComboboxAnchor() {
309
+ return React.useRef<HTMLDivElement | null>(null)
310
+ }
311
+
312
+ export {
313
+ Combobox,
314
+ ComboboxInput,
315
+ ComboboxContent,
316
+ ComboboxList,
317
+ ComboboxItem,
318
+ ComboboxGroup,
319
+ ComboboxLabel,
320
+ ComboboxCollection,
321
+ ComboboxEmpty,
322
+ ComboboxSeparator,
323
+ ComboboxChips,
324
+ ComboboxChip,
325
+ ComboboxChipsInput,
326
+ ComboboxTrigger,
327
+ ComboboxValue,
328
+ useComboboxAnchor,
329
+ }
@@ -0,0 +1,191 @@
1
+ import * as React from "react"
2
+ import { Command as CommandPrimitive } from "cmdk"
3
+ import { cn } from "@/lib/utils"
4
+
5
+ import {
6
+ Dialog,
7
+ DialogContent,
8
+ DialogDescription,
9
+ DialogHeader,
10
+ DialogTitle,
11
+ } from "@/components/ui/dialog"
12
+ import { InputGroup, InputGroupAddon } from "@/components/ui/input-group"
13
+ import { SearchIcon, CheckIcon } from "lucide-react"
14
+
15
+ function Command({
16
+ className,
17
+ ...props
18
+ }: React.ComponentProps<typeof CommandPrimitive>) {
19
+ return (
20
+ <CommandPrimitive
21
+ data-slot="command"
22
+ className={cn(
23
+ "flex size-full flex-col overflow-hidden rounded-2xl bg-popover p-1 text-popover-foreground shadow-(--shadow-dialog) ring-1 ring-border in-data-[slot=dialog-content]:shadow-none in-data-[slot=dialog-content]:ring-0 in-data-[slot=drawer-content]:shadow-none in-data-[slot=drawer-content]:ring-0 in-data-[slot=popover-content]:shadow-none in-data-[slot=popover-content]:ring-0 in-data-[slot=sheet-content]:shadow-none in-data-[slot=sheet-content]:ring-0",
24
+ className
25
+ )}
26
+ {...props}
27
+ />
28
+ )
29
+ }
30
+
31
+ function CommandDialog({
32
+ title = "Command Palette",
33
+ description = "Search for a command to run...",
34
+ children,
35
+ className,
36
+ showCloseButton = false,
37
+ ...props
38
+ }: Omit<React.ComponentProps<typeof Dialog>, "children"> & {
39
+ title?: string
40
+ description?: string
41
+ className?: string
42
+ showCloseButton?: boolean
43
+ children: React.ReactNode
44
+ }) {
45
+ return (
46
+ <Dialog {...props}>
47
+ <DialogContent
48
+ className={cn(
49
+ "top-1/3 translate-y-0 overflow-hidden rounded-2xl! p-0",
50
+ className
51
+ )}
52
+ showCloseButton={showCloseButton}
53
+ >
54
+ <DialogHeader className="sr-only">
55
+ <DialogTitle>{title}</DialogTitle>
56
+ <DialogDescription>{description}</DialogDescription>
57
+ </DialogHeader>
58
+ {children}
59
+ </DialogContent>
60
+ </Dialog>
61
+ )
62
+ }
63
+
64
+ function CommandInput({
65
+ className,
66
+ ...props
67
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) {
68
+ return (
69
+ <div data-slot="command-input-wrapper" className="p-0.5 pb-2">
70
+ <InputGroup>
71
+ <CommandPrimitive.Input
72
+ data-slot="command-input"
73
+ className={cn(
74
+ "w-full pr-(--ig-body) pl-2 text-xs outline-hidden disabled:cursor-not-allowed disabled:opacity-50",
75
+ className
76
+ )}
77
+ {...props}
78
+ />
79
+ <InputGroupAddon>
80
+ <SearchIcon className="size-4 shrink-0 opacity-50" />
81
+ </InputGroupAddon>
82
+ </InputGroup>
83
+ </div>
84
+ )
85
+ }
86
+
87
+ function CommandList({
88
+ className,
89
+ ...props
90
+ }: React.ComponentProps<typeof CommandPrimitive.List>) {
91
+ return (
92
+ <CommandPrimitive.List
93
+ data-slot="command-list"
94
+ className={cn(
95
+ "no-scrollbar scroll-py-1 overflow-x-hidden overflow-y-auto outline-none in-data-[slot=dialog-content]:max-h-80 in-data-[slot=drawer-content]:max-h-80 in-data-[slot=popover-content]:max-h-80 in-data-[slot=sheet-content]:max-h-80",
96
+ className
97
+ )}
98
+ {...props}
99
+ />
100
+ )
101
+ }
102
+
103
+ function CommandEmpty({
104
+ className,
105
+ ...props
106
+ }: React.ComponentProps<typeof CommandPrimitive.Empty>) {
107
+ return (
108
+ <CommandPrimitive.Empty
109
+ data-slot="command-empty"
110
+ className={cn("py-6 text-center text-sm", className)}
111
+ {...props}
112
+ />
113
+ )
114
+ }
115
+
116
+ function CommandGroup({
117
+ className,
118
+ ...props
119
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) {
120
+ return (
121
+ <CommandPrimitive.Group
122
+ data-slot="command-group"
123
+ className={cn(
124
+ "overflow-hidden p-0.5 text-foreground **:[[cmdk-group-heading]]:px-2 **:[[cmdk-group-heading]]:py-1.5 **:[[cmdk-group-heading]]:text-2xs **:[[cmdk-group-heading]]:font-medium **:[[cmdk-group-heading]]:text-muted-foreground",
125
+ className
126
+ )}
127
+ {...props}
128
+ />
129
+ )
130
+ }
131
+
132
+ function CommandSeparator({
133
+ className,
134
+ ...props
135
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) {
136
+ return (
137
+ <CommandPrimitive.Separator
138
+ data-slot="command-separator"
139
+ className={cn("-mx-1 mt-1 mb-[3px] h-px bg-border", className)}
140
+ {...props}
141
+ />
142
+ )
143
+ }
144
+
145
+ function CommandItem({
146
+ className,
147
+ children,
148
+ ...props
149
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) {
150
+ return (
151
+ <CommandPrimitive.Item
152
+ data-slot="command-item"
153
+ className={cn(
154
+ "group/command-item relative flex cursor-default items-center gap-2 rounded-lg px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50 data-selected:bg-fill-2 data-selected:text-foreground [&_svg]:pointer-events-none [&_svg]:shrink-0 [&_svg:not([class*='size-'])]:size-4 data-selected:*:[svg]:text-foreground [&>svg:not([class*='ml-auto'])]:mx-0.5",
155
+ className
156
+ )}
157
+ {...props}
158
+ >
159
+ {children}
160
+ <CheckIcon className="ml-auto opacity-0 group-has-data-[slot=command-shortcut]/command-item:hidden group-data-[checked=true]/command-item:opacity-100" />
161
+ </CommandPrimitive.Item>
162
+ )
163
+ }
164
+
165
+ function CommandShortcut({
166
+ className,
167
+ ...props
168
+ }: React.ComponentProps<"span">) {
169
+ return (
170
+ <span
171
+ data-slot="command-shortcut"
172
+ className={cn(
173
+ "ml-auto text-2xs tracking-widest text-muted-foreground group-data-selected/command-item:text-foreground",
174
+ className
175
+ )}
176
+ {...props}
177
+ />
178
+ )
179
+ }
180
+
181
+ export {
182
+ Command,
183
+ CommandDialog,
184
+ CommandInput,
185
+ CommandList,
186
+ CommandEmpty,
187
+ CommandGroup,
188
+ CommandItem,
189
+ CommandShortcut,
190
+ CommandSeparator,
191
+ }
@@ -0,0 +1,162 @@
1
+ "use client"
2
+
3
+ import * as React from "react"
4
+ import { Dialog as DialogPrimitive } from "@base-ui/react/dialog"
5
+ import { cn } from "@/lib/utils"
6
+ import { useTranslation } from "react-i18next"
7
+
8
+ import { Button } from "@/components/ui/button"
9
+ import { XIcon } from "lucide-react"
10
+
11
+ function Dialog({ ...props }: DialogPrimitive.Root.Props) {
12
+ return <DialogPrimitive.Root data-slot="dialog" {...props} />
13
+ }
14
+
15
+ function DialogTrigger({ ...props }: DialogPrimitive.Trigger.Props) {
16
+ return <DialogPrimitive.Trigger data-slot="dialog-trigger" {...props} />
17
+ }
18
+
19
+ function DialogPortal({ ...props }: DialogPrimitive.Portal.Props) {
20
+ return <DialogPrimitive.Portal data-slot="dialog-portal" {...props} />
21
+ }
22
+
23
+ function DialogClose({ ...props }: DialogPrimitive.Close.Props) {
24
+ return <DialogPrimitive.Close data-slot="dialog-close" {...props} />
25
+ }
26
+
27
+ function DialogOverlay({
28
+ className,
29
+ ...props
30
+ }: DialogPrimitive.Backdrop.Props) {
31
+ return (
32
+ <DialogPrimitive.Backdrop
33
+ data-slot="dialog-overlay"
34
+ className={cn(
35
+ "fixed inset-0 isolate z-50 bg-black/30 duration-100 supports-backdrop-filter:backdrop-blur-sm data-open:animate-in data-open:fade-in-0 data-closed:animate-out data-closed:fade-out-0",
36
+ className
37
+ )}
38
+ {...props}
39
+ />
40
+ )
41
+ }
42
+
43
+ function DialogContent({
44
+ className,
45
+ children,
46
+ showCloseButton = true,
47
+ ...props
48
+ }: DialogPrimitive.Popup.Props & {
49
+ showCloseButton?: boolean
50
+ }) {
51
+ const { t } = useTranslation()
52
+ return (
53
+ <DialogPortal>
54
+ <DialogOverlay />
55
+ <DialogPrimitive.Popup
56
+ data-slot="dialog-content"
57
+ className={cn(
58
+ "fixed top-1/2 left-1/2 z-50 grid w-full max-w-[calc(100%-2rem)] -translate-x-1/2 -translate-y-1/2 gap-6 rounded-4xl bg-popover p-6 text-sm text-popover-foreground shadow-(--shadow-dialog) ring-1 ring-border duration-100 outline-none sm:max-w-md data-open:animate-in data-open:fade-in-0 data-open:zoom-in-95 data-closed:animate-out data-closed:fade-out-0 data-closed:zoom-out-95",
59
+ className
60
+ )}
61
+ {...props}
62
+ >
63
+ {children}
64
+ {showCloseButton && (
65
+ <DialogPrimitive.Close
66
+ data-slot="dialog-close"
67
+ render={
68
+ <Button
69
+ variant="ghost"
70
+ className="absolute top-4 right-4"
71
+ size="icon-sm"
72
+ />
73
+ }
74
+ >
75
+ <XIcon />
76
+ <span className="sr-only">{t("actions.close")}</span>
77
+ </DialogPrimitive.Close>
78
+ )}
79
+ </DialogPrimitive.Popup>
80
+ </DialogPortal>
81
+ )
82
+ }
83
+
84
+ function DialogHeader({ className, ...props }: React.ComponentProps<"div">) {
85
+ return (
86
+ <div
87
+ data-slot="dialog-header"
88
+ className={cn("flex flex-col gap-1.5", className)}
89
+ {...props}
90
+ />
91
+ )
92
+ }
93
+
94
+ function DialogFooter({
95
+ className,
96
+ showCloseButton = false,
97
+ children,
98
+ ...props
99
+ }: React.ComponentProps<"div"> & {
100
+ showCloseButton?: boolean
101
+ }) {
102
+ const { t } = useTranslation()
103
+ return (
104
+ <div
105
+ data-slot="dialog-footer"
106
+ className={cn(
107
+ "flex flex-col-reverse gap-2 sm:flex-row sm:justify-end",
108
+ className
109
+ )}
110
+ {...props}
111
+ >
112
+ {children}
113
+ {showCloseButton && (
114
+ <DialogPrimitive.Close render={<Button variant="outline" />}>
115
+ {t("actions.close")}
116
+ </DialogPrimitive.Close>
117
+ )}
118
+ </div>
119
+ )
120
+ }
121
+
122
+ function DialogTitle({ className, ...props }: DialogPrimitive.Title.Props) {
123
+ return (
124
+ <DialogPrimitive.Title
125
+ data-slot="dialog-title"
126
+ className={cn(
127
+ "font-heading text-base leading-none font-medium",
128
+ className
129
+ )}
130
+ {...props}
131
+ />
132
+ )
133
+ }
134
+
135
+ function DialogDescription({
136
+ className,
137
+ ...props
138
+ }: DialogPrimitive.Description.Props) {
139
+ return (
140
+ <DialogPrimitive.Description
141
+ data-slot="dialog-description"
142
+ className={cn(
143
+ "text-sm text-muted-foreground *:[a]:underline *:[a]:underline-offset-3 *:[a]:hover:text-foreground",
144
+ className
145
+ )}
146
+ {...props}
147
+ />
148
+ )
149
+ }
150
+
151
+ export {
152
+ Dialog,
153
+ DialogClose,
154
+ DialogContent,
155
+ DialogDescription,
156
+ DialogFooter,
157
+ DialogHeader,
158
+ DialogOverlay,
159
+ DialogPortal,
160
+ DialogTitle,
161
+ DialogTrigger,
162
+ }