@hanzo/ui 4.7.0 → 4.8.3

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 (278) hide show
  1. package/assets/ai-icons.tsx +207 -0
  2. package/assets/crypto.tsx +33 -0
  3. package/assets/file-type-icon.tsx +66 -0
  4. package/assets/file.tsx +45 -0
  5. package/assets/general.tsx +2318 -0
  6. package/assets/hanzo-logo.svg +9 -0
  7. package/assets/hanzo-logo.tsx +17 -0
  8. package/assets/index.ts +122 -0
  9. package/assets/index.tsx +4 -0
  10. package/assets/llm-provider.tsx +1094 -0
  11. package/blocks/auth/index.ts +6 -0
  12. package/blocks/auth/login-2fa.tsx +165 -0
  13. package/blocks/auth/login-basic.tsx +94 -0
  14. package/blocks/auth/login-social.tsx +148 -0
  15. package/blocks/auth/magic-link.tsx +129 -0
  16. package/blocks/auth/password-reset.tsx +97 -0
  17. package/blocks/auth/signup.tsx +157 -0
  18. package/blocks/components/accordian-block.tsx +48 -0
  19. package/blocks/components/block-component-props.ts +11 -0
  20. package/blocks/components/bullet-cards-block.tsx +46 -0
  21. package/blocks/components/card-block/index.tsx +171 -0
  22. package/blocks/components/card-block/link-out-button.tsx +20 -0
  23. package/blocks/components/card-block/util.ts +28 -0
  24. package/blocks/components/carte-blanche-block/index.tsx +127 -0
  25. package/blocks/components/carte-blanche-block/variant-content-left.tsx +49 -0
  26. package/blocks/components/content.tsx +70 -0
  27. package/blocks/components/cta-block.tsx +115 -0
  28. package/blocks/components/enh-heading-block.tsx +204 -0
  29. package/blocks/components/grid-block/grid-block-mutator.ts +12 -0
  30. package/blocks/components/grid-block/index.tsx +83 -0
  31. package/blocks/components/grid-block/mutator-registry.ts +10 -0
  32. package/blocks/components/grid-block/table-borders.mutator.ts +47 -0
  33. package/blocks/components/group-block.tsx +83 -0
  34. package/blocks/components/heading-block.tsx +88 -0
  35. package/blocks/components/image-block.tsx +111 -0
  36. package/blocks/components/index.ts +30 -0
  37. package/blocks/components/screenful-block/content.tsx +123 -0
  38. package/blocks/components/screenful-block/index.tsx +107 -0
  39. package/blocks/components/screenful-block/poster-background.tsx +34 -0
  40. package/blocks/components/screenful-block/video-background.tsx +45 -0
  41. package/blocks/components/space-block.tsx +66 -0
  42. package/blocks/components/video-block.tsx +138 -0
  43. package/blocks/data-display/activity-feed.tsx +242 -0
  44. package/blocks/data-display/data-table.tsx +235 -0
  45. package/blocks/data-display/stats-grid.tsx +194 -0
  46. package/blocks/def/accordian-block.ts +14 -0
  47. package/blocks/def/block.ts +7 -0
  48. package/blocks/def/bullet-cards-block.ts +22 -0
  49. package/blocks/def/card-block.ts +22 -0
  50. package/blocks/def/carte-blanche-block.ts +21 -0
  51. package/blocks/def/cta-block.ts +19 -0
  52. package/blocks/def/element-block.ts +11 -0
  53. package/blocks/def/enh-heading-block.ts +44 -0
  54. package/blocks/def/grid-block.ts +16 -0
  55. package/blocks/def/group-block.ts +11 -0
  56. package/blocks/def/heading-block.ts +15 -0
  57. package/blocks/def/image-block.ts +31 -0
  58. package/blocks/def/index.ts +35 -0
  59. package/blocks/def/screenful-block.ts +54 -0
  60. package/blocks/def/space-block.ts +64 -0
  61. package/blocks/def/video-block.ts +9 -0
  62. package/blocks/ecommerce/checkout.tsx +242 -0
  63. package/blocks/ecommerce/index.ts +7 -0
  64. package/blocks/ecommerce/product-detail.tsx +257 -0
  65. package/blocks/ecommerce/product-grid.tsx +148 -0
  66. package/blocks/ecommerce/shopping-cart.tsx +181 -0
  67. package/blocks/index.ts +2 -0
  68. package/blocks/marketing/cta-section.tsx +207 -0
  69. package/blocks/marketing/faq.tsx +159 -0
  70. package/blocks/marketing/features-grid.tsx +156 -0
  71. package/blocks/marketing/hero-section.tsx +192 -0
  72. package/blocks/marketing/index.ts +6 -0
  73. package/blocks/marketing/pricing-table.tsx +121 -0
  74. package/blocks/marketing/testimonials.tsx +196 -0
  75. package/components/index.ts +9 -0
  76. package/dist/index.js +1408 -1514
  77. package/dist/index.mjs +1364 -1472
  78. package/dist/lib/utils.js +1 -0
  79. package/dist/lib/utils.mjs +1 -0
  80. package/dist/src/utils.js +1 -0
  81. package/dist/src/utils.mjs +1 -0
  82. package/dist/tailwind/index.js +4 -1
  83. package/dist/tailwind/index.mjs +4 -1
  84. package/dist/types/index.js +1 -0
  85. package/dist/types/index.mjs +2 -0
  86. package/dist/util/format-text.js +52 -0
  87. package/dist/util/format-text.mjs +33 -0
  88. package/dist/util/index.js +385 -0
  89. package/dist/util/index.mjs +364 -0
  90. package/frameworks/core/index.ts +6 -0
  91. package/frameworks/core/utils/index.ts +64 -0
  92. package/frameworks/react/components/button.tsx +26 -0
  93. package/frameworks/react/components/index.ts +5 -0
  94. package/frameworks/react/hooks/index.ts +5 -0
  95. package/frameworks/react/index.ts +9 -0
  96. package/frameworks/react/package.json +8 -0
  97. package/frameworks/react/utils/index.ts +2 -0
  98. package/frameworks/react-native/index.ts +9 -0
  99. package/frameworks/react-native/package.json +8 -0
  100. package/frameworks/registry.json +371 -0
  101. package/frameworks/setup.sh +69 -0
  102. package/frameworks/svelte/index.ts +9 -0
  103. package/frameworks/svelte/package.json +8 -0
  104. package/frameworks/tracker.json +1854 -0
  105. package/frameworks/vue/index.ts +9 -0
  106. package/frameworks/vue/package.json +8 -0
  107. package/helpers/file.ts +33 -0
  108. package/helpers/memoization.ts +40 -0
  109. package/package.json +49 -11
  110. package/primitives/accordion.tsx +74 -0
  111. package/primitives/action-button.tsx +42 -0
  112. package/primitives/alert-dialog.tsx +185 -0
  113. package/primitives/alert.tsx +74 -0
  114. package/primitives/apply-typography.tsx +55 -0
  115. package/primitives/aspect-ratio.tsx +5 -0
  116. package/primitives/avatar.tsx +57 -0
  117. package/primitives/background-beams.tsx +142 -0
  118. package/primitives/badge.tsx +45 -0
  119. package/primitives/breadcrumb.tsx +130 -0
  120. package/primitives/breakpoint-indicator.tsx +19 -0
  121. package/primitives/button.tsx +72 -0
  122. package/primitives/calendar.tsx +72 -0
  123. package/primitives/card.tsx +97 -0
  124. package/primitives/carousel.tsx +238 -0
  125. package/primitives/chat/chat-input-area.tsx +88 -0
  126. package/primitives/chat/chat-input.tsx +71 -0
  127. package/primitives/chat/files-preview.tsx +331 -0
  128. package/primitives/chat/index.ts +6 -0
  129. package/primitives/chat/json-form.tsx +8 -0
  130. package/primitives/chat/message-list.tsx +308 -0
  131. package/primitives/chat/message.tsx +569 -0
  132. package/primitives/chat/sqlite-preview.tsx +215 -0
  133. package/primitives/checkbox.tsx +32 -0
  134. package/primitives/collapsible.tsx +9 -0
  135. package/primitives/combobox.tsx +239 -0
  136. package/primitives/command.tsx +151 -0
  137. package/primitives/context-menu.tsx +206 -0
  138. package/primitives/copy-to-clipboard-icon.tsx +60 -0
  139. package/primitives/dialog-video-controller.tsx +38 -0
  140. package/primitives/dialog.tsx +128 -0
  141. package/primitives/dot-pattern.tsx +57 -0
  142. package/primitives/dots-loader.tsx +13 -0
  143. package/primitives/drawer.tsx +113 -0
  144. package/primitives/dropdown-menu.tsx +199 -0
  145. package/primitives/error-message.tsx +19 -0
  146. package/primitives/file-uploader.tsx +202 -0
  147. package/primitives/form.tsx +185 -0
  148. package/primitives/hover-card.tsx +28 -0
  149. package/primitives/icons/github.tsx +14 -0
  150. package/primitives/icons/index.ts +18 -0
  151. package/primitives/icons/youtube-logo.tsx +59 -0
  152. package/primitives/index-common.ts +304 -0
  153. package/primitives/index-next.ts +4 -0
  154. package/primitives/input-otp.tsx +65 -0
  155. package/primitives/input.tsx +128 -0
  156. package/primitives/label.tsx +21 -0
  157. package/primitives/list-adaptor.ts +12 -0
  158. package/primitives/list-box.tsx +74 -0
  159. package/primitives/loading-spinner.tsx +33 -0
  160. package/primitives/markdown-preview.tsx +612 -0
  161. package/primitives/mermaid.tsx +191 -0
  162. package/primitives/navigation-menu.tsx +147 -0
  163. package/primitives/next/image.tsx +91 -0
  164. package/primitives/next/index.ts +7 -0
  165. package/primitives/next/inline-icon.tsx +36 -0
  166. package/primitives/next/link-element.tsx +109 -0
  167. package/primitives/next/mdx-link.tsx +22 -0
  168. package/primitives/next/media-stack.tsx +52 -0
  169. package/primitives/next/nav-items.tsx +45 -0
  170. package/primitives/next/youtube-embed.tsx +83 -0
  171. package/primitives/pagination.tsx +117 -0
  172. package/primitives/popover.tsx +34 -0
  173. package/primitives/pretty-json-print.tsx +28 -0
  174. package/primitives/progress.tsx +27 -0
  175. package/primitives/prompt-textarea.tsx +72 -0
  176. package/primitives/qr-code.tsx +112 -0
  177. package/primitives/radio-group.tsx +42 -0
  178. package/primitives/resizable.tsx +47 -0
  179. package/primitives/scroll-area.tsx +57 -0
  180. package/primitives/search-input.tsx +66 -0
  181. package/primitives/select.tsx +122 -0
  182. package/primitives/separator.tsx +26 -0
  183. package/primitives/sheet.tsx +139 -0
  184. package/primitives/skeleton.tsx +18 -0
  185. package/primitives/slider.tsx +63 -0
  186. package/primitives/sonner.tsx +35 -0
  187. package/primitives/step-indicator.tsx +69 -0
  188. package/primitives/stepper.tsx +272 -0
  189. package/primitives/switch.tsx +27 -0
  190. package/primitives/table.tsx +105 -0
  191. package/primitives/tabs.tsx +50 -0
  192. package/primitives/text-area.tsx +26 -0
  193. package/primitives/text-link.tsx +25 -0
  194. package/primitives/textarea.tsx +64 -0
  195. package/primitives/textfield.tsx +78 -0
  196. package/primitives/toast.tsx +30 -0
  197. package/primitives/toggle-group.tsx +63 -0
  198. package/primitives/toggle.tsx +44 -0
  199. package/primitives/tooltip.tsx +47 -0
  200. package/primitives/video-player.tsx +23 -0
  201. package/src/button.ts +1 -0
  202. package/src/hooks/index.ts +7 -0
  203. package/src/hooks/use-click-away.ts +31 -0
  204. package/src/hooks/use-combined-refs.ts +22 -0
  205. package/src/hooks/use-copy-clipboard.ts +30 -0
  206. package/src/hooks/use-debounce.ts +17 -0
  207. package/src/hooks/use-fill-ids.ts +25 -0
  208. package/src/hooks/use-map.ts +26 -0
  209. package/src/hooks/use-measure.ts +42 -0
  210. package/src/hooks/use-reverse-video-playback.ts +43 -0
  211. package/src/hooks/use-scroll-restoration.ts +50 -0
  212. package/src/index-lean.ts +87 -0
  213. package/src/index.ts +54 -0
  214. package/src/mcp/README.md +141 -0
  215. package/src/mcp/enhanced-server.ts +1208 -0
  216. package/src/mcp/index.ts +518 -0
  217. package/src/mcp/package.json +10 -0
  218. package/src/registry/api.ts +164 -0
  219. package/src/registry/index.ts +60 -0
  220. package/src/registry/package.json +10 -0
  221. package/src/utils.ts +19 -0
  222. package/tailwind/colors.tailwind.js +53 -0
  223. package/tailwind/fontFamily.tailwind.ts +7 -0
  224. package/tailwind/fontSize.tailwind.ts +13 -0
  225. package/tailwind/index.ts +7 -0
  226. package/tailwind/safelist.tailwind.js +26 -0
  227. package/tailwind/screens.tailwind.js +8 -0
  228. package/tailwind/spacing.tailwind.js +65 -0
  229. package/tailwind/tailwind.config.hanzo-preset.d.ts +5 -0
  230. package/tailwind/tailwind.config.hanzo-preset.js +915 -0
  231. package/tailwind/tw-font-desc.ts +15 -0
  232. package/tailwind/typo-plugin/get-plugin-styles.js +679 -0
  233. package/tailwind/typo-plugin/index.d.ts +9 -0
  234. package/tailwind/typo-plugin/index.js +141 -0
  235. package/tailwind/typo-plugin/utils.js +60 -0
  236. package/tailwind/typography-test.mdx +35 -0
  237. package/tailwind/z-index.tailwind.js +71 -0
  238. package/types/animation-def.ts +3 -0
  239. package/types/breakpoints.ts +11 -0
  240. package/types/bullet-item.ts +10 -0
  241. package/types/button-def.ts +39 -0
  242. package/types/dimensions.ts +8 -0
  243. package/types/grid-def.ts +56 -0
  244. package/types/image-def.ts +32 -0
  245. package/types/index.ts +30 -0
  246. package/types/link-def.ts +56 -0
  247. package/types/media-stack-def.ts +31 -0
  248. package/types/t-shirt-size.ts +5 -0
  249. package/types/tshirt-dimensions.ts +20 -0
  250. package/types/video-def.ts +25 -0
  251. package/util/blob.ts +33 -0
  252. package/util/copy-to-clipboard.ts +17 -0
  253. package/util/create-shadow-root.ts +22 -0
  254. package/util/date.ts +84 -0
  255. package/util/debounce.ts +11 -0
  256. package/util/file.ts +15 -0
  257. package/util/format-and-abbreviate-as-currency.ts +125 -0
  258. package/util/format-text.ts +34 -0
  259. package/util/format-to-max-char.ts +68 -0
  260. package/util/index-client.ts +3 -0
  261. package/util/index.ts +112 -0
  262. package/util/number-abbreviate.ts +49 -0
  263. package/util/specifier.ts +43 -0
  264. package/util/spread-to-transform.ts +25 -0
  265. package/util/step-animation.ts +90 -0
  266. package/util/timing.ts +3 -0
  267. package/util/toasts.tsx +17 -0
  268. package/util/two-way-map.ts +19 -0
  269. package/dist/index.d.mts +0 -16
  270. package/dist/index.d.ts +0 -16
  271. package/dist/lib/utils.d.mts +0 -2
  272. package/dist/lib/utils.d.ts +0 -2
  273. package/dist/src/utils.d.mts +0 -7
  274. package/dist/src/utils.d.ts +0 -7
  275. package/dist/tailwind/index.d.mts +0 -2
  276. package/dist/tailwind/index.d.ts +0 -2
  277. package/dist/types/index.d.mts +0 -12
  278. package/dist/types/index.d.ts +0 -12
@@ -0,0 +1,215 @@
1
+ // import { debug } from '@tauri-apps/plugin-log'; // Not available in web environment
2
+ import { useEffect, useState } from 'react';
3
+ import * as sqljs from 'sql.js';
4
+
5
+ import { Button } from '../button';
6
+ import { Skeleton } from '../skeleton';
7
+
8
+ interface SqlitePreviewProps {
9
+ url: string;
10
+ }
11
+
12
+ const loadTableData = async (
13
+ db: sqljs.Database,
14
+ tableName: string,
15
+ ): Promise<{ values: sqljs.SqlValue[][] }> => {
16
+ void console.log(`loading table data for ${tableName}`);
17
+ const dataResult = db.exec(`SELECT * FROM ${tableName}`);
18
+ void console.log(`found ${dataResult[0].values.length} rows for ${tableName}`);
19
+ return { values: dataResult[0].values };
20
+ };
21
+
22
+ const loadDatabase = async (
23
+ url: string,
24
+ ): Promise<{
25
+ db: sqljs.Database;
26
+ tables: { name: string; columns: sqljs.SqlValue[]; count: number }[];
27
+ }> => {
28
+ void console.log(`loading database from ${url}`);
29
+ const SQL = await sqljs.default({
30
+ locateFile: (file: string) => '/sqljs/sql-wasm.wasm',
31
+ });
32
+
33
+ const response = await fetch(url);
34
+ const arrayBuffer = await response.arrayBuffer();
35
+ const db = new SQL.Database(new Uint8Array(arrayBuffer));
36
+
37
+ // Get list of tables
38
+ const tablesResult = db.exec(
39
+ "SELECT name FROM sqlite_master WHERE type='table'",
40
+ );
41
+ const tableNames = tablesResult[0].values.map((v) => v[0] as string);
42
+ void console.log(`found ${tableNames.length} tables`);
43
+ void console.log(`tables: ${tableNames.join(', ')}`);
44
+ const tables = tableNames.map((table) => {
45
+ const columnsResult = db.exec(`PRAGMA table_info(${table})`);
46
+ const columnNames = columnsResult[0].values.map((v) => v[1]);
47
+ const countResult = db.exec(`SELECT COUNT(*) FROM ${table}`);
48
+ const count = countResult[0].values[0][0] as number;
49
+ void console.log(
50
+ `found columns ${columnNames.join(', ')} and ${count} rows for ${table}`,
51
+ );
52
+ return {
53
+ name: table,
54
+ columns: columnNames,
55
+ count,
56
+ };
57
+ });
58
+
59
+ return { db, tables };
60
+ };
61
+
62
+ export const SqlitePreview: React.FC<SqlitePreviewProps> = ({ url }) => {
63
+ const [selectedTable, setSelectedTable] = useState<string>();
64
+ const [error, setError] = useState<string>();
65
+ const [loading, setLoading] = useState(true);
66
+ const [loadingData, setLoadingData] = useState(false);
67
+
68
+ const [db, setDb] = useState<sqljs.Database | null>(null);
69
+ const [tables, setTables] = useState<
70
+ { name: string; columns: sqljs.SqlValue[]; count: number }[]
71
+ >([]);
72
+ const [tableData, setTableData] = useState<{
73
+ columns: sqljs.SqlValue[];
74
+ rows: sqljs.SqlValue[][];
75
+ } | null>(null);
76
+
77
+ useEffect(() => {
78
+ const init = async () => {
79
+ setLoading(true);
80
+ try {
81
+ const { db, tables } = await loadDatabase(url);
82
+ setDb(db);
83
+ setTables(tables);
84
+ if (tables.length > 0) {
85
+ setSelectedTable(tables[0].name);
86
+ }
87
+ } catch (error) {
88
+ setError(
89
+ error instanceof Error ? error.message : 'Failed to load database',
90
+ );
91
+ } finally {
92
+ setLoading(false);
93
+ }
94
+ };
95
+ void init();
96
+ }, [url]);
97
+
98
+ useEffect(() => {
99
+ if (db && selectedTable) {
100
+ setLoadingData(true);
101
+ loadTableData(db, selectedTable)
102
+ .then(({ values }) => {
103
+ const columns = tables.find(
104
+ (table) => table.name === selectedTable,
105
+ )?.columns;
106
+ if (!columns) {
107
+ setError('Table not found');
108
+ return;
109
+ }
110
+ setTableData({
111
+ columns,
112
+ rows: values,
113
+ });
114
+ })
115
+ .catch((error) => {
116
+ setError(
117
+ error instanceof Error
118
+ ? error.message
119
+ : 'Failed to load table data',
120
+ );
121
+ })
122
+ .finally(() => {
123
+ setLoadingData(false);
124
+ });
125
+ }
126
+ }, [db, selectedTable, tables]);
127
+
128
+ useEffect(() => {
129
+ return () => {
130
+ if (db) {
131
+ void console.log('closing database connection');
132
+ db.close();
133
+ }
134
+ };
135
+ }, [db]);
136
+
137
+ if (error) {
138
+ return (
139
+ <div className="p-4 text-red-500">Error loading database: {error}</div>
140
+ );
141
+ }
142
+
143
+ if (loading) {
144
+ return (
145
+ <div className="flex flex-col gap-4 rounded-lg bg-gray-600 p-4">
146
+ <div className="flex gap-2">
147
+ <Skeleton className="h-8 w-24" />
148
+ <Skeleton className="h-8 w-24" />
149
+ </div>
150
+ <div className="space-y-2">
151
+ <Skeleton className="h-8 w-full" />
152
+ <Skeleton className="h-8 w-full" />
153
+ <Skeleton className="h-8 w-full" />
154
+ </div>
155
+ </div>
156
+ );
157
+ }
158
+
159
+ return (
160
+ <div className="flex flex-col gap-4 overflow-y-hidden rounded-lg bg-gray-600 p-4">
161
+ <div className="flex flex-wrap gap-2">
162
+ {tables.map((table) => (
163
+ <Button
164
+ key={table.name}
165
+ onClick={() => setSelectedTable(table.name)}
166
+ size="sm"
167
+ variant={selectedTable === table.name ? 'default' : 'outline'}
168
+ >
169
+ {table.name} ({table.count})
170
+ </Button>
171
+ ))}
172
+ </div>
173
+
174
+ {selectedTable && (
175
+ <div className="max-h-full min-h-fit overflow-y-auto">
176
+ <div className="overflow-x-auto">
177
+ {loadingData ? (
178
+ <div className="space-y-2">
179
+ <Skeleton className="h-8 w-full" />
180
+ <Skeleton className="h-8 w-full" />
181
+ <Skeleton className="h-8 w-full" />
182
+ </div>
183
+ ) : (
184
+ <table className="w-full text-left text-sm">
185
+ <thead className="sticky top-0 bg-gray-600">
186
+ <tr className="border-b border-gray-500">
187
+ {tableData?.columns.map((column) => (
188
+ <th
189
+ className="p-2 font-medium text-gray-300"
190
+ key={String(column)}
191
+ >
192
+ {String(column)}
193
+ </th>
194
+ ))}
195
+ </tr>
196
+ </thead>
197
+ <tbody>
198
+ {tableData?.rows.map((row, i) => (
199
+ <tr className="border-b border-gray-700" key={i}>
200
+ {row.map((cell, j) => (
201
+ <td className="text-text-secondary p-2" key={j}>
202
+ {String(cell)}
203
+ </td>
204
+ ))}
205
+ </tr>
206
+ ))}
207
+ </tbody>
208
+ </table>
209
+ )}
210
+ </div>
211
+ </div>
212
+ )}
213
+ </div>
214
+ );
215
+ };
@@ -0,0 +1,32 @@
1
+ import * as CheckboxPrimitive from '@radix-ui/react-checkbox';
2
+ import { CheckIcon } from '@radix-ui/react-icons';
3
+ import React from 'react';
4
+
5
+ import { cn } from '../src/utils';
6
+
7
+ type CheckboxProps = React.ComponentPropsWithoutRef<
8
+ typeof CheckboxPrimitive.Root
9
+ > & {
10
+ ref?: React.RefObject<React.ComponentRef<typeof CheckboxPrimitive.Root>>;
11
+ };
12
+
13
+ const Checkbox = ({ className, ref, ...props }: CheckboxProps) => (
14
+ <CheckboxPrimitive.Root
15
+ className={cn(
16
+ 'focus-visible:ring-ring data-[state=checked]:bg-brand data-[state=checked]:border-brand peer h-4 w-4 shrink-0 rounded-xs border border-gray-400 shadow-sm focus-visible:ring-1 focus-visible:outline-hidden disabled:cursor-not-allowed disabled:opacity-50 data-[state=checked]:text-black',
17
+ className,
18
+ )}
19
+ ref={ref}
20
+ {...props}
21
+ >
22
+ <CheckboxPrimitive.Indicator
23
+ className={cn('flex items-center justify-center text-current')}
24
+ >
25
+ <CheckIcon className="h-3.5 w-3.5" />
26
+ </CheckboxPrimitive.Indicator>
27
+ </CheckboxPrimitive.Root>
28
+ );
29
+ Checkbox.displayName = CheckboxPrimitive.Root.displayName;
30
+
31
+ export { Checkbox };
32
+ export default Checkbox;
@@ -0,0 +1,9 @@
1
+ import * as CollapsiblePrimitive from '@radix-ui/react-collapsible';
2
+
3
+ const Collapsible = CollapsiblePrimitive.Root;
4
+
5
+ const CollapsibleTrigger = CollapsiblePrimitive.CollapsibleTrigger;
6
+
7
+ const CollapsibleContent = CollapsiblePrimitive.CollapsibleContent;
8
+
9
+ export { Collapsible, CollapsibleTrigger, CollapsibleContent };
@@ -0,0 +1,239 @@
1
+ 'use client'
2
+ import React, { useState } from 'react'
3
+
4
+ import { Check, ChevronDown } from 'lucide-react'
5
+
6
+ import { cn } from '../util'
7
+ import Button from './button'
8
+ import {
9
+ Command,
10
+ CommandEmpty,
11
+ CommandGroup,
12
+ CommandInput,
13
+ CommandItem,
14
+ CommandList,
15
+ } from './command'
16
+
17
+ import {
18
+ Popover,
19
+ PopoverContent,
20
+ PopoverTrigger,
21
+ } from './popover'
22
+
23
+ import type ListAdaptor from './list-adaptor'
24
+
25
+ const DEFAULT_IMAGE_SIZE = 32
26
+
27
+ interface ComboboxTriggerProps<T> {
28
+ current: T | null
29
+ currentLabel: string | null
30
+ imageUrl: string | null
31
+ placeholder?: string
32
+ buttonClx?: string
33
+ imageClx?: string
34
+ disabled?: boolean
35
+ imageSize?: number
36
+ noChevron?: boolean
37
+ open: boolean
38
+ }
39
+
40
+ const DefaultTriggerInner = <T,>(
41
+ {
42
+ current,
43
+ currentLabel,
44
+ imageUrl,
45
+ buttonClx='',
46
+ imageClx='',
47
+ placeholder='(select)',
48
+ disabled=false,
49
+ imageSize=DEFAULT_IMAGE_SIZE,
50
+ noChevron=false,
51
+ open,
52
+ ...rest
53
+ }: ComboboxTriggerProps<T>,
54
+ ref: React.ForwardedRef<HTMLButtonElement>
55
+ ) => (
56
+ <Button
57
+ ref={ref}
58
+ {...rest}
59
+ variant='outline'
60
+ role='combobox'
61
+ aria-expanded={open}
62
+ className={cn(
63
+ 'flex',
64
+ noChevron ? 'justify-start' : 'justify-between',
65
+ buttonClx
66
+ )}
67
+ disabled={disabled}
68
+ >
69
+ <div className='flex justify-start items-center gap-2'>
70
+ {(current && imageUrl) ? (
71
+ <img
72
+ src={imageUrl}
73
+ alt={currentLabel + ' image'}
74
+ height={imageSize}
75
+ width={imageSize}
76
+ loading="eager"
77
+ className={cn('block', imageClx)}
78
+ />
79
+ ) : (
80
+ <div style={{width: imageSize, height: imageSize}} />
81
+ )}
82
+ <span className='block'>{currentLabel}</span>
83
+ </div>
84
+ {!noChevron && (<ChevronDown className={cn('block', open ? '' : 'opacity-50')} />)}
85
+ </Button>
86
+ )
87
+
88
+ const DefaultTrigger = React.forwardRef(DefaultTriggerInner) as <T, P>(props: P & { ref?: React.ForwardedRef<HTMLButtonElement> }) => React.ReactNode
89
+
90
+ const Combobox = <T, P extends ComboboxTriggerProps<T>>({
91
+ elements,
92
+ initial,
93
+ current,
94
+ setCurrent,
95
+ closeOnSelect=true,
96
+ adaptor,
97
+ popoverClx='',
98
+ listItemClx='',
99
+ listItemSelectedClx='',
100
+ listItemDisabledClx='',
101
+ noCheckmark=false,
102
+ listItemImageClx='',
103
+ searchPlaceholder='Search...',
104
+ noneFoundMessage='None found.',
105
+ listItemImageSize=DEFAULT_IMAGE_SIZE,
106
+ noSearch=false,
107
+ popoverAlign = 'center',
108
+ popoverSideOffset = 4,
109
+ Trigger,
110
+ triggerProps
111
+ }: {
112
+ elements: T[]
113
+ initial?: T | null
114
+ current?: T | null
115
+ setCurrent: (c: T | null) => void
116
+ closeOnSelect?: boolean
117
+ adaptor: ListAdaptor<T>
118
+ popoverClx?: string
119
+ listItemClx?: string
120
+ listItemSelectedClx?: string
121
+ listItemDisabledClx?: string
122
+ listItemImageClx?: string
123
+ listItemImageSize?: number
124
+ noCheckmark?: boolean
125
+ searchPlaceholder?: string
126
+ noneFoundMessage?: string
127
+ noSearch?: boolean
128
+ popoverAlign?: "center" | "end" | "start"
129
+ popoverSideOffset?: number
130
+ /** If (custom) Trigger is not supplied,
131
+ * passed to default trigger */
132
+ triggerProps: P
133
+ Trigger?:
134
+ <T, P>(props: P & { ref?: React.ForwardedRef<HTMLButtonElement> }) => React.ReactNode
135
+ }) => {
136
+
137
+ const [_open, _setOpen] = useState<boolean>(false)
138
+ // for non-controlled base (must declare the hook either way)
139
+ const [_current, _setCurrent] = useState<T | null>(initial ?? null)
140
+
141
+ const handleSelect = (selString: string) => {
142
+
143
+ const found = elements.find((el: T) => (adaptor.valueEquals(el, selString)))
144
+ if (found) {
145
+ // non-controlled ('initial' supplied (may have been null))
146
+ if (initial !== undefined) {
147
+ _setCurrent(found)
148
+ }
149
+ setCurrent(found)
150
+ }
151
+ if (closeOnSelect) {
152
+ _setOpen(false)
153
+ }
154
+ }
155
+
156
+ const isCurrent = (el: T): boolean => {
157
+
158
+ // non-controlled?
159
+ const curr = (current === undefined) ? _current : current
160
+ return !!curr && adaptor.equals(el, curr)
161
+ }
162
+
163
+ const _triggerProps = current ? {
164
+ ...triggerProps,
165
+ current,
166
+ currentLabel: adaptor.getLabel ? adaptor.getLabel(current) : adaptor.getValue(current),
167
+ imageUrl: adaptor.getImageUrl ? adaptor.getImageUrl(current) : null,
168
+ open: _open
169
+ } : {
170
+ ...triggerProps,
171
+ current: null,
172
+ currentLabel: null,
173
+ imageUrl: null,
174
+ open: _open
175
+ }
176
+
177
+ return (
178
+ <Popover open={_open} onOpenChange={_setOpen}>
179
+ <PopoverTrigger asChild>
180
+ {Trigger ? (
181
+ <Trigger<T, P> {..._triggerProps} />
182
+ ) : (
183
+ <DefaultTrigger<T, P> {..._triggerProps} />
184
+ )}
185
+ </PopoverTrigger>
186
+ <PopoverContent className={cn('p-0', popoverClx)} align={popoverAlign} sideOffset={popoverSideOffset}>
187
+ <Command>
188
+ {!noSearch && (<CommandInput placeholder={searchPlaceholder} />)}
189
+ <CommandList>
190
+ <CommandEmpty>{noneFoundMessage}</CommandEmpty>
191
+ <CommandGroup>
192
+ {elements.map((el) => (
193
+ <CommandItem
194
+ key={adaptor.getValue(el)}
195
+ value={adaptor.getValue(el)}
196
+ onSelect={handleSelect}
197
+ disabled={adaptor.isDisabled ? adaptor.isDisabled(el) : false}
198
+ className={cn(
199
+ 'flex',
200
+ noCheckmark ? 'justify-start' : 'justify-between',
201
+ listItemClx,
202
+ (isCurrent(el) ? listItemSelectedClx : ''),
203
+ ((adaptor.isDisabled && adaptor.isDisabled(el)) ? listItemDisabledClx : '')
204
+ )}
205
+ >
206
+ <div className='flex justify-start items-center gap-2'>
207
+ { (adaptor.getImageUrl && adaptor.getImageUrl(el)) ? (
208
+ <img
209
+ src={adaptor.getImageUrl(el)!}
210
+ alt={adaptor.getValue(el) + ' image'}
211
+ height={listItemImageSize}
212
+ width={listItemImageSize}
213
+ loading="eager"
214
+ className={listItemImageClx}
215
+ />
216
+ ) : (
217
+ <div style={{width: listItemImageSize, height: listItemImageSize}} />
218
+ )}
219
+ <span>{ adaptor.getLabel ? adaptor.getLabel(el) : adaptor.getValue(el) }</span>
220
+ </div>
221
+ {!noCheckmark && (
222
+ <div>
223
+ <Check className={cn('ml-auto', (isCurrent(el)) ? '' : 'invisible' )} />
224
+ </div>
225
+ )}
226
+ </CommandItem>
227
+ ))}
228
+ </CommandGroup>
229
+ </CommandList>
230
+ </Command>
231
+ </PopoverContent>
232
+ </Popover>
233
+ )
234
+ }
235
+
236
+ export {
237
+ Combobox as default,
238
+ type ComboboxTriggerProps
239
+ }
@@ -0,0 +1,151 @@
1
+ 'use client';
2
+
3
+ import { DialogProps } from '@radix-ui/react-dialog';
4
+ import { Command as CommandPrimitive } from 'cmdk';
5
+ import { Search } from 'lucide-react';
6
+ import React from 'react';
7
+
8
+ import { cn } from '../src/utils';
9
+ import { Dialog, DialogContent } from './dialog';
10
+
11
+ const Command = ({
12
+ className,
13
+ ...props
14
+ }: React.ComponentProps<typeof CommandPrimitive>) => (
15
+ <CommandPrimitive
16
+ className={cn(
17
+ 'bg-popover text-popover-foreground flex h-full w-full flex-col overflow-hidden rounded-md [&_[cmdk-list]]:scroll-pt-0 [&_[cmdk-list]]:scroll-auto',
18
+ className,
19
+ )}
20
+ loop={true}
21
+ {...props}
22
+ />
23
+ );
24
+ Command.displayName = CommandPrimitive.displayName;
25
+
26
+ type CommandDialogProps = DialogProps;
27
+
28
+ const CommandDialog = ({ children, ...props }: CommandDialogProps) => {
29
+ return (
30
+ <Dialog {...props}>
31
+ <DialogContent className="overflow-hidden p-0 shadow-lg">
32
+ <Command className="[&_[cmdk-group-heading]]:text-text-secondary [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:font-medium [&_[cmdk-group]]:px-2 [&_[cmdk-group]:not([hidden])_~[cmdk-group]]:pt-0 [&_[cmdk-input-wrapper]_svg]:h-5 [&_[cmdk-input-wrapper]_svg]:w-5 [&_[cmdk-input]]:h-12 [&_[cmdk-item]]:px-2 [&_[cmdk-item]]:py-3 [&_[cmdk-item]_svg]:h-5 [&_[cmdk-item]_svg]:w-5">
33
+ {children}
34
+ </Command>
35
+ </DialogContent>
36
+ </Dialog>
37
+ );
38
+ };
39
+
40
+ const CommandInput = ({
41
+ className,
42
+ ref,
43
+ ...props
44
+ }: React.ComponentProps<typeof CommandPrimitive.Input>) => (
45
+ <div className="flex items-center border-b px-3" cmdk-input-wrapper="">
46
+ <Search className="mr-2 h-4 w-4 shrink-0 opacity-50" />
47
+ <CommandPrimitive.Input
48
+ className={cn(
49
+ 'placeholder:!text-text-placeholder flex h-11 w-full rounded-md bg-transparent py-3 text-sm outline-hidden disabled:cursor-not-allowed disabled:opacity-50',
50
+ className,
51
+ )}
52
+ ref={ref}
53
+ {...props}
54
+ />
55
+ </div>
56
+ );
57
+
58
+ CommandInput.displayName = CommandPrimitive.Input.displayName;
59
+
60
+ const CommandList = ({
61
+ className,
62
+ ...props
63
+ }: React.ComponentProps<typeof CommandPrimitive.List>) => (
64
+ <CommandPrimitive.List
65
+ className={cn(
66
+ 'max-h-[300px] scroll-pt-0 overflow-x-hidden overflow-y-auto scroll-auto',
67
+ className,
68
+ )}
69
+ {...props}
70
+ />
71
+ );
72
+
73
+ CommandList.displayName = CommandPrimitive.List.displayName;
74
+
75
+ const CommandEmpty = ({
76
+ className,
77
+ ...props
78
+ }: React.ComponentProps<typeof CommandPrimitive.Empty>) => (
79
+ <CommandPrimitive.Empty className="py-6 text-center text-sm" {...props} />
80
+ );
81
+
82
+ CommandEmpty.displayName = CommandPrimitive.Empty.displayName;
83
+
84
+ const CommandGroup = ({
85
+ className,
86
+ ...props
87
+ }: React.ComponentProps<typeof CommandPrimitive.Group>) => (
88
+ <CommandPrimitive.Group
89
+ className={cn(
90
+ 'text-text-default [&_[cmdk-group-heading]]:text-text-default overflow-hidden p-1 [&_[cmdk-group-heading]]:px-2 [&_[cmdk-group-heading]]:py-1.5 [&_[cmdk-group-heading]]:text-xs [&_[cmdk-group-heading]]:font-medium',
91
+ className,
92
+ )}
93
+ {...props}
94
+ />
95
+ );
96
+
97
+ CommandGroup.displayName = CommandPrimitive.Group.displayName;
98
+
99
+ const CommandSeparator = ({
100
+ className,
101
+ ...props
102
+ }: React.ComponentProps<typeof CommandPrimitive.Separator>) => (
103
+ <CommandPrimitive.Separator
104
+ className={cn('bg-divider -mx-1 h-px', className)}
105
+ {...props}
106
+ />
107
+ );
108
+ CommandSeparator.displayName = CommandPrimitive.Separator.displayName;
109
+
110
+ const CommandItem = ({
111
+ className,
112
+ ...props
113
+ }: React.ComponentProps<typeof CommandPrimitive.Item>) => (
114
+ <CommandPrimitive.Item
115
+ className={cn(
116
+ "hover:bg-bg-secondary data-[selected=true]:text-text-default data-[selected='true']:bg-bg-secondary relative flex cursor-default items-center rounded-xs px-2 py-1.5 text-sm outline-hidden select-none data-[disabled=true]:pointer-events-none data-[disabled=true]:opacity-50",
117
+ className,
118
+ )}
119
+ {...props}
120
+ />
121
+ );
122
+
123
+ CommandItem.displayName = CommandPrimitive.Item.displayName;
124
+
125
+ const CommandShortcut = ({
126
+ className,
127
+ ...props
128
+ }: React.HTMLAttributes<HTMLSpanElement>) => {
129
+ return (
130
+ <span
131
+ className={cn(
132
+ 'text-text-secondary ml-auto text-xs tracking-widest',
133
+ className,
134
+ )}
135
+ {...props}
136
+ />
137
+ );
138
+ };
139
+ CommandShortcut.displayName = 'CommandShortcut';
140
+
141
+ export {
142
+ Command,
143
+ CommandDialog,
144
+ CommandInput,
145
+ CommandList,
146
+ CommandEmpty,
147
+ CommandGroup,
148
+ CommandItem,
149
+ CommandShortcut,
150
+ CommandSeparator,
151
+ };