UNPKG

@craftercms/studio-ui

Version:

Services, components, models & utils to build CrafterCMS authoring extensions.

212 lines (210 loc) 7.45 kB
/* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU Lesser General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU Lesser General Public License for more details. * * You should have received a copy of the GNU Lesser General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ /* * Copyright (C) 2007-2022 Crafter Software Corporation. All Rights Reserved. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 3 as published by * the Free Software Foundation. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ import { initialSearchParameters, serializeSearchFilters, useSearchState } from './utils'; import SearchUI from '../SearchUI'; import React, { useEffect, useMemo, useState } from 'react'; import useMediaQuery from '@mui/material/useMediaQuery'; import { useTheme } from '@mui/material/styles'; import { Subject } from 'rxjs'; import { useSpreadState } from '../../hooks/useSpreadState'; import { debounceTime, distinctUntilChanged } from 'rxjs/operators'; import { reversePluckProps } from '../../utils/object'; import { UNDEFINED } from '../../utils/constants'; import useMount from '../../hooks/useMount'; export function Search(props) { const { mode = 'default', onSelect, embedded = false, onAcceptSelection, onClose, initialParameters: initialParametersProp } = props; // region State const [filters, setFilters] = useSpreadState({ sortBy: initialParametersProp?.sortBy, sortOrder: initialParametersProp?.sortOrder }); const [keyword, setKeyword] = useState(''); const [checkedFilters, setCheckedFilters] = useState({}); const [searchParameters, setSearchParameters] = useSpreadState({ ...initialSearchParameters, ...initialParametersProp }); // endregion // region Hooks const theme = useTheme(); const onSearch$ = useMemo(() => new Subject(), []); const desktopScreen = useMediaQuery(theme.breakpoints.up('md')); const { error, isFetching, areAllSelected, selected, onActionClicked, selectionOptions, onHeaderButtonClick, handleClearSelected, handleSelect, handleSelectAll, onPreview, guestBase, searchResults, selectedPath, clearPath, onSelectedPathChanges, drawerOpen, toggleDrawer, currentView, handleChangeView } = useSearchState({ searchParameters, onSelect }); useMount(() => { // Set initial filters coming from props to checkedFilters setCheckedFilters(serializeSearchFilters(initialParametersProp?.filters)); }); // endregion // region Callbacks const clearFilter = (facet) => { setSearchParameters({ filters: { ...searchParameters.filters, [facet]: undefined } }); setCheckedFilters({ ...reversePluckProps(checkedFilters, facet) }); }; const clearFilters = () => { setCheckedFilters({}); // TODO: Should change the path clearing to depend on a more specific prop (e.g. `pathLock`) if (mode !== 'select') { handleFilterChange({ name: 'path', value: UNDEFINED }); onSelectedPathChanges(UNDEFINED); clearPath(); } setSearchParameters({ ...initialSearchParameters }); }; const handleFilterChange = (filter, isFilter) => { switch (filter.name) { case 'path': case 'sortBy': case 'sortOrder': { setFilters({ [filter.name]: filter.value }); setSearchParameters({ [filter.name]: filter.value }); break; } default: { const filters = { ...searchParameters.filters }; if (filter.value.includes('TODATE')) { let id = filter.value.split('ID'); let range = id[0].split('TODATE'); filters[filter.name] = { date: true, id: id[1], min: range[0] !== 'null' ? range[0] : null, max: range[1] !== 'null' ? range[1] : null }; setCheckedFilters({ ...checkedFilters, [filter.name]: filter.value }); } else if (filter.value.includes('TO')) { let range = filter.value.split('TO'); filters[filter.name] = { min: range[0] !== null && range[0] !== '' ? range[0] : null, max: range[1] !== null && range[1] !== '' ? range[1] : null }; setCheckedFilters({ ...checkedFilters, [filter.name]: filter.value }); } else { // for this filters checkedFilters is already handle on onCheckedFiltersChanges filters[filter.name] = filter.value; } setSearchParameters({ filters }); break; } } }; const handleSearchKeyword = (keyword) => { setKeyword(keyword); onSearch$.next(keyword); }; const handleChangePage = (event, newPage) => { setSearchParameters({ offset: newPage * searchParameters.limit }); }; const handleChangeRowsPerPage = (event) => { setSearchParameters({ limit: parseInt(event.target.value, 10) }); }; const onCheckedFiltersChanges = (checkedFilters) => { setCheckedFilters(checkedFilters); }; // endregion // region Effects useEffect(() => { const subscription = onSearch$.pipe(debounceTime(400), distinctUntilChanged()).subscribe((keywords) => { setSearchParameters({ keywords }); }); return () => subscription.unsubscribe(); }, [onSearch$, setSearchParameters]); // endregion return React.createElement(SearchUI, { error: error, isFetching: isFetching, sortBy: filters.sortBy, sortOrder: filters.sortOrder, currentView: currentView, embedded: embedded, keyword: keyword, mode: mode, checkedFilters: checkedFilters, desktopScreen: desktopScreen, drawerOpen: drawerOpen, searchResults: searchResults, selectedPath: selectedPath, clearFilter: clearFilter, toggleDrawer: toggleDrawer, clearFilters: clearFilters, handleChangeView: handleChangeView, handleFilterChange: handleFilterChange, handleSearchKeyword: handleSearchKeyword, onSelectedPathChanges: onSelectedPathChanges, onCheckedFiltersChanges: onCheckedFiltersChanges, areAllSelected: areAllSelected, guestBase: guestBase, handleChangePage: handleChangePage, handleChangeRowsPerPage: handleChangeRowsPerPage, handleClearSelected: handleClearSelected, handleSelect: handleSelect, handleSelectAll: handleSelectAll, onAcceptSelection: onAcceptSelection, onActionClicked: onActionClicked, onClose: onClose, onHeaderButtonClick: onHeaderButtonClick, onPreview: onPreview, searchParameters: searchParameters, selected: selected, selectionOptions: selectionOptions }); } export default Search;