@noya-app/noya-file-explorer 0.0.3 → 0.0.4

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@noya-app/noya-file-explorer",
3
- "version": "0.0.3",
3
+ "version": "0.0.4",
4
4
  "main": "./dist/index.js",
5
5
  "module": "./dist/index.mjs",
6
6
  "types": "./dist/index.d.ts",
@@ -20,13 +20,13 @@
20
20
  "dev": "npm run build:main -- --watch"
21
21
  },
22
22
  "dependencies": {
23
- "@noya-app/noya-designsystem": "0.1.51",
24
- "@noya-app/noya-icons": "0.1.7",
25
- "@noya-app/noya-multiplayer-react": "0.1.54",
23
+ "@noya-app/noya-designsystem": "0.1.52",
24
+ "@noya-app/noya-icons": "0.1.8",
25
+ "@noya-app/noya-multiplayer-react": "0.1.55",
26
26
  "@noya-app/noya-keymap": "0.1.3",
27
- "@noya-app/react-utils": "0.1.17",
27
+ "@noya-app/react-utils": "0.1.18",
28
28
  "@noya-app/noya-utils": "0.1.5",
29
- "@noya-app/noya-schemas": "0.1.4",
29
+ "@noya-app/noya-schemas": "0.1.5",
30
30
  "imfs": "^0.1.0",
31
31
  "browser-fs-access": "0.35.0"
32
32
  },
@@ -32,7 +32,7 @@ import {
32
32
  useAssetManager,
33
33
  useAssets,
34
34
  } from "@noya-app/noya-multiplayer-react";
35
- import { MediaItem, MediaMap } from "@noya-app/noya-schemas";
35
+ import { FileMediaItem, MediaItem, MediaMap } from "@noya-app/noya-schemas";
36
36
  import { groupBy, isDeepEqual } from "@noya-app/noya-utils";
37
37
  import {
38
38
  downloadUrl,
@@ -76,18 +76,81 @@ import {
76
76
  validateMediaItemRename,
77
77
  } from "./utils/files";
78
78
 
79
+ const extensionToContentType = {
80
+ svg: "image/svg+xml",
81
+ png: "image/png",
82
+ jpeg: "image/jpeg",
83
+ } as const;
84
+
85
+ function encodeFileContentForThumbnail(
86
+ pathProp: string,
87
+ item: FileMediaItem
88
+ ): { contentType: string; url?: string } | undefined {
89
+ const extension = path.extname(pathProp).slice(1);
90
+ const contentType =
91
+ extensionToContentType[extension as keyof typeof extensionToContentType];
92
+
93
+ if (contentType) {
94
+ if (item.encoding === "base64") {
95
+ return {
96
+ contentType,
97
+ url: `data:${contentType};base64,${item.content}`,
98
+ };
99
+ } else {
100
+ try {
101
+ return {
102
+ contentType,
103
+ url: `data:${contentType},${encodeURIComponent(item.content)}`,
104
+ };
105
+ } catch (error) {
106
+ console.warn("Failed to encode content:", error);
107
+ return { contentType };
108
+ }
109
+ }
110
+ }
111
+
112
+ return undefined;
113
+ }
114
+
79
115
  const MediaThumbnailInternal = memoGeneric(
80
- ({ item, selected, size }: CollectionThumbnailProps<MediaItem>) => {
116
+ ({
117
+ item,
118
+ selected,
119
+ size,
120
+ path: pathProp,
121
+ }: CollectionThumbnailProps<MediaItem> & {
122
+ path?: string;
123
+ }) => {
81
124
  const asset = useAsset(item.kind === "asset" ? item.assetId : undefined);
82
125
  const isRoot = item.id === rootMediaItem.id;
83
126
  const isFolder = item.kind === "folder";
127
+ const isFile = item.kind === "file";
128
+
129
+ let contentType: string | undefined;
130
+ let url: string | undefined;
131
+
132
+ if (asset) {
133
+ contentType = asset.contentType;
134
+ url = asset.url;
135
+ } else if (isFile && pathProp) {
136
+ const encoded = encodeFileContentForThumbnail(pathProp, item);
137
+
138
+ if (encoded) {
139
+ contentType = encoded.contentType;
140
+ url = encoded.url;
141
+ }
142
+ }
143
+
144
+ const fileName = pathProp ? path.basename(pathProp) : undefined;
145
+
84
146
  return (
85
147
  <MediaThumbnail
86
- contentType={asset?.contentType}
148
+ contentType={contentType}
87
149
  iconName={isRoot ? "HomeIcon" : isFolder ? "FolderIcon" : undefined}
88
- url={asset?.url}
150
+ url={url}
89
151
  selected={selected}
90
152
  size={size}
153
+ fileName={fileName}
91
154
  />
92
155
  );
93
156
  }
@@ -122,10 +185,12 @@ type MediaCollectionProps = {
122
185
  ) => void;
123
186
  selectedIds?: string[];
124
187
  media: MediaMap;
125
- setMedia: (
188
+ setMedia?: (
126
189
  metadata: Partial<MultiplayerPatchMetadata>,
127
190
  media: MediaMap
128
191
  ) => void;
192
+ /** @default false */
193
+ readOnly?: boolean;
129
194
  /** @default "list" */
130
195
  viewType?: CollectionViewType;
131
196
  /**
@@ -176,7 +241,8 @@ export const MediaCollection = memo(
176
241
  onSelectionChange,
177
242
  selectedIds: selectedIdsProp,
178
243
  media,
179
- setMedia,
244
+ setMedia: setMediaProp,
245
+ readOnly = false,
180
246
  viewType = "list",
181
247
  fileKindFilter = "all",
182
248
  showRootItem = false,
@@ -198,6 +264,12 @@ export const MediaCollection = memo(
198
264
  },
199
265
  ref
200
266
  ) {
267
+ const setMedia = useCallback(
268
+ (...args: Parameters<Extract<typeof setMediaProp, Function>>) => {
269
+ setMediaProp?.(...args);
270
+ },
271
+ [setMediaProp]
272
+ );
201
273
  const tree = useMemo(() => createMediaItemTree(media), [media]);
202
274
  const [tempItem, setTempItem] = useState<[string, MediaItem] | undefined>(
203
275
  undefined
@@ -655,8 +727,9 @@ export const MediaCollection = memo(
655
727
  variant={viewType === "grid" ? "normal" : "bare"}
656
728
  style={{
657
729
  backgroundColor: selected
658
- ? cssVars.colors.primaryPastel
730
+ ? cssVars.colors.selectedListItemBackground
659
731
  : "transparent",
732
+ color: selected ? cssVars.colors.selectedListItemText : undefined,
660
733
  }}
661
734
  />
662
735
  );
@@ -686,12 +759,14 @@ export const MediaCollection = memo(
686
759
  <FileExplorerLayout
687
760
  title={title ?? rootMediaItemName}
688
761
  right={
689
- <FileExplorerUploadButton
690
- showUploadButton={showUploadButton}
691
- onUpload={() => handleUpload(rootMediaItem.id)}
692
- >
693
- {right}
694
- </FileExplorerUploadButton>
762
+ !readOnly && (
763
+ <FileExplorerUploadButton
764
+ showUploadButton={showUploadButton}
765
+ onUpload={() => handleUpload(rootMediaItem.id)}
766
+ >
767
+ {right}
768
+ </FileExplorerUploadButton>
769
+ )
695
770
  }
696
771
  className={className}
697
772
  >
@@ -733,7 +808,12 @@ export const MediaCollection = memo(
733
808
  onRename={onRename}
734
809
  renamable={renamable}
735
810
  selectedIds={selectedIds}
736
- renderThumbnail={(props) => <MediaThumbnailInternal {...props} />}
811
+ renderThumbnail={(props) => (
812
+ <MediaThumbnailInternal
813
+ {...props}
814
+ path={tree.idToPathMap.get(props.item.id)}
815
+ />
816
+ )}
737
817
  renderAction={renderAction}
738
818
  renderDetail={(file, selected) => {
739
819
  if (file.kind !== "asset") return null;
@@ -767,11 +847,11 @@ export const MediaCollection = memo(
767
847
  if (position !== "inside" || targetItem.kind === "asset") {
768
848
  return false;
769
849
  }
770
- const sourcePath = tree.findIndexPath(
850
+ const sourcePath = tree.findPath(
771
851
  rootMediaItem,
772
852
  (item) => item.id === sourceItem.id
773
853
  );
774
- const targetPath = tree.findIndexPath(
854
+ const targetPath = tree.findPath(
775
855
  rootMediaItem,
776
856
  (item) => item.id === targetItem.id
777
857
  );
@@ -146,7 +146,7 @@ export const moveUpAFolder = ({
146
146
  media: MediaMap;
147
147
  selectedIds: string[];
148
148
  }) => {
149
- const indexPath = tree.findIndexPath(
149
+ const indexPath = tree.findPath(
150
150
  rootMediaItem,
151
151
  (item) => item.id === selectedIds[0]
152
152
  );
@@ -270,7 +270,7 @@ export const moveMediaInsideFolder = ({
270
270
  export const getParentDirectories = (mediaMap: MediaMap, folderId: string) => {
271
271
  const tree = createMediaItemTree(mediaMap);
272
272
 
273
- const indexPath = tree.findIndexPath(
273
+ const indexPath = tree.findPath(
274
274
  rootMediaItem,
275
275
  (item) => item.id === folderId
276
276
  );