@lvce-editor/explorer-view 2.29.0 → 2.31.0

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.
@@ -985,8 +985,12 @@ const getMissingIconRequests = (dirents, fileIconCache) => {
985
985
  return missingRequests;
986
986
  };
987
987
 
988
- const getPath = dirent => {
989
- return dirent.path;
988
+ const getPath = item => {
989
+ return item.path;
990
+ };
991
+
992
+ const getPaths = items => {
993
+ return items.map(getPath);
990
994
  };
991
995
 
992
996
  const DELTA_EDITING = 100;
@@ -1058,7 +1062,7 @@ const getFileIcons = async (dirents, fileIconCache) => {
1058
1062
  const missingRequests = getMissingIconRequests(dirents, fileIconCache);
1059
1063
  const newIcons = await requestFileIcons(missingRequests);
1060
1064
  const newFileIconCache = updateIconCache(fileIconCache, missingRequests, newIcons);
1061
- const paths = dirents.map(getPath);
1065
+ const paths = getPaths(dirents);
1062
1066
  const icons = getIconsCached(paths, newFileIconCache);
1063
1067
  return {
1064
1068
  icons,
@@ -1653,7 +1657,11 @@ const create2 = (uid, uri, x, y, width, height, args, parentUid, platform = 0) =
1653
1657
  end: 0
1654
1658
  },
1655
1659
  focusWord: '',
1656
- focusWordTimeout: 800
1660
+ focusWordTimeout: 800,
1661
+ finalDeltaY: 0,
1662
+ handleOffset: 0,
1663
+ scrollBarActive: false,
1664
+ scrollBarHeight: 0
1657
1665
  };
1658
1666
  set(uid, state, state);
1659
1667
  };
@@ -1696,7 +1704,11 @@ const create = (id, uri, x, y, width, height, args, parentUid, platform = 0) =>
1696
1704
  end: 0
1697
1705
  },
1698
1706
  focusWord: '',
1699
- focusWordTimeout: 800
1707
+ focusWordTimeout: 800,
1708
+ finalDeltaY: 0,
1709
+ handleOffset: 0,
1710
+ scrollBarActive: false,
1711
+ scrollBarHeight: 0
1700
1712
  };
1701
1713
  set(state.uid, state, state);
1702
1714
  return state;
@@ -2968,23 +2980,37 @@ const handleDragOver = (state, x, y) => {
2968
2980
  };
2969
2981
  };
2970
2982
 
2971
- const getTopLevelDirents = (root, pathSeparator, excluded) => {
2972
- if (!root) {
2973
- return [];
2974
- }
2975
- return getChildDirents(pathSeparator, {
2976
- depth: 0,
2977
- path: root,
2978
- type: Directory
2979
- }, excluded);
2983
+ const isExpanded = item => {
2984
+ return item.type === DirectoryExpanded || item.type === DirectoryExpanding;
2980
2985
  };
2981
2986
 
2982
- const mergeDirents$2 = (oldDirents, newDirents) => {
2983
- const merged = [];
2984
- for (const newDirent of newDirents) {
2985
- merged.push(newDirent);
2987
+ const getExpandedDirents = items => {
2988
+ return items.filter(isExpanded);
2989
+ };
2990
+
2991
+ const refreshChildDirent = async (folder, dirent, pathSeparator, expandedFolders) => {
2992
+ const path = folder.path.endsWith(pathSeparator) ? `${folder.path}${dirent.name}` : `${folder.path}${pathSeparator}${dirent.name}`;
2993
+ const isExpandedFolder = expandedFolders.includes(path);
2994
+ const type = dirent.type === 'directory' ? isExpandedFolder ? DirectoryExpanded : Directory : File;
2995
+ const item = {
2996
+ name: dirent.name,
2997
+ type,
2998
+ path,
2999
+ depth: folder.depth + 1,
3000
+ selected: false
3001
+ };
3002
+ if (isExpandedFolder) {
3003
+ const nestedItems = await refreshChildDirents(item, pathSeparator, expandedFolders);
3004
+ return [item, ...nestedItems];
2986
3005
  }
2987
- return merged;
3006
+ return [item];
3007
+ };
3008
+ const refreshChildDirents = async (folder, pathSeparator, expandedFolders) => {
3009
+ const childDirents = await readDirWithFileTypes(folder.path);
3010
+ const childItems = await Promise.all(childDirents.map(async dirent => {
3011
+ return refreshChildDirent(folder, dirent, pathSeparator, expandedFolders);
3012
+ }));
3013
+ return childItems.flat();
2988
3014
  };
2989
3015
 
2990
3016
  // TODO add lots of tests for this
@@ -2995,24 +3021,73 @@ const refresh = async state => {
2995
3021
  minLineY,
2996
3022
  height,
2997
3023
  itemHeight,
2998
- fileIconCache
3024
+ fileIconCache,
3025
+ items,
3026
+ focusedIndex
2999
3027
  } = state;
3000
- const topLevelDirents = await getTopLevelDirents(root, pathSeparator, []);
3001
- const newDirents = mergeDirents$2(state.items, topLevelDirents);
3028
+
3029
+ // Get all expanded folders
3030
+ const expandedDirents = getExpandedDirents(items);
3031
+ const expandedFolders = getPaths(expandedDirents);
3032
+
3033
+ // Get top level dirents
3034
+ const topLevelDirents = await readDirWithFileTypes(root);
3035
+ const newDirents = topLevelDirents.map(dirent => ({
3036
+ name: dirent.name,
3037
+ type: dirent.type === 'directory' ? Directory : File,
3038
+ path: root.endsWith(pathSeparator) ? `${root}${dirent.name}` : `${root}${pathSeparator}${dirent.name}`,
3039
+ depth: 0,
3040
+ selected: false
3041
+ }));
3042
+
3043
+ // Process expanded folders in parallel
3044
+ const expandedFolderResults = await Promise.all(expandedFolders.map(async folderPath => {
3045
+ const folderIndex = newDirents.findIndex(item => item.path === folderPath);
3046
+ if (folderIndex !== -1) {
3047
+ const folder = newDirents[folderIndex];
3048
+ if (folder.type === Directory) {
3049
+ const childItems = await refreshChildDirents(folder, pathSeparator, expandedFolders);
3050
+ // @ts-ignore
3051
+ folder.type = DirectoryExpanded;
3052
+ return {
3053
+ folderIndex,
3054
+ childItems
3055
+ };
3056
+ }
3057
+ }
3058
+ return null;
3059
+ }));
3060
+
3061
+ // Insert child items in the correct order
3062
+ let offset = 0;
3063
+ for (const result of expandedFolderResults) {
3064
+ if (result) {
3065
+ const {
3066
+ folderIndex,
3067
+ childItems
3068
+ } = result;
3069
+ newDirents.splice(folderIndex + 1 + offset, 0, ...childItems);
3070
+ offset += childItems.length;
3071
+ }
3072
+ }
3002
3073
  const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, newDirents.length);
3003
3074
  const visible = newDirents.slice(minLineY, maxLineY);
3004
3075
  const {
3005
3076
  icons,
3006
3077
  newFileIconCache
3007
3078
  } = await getFileIcons(visible, fileIconCache);
3008
- const state3 = {
3079
+ let newFocusedIndex = focusedIndex;
3080
+ if (focusedIndex >= newDirents.length) {
3081
+ newFocusedIndex = newDirents.length - 1;
3082
+ }
3083
+ return {
3009
3084
  ...state,
3010
3085
  items: newDirents,
3011
3086
  fileIconCache: newFileIconCache,
3012
3087
  icons,
3013
- maxLineY
3088
+ maxLineY,
3089
+ focusedIndex: newFocusedIndex
3014
3090
  };
3015
- return state3;
3016
3091
  };
3017
3092
 
3018
3093
  const applyOperation = operation => {
@@ -3109,7 +3184,7 @@ const uploadFileSystemHandles = async (root, pathSeparator, fileSystemHandles) =
3109
3184
  return true;
3110
3185
  };
3111
3186
 
3112
- const mergeDirents$1 = (oldDirents, newDirents) => {
3187
+ const mergeDirents$2 = (oldDirents, newDirents) => {
3113
3188
  return newDirents;
3114
3189
  };
3115
3190
  const getMergedDirents$2 = async (root, pathSeparator, dirents) => {
@@ -3117,7 +3192,7 @@ const getMergedDirents$2 = async (root, pathSeparator, dirents) => {
3117
3192
  path: root,
3118
3193
  depth: 0
3119
3194
  });
3120
- const mergedDirents = mergeDirents$1(dirents, childDirents);
3195
+ const mergedDirents = mergeDirents$2(dirents, childDirents);
3121
3196
  return mergedDirents;
3122
3197
  };
3123
3198
  const handleDrop$2 = async (state, fileHandles, files) => {
@@ -3166,7 +3241,7 @@ const copyFilesElectron = async (root, pathSeparator, fileHandles, files, paths)
3166
3241
  await applyFileOperations(operations);
3167
3242
  };
3168
3243
 
3169
- const mergeDirents = (oldDirents, newDirents) => {
3244
+ const mergeDirents$1 = (oldDirents, newDirents) => {
3170
3245
  return newDirents;
3171
3246
  };
3172
3247
  const getMergedDirents$1 = async (root, pathSeparator, dirents) => {
@@ -3174,7 +3249,7 @@ const getMergedDirents$1 = async (root, pathSeparator, dirents) => {
3174
3249
  path: root,
3175
3250
  depth: 0
3176
3251
  });
3177
- const mergedDirents = mergeDirents(dirents, childDirents);
3252
+ const mergedDirents = mergeDirents$1(dirents, childDirents);
3178
3253
  return mergedDirents;
3179
3254
  };
3180
3255
  const handleDrop$1 = async (state, fileHandles, files, paths) => {
@@ -3427,6 +3502,25 @@ const handleKeyDown = (state, key) => {
3427
3502
  };
3428
3503
  };
3429
3504
 
3505
+ const getTopLevelDirents = (root, pathSeparator, excluded) => {
3506
+ if (!root) {
3507
+ return [];
3508
+ }
3509
+ return getChildDirents(pathSeparator, {
3510
+ depth: 0,
3511
+ path: root,
3512
+ type: Directory
3513
+ }, excluded);
3514
+ };
3515
+
3516
+ const mergeDirents = (oldDirents, newDirents) => {
3517
+ const merged = [];
3518
+ for (const newDirent of newDirents) {
3519
+ merged.push(newDirent);
3520
+ }
3521
+ return merged;
3522
+ };
3523
+
3430
3524
  // TODO add lots of tests for this
3431
3525
  const updateRoot = async state1 => {
3432
3526
  // @ts-ignore
@@ -3441,7 +3535,7 @@ const updateRoot = async state1 => {
3441
3535
  // if (state2.disposed || state2.root !== state1.root) {
3442
3536
  // return state2
3443
3537
  // }
3444
- const newDirents = mergeDirents$2(state1.items, topLevelDirents);
3538
+ const newDirents = mergeDirents(state1.items, topLevelDirents);
3445
3539
  const state3 = {
3446
3540
  ...state1,
3447
3541
  items: newDirents
@@ -3749,10 +3843,11 @@ const loadContent = async (state, savedState) => {
3749
3843
  deltaY = savedState.deltaY;
3750
3844
  }
3751
3845
  const maxLineY = getExplorerMaxLineY(minLineY, height, itemHeight, restoredDirents.length);
3846
+ const visible = restoredDirents.slice(minLineY, maxLineY);
3752
3847
  const {
3753
3848
  icons,
3754
3849
  newFileIconCache
3755
- } = await getFileIcons(restoredDirents, fileIconCache);
3850
+ } = await getFileIcons(visible, fileIconCache);
3756
3851
  return {
3757
3852
  ...state,
3758
3853
  root,
@@ -3929,70 +4024,35 @@ const openContainingFolder = async state => {
3929
4024
  return state;
3930
4025
  };
3931
4026
 
3932
- // TODO support multiselection and removing multiple dirents
3933
- const removeDirent = async state => {
3934
- if (state.focusedIndex < 0) {
3935
- return state;
3936
- }
3937
- const dirent = getFocusedDirent$1(state);
3938
- if (!dirent) {
3939
- return state;
3940
- }
3941
- const absolutePath = dirent.path;
3942
- try {
3943
- // TODO handle error
3944
- await remove(absolutePath);
3945
- } catch {
3946
- // TODO vscode shows error as alert (no stacktrace) and retry button
3947
- // maybe should show alert as well, but where to put stacktrace?
3948
- // on web should probably show notification (dialog)
3949
- // ErrorHandling.handleError(error)
3950
- // await ErrorHandling.showErrorDialog(error)
3951
- return state;
3952
- }
3953
- // TODO avoid state mutation
3954
- // @ts-ignore
3955
- const newVersion = ++state.version;
3956
- // TODO race condition
3957
- // const newState = await loadContent(state:any)
3958
- // @ts-ignore
3959
- if (state.version !== newVersion || state.disposed) {
3960
- return state;
3961
- }
3962
- // TODO is it possible to make this more functional instead of mutating state?
3963
- // maybe every function returns a new state?
3964
- const index = state.items.indexOf(dirent);
3965
- let deleteEnd = index + 1;
3966
- for (; deleteEnd < state.items.length; deleteEnd++) {
3967
- if (state.items[deleteEnd].depth <= dirent.depth) {
3968
- break;
4027
+ const getSelectedItems = (items, focusedIndex) => {
4028
+ const dirent = items[focusedIndex];
4029
+ const selectedItems = items.filter(item => item.selected || item === dirent);
4030
+ return selectedItems;
4031
+ };
4032
+
4033
+ const removePaths = async paths => {
4034
+ for (const item of paths) {
4035
+ try {
4036
+ await remove(item);
4037
+ } catch {
4038
+ // ignore
3969
4039
  }
3970
4040
  }
3971
- const deleteCount = deleteEnd - index;
3972
- const newDirents = [...state.items];
3973
- newDirents.splice(index, deleteCount);
3974
- let indexToFocus = -1;
3975
- if (newDirents.length === 0) {
3976
- indexToFocus = -1;
3977
- } else if (index < state.focusedIndex) {
3978
- indexToFocus = state.focusedIndex - 1;
3979
- } else if (index === state.focusedIndex) {
3980
- indexToFocus = Math.max(state.focusedIndex - 1, 0);
3981
- } else {
3982
- indexToFocus = Math.max(state.focusedIndex - 1, 0);
3983
- }
3984
- const visible = newDirents.slice(state.minLineY, state.maxLineY);
4041
+ };
4042
+
4043
+ const removeDirent = async state => {
3985
4044
  const {
3986
- icons,
3987
- newFileIconCache
3988
- } = await getFileIcons(visible, state.fileIconCache);
3989
- return {
3990
- ...state,
3991
- items: newDirents,
3992
- icons,
3993
- fileIconCache: newFileIconCache,
3994
- focusedIndex: indexToFocus
3995
- };
4045
+ items,
4046
+ focusedIndex
4047
+ } = state;
4048
+ const selectedItems = getSelectedItems(items, focusedIndex);
4049
+ if (selectedItems.length === 0) {
4050
+ return state;
4051
+ }
4052
+ const toRemove = getPaths(selectedItems);
4053
+ await removePaths(toRemove);
4054
+ const newState = await refresh(state);
4055
+ return newState;
3996
4056
  };
3997
4057
 
3998
4058
  const getNewDirentsForRename = (items, focusedIndex) => {
@@ -4083,14 +4143,14 @@ const Chevron = 'Chevron';
4083
4143
  const Empty = '';
4084
4144
  const Explorer = 'Explorer';
4085
4145
  const ExplorerDropTarget = 'DropTarget';
4146
+ const ExplorerInputBox = 'ExplorerInputBox';
4086
4147
  const FileIcon = 'FileIcon';
4087
4148
  const FocusOutline = 'FocusOutline';
4088
- const ListItems = 'ListItems';
4089
4149
  const IconButton = 'IconButton';
4090
4150
  const InputBox = 'InputBox';
4091
- const ExplorerInputBox = 'ExplorerInputBox';
4092
4151
  const InputValidationError = 'InputValidationError';
4093
4152
  const Label = 'Label';
4153
+ const ListItems = 'ListItems';
4094
4154
  const MaskIconChevronDown = 'MaskIconChevronDown';
4095
4155
  const MaskIconChevronRight = 'MaskIconChevronRight';
4096
4156
  const TreeItem = 'TreeItem';
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@lvce-editor/explorer-view",
3
- "version": "2.29.0",
3
+ "version": "2.31.0",
4
4
  "description": "Explorer Worker",
5
5
  "repository": {
6
6
  "type": "git",