Skip to content

Commit

Permalink
Merge pull request #732 from contember/feat/ui-lib-up
Browse files Browse the repository at this point in the history
update some ui components
  • Loading branch information
matej21 authored Jun 26, 2024
2 parents 979ec6a + cfe2103 commit 53e47ed
Show file tree
Hide file tree
Showing 14 changed files with 279 additions and 97 deletions.
21 changes: 16 additions & 5 deletions build/api/react-ui-lib.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -223,10 +223,14 @@ export const baseEditorPlugins: {
};

// @public (undocumented)
export type BaseFieldProps = Omit<FormContainerProps, 'children'> & UploaderBaseFieldProps;
export type BaseFieldProps = Omit<FormContainerProps, 'children'> & UploaderBaseFieldProps & {
dropzonePlaceholder?: ReactNode;
};

// @public (undocumented)
export type BaseFileRepeaterFieldProps = Omit<FormContainerProps, 'children'> & RepeaterProps & UploaderBaseFieldProps;
export type BaseFileRepeaterFieldProps = Omit<FormContainerProps, 'children'> & RepeaterProps & UploaderBaseFieldProps & {
dropzonePlaceholder?: ReactNode;
};

// @public (undocumented)
export const Binding: ({ children }: {
Expand Down Expand Up @@ -633,6 +637,8 @@ export type DataGridColumnProps = {
name?: string;
hidingName?: string;
sortingField?: string;
cellClassName?: string;
headerClassName?: string;
};

// @public (undocumented)
Expand Down Expand Up @@ -908,6 +914,8 @@ export const DataGridTiles: React_2.NamedExoticComponent<DataGridTilesProps>;
export interface DataGridTilesProps {
// (undocumented)
children: React_2.ReactNode;
// (undocumented)
className?: string;
}

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

// @public (undocumented)
export type DefaultRepeaterProps = {
title?: string;
title?: ReactNode;
addButtonPosition?: 'none' | 'after' | 'before' | 'around';
} & RepeaterProps;

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

// @public (undocumented)
export const RepeaterAddItemButton: ({ children }: {
export const RepeaterAddItemButton: ({ children, index }: {
children?: React.ReactNode;
index?: RepeaterAddItemIndex;
}) => JSX_2.Element;

// @public (undocumented)
Expand Down Expand Up @@ -2473,8 +2483,9 @@ export type UploadedVideoViewProps = FileUrlDataExtractorProps & GenericFileMeta
};

// @public (undocumented)
export const UploaderDropzone: ({ inactiveOnUpload }: {
export const UploaderDropzone: ({ inactiveOnUpload, dropzonePlaceholder }: {
inactiveOnUpload?: boolean;
dropzonePlaceholder?: ReactNode;
}) => JSX_2.Element;

// @public (undocumented)
Expand Down
12 changes: 12 additions & 0 deletions packages/playground/admin/app/components/UseEntity.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
import { Component, EntityAccessor, useEntity } from '@contember/interface'
import React from 'react'

export const UseEntity = Component<{ render: (accessor?: EntityAccessor) => React.ReactNode }>(({ render }) => {
const entity = useEntity()
return <>{render(entity)}</>
},
({ render }) => {
return <>
{render()}
</>
})
2 changes: 1 addition & 1 deletion packages/playground/admin/app/pages/repeater.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ const repeaterDropdown = (
export default <>
<Binding>
<Slots.Actions><PersistButton /></Slots.Actions>
<DefaultRepeater entities={'RepeaterItem'} sortableBy={'order'} title="Foo items">
<DefaultRepeater entities={'RepeaterItem'} sortableBy={'order'} title="Foo items" addButtonPosition="around">
<Field field={'title'} />

<RepeaterItemActions>
Expand Down
126 changes: 111 additions & 15 deletions packages/playground/admin/app/pages/upload.tsx
Original file line number Diff line number Diff line change
@@ -1,31 +1,120 @@
import { EntitySubTree } from '@contember/react-binding'
import { EntitySubTree, StaticRender, useEntity } from '@contember/react-binding'
import * as React from 'react'
import { useState } from 'react'
import { Binding, PersistButton } from '@app/lib/binding'
import { Slots } from '@app/lib/layout'
import { AudioField, FileField, ImageField, ImageRepeaterField, VideoField } from '@app/lib/form'
import { Dialog, DialogContent, DialogTrigger } from '@app/lib/ui/dialog'
import { Button } from '@app/lib/ui/button'
import { DataGrid, DataGridColumn, DataGridLoader, DataGridPagination, DataGridTable, DataGridTextColumn, DataGridTiles, DataGridToolbar } from '@app/lib/datagrid'
import { UploadedImageView, UploaderDropzoneAreaUI } from '@app/lib/upload'
import { UseEntity } from '@app/app/components/UseEntity'
import { EntityAccessor, Field } from '@contember/interface'
import { FileUrlDataExtractorProps, GenericFileMetadataExtractorProps, ImageFileDataExtractorProps } from '@contember/react-uploader'
import { UploadIcon } from 'lucide-react'
import { dict } from '@app/lib/dict'


const imageFields: FileUrlDataExtractorProps & GenericFileMetadataExtractorProps & ImageFileDataExtractorProps = {
urlField: 'url',
widthField: 'width',
heightField: 'height',
fileNameField: 'meta.fileName',
fileSizeField: 'meta.fileSize',
fileTypeField: 'meta.fileType',
lastModifiedField: 'meta.lastModified',
}

const SelectImage = () => {
const entity = useEntity()

return <SelectImageInner connect={it => {
entity.connectEntityAtField('image', it)
}} closeOnSelect />
}

const SelectImageRepeater = () => {
const entity = useEntity()

return <SelectImageInner connect={it => {
entity.getEntityList('imageList.items').createNewEntity(entity => {
entity().connectEntityAtField('image', it)
})
}} />
}

const SelectImageInner = ({ connect, closeOnSelect }: { connect: (entity: EntityAccessor) => void, closeOnSelect?: boolean }) => {
const [open, setOpen] = useState(false)
return (
<Dialog open={open} onOpenChange={setOpen}>
<DialogTrigger asChild>
<Button size="sm" variant="outline" onClick={e => e.stopPropagation()}>Select image</Button>
</DialogTrigger>
<DialogContent className="max-w-[90vw]">
<DataGrid entities="UploadImage">
<DataGridToolbar></DataGridToolbar>
<DataGridLoader>
<DataGridTiles className="md:grid-cols-[repeat(auto-fill,minmax(10rem,1fr))]">
<UseEntity render={it => (
<div className="relative border rounded shadow hover:shadow-md hover:border-yellow-500" onClick={() => {
it && connect(it)
closeOnSelect && setOpen(false)
}}>
<UploadedImageView {...imageFields} />
</div>
)} />
</DataGridTiles>
<DataGridTable>
<DataGridColumn headerClassName="w-0">
<UseEntity render={it => (<Button onClick={() => {
it && connect(it)
closeOnSelect && setOpen(false)
}}>Select</Button>)} />
</DataGridColumn>
<DataGridTextColumn field="url" header="URL">
<Field field="url" />
<StaticRender>
<ImageField {...imageFields} />
</StaticRender>
</DataGridTextColumn>
</DataGridTable>
</DataGridLoader>
<DataGridPagination />
</DataGrid>
</DialogContent>
</Dialog>
)
}

export const image = () => <>

<Binding>
<Slots.Actions><PersistButton /></Slots.Actions>
<EntitySubTree entity="UploadRoot(unique = unique)" setOnCreate="(unique = unique)">
<ImageField
baseField="image"
urlField="url"
widthField="width"
heightField="height"
fileNameField="meta.fileName"
fileSizeField="meta.fileSize"
fileTypeField="meta.fileType"
lastModifiedField="meta.lastModified"
{...imageFields}
label="Image file"
description="Some description of the image file."
dropzonePlaceholder={(
<UploaderDropzoneAreaUI className="w-60">
<UploadIcon className={'w-12 h-12 text-gray-400'} />
<div className={'font-semibold text-sm'}>{dict.uploader.dropFiles}</div>
<div className={'text-xs'}>{dict.uploader.or}</div>
<div className={'flex gap-2 items-center text-xs'}>
<Button size={'sm'} variant={'outline'}>{dict.uploader.browseFiles}</Button>
<div onClick={e => e.stopPropagation()}>
<SelectImage />
</div>
</div>
</UploaderDropzoneAreaUI>
)}
/>
</EntitySubTree>
</Binding>
</>


export const imageTrivial = () => <>

<Binding>
Expand Down Expand Up @@ -112,15 +201,22 @@ export const imageList = () => <>
field="imageList.items"
baseField="image"
sortableBy="order"
urlField="url"
widthField="width"
heightField="height"
fileNameField="meta.fileName"
fileSizeField="meta.fileSize"
fileTypeField="meta.fileType"
lastModifiedField="meta.lastModified"
{...imageFields}
label="Image file"
description="Some description of the image file."
dropzonePlaceholder={(
<UploaderDropzoneAreaUI className="w-60">
<UploadIcon className={'w-12 h-12 text-gray-400'} />
<div className={'font-semibold text-sm'}>{dict.uploader.dropFiles}</div>
<div className={'text-xs'}>{dict.uploader.or}</div>
<div className={'flex gap-2 items-center text-xs'}>
<Button size={'sm'} variant={'outline'}>{dict.uploader.browseFiles}</Button>
<div onClick={e => e.stopPropagation()}>
<SelectImageRepeater />
</div>
</div>
</UploaderDropzoneAreaUI>
)}
/>
</EntitySubTree>
</Binding>
Expand Down
9 changes: 6 additions & 3 deletions packages/react-ui-lib/src/datagrid/columns.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { DataGridColumnHeader } from './column-header'
import { DataViewElement } from '@contember/react-dataview'
import { formatBoolean, formatDate, formatNumber } from '../formatting'
import { DataGridEnumCell, DataGridHasManyCell, DataGridHasManyCellProps, DataGridHasOneCell, DataGridHasOneCellProps } from './cells'
import { cn } from '../utils'

export const DataGridActionColumn = Component<{ children: ReactNode }>(({ children }) => (
<DataGridColumnLeaf
Expand Down Expand Up @@ -127,9 +128,11 @@ export type DataGridColumnProps = {
name?: string
hidingName?: string
sortingField?: string
cellClassName?: string
headerClassName?: string
}

export const DataGridColumn = Component<DataGridColumnProps>(({ children, header, name, hidingName, sortingField }) => {
export const DataGridColumn = Component<DataGridColumnProps>(({ children, header, name, hidingName, sortingField, cellClassName, headerClassName }) => {
const wrapIsVisible = (child: ReactNode) => {
const resolvedName = hidingName ?? name
return resolvedName ? <DataViewElement name={resolvedName} label={header}>{child}</DataViewElement> : child
Expand All @@ -140,14 +143,14 @@ export const DataGridColumn = Component<DataGridColumnProps>(({ children, header
name={name}
header={
wrapIsVisible(
<TableHead className={'text-center'}>
<TableHead className={cn('text-center', headerClassName)}>
{header ? <DataGridColumnHeader hidingName={hidingName ?? name} sortingField={sortingField}>
{header}
</DataGridColumnHeader> : null}
</TableHead>,
)
}
cell={wrapIsVisible(<TableCell>{children}</TableCell>)}
cell={wrapIsVisible(<TableCell className={cellClassName}>{children}</TableCell>)}
/>
)
})
Expand Down
6 changes: 4 additions & 2 deletions packages/react-ui-lib/src/datagrid/tiles.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,18 +3,20 @@ import { DataViewEachRow, DataViewLayout } from '@contember/react-dataview'
import * as React from 'react'
import { dict } from '../dict'
import { LayoutGridIcon } from 'lucide-react'
import { cn } from '../utils'

export interface DataGridTilesProps {
children: React.ReactNode
className?: string
}

export const DataGridTiles = Component< DataGridTilesProps>(({ children }) => {
export const DataGridTiles = Component< DataGridTilesProps>(({ children, className }) => {
return (
<DataViewLayout name={'grid'} label={<>
<LayoutGridIcon className={'w-3 h-3'} />
<span>{dict.datagrid.showGrid}</span>
</>}>
<div className="grid grid-cols-2 md:grid-cols-4 gap-4">
<div className={cn('grid grid-cols-2 md:grid-cols-4 gap-4', className)}>
<DataViewEachRow>
{children}
</DataViewEachRow>
Expand Down
2 changes: 1 addition & 1 deletion packages/react-ui-lib/src/datagrid/toolbar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -53,7 +53,7 @@ export const DataGridToolbar = Component<DataGridToolbarProps>(({ children }) =>
<DataGridAutoExport />
</div>

<div className="flex flex-wrap gap-2">
<div className="flex flex-1 flex-wrap gap-2">
{children ?? <>
<DataGridQueryFilter />
</>}
Expand Down
Loading

0 comments on commit 53e47ed

Please sign in to comment.