@vryzel/file-next-ui 0.4.2 → 0.4.4

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.js CHANGED
@@ -147,6 +147,62 @@ function useExplorerLabels() {
147
147
  function formatLabel(template, vars) {
148
148
  return template.replace(/\{(\w+)\}/g, (_, key) => String(vars[key] ?? ""));
149
149
  }
150
+ function RenameInput({
151
+ name,
152
+ kind,
153
+ ariaLabel,
154
+ onCommit,
155
+ onCancel,
156
+ className
157
+ }) {
158
+ const ref = useRef(null);
159
+ const done = useRef(false);
160
+ useEffect(() => {
161
+ const el = ref.current;
162
+ if (!el) return;
163
+ el.focus();
164
+ const dot = name.lastIndexOf(".");
165
+ if (kind === "file" && dot > 0) el.setSelectionRange(0, dot);
166
+ else el.select();
167
+ }, [kind, name]);
168
+ const finish = (next) => {
169
+ if (done.current) return;
170
+ done.current = true;
171
+ const trimmed = next?.trim() ?? "";
172
+ if (!trimmed) {
173
+ onCancel();
174
+ return;
175
+ }
176
+ onCommit(trimmed);
177
+ };
178
+ return /* @__PURE__ */ jsx(
179
+ "input",
180
+ {
181
+ ref,
182
+ defaultValue: name,
183
+ "aria-label": ariaLabel ?? "Rename",
184
+ className: cn(
185
+ "h-7 min-w-0 flex-1 rounded-[6px] border border-ring bg-background px-1.5 text-sm outline-none",
186
+ className
187
+ ),
188
+ onClick: (event) => event.stopPropagation(),
189
+ onDoubleClick: (event) => event.stopPropagation(),
190
+ onPointerDown: (event) => event.stopPropagation(),
191
+ onKeyDown: (event) => {
192
+ event.stopPropagation();
193
+ if (event.key === "Enter") {
194
+ event.preventDefault();
195
+ finish(event.currentTarget.value);
196
+ }
197
+ if (event.key === "Escape") {
198
+ event.preventDefault();
199
+ finish(null);
200
+ }
201
+ },
202
+ onBlur: (event) => finish(event.currentTarget.value)
203
+ }
204
+ );
205
+ }
150
206
  var LONG_PRESS_MS = 450;
151
207
  var MOVE_CANCEL_PX = 10;
152
208
  function useCoarsePointer() {
@@ -244,6 +300,9 @@ function ExplorerListView(props) {
244
300
  sortKey,
245
301
  sortDirection,
246
302
  onSortChange,
303
+ renamingId,
304
+ onRenameCommit,
305
+ onRenameCancel,
247
306
  className
248
307
  } = props;
249
308
  const labels = useExplorerLabels();
@@ -284,7 +343,10 @@ function ExplorerListView(props) {
284
343
  onDragStart,
285
344
  onDragEnd,
286
345
  onDrop,
287
- onDragOverRow
346
+ onDragOverRow,
347
+ renaming: file.id === renamingId,
348
+ onRenameCommit,
349
+ onRenameCancel
288
350
  },
289
351
  file.id
290
352
  )) })
@@ -306,7 +368,10 @@ function ExplorerListRow({
306
368
  onDragStart,
307
369
  onDragEnd,
308
370
  onDrop,
309
- onDragOverRow
371
+ onDragOverRow,
372
+ renaming,
373
+ onRenameCommit,
374
+ onRenameCancel
310
375
  }) {
311
376
  const labels = useExplorerLabels();
312
377
  const isFolder = file.kind === "folder";
@@ -317,17 +382,18 @@ function ExplorerListRow({
317
382
  role: "row",
318
383
  "data-file-id": file.id,
319
384
  "aria-selected": selected,
320
- draggable: !coarse,
385
+ draggable: !coarse && !renaming,
321
386
  onDragStart: (e) => {
322
387
  e.dataTransfer.setData(DRAG_MIME, file.id);
323
388
  e.dataTransfer.effectAllowed = "move";
324
389
  onDragStart(file.id);
325
390
  },
326
391
  onDragEnd,
327
- onPointerDown: pointer.onPointerDown,
328
- onClick: pointer.onClick,
329
- onDoubleClick: pointer.onDoubleClick,
392
+ onPointerDown: renaming ? void 0 : pointer.onPointerDown,
393
+ onClick: renaming ? void 0 : pointer.onClick,
394
+ onDoubleClick: renaming ? void 0 : pointer.onDoubleClick,
330
395
  onKeyDown: (e) => {
396
+ if (renaming) return;
331
397
  if (e.key === "Enter") {
332
398
  e.preventDefault();
333
399
  onActivate(file);
@@ -369,7 +435,16 @@ function ExplorerListRow({
369
435
  /* @__PURE__ */ jsx("span", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground tabular-nums", children: String(idx + 1).padStart(2, "0") }),
370
436
  /* @__PURE__ */ jsx("span", { className: isFolder ? "text-primary" : "text-muted-foreground", children: /* @__PURE__ */ jsx(FileIcon, { kind: file.kind, mimeType: file.mimeType, className: "size-5 text-current sm:size-4" }) }),
371
437
  /* @__PURE__ */ jsxs("span", { className: "flex min-w-0 items-center gap-1.5", children: [
372
- /* @__PURE__ */ jsx("span", { className: cn("truncate", isFolder && "text-primary"), children: file.name }),
438
+ renaming && onRenameCommit && onRenameCancel ? /* @__PURE__ */ jsx(
439
+ RenameInput,
440
+ {
441
+ name: file.name,
442
+ kind: file.kind,
443
+ ariaLabel: labels.rename,
444
+ onCommit: onRenameCommit,
445
+ onCancel: onRenameCancel
446
+ }
447
+ ) : /* @__PURE__ */ jsx("span", { className: cn("truncate", isFolder && "text-primary"), children: file.name }),
373
448
  isProtected ? /* @__PURE__ */ jsxs("span", { className: "flex shrink-0 items-center gap-0.5 rounded-[10px] bg-primary/10 px-1.5 text-[10px] text-primary", children: [
374
449
  /* @__PURE__ */ jsx(Link2, { "aria-hidden": "true", className: "size-2.5" }),
375
450
  labels.inUse
@@ -446,6 +521,9 @@ function ExplorerGridView(props) {
446
521
  draggingIds,
447
522
  dropTargetId,
448
523
  onDragOverRow,
524
+ renamingId,
525
+ onRenameCommit,
526
+ onRenameCancel,
449
527
  className
450
528
  } = props;
451
529
  const labels = useExplorerLabels();
@@ -471,7 +549,10 @@ function ExplorerGridView(props) {
471
549
  onDragStart,
472
550
  onDragEnd,
473
551
  onDrop,
474
- onDragOverRow
552
+ onDragOverRow,
553
+ renaming: file.id === renamingId,
554
+ onRenameCommit,
555
+ onRenameCancel
475
556
  },
476
557
  file.id
477
558
  )) });
@@ -495,9 +576,42 @@ function ExplorerGridItem({
495
576
  onDragStart,
496
577
  onDragEnd,
497
578
  onDrop,
498
- onDragOverRow
579
+ onDragOverRow,
580
+ renaming,
581
+ onRenameCommit,
582
+ onRenameCancel
499
583
  }) {
584
+ const labels = useExplorerLabels();
500
585
  const pointer = useExplorerItemPointer({ file, coarse, onSelect, onActivate });
586
+ const bodyClass = cn(
587
+ "group relative flex w-full flex-col items-stretch gap-1 overflow-hidden rounded-[10px] border border-border bg-background p-2 text-left outline-none",
588
+ "touch-manipulation select-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring",
589
+ selected && "border-primary bg-muted",
590
+ dragging && "opacity-50",
591
+ dropTarget && "ring-2 ring-primary ring-inset"
592
+ );
593
+ const inner = /* @__PURE__ */ jsxs(Fragment, { children: [
594
+ /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 pr-8", children: [
595
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[9px] uppercase tracking-[0.12em] text-muted-foreground tabular-nums", children: String(idx + 1).padStart(2, "0") }),
596
+ isProtected ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 rounded-[10px] bg-primary/10 px-1.5 py-0.5 text-[10px] text-primary", children: [
597
+ /* @__PURE__ */ jsx(Link2, { "aria-hidden": "true", className: "size-2.5" }),
598
+ /* @__PURE__ */ jsx("span", { className: "sr-only", children: inUseLabel })
599
+ ] }) : null
600
+ ] }),
601
+ /* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center py-1", file.kind === "folder" ? "text-primary" : "text-muted-foreground"), children: /* @__PURE__ */ jsx(FileIcon, { kind: file.kind, mimeType: file.mimeType, className: "size-12 text-current" }) }),
602
+ renaming && onRenameCommit && onRenameCancel ? /* @__PURE__ */ jsx(
603
+ RenameInput,
604
+ {
605
+ name: file.name,
606
+ kind: file.kind,
607
+ ariaLabel: labels.rename,
608
+ className: "text-[11px]",
609
+ onCommit: onRenameCommit,
610
+ onCancel: onRenameCancel
611
+ }
612
+ ) : /* @__PURE__ */ jsx("span", { className: cn("truncate text-[11px]", file.kind === "folder" && "text-primary"), children: file.name }),
613
+ /* @__PURE__ */ jsx("span", { className: "font-mono text-[9px] uppercase tracking-[0.12em] text-muted-foreground", children: file.kind === "folder" ? folderLabel : formatSizeShort(file.size) })
614
+ ] });
501
615
  return /* @__PURE__ */ jsxs("li", { className: "relative", children: [
502
616
  coarse && onContextMenu ? /* @__PURE__ */ jsx(
503
617
  "button",
@@ -513,7 +627,7 @@ function ExplorerGridItem({
513
627
  children: /* @__PURE__ */ jsx(MoreHorizontal, { "aria-hidden": "true", className: "size-5" })
514
628
  }
515
629
  ) : null,
516
- /* @__PURE__ */ jsxs(
630
+ renaming ? /* @__PURE__ */ jsx("div", { "data-file-id": file.id, className: bodyClass, children: inner }) : /* @__PURE__ */ jsx(
517
631
  "button",
518
632
  {
519
633
  type: "button",
@@ -555,25 +669,8 @@ function ExplorerGridItem({
555
669
  e.preventDefault();
556
670
  onDrop(file.id);
557
671
  },
558
- className: cn(
559
- "group relative flex w-full flex-col items-stretch gap-1 overflow-hidden rounded-[10px] border border-border bg-background p-2 text-left outline-none",
560
- "touch-manipulation select-none hover:bg-muted focus-visible:ring-2 focus-visible:ring-ring",
561
- selected && "border-primary bg-muted",
562
- dragging && "opacity-50",
563
- dropTarget && "ring-2 ring-primary ring-inset"
564
- ),
565
- children: [
566
- /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-1 pr-8", children: [
567
- /* @__PURE__ */ jsx("span", { className: "font-mono text-[9px] uppercase tracking-[0.12em] text-muted-foreground tabular-nums", children: String(idx + 1).padStart(2, "0") }),
568
- isProtected ? /* @__PURE__ */ jsxs("span", { className: "flex items-center gap-0.5 rounded-[10px] bg-primary/10 px-1.5 py-0.5 text-[10px] text-primary", children: [
569
- /* @__PURE__ */ jsx(Link2, { "aria-hidden": "true", className: "size-2.5" }),
570
- /* @__PURE__ */ jsx("span", { className: "sr-only", children: inUseLabel })
571
- ] }) : null
572
- ] }),
573
- /* @__PURE__ */ jsx("span", { className: cn("flex items-center justify-center py-1", file.kind === "folder" ? "text-primary" : "text-muted-foreground"), children: /* @__PURE__ */ jsx(FileIcon, { kind: file.kind, mimeType: file.mimeType, className: "size-12 text-current" }) }),
574
- /* @__PURE__ */ jsx("span", { className: cn("truncate text-[11px]", file.kind === "folder" && "text-primary"), children: file.name }),
575
- /* @__PURE__ */ jsx("span", { className: "font-mono text-[9px] uppercase tracking-[0.12em] text-muted-foreground", children: file.kind === "folder" ? folderLabel : formatSizeShort(file.size) })
576
- ]
672
+ className: bodyClass,
673
+ children: inner
577
674
  }
578
675
  )
579
676
  ] });
@@ -742,6 +839,13 @@ function swallowClickThrough() {
742
839
  window.removeEventListener("pointerup", swallow, true);
743
840
  }, 400);
744
841
  }
842
+
843
+ // src/share-url.ts
844
+ function absoluteShareUrl(link) {
845
+ if (link.startsWith("http://") || link.startsWith("https://")) return link;
846
+ const origin = typeof window === "undefined" ? "" : window.location.origin;
847
+ return `${origin}${link.startsWith("/") ? link : `/${link}`}`;
848
+ }
745
849
  function ExplorerContextMenu(props) {
746
850
  const {
747
851
  target,
@@ -894,7 +998,7 @@ function ExplorerContextMenu(props) {
894
998
  /* @__PURE__ */ jsx(Copy, { "aria-hidden": "true", className: "size-4" }),
895
999
  labels.paste
896
1000
  ] }) : null,
897
- actions.createShare ? /* @__PURE__ */ jsxs(
1001
+ target.file.kind === "file" && actions.createShare ? /* @__PURE__ */ jsxs(
898
1002
  "button",
899
1003
  {
900
1004
  type: "button",
@@ -903,7 +1007,7 @@ function ExplorerContextMenu(props) {
903
1007
  onClick: () => {
904
1008
  close();
905
1009
  void actions.createShare({ id: target.file.id }).then(
906
- (token) => navigator.clipboard.writeText(token)
1010
+ (link) => navigator.clipboard.writeText(absoluteShareUrl(link))
907
1011
  );
908
1012
  },
909
1013
  children: [
@@ -963,92 +1067,6 @@ function Overlay({
963
1067
  }
964
1068
  );
965
1069
  }
966
- function CreateFolderDialog({
967
- open,
968
- onOpenChange,
969
- onCreate,
970
- title,
971
- description,
972
- confirmLabel,
973
- initialName = ""
974
- }) {
975
- const labels = useExplorerLabels();
976
- const [name, setName] = useState(initialName);
977
- const [saving, setSaving] = useState(false);
978
- const [error, setError] = useState(null);
979
- const inputRef = useRef(null);
980
- useEffect(() => {
981
- if (!open) {
982
- setSaving(false);
983
- setError(null);
984
- return;
985
- }
986
- setName(initialName);
987
- const id = window.setTimeout(() => {
988
- inputRef.current?.focus();
989
- inputRef.current?.select();
990
- }, 0);
991
- return () => window.clearTimeout(id);
992
- }, [initialName, open]);
993
- async function submit() {
994
- const trimmed = name.trim();
995
- if (!trimmed || saving) return;
996
- setSaving(true);
997
- setError(null);
998
- try {
999
- await onCreate(trimmed);
1000
- onOpenChange(false);
1001
- } catch (cause) {
1002
- const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
1003
- setError(code === "Conflict" ? labels.folderExists : labels.folderCreateFailed);
1004
- } finally {
1005
- setSaving(false);
1006
- }
1007
- }
1008
- return /* @__PURE__ */ jsxs(Overlay, { open, onClose: () => onOpenChange(false), children: [
1009
- /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title ?? labels.newFolder }),
1010
- /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description ?? labels.folderNameHint }),
1011
- /* @__PURE__ */ jsx(
1012
- "input",
1013
- {
1014
- ref: inputRef,
1015
- value: name,
1016
- onChange: (event) => setName(event.target.value),
1017
- onKeyDown: (event) => {
1018
- if (event.key === "Enter") {
1019
- event.preventDefault();
1020
- void submit();
1021
- }
1022
- },
1023
- placeholder: labels.folderName,
1024
- "aria-invalid": Boolean(error),
1025
- className: "mt-3 h-9 w-full rounded-[10px] border border-border bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
1026
- }
1027
- ),
1028
- error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-destructive", children: error }) : null,
1029
- /* @__PURE__ */ jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
1030
- /* @__PURE__ */ jsx(
1031
- "button",
1032
- {
1033
- type: "button",
1034
- className: "inline-flex h-8 items-center rounded-[10px] border border-border px-3 text-sm",
1035
- onClick: () => onOpenChange(false),
1036
- children: labels.cancel
1037
- }
1038
- ),
1039
- /* @__PURE__ */ jsx(
1040
- "button",
1041
- {
1042
- type: "button",
1043
- disabled: !name.trim() || saving,
1044
- className: "inline-flex h-8 items-center rounded-[10px] bg-primary px-3 text-sm text-primary-foreground disabled:opacity-50",
1045
- onClick: () => void submit(),
1046
- children: confirmLabel ?? labels.createFolder
1047
- }
1048
- )
1049
- ] })
1050
- ] });
1051
- }
1052
1070
  function ConfirmDeleteDialog({
1053
1071
  open,
1054
1072
  names,
@@ -1514,6 +1532,32 @@ function UploadQueueProvider({
1514
1532
  }
1515
1533
  var PAGE_SIZE = 50;
1516
1534
  var DEFAULT_PREFS = { view: "list", sortKey: "name", sortDir: "asc" };
1535
+ var DRAFT_FOLDER_ID = "__draft-folder__";
1536
+ function unusedFolderName(files, base) {
1537
+ const taken = new Set(files.map((file) => file.name.toLowerCase()));
1538
+ if (!taken.has(base.toLowerCase())) return base;
1539
+ let n = 2;
1540
+ while (taken.has(`${base} ${n}`.toLowerCase())) n += 1;
1541
+ return `${base} ${n}`;
1542
+ }
1543
+ function draftFolder(name, tenantId, parentId) {
1544
+ return {
1545
+ id: DRAFT_FOLDER_ID,
1546
+ tenantId,
1547
+ parentId,
1548
+ name,
1549
+ path: name,
1550
+ kind: "folder",
1551
+ size: 0,
1552
+ mimeType: "",
1553
+ s3Key: "",
1554
+ ownerId: "",
1555
+ metadata: {},
1556
+ createdAt: /* @__PURE__ */ new Date(0),
1557
+ updatedAt: /* @__PURE__ */ new Date(0),
1558
+ deletedAt: null
1559
+ };
1560
+ }
1517
1561
  function reviveFile(node) {
1518
1562
  const asDate = (value) => value instanceof Date ? value : new Date(value);
1519
1563
  return {
@@ -1637,7 +1681,7 @@ function FileExplorerInner(props) {
1637
1681
  const [prefsReady, setPrefsReady] = useState(!persistViewKey);
1638
1682
  const skipPrefsWrite = useRef(!persistViewKey);
1639
1683
  const [menu, setMenu] = useState(null);
1640
- const [folderOpen, setFolderOpen] = useState(false);
1684
+ const [draftFolderName, setDraftFolderName] = useState(null);
1641
1685
  const [renameTarget, setRenameTarget] = useState(null);
1642
1686
  const [deleteTargets, setDeleteTargets] = useState([]);
1643
1687
  const [purgeTargets, setPurgeTargets] = useState([]);
@@ -1819,6 +1863,7 @@ function FileExplorerInner(props) {
1819
1863
  }, [refreshKey]);
1820
1864
  const handleActivate = useCallback(
1821
1865
  (file) => {
1866
+ if (file.id === DRAFT_FOLDER_ID) return;
1822
1867
  if (file.kind === "folder") {
1823
1868
  onOpenFolder?.(file);
1824
1869
  return;
@@ -1830,6 +1875,7 @@ function FileExplorerInner(props) {
1830
1875
  [onActivate, onDownload, onOpenFolder, onPreview]
1831
1876
  );
1832
1877
  const handleContextMenu = useCallback((file, event) => {
1878
+ if (file.id === DRAFT_FOLDER_ID) return;
1833
1879
  setMenu({ kind: "item", file, x: event.clientX, y: event.clientY });
1834
1880
  }, []);
1835
1881
  const clearSelection = useCallback(() => {
@@ -1841,6 +1887,7 @@ function FileExplorerInner(props) {
1841
1887
  }, [clearSelection, parentId, trashOpen]);
1842
1888
  const handleSelect = useCallback(
1843
1889
  (file, event) => {
1890
+ if (file.id === DRAFT_FOLDER_ID) return;
1844
1891
  const id = file.id;
1845
1892
  if (event.shiftKey && anchorId.current) {
1846
1893
  const from = sortedFiles.findIndex((item) => item.id === anchorId.current);
@@ -1979,6 +2026,37 @@ function FileExplorerInner(props) {
1979
2026
  trashOpen
1980
2027
  ]
1981
2028
  );
2029
+ const commitRename = (name) => {
2030
+ if (draftFolderName != null) {
2031
+ setDraftFolderName(null);
2032
+ void actions.createFolder?.({ name, parentId }).then(() => reload());
2033
+ return;
2034
+ }
2035
+ if (!renameTarget) return;
2036
+ const target = renameTarget;
2037
+ setRenameTarget(null);
2038
+ if (name === target.name) return;
2039
+ void actions.renameFile(target.id, name).then(() => reload());
2040
+ };
2041
+ const cancelRename = () => {
2042
+ setDraftFolderName(null);
2043
+ setRenameTarget(null);
2044
+ };
2045
+ const startCreateFolder = () => {
2046
+ if (!actions.createFolder || trashOpen) return;
2047
+ setMenu(null);
2048
+ setRenameTarget(null);
2049
+ setQuery("");
2050
+ setDraftFolderName(unusedFolderName(pageItems, labels.newFolder));
2051
+ };
2052
+ const displayFiles = useMemo(() => {
2053
+ if (draftFolderName == null) return sortedFiles;
2054
+ return [
2055
+ draftFolder(draftFolderName, tenantId, parentId),
2056
+ ...sortedFiles
2057
+ ];
2058
+ }, [draftFolderName, parentId, sortedFiles, tenantId]);
2059
+ const renamingId = draftFolderName != null ? DRAFT_FOLDER_ID : renameTarget?.id ?? null;
1982
2060
  const openUpload = () => uploadInputRef.current?.click();
1983
2061
  return /* @__PURE__ */ jsxs(
1984
2062
  "div",
@@ -2027,7 +2105,7 @@ function FileExplorerInner(props) {
2027
2105
  view: explorer.view,
2028
2106
  onViewChange: explorer.setView,
2029
2107
  onUpload: requestUpload && !trashOpen ? openUpload : void 0,
2030
- onNewFolder: actions.createFolder && !trashOpen ? () => setFolderOpen(true) : void 0,
2108
+ onNewFolder: actions.createFolder && !trashOpen ? startCreateFolder : void 0,
2031
2109
  query,
2032
2110
  onQueryChange: searchFiles ? setQuery : void 0,
2033
2111
  trashOpen,
@@ -2042,7 +2120,7 @@ function FileExplorerInner(props) {
2042
2120
  }
2043
2121
  )
2044
2122
  ] }),
2045
- /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: (overlayLoading || pageStatus === "loading") && sortedFiles.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-12 text-center font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: labels.loading }) : overlayError || pageStatus === "error" && pageError ? /* @__PURE__ */ jsx("div", { className: "border-b border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive", children: (overlayError ?? pageError)?.message ?? labels.folderCreateFailed }) : sortedFiles.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col justify-center px-4 py-16 text-center", children: [
2123
+ /* @__PURE__ */ jsx("div", { className: "min-h-0 flex-1 overflow-y-auto", children: (overlayLoading || pageStatus === "loading") && displayFiles.length === 0 ? /* @__PURE__ */ jsx("p", { className: "px-4 py-12 text-center font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: labels.loading }) : overlayError || pageStatus === "error" && pageError ? /* @__PURE__ */ jsx("div", { className: "border-b border-destructive/30 bg-destructive/5 px-4 py-3 text-sm text-destructive", children: (overlayError ?? pageError)?.message ?? labels.folderCreateFailed }) : displayFiles.length === 0 ? /* @__PURE__ */ jsxs("div", { className: "flex min-h-full flex-col justify-center px-4 py-16 text-center", children: [
2046
2124
  /* @__PURE__ */ jsx("p", { className: "font-mono text-[10px] uppercase tracking-[0.12em] text-muted-foreground", children: trashOpen ? labels.emptyTrash : query.trim() ? labels.noMatches : labels.emptyFolder }),
2047
2125
  trashOpen ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.trashRetention }) : !query.trim() ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-muted-foreground", children: labels.dropHint }) : null
2048
2126
  ] }) : explorer.view === "list" ? /* @__PURE__ */ jsx(
@@ -2055,7 +2133,7 @@ function FileExplorerInner(props) {
2055
2133
  children: /* @__PURE__ */ jsx(
2056
2134
  ExplorerListView,
2057
2135
  {
2058
- files: sortedFiles,
2136
+ files: displayFiles,
2059
2137
  protectedIds: protectedSet,
2060
2138
  selectedIds,
2061
2139
  onSelect: handleSelect,
@@ -2070,7 +2148,10 @@ function FileExplorerInner(props) {
2070
2148
  onDragOverRow: explorer.setDropTarget,
2071
2149
  sortKey,
2072
2150
  sortDirection: sortDir,
2073
- onSortChange: handleSortChange
2151
+ onSortChange: handleSortChange,
2152
+ renamingId,
2153
+ onRenameCommit: commitRename,
2154
+ onRenameCancel: cancelRename
2074
2155
  }
2075
2156
  )
2076
2157
  }
@@ -2084,7 +2165,7 @@ function FileExplorerInner(props) {
2084
2165
  children: /* @__PURE__ */ jsx(
2085
2166
  ExplorerGridView,
2086
2167
  {
2087
- files: sortedFiles,
2168
+ files: displayFiles,
2088
2169
  protectedIds: protectedSet,
2089
2170
  selectedIds,
2090
2171
  onSelect: handleSelect,
@@ -2096,7 +2177,10 @@ function FileExplorerInner(props) {
2096
2177
  draggingId: explorer.draggingId,
2097
2178
  draggingIds,
2098
2179
  dropTargetId: explorer.dropTargetId,
2099
- onDragOverRow: explorer.setDropTarget
2180
+ onDragOverRow: explorer.setDropTarget,
2181
+ renamingId,
2182
+ onRenameCommit: commitRename,
2183
+ onRenameCancel: cancelRename
2100
2184
  }
2101
2185
  )
2102
2186
  }
@@ -2160,7 +2244,7 @@ function FileExplorerInner(props) {
2160
2244
  onRequestPurge: actions.purgeNode ? (file) => setPurgeTargets([file]) : void 0,
2161
2245
  onCopy: (file) => copyToClipboard([file]),
2162
2246
  onPaste: clipboard.length > 0 && !trashOpen ? (folder) => void pasteClipboard(folder ? folder.id : parentId) : void 0,
2163
- onNewFolder: actions.createFolder ? () => setFolderOpen(true) : void 0,
2247
+ onNewFolder: actions.createFolder ? startCreateFolder : void 0,
2164
2248
  onUpload: requestUpload ? openUpload : void 0
2165
2249
  }
2166
2250
  ),
@@ -2178,7 +2262,11 @@ function FileExplorerInner(props) {
2178
2262
  if (file.kind !== "file") continue;
2179
2263
  tokens.push(await actions.createShare({ id: file.id }));
2180
2264
  }
2181
- if (tokens.length > 0) await navigator.clipboard.writeText(tokens.join("\n"));
2265
+ if (tokens.length > 0) {
2266
+ await navigator.clipboard.writeText(
2267
+ tokens.map(absoluteShareUrl).join("\n")
2268
+ );
2269
+ }
2182
2270
  } : void 0,
2183
2271
  onDelete: () => setDeleteTargets(selectedFiles),
2184
2272
  onRestore: actions.restoreNode ? async () => {
@@ -2198,36 +2286,6 @@ function FileExplorerInner(props) {
2198
2286
  onClear: () => setClipboard([])
2199
2287
  }
2200
2288
  ) }),
2201
- /* @__PURE__ */ jsx(
2202
- CreateFolderDialog,
2203
- {
2204
- open: folderOpen,
2205
- onOpenChange: setFolderOpen,
2206
- onCreate: async (name) => {
2207
- await actions.createFolder?.({ name, parentId });
2208
- await reload();
2209
- }
2210
- }
2211
- ),
2212
- /* @__PURE__ */ jsx(
2213
- CreateFolderDialog,
2214
- {
2215
- open: Boolean(renameTarget),
2216
- onOpenChange: (open) => {
2217
- if (!open) setRenameTarget(null);
2218
- },
2219
- title: labels.rename,
2220
- description: labels.renameHint,
2221
- confirmLabel: labels.rename,
2222
- initialName: renameTarget?.name ?? "",
2223
- onCreate: async (name) => {
2224
- if (!renameTarget) return;
2225
- await actions.renameFile(renameTarget.id, name);
2226
- setRenameTarget(null);
2227
- await reload();
2228
- }
2229
- }
2230
- ),
2231
2289
  /* @__PURE__ */ jsx(
2232
2290
  ConfirmDeleteDialog,
2233
2291
  {
@@ -2382,6 +2440,92 @@ function FilePreviewDialog({
2382
2440
  ) })
2383
2441
  ] });
2384
2442
  }
2443
+ function CreateFolderDialog({
2444
+ open,
2445
+ onOpenChange,
2446
+ onCreate,
2447
+ title,
2448
+ description,
2449
+ confirmLabel,
2450
+ initialName = ""
2451
+ }) {
2452
+ const labels = useExplorerLabels();
2453
+ const [name, setName] = useState(initialName);
2454
+ const [saving, setSaving] = useState(false);
2455
+ const [error, setError] = useState(null);
2456
+ const inputRef = useRef(null);
2457
+ useEffect(() => {
2458
+ if (!open) {
2459
+ setSaving(false);
2460
+ setError(null);
2461
+ return;
2462
+ }
2463
+ setName(initialName);
2464
+ const id = window.setTimeout(() => {
2465
+ inputRef.current?.focus();
2466
+ inputRef.current?.select();
2467
+ }, 0);
2468
+ return () => window.clearTimeout(id);
2469
+ }, [initialName, open]);
2470
+ async function submit() {
2471
+ const trimmed = name.trim();
2472
+ if (!trimmed || saving) return;
2473
+ setSaving(true);
2474
+ setError(null);
2475
+ try {
2476
+ await onCreate(trimmed);
2477
+ onOpenChange(false);
2478
+ } catch (cause) {
2479
+ const code = cause && typeof cause === "object" && "code" in cause ? String(cause.code) : "";
2480
+ setError(code === "Conflict" ? labels.folderExists : labels.folderCreateFailed);
2481
+ } finally {
2482
+ setSaving(false);
2483
+ }
2484
+ }
2485
+ return /* @__PURE__ */ jsxs(Overlay, { open, onClose: () => onOpenChange(false), children: [
2486
+ /* @__PURE__ */ jsx("h2", { className: "text-base font-semibold", children: title ?? labels.newFolder }),
2487
+ /* @__PURE__ */ jsx("p", { className: "mt-1 text-sm text-muted-foreground", children: description ?? labels.folderNameHint }),
2488
+ /* @__PURE__ */ jsx(
2489
+ "input",
2490
+ {
2491
+ ref: inputRef,
2492
+ value: name,
2493
+ onChange: (event) => setName(event.target.value),
2494
+ onKeyDown: (event) => {
2495
+ if (event.key === "Enter") {
2496
+ event.preventDefault();
2497
+ void submit();
2498
+ }
2499
+ },
2500
+ placeholder: labels.folderName,
2501
+ "aria-invalid": Boolean(error),
2502
+ className: "mt-3 h-9 w-full rounded-[10px] border border-border bg-background px-3 text-sm outline-none focus-visible:ring-2 focus-visible:ring-ring"
2503
+ }
2504
+ ),
2505
+ error ? /* @__PURE__ */ jsx("p", { className: "mt-2 text-sm text-destructive", children: error }) : null,
2506
+ /* @__PURE__ */ jsxs("div", { className: "mt-4 flex justify-end gap-2", children: [
2507
+ /* @__PURE__ */ jsx(
2508
+ "button",
2509
+ {
2510
+ type: "button",
2511
+ className: "inline-flex h-8 items-center rounded-[10px] border border-border px-3 text-sm",
2512
+ onClick: () => onOpenChange(false),
2513
+ children: labels.cancel
2514
+ }
2515
+ ),
2516
+ /* @__PURE__ */ jsx(
2517
+ "button",
2518
+ {
2519
+ type: "button",
2520
+ disabled: !name.trim() || saving,
2521
+ className: "inline-flex h-8 items-center rounded-[10px] bg-primary px-3 text-sm text-primary-foreground disabled:opacity-50",
2522
+ onClick: () => void submit(),
2523
+ children: confirmLabel ?? labels.createFolder
2524
+ }
2525
+ )
2526
+ ] })
2527
+ ] });
2528
+ }
2385
2529
 
2386
2530
  export { Breadcrumbs, ConfirmDeleteDialog, CreateFolderDialog, EmptyState, ErrorState, ExplorerClipboardToast, ExplorerContextMenu, ExplorerGridView, ExplorerLabelsProvider, ExplorerListView, ExplorerSelectionToast, ExplorerToolbar, ExplorerUploadStatus, FILE_ICON_MAP, FileExplorer, FileIcon, FilePreviewDialog, UploadQueueProvider, canPreviewFile, cn, defaultLabels, resolveFileIcon, useExplorerLabels, useUploadEnqueue, useUploadQueue };
2387
2531
  //# sourceMappingURL=index.js.map