-
Notifications
You must be signed in to change notification settings - Fork 50
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
29 changed files
with
859 additions
and
123 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
9 changes: 3 additions & 6 deletions
9
...FolderPage/GridItem/GridItemWithImage.tsx → ...data-browser/src/components/Thumbnail.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
92 changes: 92 additions & 0 deletions
92
browser/data-browser/src/components/forms/FilePicker/FIlePickerItem.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,92 @@ | ||
import { useResource } from '@tomic/react'; | ||
import { styled } from 'styled-components'; | ||
import { ErrorBoundary } from '../../../views/ErrorPage'; | ||
import { FilePreviewThumbnail } from '../../../views/File/FilePreviewThumbnail'; | ||
|
||
interface FilePickerItemProps { | ||
subject: string; | ||
onClick?: () => void; | ||
} | ||
|
||
export function FilePickerItem({ | ||
subject, | ||
onClick, | ||
}: FilePickerItemProps): React.JSX.Element { | ||
const resource = useResource(subject); | ||
|
||
if (resource.loading) { | ||
return <div>loading</div>; | ||
} | ||
|
||
return ( | ||
<ErrorBoundary FallBackComponent={ItemError}> | ||
<ItemWrapper onClick={onClick}> | ||
<ItemCard> | ||
<FilePreviewThumbnail resource={resource} /> | ||
</ItemCard> | ||
<span>{resource.title}</span> | ||
</ItemWrapper> | ||
</ErrorBoundary> | ||
); | ||
} | ||
|
||
const ItemCard = styled.div` | ||
background-color: ${p => p.theme.colors.bg1}; | ||
border-radius: ${p => p.theme.radius}; | ||
overflow: hidden; | ||
box-shadow: var(--shadow), var(--interaction-shadow); | ||
border: 1px solid ${p => p.theme.colors.bg2}; | ||
height: 100%; | ||
width: 100%; | ||
touch-action: none; | ||
pointer-events: none; | ||
user-select: none; | ||
transition: border 0.1s ease-in-out, box-shadow 0.1s ease-in-out; | ||
`; | ||
|
||
const ItemWrapper = styled.button` | ||
appearance: none; | ||
text-align: start; | ||
border: none; | ||
padding: 0; | ||
background-color: transparent; | ||
--shadow: 0px 0.7px 1.3px rgba(0, 0, 0, 0.06), | ||
0px 1.8px 3.2px rgba(0, 0, 0, 0.043), 0px 3.4px 6px rgba(0, 0, 0, 0.036), | ||
0px 6px 10.7px rgba(0, 0, 0, 0.03), 0px 11.3px 20.1px rgba(0, 0, 0, 0.024), | ||
0px 27px 48px rgba(0, 0, 0, 0.017); | ||
--interaction-shadow: 0px 0px 0px 0px ${p => p.theme.colors.main}; | ||
--card-banner-height: 0px; | ||
display: flex; | ||
gap: 0.5rem; | ||
flex-direction: column; | ||
align-items: center; | ||
outline: none; | ||
text-decoration: none; | ||
color: ${p => p.theme.colors.text1}; | ||
width: 100%; | ||
aspect-ratio: 1 / 1; | ||
cursor: pointer; | ||
&:hover ${ItemCard}, &:focus ${ItemCard} { | ||
--interaction-shadow: 0px 0px 0px 1px ${p => p.theme.colors.main}; | ||
border: 1px solid ${p => p.theme.colors.main}; | ||
} | ||
&:hover, | ||
&:focus { | ||
color: ${p => p.theme.colors.main}; | ||
} | ||
`; | ||
|
||
interface ItemErrorProps { | ||
error: Error; | ||
} | ||
|
||
const ItemError: React.FC<ItemErrorProps> = ({ error }) => { | ||
return <ItemErrorWrapper>{error.message}</ItemErrorWrapper>; | ||
}; | ||
|
||
const ItemErrorWrapper = styled.div` | ||
color: ${p => p.theme.colors.alert}; | ||
text-align: center; | ||
`; |
119 changes: 119 additions & 0 deletions
119
browser/data-browser/src/components/forms/FilePicker/FilePicker.tsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,119 @@ | ||
import { useEffect, useState } from 'react'; | ||
import { Button } from '../../Button'; | ||
import { FilePickerDialog } from './FilePickerDialog'; | ||
import { SelectedFileBlob, SelectedFileResource } from './SelectedFile'; | ||
import { InputProps } from '../ResourceField'; | ||
import { FaFileCirclePlus } from 'react-icons/fa6'; | ||
import { StoreEvents, useStore, useSubject } from '@tomic/react'; | ||
import { useUpload } from '../../../hooks/useUpload'; | ||
import { VisuallyHidden } from '../../VisuallyHidden'; | ||
import { styled } from 'styled-components'; | ||
|
||
/** | ||
* Button that opens a dialog that lists all files in the drive and allows the user to upload a new file. | ||
* Handles uploads and makes sure files are uploaded even when the parent resource is not saved yet. | ||
*/ | ||
export function FilePicker({ | ||
resource, | ||
property, | ||
disabled, | ||
required, | ||
commit, | ||
}: InputProps): React.JSX.Element { | ||
const store = useStore(); | ||
const { upload } = useUpload(resource); | ||
const [value, setValue] = useSubject(resource, property.subject, { | ||
validate: false, | ||
commit: commit, | ||
}); | ||
const [show, setShow] = useState(false); | ||
const [selectedSubject, setSelectedSubject] = useState<string | undefined>( | ||
value, | ||
); | ||
const [selectedFile, setSelectedFile] = useState<File | undefined>(); | ||
|
||
const [unsubScheduledUpload, setUnsubScheduledUpload] = | ||
useState<() => void | undefined>(); | ||
|
||
useEffect(() => { | ||
if (selectedSubject) { | ||
setValue(selectedSubject); | ||
} else if (selectedFile) { | ||
if (resource.new) { | ||
// We can't upload the file yet because its parent has not saved yet so we set the value to a placeholder and then schedule an upload when the resource is saved. | ||
setValue('https://placeholder'); | ||
setUnsubScheduledUpload(prevUnsub => { | ||
prevUnsub?.(); | ||
|
||
const thisUnsub = store.on( | ||
StoreEvents.ResourceSaved, | ||
async savedResource => { | ||
if (savedResource.getSubject() === resource.getSubject()) { | ||
thisUnsub(); | ||
const [subject] = await upload([selectedFile]); | ||
await setValue(subject); | ||
resource.save(store); | ||
} | ||
}, | ||
); | ||
|
||
return thisUnsub; | ||
}); | ||
} else { | ||
upload([selectedFile]).then(([subject]) => { | ||
setValue(subject); | ||
}); | ||
} | ||
} else { | ||
setValue(undefined); | ||
} | ||
}, [selectedSubject, selectedFile]); | ||
|
||
return ( | ||
<Wrapper> | ||
<VisuallyHidden> | ||
{value} | ||
<input | ||
aria-hidden | ||
type='text' | ||
value={value ?? ''} | ||
required={required} | ||
disabled={disabled} | ||
/> | ||
</VisuallyHidden> | ||
{!selectedFile && !selectedSubject && ( | ||
<Button subtle onClick={() => setShow(true)} disabled={disabled}> | ||
<FaFileCirclePlus /> | ||
Select File | ||
</Button> | ||
)} | ||
{selectedSubject && ( | ||
<SelectedFileResource | ||
disabled={disabled} | ||
subject={selectedSubject} | ||
onClear={() => setSelectedSubject(undefined)} | ||
/> | ||
)} | ||
{selectedFile && ( | ||
<SelectedFileBlob | ||
file={selectedFile} | ||
disabled={disabled} | ||
onClear={() => { | ||
setSelectedFile(undefined); | ||
unsubScheduledUpload?.(); | ||
}} | ||
/> | ||
)} | ||
<FilePickerDialog | ||
show={show} | ||
onShowChange={setShow} | ||
onResourcePicked={setSelectedSubject} | ||
onNewFilePicked={setSelectedFile} | ||
/> | ||
</Wrapper> | ||
); | ||
} | ||
|
||
const Wrapper = styled.div` | ||
position: relative; | ||
`; |
Oops, something went wrong.