@marimo-team/frontend 0.23.2-dev53 → 0.23.2-dev54
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/index.html
CHANGED
|
@@ -66,7 +66,7 @@
|
|
|
66
66
|
<marimo-server-token data-token="{{ server_token }}" hidden></marimo-server-token>
|
|
67
67
|
<!-- /TODO -->
|
|
68
68
|
<title>{{ title }}</title>
|
|
69
|
-
<script type="module" crossorigin src="./assets/index-
|
|
69
|
+
<script type="module" crossorigin src="./assets/index-DhasKuQs.js"></script>
|
|
70
70
|
<link rel="modulepreload" crossorigin href="./assets/preload-helper-BFv3hW2s.js">
|
|
71
71
|
<link rel="modulepreload" crossorigin href="./assets/chunk-LvLJmgfZ.js">
|
|
72
72
|
<link rel="modulepreload" crossorigin href="./assets/react-Bj1aDYRI.js">
|
|
@@ -243,7 +243,7 @@
|
|
|
243
243
|
<link rel="stylesheet" crossorigin href="./assets/cells-jmgGt1lS.css">
|
|
244
244
|
<link rel="stylesheet" crossorigin href="./assets/markdown-renderer-DdDKmWlR.css">
|
|
245
245
|
<link rel="stylesheet" crossorigin href="./assets/JsonOutput-B7vuddcd.css">
|
|
246
|
-
<link rel="stylesheet" crossorigin href="./assets/index-
|
|
246
|
+
<link rel="stylesheet" crossorigin href="./assets/index-4VEstEY3.css">
|
|
247
247
|
</head>
|
|
248
248
|
<body>
|
|
249
249
|
<div id="root"></div>
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
/* Copyright 2026 Marimo. All rights reserved. */
|
|
2
2
|
|
|
3
|
-
import { CornerLeftUp } from "lucide-react";
|
|
3
|
+
import { type LucideIcon, CornerLeftUp } from "lucide-react";
|
|
4
4
|
import { type JSX, useEffect, useState } from "react";
|
|
5
5
|
import { z } from "zod";
|
|
6
6
|
import {
|
|
@@ -128,6 +128,43 @@ interface FileBrowserProps extends Data, PluginFunctions {
|
|
|
128
128
|
host: HTMLElement;
|
|
129
129
|
}
|
|
130
130
|
|
|
131
|
+
interface CheckboxOrIconProps {
|
|
132
|
+
isSelected: boolean;
|
|
133
|
+
canSelect: boolean;
|
|
134
|
+
Icon: LucideIcon;
|
|
135
|
+
onSelect: () => void;
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
function CheckboxOrIcon({
|
|
139
|
+
isSelected,
|
|
140
|
+
canSelect,
|
|
141
|
+
Icon,
|
|
142
|
+
onSelect,
|
|
143
|
+
}: CheckboxOrIconProps) {
|
|
144
|
+
if (canSelect) {
|
|
145
|
+
return (
|
|
146
|
+
<>
|
|
147
|
+
<Checkbox
|
|
148
|
+
checked={isSelected}
|
|
149
|
+
onClick={(e) => {
|
|
150
|
+
onSelect();
|
|
151
|
+
e.stopPropagation();
|
|
152
|
+
}}
|
|
153
|
+
className={cn({ "hidden group-hover:flex": !isSelected })}
|
|
154
|
+
/>
|
|
155
|
+
<Icon
|
|
156
|
+
size={16}
|
|
157
|
+
className={cn("mr-2", {
|
|
158
|
+
hidden: isSelected,
|
|
159
|
+
"group-hover:hidden": !isSelected,
|
|
160
|
+
})}
|
|
161
|
+
/>
|
|
162
|
+
</>
|
|
163
|
+
);
|
|
164
|
+
}
|
|
165
|
+
return <Icon size={16} className="mr-2" />;
|
|
166
|
+
}
|
|
167
|
+
|
|
131
168
|
/**
|
|
132
169
|
* File browser component.
|
|
133
170
|
*
|
|
@@ -145,7 +182,6 @@ export const FileBrowser = ({
|
|
|
145
182
|
host,
|
|
146
183
|
}: FileBrowserProps): JSX.Element | null => {
|
|
147
184
|
const [path, setPath] = useInternalStateWithSync(initialPath);
|
|
148
|
-
const [selectAllLabel, setSelectAllLabel] = useState("Select all");
|
|
149
185
|
const [isUpdatingPath, setIsUpdatingPath] = useState(false);
|
|
150
186
|
const [showLoadingOverlay, setShowLoadingOverlay] = useState(false);
|
|
151
187
|
|
|
@@ -158,7 +194,6 @@ export const FileBrowser = ({
|
|
|
158
194
|
const { data, error, isPending } = useAsyncData(() => {
|
|
159
195
|
return list_directory({ path: path });
|
|
160
196
|
}, [path, randomId]);
|
|
161
|
-
const spinnerLabel = "Listing files...";
|
|
162
197
|
|
|
163
198
|
useEffect(() => {
|
|
164
199
|
if (!isPending) {
|
|
@@ -175,25 +210,29 @@ export const FileBrowser = ({
|
|
|
175
210
|
};
|
|
176
211
|
}, [isPending]);
|
|
177
212
|
|
|
213
|
+
const files = data?.files ?? [];
|
|
214
|
+
const selectedPaths = new Set(value.map((x) => x.path));
|
|
215
|
+
const canSelectDirectories =
|
|
216
|
+
selectionMode === "directory" || selectionMode === "all";
|
|
217
|
+
const canSelectFiles = selectionMode === "file" || selectionMode === "all";
|
|
218
|
+
|
|
219
|
+
const selectable = files.filter(
|
|
220
|
+
(f) =>
|
|
221
|
+
(canSelectDirectories && f.is_directory) ||
|
|
222
|
+
(canSelectFiles && !f.is_directory),
|
|
223
|
+
);
|
|
224
|
+
const allSelected =
|
|
225
|
+
selectable.length > 0 && selectable.every((f) => selectedPaths.has(f.path));
|
|
226
|
+
|
|
178
227
|
if (!data && error) {
|
|
179
228
|
return <Banner kind="danger">{error.message}</Banner>;
|
|
180
229
|
}
|
|
181
230
|
|
|
182
|
-
let { files } = data || {};
|
|
183
|
-
if (files === undefined) {
|
|
184
|
-
files = [];
|
|
185
|
-
}
|
|
186
|
-
|
|
187
231
|
const pathBuilder = PathBuilder.guessDeliminator(initialPath);
|
|
188
232
|
const delimiter = pathBuilder.deliminator;
|
|
189
233
|
|
|
190
|
-
const selectedPaths = new Set(value.map((x) => x.path));
|
|
191
234
|
const selectedFiles = value.map((x) => <li key={x.id}>{x.path}</li>);
|
|
192
235
|
|
|
193
|
-
const canSelectDirectories =
|
|
194
|
-
selectionMode === "directory" || selectionMode === "all";
|
|
195
|
-
const canSelectFiles = selectionMode === "file" || selectionMode === "all";
|
|
196
|
-
|
|
197
236
|
function setNewPath(newPath: string) {
|
|
198
237
|
// Prevent updating path while updating
|
|
199
238
|
if (isUpdatingPath) {
|
|
@@ -230,9 +269,7 @@ export const FileBrowser = ({
|
|
|
230
269
|
return;
|
|
231
270
|
}
|
|
232
271
|
|
|
233
|
-
// Update path and reset select all label
|
|
234
272
|
setPath(newPath);
|
|
235
|
-
setSelectAllLabel("Select all");
|
|
236
273
|
setIsUpdatingPath(false);
|
|
237
274
|
}
|
|
238
275
|
|
|
@@ -264,28 +301,18 @@ export const FileBrowser = ({
|
|
|
264
301
|
}) {
|
|
265
302
|
const fileInfo = createFileInfo({ path, name, isDirectory });
|
|
266
303
|
|
|
267
|
-
if (
|
|
268
|
-
|
|
269
|
-
setValue(value.filter((x) => x.path !== path));
|
|
270
|
-
setSelectAllLabel("Select all");
|
|
271
|
-
} else {
|
|
272
|
-
setValue([...value, fileInfo]);
|
|
273
|
-
}
|
|
304
|
+
if (selectedPaths.has(path)) {
|
|
305
|
+
setValue(value.filter((x) => x.path !== path));
|
|
274
306
|
} else {
|
|
275
|
-
setValue([fileInfo]);
|
|
307
|
+
setValue(multiple ? [...value, fileInfo] : [fileInfo]);
|
|
276
308
|
}
|
|
277
309
|
}
|
|
278
310
|
|
|
279
311
|
function deselectAllFiles() {
|
|
280
312
|
setValue(value.filter((x) => Paths.dirname(x.path) !== path));
|
|
281
|
-
setSelectAllLabel("Select all");
|
|
282
313
|
}
|
|
283
314
|
|
|
284
315
|
function selectAllFiles() {
|
|
285
|
-
if (!files) {
|
|
286
|
-
return;
|
|
287
|
-
}
|
|
288
|
-
|
|
289
316
|
const filesInView: FileInfo[] = [];
|
|
290
317
|
|
|
291
318
|
for (const file of files) {
|
|
@@ -304,7 +331,6 @@ export const FileBrowser = ({
|
|
|
304
331
|
}
|
|
305
332
|
|
|
306
333
|
setValue([...value, ...filesInView]);
|
|
307
|
-
setSelectAllLabel("Deselect all");
|
|
308
334
|
}
|
|
309
335
|
|
|
310
336
|
// Create rows for directories and files
|
|
@@ -313,7 +339,7 @@ export const FileBrowser = ({
|
|
|
313
339
|
// Parent directory ".." row button
|
|
314
340
|
fileRows.push(
|
|
315
341
|
<TableRow
|
|
316
|
-
className="hover:bg-
|
|
342
|
+
className="hover:bg-accent select-none"
|
|
317
343
|
key={"Parent directory"}
|
|
318
344
|
onClick={() => setNewPath(PARENT_DIRECTORY)}
|
|
319
345
|
>
|
|
@@ -344,50 +370,13 @@ export const FileBrowser = ({
|
|
|
344
370
|
const Icon = FILE_TYPE_ICONS[fileType];
|
|
345
371
|
|
|
346
372
|
const isSelected = selectedPaths.has(filePath);
|
|
347
|
-
const renderCheckboxOrIcon = () => {
|
|
348
|
-
if (
|
|
349
|
-
(canSelectDirectories && file.is_directory) ||
|
|
350
|
-
(canSelectFiles && !file.is_directory)
|
|
351
|
-
) {
|
|
352
|
-
return (
|
|
353
|
-
<>
|
|
354
|
-
<Checkbox
|
|
355
|
-
checked={isSelected}
|
|
356
|
-
onClick={(e) => {
|
|
357
|
-
handleSelection({
|
|
358
|
-
path: filePath,
|
|
359
|
-
name: file.name,
|
|
360
|
-
isDirectory: file.is_directory,
|
|
361
|
-
});
|
|
362
|
-
e.stopPropagation();
|
|
363
|
-
}}
|
|
364
|
-
className={cn("", {
|
|
365
|
-
"hidden group-hover:flex": !isSelected,
|
|
366
|
-
})}
|
|
367
|
-
/>
|
|
368
|
-
<Icon
|
|
369
|
-
size={16}
|
|
370
|
-
className={cn("mr-2", {
|
|
371
|
-
hidden: isSelected,
|
|
372
|
-
"group-hover:hidden": !isSelected,
|
|
373
|
-
})}
|
|
374
|
-
/>
|
|
375
|
-
</>
|
|
376
|
-
);
|
|
377
|
-
}
|
|
378
|
-
|
|
379
|
-
return <Icon size={16} className="mr-2" />;
|
|
380
|
-
};
|
|
381
373
|
|
|
382
374
|
fileRows.push(
|
|
383
375
|
<TableRow
|
|
384
376
|
key={file.id}
|
|
385
|
-
className={cn(
|
|
386
|
-
"
|
|
387
|
-
|
|
388
|
-
"bg-primary bg-opacity-25": isSelected,
|
|
389
|
-
},
|
|
390
|
-
)}
|
|
377
|
+
className={cn("hover:bg-accent group select-none", {
|
|
378
|
+
"bg-primary/25 hover:bg-primary/35": isSelected,
|
|
379
|
+
})}
|
|
391
380
|
onClick={() =>
|
|
392
381
|
handleClick({
|
|
393
382
|
path: filePath,
|
|
@@ -397,7 +386,21 @@ export const FileBrowser = ({
|
|
|
397
386
|
}
|
|
398
387
|
>
|
|
399
388
|
<TableCell className="w-[50px] pl-4">
|
|
400
|
-
|
|
389
|
+
<CheckboxOrIcon
|
|
390
|
+
isSelected={isSelected}
|
|
391
|
+
canSelect={
|
|
392
|
+
(canSelectDirectories && file.is_directory) ||
|
|
393
|
+
(canSelectFiles && !file.is_directory)
|
|
394
|
+
}
|
|
395
|
+
Icon={Icon}
|
|
396
|
+
onSelect={() =>
|
|
397
|
+
handleSelection({
|
|
398
|
+
path: filePath,
|
|
399
|
+
name: file.name,
|
|
400
|
+
isDirectory: file.is_directory,
|
|
401
|
+
})
|
|
402
|
+
}
|
|
403
|
+
/>
|
|
401
404
|
</TableCell>
|
|
402
405
|
<TableCell>{file.name}</TableCell>
|
|
403
406
|
</TableRow>,
|
|
@@ -423,8 +426,9 @@ export const FileBrowser = ({
|
|
|
423
426
|
: PluralWords.of("file");
|
|
424
427
|
|
|
425
428
|
const renderHeader = () => {
|
|
426
|
-
|
|
427
|
-
|
|
429
|
+
const displayLabel =
|
|
430
|
+
label ?? `Select ${selectionKindLabel.join(" and ", 2)}...`;
|
|
431
|
+
const labelText = <Label>{renderHTML({ html: displayLabel })}</Label>;
|
|
428
432
|
|
|
429
433
|
if (multiple) {
|
|
430
434
|
return (
|
|
@@ -434,13 +438,9 @@ export const FileBrowser = ({
|
|
|
434
438
|
<Button
|
|
435
439
|
size="xs"
|
|
436
440
|
variant="link"
|
|
437
|
-
onClick={
|
|
438
|
-
selectAllLabel === "Select all"
|
|
439
|
-
? () => selectAllFiles()
|
|
440
|
-
: () => deselectAllFiles()
|
|
441
|
-
}
|
|
441
|
+
onClick={allSelected ? deselectAllFiles : selectAllFiles}
|
|
442
442
|
>
|
|
443
|
-
{
|
|
443
|
+
{allSelected ? "Deselect all" : "Select all"}
|
|
444
444
|
</Button>
|
|
445
445
|
</div>
|
|
446
446
|
</div>
|
|
@@ -461,7 +461,7 @@ export const FileBrowser = ({
|
|
|
461
461
|
onChange={(e) => setNewPath(e.target.value)}
|
|
462
462
|
>
|
|
463
463
|
{parentDirectories.map((dir) => (
|
|
464
|
-
<option value={dir} key={dir}
|
|
464
|
+
<option value={dir} key={dir}>
|
|
465
465
|
{dir}
|
|
466
466
|
</option>
|
|
467
467
|
))}
|
|
@@ -487,7 +487,7 @@ export const FileBrowser = ({
|
|
|
487
487
|
role="status"
|
|
488
488
|
>
|
|
489
489
|
<Spinner size="small" />
|
|
490
|
-
<span>
|
|
490
|
+
<span>Listing files...</span>
|
|
491
491
|
</div>
|
|
492
492
|
)}
|
|
493
493
|
<Table className="cursor-pointer table-fixed">
|