Skip to content

Commit 1cfaee0

Browse files
committed
add initial modal for import asset
1 parent 03ab674 commit 1cfaee0

File tree

5 files changed

+96
-10
lines changed

5 files changed

+96
-10
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
{
2+
"typescript.tsdk": "node_modules/typescript/lib"
3+
}

packages/@dcl/inspector/src/components/FileInput/FileInput.tsx

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ export interface InputRef {
1616
}
1717

1818
export const FileInput = React.forwardRef<InputRef, React.PropsWithChildren<PropTypes>>((props, parentRef) => {
19-
const { onDrop } = props
19+
const { disabled, onDrop } = props
2020
const acceptExtensions = Object.values(props.accept ?? []).flat()
2121
const inputRef = useRef<HTMLInputElement>(null)
2222

@@ -27,7 +27,7 @@ export const FileInput = React.forwardRef<InputRef, React.PropsWithChildren<Prop
2727
if (onDrop) onDrop(item.files)
2828
},
2929
canDrop(item: { files: File[] }) {
30-
return item.files.every((file) => !!acceptExtensions.find((ext) => file.name.endsWith(ext)))
30+
return !disabled && item.files.every((file) => !!acceptExtensions.find((ext) => file.name.endsWith(ext)))
3131
},
3232
collect: (monitor) => ({
3333
isHover: monitor.canDrop() && monitor.isOver()
@@ -60,7 +60,7 @@ export const FileInput = React.forwardRef<InputRef, React.PropsWithChildren<Prop
6060
return (
6161
<div ref={drop}>
6262
<input
63-
disabled={props.disabled}
63+
disabled={disabled}
6464
ref={inputRef}
6565
accept={parseAccept(props.accept)}
6666
type="file"

packages/@dcl/inspector/src/components/ImportAsset/ImportAsset.tsx

Lines changed: 58 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -16,12 +16,14 @@ import { TextField } from '../ui/TextField'
1616
import { Block } from '../Block'
1717
import { Button } from '../Button'
1818
import { AssetPreview } from '../AssetPreview'
19+
import { Modal } from '../Modal'
20+
import { Input } from '../Input'
21+
import { InputRef } from '../FileInput/FileInput'
1922

20-
import { processAssets } from './utils'
23+
import { formatFileName, processAssets, getAssetSize, getAssetResources } from './utils'
2124
import { Asset } from './types'
2225

2326
import './ImportAsset.css'
24-
import { InputRef } from '../FileInput/FileInput'
2527

2628
const ACCEPTED_FILE_TYPES = {
2729
'model/gltf-binary': ['.gltf', '.glb', '.bin'],
@@ -48,6 +50,7 @@ const ImportAsset = React.forwardRef<InputRef, React.PropsWithChildren<PropTypes
4850
const uploadFile = useAppSelector(selectUploadFile)
4951

5052
const [files, setFiles] = useState<Asset[]>([])
53+
const [screenshots, setScreenshots] = useState<Map<string, string>>(new Map())
5154
const [isHover, setIsHover] = useState(false)
5255
const { basePath, assets } = catalog ?? { basePath: '', assets: [] }
5356

@@ -68,10 +71,20 @@ const ImportAsset = React.forwardRef<InputRef, React.PropsWithChildren<PropTypes
6871
setIsHover(isHover)
6972
}, [])
7073

71-
function removeAsset(asset: Asset) {
74+
const removeAsset = useCallback((asset: Asset) => {
7275
// e.stopPropagation()
7376
setFiles(files.filter((file) => file.name !== asset.name))
74-
}
77+
}, [])
78+
79+
const handleCloseModal = useCallback(() => {
80+
setFiles([])
81+
setScreenshots(new Map())
82+
}, [])
83+
84+
const handleScreenshot = useCallback((file: Asset) => (thumbnail: string) => {
85+
const map = screenshots.set(formatFileName(file), thumbnail)
86+
setScreenshots(new Map(map))
87+
}, [])
7588

7689
return (
7790
<div className={cx("ImportAsset", { ImportAssetHover: isHover })}>
@@ -83,7 +96,47 @@ const ImportAsset = React.forwardRef<InputRef, React.PropsWithChildren<PropTypes
8396
</div>
8497
<span className="text">Drop {ACCEPTED_FILE_TYPES_STR} files</span>
8598
</>
86-
) : children}
99+
) : (
100+
<>
101+
{files.map(($, i) => {
102+
const resources = getAssetResources($)
103+
return (
104+
<div key={i} style={{ display: 'none' }}>
105+
<AssetPreview value={$.blob} resources={resources} onScreenshot={handleScreenshot($)} />
106+
</div>
107+
)
108+
})}
109+
{children}
110+
</>
111+
)}
112+
<Modal
113+
isOpen={!!files.length}
114+
onRequestClose={handleCloseModal}
115+
className="ImportAssetModal"
116+
overlayClassName="ImportAssetModalOverlay"
117+
>
118+
<h2>Import Assets</h2>
119+
<div className="slider">
120+
{files.length > 1 && <span className="counter">{files.length}</span>}
121+
<div className="content">
122+
{files.length > 1 && <span className="left"></span>}
123+
<div className="slides">
124+
{files.map(($, i) => {
125+
const name = formatFileName($)
126+
return (
127+
<div className="asset" key={i}>
128+
<img className="thumbnail" src={screenshots.get(name)} />
129+
<Input value={name} />
130+
<span className="size">{getAssetSize($)}</span>
131+
</div>
132+
)
133+
})}
134+
</div>
135+
{files.length > 1 && <span className="right"></span>}
136+
</div>
137+
</div>
138+
<Button type="danger" size="big">IMPORT ALL</Button>
139+
</Modal>
87140
</FileInput>
88141
</div>
89142
)

packages/@dcl/inspector/src/components/ImportAsset/types.ts

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,3 +26,8 @@ export type BabylonValidationIssue = {
2626
message: string
2727
pointer: string
2828
}
29+
30+
export const isGltfAsset = (asset: Asset): asset is GltfAsset => {
31+
const _asset = asset as any
32+
return _asset.buffers && _asset.images
33+
}

packages/@dcl/inspector/src/components/ImportAsset/utils.ts

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
import { GLTFValidation } from '@babylonjs/loaders'
22

3-
import { FileAsset, GltfAsset, BabylonValidationIssue, ValidationError, Asset, Uri, GltfFile } from './types'
3+
import { FileAsset, GltfAsset, BabylonValidationIssue, ValidationError, Asset, Uri, GltfFile, isGltfAsset } from './types'
44

55
const sampleIndex = (list: any[]) => Math.floor(Math.random() * list.length)
66

@@ -148,7 +148,7 @@ function extractFileInfo(fileName: string): [string, string] {
148148
return match ? [match[1], match[2]?.toLowerCase() || ""] : [fileName, ""]
149149
}
150150

151-
function formatFileName(file: FileAsset): string {
151+
export function formatFileName(file: FileAsset): string {
152152
return `${file.name}.${file.extension}`
153153
}
154154

@@ -225,3 +225,28 @@ export async function processAssets(files: File[]): Promise<Asset[]> {
225225
const processedFiles = await Promise.all(files.map(processFile))
226226
return processGltfAssets(processedFiles)
227227
}
228+
229+
export function normalizeBytes(bytes: number): string {
230+
const units = ['B', 'KB', 'MB', 'GB', 'TB', 'PB'];
231+
let value = bytes;
232+
let unitIndex = 0;
233+
234+
while (value >= 1024 && unitIndex < units.length - 1) {
235+
value /= 1024;
236+
unitIndex++;
237+
}
238+
239+
const roundedValue = Math.round(value * 100) / 100;
240+
return `${roundedValue} ${units[unitIndex]}`;
241+
}
242+
243+
export function getAssetSize(asset: Asset): string {
244+
const resources = getAssetResources(asset)
245+
const sumSize = resources.reduce((size, resource) => size + resource.size, asset.blob.size)
246+
return normalizeBytes(sumSize)
247+
}
248+
249+
export function getAssetResources(asset: Asset): File[] {
250+
if (!isGltfAsset(asset)) return []
251+
return [...asset.buffers, ...asset.images].map(($) => $.blob)
252+
}

0 commit comments

Comments
 (0)