diff --git a/src/components/data/datagrid/datagridfilter.tsx b/src/components/data/datagrid/datagridfilter.tsx new file mode 100644 index 0000000..9518482 --- /dev/null +++ b/src/components/data/datagrid/datagridfilter.tsx @@ -0,0 +1,111 @@ +import clsx from "clsx"; +import React, { useContext, useEffect, useRef, useState } from "react"; + +import { + AttributeData, + field2Title, + formatMessage, + serializeForm, + useIntl, +} from "../../../lib"; +import { FormControl } from "../../form"; +import { Outline } from "../../icon"; +import { DataGridContext } from "./datagrid"; +import { TRANSLATIONS } from "./translations"; + +/** + * DataGrid filter, encapsulates a set of FormControl's allowing the user to + * filter items on the grid. + */ +export const DataGridFilter: React.FC = () => { + const intl = useIntl(); + const onFilterTimeoutRef = useRef(); + const [filterState, setFilterState] = useState(); + + const { + dataGridId, + filterTransform, + labelFilterField, + onFilter, + renderableFields, + selectable, + } = useContext(DataGridContext); + + // Debounce filter + useEffect(() => { + const handler = () => { + // No filter state. + if (filterState === undefined) { + return; + } + onFilter(filterState || {}); + }; + onFilterTimeoutRef.current && clearTimeout(onFilterTimeoutRef.current); + onFilterTimeoutRef.current = setTimeout(handler, 300); + }, [filterState]); + + return ( + + {selectable && ( + + )} + {renderableFields.map((field) => { + const placeholder = field2Title(field.name, { lowerCase: false }); + + // eslint-disable-next-line @typescript-eslint/no-unused-vars + const { options, valueTransform, ...context } = field; + + const _labelFilterField = labelFilterField + ? formatMessage(labelFilterField, context) + : intl.formatMessage(TRANSLATIONS.LABEL_FILTER_FIELD, context); + + return ( + + {field.filterable !== false && ( + + } + form={`${dataGridId}-filter-form`} + name={field.filterLookup || field.name} + options={field.options} + min={!field.options && field.type === "number" ? 0 : undefined} + pad={field.type === "daterange" ? "v" : undefined} + placeholder={placeholder} + type={field.type} + value={field.filterValue} + onChange={( + e: React.ChangeEvent, + ) => { + e.preventDefault(); + const data = serializeForm( + e.target.form as HTMLFormElement, + ) as AttributeData; + const _data = filterTransform ? filterTransform(data) : data; + + // Reset page on filter (length of dataset may change). + setFilterState(_data); + }} + /> + )} + + ); + })} + + ); +}; diff --git a/src/components/data/datagrid/datagridthead.tsx b/src/components/data/datagrid/datagridthead.tsx index 8be9002..61a6bed 100644 --- a/src/components/data/datagrid/datagridthead.tsx +++ b/src/components/data/datagrid/datagridthead.tsx @@ -1,42 +1,21 @@ -import clsx from "clsx"; -import React, { - CSSProperties, - useContext, - useEffect, - useRef, - useState, -} from "react"; +import React, { CSSProperties, useContext, useEffect, useRef } from "react"; -import { - AttributeData, - field2Title, - formatMessage, - serializeForm, - useIntl, -} from "../../../lib"; -import { FormControl } from "../../form"; -import { Outline } from "../../icon"; +import { field2Title } from "../../../lib"; import { DataGridContext, scrollPaneRef } from "./datagrid"; +import { DataGridFilter } from "./datagridfilter"; import { DataGridHeadingCell } from "./datagridheadingcell"; -import { TRANSLATIONS } from "./translations"; /** * DataGrid table head, encapsulates a set of table rows, indicating that they * comprise the head of a table with information about the table's columns. */ export const DataGridTHead: React.FC = () => { - const intl = useIntl(); - const onFilterTimeoutRef = useRef(); const ref = useRef(null); - const [filterState, setFilterState] = useState(); const { dataGridId, filterable, - filterTransform, height, - labelFilterField, - onFilter, renderableFields, selectable, toolbarRef, @@ -85,19 +64,6 @@ export const DataGridTHead: React.FC = () => { }); }; - // Debounce filter - useEffect(() => { - const handler = () => { - // No filter state. - if (filterState === undefined) { - return; - } - onFilter(filterState || {}); - }; - onFilterTimeoutRef.current && clearTimeout(onFilterTimeoutRef.current); - onFilterTimeoutRef.current = setTimeout(handler, 300); - }, [filterState]); - return ( { {/* Filters */} - {filterable && ( - - {selectable && ( - - )} - {renderableFields.map((field) => { - const placeholder = field2Title(field.name, { lowerCase: false }); - - // eslint-disable-next-line @typescript-eslint/no-unused-vars - const { options, valueTransform, ...context } = field; - - const _labelFilterField = labelFilterField - ? formatMessage(labelFilterField, context) - : intl.formatMessage(TRANSLATIONS.LABEL_FILTER_FIELD, context); - - return ( - - {field.filterable !== false && ( - - ) - } - form={`${dataGridId}-filter-form`} - name={field.filterLookup || field.name} - options={field.options} - min={ - !field.options && field.type === "number" ? 0 : undefined - } - pad={field.type === "daterange" ? "v" : undefined} - placeholder={placeholder} - type={field.type} - value={field.filterValue} - onChange={( - e: React.ChangeEvent< - HTMLInputElement | HTMLSelectElement - >, - ) => { - e.preventDefault(); - const data = serializeForm( - e.target.form as HTMLFormElement, - ) as AttributeData; - const _data = filterTransform - ? filterTransform(data) - : data; - - // Reset page on filter (length of dataset may change). - setFilterState(_data); - }} - /> - )} - - ); - })} - - )} + {filterable && } ); };