@noya-app/noya-file-explorer 0.0.15 → 0.0.17

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.mjs CHANGED
@@ -38,10 +38,10 @@ var require_access = __commonJS({
38
38
  const accessed = _getPath(node, indexPath, options);
39
39
  return accessed[accessed.length - 1];
40
40
  }
41
- let path4 = indexPath.slice();
42
- while (path4.length > 0) {
43
- let index = path4.shift();
44
- const children = options.getChildren(node, path4);
41
+ let path7 = indexPath.slice();
42
+ while (path7.length > 0) {
43
+ let index = path7.shift();
44
+ const children = options.getChildren(node, path7);
45
45
  const child = children[index];
46
46
  if (!child) {
47
47
  return void 0;
@@ -54,12 +54,12 @@ var require_access = __commonJS({
54
54
  return _getPath(node, indexPath, options).slice(0, -1);
55
55
  }
56
56
  function _getPath(node, indexPath, options) {
57
- let path4 = indexPath.slice();
57
+ let path7 = indexPath.slice();
58
58
  let result = [node];
59
- while (path4.length > 0) {
60
- let index = path4.shift();
59
+ while (path7.length > 0) {
60
+ let index = path7.shift();
61
61
  const context = options.includeTraversalContext ? makeTraversalContext(result) : void 0;
62
- const children = options.getChildren(node, path4, context);
62
+ const children = options.getChildren(node, path7, context);
63
63
  const child = children[index];
64
64
  if (!child) {
65
65
  return result;
@@ -74,20 +74,20 @@ var require_access = __commonJS({
74
74
  const accessed = accessPath(node, indexPath, options);
75
75
  return accessed[accessed.length - 1];
76
76
  }
77
- let path4 = indexPath.slice();
78
- while (path4.length > 0) {
79
- let index = path4.shift();
80
- node = options.getChildren(node, path4)[index];
77
+ let path7 = indexPath.slice();
78
+ while (path7.length > 0) {
79
+ let index = path7.shift();
80
+ node = options.getChildren(node, path7)[index];
81
81
  }
82
82
  return node;
83
83
  }
84
84
  function accessPath(node, indexPath, options) {
85
- let path4 = indexPath.slice();
85
+ let path7 = indexPath.slice();
86
86
  let result = [node];
87
- while (path4.length > 0) {
88
- let index = path4.shift();
87
+ while (path7.length > 0) {
88
+ let index = path7.shift();
89
89
  const context = options.includeTraversalContext ? makeTraversalContext(result) : void 0;
90
- node = options.getChildren(node, path4, context)[index];
90
+ node = options.getChildren(node, path7, context)[index];
91
91
  result.push(node);
92
92
  }
93
93
  return result;
@@ -129,9 +129,9 @@ var require_ancestors = __commonJS({
129
129
  "../../node_modules/tree-visit/lib/ancestors.js"(exports) {
130
130
  "use strict";
131
131
  Object.defineProperty(exports, "__esModule", { value: true });
132
- exports.ancestorPaths = ancestorPaths2;
132
+ exports.ancestorPaths = ancestorPaths3;
133
133
  var sort_1 = require_sort();
134
- function ancestorPaths2(paths, options) {
134
+ function ancestorPaths3(paths, options) {
135
135
  var _a;
136
136
  const result = /* @__PURE__ */ new Map();
137
137
  const compare = (_a = options === null || options === void 0 ? void 0 : options.compare) !== null && _a !== void 0 ? _a : sort_1.comparePathsByComponent;
@@ -601,40 +601,40 @@ var require_transformPath = __commonJS({
601
601
  Object.defineProperty(exports, "__esModule", { value: true });
602
602
  exports.transformPath = transformPath;
603
603
  var sort_1 = require_sort();
604
- function commonAncestor(path4, otherPath) {
605
- const length = Math.min(path4.length, otherPath.length);
604
+ function commonAncestor(path7, otherPath) {
605
+ const length = Math.min(path7.length, otherPath.length);
606
606
  for (let i = 0; i < length; i++) {
607
- if (path4[i] !== otherPath[i]) {
608
- return path4.slice(0, i);
607
+ if (path7[i] !== otherPath[i]) {
608
+ return path7.slice(0, i);
609
609
  }
610
610
  }
611
- return path4.slice(0, length);
611
+ return path7.slice(0, length);
612
612
  }
613
- function transformPath(path4, operation, otherPath, count = 1) {
614
- if (otherPath.length > path4.length || (0, sort_1.comparePathsByComponent)(otherPath, path4) > 0) {
615
- return path4;
613
+ function transformPath(path7, operation, otherPath, count = 1) {
614
+ if (otherPath.length > path7.length || (0, sort_1.comparePathsByComponent)(otherPath, path7) > 0) {
615
+ return path7;
616
616
  }
617
617
  if (otherPath.length === 0 && operation === "remove") {
618
618
  return void 0;
619
619
  }
620
- const common = commonAncestor(path4, otherPath);
621
- const adjustmentIndex = common.length === path4.length || common.length === otherPath.length ? common.length - 1 : common.length;
622
- const pathValue = path4[adjustmentIndex];
620
+ const common = commonAncestor(path7, otherPath);
621
+ const adjustmentIndex = common.length === path7.length || common.length === otherPath.length ? common.length - 1 : common.length;
622
+ const pathValue = path7[adjustmentIndex];
623
623
  const otherPathValue = otherPath[adjustmentIndex];
624
624
  if (operation === "insert" && otherPathValue <= pathValue) {
625
- const newPath = [...path4];
625
+ const newPath = [...path7];
626
626
  newPath[adjustmentIndex] += count;
627
627
  return newPath;
628
628
  } else if (operation === "remove") {
629
629
  if (otherPathValue === pathValue) {
630
630
  return void 0;
631
631
  } else if (otherPathValue < pathValue) {
632
- const newPath = [...path4];
632
+ const newPath = [...path7];
633
633
  newPath[adjustmentIndex] -= count;
634
634
  return newPath;
635
635
  }
636
636
  }
637
- return path4;
637
+ return path7;
638
638
  }
639
639
  }
640
640
  });
@@ -800,15 +800,15 @@ var require_operation = __commonJS({
800
800
  switch (operation.type) {
801
801
  case "insert": {
802
802
  const otherPath = parentPath.concat(operation.index);
803
- return transformedPaths.map((path4) => path4 ? (0, transformPath_1.transformPath)(path4, "insert", otherPath, operation.nodes.length) : void 0);
803
+ return transformedPaths.map((path7) => path7 ? (0, transformPath_1.transformPath)(path7, "insert", otherPath, operation.nodes.length) : void 0);
804
804
  }
805
805
  case "remove": {
806
806
  const otherPaths = [...operation.indexes].reverse().map((index) => parentPath.concat(index));
807
- return transformedPaths.map((path4) => {
807
+ return transformedPaths.map((path7) => {
808
808
  for (const otherPath of otherPaths) {
809
- path4 = path4 ? (0, transformPath_1.transformPath)(path4, "remove", otherPath) : void 0;
809
+ path7 = path7 ? (0, transformPath_1.transformPath)(path7, "remove", otherPath) : void 0;
810
810
  }
811
- return path4;
811
+ return path7;
812
812
  });
813
813
  }
814
814
  case "removeThenInsert": {
@@ -945,22 +945,22 @@ var require_splice = __commonJS({
945
945
  return _spliceWithPathTracking(node, options);
946
946
  }
947
947
  function _spliceWithPathTracking(node, options) {
948
- const { path: path4, deleteCount = 0, nodes, track } = options;
949
- if (path4.length === 0) {
948
+ const { path: path7, deleteCount = 0, nodes, track } = options;
949
+ if (path7.length === 0) {
950
950
  throw new Error(`Can't splice at the root`);
951
951
  }
952
- const pathsToRemove = getPathsToRemove(path4, deleteCount);
953
- const operations = (0, operation_1.getInsertionOperations)(path4, nodes, (0, operation_1.getRemovalOperations)(pathsToRemove));
952
+ const pathsToRemove = getPathsToRemove(path7, deleteCount);
953
+ const operations = (0, operation_1.getInsertionOperations)(path7, nodes, (0, operation_1.getRemovalOperations)(pathsToRemove));
954
954
  const transformedPaths = track ? (0, operation_1.transformPathsByOperations)(track, operations) : [];
955
955
  return {
956
956
  node: (0, operation_1.applyOperations)(node, operations, options),
957
957
  paths: transformedPaths
958
958
  };
959
959
  }
960
- function getPathsToRemove(path4, deleteCount) {
960
+ function getPathsToRemove(path7, deleteCount) {
961
961
  let pathsToRemove = [];
962
- let parentPath = path4.slice(0, -1);
963
- let index = path4[path4.length - 1];
962
+ let parentPath = path7.slice(0, -1);
963
+ let index = path7[path7.length - 1];
964
964
  for (let i = 0; i < deleteCount; i++) {
965
965
  pathsToRemove.push(parentPath.concat(index + i));
966
966
  }
@@ -1100,15 +1100,6 @@ var require_lib = __commonJS({
1100
1100
  }
1101
1101
  });
1102
1102
 
1103
- // src/formatByteSize.ts
1104
- var byteSizeUnits = ["B", "KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB"];
1105
- function formatByteSize(size) {
1106
- const unitIndex = Math.floor(Math.log(size) / Math.log(1024));
1107
- const unit = byteSizeUnits[unitIndex];
1108
- const value = size / Math.pow(1024, unitIndex);
1109
- return `${value.toFixed(1)} ${unit}`;
1110
- }
1111
-
1112
1103
  // src/MediaCollection.tsx
1113
1104
  import {
1114
1105
  ActionMenu,
@@ -1119,7 +1110,9 @@ import {
1119
1110
  FileExplorerEmptyState,
1120
1111
  FileExplorerLayout,
1121
1112
  FileExplorerUploadButton,
1122
- MediaThumbnail
1113
+ formatByteSize,
1114
+ MediaThumbnail,
1115
+ useOpenConfirmationDialog
1123
1116
  } from "@noya-app/noya-designsystem";
1124
1117
  import {
1125
1118
  DownloadIcon,
@@ -1236,11 +1229,19 @@ var createMediaItemTree = (mediaMap) => {
1236
1229
  };
1237
1230
 
1238
1231
  // src/MediaCollection.tsx
1239
- import { path as path3 } from "imfs";
1232
+ import { path as path4 } from "imfs";
1240
1233
  import React from "react";
1241
1234
 
1242
1235
  // src/utils/files.ts
1236
+ var import_tree_visit3 = __toESM(require_lib());
1237
+ import { path as path3 } from "imfs";
1238
+
1239
+ // src/utils/resourceUtils.ts
1243
1240
  var import_tree_visit2 = __toESM(require_lib());
1241
+ import {
1242
+ createResourceTree,
1243
+ rootResource
1244
+ } from "@noya-app/noya-schemas";
1244
1245
  import { path as path2 } from "imfs";
1245
1246
  var getVisibleItems = ({
1246
1247
  expandedMap,
@@ -1251,7 +1252,7 @@ var getVisibleItems = ({
1251
1252
  showRootItem
1252
1253
  }) => {
1253
1254
  const filteredItems = [];
1254
- const relativeRootItem = tree.find(rootMediaItem, (item) => item.id === rootItemId) ?? rootMediaItem;
1255
+ const relativeRootItem = tree.find(rootResource, (item) => item.id === rootItemId) ?? rootResource;
1255
1256
  tree.visit(relativeRootItem, (item) => {
1256
1257
  if (relativeRootItem.id === item.id) {
1257
1258
  if (showRootItem) {
@@ -1259,13 +1260,16 @@ var getVisibleItems = ({
1259
1260
  }
1260
1261
  return;
1261
1262
  }
1262
- if (item.kind === "file" && fileKindFilter === "all") {
1263
+ if (item.type === "file" && fileKindFilter === "all") {
1263
1264
  filteredItems.push(item);
1264
1265
  }
1265
- if (item.kind === "asset" && (fileKindFilter === "assets" || fileKindFilter === "all")) {
1266
+ if (item.type === "asset" && (fileKindFilter === "assets" || fileKindFilter === "all")) {
1266
1267
  filteredItems.push(item);
1267
1268
  }
1268
- if (item.kind === "folder" && (fileKindFilter === "directories" || fileKindFilter === "all")) {
1269
+ if (item.type === "directory" && (fileKindFilter === "directories" || fileKindFilter === "all")) {
1270
+ filteredItems.push(item);
1271
+ }
1272
+ if (item.type === "resource" && (fileKindFilter === "resources" || fileKindFilter === "all")) {
1269
1273
  filteredItems.push(item);
1270
1274
  }
1271
1275
  if (!expandedMap[item.id] || !showAllDescendants) return "skip";
@@ -1277,7 +1281,7 @@ var basenameValidator = (basename) => {
1277
1281
  const invalidCharsRegex = /[/\\<>:"|?*]/;
1278
1282
  return !invalidCharsRegex.test(basename);
1279
1283
  };
1280
- var validateMediaItemRename = ({
1284
+ var validateResourceRename = ({
1281
1285
  basename,
1282
1286
  selectedItemPath,
1283
1287
  media
@@ -1295,7 +1299,7 @@ var movePathsIntoTarget = ({
1295
1299
  tree
1296
1300
  }) => {
1297
1301
  const ancestors = (0, import_tree_visit2.ancestorPaths)(
1298
- sourceItemPaths.map((path4) => path4.split("/"))
1302
+ sourceItemPaths.map((path7) => path7.split("/"))
1299
1303
  );
1300
1304
  const mediaClone = { ...media };
1301
1305
  for (const ancestor of ancestors) {
@@ -1313,7 +1317,7 @@ var movePathsIntoTarget = ({
1313
1317
  ancestorPath,
1314
1318
  newAncestorPath
1315
1319
  );
1316
- const newPathIsValid = validateMediaItemRename({
1320
+ const newPathIsValid = validateResourceRename({
1317
1321
  basename: path2.basename(descendantPath),
1318
1322
  selectedItemPath: newDescendantPath,
1319
1323
  media
@@ -1332,17 +1336,17 @@ var moveUpAFolder = ({
1332
1336
  selectedIds
1333
1337
  }) => {
1334
1338
  const indexPath = tree.findPath(
1335
- rootMediaItem,
1339
+ rootResource,
1336
1340
  (item) => item.id === selectedIds[0]
1337
1341
  );
1338
1342
  if (!indexPath) return;
1339
1343
  const grandparentFolder = tree.access(
1340
- rootMediaItem,
1344
+ rootResource,
1341
1345
  indexPath.slice(0, indexPath.length - 2)
1342
1346
  );
1343
1347
  const grandparentFolderPath = tree.idToPathMap.get(grandparentFolder.id);
1344
1348
  if (!grandparentFolderPath) return;
1345
- const sourceItemPaths = selectedIds.map((id) => tree.idToPathMap.get(id)).filter((path4) => Boolean(path4));
1349
+ const sourceItemPaths = selectedIds.map((id) => tree.idToPathMap.get(id)).filter((path7) => Boolean(path7));
1346
1350
  return movePathsIntoTarget({
1347
1351
  media,
1348
1352
  targetItemPath: grandparentFolderPath,
@@ -1371,7 +1375,7 @@ var updateExpandedMap = ({
1371
1375
  const newExpandedMap = { ...expandedMap };
1372
1376
  const inner = (item2, expanded2) => {
1373
1377
  if (!expandable) return {};
1374
- if (item2.id === rootMediaItem.id) return {};
1378
+ if (item2.id === rootResource.id) return {};
1375
1379
  if (!expanded2) {
1376
1380
  const children = tree.getChildren(item2, []);
1377
1381
  children.forEach((child) => inner(child, false));
@@ -1381,13 +1385,13 @@ var updateExpandedMap = ({
1381
1385
  inner(item, expanded);
1382
1386
  return newExpandedMap;
1383
1387
  };
1384
- var deleteMediaItems = ({
1388
+ var deleteResources = ({
1385
1389
  selectedIds,
1386
1390
  media,
1387
1391
  tree
1388
1392
  }) => {
1389
1393
  const itemsToDelete = selectedIds.flatMap((mediaItemId) => {
1390
- const mediaItem = tree.mediaItemsWithRoot.find(
1394
+ const mediaItem = tree.resourcesWithRoot.find(
1391
1395
  (item) => item.id === mediaItemId
1392
1396
  );
1393
1397
  if (!mediaItem) return [];
@@ -1408,7 +1412,7 @@ var moveMediaInsideFolder = ({
1408
1412
  }) => {
1409
1413
  const targetItemPath = tree.idToPathMap.get(targetItemId);
1410
1414
  if (!targetItemPath) return media;
1411
- const sourceItemPaths = sourceItemIds.map((id) => tree.idToPathMap.get(id)).filter((path4) => Boolean(path4));
1415
+ const sourceItemPaths = sourceItemIds.map((id) => tree.idToPathMap.get(id)).filter((path7) => Boolean(path7));
1412
1416
  return movePathsIntoTarget({
1413
1417
  media,
1414
1418
  sourceItemPaths,
@@ -1416,7 +1420,213 @@ var moveMediaInsideFolder = ({
1416
1420
  tree
1417
1421
  });
1418
1422
  };
1419
- var getParentDirectories = (mediaMap, folderId) => {
1423
+ var getParentDirectories = (resourceMap, folderId) => {
1424
+ const tree = createResourceTree(resourceMap);
1425
+ const indexPath = tree.findPath(rootResource, (item) => item.id === folderId);
1426
+ if (!indexPath) return [rootResource];
1427
+ return tree.accessPath(rootResource, indexPath);
1428
+ };
1429
+ var renameResourceAndDescendantPaths = ({
1430
+ newName,
1431
+ selectedItemPath,
1432
+ media,
1433
+ tree
1434
+ }) => {
1435
+ const mediaClone = { ...media };
1436
+ const selectedItem = mediaClone[selectedItemPath];
1437
+ if (!selectedItem) return mediaClone;
1438
+ const parentPath = path2.dirname(selectedItemPath);
1439
+ const newItemPath = path2.join(parentPath, newName);
1440
+ const descendants = tree.flat(selectedItem).map((item) => tree.idToPathMap.get(item.id));
1441
+ for (const descendantPath of descendants) {
1442
+ if (!descendantPath) continue;
1443
+ const newDescendantPath = descendantPath.replace(
1444
+ selectedItemPath,
1445
+ newItemPath
1446
+ );
1447
+ mediaClone[newDescendantPath] = {
1448
+ ...mediaClone[descendantPath],
1449
+ path: newDescendantPath
1450
+ };
1451
+ delete mediaClone[descendantPath];
1452
+ }
1453
+ return mediaClone;
1454
+ };
1455
+
1456
+ // src/utils/files.ts
1457
+ var mediaGetVisibleItems = ({
1458
+ expandedMap,
1459
+ fileKindFilter,
1460
+ rootItemId,
1461
+ tree,
1462
+ showAllDescendants,
1463
+ showRootItem
1464
+ }) => {
1465
+ const filteredItems = [];
1466
+ const relativeRootItem = tree.find(rootMediaItem, (item) => item.id === rootItemId) ?? rootMediaItem;
1467
+ tree.visit(relativeRootItem, (item) => {
1468
+ if (relativeRootItem.id === item.id) {
1469
+ if (showRootItem) {
1470
+ filteredItems.push(item);
1471
+ }
1472
+ return;
1473
+ }
1474
+ if (item.kind === "noyaFile" && fileKindFilter === "all") {
1475
+ filteredItems.push(item);
1476
+ }
1477
+ if (item.kind === "file" && fileKindFilter === "all") {
1478
+ filteredItems.push(item);
1479
+ }
1480
+ if (item.kind === "asset" && (fileKindFilter === "assets" || fileKindFilter === "all")) {
1481
+ filteredItems.push(item);
1482
+ }
1483
+ if (item.kind === "folder" && (fileKindFilter === "directories" || fileKindFilter === "all")) {
1484
+ filteredItems.push(item);
1485
+ }
1486
+ if (!expandedMap[item.id] || !showAllDescendants) return "skip";
1487
+ });
1488
+ return filteredItems;
1489
+ };
1490
+ var mediaValidateMediaItemRename = ({
1491
+ basename,
1492
+ selectedItemPath,
1493
+ media
1494
+ }) => {
1495
+ if (!basenameValidator(basename)) return false;
1496
+ const newItemPath = path3.join(path3.dirname(selectedItemPath), basename);
1497
+ const newPathExists = media[newItemPath];
1498
+ if (newPathExists) return false;
1499
+ return true;
1500
+ };
1501
+ var mediaMovePathsIntoTarget = ({
1502
+ media,
1503
+ sourceItemPaths,
1504
+ targetItemPath,
1505
+ tree
1506
+ }) => {
1507
+ const ancestors = (0, import_tree_visit3.ancestorPaths)(
1508
+ sourceItemPaths.map((path7) => path7.split("/"))
1509
+ );
1510
+ const mediaClone = { ...media };
1511
+ for (const ancestor of ancestors) {
1512
+ const ancestorPath = ancestor.join("/");
1513
+ const ancestorItem = mediaClone[ancestorPath];
1514
+ const newAncestorPath = path3.join(
1515
+ targetItemPath,
1516
+ path3.basename(ancestorPath)
1517
+ );
1518
+ if (!ancestorItem) continue;
1519
+ const descendantPaths = tree.flat(ancestorItem).map((item) => tree.idToPathMap.get(item.id));
1520
+ for (const descendantPath of descendantPaths) {
1521
+ if (!descendantPath) continue;
1522
+ const newDescendantPath = descendantPath.replace(
1523
+ ancestorPath,
1524
+ newAncestorPath
1525
+ );
1526
+ const newPathIsValid = mediaValidateMediaItemRename({
1527
+ basename: path3.basename(descendantPath),
1528
+ selectedItemPath: newDescendantPath,
1529
+ media
1530
+ });
1531
+ if (newPathIsValid) {
1532
+ mediaClone[newDescendantPath] = mediaClone[descendantPath];
1533
+ delete mediaClone[descendantPath];
1534
+ }
1535
+ }
1536
+ }
1537
+ return mediaClone;
1538
+ };
1539
+ var mediaMoveUpAFolder = ({
1540
+ tree,
1541
+ media,
1542
+ selectedIds
1543
+ }) => {
1544
+ const indexPath = tree.findPath(
1545
+ rootMediaItem,
1546
+ (item) => item.id === selectedIds[0]
1547
+ );
1548
+ if (!indexPath) return;
1549
+ const grandparentFolder = tree.access(
1550
+ rootMediaItem,
1551
+ indexPath.slice(0, indexPath.length - 2)
1552
+ );
1553
+ const grandparentFolderPath = tree.idToPathMap.get(grandparentFolder.id);
1554
+ if (!grandparentFolderPath) return;
1555
+ const sourceItemPaths = selectedIds.map((id) => tree.idToPathMap.get(id)).filter((path7) => Boolean(path7));
1556
+ return mediaMovePathsIntoTarget({
1557
+ media,
1558
+ targetItemPath: grandparentFolderPath,
1559
+ sourceItemPaths,
1560
+ tree
1561
+ });
1562
+ };
1563
+ var mediaGetDepthMap = (item, tree, showAllDescendants) => {
1564
+ const depthMap = {};
1565
+ tree.visit(item, (item2, indexPath) => {
1566
+ if (showAllDescendants) {
1567
+ depthMap[item2.id] = Math.max(indexPath.length - 1, 0);
1568
+ } else {
1569
+ depthMap[item2.id] = 0;
1570
+ }
1571
+ });
1572
+ return depthMap;
1573
+ };
1574
+ var mediaUpdateExpandedMap = ({
1575
+ item,
1576
+ expanded,
1577
+ expandable,
1578
+ expandedMap,
1579
+ tree
1580
+ }) => {
1581
+ const newExpandedMap = { ...expandedMap };
1582
+ const inner = (item2, expanded2) => {
1583
+ if (!expandable) return {};
1584
+ if (item2.id === rootMediaItem.id) return {};
1585
+ if (!expanded2) {
1586
+ const children = tree.getChildren(item2, []);
1587
+ children.forEach((child) => inner(child, false));
1588
+ }
1589
+ newExpandedMap[item2.id] = expanded2;
1590
+ };
1591
+ inner(item, expanded);
1592
+ return newExpandedMap;
1593
+ };
1594
+ var mediaDeleteMediaItems = ({
1595
+ selectedIds,
1596
+ media,
1597
+ tree
1598
+ }) => {
1599
+ const itemsToDelete = selectedIds.flatMap((mediaItemId) => {
1600
+ const mediaItem = tree.mediaItemsWithRoot.find(
1601
+ (item) => item.id === mediaItemId
1602
+ );
1603
+ if (!mediaItem) return [];
1604
+ return tree.flat(mediaItem);
1605
+ });
1606
+ const itemKeysToDelete = new Set(
1607
+ itemsToDelete.map((item) => tree.idToPathMap.get(item.id))
1608
+ );
1609
+ return Object.fromEntries(
1610
+ Object.entries(media).filter(([key]) => !itemKeysToDelete.has(key))
1611
+ );
1612
+ };
1613
+ var mediaMoveMediaInsideFolder = ({
1614
+ sourceItemIds,
1615
+ targetItemId,
1616
+ media,
1617
+ tree
1618
+ }) => {
1619
+ const targetItemPath = tree.idToPathMap.get(targetItemId);
1620
+ if (!targetItemPath) return media;
1621
+ const sourceItemPaths = sourceItemIds.map((id) => tree.idToPathMap.get(id)).filter((path7) => Boolean(path7));
1622
+ return mediaMovePathsIntoTarget({
1623
+ media,
1624
+ sourceItemPaths,
1625
+ targetItemPath,
1626
+ tree
1627
+ });
1628
+ };
1629
+ var mediaGetParentDirectories = (mediaMap, folderId) => {
1420
1630
  const tree = createMediaItemTree(mediaMap);
1421
1631
  const indexPath = tree.findPath(
1422
1632
  rootMediaItem,
@@ -1425,7 +1635,7 @@ var getParentDirectories = (mediaMap, folderId) => {
1425
1635
  if (!indexPath) return [rootMediaItem];
1426
1636
  return tree.accessPath(rootMediaItem, indexPath);
1427
1637
  };
1428
- var renameMediaItemAndDescendantPaths = ({
1638
+ var mediaRenameMediaItemAndDescendantPaths = ({
1429
1639
  newName,
1430
1640
  selectedItemPath,
1431
1641
  media,
@@ -1434,8 +1644,8 @@ var renameMediaItemAndDescendantPaths = ({
1434
1644
  const mediaClone = { ...media };
1435
1645
  const selectedItem = mediaClone[selectedItemPath];
1436
1646
  if (!selectedItem) return mediaClone;
1437
- const parentPath = path2.dirname(selectedItemPath);
1438
- const newItemPath = path2.join(parentPath, newName);
1647
+ const parentPath = path3.dirname(selectedItemPath);
1648
+ const newItemPath = path3.join(parentPath, newName);
1439
1649
  const descendants = tree.flat(selectedItem).map((item) => tree.idToPathMap.get(item.id));
1440
1650
  for (const descendantPath of descendants) {
1441
1651
  if (!descendantPath) continue;
@@ -1456,7 +1666,7 @@ var extensionToContentType = {
1456
1666
  jpeg: "image/jpeg"
1457
1667
  };
1458
1668
  function encodeFileContentForThumbnail(pathProp, item) {
1459
- const extension = path3.extname(pathProp).slice(1);
1669
+ const extension = path4.extname(pathProp).slice(1);
1460
1670
  const contentType = extensionToContentType[extension];
1461
1671
  if (contentType) {
1462
1672
  if (item.encoding === "base64") {
@@ -1483,7 +1693,8 @@ var MediaThumbnailInternal = memoGeneric(
1483
1693
  item,
1484
1694
  selected,
1485
1695
  size,
1486
- path: pathProp
1696
+ path: pathProp,
1697
+ renderThumbnailIcon
1487
1698
  }) => {
1488
1699
  const asset = useAsset(item.kind === "asset" ? item.assetId : void 0);
1489
1700
  const isRoot = item.id === rootMediaItem.id;
@@ -1491,9 +1702,13 @@ var MediaThumbnailInternal = memoGeneric(
1491
1702
  const isFile = item.kind === "file";
1492
1703
  let contentType;
1493
1704
  let url;
1705
+ let width;
1706
+ let height;
1494
1707
  if (asset) {
1495
1708
  contentType = asset.contentType;
1496
1709
  url = asset.url;
1710
+ width = asset.width ?? void 0;
1711
+ height = asset.height ?? void 0;
1497
1712
  } else if (isFile && pathProp) {
1498
1713
  const encoded = encodeFileContentForThumbnail(pathProp, item);
1499
1714
  if (encoded) {
@@ -1501,7 +1716,8 @@ var MediaThumbnailInternal = memoGeneric(
1501
1716
  url = encoded.url;
1502
1717
  }
1503
1718
  }
1504
- const fileName = pathProp ? path3.basename(pathProp) : void 0;
1719
+ const fileName = pathProp ? path4.basename(pathProp) : void 0;
1720
+ const dimensions = width && height ? { width, height } : void 0;
1505
1721
  return /* @__PURE__ */ React.createElement(
1506
1722
  MediaThumbnail,
1507
1723
  {
@@ -1510,7 +1726,9 @@ var MediaThumbnailInternal = memoGeneric(
1510
1726
  url,
1511
1727
  selected,
1512
1728
  size,
1513
- fileName
1729
+ fileName,
1730
+ renderThumbnailIcon,
1731
+ dimensions
1514
1732
  }
1515
1733
  );
1516
1734
  }
@@ -1542,7 +1760,10 @@ var MediaCollection = memo(
1542
1760
  sortable = false,
1543
1761
  renderEmptyState,
1544
1762
  sharedDragProps,
1545
- onClickItem
1763
+ onClickItem,
1764
+ renderThumbnailIcon,
1765
+ onDidDeleteItems,
1766
+ onAssetsUploaded
1546
1767
  }, ref) {
1547
1768
  const setMedia = useCallback(
1548
1769
  (...args) => {
@@ -1583,7 +1804,7 @@ var MediaCollection = memo(
1583
1804
  const assets = useAssets();
1584
1805
  const [expandedMap, setExpandedMap] = useState({});
1585
1806
  const visibleItems = useMemo(
1586
- () => getVisibleItems({
1807
+ () => mediaGetVisibleItems({
1587
1808
  expandedMap,
1588
1809
  fileKindFilter,
1589
1810
  rootItemId,
@@ -1601,7 +1822,7 @@ var MediaCollection = memo(
1601
1822
  ]
1602
1823
  );
1603
1824
  const depthMap = useMemo(
1604
- () => getDepthMap(rootMediaItem, treeWithTempItem, showAllDescendants),
1825
+ () => mediaGetDepthMap(rootMediaItem, treeWithTempItem, showAllDescendants),
1605
1826
  [treeWithTempItem, showAllDescendants]
1606
1827
  );
1607
1828
  const collectionRef = useRef(null);
@@ -1624,7 +1845,7 @@ var MediaCollection = memo(
1624
1845
  const itemPath = tree.idToPathMap.get(item.id);
1625
1846
  const firstSelectedPath = tree.idToPathMap.get(selectedIds[0]);
1626
1847
  if (!itemPath || !firstSelectedPath) return false;
1627
- return itemPath.startsWith(path3.dirname(firstSelectedPath));
1848
+ return itemPath.startsWith(path4.dirname(firstSelectedPath));
1628
1849
  });
1629
1850
  useEffect(() => {
1630
1851
  if (initialExpanded) {
@@ -1640,17 +1861,39 @@ var MediaCollection = memo(
1640
1861
  },
1641
1862
  [expandedMap, expandable]
1642
1863
  );
1864
+ const openConfirmationDialog = useOpenConfirmationDialog();
1643
1865
  const handleDelete = useCallback(
1644
- (selectedIds2) => {
1645
- const newMedia = deleteMediaItems({
1866
+ async (selectedIds2) => {
1867
+ const ok = await openConfirmationDialog({
1868
+ title: "Delete items",
1869
+ description: "Are you sure you want to delete these items? This action cannot be undone."
1870
+ });
1871
+ if (!ok) return;
1872
+ const deletedItems = Object.entries(media).flatMap(
1873
+ ([path7, item]) => {
1874
+ if (selectedIds2.includes(item.id)) {
1875
+ return [[path7, item]];
1876
+ }
1877
+ return [];
1878
+ }
1879
+ );
1880
+ const newMedia = mediaDeleteMediaItems({
1646
1881
  selectedIds: selectedIds2,
1647
1882
  media,
1648
1883
  tree
1649
1884
  });
1650
1885
  setSelectedIds([rootMediaItem.id]);
1651
1886
  setMedia({ name: "Delete items", timestamp: Date.now() }, newMedia);
1887
+ onDidDeleteItems?.(deletedItems);
1652
1888
  },
1653
- [media, setMedia, setSelectedIds, tree]
1889
+ [
1890
+ media,
1891
+ setMedia,
1892
+ setSelectedIds,
1893
+ tree,
1894
+ onDidDeleteItems,
1895
+ openConfirmationDialog
1896
+ ]
1654
1897
  );
1655
1898
  const onRename = useCallback(
1656
1899
  (selectedItem, newName) => {
@@ -1659,7 +1902,7 @@ var MediaCollection = memo(
1659
1902
  selectedItem.id
1660
1903
  );
1661
1904
  if (!selectedItemPath) return;
1662
- const renameIsValid = validateMediaItemRename({
1905
+ const renameIsValid = mediaValidateMediaItemRename({
1663
1906
  basename: newName,
1664
1907
  selectedItemPath,
1665
1908
  media: temp.media
@@ -1668,7 +1911,7 @@ var MediaCollection = memo(
1668
1911
  setTempItem(void 0);
1669
1912
  return;
1670
1913
  }
1671
- const mediaWithRenamedDescendantPaths = renameMediaItemAndDescendantPaths({
1914
+ const mediaWithRenamedDescendantPaths = mediaRenameMediaItemAndDescendantPaths({
1672
1915
  newName,
1673
1916
  selectedItemPath,
1674
1917
  media: temp.media,
@@ -1688,7 +1931,7 @@ var MediaCollection = memo(
1688
1931
  const currentFolderPath = tree.idToPathMap.get(currentFolderId);
1689
1932
  if (!currentFolderPath) return;
1690
1933
  setTempItem([
1691
- path3.join(currentFolderPath, PLACEHOLDER_ITEM_NAME),
1934
+ path4.join(currentFolderPath, PLACEHOLDER_ITEM_NAME),
1692
1935
  newFolder
1693
1936
  ]);
1694
1937
  setTimeout(() => {
@@ -1708,7 +1951,7 @@ var MediaCollection = memo(
1708
1951
  );
1709
1952
  if (!currentFolderPath) return;
1710
1953
  setTempItem([
1711
- path3.join(currentFolderPath, PLACEHOLDER_ITEM_NAME),
1954
+ path4.join(currentFolderPath, PLACEHOLDER_ITEM_NAME),
1712
1955
  newFile
1713
1956
  ]);
1714
1957
  setTimeout(() => {
@@ -1719,7 +1962,7 @@ var MediaCollection = memo(
1719
1962
  );
1720
1963
  const handleMoveUpAFolder = useCallback(
1721
1964
  (selectedIds2) => {
1722
- const newMedia = moveUpAFolder({
1965
+ const newMedia = mediaMoveUpAFolder({
1723
1966
  tree,
1724
1967
  media,
1725
1968
  selectedIds: selectedIds2
@@ -1739,30 +1982,35 @@ var MediaCollection = memo(
1739
1982
  if (!parentPath) return;
1740
1983
  const uploadPromises = files.map(async (file) => {
1741
1984
  const asset = await assetManager.create(file);
1742
- const assetPath = path3.join(parentPath, path3.basename(file.name));
1985
+ const assetPath = path4.join(parentPath, path4.basename(file.name));
1743
1986
  return {
1744
1987
  assetPath,
1745
1988
  asset: createMediaAsset({ assetId: asset.id })
1746
1989
  };
1747
1990
  });
1748
1991
  setIsUploading(true);
1749
- const newMediaMap = await Promise.all(uploadPromises);
1992
+ const uploadedAssets = await Promise.all(uploadPromises);
1993
+ const newMediaMap = Object.fromEntries(
1994
+ uploadedAssets.map(({ assetPath, asset }) => [assetPath, asset])
1995
+ );
1750
1996
  setMedia(
1751
1997
  { name: "Add media items", timestamp: Date.now() },
1752
1998
  {
1753
1999
  ...media,
1754
- ...Object.fromEntries(
1755
- newMediaMap.map(({ assetPath, asset }) => [assetPath, asset])
1756
- )
2000
+ ...newMediaMap
1757
2001
  }
1758
2002
  );
2003
+ onAssetsUploaded?.(newMediaMap);
1759
2004
  } catch (error) {
1760
- console.error("Failed to upload files:", error);
2005
+ if (error instanceof Error && error.name === "AbortError") {
2006
+ } else {
2007
+ console.error("Failed to upload files:", error);
2008
+ }
1761
2009
  } finally {
1762
2010
  setIsUploading(false);
1763
2011
  }
1764
2012
  },
1765
- [tree.idToPathMap, setMedia, media, assetManager]
2013
+ [tree.idToPathMap, setMedia, media, assetManager, onAssetsUploaded]
1766
2014
  );
1767
2015
  const handleDownload = useCallback(
1768
2016
  async (selectedItems) => {
@@ -1806,7 +2054,10 @@ var MediaCollection = memo(
1806
2054
  }
1807
2055
  );
1808
2056
  } catch (error) {
1809
- console.error("Failed to upload file:", error);
2057
+ if (error instanceof Error && error.name === "AbortError") {
2058
+ } else {
2059
+ console.error("Failed to upload files:", error);
2060
+ }
1810
2061
  }
1811
2062
  },
1812
2063
  [media, setMedia, assetManager, tree]
@@ -1819,10 +2070,7 @@ var MediaCollection = memo(
1819
2070
  );
1820
2071
  const handleMoveMediaInsideFolder = useCallback(
1821
2072
  (sourceItem, targetItem) => {
1822
- const sourceItemPath = tree.idToPathMap.get(sourceItem.id);
1823
- const targetItemPath = tree.idToPathMap.get(targetItem.id);
1824
- if (!sourceItemPath || !targetItemPath) return;
1825
- const newMedia = moveMediaInsideFolder({
2073
+ const newMedia = mediaMoveMediaInsideFolder({
1826
2074
  sourceItemIds: [sourceItem.id],
1827
2075
  targetItemId: targetItem.id,
1828
2076
  media,
@@ -1859,12 +2107,12 @@ var MediaCollection = memo(
1859
2107
  ],
1860
2108
  [
1861
2109
  onlySingleFolderSelected && {
1862
- title: "Add media",
2110
+ title: "Upload Files",
1863
2111
  value: "upload",
1864
2112
  icon: /* @__PURE__ */ React.createElement(UploadIcon, null)
1865
2113
  },
1866
2114
  onlySingleFolderSelected && {
1867
- title: "Add a Folder",
2115
+ title: "Add Folder",
1868
2116
  value: "addFolder",
1869
2117
  icon: /* @__PURE__ */ React.createElement(FolderIcon, null)
1870
2118
  },
@@ -1941,7 +2189,7 @@ var MediaCollection = memo(
1941
2189
  const handleSetExpanded = useCallback(
1942
2190
  (item, expanded) => {
1943
2191
  setExpandedMap(
1944
- (prev) => updateExpandedMap({
2192
+ (prev) => mediaUpdateExpandedMap({
1945
2193
  item,
1946
2194
  expanded,
1947
2195
  expandable,
@@ -1988,7 +2236,8 @@ var MediaCollection = memo(
1988
2236
  moveUpAFolder: handleMoveUpAFolder,
1989
2237
  replace: handleReplace,
1990
2238
  preview: handlePreview,
1991
- moveMediaInsideFolder: handleMoveMediaInsideFolder
2239
+ moveMediaInsideFolder: handleMoveMediaInsideFolder,
2240
+ getItemAtIndex: (index) => visibleItems[index]
1992
2241
  }));
1993
2242
  return /* @__PURE__ */ React.createElement(React.Fragment, null, /* @__PURE__ */ React.createElement(
1994
2243
  FileExplorerLayout,
@@ -2030,6 +2279,7 @@ var MediaCollection = memo(
2030
2279
  return "Enter folder name";
2031
2280
  case "asset":
2032
2281
  case "file":
2282
+ case "noyaFile":
2033
2283
  return "Enter file name";
2034
2284
  }
2035
2285
  },
@@ -2052,7 +2302,8 @@ var MediaCollection = memo(
2052
2302
  MediaThumbnailInternal,
2053
2303
  {
2054
2304
  ...props,
2055
- path: tree.idToPathMap.get(props.item.id)
2305
+ path: tree.idToPathMap.get(props.item.id),
2306
+ renderThumbnailIcon
2056
2307
  }
2057
2308
  ),
2058
2309
  renderAction,
@@ -2081,92 +2332,1084 @@ var MediaCollection = memo(
2081
2332
  if (sourceListId !== targetListId) {
2082
2333
  return false;
2083
2334
  }
2335
+ if (sourceListId !== sortableId || targetListId !== sortableId) {
2336
+ return false;
2337
+ }
2084
2338
  const sourceItem = visibleItems[sourceIndex];
2085
2339
  const targetItem = visibleItems[targetIndex];
2086
- if (position !== "inside" || targetItem.kind === "asset") {
2087
- return false;
2340
+ return acceptsMediaItemDrop({
2341
+ position,
2342
+ sourceItem,
2343
+ targetItem,
2344
+ tree
2345
+ });
2346
+ },
2347
+ onMoveItem: ({
2348
+ sourceListId,
2349
+ sourceIndex,
2350
+ targetListId,
2351
+ targetIndex,
2352
+ position
2353
+ }) => {
2354
+ if (sourceListId !== sortableId || targetListId !== sortableId) {
2355
+ return;
2356
+ }
2357
+ const sourceItem = visibleItems[sourceIndex];
2358
+ const targetItem = visibleItems[targetIndex];
2359
+ if (position === "inside") {
2360
+ handleMoveMediaInsideFolder(sourceItem, targetItem);
2361
+ }
2362
+ }
2363
+ }
2364
+ )
2365
+ ));
2366
+ })
2367
+ );
2368
+ function acceptsMediaItemDrop(parameters) {
2369
+ const { position, sourceItem, targetItem, tree } = parameters;
2370
+ if (position !== "inside" || targetItem.kind === "asset") {
2371
+ return false;
2372
+ }
2373
+ const sourcePath = tree.findPath(
2374
+ rootMediaItem,
2375
+ (item) => item.id === sourceItem.id
2376
+ );
2377
+ const targetPath = tree.findPath(
2378
+ rootMediaItem,
2379
+ (item) => item.id === targetItem.id
2380
+ );
2381
+ if (!sourcePath || !targetPath) return false;
2382
+ if (isDeepEqual(sourcePath, targetPath.slice(0, sourcePath.length))) {
2383
+ return false;
2384
+ }
2385
+ return true;
2386
+ }
2387
+
2388
+ // src/ResourceExplorer.tsx
2389
+ import {
2390
+ ActionMenu as ActionMenu2,
2391
+ Chip,
2392
+ createSectionedMenu as createSectionedMenu2,
2393
+ cssVars as cssVars2,
2394
+ FileExplorerCollection as FileExplorerCollection2,
2395
+ FileExplorerDetail as FileExplorerDetail2,
2396
+ FileExplorerEmptyState as FileExplorerEmptyState2,
2397
+ FileExplorerLayout as FileExplorerLayout2,
2398
+ FileExplorerUploadButton as FileExplorerUploadButton2,
2399
+ formatByteSize as formatByteSize2,
2400
+ MediaThumbnail as MediaThumbnail2,
2401
+ useOpenConfirmationDialog as useOpenConfirmationDialog2
2402
+ } from "@noya-app/noya-designsystem";
2403
+ import {
2404
+ DownloadIcon as DownloadIcon2,
2405
+ FolderIcon as FolderIcon2,
2406
+ InputIcon as InputIcon2,
2407
+ OpenInNewWindowIcon as OpenInNewWindowIcon2,
2408
+ ResetIcon as ResetIcon2,
2409
+ TrashIcon as TrashIcon2,
2410
+ UpdateIcon as UpdateIcon2,
2411
+ UploadIcon as UploadIcon2
2412
+ } from "@noya-app/noya-icons";
2413
+ import {
2414
+ useAsset as useAsset2,
2415
+ useAssetManager as useAssetManager2,
2416
+ useAssets as useAssets2
2417
+ } from "@noya-app/noya-multiplayer-react";
2418
+ import {
2419
+ createAssetResource as createAssetResource2,
2420
+ createDirectoryResource as createDirectoryResource2,
2421
+ createResourceTree as createResourceTree2,
2422
+ diffResourceMaps,
2423
+ PLACEHOLDER_ITEM_NAME as PLACEHOLDER_ITEM_NAME2,
2424
+ rootResource as rootResource2,
2425
+ rootResourceName
2426
+ } from "@noya-app/noya-schemas";
2427
+ import { Base64 as Base642, groupBy as groupBy2, isDeepEqual as isDeepEqual2, uuid as uuid3 } from "@noya-app/noya-utils";
2428
+ import {
2429
+ AutoSizer,
2430
+ downloadUrl as downloadUrl2,
2431
+ memoGeneric as memoGeneric2,
2432
+ useControlledOrUncontrolled as useControlledOrUncontrolled2
2433
+ } from "@noya-app/react-utils";
2434
+ import { fileOpen as fileOpen2 } from "browser-fs-access";
2435
+ import {
2436
+ forwardRef as forwardRef2,
2437
+ memo as memo2,
2438
+ useCallback as useCallback2,
2439
+ useEffect as useEffect2,
2440
+ useImperativeHandle as useImperativeHandle2,
2441
+ useMemo as useMemo2,
2442
+ useRef as useRef2,
2443
+ useState as useState2
2444
+ } from "react";
2445
+ import { path as path6 } from "imfs";
2446
+ import React2 from "react";
2447
+
2448
+ // src/utils/handleFileDrop.ts
2449
+ import {
2450
+ createAssetResource,
2451
+ createDirectoryResource
2452
+ } from "@noya-app/noya-schemas";
2453
+ import { Base64, uuid as uuid2 } from "@noya-app/noya-utils";
2454
+ import { path as path5 } from "imfs";
2455
+ function isDirectoryEntry(entry) {
2456
+ return entry.isDirectory;
2457
+ }
2458
+ function isFileEntry(entry) {
2459
+ return entry.isFile;
2460
+ }
2461
+ async function handleDataTransfer({
2462
+ accessibleByFileId,
2463
+ dataTransfer,
2464
+ rootItemPath,
2465
+ resourceMap
2466
+ }) {
2467
+ try {
2468
+ const dataTransferItems = Array.from(dataTransfer.items ?? []);
2469
+ const supportsEntries = dataTransferItems.some(
2470
+ (item) => typeof item?.webkitGetAsEntry === "function"
2471
+ );
2472
+ const collectFromEntry = async (entry, basePath) => {
2473
+ if (!entry) return [];
2474
+ if (isFileEntry(entry)) {
2475
+ const file = await new Promise(
2476
+ (resolve) => entry.file((f) => resolve(f))
2477
+ );
2478
+ return [
2479
+ {
2480
+ file,
2481
+ relativePath: path5.join(basePath, file.name)
2482
+ }
2483
+ ];
2484
+ }
2485
+ if (isDirectoryEntry(entry)) {
2486
+ const reader = entry.createReader();
2487
+ const readAll = async () => {
2488
+ const all = [];
2489
+ while (true) {
2490
+ const entries = await new Promise(
2491
+ (resolve) => reader.readEntries((ents) => resolve(ents))
2492
+ );
2493
+ if (!entries.length) break;
2494
+ all.push(...entries);
2495
+ }
2496
+ return all;
2497
+ };
2498
+ const children = await readAll();
2499
+ const results = await Promise.all(
2500
+ children.map(
2501
+ (child) => collectFromEntry(child, path5.join(basePath, entry.name))
2502
+ )
2503
+ );
2504
+ return results.flat();
2505
+ }
2506
+ return [];
2507
+ };
2508
+ let dropped = [];
2509
+ if (supportsEntries) {
2510
+ const topLevelEntries = dataTransferItems.flatMap((item) => {
2511
+ const entry = item.webkitGetAsEntry?.();
2512
+ if (!entry) return [];
2513
+ return [entry];
2514
+ });
2515
+ const nested = await Promise.all(
2516
+ topLevelEntries.map((entry) => collectFromEntry(entry, ""))
2517
+ );
2518
+ dropped = nested.flat();
2519
+ } else {
2520
+ const files = Array.from(dataTransfer.files);
2521
+ if (files.length === 0) return;
2522
+ dropped = files.map((file) => ({
2523
+ file,
2524
+ // Best effort: try webkitRelativePath; fall back to name
2525
+ relativePath: file.webkitRelativePath && file.webkitRelativePath.length > 0 ? file.webkitRelativePath : file.name
2526
+ }));
2527
+ }
2528
+ if (dropped.length === 0) return;
2529
+ const folderRelativePaths = /* @__PURE__ */ new Set();
2530
+ for (const { relativePath } of dropped) {
2531
+ const dir = path5.dirname(relativePath);
2532
+ if (dir && dir !== ".") {
2533
+ const parts = dir.split("/").filter(Boolean);
2534
+ let acc = "";
2535
+ for (const part of parts) {
2536
+ acc = acc ? path5.join(acc, part) : part;
2537
+ folderRelativePaths.add(acc);
2538
+ }
2539
+ }
2540
+ }
2541
+ const folderEntries = Array.from(folderRelativePaths).map((rel) => path5.join(rootItemPath, rel)).filter((full) => !resourceMap[full]).map((full) => [
2542
+ full,
2543
+ createDirectoryResource({
2544
+ id: uuid2(),
2545
+ path: full,
2546
+ accessibleByFileId,
2547
+ stableId: uuid2()
2548
+ })
2549
+ ]);
2550
+ const uploadResults = await Promise.all(
2551
+ dropped.map(
2552
+ async ({ file, relativePath }) => {
2553
+ return [
2554
+ path5.join(rootItemPath, relativePath),
2555
+ createAssetResource({
2556
+ id: uuid2(),
2557
+ asset: {
2558
+ content: Base64.encode(await file.arrayBuffer()),
2559
+ contentType: file.type,
2560
+ encoding: "base64"
2561
+ },
2562
+ path: path5.join(rootItemPath, relativePath),
2563
+ accessibleByFileId,
2564
+ stableId: uuid2()
2565
+ })
2566
+ ];
2567
+ }
2568
+ )
2569
+ );
2570
+ return {
2571
+ ...resourceMap,
2572
+ ...Object.fromEntries([...folderEntries, ...uploadResults])
2573
+ };
2574
+ } catch (error) {
2575
+ console.error("Failed to upload dropped files:", error);
2576
+ }
2577
+ }
2578
+
2579
+ // src/ResourceExplorer.tsx
2580
+ var gridThumbnailDimension = {
2581
+ width: 800,
2582
+ height: 800
2583
+ };
2584
+ var ResourceThumbnail = memoGeneric2(
2585
+ ({
2586
+ item,
2587
+ selected,
2588
+ size,
2589
+ path: pathProp,
2590
+ renderThumbnailIcon,
2591
+ className,
2592
+ url: urlProp,
2593
+ contentType: contentTypeProp,
2594
+ viewType
2595
+ }) => {
2596
+ const asset = useAsset2(item.type === "asset" ? item.assetId : void 0);
2597
+ const isRoot = item.id === rootResource2.id;
2598
+ const isFolder = item.type === "directory";
2599
+ let contentType;
2600
+ let url;
2601
+ if (asset) {
2602
+ contentType = asset.contentType;
2603
+ url = asset.url;
2604
+ } else if (urlProp) {
2605
+ url = urlProp;
2606
+ contentType = contentTypeProp;
2607
+ }
2608
+ const fileName = pathProp ? path6.basename(pathProp) : void 0;
2609
+ return /* @__PURE__ */ React2.createElement(
2610
+ MediaThumbnail2,
2611
+ {
2612
+ contentType,
2613
+ iconName: isRoot ? "HomeIcon" : isFolder ? "FolderIcon" : void 0,
2614
+ url,
2615
+ selected,
2616
+ size,
2617
+ fileName,
2618
+ renderThumbnailIcon,
2619
+ dimensions: viewType === "grid" ? gridThumbnailDimension : void 0,
2620
+ className
2621
+ }
2622
+ );
2623
+ }
2624
+ );
2625
+ var ResourceExplorer = memo2(
2626
+ forwardRef2(
2627
+ function ResourceExplorer2({
2628
+ parentFileId,
2629
+ sortableId,
2630
+ onSelectionChange,
2631
+ selectedIds: selectedIdsProp,
2632
+ media,
2633
+ setMedia: setMediaProp,
2634
+ readOnly = false,
2635
+ viewType = "list",
2636
+ fileKindFilter = "all",
2637
+ showRootItem = false,
2638
+ initialExpanded,
2639
+ expandable = true,
2640
+ renamable = true,
2641
+ onDoubleClickItem,
2642
+ rootItemId = rootResource2.id,
2643
+ title,
2644
+ size = "medium",
2645
+ right,
2646
+ renderAction: renderActionProp,
2647
+ className,
2648
+ showUploadButton = true,
2649
+ showAllDescendants = true,
2650
+ scrollable = false,
2651
+ sortable = false,
2652
+ renderEmptyState,
2653
+ sharedDragProps,
2654
+ onClickItem,
2655
+ renderThumbnailIcon,
2656
+ onDidDeleteItems,
2657
+ onAssetsUploaded,
2658
+ itemClassName,
2659
+ itemStyle,
2660
+ publishedResources,
2661
+ virtualized = false,
2662
+ renderUser
2663
+ }, ref) {
2664
+ const setMedia = useCallback2(
2665
+ (...args) => {
2666
+ setMediaProp?.(...args);
2667
+ },
2668
+ [setMediaProp]
2669
+ );
2670
+ const tree = useMemo2(() => createResourceTree2(media), [media]);
2671
+ const [tempItem, setTempItem] = useState2(
2672
+ void 0
2673
+ );
2674
+ const mediaWithTempItem = useMemo2(
2675
+ () => ({
2676
+ ...media,
2677
+ ...tempItem ? { [tempItem[0]]: tempItem[1] } : {}
2678
+ }),
2679
+ [media, tempItem]
2680
+ );
2681
+ const treeWithTempItem = useMemo2(
2682
+ () => createResourceTree2(mediaWithTempItem),
2683
+ [mediaWithTempItem]
2684
+ );
2685
+ const temp = useMemo2(
2686
+ () => ({
2687
+ media: mediaWithTempItem,
2688
+ tree: treeWithTempItem
2689
+ }),
2690
+ [mediaWithTempItem, treeWithTempItem]
2691
+ );
2692
+ const [selectedIds, setSelectedIds] = useControlledOrUncontrolled2({
2693
+ defaultValue: [],
2694
+ value: selectedIdsProp,
2695
+ onChange: onSelectionChange
2696
+ });
2697
+ const assetManager = useAssetManager2();
2698
+ const assets = useAssets2();
2699
+ const [expandedMap, setExpandedMap] = useState2({});
2700
+ const visibleItems = useMemo2(
2701
+ () => getVisibleItems({
2702
+ expandedMap,
2703
+ fileKindFilter,
2704
+ rootItemId,
2705
+ tree: treeWithTempItem,
2706
+ showAllDescendants,
2707
+ showRootItem
2708
+ }),
2709
+ [
2710
+ expandedMap,
2711
+ fileKindFilter,
2712
+ rootItemId,
2713
+ treeWithTempItem,
2714
+ showAllDescendants,
2715
+ showRootItem
2716
+ ]
2717
+ );
2718
+ const depthMap = useMemo2(
2719
+ () => getDepthMap(rootResource2, treeWithTempItem, showAllDescendants),
2720
+ [treeWithTempItem, showAllDescendants]
2721
+ );
2722
+ const collectionRef = useRef2(null);
2723
+ const selectedResources = useMemo2(
2724
+ () => treeWithTempItem.resourcesWithRoot.filter(
2725
+ (item) => selectedIds.includes(item.id)
2726
+ ),
2727
+ [treeWithTempItem, selectedIds]
2728
+ );
2729
+ const groupedItems = groupBy2(selectedResources, (item) => item.type);
2730
+ const selectedAssetItems = groupedItems.asset ?? [];
2731
+ const selectedFolderItems = groupedItems.folder ?? [];
2732
+ const singleItemSelected = selectedResources.length === 1;
2733
+ const onlyAssetsSelected = selectedAssetItems.length > 0 && selectedAssetItems.length === selectedResources.length;
2734
+ const onlyFoldersSelected = selectedFolderItems.length > 0 && selectedFolderItems.length === selectedResources.length;
2735
+ const onlySingleFolderSelected = onlyFoldersSelected && selectedFolderItems.length === 1;
2736
+ const onlySingleAssetSelected = onlyAssetsSelected && selectedAssetItems.length === 1;
2737
+ const rootSelected = selectedIds.includes(rootResource2.id);
2738
+ const sameParentSelected = selectedResources.every((item) => {
2739
+ const itemPath = tree.idToPathMap.get(item.id);
2740
+ const firstSelectedPath = tree.idToPathMap.get(selectedIds[0]);
2741
+ if (!itemPath || !firstSelectedPath) return false;
2742
+ return itemPath.startsWith(path6.dirname(firstSelectedPath));
2743
+ });
2744
+ useEffect2(() => {
2745
+ if (initialExpanded) {
2746
+ setExpandedMap(initialExpanded);
2747
+ }
2748
+ }, [initialExpanded]);
2749
+ const getExpanded = useCallback2(
2750
+ (item) => {
2751
+ if (!expandable) return void 0;
2752
+ if (item.type !== "directory") return void 0;
2753
+ if (item.id === rootResource2.id) return void 0;
2754
+ return expandedMap[item.id] ?? false;
2755
+ },
2756
+ [expandedMap, expandable]
2757
+ );
2758
+ const openConfirmationDialog = useOpenConfirmationDialog2();
2759
+ const handleDelete = useCallback2(
2760
+ async (selectedIds2) => {
2761
+ const ok = await openConfirmationDialog({
2762
+ title: "Delete items",
2763
+ description: "Are you sure you want to delete these items? This action cannot be undone."
2764
+ });
2765
+ if (!ok) return;
2766
+ const deletedItems = Object.entries(media).flatMap(
2767
+ ([path7, item]) => {
2768
+ if (selectedIds2.includes(item.id)) {
2769
+ return [[path7, item]];
2770
+ }
2771
+ return [];
2088
2772
  }
2089
- const sourcePath = tree.findPath(
2090
- rootMediaItem,
2091
- (item) => item.id === sourceItem.id
2773
+ );
2774
+ const newMedia = deleteResources({
2775
+ selectedIds: selectedIds2,
2776
+ media,
2777
+ tree
2778
+ });
2779
+ setSelectedIds([rootResource2.id]);
2780
+ setMedia({ name: "Delete items", timestamp: Date.now() }, newMedia);
2781
+ onDidDeleteItems?.(deletedItems);
2782
+ },
2783
+ [
2784
+ media,
2785
+ setMedia,
2786
+ setSelectedIds,
2787
+ tree,
2788
+ onDidDeleteItems,
2789
+ openConfirmationDialog
2790
+ ]
2791
+ );
2792
+ const onRename = useCallback2(
2793
+ (selectedItem, newName) => {
2794
+ if (!renamable) return;
2795
+ const selectedItemPath = treeWithTempItem.idToPathMap.get(
2796
+ selectedItem.id
2797
+ );
2798
+ if (!selectedItemPath) return;
2799
+ const renameIsValid = validateResourceRename({
2800
+ basename: newName,
2801
+ selectedItemPath,
2802
+ media: temp.media
2803
+ });
2804
+ if (!renameIsValid) {
2805
+ setTempItem(void 0);
2806
+ return;
2807
+ }
2808
+ const mediaWithRenamedDescendantPaths = renameResourceAndDescendantPaths({
2809
+ newName,
2810
+ selectedItemPath,
2811
+ media: temp.media,
2812
+ tree: temp.tree
2813
+ });
2814
+ setMedia(
2815
+ { name: "Rename media item", timestamp: Date.now() },
2816
+ mediaWithRenamedDescendantPaths
2817
+ );
2818
+ setTempItem(void 0);
2819
+ },
2820
+ [
2821
+ renamable,
2822
+ setMedia,
2823
+ temp.media,
2824
+ temp.tree,
2825
+ treeWithTempItem.idToPathMap
2826
+ ]
2827
+ );
2828
+ const handleAddFolder = useCallback2(
2829
+ (currentFolderId) => {
2830
+ const currentFolderPath = tree.idToPathMap.get(currentFolderId);
2831
+ if (!currentFolderPath) return;
2832
+ const newFolderPath = path6.join(
2833
+ currentFolderPath,
2834
+ PLACEHOLDER_ITEM_NAME2
2835
+ );
2836
+ const newFolder = createDirectoryResource2({
2837
+ path: newFolderPath,
2838
+ accessibleByFileId: parentFileId,
2839
+ id: newFolderPath,
2840
+ stableId: uuid3()
2841
+ });
2842
+ setTempItem([newFolderPath, newFolder]);
2843
+ setTimeout(() => {
2844
+ collectionRef.current?.editName(newFolder.id);
2845
+ }, 50);
2846
+ },
2847
+ [parentFileId, tree]
2848
+ );
2849
+ const handleMoveUpAFolder = useCallback2(
2850
+ (selectedIds2) => {
2851
+ const newMedia = moveUpAFolder({
2852
+ tree,
2853
+ media,
2854
+ selectedIds: selectedIds2
2855
+ });
2856
+ if (!newMedia) return;
2857
+ setMedia({ name: "Move items", timestamp: Date.now() }, newMedia);
2858
+ },
2859
+ [media, tree, setMedia]
2860
+ );
2861
+ const [isUploading, setIsUploading] = useState2(false);
2862
+ const handleUpload = useCallback2(
2863
+ async (selectedId) => {
2864
+ try {
2865
+ const files = await fileOpen2({ multiple: true });
2866
+ if (!files || !Array.isArray(files) || files.length === 0) return;
2867
+ const parentPath = tree.idToPathMap.get(selectedId);
2868
+ if (!parentPath) return;
2869
+ const uploadPromises = files.map(async (file) => {
2870
+ const assetPath = path6.join(parentPath, path6.basename(file.name));
2871
+ return {
2872
+ assetPath,
2873
+ asset: createAssetResource2({
2874
+ id: uuid3(),
2875
+ asset: {
2876
+ content: Base642.encode(await file.arrayBuffer()),
2877
+ contentType: file.type,
2878
+ encoding: "base64"
2879
+ },
2880
+ path: assetPath,
2881
+ accessibleByFileId: parentFileId,
2882
+ stableId: uuid3()
2883
+ })
2884
+ };
2885
+ });
2886
+ setIsUploading(true);
2887
+ const uploadedAssets = await Promise.all(uploadPromises);
2888
+ const newMediaMap = Object.fromEntries(
2889
+ uploadedAssets.map(({ assetPath, asset }) => [assetPath, asset])
2092
2890
  );
2093
- const targetPath = tree.findPath(
2094
- rootMediaItem,
2095
- (item) => item.id === targetItem.id
2891
+ setMedia(
2892
+ { name: "Add media items", timestamp: Date.now() },
2893
+ {
2894
+ ...media,
2895
+ ...newMediaMap
2896
+ }
2096
2897
  );
2097
- if (!sourcePath || !targetPath) return false;
2098
- if (isDeepEqual(sourcePath, targetPath.slice(0, sourcePath.length))) {
2099
- return false;
2898
+ onAssetsUploaded?.(newMediaMap);
2899
+ } catch (error) {
2900
+ if (error instanceof Error && error.name === "AbortError") {
2901
+ } else {
2902
+ console.error("Failed to upload files:", error);
2903
+ }
2904
+ } finally {
2905
+ setIsUploading(false);
2906
+ }
2907
+ },
2908
+ [tree, setMedia, media, onAssetsUploaded, parentFileId]
2909
+ );
2910
+ const handleDownload = useCallback2(
2911
+ async (selectedItems) => {
2912
+ const downloadPromises = selectedItems.filter((item) => item.type === "asset").map(async (item) => {
2913
+ const asset = assets.find((a) => a.id === item.assetId);
2914
+ if (!asset?.url) return;
2915
+ return downloadUrl2(asset.url, tree.getNameForId(item.id));
2916
+ });
2917
+ await Promise.all(downloadPromises);
2918
+ },
2919
+ [assets, tree]
2920
+ );
2921
+ const handlePreview = useCallback2(
2922
+ async (selectedItems) => {
2923
+ const previewPromises = selectedItems.filter((item) => item.type === "asset").map(async (item) => {
2924
+ const asset = assets.find((a) => a.id === item.assetId);
2925
+ if (!asset?.url) return;
2926
+ return window?.open(asset.url, "_blank");
2927
+ });
2928
+ await Promise.all(previewPromises);
2929
+ },
2930
+ [assets]
2931
+ );
2932
+ const handleReplace = useCallback2(
2933
+ async (selectedItem) => {
2934
+ try {
2935
+ const file = await fileOpen2();
2936
+ if (!file) return;
2937
+ const asset = await assetManager.create(file);
2938
+ const oldFile = selectedItem;
2939
+ const oldFilePath = tree.idToPathMap.get(oldFile.id);
2940
+ if (!oldFilePath || oldFile.type !== "asset") return;
2941
+ setMedia(
2942
+ { name: "Replace media file", timestamp: Date.now() },
2943
+ {
2944
+ ...media,
2945
+ [oldFilePath]: createAssetResource2({
2946
+ ...oldFile,
2947
+ assetId: asset.id
2948
+ })
2949
+ }
2950
+ );
2951
+ } catch (error) {
2952
+ if (error instanceof Error && error.name === "AbortError") {
2953
+ } else {
2954
+ console.error("Failed to upload files:", error);
2955
+ }
2956
+ }
2957
+ },
2958
+ [media, setMedia, assetManager, tree]
2959
+ );
2960
+ const handleRename = useCallback2(
2961
+ (selectedItemId) => {
2962
+ collectionRef.current?.editName(selectedItemId);
2963
+ },
2964
+ [collectionRef]
2965
+ );
2966
+ const handleMoveMediaInsideFolder = useCallback2(
2967
+ (sourceItems, targetItem) => {
2968
+ const newMedia = moveMediaInsideFolder({
2969
+ sourceItemIds: sourceItems.map((item) => item.id),
2970
+ targetItemId: targetItem.id,
2971
+ media,
2972
+ tree
2973
+ });
2974
+ setMedia(
2975
+ {
2976
+ name: "Move media file inside folder",
2977
+ timestamp: Date.now()
2978
+ },
2979
+ newMedia
2980
+ );
2981
+ },
2982
+ [media, setMedia, tree]
2983
+ );
2984
+ const assetContextMenuItems = useMemo2(() => {
2985
+ return createSectionedMenu2(
2986
+ [
2987
+ !rootSelected && singleItemSelected && {
2988
+ title: "Rename",
2989
+ value: "rename",
2990
+ icon: /* @__PURE__ */ React2.createElement(InputIcon2, null)
2991
+ },
2992
+ onlySingleAssetSelected && {
2993
+ title: "Replace",
2994
+ value: "replace",
2995
+ icon: /* @__PURE__ */ React2.createElement(UpdateIcon2, null)
2996
+ },
2997
+ !rootSelected && {
2998
+ title: "Delete",
2999
+ value: "delete",
3000
+ icon: /* @__PURE__ */ React2.createElement(TrashIcon2, null)
2100
3001
  }
3002
+ ],
3003
+ [
3004
+ onlySingleFolderSelected && {
3005
+ title: "Upload Files",
3006
+ value: "upload",
3007
+ icon: /* @__PURE__ */ React2.createElement(UploadIcon2, null)
3008
+ },
3009
+ onlySingleFolderSelected && {
3010
+ title: "Add Folder",
3011
+ value: "addFolder",
3012
+ icon: /* @__PURE__ */ React2.createElement(FolderIcon2, null)
3013
+ },
3014
+ onlyAssetsSelected && tree.resourcesWithRoot.length > 0 && {
3015
+ title: "Download",
3016
+ value: "download",
3017
+ icon: /* @__PURE__ */ React2.createElement(DownloadIcon2, null)
3018
+ },
3019
+ onlySingleAssetSelected && {
3020
+ title: "Preview",
3021
+ value: "preview",
3022
+ icon: /* @__PURE__ */ React2.createElement(OpenInNewWindowIcon2, null)
3023
+ }
3024
+ ],
3025
+ [
3026
+ !rootSelected && sameParentSelected && {
3027
+ title: "Move up a folder",
3028
+ value: "move",
3029
+ icon: /* @__PURE__ */ React2.createElement(ResetIcon2, null)
3030
+ }
3031
+ ]
3032
+ );
3033
+ }, [
3034
+ rootSelected,
3035
+ singleItemSelected,
3036
+ onlySingleAssetSelected,
3037
+ onlySingleFolderSelected,
3038
+ onlyAssetsSelected,
3039
+ tree.resourcesWithRoot.length,
3040
+ sameParentSelected
3041
+ ]);
3042
+ const handleMenuAction = useCallback2(
3043
+ async (action, selectedItems) => {
3044
+ if (selectedItems.length === 0) return;
3045
+ switch (action) {
3046
+ case "rename":
3047
+ handleRename(selectedItems[0].id);
3048
+ return;
3049
+ case "delete":
3050
+ handleDelete(selectedIds);
3051
+ return;
3052
+ case "replace":
3053
+ handleReplace(selectedItems[0]);
3054
+ return;
3055
+ case "upload":
3056
+ handleUpload(selectedItems[0].id);
3057
+ return;
3058
+ case "addFolder":
3059
+ handleAddFolder(selectedItems[0].id);
3060
+ return;
3061
+ case "preview":
3062
+ handlePreview(selectedItems);
3063
+ return;
3064
+ case "download":
3065
+ handleDownload(selectedItems);
3066
+ return;
3067
+ case "move":
3068
+ handleMoveUpAFolder(selectedIds);
3069
+ return;
3070
+ }
3071
+ },
3072
+ [
3073
+ handleRename,
3074
+ handleDelete,
3075
+ selectedIds,
3076
+ handleReplace,
3077
+ handleUpload,
3078
+ handleAddFolder,
3079
+ handlePreview,
3080
+ handleDownload,
3081
+ handleMoveUpAFolder
3082
+ ]
3083
+ );
3084
+ const handleSetExpanded = useCallback2(
3085
+ (item, expanded) => {
3086
+ setExpandedMap(
3087
+ (prev) => updateExpandedMap({
3088
+ item,
3089
+ expanded,
3090
+ expandable,
3091
+ expandedMap: prev,
3092
+ tree
3093
+ })
3094
+ );
3095
+ },
3096
+ [expandable, tree]
3097
+ );
3098
+ const renderAction = useMemo2(() => {
3099
+ if (renderActionProp) return renderActionProp;
3100
+ return ({
3101
+ selected,
3102
+ onOpenChange
3103
+ }) => /* @__PURE__ */ React2.createElement(
3104
+ "div",
3105
+ {
3106
+ style: {
3107
+ minWidth: "75px",
3108
+ display: "flex",
3109
+ justifyContent: "flex-end"
3110
+ }
3111
+ },
3112
+ /* @__PURE__ */ React2.createElement(
3113
+ ActionMenu2,
3114
+ {
3115
+ menuItems: assetContextMenuItems,
3116
+ onSelect: (action) => handleMenuAction(action, selectedResources),
3117
+ selected,
3118
+ onOpenChange,
3119
+ variant: viewType === "grid" ? "normal" : "bare",
3120
+ style: {
3121
+ backgroundColor: selected ? cssVars2.colors.selectedListItemBackground : "transparent",
3122
+ color: selected ? cssVars2.colors.selectedListItemText : void 0
3123
+ }
3124
+ }
3125
+ )
3126
+ );
3127
+ }, [
3128
+ assetContextMenuItems,
3129
+ handleMenuAction,
3130
+ renderActionProp,
3131
+ selectedResources,
3132
+ viewType
3133
+ ]);
3134
+ useImperativeHandle2(ref, () => ({
3135
+ upload: (selectedId) => handleUpload(selectedId),
3136
+ delete: handleDelete,
3137
+ download: handleDownload,
3138
+ rename: handleRename,
3139
+ addFolder: handleAddFolder,
3140
+ moveUpAFolder: handleMoveUpAFolder,
3141
+ replace: handleReplace,
3142
+ preview: handlePreview,
3143
+ moveMediaInsideFolder: handleMoveMediaInsideFolder,
3144
+ getItemAtIndex: (index) => visibleItems[index]
3145
+ }));
3146
+ const diffResources = useMemo2(() => {
3147
+ if (!publishedResources) return;
3148
+ const diff = diffResourceMaps(publishedResources, media, "stableId");
3149
+ return {
3150
+ added: new Set(
3151
+ diff.addedResources.map((resource) => resource.stableId)
3152
+ ),
3153
+ removed: new Set(
3154
+ diff.removedResources.map((resource) => resource.stableId)
3155
+ ),
3156
+ modified: new Set(
3157
+ diff.modifiedResources.map((resource) => resource.stableId)
3158
+ )
3159
+ };
3160
+ }, [media, publishedResources]);
3161
+ const keyExtractor = useCallback2((item) => item.id, []);
3162
+ const renderCollection = (virtualizedSize) => /* @__PURE__ */ React2.createElement(
3163
+ FileExplorerCollection2,
3164
+ {
3165
+ ref: collectionRef,
3166
+ sortableId,
3167
+ scrollable,
3168
+ sharedDragProps,
3169
+ items: visibleItems,
3170
+ viewType,
3171
+ size,
3172
+ getId: keyExtractor,
3173
+ getName: (file) => {
3174
+ if (file.id === tempItem?.[1].id) {
3175
+ return "";
3176
+ }
3177
+ return treeWithTempItem.getNameForId(file.id);
3178
+ },
3179
+ expandable,
3180
+ sortable,
3181
+ getPlaceholder: (item) => {
3182
+ switch (item.type) {
3183
+ case "directory":
3184
+ return "Enter folder name";
3185
+ case "resource":
3186
+ case "asset":
3187
+ case "file":
3188
+ return "Enter file name";
3189
+ }
3190
+ },
3191
+ getExpanded,
3192
+ setExpanded: handleSetExpanded,
3193
+ getRenamable: (item) => {
3194
+ if (item.id === rootResource2.id) return false;
2101
3195
  return true;
2102
3196
  },
2103
- onMoveItem: ({ sourceIndex, targetIndex, position }) => {
3197
+ getDepth: (item) => depthMap[item.id],
3198
+ menuItems: assetContextMenuItems,
3199
+ onSelectMenuItem: handleMenuAction,
3200
+ onSelectionChange: setSelectedIds,
3201
+ onClickItem,
3202
+ onClick: () => {
3203
+ setSelectedIds([]);
3204
+ },
3205
+ onDoubleClickItem,
3206
+ onRename,
3207
+ renamable,
3208
+ selectedIds,
3209
+ itemClassName,
3210
+ itemStyle,
3211
+ virtualized: virtualizedSize,
3212
+ renderThumbnail: (props) => /* @__PURE__ */ React2.createElement(
3213
+ ResourceThumbnail,
3214
+ {
3215
+ ...props,
3216
+ path: tree.idToPathMap.get(props.item.id),
3217
+ renderThumbnailIcon,
3218
+ viewType
3219
+ }
3220
+ ),
3221
+ renderAction,
3222
+ renderRight: (file) => {
3223
+ if (file.type !== "asset") return null;
3224
+ const publishStatus = getPublishStatus({
3225
+ resource: file,
3226
+ diff: diffResources
3227
+ });
3228
+ return /* @__PURE__ */ React2.createElement(
3229
+ "div",
3230
+ {
3231
+ style: { display: "flex", alignItems: "center", gap: "1.5px" }
3232
+ },
3233
+ publishStatus && /* @__PURE__ */ React2.createElement(
3234
+ Chip,
3235
+ {
3236
+ colorScheme: publishStatus === "Modified" ? "info" : publishStatus === "Added" ? "secondary" : "primary"
3237
+ },
3238
+ publishStatus
3239
+ ),
3240
+ renderUser?.(file)
3241
+ );
3242
+ },
3243
+ renderDetail: (file, selected) => {
3244
+ if (file.type !== "asset") return null;
3245
+ const asset = assets.find((a) => a.id === file.assetId);
3246
+ if (!asset) return null;
3247
+ return /* @__PURE__ */ React2.createElement(
3248
+ FileExplorerDetail2,
3249
+ {
3250
+ selected,
3251
+ size,
3252
+ style: { minWidth: "75px", textAlign: "right" }
3253
+ },
3254
+ formatByteSize2(asset.size)
3255
+ );
3256
+ },
3257
+ renderEmptyState: () => renderEmptyState?.() ?? /* @__PURE__ */ React2.createElement(FileExplorerEmptyState2, null),
3258
+ itemRoleDescription: "clickable file item",
3259
+ getDropTargetParentIndex: (overIndex) => {
3260
+ const item = visibleItems[overIndex];
3261
+ const parentIndex = visibleItems.findIndex(
3262
+ (i) => i.id === tree.getParentIdForId(item.id)
3263
+ );
3264
+ return parentIndex === -1 ? void 0 : parentIndex;
3265
+ },
3266
+ acceptsDrop: ({
3267
+ sourceIndex,
3268
+ targetIndex,
3269
+ position,
3270
+ sourceListId,
3271
+ targetListId
3272
+ }) => {
3273
+ if (sourceListId !== targetListId) {
3274
+ return false;
3275
+ }
3276
+ if (sourceListId !== sortableId || targetListId !== sortableId) {
3277
+ return false;
3278
+ }
2104
3279
  const sourceItem = visibleItems[sourceIndex];
2105
3280
  const targetItem = visibleItems[targetIndex];
2106
- if (position === "inside") {
2107
- handleMoveMediaInsideFolder(sourceItem, targetItem);
3281
+ return acceptsResourceDrop({
3282
+ position,
3283
+ sourceItem,
3284
+ targetItem,
3285
+ tree
3286
+ });
3287
+ },
3288
+ onMoveItem: ({
3289
+ sourceListId,
3290
+ sourceIndex,
3291
+ targetListId,
3292
+ targetIndex,
3293
+ position
3294
+ }) => {
3295
+ if (sourceListId !== sortableId || targetListId !== sortableId || position !== "inside") {
3296
+ return;
2108
3297
  }
3298
+ const selectedItems = visibleItems.filter(
3299
+ (item) => selectedIds.includes(item.id)
3300
+ );
3301
+ const sourceItem = visibleItems[sourceIndex];
3302
+ const itemsToMove = selectedIds.includes(sourceItem.id) ? selectedItems : [sourceItem];
3303
+ const targetItem = visibleItems[targetIndex];
3304
+ handleMoveMediaInsideFolder(itemsToMove, targetItem);
2109
3305
  },
2110
3306
  onFilesDrop: async (event) => {
2111
3307
  event.preventDefault();
2112
- const files = Array.from(event.dataTransfer.files);
2113
- if (files.length === 0) return;
2114
- try {
2115
- const uploadPromises = files.map(async (file) => {
2116
- const asset = await assetManager.create(file);
2117
- return {
2118
- asset: createMediaAsset({
2119
- assetId: asset.id
2120
- }),
2121
- name: file.name
2122
- };
2123
- });
2124
- const newMediaItems = await Promise.all(uploadPromises);
2125
- const rootItemPath = tree.idToPathMap.get(rootItemId);
2126
- if (!rootItemPath) return;
2127
- setMedia(
2128
- { name: "Add media files", timestamp: Date.now() },
2129
- {
2130
- ...media,
2131
- ...Object.fromEntries(
2132
- newMediaItems.map((item) => [
2133
- path3.join(rootItemPath, item.name),
2134
- item.asset
2135
- ])
2136
- )
2137
- }
2138
- );
2139
- } catch (error) {
2140
- console.error("Failed to upload dropped files:", error);
2141
- }
3308
+ const rootItemPath = tree.idToPathMap.get(rootItemId);
3309
+ if (!rootItemPath) return;
3310
+ const newMedia = await handleDataTransfer({
3311
+ dataTransfer: event.dataTransfer,
3312
+ rootItemPath,
3313
+ resourceMap: media,
3314
+ accessibleByFileId: parentFileId
3315
+ });
3316
+ if (!newMedia) return;
3317
+ setMedia(
3318
+ { name: "Add media files", timestamp: Date.now() },
3319
+ newMedia
3320
+ );
2142
3321
  }
2143
3322
  }
2144
- )
2145
- ));
2146
- })
3323
+ );
3324
+ return /* @__PURE__ */ React2.createElement(
3325
+ FileExplorerLayout2,
3326
+ {
3327
+ title: title ?? rootResourceName,
3328
+ right: !readOnly && /* @__PURE__ */ React2.createElement(
3329
+ FileExplorerUploadButton2,
3330
+ {
3331
+ showUploadButton,
3332
+ onUpload: () => handleUpload(rootResource2.id),
3333
+ isUploading
3334
+ },
3335
+ right
3336
+ ),
3337
+ className
3338
+ },
3339
+ virtualized ? /* @__PURE__ */ React2.createElement(AutoSizer, null, ({ width, height }) => (
3340
+ // add 24px to account for the -mx-3 on the Collection component
3341
+ renderCollection({ width: width + 24, height })
3342
+ )) : renderCollection(void 0)
3343
+ );
3344
+ }
3345
+ )
2147
3346
  );
3347
+ function acceptsResourceDrop(parameters) {
3348
+ const { position, sourceItem, targetItem, tree } = parameters;
3349
+ if (position !== "inside" || targetItem.type === "asset") {
3350
+ return false;
3351
+ }
3352
+ const sourcePath = tree.findPath(
3353
+ rootResource2,
3354
+ (item) => item.id === sourceItem.id
3355
+ );
3356
+ const targetPath = tree.findPath(
3357
+ rootResource2,
3358
+ (item) => item.id === targetItem.id
3359
+ );
3360
+ if (!sourcePath || !targetPath) return false;
3361
+ if (isDeepEqual2(sourcePath, targetPath.slice(0, sourcePath.length))) {
3362
+ return false;
3363
+ }
3364
+ return true;
3365
+ }
3366
+ function getPublishStatus({
3367
+ resource,
3368
+ diff
3369
+ }) {
3370
+ if (!diff) return void 0;
3371
+ return diff.modified.has(resource.stableId) ? "Modified" : diff.added.has(resource.stableId) ? "Added" : diff.removed.has(resource.stableId) ? "Removed" : (
3372
+ // We don't have to check for the specific ID, since if anything had been published
3373
+ // but removed, it will be in the removed set
3374
+ "Published"
3375
+ );
3376
+ }
2148
3377
  export {
2149
3378
  MediaCollection,
2150
3379
  PLACEHOLDER_ITEM_NAME,
3380
+ ResourceExplorer,
3381
+ ResourceThumbnail,
3382
+ acceptsMediaItemDrop,
3383
+ acceptsResourceDrop,
2151
3384
  basenameValidator,
2152
3385
  createMediaAsset,
2153
3386
  createMediaFile,
2154
3387
  createMediaFolder,
2155
3388
  createMediaItem,
2156
3389
  createMediaItemTree,
2157
- deleteMediaItems,
2158
- formatByteSize,
3390
+ deleteResources,
2159
3391
  getDepthMap,
2160
3392
  getParentDirectories,
2161
3393
  getVisibleItems,
3394
+ gridThumbnailDimension,
3395
+ mediaDeleteMediaItems,
3396
+ mediaGetDepthMap,
3397
+ mediaGetParentDirectories,
3398
+ mediaGetVisibleItems,
3399
+ mediaMoveMediaInsideFolder,
3400
+ mediaMovePathsIntoTarget,
3401
+ mediaMoveUpAFolder,
3402
+ mediaRenameMediaItemAndDescendantPaths,
3403
+ mediaUpdateExpandedMap,
3404
+ mediaValidateMediaItemRename,
2162
3405
  moveMediaInsideFolder,
2163
3406
  movePathsIntoTarget,
2164
3407
  moveUpAFolder,
2165
- renameMediaItemAndDescendantPaths,
3408
+ renameResourceAndDescendantPaths,
2166
3409
  rootMediaItem,
2167
3410
  rootMediaItemName,
2168
3411
  rootMediaItemPath,
2169
3412
  updateExpandedMap,
2170
- validateMediaItemRename
3413
+ validateResourceRename
2171
3414
  };
2172
3415
  //# sourceMappingURL=index.mjs.map