From d96aac195c131f9ed8909e5dd01e84fa9f1f8afd Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Mar 2024 17:49:51 +0100 Subject: [PATCH 01/40] feat(binding): add "name" to entity list accessor --- packages/binding/src/accessors/EntityListAccessor.ts | 4 +++- packages/binding/src/core/StateInitializer.ts | 1 + 2 files changed, 4 insertions(+), 1 deletion(-) diff --git a/packages/binding/src/accessors/EntityListAccessor.ts b/packages/binding/src/accessors/EntityListAccessor.ts index 3b15f309f5..ac39702ec2 100644 --- a/packages/binding/src/accessors/EntityListAccessor.ts +++ b/packages/binding/src/accessors/EntityListAccessor.ts @@ -1,6 +1,6 @@ import type { ListOperations } from '../core/operations' import type { Environment } from '../dao' -import type { EntityId, EntityRealmKey } from '../treeParameters' +import type { EntityId, EntityName, EntityRealmKey } from '../treeParameters' import type { AsyncBatchUpdatesOptions } from './AsyncBatchUpdatesOptions' import type { BatchUpdatesOptions } from './BatchUpdatesOptions' import type { EntityAccessor } from './EntityAccessor' @@ -10,11 +10,13 @@ import type { PersistErrorOptions } from './PersistErrorOptions' import type { PersistSuccessOptions } from './PersistSuccessOptions' import type { EntityListState } from '../core/state' import { RuntimeId } from '../accessorTree' +import { EntityListSubTreeMarker, HasManyRelationMarker } from '../markers' class EntityListAccessor implements Errorable { public constructor( private readonly state: EntityListState, private readonly operations: ListOperations, + public readonly name: EntityName, private readonly _children: ReadonlyMap, private readonly _idsPersistedOnServer: ReadonlySet, public readonly hasUnpersistedChanges: boolean, diff --git a/packages/binding/src/core/StateInitializer.ts b/packages/binding/src/core/StateInitializer.ts index f85af6a47e..ad34334e18 100644 --- a/packages/binding/src/core/StateInitializer.ts +++ b/packages/binding/src/core/StateInitializer.ts @@ -317,6 +317,7 @@ export class StateInitializer { entityListState.accessor = new EntityListAccessor( entityListState, this.listOperations, + entityListState.entityName, entityListState.children, persistedEntityIds, entityListState.unpersistedChangesCount !== 0, From 72e2d9631ca7ed355c2094b4c1b0803520e7acfc Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Mar 2024 17:50:13 +0100 Subject: [PATCH 02/40] feat(binding): add getMarker to entity list accessor --- packages/binding/src/accessors/EntityListAccessor.ts | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/packages/binding/src/accessors/EntityListAccessor.ts b/packages/binding/src/accessors/EntityListAccessor.ts index ac39702ec2..34436b3f76 100644 --- a/packages/binding/src/accessors/EntityListAccessor.ts +++ b/packages/binding/src/accessors/EntityListAccessor.ts @@ -135,6 +135,10 @@ class EntityListAccessor implements Errorable { public getParent(): EntityAccessor | undefined { return this.state.blueprint.parent?.getAccessor() } + + public getMarker(): EntityListSubTreeMarker | HasManyRelationMarker { + return this.state.blueprint.marker + } } namespace EntityListAccessor { From 676b6513009a05be54c3b65a08da5c63e1175672 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Mar 2024 17:50:41 +0100 Subject: [PATCH 03/40] feat(react-dataview): add DataViewHasFilterType component --- .../src/components/filtering/DataViewHasFilterType.tsx | 10 ++++++++++ .../react-dataview/src/components/filtering/index.ts | 1 + 2 files changed, 11 insertions(+) create mode 100644 packages/react-dataview/src/components/filtering/DataViewHasFilterType.tsx diff --git a/packages/react-dataview/src/components/filtering/DataViewHasFilterType.tsx b/packages/react-dataview/src/components/filtering/DataViewHasFilterType.tsx new file mode 100644 index 0000000000..ab3635a835 --- /dev/null +++ b/packages/react-dataview/src/components/filtering/DataViewHasFilterType.tsx @@ -0,0 +1,10 @@ +import { useDataViewFilterHandlerRegistry } from '../../contexts' + +export interface DataViewHasFilterTypeProps { + name: string + children: React.ReactNode +} +export const DataViewHasFilterType = ({ name, children }: DataViewHasFilterTypeProps) => { + const types = useDataViewFilterHandlerRegistry() + return types[name] ? <>{children} : null +} diff --git a/packages/react-dataview/src/components/filtering/index.ts b/packages/react-dataview/src/components/filtering/index.ts index 71b662f03a..79eccec1ae 100644 --- a/packages/react-dataview/src/components/filtering/index.ts +++ b/packages/react-dataview/src/components/filtering/index.ts @@ -5,3 +5,4 @@ export * from './date' export * from './number' export * from './nullCondition' export * from './text' +export * from './DataViewHasFilterType' From a25c946a47c6c217b87691f23e51be4adcf8118e Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Mar 2024 17:51:04 +0100 Subject: [PATCH 04/40] feat(react-select): auto detect options and filter fields --- .../src/components/MultiSelect.tsx | 14 +++++--- .../react-select/src/components/Select.tsx | 15 +++++--- .../src/components/SelectDataView.tsx | 11 +++--- .../src/components/SortableMultiSelect.tsx | 33 ++++++++++++++--- packages/react-select/src/contexts.ts | 6 ++++ packages/react-select/src/hooks/index.ts | 1 + .../react-select/src/hooks/useSelectFilter.ts | 35 +++++++++++++++++++ packages/react-select/src/index.ts | 1 + 8 files changed, 99 insertions(+), 17 deletions(-) create mode 100644 packages/react-select/src/hooks/index.ts create mode 100644 packages/react-select/src/hooks/useSelectFilter.ts diff --git a/packages/react-select/src/components/MultiSelect.tsx b/packages/react-select/src/components/MultiSelect.tsx index 9b820c605f..1998e665f6 100644 --- a/packages/react-select/src/components/MultiSelect.tsx +++ b/packages/react-select/src/components/MultiSelect.tsx @@ -1,19 +1,23 @@ import React, { ReactNode, useCallback, useMemo } from 'react' -import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext } from '../contexts' +import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext, SelectOptionsFilterContext } from '../contexts' import { Component, EntityAccessor, HasMany, SugaredQualifiedEntityList, SugaredRelativeEntityList, useEntityList } from '@contember/react-binding' import { useReferentiallyStableCallback } from '@contember/react-utils' import { SelectEvents } from '../types' +import { SelectFilterFieldProps, useSelectFilter } from '../hooks' export type MultiSelectProps = & { children: ReactNode field: SugaredRelativeEntityList['field'] - options: SugaredQualifiedEntityList['entities'] + options?: SugaredQualifiedEntityList['entities'] } + & SelectFilterFieldProps & SelectEvents -export const MultiSelect = Component(({ field, children, options, onSelect, onUnselect }: MultiSelectProps) => { +export const MultiSelect = Component(({ field, children, options, onSelect, onUnselect, filterField }: MultiSelectProps) => { const entities = useEntityList({ field }) + options ??= entities.name + const filter = useSelectFilter({ filterField, marker: entities.getMarker() }) const entitiesArr = useMemo(() => Array.from(entities), [entities]) const selectedEntities = useMemo(() => Array.from(entities).map(it => it.id), [entities]) @@ -39,7 +43,9 @@ export const MultiSelect = Component(({ field, children, options, onSelect, onUn - {children} + + {children} + diff --git a/packages/react-select/src/components/Select.tsx b/packages/react-select/src/components/Select.tsx index 2f7b73ec30..ac74debd77 100644 --- a/packages/react-select/src/components/Select.tsx +++ b/packages/react-select/src/components/Select.tsx @@ -1,20 +1,25 @@ import React, { ReactNode, useCallback, useMemo } from 'react' -import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext } from '../contexts' +import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext, SelectOptionsFilterContext } from '../contexts' import { Component, EntityAccessor, HasOne, SugaredQualifiedEntityList, SugaredRelativeSingleEntity, useEntity } from '@contember/react-binding' import { useReferentiallyStableCallback } from '@contember/react-utils' import { SelectEvents } from '../types' +import { SelectFilterFieldProps, useSelectFilter } from '../hooks' export type SelectProps = & { children: ReactNode field: SugaredRelativeSingleEntity['field'] - options: SugaredQualifiedEntityList['entities'] + options?: SugaredQualifiedEntityList['entities'] } + & SelectFilterFieldProps & SelectEvents -export const Select = Component(({ field, children, onUnselect, onSelect, options }: SelectProps) => { +export const Select = Component(({ field, children, onUnselect, onSelect, options, filterField }: SelectProps) => { const entity = useEntity() const selectedEntity = entity.getEntity({ field }) + options ??= selectedEntity.name + const filter = useSelectFilter({ filterField, marker: selectedEntity.getMarker() }) + const entityExists = selectedEntity.existsOnServer || selectedEntity.hasUnpersistedChanges const entitiesArr = useMemo(() => entityExists ? [selectedEntity] : [], [entityExists, selectedEntity]) @@ -37,7 +42,9 @@ export const Select = Component(({ field, children, onUnselect, onSelect, option - {children} + + {children} + diff --git a/packages/react-select/src/components/SelectDataView.tsx b/packages/react-select/src/components/SelectDataView.tsx index d8c9941aa9..c32ccce093 100644 --- a/packages/react-select/src/components/SelectDataView.tsx +++ b/packages/react-select/src/components/SelectDataView.tsx @@ -1,6 +1,6 @@ -import React, { ReactNode } from 'react' -import { useSelectHandleSelect, useSelectOptions } from '../contexts' -import { DataViewProps, DataView } from '@contember/react-dataview' +import React, { ReactNode, useMemo } from 'react' +import { useSelectHandleSelect, useSelectOptions, useSelectOptionsFilter } from '../contexts' +import { DataViewProps, DataView, DataViewFilterHandlerRegistry } from '@contember/react-dataview' export type SelectDataViewProps = & Omit @@ -11,7 +11,10 @@ export type SelectDataViewProps = export const SelectDataView = (props: SelectDataViewProps) => { const handleSelect = useSelectHandleSelect() const entities = useSelectOptions() + const filter = useSelectOptionsFilter() + const defaultFilterTypes = useMemo((): DataViewFilterHandlerRegistry => filter ? { query: filter } : {}, [filter]) + return ( - + ) } diff --git a/packages/react-select/src/components/SortableMultiSelect.tsx b/packages/react-select/src/components/SortableMultiSelect.tsx index 0a5b2b29c2..6dd64a8af2 100644 --- a/packages/react-select/src/components/SortableMultiSelect.tsx +++ b/packages/react-select/src/components/SortableMultiSelect.tsx @@ -1,24 +1,45 @@ import React, { ReactNode, useCallback, useMemo } from 'react' -import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext } from '../contexts' -import { Component, EntityAccessor, HasOne, SugaredQualifiedEntityList, SugaredRelativeEntityList, SugaredRelativeSingleEntity, SugaredRelativeSingleField, useEntityList } from '@contember/react-binding' +import { SelectCurrentEntitiesContext, SelectHandler, SelectHandleSelectContext, SelectIsSelectedContext, SelectOptionsContext, SelectOptionsFilterContext } from '../contexts' +import { Component, EntityAccessor, HasOne, PlaceholderGenerator, QueryLanguage, SugaredQualifiedEntityList, SugaredRelativeEntityList, SugaredRelativeSingleEntity, SugaredRelativeSingleField, useEntityList } from '@contember/react-binding' import { useReferentiallyStableCallback } from '@contember/react-utils' import { Repeater } from '@contember/react-repeater' import { SelectEvents } from '../types' +import { SelectFilterFieldProps, useSelectFilter } from '../hooks' +import { FieldMarker, MeaningfulMarker } from '@contember/binding' export type SortableMultiSelectProps = & { children: ReactNode field: SugaredRelativeEntityList['field'] - options: SugaredQualifiedEntityList['entities'] + options?: SugaredQualifiedEntityList['entities'] sortableBy: SugaredRelativeSingleField['field'] connectAt: SugaredRelativeSingleEntity['field'] } + & SelectFilterFieldProps & SelectEvents -export const SortableMultiSelect = Component(({ field, children, sortableBy, connectAt, options, onSelect, onUnselect }: SortableMultiSelectProps) => { +export const SortableMultiSelect = Component(({ field, children, sortableBy, connectAt, options, onSelect, onUnselect, filterField }: SortableMultiSelectProps) => { const list = useEntityList({ field }) const entitiesArr = useMemo(() => Array.from(list), [list]) + const optionsMarker = useMemo(() => { + const desugared = QueryLanguage.desugarRelativeSingleEntity({ field: connectAt }, list.environment) + let marker: Exclude = list.getMarker() + for (let relation of desugared.hasOneRelationPath) { + const placeholder = PlaceholderGenerator.getHasOneRelationPlaceholder(relation) + const relationMarker = marker.fields.markers.get(placeholder) + if (!relationMarker || relationMarker instanceof FieldMarker) { + throw new Error('Invalid marker') + } + marker = relationMarker + } + return marker + }, [connectAt, list]) + + options ??= optionsMarker.environment.getSubTreeNode().entity.name + + const filter = useSelectFilter({ filterField, marker: optionsMarker }) + const selectedEntities = useMemo(() => Array.from(list).map(it => it.getEntity({ field: connectAt }).id), [connectAt, list]) const handler = useReferentiallyStableCallback((entity, action = 'toggle') => { @@ -46,7 +67,9 @@ export const SortableMultiSelect = Component(({ field, children, sortableBy, con - {children} + + {children} + diff --git a/packages/react-select/src/contexts.ts b/packages/react-select/src/contexts.ts index a924a1e6c2..7c083aea35 100644 --- a/packages/react-select/src/contexts.ts +++ b/packages/react-select/src/contexts.ts @@ -1,5 +1,6 @@ import { createRequiredContext } from '@contember/react-utils' import { EntityAccessor, SugaredQualifiedEntityList } from '@contember/react-binding' +import { DataViewFilterHandler, TextFilterArtifacts } from '@contember/react-dataview' const _SelectCurrentEntitiesContext = createRequiredContext('SelectCurrentEntitiesContext') /** @internal */ @@ -23,3 +24,8 @@ const _SelectOptionsContext = createRequiredContext | undefined>('SelectOptionsFilterContext') +/** @internal */ +export const SelectOptionsFilterContext = _SelectOptionsFilterContext[0] +export const useSelectOptionsFilter = _SelectOptionsFilterContext[1] diff --git a/packages/react-select/src/hooks/index.ts b/packages/react-select/src/hooks/index.ts new file mode 100644 index 0000000000..575e7ab249 --- /dev/null +++ b/packages/react-select/src/hooks/index.ts @@ -0,0 +1 @@ +export * from './useSelectFilter' diff --git a/packages/react-select/src/hooks/useSelectFilter.ts b/packages/react-select/src/hooks/useSelectFilter.ts new file mode 100644 index 0000000000..4aad223fd6 --- /dev/null +++ b/packages/react-select/src/hooks/useSelectFilter.ts @@ -0,0 +1,35 @@ +import { FieldMarker, HasOneRelationMarker, MeaningfulMarker, SugaredRelativeSingleField } from '@contember/binding' +import { createCoalesceFilter } from '@contember/react-dataview' +import { useMemo } from 'react' + +export type SelectFilterFieldProps = { + filterField?: SugaredRelativeSingleField['field'] | SugaredRelativeSingleField['field'][] +} + +export const useSelectFilter = ({ filterField, marker }: SelectFilterFieldProps & { + marker: Exclude +}) => { + return useMemo(() => { + const filter = filterField ?? extractStringFields(marker) + if (!filter || (Array.isArray(filter) && filter.length === 0)) { + return undefined + } + return createCoalesceFilter(Array.isArray(filter) ? filter : [filter]) + }, [filterField, marker]) +} + +const extractStringFields = (marker: Exclude): string[] => { + const node = marker.environment.getSubTreeNode() + const textFields = [] + for (const field of marker.fields.markers.values()) { + if (field instanceof FieldMarker) { + const columnInfo = node.entity.fields.get(field.fieldName) + if (columnInfo?.type === 'String') { + textFields.push(field.fieldName) + } + } else if (field instanceof HasOneRelationMarker) { + textFields.push(...extractStringFields(field).map(it => `${field.parameters.field}.${it}`)) + } + } + return textFields +} diff --git a/packages/react-select/src/index.ts b/packages/react-select/src/index.ts index 759a922582..59faaf52dd 100644 --- a/packages/react-select/src/index.ts +++ b/packages/react-select/src/index.ts @@ -1,3 +1,4 @@ export * from './components' export * from './contexts' +export * from './hooks' export * from './types' From d01c2c62b5153c022cc3eb3be1d461ed1d903e1c Mon Sep 17 00:00:00 2001 From: David Matejka Date: Wed, 13 Mar 2024 17:51:20 +0100 Subject: [PATCH 05/40] refactor(playground): update selects --- .../playground/admin/app/pages/select.tsx | 31 +++++++++++++--- .../components/datagrid/filters/relation.tsx | 14 +++---- .../admin/lib/components/select/filter.tsx | 28 +++++--------- .../lib/components/select/multi-select.tsx | 31 ++++++++-------- .../admin/lib/components/select/select.tsx | 30 +++++++-------- .../lib/components/select/sortable-select.tsx | 37 +++++++++---------- 6 files changed, 89 insertions(+), 82 deletions(-) diff --git a/packages/playground/admin/app/pages/select.tsx b/packages/playground/admin/app/pages/select.tsx index c6cd148dbd..28f2d6ae35 100644 --- a/packages/playground/admin/app/pages/select.tsx +++ b/packages/playground/admin/app/pages/select.tsx @@ -13,10 +13,7 @@ export const hasOne = () => <>
- - - - }> +
@@ -30,7 +27,7 @@ export const hasMany = () => <>
- +
@@ -44,10 +41,32 @@ export const hasManySortable = () => <>
- +
+ +export const createNewForm = () => <> + + + + + +
+ + + + } + > + + +
+
+
+ diff --git a/packages/playground/admin/lib/components/datagrid/filters/relation.tsx b/packages/playground/admin/lib/components/datagrid/filters/relation.tsx index afaba6791b..ccb576ee8f 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/relation.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/relation.tsx @@ -2,13 +2,14 @@ import * as React from 'react' import { forwardRef, ReactNode, useCallback } from 'react' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip' import { Button } from '../../ui/button' -import { DataView, DataViewNullFilterTrigger, DataViewRelationFilterList, DataViewRelationFilterTrigger, useDataViewRelationFilterFactory, UseDataViewRelationFilterResult } from '@contember/react-dataview' +import { createCoalesceFilter, DataView, DataViewNullFilterTrigger, DataViewRelationFilterList, DataViewRelationFilterTrigger, useDataViewRelationFilterFactory, UseDataViewRelationFilterResult } from '@contember/react-dataview' import { Component, EntityId, SugarableQualifiedEntityList, SugaredQualifiedEntityList, useEntity } from '@contember/interface' import { Popover, PopoverTrigger } from '../../ui/popover' import { DataViewActiveFilterUI, DataViewExcludeActionButtonUI, DataViewFilterActionButtonUI, DataViewFilterSelectItemUI, DataViewFilterSelectTriggerUI, DataViewSingleFilterUI } from '../ui' import { DataViewNullFilter } from './common' -import { createDefaultSelectFilter, SelectListInner, SelectPopoverContent } from '../../select' +import { SelectDefaultFilter, SelectListInner, SelectPopoverContent } from '../../select' import { dict } from '../../../dict' +import { SelectFilterFieldProps } from '@contember/react-select' export const DataViewRelationFieldTooltip = ({ filter, children, actions }: { filter: string, children: ReactNode, actions?: ReactNode }) => ( @@ -75,14 +76,13 @@ const DataViewRelationFilterSelectItem = forwardRef { - const filter = filterField ? createDefaultSelectFilter(filterField) : { filterTypes: undefined, filterToolbar: undefined } + const filter = filterField ? { query: createCoalesceFilter(Array.isArray(filterField) ? filterField : [filterField]) } : undefined let filterFactory = useDataViewRelationFilterFactory(name) return ( @@ -92,11 +92,11 @@ const DataViewRelationFilterSelect = ({ name, children, options, filterField, la - { + { const [, set] = filterFactory(it.id) set('toggleInclude') }}> - + }> {children} diff --git a/packages/playground/admin/lib/components/select/filter.tsx b/packages/playground/admin/lib/components/select/filter.tsx index 563266319c..922c9584c8 100644 --- a/packages/playground/admin/lib/components/select/filter.tsx +++ b/packages/playground/admin/lib/components/select/filter.tsx @@ -1,25 +1,15 @@ import * as React from 'react' -import { ReactNode } from 'react' import { Input } from '../ui/input' -import { createTextFilter, DataViewFilterHandlerRegistry, DataViewTextFilterInput } from '@contember/react-dataview' -import { dict } from '../../../lib/dict' +import { DataViewHasFilterType, DataViewTextFilterInput } from '@contember/react-dataview' +import { dict } from '../../dict' -export const createDefaultSelectFilter = (filterField?: string): { - filterTypes?: DataViewFilterHandlerRegistry - filterToolbar?: ReactNode -} => { - if (!filterField) { - return { - filterTypes: undefined, - filterToolbar: undefined, - } - } - return { - filterTypes: { query: createTextFilter(filterField) }, - filterToolbar: ( + +export const SelectDefaultFilter = () => { + return ( + - + - ), - } + + ) } diff --git a/packages/playground/admin/lib/components/select/multi-select.tsx b/packages/playground/admin/lib/components/select/multi-select.tsx index 4d4187293a..b1650f1b81 100644 --- a/packages/playground/admin/lib/components/select/multi-select.tsx +++ b/packages/playground/admin/lib/components/select/multi-select.tsx @@ -1,28 +1,27 @@ import * as React from 'react' import { ReactNode } from 'react' import { MultiSelectItemContentUI, MultiSelectItemRemoveButtonUI, MultiSelectItemUI, SelectCreateNewTrigger, SelectDefaultPlaceholderUI, SelectInputActionsUI, SelectInputUI, SelectListItemUI, SelectPopoverContent } from './ui' -import { ChevronDownIcon, PlusIcon } from 'lucide-react' +import { ChevronDownIcon } from 'lucide-react' import { Popover, PopoverTrigger } from '../ui/popover' import { Component, SugaredQualifiedEntityList, SugaredRelativeEntityList } from '@contember/interface' -import { createDefaultSelectFilter } from './filter' +import { SelectDefaultFilter } from './filter' import { SelectListInner } from './list' -import { MultiSelect, SelectDataView, SelectEachValue, SelectItemTrigger, SelectOption, SelectPlaceholder } from '@contember/react-select' +import { MultiSelect, SelectDataView, SelectEachValue, SelectFilterFieldProps, SelectItemTrigger, SelectOption, SelectPlaceholder } from '@contember/react-select' import { CreateEntityDialog } from './create-new' -import { Button } from '../ui/button' -export interface MultiSelectInputProps { - field: SugaredRelativeEntityList['field'] - options: SugaredQualifiedEntityList['entities'] - children: ReactNode - filterField?: string - placeholder?: ReactNode - createNewForm?: ReactNode +export type MultiSelectInputProps = + & SelectFilterFieldProps + & { + field: SugaredRelativeEntityList['field'] + options?: SugaredQualifiedEntityList['entities'] + children: ReactNode + placeholder?: ReactNode + createNewForm?: ReactNode } export const MultiSelectInput = Component(({ field, filterField, options, children, placeholder, createNewForm }) => { - const filter = createDefaultSelectFilter(filterField) return ( - +
@@ -48,8 +47,8 @@ export const MultiSelectInput = Component(({ field, filte - - + + }> @@ -68,5 +67,5 @@ export const MultiSelectInput = Component(({ field, filte )}
-) + ) }) diff --git a/packages/playground/admin/lib/components/select/select.tsx b/packages/playground/admin/lib/components/select/select.tsx index 289653b184..896c0eaddf 100644 --- a/packages/playground/admin/lib/components/select/select.tsx +++ b/packages/playground/admin/lib/components/select/select.tsx @@ -7,26 +7,26 @@ import { SugaredRelativeSingleEntity } from '@contember/react-binding' import { ChevronDownIcon, XIcon } from 'lucide-react' import { SelectCreateNewTrigger, SelectDefaultPlaceholderUI, SelectInputActionsUI, SelectInputUI, SelectListItemUI, SelectPopoverContent } from './ui' import { SelectListInner } from './list' -import { createDefaultSelectFilter } from './filter' -import { Select, SelectDataView, SelectEachValue, SelectItemTrigger, SelectOption, SelectPlaceholder } from '@contember/react-select' +import { Select, SelectDataView, SelectEachValue, SelectFilterFieldProps, SelectItemTrigger, SelectOption, SelectPlaceholder } from '@contember/react-select' import { CreateEntityDialog } from './create-new' +import { SelectDefaultFilter } from './filter' -export interface SelectInputProps { - field: SugaredRelativeSingleEntity['field'] - children: ReactNode - options: SugaredQualifiedEntityList['entities'] - filterField?: string - placeholder?: ReactNode - createNewForm?: ReactNode -} +export type SelectInputProps = + & SelectFilterFieldProps + & { + field: SugaredRelativeSingleEntity['field'] + children: ReactNode + options?: SugaredQualifiedEntityList['entities'] + placeholder?: ReactNode + createNewForm?: ReactNode + } -export const SelectInput = Component(({ field, filterField, options, children, placeholder, createNewForm }, env) => { - const filter = createDefaultSelectFilter(filterField) +export const SelectInput = Component(({ field, filterField, options, children, placeholder, createNewForm }) => { const [open, setOpen] = React.useState(false) return ( - setOpen(false)} options={options} filterField={filterField}>
@@ -52,8 +52,8 @@ export const SelectInput = Component(({ field, filterField, op - - + + }> diff --git a/packages/playground/admin/lib/components/select/sortable-select.tsx b/packages/playground/admin/lib/components/select/sortable-select.tsx index 930a4d9fc2..cd9102b07a 100644 --- a/packages/playground/admin/lib/components/select/sortable-select.tsx +++ b/packages/playground/admin/lib/components/select/sortable-select.tsx @@ -1,4 +1,4 @@ -import { cn } from '../../../lib/utils/cn' +import { cn } from '../../utils/cn' import { DropIndicator } from '../ui/sortable' import * as React from 'react' import { ReactNode } from 'react' @@ -14,15 +14,14 @@ import { SelectListItemUI, SelectPopoverContent, } from './ui' -import { createDefaultSelectFilter } from './filter' import { Popover, PopoverTrigger } from '../ui/popover' -import { ChevronDownIcon, PlusIcon } from 'lucide-react' +import { ChevronDownIcon } from 'lucide-react' import { SelectListInner } from './list' import { RepeaterSortable, RepeaterSortableDragOverlay, RepeaterSortableDropIndicator, RepeaterSortableEachItem, RepeaterSortableItemActivator, RepeaterSortableItemNode } from '@contember/react-repeater-dnd-kit' import { Component, HasOne, SugaredQualifiedEntityList, SugaredRelativeEntityList, SugaredRelativeSingleEntity, SugaredRelativeSingleField } from '@contember/interface' -import { SelectDataView, SelectItemTrigger, SelectOption, SelectPlaceholder, SortableMultiSelect } from '@contember/react-select' +import { SelectDataView, SelectFilterFieldProps, SelectItemTrigger, SelectOption, SelectPlaceholder, SortableMultiSelect } from '@contember/react-select' import { CreateEntityDialog } from './create-new' -import { Button } from '../ui/button' +import { SelectDefaultFilter } from './filter' const MultiSortableSelectDropIndicator = ({ position }: { position: 'before' | 'after' }) => (
@@ -32,21 +31,21 @@ const MultiSortableSelectDropIndicator = ({ position }: { position: 'before' | '
) -export interface SortableMultiSelectInputProps { - field: SugaredRelativeEntityList['field'] - sortableBy: SugaredRelativeSingleField['field'] - connectAt: SugaredRelativeSingleEntity['field'] - children: ReactNode - options: SugaredQualifiedEntityList['entities'] - filterField?: string - placeholder?: ReactNode - createNewForm?: ReactNode -} +export type SortableMultiSelectInputProps = + & SelectFilterFieldProps + & { + field: SugaredRelativeEntityList['field'] + sortableBy: SugaredRelativeSingleField['field'] + connectAt: SugaredRelativeSingleEntity['field'] + children: ReactNode + options?: SugaredQualifiedEntityList['entities'] + placeholder?: ReactNode + createNewForm?: ReactNode + } export const SortableMultiSelectInput = Component(({ field, filterField, options, children, sortableBy, connectAt, placeholder, createNewForm }) => { - const filter = createDefaultSelectFilter(filterField) return ( - +
@@ -94,8 +93,8 @@ export const SortableMultiSelectInput = Component - - + + }> From 3d3d5f8589115647b6801a6f7525c24c3dd362d6 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Thu, 14 Mar 2024 15:41:01 +0100 Subject: [PATCH 06/40] feat(playground): add select or type field --- .../admin/app/components/navigation.tsx | 5 ++ packages/playground/admin/app/pages/input.tsx | 18 ++++++ .../admin/lib-extra/select-or-type-field.tsx | 59 +++++++++++++++++++ .../admin/lib/components/ui/input.tsx | 6 +- 4 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 packages/playground/admin/lib-extra/select-or-type-field.tsx diff --git a/packages/playground/admin/app/components/navigation.tsx b/packages/playground/admin/app/components/navigation.tsx index 471cfada1a..ecdea6160a 100644 --- a/packages/playground/admin/app/components/navigation.tsx +++ b/packages/playground/admin/app/components/navigation.tsx @@ -64,6 +64,11 @@ export const Navigation = () => { label: 'Basic inputs', to: 'input/basic', }, + { + icon: line, + label: 'Select or type', + to: 'input/selectOrType', + }, { icon: line, label: 'Textarea', diff --git a/packages/playground/admin/app/pages/input.tsx b/packages/playground/admin/app/pages/input.tsx index 73a262d4a4..44b4c66310 100644 --- a/packages/playground/admin/app/pages/input.tsx +++ b/packages/playground/admin/app/pages/input.tsx @@ -4,6 +4,7 @@ import { CheckboxField, InputField, RadioEnumField, TextareaField } from '../../ import * as React from 'react' import { Button } from '../../lib/components/ui/button' import { Binding, PersistButton } from '../../lib/components/binding' +import { SelectOrTypeField } from '../../lib-extra/select-or-type-field' export const basic = () => <> @@ -23,6 +24,23 @@ export const basic = () => <> + +export const selectOrType = () => <> + + + + + +
+ +
+
+
+ + export const textarea = () => <> diff --git a/packages/playground/admin/lib-extra/select-or-type-field.tsx b/packages/playground/admin/lib-extra/select-or-type-field.tsx new file mode 100644 index 0000000000..6d4fa52ad2 --- /dev/null +++ b/packages/playground/admin/lib-extra/select-or-type-field.tsx @@ -0,0 +1,59 @@ +import { FormContainer, FormContainerProps } from '../lib/components/form' +import { ComponentProps, useState } from 'react' +import { Input, SelectInput } from '../lib/components/ui/input' +import { cn } from '../lib/utils/cn' +import * as React from 'react' +import { FormFieldScope, FormInput, FormInputProps } from '@contember/react-form' +import { Component, Field } from '@contember/react-binding' +import { useField } from '@contember/react-binding' + +export type SelectOrTypeFieldProps = + & Omit + & Omit + & { + required?: boolean + selectProps?: ComponentProps + inputProps?: ComponentProps + options: Record + } + +export const SelectOrTypeField = Component(({ field, label, description, selectProps, inputProps, isNonbearing, defaultValue, required, options }: SelectOrTypeFieldProps) => { + const fieldAccessor = useField({ field }) + const fieldValue = fieldAccessor.value + const [showSelect, setShowSelect] = useState(!fieldValue || Object.keys(options).includes(fieldValue)) + return ( + + + {showSelect ? { + const value = e.target.value + if (value === '___other') { + setShowSelect(false) + fieldAccessor.updateValue('') + } else { + fieldAccessor.updateValue(value) + + } + }} {...selectProps} className={cn('max-w-md', selectProps?.className)}> + + {Object.entries(options ?? {}).map(([value, label]) => ( + + ))} + + + : + + } + + + ) +}, ({ field, label, description, isNonbearing, defaultValue }) => { + return ( + <> + {label} + {description} + + + ) +}) diff --git a/packages/playground/admin/lib/components/ui/input.tsx b/packages/playground/admin/lib/components/ui/input.tsx index 122cea501a..77aac73074 100644 --- a/packages/playground/admin/lib/components/ui/input.tsx +++ b/packages/playground/admin/lib/components/ui/input.tsx @@ -1,7 +1,7 @@ import { uic } from '../../../lib/utils/uic' export const Input = uic('input', { - baseClass: 'flex w-full border border-input bg-background m ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-destructive data-[invalid]:ring-destructive', + baseClass: 'flex w-full border border-input bg-background ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-destructive data-[invalid]:ring-destructive', variants: { inputSize: { default: 'h-10 rounded-md p-2 text-sm', @@ -14,6 +14,10 @@ export const Input = uic('input', { displayName: 'Input', }) +export const SelectInput = uic('select', { + baseClass: 'flex w-full h-10 rounded-md p-2 text-sm border border-input bg-background ring-offset-background file:border-0 file:bg-transparent file:text-sm file:font-medium placeholder:text-muted-foreground focus-visible:outline-none focus-visible:ring-2 focus-visible:ring-ring focus-visible:ring-offset-2 disabled:cursor-not-allowed disabled:opacity-50 data-[invalid]:border-destructive data-[invalid]:ring-destructive', + displayName: 'SelectInput', +}) export const InputLike = uic('div', { baseClass: ` From ce5aa4e42c86ac25a1f576719888f7124889a7c2 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Thu, 14 Mar 2024 15:57:11 +0100 Subject: [PATCH 07/40] fix(playground): radio enum default value --- .../playground/admin/lib/components/form/inputs.tsx | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) diff --git a/packages/playground/admin/lib/components/form/inputs.tsx b/packages/playground/admin/lib/components/form/inputs.tsx index 21312d9ff5..4472f11ace 100644 --- a/packages/playground/admin/lib/components/form/inputs.tsx +++ b/packages/playground/admin/lib/components/form/inputs.tsx @@ -4,7 +4,7 @@ import { CheckboxInput, Input, RadioInput } from '../ui/input' import { cn } from '../../utils/cn' import { TextareaAutosize } from '../ui/textarea' import { FormLabelUI } from './ui' -import { FormCheckbox, FormCheckboxProps, FormFieldScope, FormInput, FormInputProps, FormLabel, FormRadioInput } from '@contember/react-form' +import { FormCheckbox, FormCheckboxProps, FormFieldScope, FormInput, FormInputProps, FormLabel, FormRadioInput, FormRadioItemProps } from '@contember/react-form' import { FormContainer, FormContainerProps } from './container' import { Component, Field, SugaredRelativeSingleField } from '@contember/interface' @@ -71,22 +71,22 @@ export const CheckboxField = Component(({ field, label, description, inputProps, export type RadioEnumFieldProps = + & Omit & Omit & { - field: SugaredRelativeSingleField['field'] options: Record orientation?: 'horizontal' | 'vertical' inputProps?: Omit, 'defaultValue'> } -export const RadioEnumField = Component(({ field, label, description, options, inputProps, orientation }) => { +export const RadioEnumField = Component(({ field, label, description, options, inputProps, orientation, defaultValue, isNonbearing }) => { return (
{Object.entries(options).map(([value, label]) => ( - + {label} @@ -96,6 +96,4 @@ export const RadioEnumField = Component(({ field, label, de ) -}, ({ field }) => { - return }) From bdf03fc6c4c0c8537d43b8ed6739031f48ae9e32 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Thu, 14 Mar 2024 16:10:17 +0100 Subject: [PATCH 08/40] feat(react-dataview): debounce input --- .../text/DataViewTextFilterInput.tsx | 5 +-- .../text/useDataViewTextFilterInput.ts | 32 ++++++++++++++----- 2 files changed, 27 insertions(+), 10 deletions(-) diff --git a/packages/react-dataview/src/components/filtering/text/DataViewTextFilterInput.tsx b/packages/react-dataview/src/components/filtering/text/DataViewTextFilterInput.tsx index f7ada86091..c9aacdb0d9 100644 --- a/packages/react-dataview/src/components/filtering/text/DataViewTextFilterInput.tsx +++ b/packages/react-dataview/src/components/filtering/text/DataViewTextFilterInput.tsx @@ -5,9 +5,10 @@ import { useDataViewTextFilterInput } from '../../../hooks' const SlotInput = Slot as ComponentType> -export const DataViewTextFilterInput = ({ name, ...props }: { +export const DataViewTextFilterInput = ({ name, debounceMs, ...props }: { name: string + debounceMs?: number children: ReactElement }) => { - return + return } diff --git a/packages/react-dataview/src/hooks/filters/text/useDataViewTextFilterInput.ts b/packages/react-dataview/src/hooks/filters/text/useDataViewTextFilterInput.ts index c355a67617..521f0f4e6f 100644 --- a/packages/react-dataview/src/hooks/filters/text/useDataViewTextFilterInput.ts +++ b/packages/react-dataview/src/hooks/filters/text/useDataViewTextFilterInput.ts @@ -1,4 +1,4 @@ -import { ChangeEvent, useCallback } from 'react' +import { ChangeEvent, useCallback, useRef, useState } from 'react' import { useDataViewFilter } from '../../useDataViewFilter' import { TextFilterArtifacts } from '../../../filterTypes' @@ -7,17 +7,33 @@ export interface UseDataViewTextFilterInputResult { onChange: (e: ChangeEvent) => void } -export const useDataViewTextFilterInput = (name: string): UseDataViewTextFilterInputResult => { +export const useDataViewTextFilterInput = ({ name, debounceMs = 500 }: { name: string, debounceMs?: number }): UseDataViewTextFilterInputResult => { const [state, setFilter] = useDataViewFilter(name) + const [value, setValue] = useState(state?.query ?? '') + const timerRef = useRef>() + const onChange = useCallback((e: ChangeEvent) => { - setFilter(it => ({ - ...it, - query: e.target.value, - })) - }, [setFilter]) + if (debounceMs && e.target.value) { + timerRef.current && clearTimeout(timerRef.current) + timerRef.current = setTimeout(() => { + setFilter(it => ({ + ...it, + query: e.target.value, + })) + }, debounceMs) + + } else { + setFilter(it => ({ + ...it, + query: e.target.value, + })) + } + setValue(e.target.value) + + }, [debounceMs, setFilter]) return { - value: state?.query ?? '', + value, onChange, } } From f1cc90822683a1d8a66e3018486676f4322bfd1c Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 15 Mar 2024 15:08:58 +0100 Subject: [PATCH 09/40] refactor(react-dataview): drop graphql literals in filter resolver --- packages/react-datagrid/src/grid/createDataGrid.tsx | 6 +++++- .../src/internal/hooks/useDataViewPaging.ts | 2 +- .../src/internal/hooks/useDataViewResolvedFilters.ts | 10 ++++++++-- .../src/internal/hooks/useDataViewTotalCount.ts | 8 ++------ packages/react-dataview/src/types/filtering.ts | 2 +- 5 files changed, 17 insertions(+), 11 deletions(-) diff --git a/packages/react-datagrid/src/grid/createDataGrid.tsx b/packages/react-datagrid/src/grid/createDataGrid.tsx index ef3210b31f..ec3cad76d2 100644 --- a/packages/react-datagrid/src/grid/createDataGrid.tsx +++ b/packages/react-datagrid/src/grid/createDataGrid.tsx @@ -5,6 +5,7 @@ import { ControlledDataGridProps, createControlledDataGrid } from './createContr import { extractDataGridColumns } from '../internal/gridTemplateAnalyzer' import { useDataGrid } from './useDataGrid' import { DataGridColumnsContext } from '../internal/contexts' +import { replaceGraphQlLiteral } from '@contember/client' export type DataGridProps

= & { @@ -53,7 +54,7 @@ const dummyStateMethods: DataGridMethods = { const createInitialState = (props: DataGridProps<{}>, environment: Environment): DataGridState => { const entities = QueryLanguage.desugarQualifiedEntityList({ entities: props.entities }, environment) - const filter: Filter = { and: [entities.filter ?? {}] } + const filter = resolveFilter({ and: [entities.filter ?? {}] }) return { key: '_', paging: { @@ -76,3 +77,6 @@ const createInitialState = (props: DataGridProps<{}>, environment: Environment): entities: entities, } } +const resolveFilter = (input?: Filter): Filter => { + return replaceGraphQlLiteral(input) as Filter +} diff --git a/packages/react-dataview/src/internal/hooks/useDataViewPaging.ts b/packages/react-dataview/src/internal/hooks/useDataViewPaging.ts index c34c55fabe..739a5f2cd4 100644 --- a/packages/react-dataview/src/internal/hooks/useDataViewPaging.ts +++ b/packages/react-dataview/src/internal/hooks/useDataViewPaging.ts @@ -7,7 +7,7 @@ import { Filter, QualifiedEntityList } from '@contember/binding' type UseDataViewPagingArgs = & { dataViewKey?: string - filter: Filter + filter: Filter entities: QualifiedEntityList } & DataViewPagingProps diff --git a/packages/react-dataview/src/internal/hooks/useDataViewResolvedFilters.ts b/packages/react-dataview/src/internal/hooks/useDataViewResolvedFilters.ts index 655e67ee66..b58188215f 100644 --- a/packages/react-dataview/src/internal/hooks/useDataViewResolvedFilters.ts +++ b/packages/react-dataview/src/internal/hooks/useDataViewResolvedFilters.ts @@ -2,6 +2,7 @@ import { Filter, QualifiedEntityList } from '@contember/binding' import { useMemo } from 'react' import { useEnvironment } from '@contember/react-binding' import { DataViewFilterHandlerRegistry, DataViewFilteringArtifacts } from '../../types' +import { replaceGraphQlLiteral } from '@contember/client' export type UseDataViewResolvedFiltersArgs = { filters: DataViewFilteringArtifacts @@ -31,7 +32,12 @@ export const useDataViewResolvedFilters = ({ return ands }, [environment, filters, filterTypes]) - return useMemo((): Filter => { - return { and: [...customFilters, entities.filter ?? {}] } + return useMemo((): Filter => { + return resolveFilter({ and: [...customFilters, entities.filter ?? {}] }) }, [entities.filter, customFilters]) } + + +const resolveFilter = (input?: Filter): Filter => { + return replaceGraphQlLiteral(input) as Filter +} diff --git a/packages/react-dataview/src/internal/hooks/useDataViewTotalCount.ts b/packages/react-dataview/src/internal/hooks/useDataViewTotalCount.ts index 0e8baa001c..2cc5bc614b 100644 --- a/packages/react-dataview/src/internal/hooks/useDataViewTotalCount.ts +++ b/packages/react-dataview/src/internal/hooks/useDataViewTotalCount.ts @@ -7,7 +7,7 @@ import { useAbortController } from '@contember/react-utils' export type UseDataViewTotalCountArgs = & { entities: QualifiedEntityList - filter: Filter + filter: Filter } export const useDataViewTotalCount = ({ entities: { entityName }, filter }: UseDataViewTotalCountArgs): number | undefined => { @@ -24,7 +24,7 @@ export const useDataViewTotalCount = ({ entities: { entityName }, filter }: UseD const contentClient = new ContentClient(client) const qb = createQueryBuilder(schema) const query = qb.count(entityName, { - filter: resolveFilter(filter), + filter, }) try { const result = await contentClient.query(query, { @@ -44,7 +44,3 @@ export const useDataViewTotalCount = ({ entities: { entityName }, filter }: UseD return count } - -const resolveFilter = (input?: Filter): Filter => { - return replaceGraphQlLiteral(input) as Filter -} diff --git a/packages/react-dataview/src/types/filtering.ts b/packages/react-dataview/src/types/filtering.ts index dd4ad3548d..896b4f6d57 100644 --- a/packages/react-dataview/src/types/filtering.ts +++ b/packages/react-dataview/src/types/filtering.ts @@ -32,7 +32,7 @@ export type DataViewFilterHandlerRegistry = Record export type DataViewFilteringState = { artifact: DataViewFilteringArtifacts - filter: Filter + filter: Filter filterTypes: DataViewFilterHandlerRegistry } From a14a3175e6edaa5a7ec853092fdca5930ebaac82 Mon Sep 17 00:00:00 2001 From: David Matejka Date: Fri, 15 Mar 2024 15:18:36 +0100 Subject: [PATCH 10/40] refactor(playground): rename datagrid components --- packages/playground/admin/app/pages/grid.tsx | 24 +++--- .../lib/components/datagrid/column-header.tsx | 2 +- .../admin/lib/components/datagrid/columns.tsx | 81 +++++++++---------- .../admin/lib/components/datagrid/empty.tsx | 2 +- .../components/datagrid/filters/boolean.tsx | 30 +++---- .../components/datagrid/filters/common.tsx | 10 +-- .../lib/components/datagrid/filters/date.tsx | 42 +++++----- .../lib/components/datagrid/filters/enum.tsx | 64 ++++++--------- .../components/datagrid/filters/number.tsx | 34 ++++---- .../components/datagrid/filters/relation.tsx | 52 ++++++------ .../lib/components/datagrid/filters/text.tsx | 14 ++-- .../admin/lib/components/datagrid/grid.tsx | 77 ++++++++++-------- .../components/datagrid/layout-switcher.tsx | 2 +- .../admin/lib/components/datagrid/loader.tsx | 4 +- .../lib/components/datagrid/pagination.tsx | 8 +- .../admin/lib/components/datagrid/table.tsx | 12 +-- .../admin/lib/components/datagrid/ui.tsx | 22 +++-- .../admin/lib/components/select/list.tsx | 4 +- packages/playground/admin/lib/dict.ts | 2 +- 19 files changed, 241 insertions(+), 245 deletions(-) diff --git a/packages/playground/admin/app/pages/grid.tsx b/packages/playground/admin/app/pages/grid.tsx index 72770e81cf..11b596daf0 100644 --- a/packages/playground/admin/app/pages/grid.tsx +++ b/packages/playground/admin/app/pages/grid.tsx @@ -1,7 +1,7 @@ import { Component, Field, HasMany } from '@contember/interface' import { Slots } from '../../lib/components/slots' import { DataViewHasSelection } from '@contember/react-dataview' -import { DataViewColumns, DataViewRelationFieldTooltip, DefaultDataGrid } from '../../lib/components/datagrid' +import { DataGrid, DataGridColumns, DataGridRelationFieldTooltip } from '../../lib/components/datagrid' import * as React from 'react' import { DefaultDropdown, DropdownMenuItem, DropdownMenuSeparator } from '../../lib/components/ui/dropdown' import { Binding, DeleteEntityDialog } from '../../lib/components/binding' @@ -33,9 +33,9 @@ const GridTile = Component(() => (

- + - +
@@ -52,19 +52,19 @@ export default () => ( - } lastColumnActions={} columns={[ - DataViewColumns.text({ field: 'title', label: 'Title' }), - DataViewColumns.enum({ field: 'state', label: 'State', options: GridArticleStateLabels }), - DataViewColumns.date({ field: 'publishedAt', label: 'Published at' }), - DataViewColumns.hasOne({ field: 'author', valueField: 'name', label: 'Author', filterOptions: 'GridAuthor' }), - DataViewColumns.hasOne({ field: 'category', valueField: 'name', label: 'Category', filterOptions: 'GridCategory' }), - DataViewColumns.hasMany({ field: 'tags', valueField: 'name', label: 'Tags', filterOptions: 'GridTag' }), - DataViewColumns.boolean({ field: 'locked', label: 'Locked' }), - DataViewColumns.number({ field: 'views', label: 'Views' }), + DataGridColumns.text({ field: 'title', label: 'Title' }), + DataGridColumns.enum({ field: 'state', label: 'State', options: GridArticleStateLabels }), + DataGridColumns.date({ field: 'publishedAt', label: 'Published at' }), + DataGridColumns.hasOne({ field: 'author', valueField: 'name', label: 'Author' }), + DataGridColumns.hasOne({ field: 'category', valueField: 'name', label: 'Category' }), + DataGridColumns.hasMany({ field: 'tags', valueField: 'name', label: 'Tags' }), + DataGridColumns.boolean({ field: 'locked', label: 'Locked' }), + DataGridColumns.number({ field: 'views', label: 'Views' }), ]} /> diff --git a/packages/playground/admin/lib/components/datagrid/column-header.tsx b/packages/playground/admin/lib/components/datagrid/column-header.tsx index e28831a36f..a07087884b 100644 --- a/packages/playground/admin/lib/components/datagrid/column-header.tsx +++ b/packages/playground/admin/lib/components/datagrid/column-header.tsx @@ -7,7 +7,7 @@ import { DataViewSelectionTrigger, DataViewSortingSwitch, DataViewSortingTrigger import { dict } from '../../dict' -export function DataViewColumnHeader({ sortingField, hidingName, children }: { +export function DataGridColumnHeader({ sortingField, hidingName, children }: { sortingField?: string, hidingName?: string, children: ReactNode, diff --git a/packages/playground/admin/lib/components/datagrid/columns.tsx b/packages/playground/admin/lib/components/datagrid/columns.tsx index 7d1a4e1191..d0a3abc1c0 100644 --- a/packages/playground/admin/lib/components/datagrid/columns.tsx +++ b/packages/playground/admin/lib/components/datagrid/columns.tsx @@ -1,26 +1,10 @@ import * as React from 'react' import { ReactNode } from 'react' -import { - DataViewEnumFieldTooltip, - DataViewRelationFieldTooltip, - DataViewTextFilter, DefaultDataViewBooleanFilter, DefaultDataViewDateFilter, - DefaultDataViewEnumFilter, - DefaultDataViewNumberFilter, - DefaultDataViewRelationFilter, -} from './filters' +import { DataGridBooleanFilter, DataGridDateFilter, DataGridEnumFieldTooltip, DataGridEnumFilter, DataGridNumberFilter, DataGridRelationFieldTooltip, DataGridRelationFilter, DataGridTextFilter } from './filters' import { formatBoolean, formatDate, formatNumber } from '../../utils/formatting' -import { DataViewColumn } from './grid' +import { DataGridColumn } from './grid' import { Field, HasMany, HasOne } from '@contember/react-binding' -import { - createBooleanFilter, - createDateFilter, - createEnumFilter, - createHasManyFilter, - createHasOneFilter, - createNumberRangeFilter, - createTextFilter, - DataViewFilterHandler, -} from '@contember/react-dataview' +import { createBooleanFilter, createDateFilter, createEnumFilter, createHasManyFilter, createHasOneFilter, createNumberRangeFilter, createTextFilter, DataViewFilterHandler } from '@contember/react-dataview' import { SugaredQualifiedEntityList } from '@contember/interface' export interface DataViewColumnCommonArgs { @@ -40,16 +24,17 @@ export type DataViewTextColumnArgs = sortingField?: string } -export const createTextColumn = ({ field, label, ...args }: DataViewTextColumnArgs): DataViewColumn => { +export const createTextColumn = ({ field, label, ...args }: DataViewTextColumnArgs): DataGridColumn => { return { + type: 'text', + field, filterName: field, cell: , header: label, hidingName: field, sortingField: field, filterHandler: createTextFilter(field), - filterToolbar: , ...args, } } @@ -72,19 +57,21 @@ export type DataViewHasOneColumnArgs = sortingField?: string } -export const createHasOneColumn = ({ field, label, tooltipActions, filterOptions, valueField, value, filterLabel, filterField, filterOption, ...args }: DataViewHasOneColumnArgs): DataViewColumn => { +export const createHasOneColumn = ({ field, label, tooltipActions, filterOptions, valueField, value, filterLabel, filterField, filterOption, ...args }: DataViewHasOneColumnArgs): DataGridColumn => { value ??= valueField ? : null filterOption ??= value return { + type: 'hasOne', + field, filterName: field, cell: (
- + {value} - +
), @@ -93,14 +80,14 @@ export const createHasOneColumn = ({ field, label, tooltipActions, filterOptions sortingField: valueField ? field + '.' + valueField : undefined, filterHandler: createHasOneFilter(field), filterToolbar: filterOptions && ( - {filterOption} - + ), ...args, } @@ -111,17 +98,19 @@ export type DataViewHasManyColumnArgs = & DataViewRelationColumnArgs -export const createHasManyColumn = ({ field, label, tooltipActions, filterOptions, valueField, value, filterLabel, filterField, filterOption, ...args }: DataViewHasManyColumnArgs): DataViewColumn => { +export const createHasManyColumn = ({ field, label, tooltipActions, filterOptions, valueField, value, filterLabel, filterField, filterOption, ...args }: DataViewHasManyColumnArgs): DataGridColumn => { value ??= filterOption ??= value return { + type: 'hasMany', + field, filterName: field, cell: (
- + {value} - +
), @@ -129,14 +118,14 @@ export const createHasManyColumn = ({ field, label, tooltipActions, filterOption hidingName: field, filterHandler: createHasManyFilter(field), filterToolbar: filterOptions && ( - {filterOption} - + ), ...args, } @@ -148,15 +137,17 @@ export type DataViewNumberColumnArgs = sortingField?: string } -export const createNumberColumn = ({ field, label, ...args }: DataViewNumberColumnArgs): DataViewColumn => { +export const createNumberColumn = ({ field, label, ...args }: DataViewNumberColumnArgs): DataGridColumn => { return { + type: 'number', + field, filterName: field, cell: , header: label, hidingName: field, sortingField: field, filterHandler: createNumberRangeFilter(field), - filterToolbar: , + filterToolbar: , ...args, } } @@ -167,15 +158,17 @@ export type DataViewDateColumnArgs = sortingField?: string } -export const createDateColumn = ({ field, label, ...args }: DataViewDateColumnArgs): DataViewColumn => { +export const createDateColumn = ({ field, label, ...args }: DataViewDateColumnArgs): DataGridColumn => { return { + type: 'date', + field, filterName: field, cell: , header: label, hidingName: field, sortingField: field, filterHandler: createDateFilter(field), - filterToolbar: , + filterToolbar: , ...args, } } @@ -186,16 +179,18 @@ export type DataViewBooleanColumnArgs = sortingField?: string } -export const createBooleanColumn = ({ field, label, ...args }: DataViewBooleanColumnArgs): DataViewColumn => { +export const createBooleanColumn = ({ field, label, ...args }: DataViewBooleanColumnArgs): DataGridColumn => { return { + type: 'boolean', + field, filterName: field, cell: , header: label, hidingName: field, sortingField: field, filterHandler: createBooleanFilter(field), - filterToolbar: , + filterToolbar: , ...args, } } @@ -208,15 +203,17 @@ export type DataViewEnumColumnArgs = filterLabel?: ReactNode } -export const createEnumColumn = ({ field, label, options, filterLabel, ...args }: DataViewEnumColumnArgs): DataViewColumn => { +export const createEnumColumn = ({ field, label, options, filterLabel, ...args }: DataViewEnumColumnArgs): DataGridColumn => { return { + type: 'enum', + field, filterName: field, cell: (
field={field} format={it => it ? ( - + {options[it]} - + ) : null} />
), @@ -225,7 +222,7 @@ export const createEnumColumn = ({ field, label, options, filterLabel, ...args } sortingField: field, filterHandler: createEnumFilter(field), filterToolbar: ( - ( +export const DataGridNoResults = () => (
{dict.datagrid.empty}
diff --git a/packages/playground/admin/lib/components/datagrid/filters/boolean.tsx b/packages/playground/admin/lib/components/datagrid/filters/boolean.tsx index e6780bc5f6..e136d37523 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/boolean.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/boolean.tsx @@ -2,40 +2,40 @@ import * as React from 'react' import { ReactNode } from 'react' import { DataViewBooleanFilterTrigger, DataViewNullFilterTrigger } from '@contember/react-dataview' import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover' -import { DataViewActiveFilterUI, DataViewFilterSelectTriggerUI, DataViewSingleFilterUI } from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI, DataGridFilterSelectTriggerUI, DataGridSingleFilterUI } from '../ui' +import { DataGridNullFilter } from './common' import { formatBoolean } from '../../../utils/formatting' import { Button } from '../../ui/button' import { dict } from '../../../dict' -export const DataViewBooleanFilterList = ({ name }: { +export const DataGridBooleanFilterList = ({ name }: { name: string }) => ( <> {[true, false].map(value => ( - + {formatBoolean(value)} - + ))} - + {dict.datagrid.na} - + ) -export const DataViewBooleanFilterSelect = ({ name, label }: { +export const DataGridBooleanFilterSelect = ({ name, label }: { name: string label?: ReactNode }) => ( - {label} + {label}
@@ -52,19 +52,19 @@ export const DataViewBooleanFilterSelect = ({ name, label }: { ))}
- +
) -export const DefaultDataViewBooleanFilter = ({ name, label }: { +export const DataGridBooleanFilter = ({ name, label }: { name: string label: ReactNode }) => ( - - - - + + + + ) diff --git a/packages/playground/admin/lib/components/datagrid/filters/common.tsx b/packages/playground/admin/lib/components/datagrid/filters/common.tsx index fbf609f186..c6307b1303 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/common.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/common.tsx @@ -1,10 +1,10 @@ import * as React from 'react' -import { ReactEventHandler, useCallback } from 'react' +import { useCallback } from 'react' import { useDataViewNullFilter } from '@contember/react-dataview' -import { DataViewFilterSelectItemUI } from '../ui' +import { DataGridFilterSelectItemUI } from '../ui' import { dict } from '../../../dict' -export const DataViewNullFilter = ({ name }: { +export const DataGridNullFilter = ({ name }: { name: string }) => { @@ -13,7 +13,7 @@ export const DataViewNullFilter = ({ name }: { const toggleIncludeNull = useCallback(() => setNullFilter('toggleInclude'), [setNullFilter]) return <> - {dict.datagrid.na} - + } diff --git a/packages/playground/admin/lib/components/datagrid/filters/date.tsx b/packages/playground/admin/lib/components/datagrid/filters/date.tsx index f2d52e2d4b..f16dea8388 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/date.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/date.tsx @@ -1,21 +1,15 @@ import * as React from 'react' import { ReactNode } from 'react' -import { - DataViewDateFilterInput, - DataViewDateFilterResetTrigger, - DataViewNullFilterTrigger, - DateRangeFilterArtifacts, - useDataViewFilter, -} from '@contember/react-dataview' +import { DataViewDateFilterInput, DataViewDateFilterResetTrigger, DataViewNullFilterTrigger, DateRangeFilterArtifacts, useDataViewFilter } from '@contember/react-dataview' import { Component } from '@contember/interface' import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover' -import { DataViewActiveFilterUI, DataViewFilterSelectTriggerUI, DataViewSingleFilterUI } from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI, DataGridFilterSelectTriggerUI, DataGridSingleFilterUI } from '../ui' import { Input } from '../../ui/input' import { formatDate } from '../../../utils/formatting' import { dict } from '../../../dict' +import { DataGridNullFilter } from './common' -const DataViewDateFilterRange = ({ name }: { name: string }) => { +const DataGridDateFilterRange = ({ name }: { name: string }) => { const [artifact] = useDataViewFilter(name) if (!artifact) { return null @@ -35,32 +29,32 @@ const DataViewDateFilterRange = ({ name }: { name: string }) => { return undefined } -const DataViewDateFilterList = ({ name }: { +const DataGridDateFilterList = ({ name }: { name: string }) => ( <> - - - + + + - + {dict.datagrid.na} - + ) -const DataViewDateFilterSelect = ({ name, label }: { +const DataGridDateFilterSelect = ({ name, label }: { name: string label?: ReactNode }) => ( - {label} + {label}
@@ -75,21 +69,21 @@ const DataViewDateFilterSelect = ({ name, label }: {
- +
) -export const DefaultDataViewDateFilter = Component(({ name, label }: { +export const DataGridDateFilter = Component(({ name, label }: { name: string label: ReactNode }) => { return ( - - - - + + + + ) }, () => null) diff --git a/packages/playground/admin/lib/components/datagrid/filters/enum.tsx b/packages/playground/admin/lib/components/datagrid/filters/enum.tsx index b79987e16e..f61093a52c 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/enum.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/enum.tsx @@ -1,29 +1,15 @@ import * as React from 'react' -import { ReactEventHandler, ReactNode, useCallback } from 'react' +import { ReactNode, useCallback } from 'react' import { Tooltip, TooltipContent, TooltipProvider, TooltipTrigger } from '../../ui/tooltip' import { Button } from '../../ui/button' -import { - DataViewEnumFilterTrigger, - DataViewNullFilterTrigger, - EnumFilterArtifacts, - UseDataViewEnumFilter, - useDataViewEnumFilterFactory, - useDataViewFilter, -} from '@contember/react-dataview' +import { DataViewEnumFilterTrigger, DataViewNullFilterTrigger, UseDataViewEnumFilter, useDataViewEnumFilterFactory } from '@contember/react-dataview' import { Component } from '@contember/interface' import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover' -import { - DataViewActiveFilterUI, - DataViewExcludeActionButtonUI, - DataViewFilterActionButtonUI, - DataViewFilterSelectItemUI, - DataViewFilterSelectTriggerUI, - DataViewSingleFilterUI, -} from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI, DataGridExcludeActionButtonUI, DataGridFilterActionButtonUI, DataGridFilterSelectItemUI, DataGridFilterSelectTriggerUI, DataGridSingleFilterUI } from '../ui' +import { DataGridNullFilter } from './common' import { dict } from '../../../dict' -export const DataViewEnumFieldTooltip = ({ filter, children, actions, value }: { filter: string, children: ReactNode, value: string, actions?: ReactNode }) => ( +export const DataGridEnumFieldTooltip = ({ filter, children, actions, value }: { filter: string, children: ReactNode, value: string, actions?: ReactNode }) => ( @@ -34,10 +20,10 @@ export const DataViewEnumFieldTooltip = ({ filter, children, actions, value }: {
- + - + {actions}
@@ -46,29 +32,29 @@ export const DataViewEnumFieldTooltip = ({ filter, children, actions, value }: {
) -const DataViewEnumFilterList = ({ name, options }: { +const DataGridEnumFilterList = ({ name, options }: { name: string options: Record }) => ( <> {Object.entries(options).map(([value, label]) => ( - + {label} - + ))} - + {dict.datagrid.na} - + ) -const DataViewEnumFilterSelectItem = ({ value, children, filterFactory }: { +const DataGridEnumFilterSelectItem = ({ value, children, filterFactory }: { value: string children: ReactNode filterFactory: (value: string) => UseDataViewEnumFilter @@ -81,13 +67,13 @@ const DataViewEnumFilterSelectItem = ({ value, children, filterFactory }: { const isExcluded = current == 'exclude' return ( - + {children} - + ) } -const DataViewEnumFilterSelect = ({ name, options, label }: { +const DataGridEnumFilterSelect = ({ name, options, label }: { name: string options: Record label?: ReactNode @@ -97,16 +83,16 @@ const DataViewEnumFilterSelect = ({ name, options, label }: { return ( - {label} + {label}
{Object.entries(options).map(([value, label]) => ( - + {label} - + ))} - +
@@ -114,13 +100,13 @@ const DataViewEnumFilterSelect = ({ name, options, label }: { } -export const DefaultDataViewEnumFilter = Component(({ name, options, label }: { +export const DataGridEnumFilter = Component(({ name, options, label }: { name: string options: Record label: ReactNode }) => ( - - - - + + + + ), () => null) diff --git a/packages/playground/admin/lib/components/datagrid/filters/number.tsx b/packages/playground/admin/lib/components/datagrid/filters/number.tsx index 5a63e41733..9d02a7e755 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/number.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/number.tsx @@ -8,13 +8,13 @@ import { useDataViewFilter, } from '@contember/react-dataview' import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover' -import { DataViewActiveFilterUI, DataViewFilterSelectTriggerUI, DataViewSingleFilterUI } from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI, DataGridFilterSelectTriggerUI, DataGridSingleFilterUI } from '../ui' +import { DataGridNullFilter } from './common' import { Input } from '../../ui/input' import { formatNumber } from '../../../utils/formatting' import { dict } from '../../../dict' -const DataViewNumberFilterRange = ({ name }: { +const DataGridNumberFilterRange = ({ name }: { name: string }) => { const [artifact] = useDataViewFilter(name) @@ -34,32 +34,32 @@ const DataViewNumberFilterRange = ({ name }: { } -export const DataViewNumberFilterList = ({ name }: { +export const DataGridNumberFilterList = ({ name }: { name: string }) => ( <> - - - + + + - + {dict.datagrid.na} - + ) -export const DataViewNumberFilterSelect = ({ name, label }: { +export const DataGridNumberFilterSelect = ({ name, label }: { name: string label?: ReactNode }) => ( - {label} + {label}
@@ -75,19 +75,19 @@ export const DataViewNumberFilterSelect = ({ name, label }: {
- +
) -export const DefaultDataViewNumberFilter = ({ name, label }: { +export const DataGridNumberFilter = ({ name, label }: { name: string label: ReactNode }) => ( - - - - + + + + ) diff --git a/packages/playground/admin/lib/components/datagrid/filters/relation.tsx b/packages/playground/admin/lib/components/datagrid/filters/relation.tsx index ccb576ee8f..f653e1bdda 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/relation.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/relation.tsx @@ -5,13 +5,13 @@ import { Button } from '../../ui/button' import { createCoalesceFilter, DataView, DataViewNullFilterTrigger, DataViewRelationFilterList, DataViewRelationFilterTrigger, useDataViewRelationFilterFactory, UseDataViewRelationFilterResult } from '@contember/react-dataview' import { Component, EntityId, SugarableQualifiedEntityList, SugaredQualifiedEntityList, useEntity } from '@contember/interface' import { Popover, PopoverTrigger } from '../../ui/popover' -import { DataViewActiveFilterUI, DataViewExcludeActionButtonUI, DataViewFilterActionButtonUI, DataViewFilterSelectItemUI, DataViewFilterSelectTriggerUI, DataViewSingleFilterUI } from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI, DataGridExcludeActionButtonUI, DataGridFilterActionButtonUI, DataGridFilterSelectItemUI, DataGridFilterSelectTriggerUI, DataGridSingleFilterUI } from '../ui' +import { DataGridNullFilter } from './common' import { SelectDefaultFilter, SelectListInner, SelectPopoverContent } from '../../select' import { dict } from '../../../dict' import { SelectFilterFieldProps } from '@contember/react-select' -export const DataViewRelationFieldTooltip = ({ filter, children, actions }: { filter: string, children: ReactNode, actions?: ReactNode }) => ( +export const DataGridRelationFieldTooltip = ({ filter, children, actions }: { filter: string, children: ReactNode, actions?: ReactNode }) => ( @@ -22,10 +22,10 @@ export const DataViewRelationFieldTooltip = ({ filter, children, actions }: { fi
- + - + {actions}
@@ -34,7 +34,7 @@ export const DataViewRelationFieldTooltip = ({ filter, children, actions }: { fi
) -const DataViewRelationFilteredItemsList = ({ name, children, options }: { +const DataGridRelationFilteredItemsList = ({ name, children, options }: { name: string options: SugaredQualifiedEntityList['entities'] children: ReactNode @@ -42,20 +42,20 @@ const DataViewRelationFilteredItemsList = ({ name, children, options }: { <> - + {children} - + - + {dict.datagrid.na} - + ) -const DataViewRelationFilterSelectItem = forwardRef UseDataViewRelationFilterResult }>(({ children, filterFactory, ...props }, ref) => { @@ -69,14 +69,14 @@ const DataViewRelationFilterSelectItem = forwardRef + {children} - + ) }) -const DataViewRelationFilterSelect = ({ name, children, options, filterField, label }: SelectFilterFieldProps & { +const DataGridRelationFilterSelect = ({ name, children, options, filterField, label }: SelectFilterFieldProps & { name: string options: string | SugarableQualifiedEntityList children: ReactNode @@ -87,9 +87,9 @@ const DataViewRelationFilterSelect = ({ name, children, options, filterField, la return ( - + {label} - + { @@ -97,13 +97,13 @@ const DataViewRelationFilterSelect = ({ name, children, options, filterField, la set('toggleInclude') }}> }> - + {children} - +
- +
@@ -111,7 +111,7 @@ const DataViewRelationFilterSelect = ({ name, children, options, filterField, la } -export const DefaultDataViewRelationFilter = Component(({ name, options, children, label, filterField }: { +export const DataGridRelationFilter = Component(({ name, options, children, label, filterField }: { name: string options: SugaredQualifiedEntityList['entities'] children: ReactNode @@ -119,13 +119,13 @@ export const DefaultDataViewRelationFilter = Component(({ name, options, childre filterField?: string }) => { return ( - - + + {children} - - + + {children} - - + + ) }, () => null) diff --git a/packages/playground/admin/lib/components/datagrid/filters/text.tsx b/packages/playground/admin/lib/components/datagrid/filters/text.tsx index da77ed3708..f3be974d97 100644 --- a/packages/playground/admin/lib/components/datagrid/filters/text.tsx +++ b/packages/playground/admin/lib/components/datagrid/filters/text.tsx @@ -5,14 +5,14 @@ import { MoreHorizontalIcon, XIcon } from 'lucide-react' import * as React from 'react' import { DataViewNullFilterTrigger, DataViewTextFilterInput, DataViewTextFilterMatchModeLabel, DataViewTextFilterMatchModeTrigger, DataViewTextFilterResetTrigger, TextFilterArtifactsMatchMode } from '@contember/react-dataview' import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover' -import { DataViewActiveFilterUI } from '../ui' -import { DataViewNullFilter } from './common' +import { DataGridActiveFilterUI } from '../ui' +import { DataGridNullFilter } from './common' import { dict } from '../../../../lib/dict' -export const DataViewTextFilter = ({ name, label }: { +export const DataGridTextFilter = ({ name, label }: { name: string - label: React.ReactNode + label?: React.ReactNode }) => ( <> @@ -39,9 +39,9 @@ export const DataViewTextFilter = ({ name, label }: {
- + {dict.datagrid.na} - + @@ -55,7 +55,7 @@ export const DataViewTextFilter = ({ name, label }: { {dict.datagrid.textReset} - +
diff --git a/packages/playground/admin/lib/components/datagrid/grid.tsx b/packages/playground/admin/lib/components/datagrid/grid.tsx index 33d6b557c2..9086e1a56e 100644 --- a/packages/playground/admin/lib/components/datagrid/grid.tsx +++ b/packages/playground/admin/lib/components/datagrid/grid.tsx @@ -1,8 +1,10 @@ import { + createCoalesceFilter, DataView, DataViewEachRow, DataViewEmpty, DataViewFilterHandler, + DataViewHasFilterType, DataViewHasSelection, DataViewLoaderState, DataViewNonEmpty, @@ -15,30 +17,35 @@ import { Fragment, ReactNode, useMemo } from 'react' import { dict } from '../../dict' import { Button } from '../ui/button' import { DropdownMenu, DropdownMenuContent, DropdownMenuItem, DropdownMenuTrigger } from '../ui/dropdown' -import { DataViewNoResults } from './empty' -import { DataViewLayoutSwitcher } from './layout-switcher' -import { DataViewInitialLoader, DataViewLoaderOverlay } from './loader' -import { DataTablePagination } from './pagination' -import { DataViewTable, DataViewTableColumn } from './table' - -export type DataViewColumn = - & DataViewTableColumn +import { DataGridNoResults } from './empty' +import { DataGridLayoutSwitcher } from './layout-switcher' +import { DataGridInitialLoader, DataGridOverlayLoader } from './loader' +import { DataGridPagination } from './pagination' +import { DataGridTable, DataGridTableColumn } from './table' +import { DataGridToolbarUI } from './ui' +import { DataGridAutoExport } from './export' + +export type DataGridColumn = + & DataGridTableColumn & { + type: 'text' | 'hasOne' | 'hasMany' | 'boolean' | 'number' | 'enum' | 'date' + field: string filterName?: string filterHandler?: DataViewFilterHandler filterToolbar?: ReactNode } -export type DefaultDataGridProps = +export type DataGridProps = & Omit & { - columns: DataViewColumn[] + columns: DataGridColumn[] tile?: ReactNode firstColumnActions?: ReactNode lastColumnActions?: ReactNode + toolbarButtons?: ReactNode } -const DataGridToolbarFilters = ({ columns }: { columns: DataViewColumn[] }) => { +const DataGridToolbarFilters = ({ columns }: { columns: DataGridColumn[] }) => { return <> {columns .filter(it => it.filterToolbar && it.filterName) @@ -47,7 +54,7 @@ const DataGridToolbarFilters = ({ columns }: { columns: DataViewColumn[] }) => { } -const DataGridToolbarColumns = ({ columns }: { columns: DataViewColumn[] }) => { +const DataGridToolbarColumns = ({ columns }: { columns: DataGridColumn[] }) => { return