Skip to content

Commit 53e47ed

Browse files
authored
Merge pull request #732 from contember/feat/ui-lib-up
update some ui components
2 parents 979ec6a + cfe2103 commit 53e47ed

File tree

14 files changed

+279
-97
lines changed

14 files changed

+279
-97
lines changed

build/api/react-ui-lib.api.md

Lines changed: 16 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -223,10 +223,14 @@ export const baseEditorPlugins: {
223223
};
224224

225225
// @public (undocumented)
226-
export type BaseFieldProps = Omit<FormContainerProps, 'children'> & UploaderBaseFieldProps;
226+
export type BaseFieldProps = Omit<FormContainerProps, 'children'> & UploaderBaseFieldProps & {
227+
dropzonePlaceholder?: ReactNode;
228+
};
227229

228230
// @public (undocumented)
229-
export type BaseFileRepeaterFieldProps = Omit<FormContainerProps, 'children'> & RepeaterProps & UploaderBaseFieldProps;
231+
export type BaseFileRepeaterFieldProps = Omit<FormContainerProps, 'children'> & RepeaterProps & UploaderBaseFieldProps & {
232+
dropzonePlaceholder?: ReactNode;
233+
};
230234

231235
// @public (undocumented)
232236
export const Binding: ({ children }: {
@@ -633,6 +637,8 @@ export type DataGridColumnProps = {
633637
name?: string;
634638
hidingName?: string;
635639
sortingField?: string;
640+
cellClassName?: string;
641+
headerClassName?: string;
636642
};
637643

638644
// @public (undocumented)
@@ -908,6 +914,8 @@ export const DataGridTiles: React_2.NamedExoticComponent<DataGridTilesProps>;
908914
export interface DataGridTilesProps {
909915
// (undocumented)
910916
children: React_2.ReactNode;
917+
// (undocumented)
918+
className?: string;
911919
}
912920

913921
// @public (undocumented)
@@ -994,7 +1002,8 @@ export const DefaultRepeater: NamedExoticComponent<DefaultRepeaterProps>;
9941002

9951003
// @public (undocumented)
9961004
export type DefaultRepeaterProps = {
997-
title?: string;
1005+
title?: ReactNode;
1006+
addButtonPosition?: 'none' | 'after' | 'before' | 'around';
9981007
} & RepeaterProps;
9991008

10001009
// @public (undocumented)
@@ -1793,8 +1802,9 @@ className?: string | undefined;
17931802
}, "ref"> & RefAttributes<HTMLInputElement>>;
17941803

17951804
// @public (undocumented)
1796-
export const RepeaterAddItemButton: ({ children }: {
1805+
export const RepeaterAddItemButton: ({ children, index }: {
17971806
children?: React.ReactNode;
1807+
index?: RepeaterAddItemIndex;
17981808
}) => JSX_2.Element;
17991809

18001810
// @public (undocumented)
@@ -2473,8 +2483,9 @@ export type UploadedVideoViewProps = FileUrlDataExtractorProps & GenericFileMeta
24732483
};
24742484

24752485
// @public (undocumented)
2476-
export const UploaderDropzone: ({ inactiveOnUpload }: {
2486+
export const UploaderDropzone: ({ inactiveOnUpload, dropzonePlaceholder }: {
24772487
inactiveOnUpload?: boolean;
2488+
dropzonePlaceholder?: ReactNode;
24782489
}) => JSX_2.Element;
24792490

24802491
// @public (undocumented)
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
import { Component, EntityAccessor, useEntity } from '@contember/interface'
2+
import React from 'react'
3+
4+
export const UseEntity = Component<{ render: (accessor?: EntityAccessor) => React.ReactNode }>(({ render }) => {
5+
const entity = useEntity()
6+
return <>{render(entity)}</>
7+
},
8+
({ render }) => {
9+
return <>
10+
{render()}
11+
</>
12+
})

packages/playground/admin/app/pages/repeater.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ const repeaterDropdown = (
2424
export default <>
2525
<Binding>
2626
<Slots.Actions><PersistButton /></Slots.Actions>
27-
<DefaultRepeater entities={'RepeaterItem'} sortableBy={'order'} title="Foo items">
27+
<DefaultRepeater entities={'RepeaterItem'} sortableBy={'order'} title="Foo items" addButtonPosition="around">
2828
<Field field={'title'} />
2929

3030
<RepeaterItemActions>

packages/playground/admin/app/pages/upload.tsx

Lines changed: 111 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,31 +1,120 @@
1-
import { EntitySubTree } from '@contember/react-binding'
1+
import { EntitySubTree, StaticRender, useEntity } from '@contember/react-binding'
22
import * as React from 'react'
3+
import { useState } from 'react'
34
import { Binding, PersistButton } from '@app/lib/binding'
45
import { Slots } from '@app/lib/layout'
56
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'
616

717

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+
889
export const image = () => <>
990

1091
<Binding>
1192
<Slots.Actions><PersistButton /></Slots.Actions>
1293
<EntitySubTree entity="UploadRoot(unique = unique)" setOnCreate="(unique = unique)">
1394
<ImageField
1495
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}
2297
label="Image file"
2398
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+
)}
24112
/>
25113
</EntitySubTree>
26114
</Binding>
27115
</>
28116

117+
29118
export const imageTrivial = () => <>
30119

31120
<Binding>
@@ -112,15 +201,22 @@ export const imageList = () => <>
112201
field="imageList.items"
113202
baseField="image"
114203
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}
122205
label="Image file"
123206
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+
)}
124220
/>
125221
</EntitySubTree>
126222
</Binding>

packages/react-ui-lib/src/datagrid/columns.tsx

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ import { DataGridColumnHeader } from './column-header'
66
import { DataViewElement } from '@contember/react-dataview'
77
import { formatBoolean, formatDate, formatNumber } from '../formatting'
88
import { DataGridEnumCell, DataGridHasManyCell, DataGridHasManyCellProps, DataGridHasOneCell, DataGridHasOneCellProps } from './cells'
9+
import { cn } from '../utils'
910

1011
export const DataGridActionColumn = Component<{ children: ReactNode }>(({ children }) => (
1112
<DataGridColumnLeaf
@@ -127,9 +128,11 @@ export type DataGridColumnProps = {
127128
name?: string
128129
hidingName?: string
129130
sortingField?: string
131+
cellClassName?: string
132+
headerClassName?: string
130133
}
131134

132-
export const DataGridColumn = Component<DataGridColumnProps>(({ children, header, name, hidingName, sortingField }) => {
135+
export const DataGridColumn = Component<DataGridColumnProps>(({ children, header, name, hidingName, sortingField, cellClassName, headerClassName }) => {
133136
const wrapIsVisible = (child: ReactNode) => {
134137
const resolvedName = hidingName ?? name
135138
return resolvedName ? <DataViewElement name={resolvedName} label={header}>{child}</DataViewElement> : child
@@ -140,14 +143,14 @@ export const DataGridColumn = Component<DataGridColumnProps>(({ children, header
140143
name={name}
141144
header={
142145
wrapIsVisible(
143-
<TableHead className={'text-center'}>
146+
<TableHead className={cn('text-center', headerClassName)}>
144147
{header ? <DataGridColumnHeader hidingName={hidingName ?? name} sortingField={sortingField}>
145148
{header}
146149
</DataGridColumnHeader> : null}
147150
</TableHead>,
148151
)
149152
}
150-
cell={wrapIsVisible(<TableCell>{children}</TableCell>)}
153+
cell={wrapIsVisible(<TableCell className={cellClassName}>{children}</TableCell>)}
151154
/>
152155
)
153156
})

packages/react-ui-lib/src/datagrid/tiles.tsx

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,18 +3,20 @@ import { DataViewEachRow, DataViewLayout } from '@contember/react-dataview'
33
import * as React from 'react'
44
import { dict } from '../dict'
55
import { LayoutGridIcon } from 'lucide-react'
6+
import { cn } from '../utils'
67

78
export interface DataGridTilesProps {
89
children: React.ReactNode
10+
className?: string
911
}
1012

11-
export const DataGridTiles = Component< DataGridTilesProps>(({ children }) => {
13+
export const DataGridTiles = Component< DataGridTilesProps>(({ children, className }) => {
1214
return (
1315
<DataViewLayout name={'grid'} label={<>
1416
<LayoutGridIcon className={'w-3 h-3'} />
1517
<span>{dict.datagrid.showGrid}</span>
1618
</>}>
17-
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
19+
<div className={cn('grid grid-cols-2 md:grid-cols-4 gap-4', className)}>
1820
<DataViewEachRow>
1921
{children}
2022
</DataViewEachRow>

packages/react-ui-lib/src/datagrid/toolbar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -53,7 +53,7 @@ export const DataGridToolbar = Component<DataGridToolbarProps>(({ children }) =>
5353
<DataGridAutoExport />
5454
</div>
5555

56-
<div className="flex flex-wrap gap-2">
56+
<div className="flex flex-1 flex-wrap gap-2">
5757
{children ?? <>
5858
<DataGridQueryFilter />
5959
</>}

0 commit comments

Comments
 (0)