added "File" Prop Editor to studio project

This commit is contained in:
João Leite 2023-03-13 16:01:54 +00:00 committed by Aria Minaei
parent 91b0ec5cfa
commit a7325aebc9
4 changed files with 161 additions and 5 deletions

View file

@ -0,0 +1,154 @@
import type {PropTypeConfig_File} from '@theatre/core/propTypes'
import {Package, Trash} from '@theatre/studio/uiComponents/icons'
import React, {useCallback, useEffect} from 'react'
import styled, {css} from 'styled-components'
import type {ISimplePropEditorReactProps} from './ISimplePropEditorReactProps'
const Container = styled.div<{empty: boolean}>`
display: flex;
align-items: center;
height: 100%;
gap: 4px;
`
const AddFile = styled.div`
position: absolute;
inset: -5px;
// rotate 45deg
transform: rotate(45deg);
--checker-color: #ededed36;
&:hover {
--checker-color: #ededed77;
}
// checkerboard background with 4px squares
background-image: linear-gradient(
45deg,
var(--checker-color) 25%,
transparent 25%
),
linear-gradient(-45deg, var(--checker-color) 25%, transparent 25%),
linear-gradient(45deg, transparent 75%, var(--checker-color) 75%),
linear-gradient(-45deg, transparent 75%, var(--checker-color) 75%);
background-size: 5px 5px;
`
const InputLabel = styled.label<{empty: boolean}>`
position: relative;
cursor: default;
box-sizing: border-box;
height: 18px;
aspect-ratio: 1;
display: flex;
justify-content: center;
align-items: center;
font-size: 16px;
overflow: hidden;
color: #ccc;
&:hover {
color: white;
}
border-radius: 99999px;
border: 1px solid hwb(220deg 40% 52%);
&:hover {
border-color: hwb(220deg 45% 52%);
}
${(props) => (props.empty ? css`` : css``)}
`
// file input
const Input = styled.input.attrs({type: 'file'})`
display: none;
`
const DeleteButton = styled.button`
display: flex;
align-items: center;
justify-content: center;
outline: none;
background: transparent;
color: #a8a8a9;
border: none;
height: 100%;
aspect-ratio: 1/1;
opacity: 0;
${Container}:hover & {
opacity: 0.8;
}
&:hover {
opacity: 1;
color: white;
}
`
function FilePropEditor({
propConfig,
editingTools,
value,
autoFocus,
}: ISimplePropEditorReactProps<PropTypeConfig_File>) {
const [previewUrl, setPreviewUrl] = React.useState<string>()
useEffect(() => {
if (value) {
setPreviewUrl(editingTools.getAssetUrl(value))
} else {
setPreviewUrl(undefined)
}
}, [value])
const onChange = useCallback(
async (event) => {
const file = event.target.files[0]
editingTools.permanentlySetValue({type: 'file', id: undefined})
const fileId = await editingTools.createAsset(file)
if (!fileId) {
editingTools.permanentlySetValue(value)
} else {
editingTools.permanentlySetValue({
type: 'file',
id: fileId,
})
}
event.target.value = null
},
[editingTools, value],
)
const empty = !value?.id
return (
<Container empty={empty}>
<InputLabel
empty={empty}
title={
empty ? 'Upload file' : `"${value.id}" (Click to upload new file)`
}
>
<Input type="file" onChange={onChange} autoFocus={autoFocus} />
{previewUrl ? <Package /> : <AddFile />}
</InputLabel>
{!empty && (
<DeleteButton
title="Delete file"
onClick={() => {
editingTools.permanentlySetValue({type: 'file', id: undefined})
}}
>
<Trash />
</DeleteButton>
)}
</Container>
)
}
export default FilePropEditor

View file

@ -8,6 +8,7 @@ import RgbaPropEditor from './RgbaPropEditor'
import type {ISimplePropEditorReactProps} from './ISimplePropEditorReactProps' import type {ISimplePropEditorReactProps} from './ISimplePropEditorReactProps'
import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType' import type {PropConfigForType} from '@theatre/studio/propEditors/utils/PropConfigForType'
import ImagePropEditor from './ImagePropEditor' import ImagePropEditor from './ImagePropEditor'
import FilePropEditor from './FilePropEditor'
export const simplePropEditorByPropType: ISimplePropEditorByPropType = { export const simplePropEditorByPropType: ISimplePropEditorByPropType = {
number: NumberPropEditor, number: NumberPropEditor,
@ -16,6 +17,7 @@ export const simplePropEditorByPropType: ISimplePropEditorByPropType = {
stringLiteral: StringLiteralPropEditor, stringLiteral: StringLiteralPropEditor,
rgba: RgbaPropEditor, rgba: RgbaPropEditor,
image: ImagePropEditor, image: ImagePropEditor,
file: FilePropEditor,
} }
type ISimplePropEditorByPropType = { type ISimplePropEditorByPropType = {

View file

@ -20,7 +20,7 @@ import type {NearbyKeyframes} from './getNearbyKeyframesOfTrack'
import {getNearbyKeyframesOfTrack} from './getNearbyKeyframesOfTrack' import {getNearbyKeyframesOfTrack} from './getNearbyKeyframesOfTrack'
import type {NearbyKeyframesControls} from './NextPrevKeyframeCursors' import type {NearbyKeyframesControls} from './NextPrevKeyframeCursors'
import NextPrevKeyframeCursors from './NextPrevKeyframeCursors' import NextPrevKeyframeCursors from './NextPrevKeyframeCursors'
import type {Asset} from '@theatre/shared/utils/assets' import type {Asset, File as AssetFile} from '@theatre/shared/utils/assets'
interface EditingToolsCommon<T> { interface EditingToolsCommon<T> {
value: T value: T
@ -33,7 +33,7 @@ interface EditingToolsCommon<T> {
discardTemporaryValue(): void discardTemporaryValue(): void
permanentlySetValue(v: T): void permanentlySetValue(v: T): void
getAssetUrl: (asset: Asset) => string | undefined getAssetUrl: (asset: Asset | AssetFile) => string | undefined
createAsset(asset: File): Promise<string | null> createAsset(asset: File): Promise<string | null>
} }
@ -116,7 +116,7 @@ function createPrism<T extends SerializablePrimitive>(
const editAssets = { const editAssets = {
createAsset: (asset: File): Promise<string | null> => createAsset: (asset: File): Promise<string | null> =>
obj.sheet.project.assetStorage.createAsset(asset), obj.sheet.project.assetStorage.createAsset(asset),
getAssetUrl: (asset: Asset) => getAssetUrl: (asset: Asset | AssetFile) =>
asset.id asset.id
? obj.sheet.project.assetStorage.getAssetUrl(asset.id) ? obj.sheet.project.assetStorage.getAssetUrl(asset.id)
: undefined, : undefined,

View file

@ -1,10 +1,10 @@
import type {Asset} from '@theatre/shared/utils/assets' import type {Asset, File} from '@theatre/shared/utils/assets'
export interface IEditingTools<T> { export interface IEditingTools<T> {
temporarilySetValue(v: T): void temporarilySetValue(v: T): void
discardTemporaryValue(): void discardTemporaryValue(): void
permanentlySetValue(v: T): void permanentlySetValue(v: T): void
getAssetUrl(asset: Asset): string | undefined getAssetUrl(asset: Asset | File): string | undefined
createAsset(asset: Blob): Promise<string | null> createAsset(asset: Blob): Promise<string | null>
} }