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