import React, { Fragment, ReactNode, useEffect, useMemo, useState, } from 'react'; import { Box } from '@wix/design-system'; import { FilterElement } from './filterElement'; import { useFiltersLayout } from './useFiltersLayout'; import { useToolbarCollectionContext } from '../ToolbarCollectionContext'; import { AppliedFiltersTagList } from '../AppliedFiltersTagList'; import { observer } from 'mobx-react-lite'; import { ToolbarItemBox } from '../ToolbarItemBox'; import { AppliedFiltersTagListState } from '../AppliedFiltersTagList/AppliedFiltersTagListState'; import { CollectionToolbarFiltersState } from './CollectionToolbarFiltersState'; import { getFieldTypeIcon } from '../../utils/fieldTypePrefixIcons'; import { CollectionFilter } from '../CollectionFilter'; import { FiltersPanelSidePanelEntry } from './FiltersPanelSidePanelEntry'; import { MoreFiltersCTA } from '../MoreFiltersCTA'; import { useWixPatternsContainer } from '@wix/bex-core/react'; import { useFiltersSyncProps } from './useFiltersSyncProps'; export interface CollectionToolbarFiltersProps { /** * Number of filter controls to be rendered inside the table toolbar */ inline?: 0 | 1 | 2 | 3; /** * A flag that indicates whether Patterns should use a new filters layout our not. */ useNewFilters?: true; maxInlineFilters?: number; maxInlineLabels?: number; /** * Title of the filters panel * @external */ panelTitle?: string | ReactNode; /** * `React` children elements, that must accept [FilterProps](./?path=/story/features-filter-props--filterprops), such as: * - [MultiSelectCheckboxFilter](./?path=/story/features-filter-components--multiselectcheckboxfilter) * - [Custom Filters](./?path=/story/features-filter-factories--customfilter) * @external */ children: | (FilterElement | undefined | boolean) | (FilterElement | undefined | boolean)[]; onPanelClearButtonClicked?: () => unknown; onSearchAppliedFilterTagRemove?: (item: string) => unknown; onSubToolbarClearButtonClicked?: () => unknown; hideAppliedFiltersTagList?: boolean; } function _CollectionToolbarFilters(props: CollectionToolbarFiltersProps) { const { children, inline = 2, maxInlineFilters, maxInlineLabels, panelTitle, onPanelClearButtonClicked, onSearchAppliedFilterTagRemove, onSubToolbarClearButtonClicked, hideAppliedFiltersTagList, useNewFilters, } = props; const childrenArr = useFiltersSyncProps(children); const state = useToolbarCollectionContext(); const [toolbarFiltersState] = useState(() => { state.toolbarFiltersState ??= new CollectionToolbarFiltersState({ toolbar: state, }); state.setToolbarFiltersStateInitialized(true); return state.toolbarFiltersState; }); useEffect(() => { state.panels.registerSidePanel('filters', FiltersPanelSidePanelEntry); }, []); useEffect(() => { toolbarFiltersState.onClearButtonClicked = onPanelClearButtonClicked; }, [onPanelClearButtonClicked]); const forceShowSidePanel = useMemo( () => !!childrenArr.slice(0, inline).find((filter) => { return ( filter.type.displayName && [ 'DateRangeFilter', 'MultiSelectCollectionFilter', 'RadioGroupFilter', 'MultiInlineCheckboxFilter', ].includes(filter.type.displayName) ); }), [childrenArr], ); const { viewsProps, collection } = state; const customInlineElements = viewsProps ? 1 : 0; const { showFiltersPanelButton, totalInlineFilters, showLabels } = useFiltersLayout({ useNewFilters, filters: childrenArr.length, totalFilters: childrenArr.length + customInlineElements, inline, maxInlineFilters, maxInlineLabels, customInlineElements, forceShowSidePanel, }); useEffect(() => { toolbarFiltersState.panelTitle.set(panelTitle); }, [panelTitle]); useEffect(() => { toolbarFiltersState.totalInlineFilters.set(totalInlineFilters); }, [totalInlineFilters]); useEffect(() => { toolbarFiltersState.filterElements.set(childrenArr); }, [childrenArr]); const container = useWixPatternsContainer(); const { filters } = collection; const filtersProps = useMemo(() => { // share essential filter props globally return [ ...childrenArr.map(({ props: filterChildProps, type }) => { const { filter, onAppliedFilterTagRemove, renderToolbarTag, toolbarTagProps, accordionItemProps, } = filterChildProps; return { filter, onAppliedFilterTagRemove, toolbarTagProps, accordionItemProps, renderToolbarTag: renderToolbarTag ?? type.renderPresetToolbarTag?.(container, { filterProps: filterChildProps, }), }; }), { filter: filters.search, onAppliedFilterTagRemove: onSearchAppliedFilterTagRemove, }, ]; }, [ childrenArr.length, ...childrenArr.map(({ props: { filter } }) => filter), ]); useEffect(() => { state.setFilterTagsPropsIfChanged(filtersProps); return () => { state.setFilterTagsPropsIfChanged(null); }; }, [filtersProps]); useEffect(() => { if (showFiltersPanelButton) { state.featuredComponents.moreFiltersButton.set(() => ); return () => { state.featuredComponents.moreFiltersButton.set(null); }; } state.featuredComponents.moreFiltersButton.set(null); return; }, [showFiltersPanelButton]); const [tagListState] = useState( () => new AppliedFiltersTagListState({ table: state, }), ); useEffect(() => { const disposers = [tagListState.init()]; if (hideAppliedFiltersTagList) { state.featuredComponents.appliedFiltersTagList.set(null); } else { state.featuredComponents.appliedFiltersTagList.set({ state: tagListState, component: () => ( ), }); } return () => { disposers.forEach((d) => d()); state.featuredComponents.appliedFiltersTagList.set(null); }; }, [hideAppliedFiltersTagList]); const showFieldTypeIcons = state.showFieldTypeIcons; return ( // workaround to support bad enzyme version not detecting this as a valid fragment component // https://wix.slack.com/archives/C02K83200PL/p1671962461443749 {childrenArr.slice(0, totalInlineFilters).map((element, index) => { const { props: filterProps } = element; const toolbarLabel = filterProps.toolbarItemProps?.label; let label: React.ReactNode = showLabels && toolbarLabel; if (showFieldTypeIcons && showLabels && toolbarLabel) { const toolbarIcon = getFieldTypeIcon({ fieldType: filterProps.fieldType, size: 'medium' }); label = {toolbarIcon}{toolbarLabel}; } return ( {filterProps.filter ? ( {element} ) : ( element )} ); })} ); } export const CollectionToolbarFilters = observer(_CollectionToolbarFilters);