@hanzo/ui 4.7.0 → 4.8.2
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/ai-icons.tsx +207 -0
- package/assets/crypto.tsx +33 -0
- package/assets/file-type-icon.tsx +66 -0
- package/assets/file.tsx +45 -0
- package/assets/general.tsx +2318 -0
- package/assets/hanzo-logo.svg +9 -0
- package/assets/hanzo-logo.tsx +17 -0
- package/assets/index.ts +122 -0
- package/assets/index.tsx +4 -0
- package/assets/llm-provider.tsx +1094 -0
- package/blocks/auth/index.ts +6 -0
- package/blocks/auth/login-2fa.tsx +165 -0
- package/blocks/auth/login-basic.tsx +94 -0
- package/blocks/auth/login-social.tsx +148 -0
- package/blocks/auth/magic-link.tsx +129 -0
- package/blocks/auth/password-reset.tsx +97 -0
- package/blocks/auth/signup.tsx +157 -0
- package/blocks/components/accordian-block.tsx +48 -0
- package/blocks/components/block-component-props.ts +11 -0
- package/blocks/components/bullet-cards-block.tsx +46 -0
- package/blocks/components/card-block/index.tsx +171 -0
- package/blocks/components/card-block/link-out-button.tsx +20 -0
- package/blocks/components/card-block/util.ts +28 -0
- package/blocks/components/carte-blanche-block/index.tsx +127 -0
- package/blocks/components/carte-blanche-block/variant-content-left.tsx +49 -0
- package/blocks/components/content.tsx +70 -0
- package/blocks/components/cta-block.tsx +115 -0
- package/blocks/components/enh-heading-block.tsx +204 -0
- package/blocks/components/grid-block/grid-block-mutator.ts +12 -0
- package/blocks/components/grid-block/index.tsx +83 -0
- package/blocks/components/grid-block/mutator-registry.ts +10 -0
- package/blocks/components/grid-block/table-borders.mutator.ts +47 -0
- package/blocks/components/group-block.tsx +83 -0
- package/blocks/components/heading-block.tsx +88 -0
- package/blocks/components/image-block.tsx +111 -0
- package/blocks/components/index.ts +30 -0
- package/blocks/components/screenful-block/content.tsx +123 -0
- package/blocks/components/screenful-block/index.tsx +107 -0
- package/blocks/components/screenful-block/poster-background.tsx +34 -0
- package/blocks/components/screenful-block/video-background.tsx +45 -0
- package/blocks/components/space-block.tsx +66 -0
- package/blocks/components/video-block.tsx +138 -0
- package/blocks/data-display/activity-feed.tsx +242 -0
- package/blocks/data-display/data-table.tsx +235 -0
- package/blocks/data-display/stats-grid.tsx +194 -0
- package/blocks/def/accordian-block.ts +14 -0
- package/blocks/def/block.ts +7 -0
- package/blocks/def/bullet-cards-block.ts +22 -0
- package/blocks/def/card-block.ts +22 -0
- package/blocks/def/carte-blanche-block.ts +21 -0
- package/blocks/def/cta-block.ts +19 -0
- package/blocks/def/element-block.ts +11 -0
- package/blocks/def/enh-heading-block.ts +44 -0
- package/blocks/def/grid-block.ts +16 -0
- package/blocks/def/group-block.ts +11 -0
- package/blocks/def/heading-block.ts +15 -0
- package/blocks/def/image-block.ts +31 -0
- package/blocks/def/index.ts +35 -0
- package/blocks/def/screenful-block.ts +54 -0
- package/blocks/def/space-block.ts +64 -0
- package/blocks/def/video-block.ts +9 -0
- package/blocks/ecommerce/checkout.tsx +242 -0
- package/blocks/ecommerce/index.ts +7 -0
- package/blocks/ecommerce/product-detail.tsx +257 -0
- package/blocks/ecommerce/product-grid.tsx +148 -0
- package/blocks/ecommerce/shopping-cart.tsx +181 -0
- package/blocks/index.ts +2 -0
- package/blocks/marketing/cta-section.tsx +207 -0
- package/blocks/marketing/faq.tsx +159 -0
- package/blocks/marketing/features-grid.tsx +156 -0
- package/blocks/marketing/hero-section.tsx +192 -0
- package/blocks/marketing/index.ts +6 -0
- package/blocks/marketing/pricing-table.tsx +121 -0
- package/blocks/marketing/testimonials.tsx +196 -0
- package/components/index.ts +9 -0
- package/dist/index.js +1407 -1514
- package/dist/index.mjs +1363 -1472
- package/dist/tailwind/index.js +3 -1
- package/dist/tailwind/index.mjs +3 -1
- package/dist/util/format-text.js +51 -0
- package/dist/util/format-text.mjs +32 -0
- package/dist/util/index.js +384 -0
- package/dist/util/index.mjs +363 -0
- package/frameworks/core/index.ts +6 -0
- package/frameworks/core/utils/index.ts +64 -0
- package/frameworks/react/components/button.tsx +26 -0
- package/frameworks/react/components/index.ts +5 -0
- package/frameworks/react/hooks/index.ts +5 -0
- package/frameworks/react/index.ts +9 -0
- package/frameworks/react/package.json +8 -0
- package/frameworks/react/utils/index.ts +2 -0
- package/frameworks/react-native/index.ts +9 -0
- package/frameworks/react-native/package.json +8 -0
- package/frameworks/registry.json +371 -0
- package/frameworks/setup.sh +69 -0
- package/frameworks/svelte/index.ts +9 -0
- package/frameworks/svelte/package.json +8 -0
- package/frameworks/tracker.json +1854 -0
- package/frameworks/vue/index.ts +9 -0
- package/frameworks/vue/package.json +8 -0
- package/helpers/file.ts +33 -0
- package/helpers/memoization.ts +40 -0
- package/package.json +49 -11
- package/primitives/accordion.tsx +74 -0
- package/primitives/action-button.tsx +42 -0
- package/primitives/alert-dialog.tsx +185 -0
- package/primitives/alert.tsx +74 -0
- package/primitives/apply-typography.tsx +55 -0
- package/primitives/aspect-ratio.tsx +5 -0
- package/primitives/avatar.tsx +57 -0
- package/primitives/background-beams.tsx +142 -0
- package/primitives/badge.tsx +45 -0
- package/primitives/breadcrumb.tsx +130 -0
- package/primitives/breakpoint-indicator.tsx +19 -0
- package/primitives/button.tsx +72 -0
- package/primitives/calendar.tsx +72 -0
- package/primitives/card.tsx +97 -0
- package/primitives/carousel.tsx +238 -0
- package/primitives/chat/chat-input-area.tsx +88 -0
- package/primitives/chat/chat-input.tsx +71 -0
- package/primitives/chat/files-preview.tsx +331 -0
- package/primitives/chat/index.ts +6 -0
- package/primitives/chat/json-form.tsx +8 -0
- package/primitives/chat/message-list.tsx +308 -0
- package/primitives/chat/message.tsx +569 -0
- package/primitives/chat/sqlite-preview.tsx +215 -0
- package/primitives/checkbox.tsx +32 -0
- package/primitives/collapsible.tsx +9 -0
- package/primitives/combobox.tsx +239 -0
- package/primitives/command.tsx +151 -0
- package/primitives/context-menu.tsx +206 -0
- package/primitives/copy-to-clipboard-icon.tsx +60 -0
- package/primitives/dialog-video-controller.tsx +38 -0
- package/primitives/dialog.tsx +128 -0
- package/primitives/dot-pattern.tsx +57 -0
- package/primitives/dots-loader.tsx +13 -0
- package/primitives/drawer.tsx +113 -0
- package/primitives/dropdown-menu.tsx +199 -0
- package/primitives/error-message.tsx +19 -0
- package/primitives/file-uploader.tsx +202 -0
- package/primitives/form.tsx +183 -0
- package/primitives/hover-card.tsx +28 -0
- package/primitives/icons/github.tsx +14 -0
- package/primitives/icons/index.ts +18 -0
- package/primitives/icons/youtube-logo.tsx +59 -0
- package/primitives/index-common.ts +304 -0
- package/primitives/index-next.ts +4 -0
- package/primitives/input-otp.tsx +65 -0
- package/primitives/input.tsx +126 -0
- package/primitives/label.tsx +21 -0
- package/primitives/list-adaptor.ts +12 -0
- package/primitives/list-box.tsx +74 -0
- package/primitives/loading-spinner.tsx +33 -0
- package/primitives/markdown-preview.tsx +612 -0
- package/primitives/mermaid.tsx +191 -0
- package/primitives/navigation-menu.tsx +147 -0
- package/primitives/next/image.tsx +91 -0
- package/primitives/next/index.ts +7 -0
- package/primitives/next/inline-icon.tsx +36 -0
- package/primitives/next/link-element.tsx +109 -0
- package/primitives/next/mdx-link.tsx +22 -0
- package/primitives/next/media-stack.tsx +52 -0
- package/primitives/next/nav-items.tsx +45 -0
- package/primitives/next/youtube-embed.tsx +83 -0
- package/primitives/pagination.tsx +117 -0
- package/primitives/popover.tsx +34 -0
- package/primitives/pretty-json-print.tsx +28 -0
- package/primitives/progress.tsx +27 -0
- package/primitives/prompt-textarea.tsx +72 -0
- package/primitives/qr-code.tsx +112 -0
- package/primitives/radio-group.tsx +42 -0
- package/primitives/resizable.tsx +47 -0
- package/primitives/scroll-area.tsx +57 -0
- package/primitives/search-input.tsx +66 -0
- package/primitives/select.tsx +122 -0
- package/primitives/separator.tsx +26 -0
- package/primitives/sheet.tsx +139 -0
- package/primitives/skeleton.tsx +18 -0
- package/primitives/slider.tsx +63 -0
- package/primitives/sonner.tsx +35 -0
- package/primitives/step-indicator.tsx +69 -0
- package/primitives/stepper.tsx +272 -0
- package/primitives/switch.tsx +27 -0
- package/primitives/table.tsx +105 -0
- package/primitives/tabs.tsx +50 -0
- package/primitives/text-area.tsx +26 -0
- package/primitives/text-link.tsx +25 -0
- package/primitives/textarea.tsx +62 -0
- package/primitives/textfield.tsx +76 -0
- package/primitives/toast.tsx +30 -0
- package/primitives/toggle-group.tsx +63 -0
- package/primitives/toggle.tsx +44 -0
- package/primitives/tooltip.tsx +47 -0
- package/primitives/video-player.tsx +23 -0
- package/src/button.ts +1 -0
- package/src/hooks/index.ts +7 -0
- package/src/hooks/use-click-away.ts +31 -0
- package/src/hooks/use-combined-refs.ts +22 -0
- package/src/hooks/use-copy-clipboard.ts +30 -0
- package/src/hooks/use-debounce.ts +17 -0
- package/src/hooks/use-fill-ids.ts +25 -0
- package/src/hooks/use-map.ts +26 -0
- package/src/hooks/use-measure.ts +42 -0
- package/src/hooks/use-reverse-video-playback.ts +43 -0
- package/src/hooks/use-scroll-restoration.ts +50 -0
- package/src/index-lean.ts +87 -0
- package/src/index.ts +54 -0
- package/src/mcp/README.md +141 -0
- package/src/mcp/enhanced-server.ts +1208 -0
- package/src/mcp/index.ts +518 -0
- package/src/mcp/package.json +10 -0
- package/src/registry/api.ts +164 -0
- package/src/registry/index.ts +60 -0
- package/src/registry/package.json +10 -0
- package/src/utils.ts +19 -0
- package/tailwind/colors.tailwind.js +53 -0
- package/tailwind/fontFamily.tailwind.ts +7 -0
- package/tailwind/fontSize.tailwind.ts +13 -0
- package/tailwind/index.ts +7 -0
- package/tailwind/safelist.tailwind.js +26 -0
- package/tailwind/screens.tailwind.js +8 -0
- package/tailwind/spacing.tailwind.js +65 -0
- package/tailwind/tailwind.config.hanzo-preset.d.ts +5 -0
- package/tailwind/tailwind.config.hanzo-preset.js +915 -0
- package/tailwind/tw-font-desc.ts +15 -0
- package/tailwind/typo-plugin/get-plugin-styles.js +679 -0
- package/tailwind/typo-plugin/index.d.ts +9 -0
- package/tailwind/typo-plugin/index.js +141 -0
- package/tailwind/typo-plugin/utils.js +60 -0
- package/tailwind/typography-test.mdx +35 -0
- package/tailwind/z-index.tailwind.js +71 -0
- package/types/animation-def.ts +3 -0
- package/types/breakpoints.ts +11 -0
- package/types/bullet-item.ts +10 -0
- package/types/button-def.ts +39 -0
- package/types/dimensions.ts +8 -0
- package/types/grid-def.ts +56 -0
- package/types/image-def.ts +32 -0
- package/types/index.ts +30 -0
- package/types/link-def.ts +56 -0
- package/types/media-stack-def.ts +31 -0
- package/types/t-shirt-size.ts +5 -0
- package/types/tshirt-dimensions.ts +20 -0
- package/types/video-def.ts +25 -0
- package/util/blob.ts +33 -0
- package/util/copy-to-clipboard.ts +17 -0
- package/util/create-shadow-root.ts +22 -0
- package/util/date.ts +84 -0
- package/util/debounce.ts +11 -0
- package/util/file.ts +15 -0
- package/util/format-and-abbreviate-as-currency.ts +125 -0
- package/util/format-text.ts +34 -0
- package/util/format-to-max-char.ts +68 -0
- package/util/index-client.ts +3 -0
- package/util/index.ts +112 -0
- package/util/number-abbreviate.ts +49 -0
- package/util/specifier.ts +43 -0
- package/util/spread-to-transform.ts +25 -0
- package/util/step-animation.ts +90 -0
- package/util/timing.ts +3 -0
- package/util/toasts.tsx +17 -0
- package/util/two-way-map.ts +19 -0
- package/dist/index.d.mts +0 -16
- package/dist/index.d.ts +0 -16
- package/dist/lib/utils.d.mts +0 -2
- package/dist/lib/utils.d.ts +0 -2
- package/dist/src/utils.d.mts +0 -7
- package/dist/src/utils.d.ts +0 -7
- package/dist/tailwind/index.d.mts +0 -2
- package/dist/tailwind/index.d.ts +0 -2
- package/dist/types/index.d.mts +0 -12
- 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
|
+
};
|