@noya-app/noya-file-explorer 0.0.3 → 0.0.5
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/.turbo/turbo-build.log +13 -13
- package/CHANGELOG.md +20 -0
- package/dist/index.css +618 -573
- package/dist/index.css.map +1 -1
- package/dist/index.d.mts +25342 -13464
- package/dist/index.d.ts +25342 -13464
- package/dist/index.js +371 -115
- package/dist/index.js.map +1 -1
- package/dist/index.mjs +371 -115
- package/dist/index.mjs.map +1 -1
- package/package.json +6 -6
- package/src/MediaCollection.tsx +96 -16
- package/src/utils/files.ts +2 -2
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@noya-app/noya-file-explorer",
|
|
3
|
-
"version": "0.0.
|
|
3
|
+
"version": "0.0.5",
|
|
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.
|
|
24
|
-
"@noya-app/noya-icons": "0.1.
|
|
25
|
-
"@noya-app/noya-multiplayer-react": "0.1.
|
|
23
|
+
"@noya-app/noya-designsystem": "0.1.53",
|
|
24
|
+
"@noya-app/noya-icons": "0.1.8",
|
|
25
|
+
"@noya-app/noya-multiplayer-react": "0.1.56",
|
|
26
26
|
"@noya-app/noya-keymap": "0.1.3",
|
|
27
|
-
"@noya-app/react-utils": "0.1.
|
|
27
|
+
"@noya-app/react-utils": "0.1.18",
|
|
28
28
|
"@noya-app/noya-utils": "0.1.5",
|
|
29
|
-
"@noya-app/noya-schemas": "0.1.
|
|
29
|
+
"@noya-app/noya-schemas": "0.1.5",
|
|
30
30
|
"imfs": "^0.1.0",
|
|
31
31
|
"browser-fs-access": "0.35.0"
|
|
32
32
|
},
|
package/src/MediaCollection.tsx
CHANGED
|
@@ -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
|
-
({
|
|
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={
|
|
148
|
+
contentType={contentType}
|
|
87
149
|
iconName={isRoot ? "HomeIcon" : isFolder ? "FolderIcon" : undefined}
|
|
88
|
-
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.
|
|
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
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
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) =>
|
|
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.
|
|
850
|
+
const sourcePath = tree.findPath(
|
|
771
851
|
rootMediaItem,
|
|
772
852
|
(item) => item.id === sourceItem.id
|
|
773
853
|
);
|
|
774
|
-
const targetPath = tree.
|
|
854
|
+
const targetPath = tree.findPath(
|
|
775
855
|
rootMediaItem,
|
|
776
856
|
(item) => item.id === targetItem.id
|
|
777
857
|
);
|
package/src/utils/files.ts
CHANGED
|
@@ -146,7 +146,7 @@ export const moveUpAFolder = ({
|
|
|
146
146
|
media: MediaMap;
|
|
147
147
|
selectedIds: string[];
|
|
148
148
|
}) => {
|
|
149
|
-
const indexPath = tree.
|
|
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.
|
|
273
|
+
const indexPath = tree.findPath(
|
|
274
274
|
rootMediaItem,
|
|
275
275
|
(item) => item.id === folderId
|
|
276
276
|
);
|