From 96c5d3953416474d8aed77cfa1bf9c5e7e3800aa Mon Sep 17 00:00:00 2001 From: devgioele Date: Tue, 19 Sep 2023 13:30:47 +0200 Subject: [PATCH 1/2] add filter fields --- src/components/filter/SearchFilterField.tsx | 33 ++++++++++ src/components/filter/SelectFilterField.tsx | 71 +++++++++++++++++++++ src/components/filter/theme.ts | 8 +++ src/examples/List.stories.tsx | 66 ++++++++----------- src/framework/theme/theme.ts | 2 + 5 files changed, 142 insertions(+), 38 deletions(-) create mode 100644 src/components/filter/SearchFilterField.tsx create mode 100644 src/components/filter/SelectFilterField.tsx create mode 100644 src/components/filter/theme.ts diff --git a/src/components/filter/SearchFilterField.tsx b/src/components/filter/SearchFilterField.tsx new file mode 100644 index 00000000..94077fa1 --- /dev/null +++ b/src/components/filter/SearchFilterField.tsx @@ -0,0 +1,33 @@ +import classNames from 'classnames' +import { forwardRef } from 'react' +import { FormVariant, SearchField, SearchFieldProps } from '../form' +import { FilterOptions, useFilter } from './useFilter' + +export type SearchFilterFieldProps = SearchFieldProps & { + filter: { + value: HTMLInputElement['value'] + setValue: (v: HTMLInputElement['value']) => void + options?: FilterOptions + } +} + +export const SearchFilterField = forwardRef< + HTMLInputElement, + SearchFilterFieldProps +>(function SearchFilterField({ filter, className, ...props }, ref) { + const filterProps = useFilter()( + filter.value, + filter.setValue, + { debounce: true, ...filter.options }, + ) + + return ( + + ) +}) diff --git a/src/components/filter/SelectFilterField.tsx b/src/components/filter/SelectFilterField.tsx new file mode 100644 index 00000000..31378fe7 --- /dev/null +++ b/src/components/filter/SelectFilterField.tsx @@ -0,0 +1,71 @@ +import { useMatchMediaQuery } from '@aboutbits/react-toolbox' +import classNames from 'classnames' +import { ForwardedRef, forwardRef } from 'react' +import { SelectFieldProps, SelectField, FormVariant } from '../form' +import { LoadingInput } from '../loading' +import { useTheme } from '../../framework' +import { FilterOptions, useFilter } from './useFilter' + +export type HTMLElementWithValue = HTMLElement & { + value: unknown +} + +export type FilterProps< + TElement extends HTMLElementWithValue, + TValue extends TElement['value'], +> = { + filter: { + value: TValue + setValue: (v: TValue) => void + options?: FilterOptions + } +} + +export type SelectFilterFieldProps = + SelectFieldProps & FilterProps + +function SelectFilterFieldComponent( + { filter, className, ...props }: SelectFilterFieldProps, + ref: ForwardedRef, +) { + const isScreenMedium = useMatchMediaQuery('(min-width: 768px)') + + const filterProps = useFilter()( + filter.value, + filter.setValue, + filter.options, + ) + + const { + filter: { select: theme }, + } = useTheme() + + return ( + + ) +} + +/** + * A SelectField configured to be used for filtering a list. + */ +export const SelectFilterField = forwardRef(SelectFilterFieldComponent) as < + TValue extends HTMLSelectElement['value'], +>( + props: SelectFilterFieldProps & { + ref?: ForwardedRef + }, +) => ReturnType + +export function LoadingSelectFilterField() { + const { + filter: { select: theme }, + } = useTheme() + + return +} diff --git a/src/components/filter/theme.ts b/src/components/filter/theme.ts new file mode 100644 index 00000000..0638d9c2 --- /dev/null +++ b/src/components/filter/theme.ts @@ -0,0 +1,8 @@ +const className = { + select: { + normal: 'w-full md:w-auto', + loading: 'w-full md:w-40', + }, +} + +export default className diff --git a/src/examples/List.stories.tsx b/src/examples/List.stories.tsx index decaaab7..3c2440c1 100644 --- a/src/examples/List.stories.tsx +++ b/src/examples/List.stories.tsx @@ -4,12 +4,10 @@ import { Meta, StoryFn } from '@storybook/react' import { useMemo, useState } from 'react' import { IndexType } from '@aboutbits/pagination' import IconAdd from '@aboutbits/react-material-icons/dist/IconAdd' -import { useMatchMediaQuery } from '@aboutbits/react-toolbox' import { Markdown } from '@storybook/addon-docs' import { ButtonIcon, ButtonVariant, - FormVariant, Section, SectionContainer, SectionContentEmpty, @@ -24,12 +22,11 @@ import { SectionHeaderSpacer, SectionHeaderTitle, SectionListItemButton, - SelectField, Tone, Option, - useFilter, - SearchField, } from '../components' +import { SearchFilterField } from '../components/filter/SearchFilterField' +import { SelectFilterField } from '../components/filter/SelectFilterField' const meta = { component: SectionContentList, @@ -102,10 +99,6 @@ export const EmptySimpleList: Story = () => * The following example shows how multiple section components and the in memory pagination are used to create an overview list with filters. */ export const ListWithFilter: Story = () => { - const isScreenMedium = useMatchMediaQuery('(min-width: 768px)') - const filterVariant = isScreenMedium - ? FormVariant.Soft - : FormVariant.Transparent const numberOfTotalItems = 1000 const numberOfItemsPerPage = 5 const [page, setPage] = useState(1) @@ -116,24 +109,6 @@ export const ListWithFilter: Story = () => { (filter.role === '' || item.role === filter.role) && (filter.department === '' || item.department === filter.department), ) - const searchFilterProps = useFilter()( - filter.search, - (v) => { - setFilter((prevFilter) => ({ ...prevFilter, search: v })) - }, - { - debounce: true, - }, - ) - const roleFilterProps = useFilter()(filter.role, (v) => { - setFilter((prevFilter) => ({ ...prevFilter, role: v })) - }) - const departmentFilterProps = useFilter()( - filter.department, - (v) => { - setFilter((prevFilter) => ({ ...prevFilter, department: v })) - }, - ) return (
@@ -152,30 +127,45 @@ export const ListWithFilter: Story = () => { spacing={SectionHeaderGroupSpacing.Md} className="flex-col gap-y-2 md:flex-row" > - { + setFilter((prevFilter) => ({ ...prevFilter, search: v })) + }, + }} /> - { + setFilter((prevFilter) => ({ ...prevFilter, role: v })) + }, + }} > - - + { + setFilter((prevFilter) => ({ + ...prevFilter, + department: v, + })) + }, + }} > - + diff --git a/src/framework/theme/theme.ts b/src/framework/theme/theme.ts index 5435997a..65eda2ec 100644 --- a/src/framework/theme/theme.ts +++ b/src/framework/theme/theme.ts @@ -14,6 +14,7 @@ import menu from '../../components/menu/theme' import pagination from '../../components/pagination/theme' import section from '../../components/section/theme' import tabs from '../../components/tabs/theme' +import filter from '../../components/filter/theme' export const defaultTheme = { action, @@ -30,4 +31,5 @@ export const defaultTheme = { menu, pagination, tabs, + filter, } From 1bfb57b022e64023c7b21e079292bf48e418c101 Mon Sep 17 00:00:00 2001 From: devgioele Date: Tue, 19 Sep 2023 14:16:15 +0200 Subject: [PATCH 2/2] move styles to theme file --- src/components/filter/SearchFilterField.tsx | 7 ++++++- src/components/filter/theme.ts | 3 +++ 2 files changed, 9 insertions(+), 1 deletion(-) diff --git a/src/components/filter/SearchFilterField.tsx b/src/components/filter/SearchFilterField.tsx index 94077fa1..3c5e7a1a 100644 --- a/src/components/filter/SearchFilterField.tsx +++ b/src/components/filter/SearchFilterField.tsx @@ -1,6 +1,7 @@ import classNames from 'classnames' import { forwardRef } from 'react' import { FormVariant, SearchField, SearchFieldProps } from '../form' +import { useTheme } from '../../framework' import { FilterOptions, useFilter } from './useFilter' export type SearchFilterFieldProps = SearchFieldProps & { @@ -15,6 +16,10 @@ export const SearchFilterField = forwardRef< HTMLInputElement, SearchFilterFieldProps >(function SearchFilterField({ filter, className, ...props }, ref) { + const { + filter: { search: theme }, + } = useTheme() + const filterProps = useFilter()( filter.value, filter.setValue, @@ -25,7 +30,7 @@ export const SearchFilterField = forwardRef< diff --git a/src/components/filter/theme.ts b/src/components/filter/theme.ts index 0638d9c2..39dbaf28 100644 --- a/src/components/filter/theme.ts +++ b/src/components/filter/theme.ts @@ -1,4 +1,7 @@ const className = { + search: { + base: 'w-full grow md:w-auto', + }, select: { normal: 'w-full md:w-auto', loading: 'w-full md:w-40',