@sqlrooms/s3-browser 0.27.0 → 0.28.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md
CHANGED
|
@@ -30,6 +30,7 @@ yarn add @sqlrooms/s3-browser
|
|
|
30
30
|
import {useState} from 'react';
|
|
31
31
|
import {S3FileBrowser, S3CredentialsForm, S3State} from '@sqlrooms/s3-browser';
|
|
32
32
|
import {S3FileOrDirectory, S3Config, S3Connection} from '@sqlrooms/s3-utils';
|
|
33
|
+
import {Button} from '@sqlrooms/ui';
|
|
33
34
|
|
|
34
35
|
type S3BrowserProps = {
|
|
35
36
|
listS3Files: (args: {
|
|
@@ -115,9 +116,9 @@ export const S3Browser = ({
|
|
|
115
116
|
}}
|
|
116
117
|
onCanConfirmChange={() => {}}
|
|
117
118
|
/>
|
|
118
|
-
<
|
|
119
|
+
<Button disabled={!selectedFiles.length} onClick={handleLoadFiles}>
|
|
119
120
|
Add Selected
|
|
120
|
-
</
|
|
121
|
+
</Button>
|
|
121
122
|
</div>
|
|
122
123
|
)}
|
|
123
124
|
</div>
|
|
@@ -102,7 +102,7 @@ export function S3CredentialsForm({ onConnect, isLoading, saveS3Credentials, loa
|
|
|
102
102
|
return () => {
|
|
103
103
|
isMounted = false;
|
|
104
104
|
};
|
|
105
|
-
}, []);
|
|
105
|
+
}, [loadS3Credentials]);
|
|
106
106
|
const resolver = zodResolver(formSchema);
|
|
107
107
|
const form = useForm({
|
|
108
108
|
resolver,
|
|
@@ -189,9 +189,9 @@ export function S3CredentialsForm({ onConnect, isLoading, saveS3Credentials, loa
|
|
|
189
189
|
} }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "region", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Region" }), _jsx(FormControl, { children: _jsxs(Select, { onValueChange: (e) => {
|
|
190
190
|
field.onChange(e);
|
|
191
191
|
handleInputChange();
|
|
192
|
-
}, defaultValue: field.value, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Select a Region" }) }), _jsx(SelectContent, { children: S3_REGIONS.map((region) => (_jsx(SelectItem, { value: region.value, children: region.label }, region.value))) })] }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "name", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Connection Name (Optional)" }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: "text", placeholder: "My S3 Connection" }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "saveCredential", render: ({ field }) => (_jsxs(FormItem, { className: "flex items-center space-
|
|
192
|
+
}, defaultValue: field.value, children: [_jsx(SelectTrigger, { children: _jsx(SelectValue, { placeholder: "Select a Region" }) }), _jsx(SelectContent, { children: S3_REGIONS.map((region) => (_jsx(SelectItem, { value: region.value, children: region.label }, region.value))) })] }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "name", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Connection Name (Optional)" }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: "text", placeholder: "My S3 Connection" }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "saveCredential", render: ({ field }) => (_jsxs(FormItem, { className: "flex items-center space-y-0 space-x-2", children: [_jsx(FormControl, { children: _jsx(Checkbox, { onCheckedChange: field.onChange, checked: field.value, className: "h-4 w-4" }) }), _jsx(FormLabel, { children: "Save credential" }), _jsxs(Tooltip, { children: [_jsx(TooltipTrigger, { asChild: true, children: _jsx(Info, { size: 16, className: "text-muted-foreground hover:cursor-pointer" }) }), _jsx(TooltipContent, { side: "bottom", children: _jsx("pre", { className: "w-[300px] text-xs text-wrap wrap-break-word", children: "Save this credential securely on your computer for future use. Credentials will be encrypted and stored locally" }) })] }), _jsx(FormMessage, {})] })) }), notification.show && (_jsxs("div", { className: `mb-4 flex items-center gap-2 rounded-lg p-3 text-sm ${notification.type === 'error'
|
|
193
193
|
? 'text-destructive-foreground bg-destructive'
|
|
194
|
-
: 'bg-green-50 text-green-700'}`, children: [notification.type === 'error' ? (_jsx(AlertCircle, { size: 16 })) : (_jsx(Check, { size: 16 })), notification.message] }))] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm font-medium
|
|
194
|
+
: 'bg-green-50 text-green-700'}`, children: [notification.type === 'error' ? (_jsx(AlertCircle, { size: 16 })) : (_jsx(Check, { size: 16 })), notification.message] }))] }), _jsxs("div", { className: "flex flex-col gap-4", children: [_jsxs("div", { className: "space-y-2", children: [_jsx("label", { className: "text-sm leading-none font-medium", children: "Auto-fill Credentials" }), _jsx(Textarea, { value: pasteText, onChange: (e) => setPasteText(e.target.value), placeholder: 'Paste AWS cli command output here...\n\nExample export:\nexport AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE\nexport AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/b...\nexport AWS_SESSION_TOKEN=AQoEXAMPLEH4aoAH0gNCAPy...\n\nExample credential_process output:\n{\n "AccessKeyId": "AKIAIOSFODNN7EXAMPLE",\n "SecretAccessKey": "wJalrXUtnFEMI/K7MDENG/bPxRfiCY...",\n "SessionToken": "AQoEXAMPLEH4aoAH0gNCAPy..."\n}', className: "h-40" })] }), _jsxs("div", { className: "flex gap-3", children: [_jsxs(Button, { variant: "secondary", onClick: (e) => {
|
|
195
195
|
e.preventDefault();
|
|
196
196
|
handleAutofill();
|
|
197
197
|
}, children: [_jsx(Upload, { size: 16 }), "Auto-fill Credentials"] }), _jsx(Button, { variant: "outline", onClick: () => setPasteText(''), children: "Clear" })] }), _jsx(FormField, { control: form.control, name: "accessKeyId", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Access Key Id" }), _jsx(FormControl, { children: _jsx(Input, { ...field, type: "text", className: "w-full rounded-md border px-3 py-2", placeholder: "AKIAXXXXXXXXXXXXXXXX", onChange: (e) => {
|
|
@@ -200,9 +200,9 @@ export function S3CredentialsForm({ onConnect, isLoading, saveS3Credentials, loa
|
|
|
200
200
|
} }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "secretAccessKey", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Secret Access Key" }), _jsx(FormControl, { children: _jsxs("div", { className: "relative", children: [_jsx(Input, { ...field, type: showSecrets.secretAccessKey ? 'text' : 'password', placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: "pr-8", onChange: (e) => {
|
|
201
201
|
field.onChange(e);
|
|
202
202
|
handleInputChange();
|
|
203
|
-
} }), _jsx("div", { className: "text-muted-foreground absolute right-3
|
|
203
|
+
} }), _jsx("div", { className: "text-muted-foreground absolute top-0 right-3 flex h-9 gap-1", children: _jsx("button", { type: "button", onClick: () => toggleVisibility('secretAccessKey'), className: "ml-1", children: showSecrets.secretAccessKey ? (_jsx(EyeOff, { size: 16 })) : (_jsx(Eye, { size: 16 })) }) })] }) }), _jsx(FormMessage, {})] })) }), _jsx(FormField, { control: form.control, name: "sessionToken", render: ({ field }) => (_jsxs(FormItem, { children: [_jsx(FormLabel, { children: "Session Token (Optional - for temporary credentials)" }), _jsx(FormControl, { children: _jsxs("div", { className: "relative", children: [_jsx(Input, { ...field, type: showSecrets.sessionToken ? 'text' : 'password', placeholder: "\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022\u2022", className: "pr-8", onChange: (e) => {
|
|
204
204
|
field.onChange(e);
|
|
205
205
|
handleInputChange();
|
|
206
|
-
} }), _jsx("div", { className: "text-muted-foreground absolute right-3
|
|
206
|
+
} }), _jsx("div", { className: "text-muted-foreground absolute top-0 right-3 flex h-9 gap-1", children: _jsx("button", { type: "button", onClick: () => toggleVisibility('sessionToken'), className: "ml-1", children: showSecrets.sessionToken ? (_jsx(EyeOff, { size: 16 })) : (_jsx(Eye, { size: 16 })) }) })] }) }), _jsx(FormMessage, {})] })) }), _jsx(Button, { type: "submit", disabled: form.formState.isSubmitting || !form.formState.isValid, className: "w-full", children: isLoading ? 'Connecting...' : 'Connect to S3' })] })] }) }) }) }), _jsx(TabsContent, { value: "saved", className: "mt-0 w-full", children: _jsx("div", { className: "space-y-4", children: savedCredentials.length === 0 ? (_jsxs("div", { className: "flex flex-col items-center justify-between py-12", children: [_jsx(Database, { className: "text-muted-foreground mx-auto mb-4 h-8 w-8" }), _jsx("h3", { className: "mb-2 text-lg font-medium", children: "No saved credentials" }), _jsx("p", { className: "text-muted-foreground", children: "Create your first credential to get started" })] })) : (_jsx("div", { className: "grid gap-4", children: savedCredentials.map((credential) => (_jsx("div", { className: "rounded-lg border p-4 transition-shadow hover:cursor-pointer hover:shadow-sm", children: _jsxs("div", { className: "flex items-start justify-between", children: [_jsxs("div", { className: "flex-1", children: [_jsx("h3", { className: "mb-1 font-semibold", children: credential.name }), _jsx("p", { className: "text-muted-foreground mb-2 text-sm", children: credential.bucket }), _jsxs("div", { className: "text-muted-foreground flex items-center space-x-4 text-xs", children: [_jsxs("span", { children: ["Region: ", credential.region] }), _jsxs("span", { children: ["Created:", ' ', new Date(credential.createdAt).toLocaleDateString()] })] })] }), _jsxs("div", { className: "flex space-x-2", children: [_jsx("button", { onClick: () => onConnect(credential), className: "text-primary rounded-md bg-blue-100 px-3 py-1 text-sm hover:bg-blue-200", children: "Connect" }), deleteCredential ? (_jsx("button", { onClick: () => deleteCredential(credential.id), className: "rounded-md bg-red-100 px-3 py-1 text-sm text-red-700 hover:bg-red-200", children: _jsx(Trash2, { className: "h-4 w-4" }) })) : null] })] }) }, credential.id))) })) }) })] }));
|
|
207
207
|
}
|
|
208
208
|
//# sourceMappingURL=S3CredentialsForm.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"S3CredentialsForm.js","sourceRoot":"","sources":["../src/S3CredentialsForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useState, useCallback, useEffect} from 'react';\nimport {useForm, SubmitHandler} from 'react-hook-form';\nimport {zodResolver} from '@hookform/resolvers/zod';\nimport * as z from 'zod';\nimport {Tabs, TabsList, TabsTrigger, TabsContent} from '@sqlrooms/ui';\nimport {S3Config, S3Credentials} from '@sqlrooms/s3-browser-config';\n\nimport {\n Input,\n Textarea,\n Button,\n Checkbox,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n} from '@sqlrooms/ui';\n\nimport {\n Eye,\n EyeOff,\n Upload,\n Check,\n AlertCircle,\n Info,\n PlusIcon,\n Database,\n Trash2,\n} from 'lucide-react';\n\nconst S3_REGIONS = [\n {value: 'us-east-1', label: 'us-east-1'},\n {value: 'us-east-2', label: 'us-east-2'},\n {value: 'us-west-1', label: 'us-west-1'},\n {value: 'us-west-2', label: 'us-west-2'},\n {value: 'eu-west-1', label: 'eu-west-1'},\n {value: 'eu-central-1', label: 'eu-central-1'},\n {value: 'ap-northeast-1', label: 'ap-northeast-1'},\n {value: 'ap-southeast-1', label: 'ap-southeast-1'},\n];\n\nconst formSchema = z.object({\n accessKeyId: z.string().min(1, 'Required'),\n secretAccessKey: z.string().min(1, 'Required'),\n sessionToken: z.string().optional(),\n region: z.string().min(1, 'Required'),\n bucket: z.string().min(1, 'Required'),\n name: z.string().max(100, 'Max 100 characters').optional(),\n saveCredential: z.boolean().default(false).optional(),\n});\n\ntype FormData = z.infer<typeof formSchema>;\n\ntype ParsedType = {\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n region?: string;\n};\n\nconst parseAWSExport = (text: string): ParsedType => {\n const lines = text.split('\\n');\n const parsed = {} as Record<string, string>;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith('export AWS_ACCESS_KEY_ID=')) {\n parsed.accessKeyId = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_SECRET_ACCESS_KEY=')) {\n parsed.secretAccessKey =\n trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_SESSION_TOKEN=')) {\n parsed.sessionToken = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_DEFAULT_REGION=')) {\n parsed.region = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n }\n }\n\n return parsed;\n};\n\nconst parseCredentialProcess = (text: string): ParsedType => {\n try {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const jsonStr = jsonMatch[0];\n const raw = JSON.parse(jsonStr);\n const parsed = {} as Record<string, string>;\n\n if (raw.AccessKeyId) {\n parsed.accessKeyId = raw.AccessKeyId;\n }\n if (raw.SecretAccessKey) {\n parsed.secretAccessKey = raw.SecretAccessKey;\n }\n if (raw.SessionToken) {\n parsed.sessionToken = raw.SessionToken;\n }\n if (raw.region) {\n // Check if region is present\n parsed.region = raw.region;\n }\n return parsed;\n }\n } catch (e) {\n console.error('Failed to parse JSON:', e);\n }\n return {};\n};\n\n/**\n * A form component for managing S3 credentials and connections.\n *\n * This component provides:\n * - Input fields for S3 credentials (access key, secret key, region, bucket)\n * - Option to save credentials for later use\n * - Ability to paste AWS credentials export format\n * - Management of saved credentials\n *\n * @example\n * ```tsx\n * const handleConnect = async (credentials) => {\n * // Handle the connection\n * console.log('Connecting with:', credentials);\n * };\n *\n * const handleSaveCredential = async (config) => {\n * // Save the credential to your storage\n * await saveToStorage(config);\n * };\n *\n * const handleLoadCredentials = async () => {\n * // Load saved credentials from your storage\n * return await loadFromStorage();\n * };\n *\n * const handleDeleteCredential = async (id) => {\n * // Delete a saved credential\n * await deleteFromStorage(id);\n * };\n *\n * return (\n * <S3CredentialsForm\n * onConnect={handleConnect}\n * isLoading={false}\n * saveS3Credentials={handleSaveCredential}\n * loadS3Credentials={handleLoadCredentials}\n * deleteS3Credentials={handleDeleteCredential}\n * />\n * );\n * ```\n */\nexport type S3CredentialsFormProps = {\n onConnect: (data: FormData) => void;\n isLoading?: boolean;\n saveS3Credentials: (data: S3Config) => Promise<void>;\n loadS3Credentials: () => Promise<S3Credentials[]>;\n deleteS3Credentials?: (id: string) => Promise<void>;\n onInputChange?: () => void;\n};\n\nexport function S3CredentialsForm({\n onConnect,\n isLoading,\n saveS3Credentials,\n loadS3Credentials,\n deleteS3Credentials,\n onInputChange,\n}: S3CredentialsFormProps) {\n const [showSecrets, setShowSecrets] = useState({\n secretAccessKey: false,\n sessionToken: false,\n });\n const [pasteText, setPasteText] = useState('');\n const [notification, setNotification] = useState({\n show: false,\n message: '',\n type: 'success',\n });\n const [savedCredentials, setSavedCredentials] = useState<S3Credentials[]>([]);\n const [tab, setTab] = useState('new');\n\n // Load saved connections on component mount\n useEffect(() => {\n let isMounted = true;\n async function fetchConnections() {\n // You can await here\n const response = await loadS3Credentials();\n if (response && response.length > 0 && isMounted) {\n // Assuming loadS3Credentials returns an array of connections\n setSavedCredentials(response);\n }\n }\n fetchConnections();\n return () => {\n isMounted = false;\n };\n }, []);\n\n const resolver = zodResolver(formSchema);\n const form = useForm<FormData>({\n resolver,\n defaultValues: {\n accessKeyId: '',\n secretAccessKey: '',\n sessionToken: '',\n region: 'us-east-1',\n bucket: '',\n saveCredential: false,\n },\n });\n const toggleVisibility = useCallback(\n (field: 'secretAccessKey' | 'sessionToken') => {\n setShowSecrets((prev) => ({\n ...prev,\n [field]: !prev[field],\n }));\n },\n [setShowSecrets],\n );\n const showNotification = useCallback(\n (message: string, type = 'success') => {\n setNotification({show: true, message, type});\n setTimeout(\n () => setNotification({show: false, message: '', type: 'success'}),\n 3000,\n );\n },\n [setNotification],\n );\n\n const handleAutofill = useCallback(() => {\n if (!pasteText.trim()) {\n showNotification('Please paste in aws credential first', 'error');\n return;\n }\n\n let parsed = null;\n\n // Try parsing as export format first\n if (pasteText.includes('export AWS_')) {\n parsed = parseAWSExport(pasteText);\n }\n // Try parsing as credential_process JSON format\n else if (pasteText.includes('AccessKeyId')) {\n parsed = parseCredentialProcess(pasteText);\n }\n\n if (parsed) {\n for (const key in parsed) {\n if (key in parsed) {\n form.setValue(\n key as keyof ParsedType,\n parsed[key as keyof ParsedType] || '',\n );\n }\n }\n\n showNotification('Credentials auto-filled successfully!');\n setPasteText('');\n onInputChange?.();\n } else {\n showNotification(\n 'Could not parse credentials. Please check the format.',\n 'error',\n );\n }\n }, [pasteText, form, showNotification, onInputChange]);\n\n const handleSubmit: SubmitHandler<FormData> = useCallback(\n async (data: FormData) => {\n onConnect(data);\n if (data.saveCredential) {\n try {\n await saveS3Credentials({\n accessKeyId: data.accessKeyId,\n secretAccessKey: data.secretAccessKey,\n sessionToken: data.sessionToken || undefined,\n region: data.region,\n bucket: data.bucket,\n name: data.name || `${data.bucket}-${data.region}`,\n });\n showNotification('Connection saved successfully!');\n } catch (err) {\n showNotification(`Error saving to S3: ${err}`, 'error');\n }\n }\n },\n [onConnect, saveS3Credentials, showNotification],\n );\n\n const deleteCredential = useCallback(\n async (id: string) => {\n if (deleteS3Credentials) {\n await deleteS3Credentials(id);\n const credentials = await loadS3Credentials();\n setSavedCredentials(credentials);\n }\n },\n [deleteS3Credentials, setSavedCredentials, loadS3Credentials],\n );\n\n // Add a handler for input changes\n const handleInputChange = useCallback(() => {\n onInputChange?.();\n }, [onInputChange]);\n\n return (\n <Tabs\n value={tab}\n onValueChange={setTab}\n orientation=\"vertical\"\n defaultValue=\"new\"\n className=\"flex w-full gap-6 px-4\"\n >\n <TabsList className=\"mb-4 flex h-auto flex-col items-stretch justify-start gap-4 border-r bg-transparent pr-6\">\n <TabsTrigger\n value=\"new\"\n className=\"data-[state=active]:border-primary data-[state=active]:text-primary rounded-md border px-4 py-2 data-[state=active]:shadow-none\"\n >\n <PlusIcon size={16} className=\"mr-2\" />\n New Credential\n </TabsTrigger>\n <TabsTrigger\n value=\"saved\"\n className=\"data-[state=active]:border-primary data-[state=active]:text-primary rounded-md border px-4 py-2 data-[state=active]:shadow-none\"\n >\n <Database size={16} className=\"mr-2\" />\n Saved Credential\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"new\" className=\"mt-0 w-full\">\n <Form<FormData> {...form}>\n <form onSubmit={form.handleSubmit(handleSubmit)} className=\"w-full\">\n <div className=\"grid h-full w-full grid-cols-[240px_1fr] gap-8\">\n <div className=\"flex flex-col gap-4\">\n <FormField<FormData, 'bucket'>\n control={form.control}\n name=\"bucket\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Bucket Name</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n placeholder=\"my-bucket-name\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'region'>\n control={form.control}\n name=\"region\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Region</FormLabel>\n <FormControl>\n <Select\n onValueChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n defaultValue={field.value}\n >\n <SelectTrigger>\n <SelectValue placeholder=\"Select a Region\" />\n </SelectTrigger>\n <SelectContent>\n {S3_REGIONS.map((region) => (\n <SelectItem\n key={region.value}\n value={region.value}\n >\n {region.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'name'>\n control={form.control}\n name=\"name\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Connection Name (Optional)</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n placeholder=\"My S3 Connection\"\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'saveCredential'>\n control={form.control}\n name=\"saveCredential\"\n render={({field}) => (\n <FormItem className=\"flex items-center space-x-2 space-y-0\">\n <FormControl>\n <Checkbox\n onCheckedChange={field.onChange}\n checked={field.value}\n className=\"h-4 w-4\"\n />\n </FormControl>\n <FormLabel>Save credential</FormLabel>\n <Tooltip>\n <TooltipTrigger asChild>\n <Info\n size={16}\n className=\"text-muted-foreground hover:cursor-pointer\"\n />\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">\n <pre className=\"w-[300px] text-wrap break-words text-xs\">\n Save this credential securely on your computer for\n future use. Credentials will be encrypted and stored\n locally\n </pre>\n </TooltipContent>\n </Tooltip>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {notification.show && (\n <div\n className={`mb-4 flex items-center gap-2 rounded-lg p-3 text-sm ${\n notification.type === 'error'\n ? 'text-destructive-foreground bg-destructive'\n : 'bg-green-50 text-green-700'\n }`}\n >\n {notification.type === 'error' ? (\n <AlertCircle size={16} />\n ) : (\n <Check size={16} />\n )}\n {notification.message}\n </div>\n )}\n </div>\n\n {/* From Section */}\n <div className=\"flex flex-col gap-4\">\n {/* Auto-fill Section */}\n <div className=\"space-y-2\">\n <label className=\"text-sm font-medium leading-none\">\n Auto-fill Credentials\n </label>\n <Textarea\n value={pasteText}\n onChange={(e) => setPasteText(e.target.value)}\n placeholder='Paste AWS cli command output here... Example export: export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/b... export AWS_SESSION_TOKEN=AQoEXAMPLEH4aoAH0gNCAPy... Example credential_process output: { \"AccessKeyId\": \"AKIAIOSFODNN7EXAMPLE\", \"SecretAccessKey\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCY...\", \"SessionToken\": \"AQoEXAMPLEH4aoAH0gNCAPy...\" }'\n className=\"h-40\"\n />\n </div>\n <div className=\"flex gap-3\">\n <Button\n variant=\"secondary\"\n onClick={(e) => {\n e.preventDefault();\n handleAutofill();\n }}\n >\n <Upload size={16} />\n Auto-fill Credentials\n </Button>\n <Button variant=\"outline\" onClick={() => setPasteText('')}>\n Clear\n </Button>\n </div>\n <FormField<FormData, 'accessKeyId'>\n control={form.control}\n name=\"accessKeyId\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Access Key Id</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2\"\n placeholder=\"AKIAXXXXXXXXXXXXXXXX\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'secretAccessKey'>\n control={form.control}\n name=\"secretAccessKey\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Secret Access Key</FormLabel>\n <FormControl>\n <div className=\"relative\">\n <Input\n {...field}\n type={\n showSecrets.secretAccessKey ? 'text' : 'password'\n }\n placeholder=\"••••••••••••••••••••••••\"\n className=\"pr-8\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n <div className=\"text-muted-foreground absolute right-3 top-0 flex h-9 gap-1\">\n <button\n type=\"button\"\n onClick={() =>\n toggleVisibility('secretAccessKey')\n }\n className=\"ml-1\"\n >\n {showSecrets.secretAccessKey ? (\n <EyeOff size={16} />\n ) : (\n <Eye size={16} />\n )}\n </button>\n </div>\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField<FormData, 'sessionToken'>\n control={form.control}\n name=\"sessionToken\"\n render={({field}) => (\n <FormItem>\n <FormLabel>\n Session Token (Optional - for temporary credentials)\n </FormLabel>\n <FormControl>\n <div className=\"relative\">\n <Input\n {...field}\n type={\n showSecrets.sessionToken ? 'text' : 'password'\n }\n placeholder=\"••••••••••••••••••••••••\"\n className=\"pr-8\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n <div className=\"text-muted-foreground absolute right-3 top-0 flex h-9 gap-1\">\n <button\n type=\"button\"\n onClick={() => toggleVisibility('sessionToken')}\n className=\"ml-1\"\n >\n {showSecrets.sessionToken ? (\n <EyeOff size={16} />\n ) : (\n <Eye size={16} />\n )}\n </button>\n </div>\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button\n type=\"submit\"\n disabled={\n form.formState.isSubmitting || !form.formState.isValid\n }\n className=\"w-full\"\n >\n {isLoading ? 'Connecting...' : 'Connect to S3'}\n </Button>\n </div>\n </div>\n </form>\n </Form>\n </TabsContent>\n <TabsContent value=\"saved\" className=\"mt-0 w-full\">\n <div className=\"space-y-4\">\n {savedCredentials.length === 0 ? (\n <div className=\"flex flex-col items-center justify-between py-12\">\n <Database className=\"text-muted-foreground mx-auto mb-4 h-8 w-8\" />\n <h3 className=\"mb-2 text-lg font-medium\">No saved credentials</h3>\n <p className=\"text-muted-foreground\">\n Create your first credential to get started\n </p>\n </div>\n ) : (\n <div className=\"grid gap-4\">\n {savedCredentials.map((credential) => (\n <div\n key={credential.id}\n className=\"rounded-lg border p-4 transition-shadow hover:cursor-pointer hover:shadow-sm\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <h3 className=\"mb-1 font-semibold\">{credential.name}</h3>\n <p className=\"text-muted-foreground mb-2 text-sm\">\n {credential.bucket}\n </p>\n <div className=\"text-muted-foreground flex items-center space-x-4 text-xs\">\n <span>Region: {credential.region}</span>\n <span>\n Created:{' '}\n {new Date(credential.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n <div className=\"flex space-x-2\">\n <button\n onClick={() => onConnect(credential)}\n className=\"text-primary rounded-md bg-blue-100 px-3 py-1 text-sm hover:bg-blue-200\"\n >\n Connect\n </button>\n {deleteCredential ? (\n <button\n onClick={() => deleteCredential(credential.id)}\n className=\"rounded-md bg-red-100 px-3 py-1 text-sm text-red-700 hover:bg-red-200\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </TabsContent>\n </Tabs>\n );\n}\n"]}
|
|
1
|
+
{"version":3,"file":"S3CredentialsForm.js","sourceRoot":"","sources":["../src/S3CredentialsForm.tsx"],"names":[],"mappings":"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","sourcesContent":["import React, {useState, useCallback, useEffect} from 'react';\nimport {useForm, SubmitHandler} from 'react-hook-form';\nimport {zodResolver} from '@hookform/resolvers/zod';\nimport * as z from 'zod';\nimport {Tabs, TabsList, TabsTrigger, TabsContent} from '@sqlrooms/ui';\nimport {S3Config, S3Credentials} from '@sqlrooms/s3-browser-config';\n\nimport {\n Input,\n Textarea,\n Button,\n Checkbox,\n Form,\n FormControl,\n FormField,\n FormItem,\n FormLabel,\n FormMessage,\n Select,\n SelectTrigger,\n SelectValue,\n SelectContent,\n SelectItem,\n Tooltip,\n TooltipTrigger,\n TooltipContent,\n} from '@sqlrooms/ui';\n\nimport {\n Eye,\n EyeOff,\n Upload,\n Check,\n AlertCircle,\n Info,\n PlusIcon,\n Database,\n Trash2,\n} from 'lucide-react';\n\nconst S3_REGIONS = [\n {value: 'us-east-1', label: 'us-east-1'},\n {value: 'us-east-2', label: 'us-east-2'},\n {value: 'us-west-1', label: 'us-west-1'},\n {value: 'us-west-2', label: 'us-west-2'},\n {value: 'eu-west-1', label: 'eu-west-1'},\n {value: 'eu-central-1', label: 'eu-central-1'},\n {value: 'ap-northeast-1', label: 'ap-northeast-1'},\n {value: 'ap-southeast-1', label: 'ap-southeast-1'},\n];\n\nconst formSchema = z.object({\n accessKeyId: z.string().min(1, 'Required'),\n secretAccessKey: z.string().min(1, 'Required'),\n sessionToken: z.string().optional(),\n region: z.string().min(1, 'Required'),\n bucket: z.string().min(1, 'Required'),\n name: z.string().max(100, 'Max 100 characters').optional(),\n saveCredential: z.boolean().default(false).optional(),\n});\n\ntype FormData = z.infer<typeof formSchema>;\n\ntype ParsedType = {\n accessKeyId?: string;\n secretAccessKey?: string;\n sessionToken?: string;\n region?: string;\n};\n\nconst parseAWSExport = (text: string): ParsedType => {\n const lines = text.split('\\n');\n const parsed = {} as Record<string, string>;\n\n for (const line of lines) {\n const trimmed = line.trim();\n if (trimmed.startsWith('export AWS_ACCESS_KEY_ID=')) {\n parsed.accessKeyId = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_SECRET_ACCESS_KEY=')) {\n parsed.secretAccessKey =\n trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_SESSION_TOKEN=')) {\n parsed.sessionToken = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n } else if (trimmed.startsWith('export AWS_DEFAULT_REGION=')) {\n parsed.region = trimmed.split('=')[1]?.replace(/['\"]/g, '') || '';\n }\n }\n\n return parsed;\n};\n\nconst parseCredentialProcess = (text: string): ParsedType => {\n try {\n const jsonMatch = text.match(/\\{[\\s\\S]*\\}/);\n if (jsonMatch) {\n const jsonStr = jsonMatch[0];\n const raw = JSON.parse(jsonStr);\n const parsed = {} as Record<string, string>;\n\n if (raw.AccessKeyId) {\n parsed.accessKeyId = raw.AccessKeyId;\n }\n if (raw.SecretAccessKey) {\n parsed.secretAccessKey = raw.SecretAccessKey;\n }\n if (raw.SessionToken) {\n parsed.sessionToken = raw.SessionToken;\n }\n if (raw.region) {\n // Check if region is present\n parsed.region = raw.region;\n }\n return parsed;\n }\n } catch (e) {\n console.error('Failed to parse JSON:', e);\n }\n return {};\n};\n\n/**\n * A form component for managing S3 credentials and connections.\n *\n * This component provides:\n * - Input fields for S3 credentials (access key, secret key, region, bucket)\n * - Option to save credentials for later use\n * - Ability to paste AWS credentials export format\n * - Management of saved credentials\n *\n * @example\n * ```tsx\n * const handleConnect = async (credentials) => {\n * // Handle the connection\n * console.log('Connecting with:', credentials);\n * };\n *\n * const handleSaveCredential = async (config) => {\n * // Save the credential to your storage\n * await saveToStorage(config);\n * };\n *\n * const handleLoadCredentials = async () => {\n * // Load saved credentials from your storage\n * return await loadFromStorage();\n * };\n *\n * const handleDeleteCredential = async (id) => {\n * // Delete a saved credential\n * await deleteFromStorage(id);\n * };\n *\n * return (\n * <S3CredentialsForm\n * onConnect={handleConnect}\n * isLoading={false}\n * saveS3Credentials={handleSaveCredential}\n * loadS3Credentials={handleLoadCredentials}\n * deleteS3Credentials={handleDeleteCredential}\n * />\n * );\n * ```\n */\nexport type S3CredentialsFormProps = {\n onConnect: (data: FormData) => void;\n isLoading?: boolean;\n saveS3Credentials: (data: S3Config) => Promise<void>;\n loadS3Credentials: () => Promise<S3Credentials[]>;\n deleteS3Credentials?: (id: string) => Promise<void>;\n onInputChange?: () => void;\n};\n\nexport function S3CredentialsForm({\n onConnect,\n isLoading,\n saveS3Credentials,\n loadS3Credentials,\n deleteS3Credentials,\n onInputChange,\n}: S3CredentialsFormProps) {\n const [showSecrets, setShowSecrets] = useState({\n secretAccessKey: false,\n sessionToken: false,\n });\n const [pasteText, setPasteText] = useState('');\n const [notification, setNotification] = useState({\n show: false,\n message: '',\n type: 'success',\n });\n const [savedCredentials, setSavedCredentials] = useState<S3Credentials[]>([]);\n const [tab, setTab] = useState('new');\n\n // Load saved connections on component mount\n useEffect(() => {\n let isMounted = true;\n async function fetchConnections() {\n // You can await here\n const response = await loadS3Credentials();\n if (response && response.length > 0 && isMounted) {\n // Assuming loadS3Credentials returns an array of connections\n setSavedCredentials(response);\n }\n }\n fetchConnections();\n return () => {\n isMounted = false;\n };\n }, [loadS3Credentials]);\n\n const resolver = zodResolver(formSchema);\n const form = useForm<FormData>({\n resolver,\n defaultValues: {\n accessKeyId: '',\n secretAccessKey: '',\n sessionToken: '',\n region: 'us-east-1',\n bucket: '',\n saveCredential: false,\n },\n });\n const toggleVisibility = useCallback(\n (field: 'secretAccessKey' | 'sessionToken') => {\n setShowSecrets((prev) => ({\n ...prev,\n [field]: !prev[field],\n }));\n },\n [setShowSecrets],\n );\n const showNotification = useCallback(\n (message: string, type = 'success') => {\n setNotification({show: true, message, type});\n setTimeout(\n () => setNotification({show: false, message: '', type: 'success'}),\n 3000,\n );\n },\n [setNotification],\n );\n\n const handleAutofill = useCallback(() => {\n if (!pasteText.trim()) {\n showNotification('Please paste in aws credential first', 'error');\n return;\n }\n\n let parsed = null;\n\n // Try parsing as export format first\n if (pasteText.includes('export AWS_')) {\n parsed = parseAWSExport(pasteText);\n }\n // Try parsing as credential_process JSON format\n else if (pasteText.includes('AccessKeyId')) {\n parsed = parseCredentialProcess(pasteText);\n }\n\n if (parsed) {\n for (const key in parsed) {\n if (key in parsed) {\n form.setValue(\n key as keyof ParsedType,\n parsed[key as keyof ParsedType] || '',\n );\n }\n }\n\n showNotification('Credentials auto-filled successfully!');\n setPasteText('');\n onInputChange?.();\n } else {\n showNotification(\n 'Could not parse credentials. Please check the format.',\n 'error',\n );\n }\n }, [pasteText, form, showNotification, onInputChange]);\n\n const handleSubmit: SubmitHandler<FormData> = useCallback(\n async (data: FormData) => {\n onConnect(data);\n if (data.saveCredential) {\n try {\n await saveS3Credentials({\n accessKeyId: data.accessKeyId,\n secretAccessKey: data.secretAccessKey,\n sessionToken: data.sessionToken || undefined,\n region: data.region,\n bucket: data.bucket,\n name: data.name || `${data.bucket}-${data.region}`,\n });\n showNotification('Connection saved successfully!');\n } catch (err) {\n showNotification(`Error saving to S3: ${err}`, 'error');\n }\n }\n },\n [onConnect, saveS3Credentials, showNotification],\n );\n\n const deleteCredential = useCallback(\n async (id: string) => {\n if (deleteS3Credentials) {\n await deleteS3Credentials(id);\n const credentials = await loadS3Credentials();\n setSavedCredentials(credentials);\n }\n },\n [deleteS3Credentials, setSavedCredentials, loadS3Credentials],\n );\n\n // Add a handler for input changes\n const handleInputChange = useCallback(() => {\n onInputChange?.();\n }, [onInputChange]);\n\n return (\n <Tabs\n value={tab}\n onValueChange={setTab}\n orientation=\"vertical\"\n defaultValue=\"new\"\n className=\"flex w-full gap-6 px-4\"\n >\n <TabsList className=\"mb-4 flex h-auto flex-col items-stretch justify-start gap-4 border-r bg-transparent pr-6\">\n <TabsTrigger\n value=\"new\"\n className=\"data-[state=active]:border-primary data-[state=active]:text-primary rounded-md border px-4 py-2 data-[state=active]:shadow-none\"\n >\n <PlusIcon size={16} className=\"mr-2\" />\n New Credential\n </TabsTrigger>\n <TabsTrigger\n value=\"saved\"\n className=\"data-[state=active]:border-primary data-[state=active]:text-primary rounded-md border px-4 py-2 data-[state=active]:shadow-none\"\n >\n <Database size={16} className=\"mr-2\" />\n Saved Credential\n </TabsTrigger>\n </TabsList>\n <TabsContent value=\"new\" className=\"mt-0 w-full\">\n <Form<FormData> {...form}>\n <form onSubmit={form.handleSubmit(handleSubmit)} className=\"w-full\">\n <div className=\"grid h-full w-full grid-cols-[240px_1fr] gap-8\">\n <div className=\"flex flex-col gap-4\">\n <FormField<FormData, 'bucket'>\n control={form.control}\n name=\"bucket\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Bucket Name</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n placeholder=\"my-bucket-name\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'region'>\n control={form.control}\n name=\"region\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Region</FormLabel>\n <FormControl>\n <Select\n onValueChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n defaultValue={field.value}\n >\n <SelectTrigger>\n <SelectValue placeholder=\"Select a Region\" />\n </SelectTrigger>\n <SelectContent>\n {S3_REGIONS.map((region) => (\n <SelectItem\n key={region.value}\n value={region.value}\n >\n {region.label}\n </SelectItem>\n ))}\n </SelectContent>\n </Select>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'name'>\n control={form.control}\n name=\"name\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Connection Name (Optional)</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n placeholder=\"My S3 Connection\"\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'saveCredential'>\n control={form.control}\n name=\"saveCredential\"\n render={({field}) => (\n <FormItem className=\"flex items-center space-y-0 space-x-2\">\n <FormControl>\n <Checkbox\n onCheckedChange={field.onChange}\n checked={field.value}\n className=\"h-4 w-4\"\n />\n </FormControl>\n <FormLabel>Save credential</FormLabel>\n <Tooltip>\n <TooltipTrigger asChild>\n <Info\n size={16}\n className=\"text-muted-foreground hover:cursor-pointer\"\n />\n </TooltipTrigger>\n <TooltipContent side=\"bottom\">\n <pre className=\"w-[300px] text-xs text-wrap wrap-break-word\">\n Save this credential securely on your computer for\n future use. Credentials will be encrypted and stored\n locally\n </pre>\n </TooltipContent>\n </Tooltip>\n <FormMessage />\n </FormItem>\n )}\n />\n\n {notification.show && (\n <div\n className={`mb-4 flex items-center gap-2 rounded-lg p-3 text-sm ${\n notification.type === 'error'\n ? 'text-destructive-foreground bg-destructive'\n : 'bg-green-50 text-green-700'\n }`}\n >\n {notification.type === 'error' ? (\n <AlertCircle size={16} />\n ) : (\n <Check size={16} />\n )}\n {notification.message}\n </div>\n )}\n </div>\n\n {/* From Section */}\n <div className=\"flex flex-col gap-4\">\n {/* Auto-fill Section */}\n <div className=\"space-y-2\">\n <label className=\"text-sm leading-none font-medium\">\n Auto-fill Credentials\n </label>\n <Textarea\n value={pasteText}\n onChange={(e) => setPasteText(e.target.value)}\n placeholder='Paste AWS cli command output here... Example export: export AWS_ACCESS_KEY_ID=AKIAIOSFODNN7EXAMPLE export AWS_SECRET_ACCESS_KEY=wJalrXUtnFEMI/K7MDENG/b... export AWS_SESSION_TOKEN=AQoEXAMPLEH4aoAH0gNCAPy... Example credential_process output: { \"AccessKeyId\": \"AKIAIOSFODNN7EXAMPLE\", \"SecretAccessKey\": \"wJalrXUtnFEMI/K7MDENG/bPxRfiCY...\", \"SessionToken\": \"AQoEXAMPLEH4aoAH0gNCAPy...\" }'\n className=\"h-40\"\n />\n </div>\n <div className=\"flex gap-3\">\n <Button\n variant=\"secondary\"\n onClick={(e) => {\n e.preventDefault();\n handleAutofill();\n }}\n >\n <Upload size={16} />\n Auto-fill Credentials\n </Button>\n <Button variant=\"outline\" onClick={() => setPasteText('')}>\n Clear\n </Button>\n </div>\n <FormField<FormData, 'accessKeyId'>\n control={form.control}\n name=\"accessKeyId\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Access Key Id</FormLabel>\n <FormControl>\n <Input\n {...field}\n type=\"text\"\n className=\"w-full rounded-md border px-3 py-2\"\n placeholder=\"AKIAXXXXXXXXXXXXXXXX\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <FormField<FormData, 'secretAccessKey'>\n control={form.control}\n name=\"secretAccessKey\"\n render={({field}) => (\n <FormItem>\n <FormLabel>Secret Access Key</FormLabel>\n <FormControl>\n <div className=\"relative\">\n <Input\n {...field}\n type={\n showSecrets.secretAccessKey ? 'text' : 'password'\n }\n placeholder=\"••••••••••••••••••••••••\"\n className=\"pr-8\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n <div className=\"text-muted-foreground absolute top-0 right-3 flex h-9 gap-1\">\n <button\n type=\"button\"\n onClick={() =>\n toggleVisibility('secretAccessKey')\n }\n className=\"ml-1\"\n >\n {showSecrets.secretAccessKey ? (\n <EyeOff size={16} />\n ) : (\n <Eye size={16} />\n )}\n </button>\n </div>\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n\n <FormField<FormData, 'sessionToken'>\n control={form.control}\n name=\"sessionToken\"\n render={({field}) => (\n <FormItem>\n <FormLabel>\n Session Token (Optional - for temporary credentials)\n </FormLabel>\n <FormControl>\n <div className=\"relative\">\n <Input\n {...field}\n type={\n showSecrets.sessionToken ? 'text' : 'password'\n }\n placeholder=\"••••••••••••••••••••••••\"\n className=\"pr-8\"\n onChange={(e) => {\n field.onChange(e);\n handleInputChange();\n }}\n />\n <div className=\"text-muted-foreground absolute top-0 right-3 flex h-9 gap-1\">\n <button\n type=\"button\"\n onClick={() => toggleVisibility('sessionToken')}\n className=\"ml-1\"\n >\n {showSecrets.sessionToken ? (\n <EyeOff size={16} />\n ) : (\n <Eye size={16} />\n )}\n </button>\n </div>\n </div>\n </FormControl>\n <FormMessage />\n </FormItem>\n )}\n />\n <Button\n type=\"submit\"\n disabled={\n form.formState.isSubmitting || !form.formState.isValid\n }\n className=\"w-full\"\n >\n {isLoading ? 'Connecting...' : 'Connect to S3'}\n </Button>\n </div>\n </div>\n </form>\n </Form>\n </TabsContent>\n <TabsContent value=\"saved\" className=\"mt-0 w-full\">\n <div className=\"space-y-4\">\n {savedCredentials.length === 0 ? (\n <div className=\"flex flex-col items-center justify-between py-12\">\n <Database className=\"text-muted-foreground mx-auto mb-4 h-8 w-8\" />\n <h3 className=\"mb-2 text-lg font-medium\">No saved credentials</h3>\n <p className=\"text-muted-foreground\">\n Create your first credential to get started\n </p>\n </div>\n ) : (\n <div className=\"grid gap-4\">\n {savedCredentials.map((credential) => (\n <div\n key={credential.id}\n className=\"rounded-lg border p-4 transition-shadow hover:cursor-pointer hover:shadow-sm\"\n >\n <div className=\"flex items-start justify-between\">\n <div className=\"flex-1\">\n <h3 className=\"mb-1 font-semibold\">{credential.name}</h3>\n <p className=\"text-muted-foreground mb-2 text-sm\">\n {credential.bucket}\n </p>\n <div className=\"text-muted-foreground flex items-center space-x-4 text-xs\">\n <span>Region: {credential.region}</span>\n <span>\n Created:{' '}\n {new Date(credential.createdAt).toLocaleDateString()}\n </span>\n </div>\n </div>\n <div className=\"flex space-x-2\">\n <button\n onClick={() => onConnect(credential)}\n className=\"text-primary rounded-md bg-blue-100 px-3 py-1 text-sm hover:bg-blue-200\"\n >\n Connect\n </button>\n {deleteCredential ? (\n <button\n onClick={() => deleteCredential(credential.id)}\n className=\"rounded-md bg-red-100 px-3 py-1 text-sm text-red-700 hover:bg-red-200\"\n >\n <Trash2 className=\"h-4 w-4\" />\n </button>\n ) : null}\n </div>\n </div>\n </div>\n ))}\n </div>\n )}\n </div>\n </TabsContent>\n </Tabs>\n );\n}\n"]}
|
package/dist/S3FileBrowser.js
CHANGED
|
@@ -96,7 +96,7 @@ const S3FileBrowser = (props) => {
|
|
|
96
96
|
const dir = selectedDirectory.split('/').slice(0, -2).join('/');
|
|
97
97
|
return dir ? `${dir}/` : '';
|
|
98
98
|
}, [selectedDirectory]);
|
|
99
|
-
return (_jsxs("div", { className: "relative h-full w-full overflow-hidden", children: [_jsxs("div", { className: "flex w-full justify-end px-
|
|
99
|
+
return (_jsxs("div", { className: "relative h-full w-full overflow-hidden", children: [_jsxs("div", { className: "flex w-full justify-end px-px py-2", children: [renderFileActions ? renderFileActions() : null, _jsxs("div", { className: "relative w-[240px] shrink-0", children: [_jsx("div", { className: "pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3", children: _jsx(Search, { className: "h-5 w-5 text-gray-400" }) }), _jsx(Input, { type: "text", placeholder: "Search files by name", value: searchQuery, onChange: handleSearchInputChange, className: "h-8 py-2 pl-10 text-xs leading-5 md:text-xs" })] })] }), _jsx("div", { className: "absolute flex h-full w-full flex-col items-start overflow-x-auto overflow-y-auto py-0", children: _jsx("div", { className: "border-border w-full overflow-y-auto rounded-lg border", children: _jsxs(Table, { disableWrapper: true, children: [_jsxs(TableHeader, { children: [selectedDirectory ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "bg-secondary text-secondary-foreground", children: _jsxs("div", { className: "flex items-center gap-2", children: [_jsxs(Button, { size: "sm", variant: "outline", onClick: () => onChangeSelectedDirectory(parentDirectory), children: [_jsx(Undo2Icon, { className: "mr-1 h-3 w-3" }), ".."] }), _jsx(Breadcrumb, { children: _jsxs(BreadcrumbList, { children: [_jsx(BreadcrumbItem, { children: _jsx(BreadcrumbLink, { onClick: () => onChangeSelectedDirectory(''), className: "text-primary text-xs", children: "Home" }) }), selectedDirectory.split('/').map((directory, i) => {
|
|
100
100
|
if (!directory)
|
|
101
101
|
return null;
|
|
102
102
|
const path = selectedDirectory
|
|
@@ -112,7 +112,7 @@ const S3FileBrowser = (props) => {
|
|
|
112
112
|
onChangeSelectedDirectory(path);
|
|
113
113
|
}
|
|
114
114
|
}, children: directory })] }, i));
|
|
115
|
-
})] }) })] }) }) })) : null, _jsxs(TableRow, { className: "bg-accent text-primary-foreground sticky top-0 z-
|
|
115
|
+
})] }) })] }) }) })) : null, _jsxs(TableRow, { className: "bg-accent text-primary-foreground sticky top-0 z-2", children: [_jsx(TableHead, { className: "w-[1%]", children: _jsx(Checkbox, { checked: selectedFiles.length === filesInDirectory.length, onCheckedChange: handleSelectAll }) }), _jsx(TableHead, { className: "text-foreground py-2", children: "Name" }), _jsx(TableHead, { className: "text-foreground py-2", children: "Type" }), _jsx(TableHead, { className: "text-foreground text-right", children: "Size" }), _jsx(TableHead, { className: "text-foreground text-right", children: "Modified" })] })] }), _jsx(TableBody, { children: filteredFiles?.map((object) => {
|
|
116
116
|
const { key, isDirectory } = object;
|
|
117
117
|
return (_jsxs(TableRow, { className: "text-foreground hover:bg-accent cursor-pointer", onClick: (evt) => {
|
|
118
118
|
if (isDirectory) {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"S3FileBrowser.js","sourceRoot":"","sources":["../src/S3FileBrowser.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbSeparator,\n Button,\n Checkbox,\n Input,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n cn,\n} from '@sqlrooms/ui';\nimport {S3FileOrDirectory} from '@sqlrooms/s3-browser-config';\nimport {Undo2Icon, FolderIcon, Search} from 'lucide-react';\nimport {formatBytes, formatTimeRelative} from '@sqlrooms/utils';\nimport {FC, useCallback, useEffect, useMemo, useState} from 'react';\n\nfunction renderFileName(\n key: string,\n searchQuery: string,\n isDirectory: boolean,\n) {\n return (\n <>\n {searchQuery ? (\n <span\n dangerouslySetInnerHTML={{\n __html:\n key.replace(\n new RegExp(`(${searchQuery})`, 'gi'),\n '<mark class=\"bg-yellow-200\">$1</mark>',\n ) + (isDirectory ? '/' : ''),\n }}\n />\n ) : (\n <span>\n {key}\n {isDirectory ? '/' : ''}\n </span>\n )}\n </>\n );\n}\n/**\n * A file browser component for navigating and selecting files from an S3-like storage.\n *\n * This component provides a familiar file explorer interface with features like:\n * - Directory navigation with breadcrumbs\n * - File and directory listing\n * - Multiple file selection\n * - File metadata display (size, type, last modified)\n *\n * \n *\n * @example\n * ```tsx\n * const [selectedFiles, setSelectedFiles] = useState<string[]>([]);\n * const [selectedDirectory, setSelectedDirectory] = useState('');\n *\n * return (\n * <S3FileBrowser\n * files={[\n * { key: 'documents', isDirectory: true },\n * {\n * key: 'example.txt',\n * isDirectory: false,\n * size: 1024,\n * contentType: 'text/plain',\n * lastModified: new Date()\n * }\n * ]}\n * selectedFiles={selectedFiles}\n * selectedDirectory={selectedDirectory}\n * onCanConfirmChange={(canConfirm) => console.log('Can confirm:', canConfirm)}\n * onChangeSelectedDirectory={setSelectedDirectory}\n * onChangeSelectedFiles={setSelectedFiles}\n * />\n * );\n * ```\n *\n * @param props - The component props\n * @param props.files - Array of files and directories to display\n * @param props.selectedFiles - Array of currently selected file keys\n * @param props.selectedDirectory - Current directory path (empty string for root)\n * @param props.onCanConfirmChange - Callback fired when selection state changes\n * @param props.onChangeSelectedDirectory - Callback fired when directory navigation occurs\n * @param props.onChangeSelectedFiles - Callback fired when file selection changes\n */\nconst S3FileBrowser: FC<{\n files?: S3FileOrDirectory[];\n selectedFiles: string[];\n selectedDirectory: string;\n onCanConfirmChange: (canConfirm: boolean) => void;\n onChangeSelectedDirectory: (directory: string) => void;\n onChangeSelectedFiles: (files: string[]) => void;\n renderFileActions?: () => React.ReactNode;\n}> = (props) => {\n const {\n files,\n selectedDirectory,\n selectedFiles,\n onCanConfirmChange,\n onChangeSelectedFiles,\n onChangeSelectedDirectory,\n renderFileActions,\n } = props;\n\n useEffect(() => {\n onCanConfirmChange(Boolean(selectedFiles?.length));\n }, [selectedFiles, onCanConfirmChange]);\n const [searchQuery, setSearchQuery] = useState('');\n\n const filteredFiles = useMemo(() => {\n if (!searchQuery.trim() || !files) {\n return files;\n }\n\n return files.filter((file) =>\n file.key.toLowerCase().includes(searchQuery.toLowerCase()),\n );\n }, [files, searchQuery]);\n\n const handleSelectFile = useCallback(\n (key: string) => {\n if (selectedFiles.includes(key)) {\n onChangeSelectedFiles(selectedFiles.filter((id) => id !== key));\n } else {\n onChangeSelectedFiles([...selectedFiles, key]);\n }\n },\n [onChangeSelectedFiles, selectedFiles],\n );\n\n const handleSelectDirectory = useCallback(\n (key: string) => {\n onChangeSelectedDirectory(`${selectedDirectory}${key}/`);\n },\n [selectedDirectory, onChangeSelectedDirectory],\n );\n\n const filesInDirectory = useMemo(\n () => files?.filter(({isDirectory}) => !isDirectory) ?? [],\n [files],\n );\n\n const handleSelectAll = useCallback(() => {\n if (selectedFiles.length === filesInDirectory.length) {\n onChangeSelectedFiles([]);\n } else {\n onChangeSelectedFiles(filesInDirectory.map(({key}) => key) ?? []);\n }\n }, [filesInDirectory, onChangeSelectedFiles, selectedFiles.length]);\n const handleSearchInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n setSearchQuery(value);\n if (value.trim() === '') {\n onChangeSelectedFiles([]);\n }\n },\n [onChangeSelectedFiles, setSearchQuery],\n );\n const parentDirectory = useMemo(() => {\n const dir = selectedDirectory.split('/').slice(0, -2).join('/');\n return dir ? `${dir}/` : '';\n }, [selectedDirectory]);\n\n return (\n <div className=\"relative h-full w-full overflow-hidden\">\n {/* Search Box */}\n <div className=\"flex w-full justify-end px-[1px] py-2\">\n {renderFileActions ? renderFileActions() : null}\n <div className=\"relative w-[240px] shrink-0\">\n <div className=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\">\n <Search className=\"h-5 w-5 text-gray-400\" />\n </div>\n <Input\n type=\"text\"\n placeholder=\"Search files by name\"\n value={searchQuery}\n onChange={handleSearchInputChange}\n className=\"h-8 py-2 pl-10 text-xs leading-5 md:text-xs\"\n />\n </div>\n </div>\n <div className=\"absolute flex h-full w-full flex-col items-start overflow-x-auto overflow-y-auto py-0\">\n <div className=\"border-border w-full overflow-y-auto rounded-lg border\">\n <Table disableWrapper>\n <TableHeader>\n {selectedDirectory ? (\n <TableRow>\n <TableCell\n colSpan={5}\n className=\"bg-secondary text-secondary-foreground\"\n >\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n onChangeSelectedDirectory(parentDirectory)\n }\n >\n <Undo2Icon className=\"mr-1 h-3 w-3\" />\n ..\n </Button>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink\n onClick={() => onChangeSelectedDirectory('')}\n className=\"text-primary text-xs\"\n >\n Home\n </BreadcrumbLink>\n </BreadcrumbItem>\n\n {selectedDirectory.split('/').map((directory, i) => {\n if (!directory) return null;\n const path = selectedDirectory\n .split('/')\n .slice(0, i + 1)\n .join('/')\n .concat('/');\n const isCurrent = path === selectedDirectory;\n return (\n <BreadcrumbItem key={i}>\n <BreadcrumbSeparator />\n <BreadcrumbLink\n className={cn(\n 'text-primary text-xs',\n isCurrent\n ? 'cursor-default hover:no-underline'\n : 'cursor-pointer',\n )}\n onClick={() => {\n if (!isCurrent) {\n onChangeSelectedDirectory(path);\n }\n }}\n >\n {directory}\n </BreadcrumbLink>\n </BreadcrumbItem>\n );\n })}\n </BreadcrumbList>\n </Breadcrumb>\n </div>\n </TableCell>\n </TableRow>\n ) : null}\n <TableRow className=\"bg-accent text-primary-foreground sticky top-0 z-[2]\">\n <TableHead className=\"w-[1%]\">\n <Checkbox\n checked={selectedFiles.length === filesInDirectory.length}\n onCheckedChange={handleSelectAll}\n />\n </TableHead>\n <TableHead className=\"text-foreground py-2\">Name</TableHead>\n <TableHead className=\"text-foreground py-2\">Type</TableHead>\n <TableHead className=\"text-foreground text-right\">\n Size\n </TableHead>\n <TableHead className=\"text-foreground text-right\">\n Modified\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {filteredFiles?.map((object) => {\n const {key, isDirectory} = object;\n return (\n <TableRow\n key={key}\n className=\"text-foreground hover:bg-accent cursor-pointer\"\n onClick={(evt) => {\n if (isDirectory) {\n handleSelectDirectory(key);\n } else {\n handleSelectFile(key);\n evt.preventDefault(); // prevent double change when clicking checkbox\n }\n }}\n >\n <TableCell>\n <Checkbox\n disabled={isDirectory}\n checked={selectedFiles.includes(key)}\n />\n </TableCell>\n <TableCell className=\"text-xs\">\n {isDirectory ? (\n <div className=\"flex items-center gap-2\">\n <FolderIcon className=\"h-4 w-4\" />\n {renderFileName(key, searchQuery, true)}\n </div>\n ) : (\n renderFileName(key, searchQuery, false)\n )}\n </TableCell>\n <TableCell className=\"text-xs\">\n {isDirectory ? 'Directory' : object.contentType}\n </TableCell>\n <TableCell className=\"text-right text-xs\">\n {!isDirectory && object.size !== undefined\n ? formatBytes(object.size)\n : ''}\n </TableCell>\n <TableCell className=\"text-right text-xs\">\n {!isDirectory && object.lastModified\n ? formatTimeRelative(object.lastModified)\n : ''}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </div>\n );\n};\n\nexport default S3FileBrowser;\n"]}
|
|
1
|
+
{"version":3,"file":"S3FileBrowser.js","sourceRoot":"","sources":["../src/S3FileBrowser.tsx"],"names":[],"mappings":"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","sourcesContent":["import {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbSeparator,\n Button,\n Checkbox,\n Input,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n cn,\n} from '@sqlrooms/ui';\nimport {S3FileOrDirectory} from '@sqlrooms/s3-browser-config';\nimport {Undo2Icon, FolderIcon, Search} from 'lucide-react';\nimport {formatBytes, formatTimeRelative} from '@sqlrooms/utils';\nimport {FC, useCallback, useEffect, useMemo, useState} from 'react';\n\nfunction renderFileName(\n key: string,\n searchQuery: string,\n isDirectory: boolean,\n) {\n return (\n <>\n {searchQuery ? (\n <span\n dangerouslySetInnerHTML={{\n __html:\n key.replace(\n new RegExp(`(${searchQuery})`, 'gi'),\n '<mark class=\"bg-yellow-200\">$1</mark>',\n ) + (isDirectory ? '/' : ''),\n }}\n />\n ) : (\n <span>\n {key}\n {isDirectory ? '/' : ''}\n </span>\n )}\n </>\n );\n}\n/**\n * A file browser component for navigating and selecting files from an S3-like storage.\n *\n * This component provides a familiar file explorer interface with features like:\n * - Directory navigation with breadcrumbs\n * - File and directory listing\n * - Multiple file selection\n * - File metadata display (size, type, last modified)\n *\n * \n *\n * @example\n * ```tsx\n * const [selectedFiles, setSelectedFiles] = useState<string[]>([]);\n * const [selectedDirectory, setSelectedDirectory] = useState('');\n *\n * return (\n * <S3FileBrowser\n * files={[\n * { key: 'documents', isDirectory: true },\n * {\n * key: 'example.txt',\n * isDirectory: false,\n * size: 1024,\n * contentType: 'text/plain',\n * lastModified: new Date()\n * }\n * ]}\n * selectedFiles={selectedFiles}\n * selectedDirectory={selectedDirectory}\n * onCanConfirmChange={(canConfirm) => console.log('Can confirm:', canConfirm)}\n * onChangeSelectedDirectory={setSelectedDirectory}\n * onChangeSelectedFiles={setSelectedFiles}\n * />\n * );\n * ```\n *\n * @param props - The component props\n * @param props.files - Array of files and directories to display\n * @param props.selectedFiles - Array of currently selected file keys\n * @param props.selectedDirectory - Current directory path (empty string for root)\n * @param props.onCanConfirmChange - Callback fired when selection state changes\n * @param props.onChangeSelectedDirectory - Callback fired when directory navigation occurs\n * @param props.onChangeSelectedFiles - Callback fired when file selection changes\n */\nconst S3FileBrowser: FC<{\n files?: S3FileOrDirectory[];\n selectedFiles: string[];\n selectedDirectory: string;\n onCanConfirmChange: (canConfirm: boolean) => void;\n onChangeSelectedDirectory: (directory: string) => void;\n onChangeSelectedFiles: (files: string[]) => void;\n renderFileActions?: () => React.ReactNode;\n}> = (props) => {\n const {\n files,\n selectedDirectory,\n selectedFiles,\n onCanConfirmChange,\n onChangeSelectedFiles,\n onChangeSelectedDirectory,\n renderFileActions,\n } = props;\n\n useEffect(() => {\n onCanConfirmChange(Boolean(selectedFiles?.length));\n }, [selectedFiles, onCanConfirmChange]);\n const [searchQuery, setSearchQuery] = useState('');\n\n const filteredFiles = useMemo(() => {\n if (!searchQuery.trim() || !files) {\n return files;\n }\n\n return files.filter((file) =>\n file.key.toLowerCase().includes(searchQuery.toLowerCase()),\n );\n }, [files, searchQuery]);\n\n const handleSelectFile = useCallback(\n (key: string) => {\n if (selectedFiles.includes(key)) {\n onChangeSelectedFiles(selectedFiles.filter((id) => id !== key));\n } else {\n onChangeSelectedFiles([...selectedFiles, key]);\n }\n },\n [onChangeSelectedFiles, selectedFiles],\n );\n\n const handleSelectDirectory = useCallback(\n (key: string) => {\n onChangeSelectedDirectory(`${selectedDirectory}${key}/`);\n },\n [selectedDirectory, onChangeSelectedDirectory],\n );\n\n const filesInDirectory = useMemo(\n () => files?.filter(({isDirectory}) => !isDirectory) ?? [],\n [files],\n );\n\n const handleSelectAll = useCallback(() => {\n if (selectedFiles.length === filesInDirectory.length) {\n onChangeSelectedFiles([]);\n } else {\n onChangeSelectedFiles(filesInDirectory.map(({key}) => key) ?? []);\n }\n }, [filesInDirectory, onChangeSelectedFiles, selectedFiles.length]);\n const handleSearchInputChange = useCallback(\n (e: React.ChangeEvent<HTMLInputElement>) => {\n const value = e.target.value;\n setSearchQuery(value);\n if (value.trim() === '') {\n onChangeSelectedFiles([]);\n }\n },\n [onChangeSelectedFiles, setSearchQuery],\n );\n const parentDirectory = useMemo(() => {\n const dir = selectedDirectory.split('/').slice(0, -2).join('/');\n return dir ? `${dir}/` : '';\n }, [selectedDirectory]);\n\n return (\n <div className=\"relative h-full w-full overflow-hidden\">\n {/* Search Box */}\n <div className=\"flex w-full justify-end px-px py-2\">\n {renderFileActions ? renderFileActions() : null}\n <div className=\"relative w-[240px] shrink-0\">\n <div className=\"pointer-events-none absolute inset-y-0 left-0 flex items-center pl-3\">\n <Search className=\"h-5 w-5 text-gray-400\" />\n </div>\n <Input\n type=\"text\"\n placeholder=\"Search files by name\"\n value={searchQuery}\n onChange={handleSearchInputChange}\n className=\"h-8 py-2 pl-10 text-xs leading-5 md:text-xs\"\n />\n </div>\n </div>\n <div className=\"absolute flex h-full w-full flex-col items-start overflow-x-auto overflow-y-auto py-0\">\n <div className=\"border-border w-full overflow-y-auto rounded-lg border\">\n <Table disableWrapper>\n <TableHeader>\n {selectedDirectory ? (\n <TableRow>\n <TableCell\n colSpan={5}\n className=\"bg-secondary text-secondary-foreground\"\n >\n <div className=\"flex items-center gap-2\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n onChangeSelectedDirectory(parentDirectory)\n }\n >\n <Undo2Icon className=\"mr-1 h-3 w-3\" />\n ..\n </Button>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink\n onClick={() => onChangeSelectedDirectory('')}\n className=\"text-primary text-xs\"\n >\n Home\n </BreadcrumbLink>\n </BreadcrumbItem>\n\n {selectedDirectory.split('/').map((directory, i) => {\n if (!directory) return null;\n const path = selectedDirectory\n .split('/')\n .slice(0, i + 1)\n .join('/')\n .concat('/');\n const isCurrent = path === selectedDirectory;\n return (\n <BreadcrumbItem key={i}>\n <BreadcrumbSeparator />\n <BreadcrumbLink\n className={cn(\n 'text-primary text-xs',\n isCurrent\n ? 'cursor-default hover:no-underline'\n : 'cursor-pointer',\n )}\n onClick={() => {\n if (!isCurrent) {\n onChangeSelectedDirectory(path);\n }\n }}\n >\n {directory}\n </BreadcrumbLink>\n </BreadcrumbItem>\n );\n })}\n </BreadcrumbList>\n </Breadcrumb>\n </div>\n </TableCell>\n </TableRow>\n ) : null}\n <TableRow className=\"bg-accent text-primary-foreground sticky top-0 z-2\">\n <TableHead className=\"w-[1%]\">\n <Checkbox\n checked={selectedFiles.length === filesInDirectory.length}\n onCheckedChange={handleSelectAll}\n />\n </TableHead>\n <TableHead className=\"text-foreground py-2\">Name</TableHead>\n <TableHead className=\"text-foreground py-2\">Type</TableHead>\n <TableHead className=\"text-foreground text-right\">\n Size\n </TableHead>\n <TableHead className=\"text-foreground text-right\">\n Modified\n </TableHead>\n </TableRow>\n </TableHeader>\n <TableBody>\n {filteredFiles?.map((object) => {\n const {key, isDirectory} = object;\n return (\n <TableRow\n key={key}\n className=\"text-foreground hover:bg-accent cursor-pointer\"\n onClick={(evt) => {\n if (isDirectory) {\n handleSelectDirectory(key);\n } else {\n handleSelectFile(key);\n evt.preventDefault(); // prevent double change when clicking checkbox\n }\n }}\n >\n <TableCell>\n <Checkbox\n disabled={isDirectory}\n checked={selectedFiles.includes(key)}\n />\n </TableCell>\n <TableCell className=\"text-xs\">\n {isDirectory ? (\n <div className=\"flex items-center gap-2\">\n <FolderIcon className=\"h-4 w-4\" />\n {renderFileName(key, searchQuery, true)}\n </div>\n ) : (\n renderFileName(key, searchQuery, false)\n )}\n </TableCell>\n <TableCell className=\"text-xs\">\n {isDirectory ? 'Directory' : object.contentType}\n </TableCell>\n <TableCell className=\"text-right text-xs\">\n {!isDirectory && object.size !== undefined\n ? formatBytes(object.size)\n : ''}\n </TableCell>\n <TableCell className=\"text-right text-xs\">\n {!isDirectory && object.lastModified\n ? formatTimeRelative(object.lastModified)\n : ''}\n </TableCell>\n </TableRow>\n );\n })}\n </TableBody>\n </Table>\n </div>\n </div>\n </div>\n );\n};\n\nexport default S3FileBrowser;\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@sqlrooms/s3-browser",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.28.0",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -20,10 +20,10 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@hookform/resolvers": "^5.0.0",
|
|
23
|
-
"@sqlrooms/room-shell": "0.
|
|
24
|
-
"@sqlrooms/s3-browser-config": "0.
|
|
25
|
-
"@sqlrooms/ui": "0.
|
|
26
|
-
"@sqlrooms/utils": "0.
|
|
23
|
+
"@sqlrooms/room-shell": "0.28.0",
|
|
24
|
+
"@sqlrooms/s3-browser-config": "0.28.0",
|
|
25
|
+
"@sqlrooms/ui": "0.28.0",
|
|
26
|
+
"@sqlrooms/utils": "0.28.0",
|
|
27
27
|
"immer": "^11.0.1",
|
|
28
28
|
"lucide-react": "^0.556.0",
|
|
29
29
|
"react-hook-form": "^7.63.0",
|
|
@@ -41,5 +41,5 @@
|
|
|
41
41
|
"typecheck": "tsc --noEmit",
|
|
42
42
|
"typedoc": "typedoc"
|
|
43
43
|
},
|
|
44
|
-
"gitHead": "
|
|
44
|
+
"gitHead": "dcac54f8adf77240e293c93d224a0ce9fd8142a9"
|
|
45
45
|
}
|