|
1 | | -import { EntitySubTree } from '@contember/react-binding' |
| 1 | +import { EntitySubTree, StaticRender, useEntity } from '@contember/react-binding' |
2 | 2 | import * as React from 'react' |
| 3 | +import { useState } from 'react' |
3 | 4 | import { Binding, PersistButton } from '@app/lib/binding' |
4 | 5 | import { Slots } from '@app/lib/layout' |
5 | 6 | import { AudioField, FileField, ImageField, ImageRepeaterField, VideoField } from '@app/lib/form' |
| 7 | +import { Dialog, DialogContent, DialogTrigger } from '@app/lib/ui/dialog' |
| 8 | +import { Button } from '@app/lib/ui/button' |
| 9 | +import { DataGrid, DataGridColumn, DataGridLoader, DataGridPagination, DataGridTable, DataGridTextColumn, DataGridTiles, DataGridToolbar } from '@app/lib/datagrid' |
| 10 | +import { UploadedImageView, UploaderDropzoneAreaUI } from '@app/lib/upload' |
| 11 | +import { UseEntity } from '@app/app/components/UseEntity' |
| 12 | +import { EntityAccessor, Field } from '@contember/interface' |
| 13 | +import { FileUrlDataExtractorProps, GenericFileMetadataExtractorProps, ImageFileDataExtractorProps } from '@contember/react-uploader' |
| 14 | +import { UploadIcon } from 'lucide-react' |
| 15 | +import { dict } from '@app/lib/dict' |
6 | 16 |
|
7 | 17 |
|
| 18 | +const imageFields: FileUrlDataExtractorProps & GenericFileMetadataExtractorProps & ImageFileDataExtractorProps = { |
| 19 | + urlField: 'url', |
| 20 | + widthField: 'width', |
| 21 | + heightField: 'height', |
| 22 | + fileNameField: 'meta.fileName', |
| 23 | + fileSizeField: 'meta.fileSize', |
| 24 | + fileTypeField: 'meta.fileType', |
| 25 | + lastModifiedField: 'meta.lastModified', |
| 26 | +} |
| 27 | + |
| 28 | +const SelectImage = () => { |
| 29 | + const entity = useEntity() |
| 30 | + |
| 31 | + return <SelectImageInner connect={it => { |
| 32 | + entity.connectEntityAtField('image', it) |
| 33 | + }} closeOnSelect /> |
| 34 | +} |
| 35 | + |
| 36 | +const SelectImageRepeater = () => { |
| 37 | + const entity = useEntity() |
| 38 | + |
| 39 | + return <SelectImageInner connect={it => { |
| 40 | + entity.getEntityList('imageList.items').createNewEntity(entity => { |
| 41 | + entity().connectEntityAtField('image', it) |
| 42 | + }) |
| 43 | + }} /> |
| 44 | +} |
| 45 | + |
| 46 | +const SelectImageInner = ({ connect, closeOnSelect }: { connect: (entity: EntityAccessor) => void, closeOnSelect?: boolean }) => { |
| 47 | + const [open, setOpen] = useState(false) |
| 48 | + return ( |
| 49 | + <Dialog open={open} onOpenChange={setOpen}> |
| 50 | + <DialogTrigger asChild> |
| 51 | + <Button size="sm" variant="outline" onClick={e => e.stopPropagation()}>Select image</Button> |
| 52 | + </DialogTrigger> |
| 53 | + <DialogContent className="max-w-[90vw]"> |
| 54 | + <DataGrid entities="UploadImage"> |
| 55 | + <DataGridToolbar></DataGridToolbar> |
| 56 | + <DataGridLoader> |
| 57 | + <DataGridTiles className="md:grid-cols-[repeat(auto-fill,minmax(10rem,1fr))]"> |
| 58 | + <UseEntity render={it => ( |
| 59 | + <div className="relative border rounded shadow hover:shadow-md hover:border-yellow-500" onClick={() => { |
| 60 | + it && connect(it) |
| 61 | + closeOnSelect && setOpen(false) |
| 62 | + }}> |
| 63 | + <UploadedImageView {...imageFields} /> |
| 64 | + </div> |
| 65 | + )} /> |
| 66 | + </DataGridTiles> |
| 67 | + <DataGridTable> |
| 68 | + <DataGridColumn headerClassName="w-0"> |
| 69 | + <UseEntity render={it => (<Button onClick={() => { |
| 70 | + it && connect(it) |
| 71 | + closeOnSelect && setOpen(false) |
| 72 | + }}>Select</Button>)} /> |
| 73 | + </DataGridColumn> |
| 74 | + <DataGridTextColumn field="url" header="URL"> |
| 75 | + <Field field="url" /> |
| 76 | + <StaticRender> |
| 77 | + <ImageField {...imageFields} /> |
| 78 | + </StaticRender> |
| 79 | + </DataGridTextColumn> |
| 80 | + </DataGridTable> |
| 81 | + </DataGridLoader> |
| 82 | + <DataGridPagination /> |
| 83 | + </DataGrid> |
| 84 | + </DialogContent> |
| 85 | + </Dialog> |
| 86 | + ) |
| 87 | +} |
| 88 | + |
8 | 89 | export const image = () => <> |
9 | 90 |
|
10 | 91 | <Binding> |
11 | 92 | <Slots.Actions><PersistButton /></Slots.Actions> |
12 | 93 | <EntitySubTree entity="UploadRoot(unique = unique)" setOnCreate="(unique = unique)"> |
13 | 94 | <ImageField |
14 | 95 | baseField="image" |
15 | | - urlField="url" |
16 | | - widthField="width" |
17 | | - heightField="height" |
18 | | - fileNameField="meta.fileName" |
19 | | - fileSizeField="meta.fileSize" |
20 | | - fileTypeField="meta.fileType" |
21 | | - lastModifiedField="meta.lastModified" |
| 96 | + {...imageFields} |
22 | 97 | label="Image file" |
23 | 98 | description="Some description of the image file." |
| 99 | + dropzonePlaceholder={( |
| 100 | + <UploaderDropzoneAreaUI className="w-60"> |
| 101 | + <UploadIcon className={'w-12 h-12 text-gray-400'} /> |
| 102 | + <div className={'font-semibold text-sm'}>{dict.uploader.dropFiles}</div> |
| 103 | + <div className={'text-xs'}>{dict.uploader.or}</div> |
| 104 | + <div className={'flex gap-2 items-center text-xs'}> |
| 105 | + <Button size={'sm'} variant={'outline'}>{dict.uploader.browseFiles}</Button> |
| 106 | + <div onClick={e => e.stopPropagation()}> |
| 107 | + <SelectImage /> |
| 108 | + </div> |
| 109 | + </div> |
| 110 | + </UploaderDropzoneAreaUI> |
| 111 | + )} |
24 | 112 | /> |
25 | 113 | </EntitySubTree> |
26 | 114 | </Binding> |
27 | 115 | </> |
28 | 116 |
|
| 117 | + |
29 | 118 | export const imageTrivial = () => <> |
30 | 119 |
|
31 | 120 | <Binding> |
@@ -112,15 +201,22 @@ export const imageList = () => <> |
112 | 201 | field="imageList.items" |
113 | 202 | baseField="image" |
114 | 203 | sortableBy="order" |
115 | | - urlField="url" |
116 | | - widthField="width" |
117 | | - heightField="height" |
118 | | - fileNameField="meta.fileName" |
119 | | - fileSizeField="meta.fileSize" |
120 | | - fileTypeField="meta.fileType" |
121 | | - lastModifiedField="meta.lastModified" |
| 204 | + {...imageFields} |
122 | 205 | label="Image file" |
123 | 206 | description="Some description of the image file." |
| 207 | + dropzonePlaceholder={( |
| 208 | + <UploaderDropzoneAreaUI className="w-60"> |
| 209 | + <UploadIcon className={'w-12 h-12 text-gray-400'} /> |
| 210 | + <div className={'font-semibold text-sm'}>{dict.uploader.dropFiles}</div> |
| 211 | + <div className={'text-xs'}>{dict.uploader.or}</div> |
| 212 | + <div className={'flex gap-2 items-center text-xs'}> |
| 213 | + <Button size={'sm'} variant={'outline'}>{dict.uploader.browseFiles}</Button> |
| 214 | + <div onClick={e => e.stopPropagation()}> |
| 215 | + <SelectImageRepeater /> |
| 216 | + </div> |
| 217 | + </div> |
| 218 | + </UploaderDropzoneAreaUI> |
| 219 | + )} |
124 | 220 | /> |
125 | 221 | </EntitySubTree> |
126 | 222 | </Binding> |
|
0 commit comments