Skip to content

Commit

Permalink
Merge pull request #715 from contember/feat/date-filter-today
Browse files Browse the repository at this point in the history
date filter improvements
  • Loading branch information
matej21 authored Jun 4, 2024
2 parents 5e27d87 + 32c374c commit 2da747d
Show file tree
Hide file tree
Showing 5 changed files with 116 additions and 36 deletions.
3 changes: 2 additions & 1 deletion build/api/react-dataview.api.md
Original file line number Diff line number Diff line change
Expand Up @@ -188,12 +188,13 @@ export interface DataViewDateFilterProps {
}

// @public (undocumented)
export const DataViewDateFilterResetTrigger: ({ name, ...props }: DataViewDateFilterResetTriggerProps) => JSX_2.Element | null;
export const DataViewDateFilterResetTrigger: ({ name, type, ...props }: DataViewDateFilterResetTriggerProps) => JSX_2.Element | null;

// @public (undocumented)
export type DataViewDateFilterResetTriggerProps = {
name?: string;
children: ReactElement;
type?: 'start' | 'end';
};

// @internal (undocumented)
Expand Down
15 changes: 14 additions & 1 deletion packages/playground/admin/app/pages/grid.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Component, Field, HasMany, HasOne, If } from '@contember/interface'
import { Slots } from '../../lib/components/slots'
import { DataViewEachRow, DataViewElement, DataViewLayout } from '@contember/react-dataview'
import {
createDataGridDateRange,
DataGrid,
DataGridActionColumn,
DataGridBooleanColumn,
Expand All @@ -21,6 +22,7 @@ import {
DataGridNumberColumn,
DataGridNumberFilter,
DataGridPagination,
DataGridPredefinedDateRange,
DataGridQueryFilter,
DataGridTable,
DataGridTextColumn,
Expand All @@ -29,6 +31,7 @@ import {
DataGridTooltipLabel,
} from '../../lib/components/datagrid'
import * as React from 'react'
import { ReactNode } from 'react'
import { DefaultDropdown, DropdownMenuItem, DropdownMenuSeparator } from '../../lib/components/ui/dropdown'
import { Binding, DeleteEntityDialog } from '../../lib/components/binding'
import { GridArticleStateLabels } from '../labels'
Expand Down Expand Up @@ -167,7 +170,17 @@ const CustomGridFilters = Component(() => {
<>
<DataGridQueryFilter />
<DataGridEnumFilter field={'state'} options={GridArticleStateLabels} label="State" />
<DataGridDateFilter field={'publishedAt'} label="Published at" />
<DataGridDateFilter
field={'publishedAt'}
label="Published at"
ranges={[
createDataGridDateRange('Today', 0, 0),
createDataGridDateRange('Yesterday', -1, -1),
createDataGridDateRange('Last 7 days', -7, 0),
createDataGridDateRange('Last 30 days', -30, 0),
createDataGridDateRange('Last 90 days', -90, 0),
]}
/>
<DataGridHasOneFilter field={'author'} label="Author">
<Field field="name" />
</DataGridHasOneFilter>
Expand Down
112 changes: 86 additions & 26 deletions packages/playground/admin/lib/components/datagrid/filters/date.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import * as React from 'react'
import { ReactNode } from 'react'
import { ReactNode, useId } from 'react'
import { DataViewDateFilter, DataViewDateFilterInput, DataViewDateFilterProps, DataViewDateFilterResetTrigger, DataViewNullFilterTrigger, DateRangeFilterArtifacts, useDataViewFilter, useDataViewFilterName } from '@contember/react-dataview'
import { Component } from '@contember/interface'
import { Popover, PopoverContent, PopoverTrigger } from '../../ui/popover'
Expand All @@ -9,18 +9,22 @@ import { formatDate } from '../../../utils/formatting'
import { dict } from '../../../dict'
import { DataGridNullFilter } from './common'
import { DataGridFilterMobileHiding } from './mobile'
import { Button } from '../../ui/button'
import { Label } from '../../ui/label'
import { XIcon } from 'lucide-react'

export type DataGridDateFilterProps =
& Omit<DataViewDateFilterProps, 'children'>
& {
label: ReactNode
ranges?: DataGridPredefinedDateRange[]
}

export const DataGridDateFilter = Component(({ label, ...props }: DataGridDateFilterProps) => (
export const DataGridDateFilter = Component(({ label, ranges, ...props }: DataGridDateFilterProps) => (
<DataViewDateFilter {...props}>
<DataGridFilterMobileHiding>
<DataGridSingleFilterUI>
<DataGridDateFilterSelect label={label} />
<DataGridDateFilterSelect label={label} ranges={ranges} />
<DataGridDateFilterList />
</DataGridSingleFilterUI>
</DataGridFilterMobileHiding>
Expand Down Expand Up @@ -65,29 +69,85 @@ const DataGridDateFilterList = () => (
</>
)

export const createDataGridDateRange = (label: ReactNode, dayDeltaStart: number, dayDeltaEnd: number): DataGridPredefinedDateRange => {
const start = new Date(new Date().setDate(new Date().getDate() + dayDeltaStart))
const end = new Date(new Date().setDate(new Date().getDate() + dayDeltaEnd))
return {
label,
start: `${start.getFullYear()}-${String(start.getMonth() + 1).padStart(2, '0')}-${String(start.getDate()).padStart(2, '0')}`,
end: `${end.getFullYear()}-${String(end.getMonth() + 1).padStart(2, '0')}-${String(end.getDate()).padStart(2, '0')}`,
}
}

const DataGridDateFilterSelect = ({ label }: {
const defaultRanges = [createDataGridDateRange(dict.datagrid.today, 0, 0)]

const DataGridDateFilterSelect = ({ label, ranges = defaultRanges }: {
label?: ReactNode
}) => (
<Popover>
<PopoverTrigger asChild>
<DataGridFilterSelectTriggerUI>{label}</DataGridFilterSelectTriggerUI>
</PopoverTrigger>
<PopoverContent>
<div className={'relative flex flex-col gap-4'}>
<div className={'flex justify-center items-center'}>
<DataViewDateFilterInput type={'start'}>
<Input inputSize={'sm'} placeholder={dict.datagrid.dateStart} type={'date'} />
</DataViewDateFilterInput>
<span className={'mx-4 font-bold '}>
</span>
<DataViewDateFilterInput type={'end'}>
<Input inputSize={'sm'} placeholder={dict.datagrid.dateEnd} type={'date'} />
</DataViewDateFilterInput>
ranges?: DataGridPredefinedDateRange[]
}) => {
const id = useId()
return (
<Popover>
<PopoverTrigger asChild>
<DataGridFilterSelectTriggerUI>{label}</DataGridFilterSelectTriggerUI>
</PopoverTrigger>
<PopoverContent className="p-0">
<div className="flex">
{ranges?.length > 0 && <div className="flex flex-col gap-2 bg-gray-50 p-4 border-r">
{ranges.map(({ start, end, label }) => (
<DataGridRangeFilter start={start} end={end} label={label} key={`${start}-${end}`} />
))}
</div>}
<div className={'relative flex flex-col'}>
<div className={!ranges?.length ? 'flex gap-4 px-4 py-2' : 'flex flex-col px-4 py-2 gap-2'}>
<div className="space-y-2">
<div className="flex justify-between items-end h-5">
<Label htmlFor={`${id}-start`}>
{dict.datagrid.dateStart}:
</Label>
<DataViewDateFilterResetTrigger type="start">
<span className="text-sm text-gray-500 cursor-pointer hover:bg-gray-100 rounded p-0.5"><XIcon className="w-3 h-3" /></span>
</DataViewDateFilterResetTrigger>
</div>
<DataViewDateFilterInput type={'start'}>
<Input inputSize={'sm'} placeholder={dict.datagrid.dateStart} type={'date'} id={`${id}-start`} />
</DataViewDateFilterInput>
</div>
<div className="space-y-2">
<div className="flex justify-between items-end h-5">
<Label htmlFor={`${id}-end`}>
{dict.datagrid.dateEnd}:
</Label>
<DataViewDateFilterResetTrigger type="end">
<span className="text-sm text-gray-500 cursor-pointer hover:bg-gray-100 rounded p-0.5"><XIcon className="w-3 h-3" /></span>
</DataViewDateFilterResetTrigger>
</div>
<DataViewDateFilterInput type={'end'}>
<Input inputSize={'sm'} placeholder={dict.datagrid.dateEnd} type={'date'} id={`${id}-end`} />
</DataViewDateFilterInput>
</div>
</div>
<div className="mt-auto p-2 border-t">
<DataGridNullFilter />
</div>
</div>
</div>
<DataGridNullFilter />
</div>
</PopoverContent>
</Popover>
)
</PopoverContent>
</Popover>
)
}

export type DataGridPredefinedDateRange = { start: string, end: string, label: ReactNode }
const DataGridRangeFilter = ({ start, end, label }: DataGridPredefinedDateRange) => {
const name = useDataViewFilterName()
const [filter, setFilter] = useDataViewFilter<DateRangeFilterArtifacts>(name)
const isActive = filter?.start === start && filter?.end === end
return (
<Button variant="outline" size="sm" className={isActive ? 'shadow-inner bg-gray-100' : ''} onClick={() => {
setFilter({
start,
end,
})
}}>{label}</Button>
)
}
5 changes: 3 additions & 2 deletions packages/playground/admin/lib/dict.ts
Original file line number Diff line number Diff line change
Expand Up @@ -27,8 +27,9 @@ export const dict = {
},
datagrid: {
na: 'N/A',
dateStart: 'Start',
dateEnd: 'End',
dateStart: 'From',
today: 'Today',
dateEnd: 'To',
numberFrom: 'From',
numberTo: 'To',
textReset: 'Reset filter',
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,21 +8,26 @@ import { useDataViewFilterName } from '../../../contexts'
export type DataViewDateFilterResetTriggerProps = {
name?: string
children: ReactElement
type?: 'start' | 'end'
}

export const DataViewDateFilterResetTrigger = ({ name, ...props }: DataViewDateFilterResetTriggerProps) => {
export const DataViewDateFilterResetTrigger = ({ name, type, ...props }: DataViewDateFilterResetTriggerProps) => {
// eslint-disable-next-line react-hooks/rules-of-hooks
name ??= useDataViewFilterName()
const [state, setFilter] = useDataViewFilter<DateRangeFilterArtifacts>(name)
const cb = useCallback(() => {
setFilter(it => ({
...it,
start: undefined,
end: undefined,
start: !type || type === 'start' ? undefined : it?.start,
end: !type || type === 'end' ? undefined : it?.end,
}))
}, [setFilter])

if (state?.start === undefined && state?.end === undefined) {
}, [setFilter, type])
const hasStart = state?.start !== undefined
const hasEnd = state?.end !== undefined
if (!hasStart && !hasEnd) {
return null
}
if (type === 'start' && !hasStart || type === 'end' && !hasEnd) {
return null
}

Expand Down

0 comments on commit 2da747d

Please sign in to comment.