@unciatech/file-manager 0.0.32 → 0.0.34
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 +125 -32
- package/dist/chunk-DXJH5W7A.js +18 -0
- package/dist/chunk-SV5VFLC2.cjs +18 -0
- package/dist/cli.cjs +274 -164
- package/dist/cli.js +273 -140
- package/dist/index.cjs +18 -7714
- package/dist/index.d.cts +4 -3
- package/dist/index.d.ts +4 -3
- package/dist/index.js +18 -7666
- package/dist/{mock-provider-nCBvw7nl.d.cts → mock-provider-DrtiUc3h.d.cts} +32 -4
- package/dist/{mock-provider-nCBvw7nl.d.ts → mock-provider-DrtiUc3h.d.ts} +32 -4
- package/dist/mock.cjs +1 -1395
- package/dist/mock.d.cts +1 -1
- package/dist/mock.d.ts +1 -1
- package/dist/mock.js +1 -1361
- package/dist/styles.css +75 -56
- package/dist/styles.d.ts +1 -0
- package/package.json +8 -6
- package/styles.css +0 -62
package/dist/cli.js
CHANGED
|
@@ -1,25 +1,11 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
+
import t from'fs';import i from'path';import {execSync}from'child_process';import g from'readline';var p=process.argv.slice(2),v=p[0],l=p[1],u=g.createInterface({input:process.stdin,output:process.stdout}),x=o=>new Promise(e=>u.question(o,e)),h=(o="/media")=>`"use client";
|
|
2
3
|
|
|
3
|
-
|
|
4
|
-
import * as fs from "fs";
|
|
5
|
-
import * as path from "path";
|
|
6
|
-
import { execSync } from "child_process";
|
|
7
|
-
import * as readline from "readline";
|
|
8
|
-
var args = process.argv.slice(2);
|
|
9
|
-
var command = args[0];
|
|
10
|
-
var projectName = args[1];
|
|
11
|
-
var rl = readline.createInterface({
|
|
12
|
-
input: process.stdin,
|
|
13
|
-
output: process.stdout
|
|
14
|
-
});
|
|
15
|
-
var askQuestion = (query) => new Promise((resolve) => rl.question(query, resolve));
|
|
16
|
-
var TEMPLATE = `"use client";
|
|
17
|
-
|
|
18
|
-
import React, { Suspense, useMemo } from "react";
|
|
4
|
+
import React, { Suspense } from "react";
|
|
19
5
|
import { FileManager, MockProvider } from "@unciatech/file-manager";
|
|
20
6
|
|
|
21
7
|
export default function FileManagerDemo() {
|
|
22
|
-
const
|
|
8
|
+
const mockProvider = new MockProvider();
|
|
23
9
|
|
|
24
10
|
return (
|
|
25
11
|
<div className="h-screen w-full">
|
|
@@ -27,153 +13,300 @@ export default function FileManagerDemo() {
|
|
|
27
13
|
<FileManager
|
|
28
14
|
allowedFileTypes={["audios", "videos", "images", "files"]}
|
|
29
15
|
viewMode="grid"
|
|
30
|
-
provider={
|
|
16
|
+
provider={mockProvider}
|
|
17
|
+
basePath="${o}"
|
|
31
18
|
/>
|
|
32
19
|
</Suspense>
|
|
33
20
|
</div>
|
|
34
21
|
);
|
|
35
22
|
}
|
|
36
|
-
`;
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
targetDir2 = path.join(process.cwd(), "src/components");
|
|
47
|
-
} else if (fs.existsSync(path.join(process.cwd(), "components"))) {
|
|
48
|
-
targetDir2 = path.join(process.cwd(), "components");
|
|
49
|
-
}
|
|
50
|
-
const file = path.join(targetDir2, "FileManagerDemo.tsx");
|
|
51
|
-
fs.writeFileSync(file, TEMPLATE);
|
|
52
|
-
console.log(`\u2705 Created ${file}`);
|
|
53
|
-
process.exit(0);
|
|
54
|
-
}
|
|
55
|
-
console.log(`
|
|
56
|
-
\u{1F680} Initializing project: ${projectName}
|
|
57
|
-
`);
|
|
58
|
-
console.log("Choose framework:");
|
|
59
|
-
console.log("1) Next.js");
|
|
60
|
-
console.log("2) Vite (React)");
|
|
61
|
-
console.log("3) Cancel");
|
|
62
|
-
const choice = await askQuestion("\nSelect option (1-3): ");
|
|
63
|
-
rl.close();
|
|
64
|
-
const targetDir = path.join(process.cwd(), projectName);
|
|
65
|
-
if (fs.existsSync(targetDir)) {
|
|
66
|
-
console.error("\u274C Directory already exists");
|
|
67
|
-
process.exit(1);
|
|
68
|
-
}
|
|
69
|
-
try {
|
|
70
|
-
if (choice === "1") {
|
|
71
|
-
await scaffoldNextjs(projectName, targetDir);
|
|
72
|
-
} else if (choice === "2") {
|
|
73
|
-
await scaffoldVite(projectName, targetDir);
|
|
74
|
-
} else {
|
|
75
|
-
console.log("Canceled.");
|
|
76
|
-
}
|
|
77
|
-
} catch (e) {
|
|
78
|
-
console.error("\u274C Error:", e);
|
|
79
|
-
}
|
|
80
|
-
}
|
|
81
|
-
async function scaffoldNextjs(projectName2, targetDir) {
|
|
82
|
-
console.log("\n\u{1F4E6} Creating Next.js app...");
|
|
83
|
-
execSync(
|
|
84
|
-
`npx create-next-app@latest ${projectName2} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm`,
|
|
85
|
-
{ stdio: "inherit" }
|
|
86
|
-
);
|
|
87
|
-
console.log("\n\u{1F4E6} Installing dependencies...");
|
|
88
|
-
execSync("npm install @unciatech/file-manager tailwindcss-animate", {
|
|
89
|
-
cwd: targetDir,
|
|
90
|
-
stdio: "inherit"
|
|
91
|
-
});
|
|
92
|
-
const componentsDir = path.join(targetDir, "src/components");
|
|
93
|
-
fs.mkdirSync(componentsDir, { recursive: true });
|
|
94
|
-
fs.writeFileSync(
|
|
95
|
-
path.join(componentsDir, "FileManagerDemo.tsx"),
|
|
96
|
-
TEMPLATE
|
|
97
|
-
);
|
|
98
|
-
const pagePath = path.join(targetDir, "src/app/page.tsx");
|
|
99
|
-
fs.writeFileSync(
|
|
100
|
-
pagePath,
|
|
101
|
-
`import FileManagerDemo from "@/components/FileManagerDemo";
|
|
23
|
+
`;async function w(){if(v!=="init"&&(console.log("Usage: npx @unciatech/file-manager init [project-name]"),process.exit(0)),!l){console.log("\u{1F680} Generating <FileManagerDemo /> component in the current project...");let s=process.cwd();t.existsSync(i.join(process.cwd(),"components"))?s=i.join(process.cwd(),"components"):t.existsSync(i.join(process.cwd(),"src","components"))&&(s=i.join(process.cwd(),"src","components"));let a=i.join(s,"FileManagerDemo.tsx");t.existsSync(a)&&(console.error(`\u274C Error: ${a} already exists.`),process.exit(1)),t.writeFileSync(a,h("/"),"utf-8"),console.log(`\u2705 Success! Created ${a}`),console.log(""),console.log("You can now import and render <FileManagerDemo /> anywhere in your application."),console.log("Don't forget to configure your Tailwind CSS content to scan the library for styles!"),process.exit(0);}console.log(`
|
|
24
|
+
\u{1F680} Initializing a new application: ${l}
|
|
25
|
+
`),console.log("Which framework would you like to use?"),console.log(" 1) Next.js (App Router, Tailwind v4)"),console.log(" 2) Vite (React, Tailwind v4)"),console.log(" 3) Cancel");let o=await x(`
|
|
26
|
+
Select an option (1-3): `),e=i.join(process.cwd(),l);t.existsSync(e)&&(console.error(`
|
|
27
|
+
\u274C Error: Directory "${l}" already exists in ${process.cwd()}.`),console.error(" Please choose a different project name or delete the existing directory first."),u.close(),process.exit(1));try{o==="1"?await b(l,e):o==="2"?await y(l,e):(console.log("Canceled."),process.exit(0));}catch(s){console.error(`
|
|
28
|
+
\u274C Scaffolding failed:`,s),process.exit(1);}process.exit(0);}async function b(o,e){console.log(`
|
|
29
|
+
\u{1F4E6} Creating Next.js application (this may take a minute)...`),execSync(`npx create-next-app@latest ${o} --ts --tailwind --eslint --app --src-dir --import-alias "@/*" --use-npm`,{stdio:"inherit"}),console.log(`
|
|
30
|
+
\u{1F4E6} Installing dependencies (@unciatech/file-manager, tailwindcss-animate)...`),execSync("npm install @unciatech/file-manager tailwindcss-animate",{cwd:e,stdio:"inherit"});let s=i.join(e,"src","app","page.tsx");t.writeFileSync(s,`"use client";
|
|
31
|
+
|
|
32
|
+
import { redirect } from "next/navigation";
|
|
102
33
|
|
|
103
34
|
export default function Home() {
|
|
35
|
+
redirect("/media");
|
|
36
|
+
}
|
|
37
|
+
`);let a=i.join(e,"src","app","media","[[...path]]");t.mkdirSync(a,{recursive:true}),t.writeFileSync(i.join(a,"page.tsx"),`"use client";
|
|
38
|
+
|
|
39
|
+
import { Suspense, useState } from "react";
|
|
40
|
+
import { FileManager, MockProvider } from "@unciatech/file-manager";
|
|
41
|
+
import Link from "next/link";
|
|
42
|
+
|
|
43
|
+
function MediaPageContent() {
|
|
44
|
+
const [provider] = useState(() => new MockProvider());
|
|
45
|
+
|
|
104
46
|
return (
|
|
105
|
-
<
|
|
106
|
-
<
|
|
107
|
-
|
|
47
|
+
<div className="h-screen w-full flex flex-col">
|
|
48
|
+
<div className="flex gap-4 p-4 border-b border-border items-center justify-between">
|
|
49
|
+
<h1 className="text-xl font-bold">Full Page View</h1>
|
|
50
|
+
<Link
|
|
51
|
+
href="/modal-demo"
|
|
52
|
+
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium"
|
|
53
|
+
>
|
|
54
|
+
Go to Modal Demo
|
|
55
|
+
</Link>
|
|
56
|
+
</div>
|
|
57
|
+
<div className="flex-1 relative overflow-hidden">
|
|
58
|
+
<FileManager
|
|
59
|
+
allowedFileTypes={["audios", "videos", "images", "files"]}
|
|
60
|
+
viewMode="grid"
|
|
61
|
+
provider={provider}
|
|
62
|
+
basePath="/media"
|
|
63
|
+
/>
|
|
64
|
+
</div>
|
|
65
|
+
</div>
|
|
108
66
|
);
|
|
109
67
|
}
|
|
110
|
-
|
|
68
|
+
|
|
69
|
+
export default function MediaPage() {
|
|
70
|
+
return (
|
|
71
|
+
<Suspense fallback={<div className="p-4">Loading Media Library...</div>}>
|
|
72
|
+
<MediaPageContent />
|
|
73
|
+
</Suspense>
|
|
111
74
|
);
|
|
112
|
-
printSuccess(projectName2);
|
|
113
75
|
}
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
76
|
+
`);let d=i.join(e,"src","app","modal-demo");t.mkdirSync(d,{recursive:true}),t.writeFileSync(i.join(d,"page.tsx"),`"use client";
|
|
77
|
+
|
|
78
|
+
import { Suspense, useState } from "react";
|
|
79
|
+
import { FileManagerModal, MockProvider } from "@unciatech/file-manager";
|
|
80
|
+
import type { FileMetaData } from "@unciatech/file-manager";
|
|
81
|
+
import Link from "next/link";
|
|
82
|
+
|
|
83
|
+
function ModalDemoContent() {
|
|
84
|
+
const [provider] = useState(() => new MockProvider());
|
|
85
|
+
const [isOpen, setIsOpen] = useState(false);
|
|
86
|
+
const [selectedFiles, setSelectedFiles] = useState<FileMetaData[]>([]);
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<div className="p-10 flex flex-col items-start gap-6 min-h-screen w-full">
|
|
90
|
+
<div>
|
|
91
|
+
<h1 className="text-3xl font-bold mb-2">File Manager Demo</h1>
|
|
92
|
+
<p className="text-gray-500">Select files using the modal or browse the full page view.</p>
|
|
93
|
+
</div>
|
|
94
|
+
|
|
95
|
+
<div className="flex gap-4">
|
|
96
|
+
<button
|
|
97
|
+
onClick={() => setIsOpen(true)}
|
|
98
|
+
className="px-6 py-2.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium shadow-sm transition-colors"
|
|
99
|
+
>
|
|
100
|
+
Open File Picker Modal
|
|
101
|
+
</button>
|
|
102
|
+
<Link
|
|
103
|
+
href="/media"
|
|
104
|
+
className="px-6 py-2.5 bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-700 cursor-pointer text-sm font-medium transition-colors"
|
|
105
|
+
>
|
|
106
|
+
Go to Full Page View
|
|
107
|
+
</Link>
|
|
108
|
+
</div>
|
|
109
|
+
|
|
110
|
+
{selectedFiles.length > 0 && (
|
|
111
|
+
<div className="w-full mt-8">
|
|
112
|
+
<h2 className="text-xl font-semibold mb-4 border-b pb-2">
|
|
113
|
+
Selected Files ({selectedFiles.length})
|
|
114
|
+
</h2>
|
|
115
|
+
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
|
116
|
+
{selectedFiles.map((file, idx) => (
|
|
117
|
+
<div key={idx} className="border rounded-lg p-3 flex flex-col items-center gap-3 shadow-sm">
|
|
118
|
+
<div className="w-full aspect-square bg-gray-100 dark:bg-gray-800 rounded-md overflow-hidden flex items-center justify-center relative">
|
|
119
|
+
{file.url && file.mime?.startsWith("image/") ? (
|
|
120
|
+
<img src={file.url} alt={file.name} className="w-full h-full object-cover" />
|
|
121
|
+
) : (
|
|
122
|
+
<span className="text-gray-400 font-mono text-xs p-2 overflow-hidden text-ellipsis">
|
|
123
|
+
{file.ext?.toUpperCase() || "FILE"}
|
|
124
|
+
</span>
|
|
125
|
+
)}
|
|
126
|
+
</div>
|
|
127
|
+
<div className="w-full text-center">
|
|
128
|
+
<p className="text-sm font-medium truncate w-full" title={file.name}>{file.name}</p>
|
|
129
|
+
<p className="text-xs text-gray-500 mt-0.5">{(file.size / 1024).toFixed(1)} KB</p>
|
|
130
|
+
</div>
|
|
131
|
+
</div>
|
|
132
|
+
))}
|
|
133
|
+
</div>
|
|
134
|
+
</div>
|
|
135
|
+
)}
|
|
136
|
+
|
|
137
|
+
<FileManagerModal
|
|
138
|
+
open={isOpen}
|
|
139
|
+
onClose={() => setIsOpen(false)}
|
|
140
|
+
provider={provider}
|
|
141
|
+
allowedFileTypes={["images", "videos", "audios", "files"]}
|
|
142
|
+
onFilesSelected={(files: FileMetaData[]) => {
|
|
143
|
+
setSelectedFiles(files);
|
|
144
|
+
setIsOpen(false);
|
|
145
|
+
}}
|
|
146
|
+
basePath="/media"
|
|
147
|
+
/>
|
|
148
|
+
</div>
|
|
119
149
|
);
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
{
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
export default function ModalDemoPage() {
|
|
153
|
+
return (
|
|
154
|
+
<Suspense fallback={<div className="p-10">Loading...</div>}>
|
|
155
|
+
<ModalDemoContent />
|
|
156
|
+
</Suspense>
|
|
125
157
|
);
|
|
126
|
-
|
|
158
|
+
}
|
|
159
|
+
`);let r=i.join(e,"src","app","layout.tsx");if(t.existsSync(r)){let n=t.readFileSync(r,"utf8");n.includes("@unciatech/file-manager/styles")||(n=n.replace(/^(import type)/m,`import '@unciatech/file-manager/styles';
|
|
160
|
+
$1`),t.writeFileSync(r,n));}let m=i.join(e,"src","app","globals.css");t.writeFileSync(m,`@import "tailwindcss";
|
|
161
|
+
@import "@unciatech/file-manager/styles";
|
|
162
|
+
@import "tw-animate-css";
|
|
163
|
+
|
|
164
|
+
@source "../../node_modules/@unciatech/file-manager";
|
|
165
|
+
|
|
166
|
+
@theme {
|
|
167
|
+
--font-sans: "Inter", ui-sans-serif, system-ui, sans-serif;
|
|
168
|
+
--font-mono: ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, monospace;
|
|
169
|
+
}
|
|
170
|
+
`),f(o);}async function y(o,e){console.log(`
|
|
171
|
+
\u{1F4E6} Creating Vite React application...`),execSync(`npm create vite@latest ${o} -- --template react-ts`,{stdio:"inherit"}),console.log(`
|
|
172
|
+
\u{1F4E6} Installing dependencies (Tailwind + File Manager + React Router)...`),execSync("npm install",{cwd:e,stdio:"inherit"}),execSync("npm install tailwindcss @tailwindcss/vite @unciatech/file-manager react-router-dom",{cwd:e,stdio:"inherit"});let s=i.join(e,"vite.config.ts");t.writeFileSync(s,`import { defineConfig } from 'vite'
|
|
127
173
|
import react from '@vitejs/plugin-react'
|
|
128
174
|
import tailwindcss from '@tailwindcss/vite'
|
|
129
175
|
|
|
130
176
|
export default defineConfig({
|
|
131
|
-
plugins: [
|
|
177
|
+
plugins: [
|
|
178
|
+
react(),
|
|
179
|
+
tailwindcss(),
|
|
180
|
+
],
|
|
132
181
|
})
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
182
|
+
`);let d=i.join(e,"src","index.css");t.writeFileSync(d,`@import "tailwindcss";
|
|
183
|
+
@import "@unciatech/file-manager/styles";
|
|
184
|
+
@source "../node_modules/@unciatech/file-manager";
|
|
185
|
+
`);let r=i.join(e,"src","main.tsx");t.writeFileSync(r,`import React from 'react'
|
|
186
|
+
import ReactDOM from 'react-dom/client'
|
|
187
|
+
import { BrowserRouter } from 'react-router-dom'
|
|
188
|
+
import App from './App.tsx'
|
|
189
|
+
import './index.css'
|
|
137
190
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
`
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
)
|
|
154
|
-
const app = `import FileManagerDemo from "./components/FileManagerDemo";
|
|
191
|
+
ReactDOM.createRoot(document.getElementById('root')!).render(
|
|
192
|
+
<React.StrictMode>
|
|
193
|
+
<BrowserRouter>
|
|
194
|
+
<App />
|
|
195
|
+
</BrowserRouter>
|
|
196
|
+
</React.StrictMode>,
|
|
197
|
+
)
|
|
198
|
+
`);let m=i.join(e,"src","App.tsx");t.writeFileSync(m,`import { useState } from 'react'
|
|
199
|
+
import { useNavigate, Routes, Route, Link } from 'react-router-dom'
|
|
200
|
+
import { FileManager, FileManagerModal, MockProvider } from '@unciatech/file-manager'
|
|
201
|
+
import type { FileMetaData } from '@unciatech/file-manager'
|
|
202
|
+
|
|
203
|
+
const provider = new MockProvider()
|
|
204
|
+
|
|
205
|
+
function FullPage() {
|
|
206
|
+
const navigate = useNavigate()
|
|
155
207
|
|
|
156
|
-
function App() {
|
|
157
208
|
return (
|
|
158
|
-
<div className="
|
|
159
|
-
<
|
|
209
|
+
<div className="h-screen w-full flex flex-col">
|
|
210
|
+
<div className="flex gap-4 p-4 border-b border-border items-center justify-between">
|
|
211
|
+
<h1 className="text-xl font-bold">Full Page View</h1>
|
|
212
|
+
<Link
|
|
213
|
+
to="/"
|
|
214
|
+
className="px-4 py-2 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium"
|
|
215
|
+
>
|
|
216
|
+
Back to Modal Demo
|
|
217
|
+
</Link>
|
|
218
|
+
</div>
|
|
219
|
+
|
|
220
|
+
<div className="flex-1 relative overflow-hidden">
|
|
221
|
+
<FileManager
|
|
222
|
+
provider={provider}
|
|
223
|
+
basePath="full"
|
|
224
|
+
allowedFileTypes={["images", "videos", "audios", "files"]}
|
|
225
|
+
viewMode="grid"
|
|
226
|
+
onNavigate={(url, opts) => navigate(url, { replace: opts?.replace })}
|
|
227
|
+
/>
|
|
228
|
+
</div>
|
|
160
229
|
</div>
|
|
161
|
-
)
|
|
230
|
+
)
|
|
162
231
|
}
|
|
163
232
|
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
233
|
+
function ModalDemo() {
|
|
234
|
+
const navigate = useNavigate()
|
|
235
|
+
const [isModalOpen, setIsModalOpen] = useState(false)
|
|
236
|
+
const [selectedFiles, setSelectedFiles] = useState<FileMetaData[]>([])
|
|
237
|
+
|
|
238
|
+
return (
|
|
239
|
+
<div className="p-10 flex flex-col items-start gap-6 min-h-screen w-full bg-background text-foreground">
|
|
240
|
+
<div>
|
|
241
|
+
<h1 className="text-3xl font-bold mb-2">File Manager Demo</h1>
|
|
242
|
+
<p className="text-muted-foreground">Select files using the modal or browse the full page view.</p>
|
|
243
|
+
</div>
|
|
244
|
+
|
|
245
|
+
<div className="flex gap-4">
|
|
246
|
+
<button
|
|
247
|
+
onClick={() => setIsModalOpen(true)}
|
|
248
|
+
className="px-6 py-2.5 bg-blue-600 text-white rounded-md hover:bg-blue-700 cursor-pointer text-sm font-medium shadow-sm transition-colors"
|
|
249
|
+
>
|
|
250
|
+
Open File Picker Modal
|
|
251
|
+
</button>
|
|
252
|
+
<Link
|
|
253
|
+
to="/full"
|
|
254
|
+
className="px-6 py-2.5 bg-zinc-100 dark:bg-zinc-800 text-zinc-900 dark:text-zinc-100 rounded-md hover:bg-zinc-200 dark:hover:bg-zinc-700 cursor-pointer text-sm font-medium transition-colors"
|
|
255
|
+
>
|
|
256
|
+
Go to Full Page View
|
|
257
|
+
</Link>
|
|
258
|
+
</div>
|
|
259
|
+
|
|
260
|
+
{selectedFiles.length > 0 && (
|
|
261
|
+
<div className="w-full mt-8">
|
|
262
|
+
<h2 className="text-xl font-semibold mb-4 border-b pb-2">Selected Files ({selectedFiles.length})</h2>
|
|
263
|
+
<div className="grid grid-cols-2 md:grid-cols-4 lg:grid-cols-6 gap-4">
|
|
264
|
+
{selectedFiles.map((file, idx) => (
|
|
265
|
+
<div key={idx} className="border border-border rounded-lg p-3 flex flex-col items-center gap-3 bg-card shadow-sm">
|
|
266
|
+
<div className="w-full aspect-square bg-muted rounded-md overflow-hidden flex items-center justify-center relative">
|
|
267
|
+
{file.url && file.mime?.startsWith('image/') ? (
|
|
268
|
+
<img src={file.url} alt={file.name} className="w-full h-full object-cover" />
|
|
269
|
+
) : (
|
|
270
|
+
<span className="text-muted-foreground font-mono text-xs p-2 overflow-hidden text-ellipsis">{file.ext?.toUpperCase() || 'FILE'}</span>
|
|
271
|
+
)}
|
|
272
|
+
</div>
|
|
273
|
+
<div className="w-full text-center">
|
|
274
|
+
<p className="text-sm font-medium truncate w-full" title={file.name}>{file.name}</p>
|
|
275
|
+
<p className="text-xs text-muted-foreground mt-0.5">{(file.size / 1024).toFixed(1)} KB</p>
|
|
276
|
+
</div>
|
|
277
|
+
</div>
|
|
278
|
+
))}
|
|
279
|
+
</div>
|
|
280
|
+
</div>
|
|
281
|
+
)}
|
|
282
|
+
|
|
283
|
+
<FileManagerModal
|
|
284
|
+
open={isModalOpen}
|
|
285
|
+
onClose={() => setIsModalOpen(false)}
|
|
286
|
+
provider={provider}
|
|
287
|
+
basePath="/"
|
|
288
|
+
allowedFileTypes={["images", "videos", "audios", "files"]}
|
|
289
|
+
onFilesSelected={(files: FileMetaData[]) => {
|
|
290
|
+
setSelectedFiles(files)
|
|
291
|
+
}}
|
|
292
|
+
onNavigate={(url, opts) => navigate(url, { replace: opts?.replace })}
|
|
293
|
+
/>
|
|
294
|
+
</div>
|
|
295
|
+
)
|
|
168
296
|
}
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
`);
|
|
297
|
+
|
|
298
|
+
function App() {
|
|
299
|
+
return (
|
|
300
|
+
<Routes>
|
|
301
|
+
<Route path="/" element={<ModalDemo />} />
|
|
302
|
+
<Route path="/full/*" element={<FullPage />} />
|
|
303
|
+
</Routes>
|
|
304
|
+
)
|
|
178
305
|
}
|
|
179
|
-
|
|
306
|
+
|
|
307
|
+
export default App
|
|
308
|
+
`);let n=i.join(e,"src","App.css");t.existsSync(n)&&t.unlinkSync(n),f(o,"npm run dev");}function f(o,e="npm run dev"){console.log(`
|
|
309
|
+
=========================================`),console.log("\u{1F389} Your Media Library application is ready!"),console.log("========================================="),console.log(`
|
|
310
|
+
Next steps:`),console.log(` cd ${o}`),console.log(` ${e}`),console.log(`
|
|
311
|
+
Enjoy building! \u{1F5C2}\uFE0F
|
|
312
|
+
`);}w();
|