@sqlrooms/s3-browser 0.7.0 → 0.8.1
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/dist/S3FileBrowser.js
CHANGED
|
@@ -77,7 +77,7 @@ const S3FileBrowser = (props) => {
|
|
|
77
77
|
const dir = selectedDirectory.split('/').slice(0, -2).join('/');
|
|
78
78
|
return dir ? `${dir}/` : '';
|
|
79
79
|
}, [selectedDirectory]);
|
|
80
|
-
return (_jsx("div", { className: "relative
|
|
80
|
+
return (_jsx("div", { className: "relative h-full w-full overflow-hidden", children: _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: "w-full overflow-y-auto rounded-lg border border-gray-600", children: _jsxs(Table, { disableWrapper: true, children: [_jsxs(TableHeader, { children: [selectedDirectory ? (_jsx(TableRow, { children: _jsx(TableCell, { colSpan: 5, className: "bg-gray-800 py-3 text-gray-100", 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-xs text-blue-400", children: "Home" }) }), selectedDirectory.split('/').map((directory, i) => {
|
|
81
81
|
if (!directory)
|
|
82
82
|
return null;
|
|
83
83
|
const path = selectedDirectory
|
|
@@ -92,9 +92,9 @@ const S3FileBrowser = (props) => {
|
|
|
92
92
|
onChangeSelectedDirectory(path);
|
|
93
93
|
}
|
|
94
94
|
}, children: directory })] }, i));
|
|
95
|
-
})] }) })] }) }) })) : null, _jsxs(TableRow, { className: "sticky top-0 z-[2] bg-gray-600", children: [_jsx(TableHead, { className: "w-[1%]", children: _jsx(Checkbox, { checked: selectedFiles.length === filesInDirectory.length, onCheckedChange: handleSelectAll }) }), _jsx(TableHead, { className: "py-2
|
|
95
|
+
})] }) })] }) }) })) : null, _jsxs(TableRow, { className: "sticky top-0 z-[2] bg-gray-600", 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: files?.map((object) => {
|
|
96
96
|
const { key, isDirectory } = object;
|
|
97
|
-
return (_jsxs(TableRow, { className: "cursor-pointer text-blue-300 hover:bg-blue-700
|
|
97
|
+
return (_jsxs(TableRow, { className: "hover:text-foreground cursor-pointer text-blue-300 hover:bg-blue-700", onClick: (evt) => {
|
|
98
98
|
if (isDirectory) {
|
|
99
99
|
handleSelectDirectory(key);
|
|
100
100
|
}
|
|
@@ -102,9 +102,9 @@ const S3FileBrowser = (props) => {
|
|
|
102
102
|
handleSelectFile(key);
|
|
103
103
|
evt.preventDefault(); // prevent double change when clicking checkbox
|
|
104
104
|
}
|
|
105
|
-
}, children: [_jsx(TableCell, { children: _jsx(Checkbox, { disabled: isDirectory, checked: selectedFiles.includes(key) }) }), _jsx(TableCell, { className: "text-xs", children: isDirectory ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FolderIcon, { className: "
|
|
105
|
+
}, children: [_jsx(TableCell, { children: _jsx(Checkbox, { disabled: isDirectory, checked: selectedFiles.includes(key) }) }), _jsx(TableCell, { className: "text-xs", children: isDirectory ? (_jsxs("div", { className: "flex items-center gap-2", children: [_jsx(FolderIcon, { className: "h-4 w-4" }), _jsx("span", { children: `${key}/` })] })) : (key) }), _jsx(TableCell, { className: "text-xs", children: isDirectory ? 'Directory' : object.contentType }), _jsx(TableCell, { className: "text-right text-xs", children: !isDirectory && object.size !== undefined
|
|
106
106
|
? formatBytes(object.size)
|
|
107
|
-
: '' }), _jsx(TableCell, { className: "text-
|
|
107
|
+
: '' }), _jsx(TableCell, { className: "text-right text-xs", children: !isDirectory && object.lastModified
|
|
108
108
|
? formatTimeRelative(object.lastModified)
|
|
109
109
|
: '' })] }, key));
|
|
110
110
|
}) })] }) }) }) }));
|
|
@@ -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 Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n cn,\n} from '@sqlrooms/ui';\nimport {Undo2Icon, FolderIcon} from 'lucide-react';\nimport {formatBytes, formatTimeRelative} from '@sqlrooms/utils';\nimport {FC, useCallback, useEffect, useMemo} from 'react';\nimport {S3FileOrDirectory} from './S3FileOrDirectory';\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}> = (props) => {\n const {\n files,\n selectedDirectory,\n selectedFiles,\n onCanConfirmChange,\n onChangeSelectedFiles,\n onChangeSelectedDirectory,\n } = props;\n\n useEffect(() => {\n onCanConfirmChange(Boolean(selectedFiles?.length));\n }, [selectedFiles, onCanConfirmChange]);\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\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 w-full h-full overflow-hidden\">\n <div className=\"absolute w-full h-full overflow-x-auto overflow-y-auto flex flex-col py-0 items-start\">\n <div className=\"w-full rounded-lg border border-gray-600 overflow-y-auto\">\n <Table disableWrapper>\n <TableHeader>\n {selectedDirectory ? (\n <TableRow>\n <TableCell\n colSpan={5}\n className=\"py-3 text-gray-100 bg-gray-800\"\n >\n <div className=\"flex gap-2 items-center\">\n <Button\n size=\"sm\"\n variant=\"outline\"\n onClick={() =>\n onChangeSelectedDirectory(parentDirectory)\n }\n >\n <Undo2Icon className=\"w-3 h-3 mr-1\" />\n ..\n </Button>\n <Breadcrumb>\n <BreadcrumbList>\n <BreadcrumbItem>\n <BreadcrumbLink\n onClick={() => onChangeSelectedDirectory('')}\n className=\"text-xs text-blue-400\"\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-xs text-blue-400',\n isCurrent &&\n 'cursor-default hover:no-underline',\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=\"sticky top-0 z-[2] bg-gray-600\">\n <TableHead className=\"w-[1%]\">\n <Checkbox\n checked={selectedFiles.length === filesInDirectory.length}\n onCheckedChange={handleSelectAll}\n />\n </TableHead>\n <TableHead className=\"py-2 text-foreground\">Name</TableHead>\n <TableHead className=\"py-2 text-foreground\">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 {files?.map((object) => {\n const {key, isDirectory} = object;\n return (\n <TableRow\n key={key}\n className=\"cursor-pointer text-blue-300 hover:bg-blue-700 hover:text-foreground\"\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=\"w-4 h-4\" />\n <span>{`${key}/`}</span>\n </div>\n ) : (\n key\n )}\n </TableCell>\n <TableCell className=\"text-xs\">\n {isDirectory ? 'Directory' : object.contentType}\n </TableCell>\n <TableCell className=\"text-xs text-right\">\n {!isDirectory && object.size !== undefined\n ? formatBytes(object.size)\n : ''}\n </TableCell>\n <TableCell className=\"text-xs text-right\">\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":";AAAA,OAAO,EACL,UAAU,EACV,cAAc,EACd,cAAc,EACd,cAAc,EACd,mBAAmB,EACnB,MAAM,EACN,QAAQ,EACR,KAAK,EACL,SAAS,EACT,SAAS,EACT,SAAS,EACT,WAAW,EACX,QAAQ,EACR,EAAE,GACH,MAAM,cAAc,CAAC;AACtB,OAAO,EAAC,SAAS,EAAE,UAAU,EAAC,MAAM,cAAc,CAAC;AACnD,OAAO,EAAC,WAAW,EAAE,kBAAkB,EAAC,MAAM,iBAAiB,CAAC;AAChE,OAAO,EAAK,WAAW,EAAE,SAAS,EAAE,OAAO,EAAC,MAAM,OAAO,CAAC;AAG1D;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GA4CG;AACH,MAAM,aAAa,GAOd,CAAC,KAAK,EAAE,EAAE;IACb,MAAM,EACJ,KAAK,EACL,iBAAiB,EACjB,aAAa,EACb,kBAAkB,EAClB,qBAAqB,EACrB,yBAAyB,GAC1B,GAAG,KAAK,CAAC;IAEV,SAAS,CAAC,GAAG,EAAE;QACb,kBAAkB,CAAC,OAAO,CAAC,aAAa,EAAE,MAAM,CAAC,CAAC,CAAC;IACrD,CAAC,EAAE,CAAC,aAAa,EAAE,kBAAkB,CAAC,CAAC,CAAC;IAExC,MAAM,gBAAgB,GAAG,WAAW,CAClC,CAAC,GAAW,EAAE,EAAE;QACd,IAAI,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,EAAE,CAAC;YAChC,qBAAqB,CAAC,aAAa,CAAC,MAAM,CAAC,CAAC,EAAE,EAAE,EAAE,CAAC,EAAE,KAAK,GAAG,CAAC,CAAC,CAAC;QAClE,CAAC;aAAM,CAAC;YACN,qBAAqB,CAAC,CAAC,GAAG,aAAa,EAAE,GAAG,CAAC,CAAC,CAAC;QACjD,CAAC;IACH,CAAC,EACD,CAAC,qBAAqB,EAAE,aAAa,CAAC,CACvC,CAAC;IAEF,MAAM,qBAAqB,GAAG,WAAW,CACvC,CAAC,GAAW,EAAE,EAAE;QACd,yBAAyB,CAAC,GAAG,iBAAiB,GAAG,GAAG,GAAG,CAAC,CAAC;IAC3D,CAAC,EACD,CAAC,iBAAiB,EAAE,yBAAyB,CAAC,CAC/C,CAAC;IAEF,MAAM,gBAAgB,GAAG,OAAO,CAC9B,GAAG,EAAE,CAAC,KAAK,EAAE,MAAM,CAAC,CAAC,EAAC,WAAW,EAAC,EAAE,EAAE,CAAC,CAAC,WAAW,CAAC,IAAI,EAAE,EAC1D,CAAC,KAAK,CAAC,CACR,CAAC;IAEF,MAAM,eAAe,GAAG,WAAW,CAAC,GAAG,EAAE;QACvC,IAAI,aAAa,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,EAAE,CAAC;YACrD,qBAAqB,CAAC,EAAE,CAAC,CAAC;QAC5B,CAAC;aAAM,CAAC;YACN,qBAAqB,CAAC,gBAAgB,CAAC,GAAG,CAAC,CAAC,EAAC,GAAG,EAAC,EAAE,EAAE,CAAC,GAAG,CAAC,IAAI,EAAE,CAAC,CAAC;QACpE,CAAC;IACH,CAAC,EAAE,CAAC,gBAAgB,EAAE,qBAAqB,EAAE,aAAa,CAAC,MAAM,CAAC,CAAC,CAAC;IAEpE,MAAM,eAAe,GAAG,OAAO,CAAC,GAAG,EAAE;QACnC,MAAM,GAAG,GAAG,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC;QAChE,OAAO,GAAG,CAAC,CAAC,CAAC,GAAG,GAAG,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC;IAC9B,CAAC,EAAE,CAAC,iBAAiB,CAAC,CAAC,CAAC;IAExB,OAAO,CACL,cAAK,SAAS,EAAC,wCAAwC,YACrD,cAAK,SAAS,EAAC,uFAAuF,YACpG,cAAK,SAAS,EAAC,0DAA0D,YACvE,MAAC,KAAK,IAAC,cAAc,mBACnB,MAAC,WAAW,eACT,iBAAiB,CAAC,CAAC,CAAC,CACnB,KAAC,QAAQ,cACP,KAAC,SAAS,IACR,OAAO,EAAE,CAAC,EACV,SAAS,EAAC,gCAAgC,YAE1C,eAAK,SAAS,EAAC,yBAAyB,aACtC,MAAC,MAAM,IACL,IAAI,EAAC,IAAI,EACT,OAAO,EAAC,SAAS,EACjB,OAAO,EAAE,GAAG,EAAE,CACZ,yBAAyB,CAAC,eAAe,CAAC,aAG5C,KAAC,SAAS,IAAC,SAAS,EAAC,cAAc,GAAG,UAE/B,EACT,KAAC,UAAU,cACT,MAAC,cAAc,eACb,KAAC,cAAc,cACb,KAAC,cAAc,IACb,OAAO,EAAE,GAAG,EAAE,CAAC,yBAAyB,CAAC,EAAE,CAAC,EAC5C,SAAS,EAAC,uBAAuB,qBAGlB,GACF,EAEhB,iBAAiB,CAAC,KAAK,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC,SAAS,EAAE,CAAC,EAAE,EAAE;gEACjD,IAAI,CAAC,SAAS;oEAAE,OAAO,IAAI,CAAC;gEAC5B,MAAM,IAAI,GAAG,iBAAiB;qEAC3B,KAAK,CAAC,GAAG,CAAC;qEACV,KAAK,CAAC,CAAC,EAAE,CAAC,GAAG,CAAC,CAAC;qEACf,IAAI,CAAC,GAAG,CAAC;qEACT,MAAM,CAAC,GAAG,CAAC,CAAC;gEACf,MAAM,SAAS,GAAG,IAAI,KAAK,iBAAiB,CAAC;gEAC7C,OAAO,CACL,MAAC,cAAc,eACb,KAAC,mBAAmB,KAAG,EACvB,KAAC,cAAc,IACb,SAAS,EAAE,EAAE,CACX,uBAAuB,EACvB,SAAS;gFACP,mCAAmC,CACtC,EACD,OAAO,EAAE,GAAG,EAAE;gFACZ,IAAI,CAAC,SAAS,EAAE,CAAC;oFACf,yBAAyB,CAAC,IAAI,CAAC,CAAC;gFAClC,CAAC;4EACH,CAAC,YAEA,SAAS,GACK,KAfE,CAAC,CAgBL,CAClB,CAAC;4DACJ,CAAC,CAAC,IACa,GACN,IACT,GACI,GACH,CACZ,CAAC,CAAC,CAAC,IAAI,EACR,MAAC,QAAQ,IAAC,SAAS,EAAC,gCAAgC,aAClD,KAAC,SAAS,IAAC,SAAS,EAAC,QAAQ,YAC3B,KAAC,QAAQ,IACP,OAAO,EAAE,aAAa,CAAC,MAAM,KAAK,gBAAgB,CAAC,MAAM,EACzD,eAAe,EAAE,eAAe,GAChC,GACQ,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,sBAAsB,qBAAiB,EAC5D,KAAC,SAAS,IAAC,SAAS,EAAC,sBAAsB,qBAAiB,EAC5D,KAAC,SAAS,IAAC,SAAS,EAAC,4BAA4B,qBAErC,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,4BAA4B,yBAErC,IACH,IACC,EACd,KAAC,SAAS,cACP,KAAK,EAAE,GAAG,CAAC,CAAC,MAAM,EAAE,EAAE;gCACrB,MAAM,EAAC,GAAG,EAAE,WAAW,EAAC,GAAG,MAAM,CAAC;gCAClC,OAAO,CACL,MAAC,QAAQ,IAEP,SAAS,EAAC,sEAAsE,EAChF,OAAO,EAAE,CAAC,GAAG,EAAE,EAAE;wCACf,IAAI,WAAW,EAAE,CAAC;4CAChB,qBAAqB,CAAC,GAAG,CAAC,CAAC;wCAC7B,CAAC;6CAAM,CAAC;4CACN,gBAAgB,CAAC,GAAG,CAAC,CAAC;4CACtB,GAAG,CAAC,cAAc,EAAE,CAAC,CAAC,+CAA+C;wCACvE,CAAC;oCACH,CAAC,aAED,KAAC,SAAS,cACR,KAAC,QAAQ,IACP,QAAQ,EAAE,WAAW,EACrB,OAAO,EAAE,aAAa,CAAC,QAAQ,CAAC,GAAG,CAAC,GACpC,GACQ,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,YAC3B,WAAW,CAAC,CAAC,CAAC,CACb,eAAK,SAAS,EAAC,yBAAyB,aACtC,KAAC,UAAU,IAAC,SAAS,EAAC,SAAS,GAAG,EAClC,yBAAO,GAAG,GAAG,GAAG,GAAQ,IACpB,CACP,CAAC,CAAC,CAAC,CACF,GAAG,CACJ,GACS,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,SAAS,YAC3B,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,MAAM,CAAC,WAAW,GACrC,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,oBAAoB,YACtC,CAAC,WAAW,IAAI,MAAM,CAAC,IAAI,KAAK,SAAS;gDACxC,CAAC,CAAC,WAAW,CAAC,MAAM,CAAC,IAAI,CAAC;gDAC1B,CAAC,CAAC,EAAE,GACI,EACZ,KAAC,SAAS,IAAC,SAAS,EAAC,oBAAoB,YACtC,CAAC,WAAW,IAAI,MAAM,CAAC,YAAY;gDAClC,CAAC,CAAC,kBAAkB,CAAC,MAAM,CAAC,YAAY,CAAC;gDACzC,CAAC,CAAC,EAAE,GACI,KAvCP,GAAG,CAwCC,CACZ,CAAC;4BACJ,CAAC,CAAC,GACQ,IACN,GACJ,GACF,GACF,CACP,CAAC;AACJ,CAAC,CAAC;AAEF,eAAe,aAAa,CAAC","sourcesContent":["import {\n Breadcrumb,\n BreadcrumbItem,\n BreadcrumbLink,\n BreadcrumbList,\n BreadcrumbSeparator,\n Button,\n Checkbox,\n Table,\n TableBody,\n TableCell,\n TableHead,\n TableHeader,\n TableRow,\n cn,\n} from '@sqlrooms/ui';\nimport {Undo2Icon, FolderIcon} from 'lucide-react';\nimport {formatBytes, formatTimeRelative} from '@sqlrooms/utils';\nimport {FC, useCallback, useEffect, useMemo} from 'react';\nimport {S3FileOrDirectory} from './S3FileOrDirectory';\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}> = (props) => {\n const {\n files,\n selectedDirectory,\n selectedFiles,\n onCanConfirmChange,\n onChangeSelectedFiles,\n onChangeSelectedDirectory,\n } = props;\n\n useEffect(() => {\n onCanConfirmChange(Boolean(selectedFiles?.length));\n }, [selectedFiles, onCanConfirmChange]);\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\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 <div className=\"absolute flex h-full w-full flex-col items-start overflow-x-auto overflow-y-auto py-0\">\n <div className=\"w-full overflow-y-auto rounded-lg border border-gray-600\">\n <Table disableWrapper>\n <TableHeader>\n {selectedDirectory ? (\n <TableRow>\n <TableCell\n colSpan={5}\n className=\"bg-gray-800 py-3 text-gray-100\"\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-xs text-blue-400\"\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-xs text-blue-400',\n isCurrent &&\n 'cursor-default hover:no-underline',\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=\"sticky top-0 z-[2] bg-gray-600\">\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 {files?.map((object) => {\n const {key, isDirectory} = object;\n return (\n <TableRow\n key={key}\n className=\"hover:text-foreground cursor-pointer text-blue-300 hover:bg-blue-700\"\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 <span>{`${key}/`}</span>\n </div>\n ) : (\n key\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.8.1",
|
|
4
4
|
"main": "dist/index.js",
|
|
5
5
|
"types": "dist/index.d.ts",
|
|
6
6
|
"module": "dist/index.js",
|
|
@@ -20,8 +20,8 @@
|
|
|
20
20
|
},
|
|
21
21
|
"dependencies": {
|
|
22
22
|
"@aws-sdk/client-s3": "^3.726.1",
|
|
23
|
-
"@sqlrooms/ui": "0.
|
|
24
|
-
"@sqlrooms/utils": "0.
|
|
23
|
+
"@sqlrooms/ui": "0.8.1",
|
|
24
|
+
"@sqlrooms/utils": "0.8.1",
|
|
25
25
|
"lucide-react": "^0.474.0",
|
|
26
26
|
"zod": "^3.24.1"
|
|
27
27
|
},
|
|
@@ -34,5 +34,5 @@
|
|
|
34
34
|
"lint": "eslint .",
|
|
35
35
|
"typedoc": "typedoc"
|
|
36
36
|
},
|
|
37
|
-
"gitHead": "
|
|
37
|
+
"gitHead": "7b5e727b79d675c17b93412c109d1ba1b22699c8"
|
|
38
38
|
}
|