@craftercms/studio-ui
Version:
Services, components, models & utils to build CrafterCMS authoring extensions.
212 lines (210 loc) • 7.45 kB
JavaScript
/*
* 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;