@databiosphere/findable-ui 2.0.1 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/lib/components/Filter/components/FilterLabel/filterLabel.styles.js +1 -2
- package/lib/components/Filter/components/Filters/filters.d.ts +1 -1
- package/lib/components/Filter/components/Filters/filters.js +11 -1
- package/lib/components/Filter/components/Filters/filters.styles.d.ts +5 -0
- package/lib/components/Filter/components/Filters/filters.styles.js +15 -2
- package/lib/components/Filter/components/VariableSizeListItem/variableSizeListItem.js +2 -1
- package/lib/config/entities.d.ts +7 -1
- package/lib/providers/exploreState/constants.d.ts +3 -3
- package/lib/providers/exploreState/constants.js +3 -26
- package/lib/providers/exploreState/entities.d.ts +10 -3
- package/lib/providers/exploreState/initializer/constants.js +2 -0
- package/lib/providers/exploreState/initializer/utils.js +63 -4
- package/lib/providers/exploreState/payloads/entities.d.ts +8 -0
- package/lib/providers/exploreState/utils.d.ts +42 -10
- package/lib/providers/exploreState/utils.js +75 -13
- package/lib/providers/exploreState.d.ts +12 -4
- package/lib/providers/exploreState.js +30 -4
- package/lib/views/ExploreView/exploreView.js +14 -8
- package/package.json +1 -1
- package/src/components/Filter/components/FilterLabel/filterLabel.styles.ts +1 -2
- package/src/components/Filter/components/Filters/filters.styles.ts +19 -1
- package/src/components/Filter/components/Filters/filters.tsx +23 -4
- package/src/components/Filter/components/VariableSizeListItem/variableSizeListItem.tsx +6 -3
- package/src/config/entities.ts +11 -1
- package/src/providers/exploreState/constants.ts +3 -0
- package/src/providers/exploreState/entities.ts +20 -2
- package/src/providers/exploreState/initializer/constants.ts +2 -0
- package/src/providers/exploreState/initializer/utils.ts +73 -2
- package/src/providers/exploreState/payloads/entities.ts +9 -0
- package/src/providers/exploreState/utils.ts +104 -20
- package/src/providers/exploreState.tsx +67 -10
- package/src/views/ExploreView/exploreView.tsx +16 -8
|
@@ -93,6 +93,7 @@ exports.ExploreStateProvider = ExploreStateProvider;
|
|
|
93
93
|
*/
|
|
94
94
|
var ExploreActionKind;
|
|
95
95
|
(function (ExploreActionKind) {
|
|
96
|
+
ExploreActionKind["ApplySavedFilter"] = "APPLY_SAVED_FILTER";
|
|
96
97
|
ExploreActionKind["ClearFilters"] = "CLEAR_FILTERS";
|
|
97
98
|
ExploreActionKind["PaginateTable"] = "PAGINATE_TABLE";
|
|
98
99
|
ExploreActionKind["ProcessExploreResponse"] = "PROCESS_EXPLORE_RESPONSE";
|
|
@@ -116,13 +117,30 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
116
117
|
const { payload, type } = action;
|
|
117
118
|
const { config, entityList } = exploreContext;
|
|
118
119
|
switch (type) {
|
|
120
|
+
/**
|
|
121
|
+
* Apply saved filter
|
|
122
|
+
**/
|
|
123
|
+
case ExploreActionKind.ApplySavedFilter: {
|
|
124
|
+
const filterState = (0, utils_2.buildNextSavedFilterState)(state, payload.selectedValue, payload.selected);
|
|
125
|
+
const savedFilterState = (0, utils_2.buildEntityStateSavedFilterState)(payload.categoryKey, payload.selectedValue, payload.selected);
|
|
126
|
+
const savedSorting = (0, utils_2.getEntityStateSavedSorting)(state, payload.selectedValue, payload.selected);
|
|
127
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
|
|
128
|
+
filterState,
|
|
129
|
+
savedFilterState,
|
|
130
|
+
});
|
|
131
|
+
return Object.assign(Object.assign({}, state), { entityPageState: (0, utils_2.updateEntityPageStateSorting)(state, savedSorting), filterCount: (0, utils_2.getFilterCount)(filterState), filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
132
|
+
}
|
|
119
133
|
/**
|
|
120
134
|
* Clear all filters
|
|
121
135
|
**/
|
|
122
136
|
case ExploreActionKind.ClearFilters: {
|
|
123
137
|
const filterCount = 0;
|
|
124
138
|
const filterState = [];
|
|
125
|
-
|
|
139
|
+
const savedFilterState = [];
|
|
140
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
|
|
141
|
+
filterState,
|
|
142
|
+
savedFilterState,
|
|
143
|
+
});
|
|
126
144
|
return Object.assign(Object.assign({}, state), { filterCount,
|
|
127
145
|
filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
128
146
|
}
|
|
@@ -145,8 +163,12 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
145
163
|
* Process explore response
|
|
146
164
|
**/
|
|
147
165
|
case ExploreActionKind.ProcessExploreResponse: {
|
|
166
|
+
const entityState = (0, utils_2.getEntityState)(state);
|
|
148
167
|
const nextCategoryViews = payload.selectCategories
|
|
149
|
-
? (0, useCategoryFilter_1.buildCategoryViews)(
|
|
168
|
+
? (0, useCategoryFilter_1.buildCategoryViews)([
|
|
169
|
+
...payload.selectCategories,
|
|
170
|
+
...entityState.savedSelectCategories, // "savedFilter" select categories are built from config at reducer initialization.
|
|
171
|
+
], entityState.categoryConfigs, [...state.filterState, ...entityState.savedFilterState])
|
|
150
172
|
: state.categoryViews;
|
|
151
173
|
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
|
|
152
174
|
categoryViews: nextCategoryViews,
|
|
@@ -178,7 +200,7 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
178
200
|
if (payload === state.tabValue) {
|
|
179
201
|
return state;
|
|
180
202
|
}
|
|
181
|
-
const entityState = (0, utils_2.getEntityState)((0, utils_2.getEntityCategoryGroupConfigKey)(payload, state.entityPageState)
|
|
203
|
+
const entityState = (0, utils_2.getEntityState)(state, (0, utils_2.getEntityCategoryGroupConfigKey)(payload, state.entityPageState));
|
|
182
204
|
return Object.assign(Object.assign({}, state), { categoryGroups: entityState.categoryGroups, categoryViews: entityState.categoryViews, filterCount: (0, utils_2.getFilterCount)(entityState.filterState), filterState: entityState.filterState, listItems: [], loading: true, paginationState: (0, utils_2.resetPage)(state.paginationState), tabValue: payload });
|
|
183
205
|
}
|
|
184
206
|
/**
|
|
@@ -192,7 +214,11 @@ function exploreReducer(state, action, exploreContext) {
|
|
|
192
214
|
**/
|
|
193
215
|
case ExploreActionKind.UpdateFilter: {
|
|
194
216
|
const filterState = (0, useCategoryFilter_1.buildNextFilterState)(state.filterState, payload.categoryKey, payload.selectedValue, payload.selected);
|
|
195
|
-
|
|
217
|
+
const savedFilterState = []; // Clear saved filter state.
|
|
218
|
+
(0, utils_2.updateEntityStateByCategoryGroupConfigKey)(state, {
|
|
219
|
+
filterState,
|
|
220
|
+
savedFilterState,
|
|
221
|
+
});
|
|
196
222
|
return Object.assign(Object.assign({}, state), { filterCount: (0, utils_2.getFilterCount)(filterState), filterState, paginationState: (0, utils_2.resetPage)(state.paginationState) });
|
|
197
223
|
}
|
|
198
224
|
/**
|
|
@@ -46,6 +46,7 @@ const useEntityListRelatedView_1 = require("../../hooks/useEntityListRelatedView
|
|
|
46
46
|
const useExploreState_1 = require("../../hooks/useExploreState");
|
|
47
47
|
const useSummary_1 = require("../../hooks/useSummary");
|
|
48
48
|
const exploreState_1 = require("../../providers/exploreState");
|
|
49
|
+
const constants_1 = require("../../providers/exploreState/constants");
|
|
49
50
|
const breakpoints_1 = require("../../theme/common/breakpoints");
|
|
50
51
|
/**
|
|
51
52
|
* Returns tabs to be used as a prop for the Tabs component.
|
|
@@ -93,13 +94,16 @@ const ExploreView = (props) => {
|
|
|
93
94
|
*/
|
|
94
95
|
const onFilterChange = (fromSearchAll, categoryKey, selectedCategoryValue, selected, categorySection, searchTerm) => {
|
|
95
96
|
var _a;
|
|
97
|
+
const dispatchType = categoryKey === constants_1.SELECT_CATEGORY_KEY.SAVED_FILTERS
|
|
98
|
+
? exploreState_1.ExploreActionKind.ApplySavedFilter
|
|
99
|
+
: exploreState_1.ExploreActionKind.UpdateFilter;
|
|
96
100
|
exploreDispatch({
|
|
97
101
|
payload: {
|
|
98
102
|
categoryKey,
|
|
99
103
|
selected,
|
|
100
104
|
selectedValue: selectedCategoryValue,
|
|
101
105
|
},
|
|
102
|
-
type:
|
|
106
|
+
type: dispatchType,
|
|
103
107
|
});
|
|
104
108
|
(_a = trackingConfig === null || trackingConfig === void 0 ? void 0 : trackingConfig.trackFilterApplied) === null || _a === void 0 ? void 0 : _a.call(trackingConfig, {
|
|
105
109
|
category: categoryKey,
|
|
@@ -166,18 +170,20 @@ function buildCategoryFilters(selectCategoryViews, categoryGroups) {
|
|
|
166
170
|
if (!categoryGroups) {
|
|
167
171
|
return [{ categoryViews: selectCategoryViews }];
|
|
168
172
|
}
|
|
169
|
-
return categoryGroups.
|
|
173
|
+
return categoryGroups.reduce((accGroups, { categoryConfigs, label }) => {
|
|
170
174
|
// Grab the category views for the configured grouped categories.
|
|
171
|
-
const categoryViews = categoryConfigs.reduce((
|
|
175
|
+
const categoryViews = categoryConfigs.reduce((accViews, { key: categoryKey }) => {
|
|
172
176
|
const categoryView = selectCategoryViews.find(({ key }) => key === categoryKey);
|
|
173
177
|
if (categoryView) {
|
|
174
|
-
|
|
178
|
+
accViews.push(categoryView);
|
|
175
179
|
}
|
|
176
|
-
return
|
|
180
|
+
return accViews;
|
|
177
181
|
}, []);
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
182
|
+
if (categoryViews.length > 0) {
|
|
183
|
+
accGroups.push({ categoryViews, label });
|
|
184
|
+
}
|
|
185
|
+
return accGroups;
|
|
186
|
+
}, []);
|
|
181
187
|
}
|
|
182
188
|
/**
|
|
183
189
|
* Optionally renders component config.
|
package/package.json
CHANGED
|
@@ -3,7 +3,7 @@ import { Button } from "@mui/material";
|
|
|
3
3
|
import { mediaDesktopSmallUp } from "../../../../styles/common/mixins/breakpoints";
|
|
4
4
|
|
|
5
5
|
export const FilterLabel = styled(Button)`
|
|
6
|
-
|
|
6
|
+
font-weight: inherit;
|
|
7
7
|
gap: 0;
|
|
8
8
|
justify-content: space-between;
|
|
9
9
|
padding: 10px 16px;
|
|
@@ -15,7 +15,6 @@ export const FilterLabel = styled(Button)`
|
|
|
15
15
|
}
|
|
16
16
|
|
|
17
17
|
&.Mui-disabled {
|
|
18
|
-
color: ${({ theme }) => theme.palette.ink.main};
|
|
19
18
|
opacity: 0.3;
|
|
20
19
|
}
|
|
21
20
|
|
|
@@ -1,15 +1,23 @@
|
|
|
1
1
|
import { css } from "@emotion/react";
|
|
2
2
|
import styled from "@emotion/styled";
|
|
3
3
|
import { mediaDesktopSmallUp } from "../../../../styles/common/mixins/breakpoints";
|
|
4
|
+
import { inkLight, inkMain } from "../../../../styles/common/mixins/colors";
|
|
5
|
+
import {
|
|
6
|
+
textBody400,
|
|
7
|
+
textBody500,
|
|
8
|
+
} from "../../../../styles/common/mixins/fonts";
|
|
4
9
|
|
|
5
10
|
interface Props {
|
|
6
11
|
disabled: boolean;
|
|
7
12
|
height: number;
|
|
13
|
+
isBaseStyle: boolean;
|
|
8
14
|
}
|
|
9
15
|
|
|
10
16
|
export const Filters = styled("div", {
|
|
11
|
-
shouldForwardProp: (prop) => prop !== "height",
|
|
17
|
+
shouldForwardProp: (prop) => prop !== "height" && prop !== "isBaseStyle",
|
|
12
18
|
})<Props>`
|
|
19
|
+
${(props) => (props.isBaseStyle ? textBody500(props) : textBody400(props))};
|
|
20
|
+
color: ${(props) => (props.isBaseStyle ? inkMain(props) : inkLight(props))};
|
|
13
21
|
height: ${({ height }) => height}px;
|
|
14
22
|
margin: 8px 0;
|
|
15
23
|
overflow: auto;
|
|
@@ -32,3 +40,13 @@ export const Filters = styled("div", {
|
|
|
32
40
|
padding: 0 12px 0 16px;
|
|
33
41
|
}
|
|
34
42
|
`;
|
|
43
|
+
|
|
44
|
+
export const CategoryViewsLabel = styled("div")`
|
|
45
|
+
${textBody500};
|
|
46
|
+
color: ${inkMain};
|
|
47
|
+
padding: 8px 16px;
|
|
48
|
+
|
|
49
|
+
${mediaDesktopSmallUp} {
|
|
50
|
+
padding: 8px 0;
|
|
51
|
+
}
|
|
52
|
+
`;
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { Divider } from "@mui/material";
|
|
2
|
-
import {
|
|
3
|
-
import React, { Fragment, useEffect, useRef, useState } from "react";
|
|
2
|
+
import React, { Fragment, useEffect, useMemo, useRef, useState } from "react";
|
|
4
3
|
import { CategoryTag, SelectCategoryView } from "../../../../common/entities";
|
|
4
|
+
import { TrackFilterOpenedFunction } from "../../../../config/entities";
|
|
5
5
|
import {
|
|
6
6
|
BREAKPOINT_FN_NAME,
|
|
7
7
|
useBreakpointHelper,
|
|
@@ -11,7 +11,7 @@ import { useWindowResize } from "../../../../hooks/useWindowResize";
|
|
|
11
11
|
import { DESKTOP_SM } from "../../../../theme/common/breakpoints";
|
|
12
12
|
import { Filter } from "../Filter/filter";
|
|
13
13
|
import { FilterTags } from "../FilterTags/filterTags";
|
|
14
|
-
import { Filters as FilterList } from "./filters.styles";
|
|
14
|
+
import { CategoryViewsLabel, Filters as FilterList } from "./filters.styles";
|
|
15
15
|
|
|
16
16
|
export interface CategoryFilter {
|
|
17
17
|
categoryViews: SelectCategoryView[];
|
|
@@ -76,16 +76,26 @@ export const Filters = ({
|
|
|
76
76
|
const { height: windowHeight } = useWindowResize();
|
|
77
77
|
const filterListRef = useRef<HTMLDivElement>(null);
|
|
78
78
|
const [height, setHeight] = useState<number>(0);
|
|
79
|
+
const isLabeled = useMemo(
|
|
80
|
+
() => isCategoryViewsLabeled(categoryFilters),
|
|
81
|
+
[categoryFilters]
|
|
82
|
+
);
|
|
79
83
|
|
|
80
84
|
useEffect(() => {
|
|
81
85
|
setHeight(calculateListHeight(windowHeight, filterListRef.current));
|
|
82
86
|
}, [windowHeight]);
|
|
83
87
|
|
|
84
88
|
return (
|
|
85
|
-
<FilterList
|
|
89
|
+
<FilterList
|
|
90
|
+
disabled={disabled}
|
|
91
|
+
height={height}
|
|
92
|
+
isBaseStyle={!isLabeled}
|
|
93
|
+
ref={filterListRef}
|
|
94
|
+
>
|
|
86
95
|
{categoryFilters.map(({ categoryViews, label }, i) => (
|
|
87
96
|
<Fragment key={i}>
|
|
88
97
|
{i !== 0 && <Divider />}
|
|
98
|
+
{label && <CategoryViewsLabel>{label}</CategoryViewsLabel>}
|
|
89
99
|
{categoryViews.map((categoryView) => (
|
|
90
100
|
<Filter
|
|
91
101
|
key={categoryView.key}
|
|
@@ -116,3 +126,12 @@ function calculateListHeight(
|
|
|
116
126
|
): number {
|
|
117
127
|
return windowHeight - (filterListEl?.getBoundingClientRect()?.top ?? 0);
|
|
118
128
|
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Returns true if any category views are labelled.
|
|
132
|
+
* @param categoryFilters - Category filters.
|
|
133
|
+
* @returns true if any category views are labelled.
|
|
134
|
+
*/
|
|
135
|
+
function isCategoryViewsLabeled(categoryFilters: CategoryFilter[]): boolean {
|
|
136
|
+
return categoryFilters.some(({ label }) => label);
|
|
137
|
+
}
|
|
@@ -7,6 +7,7 @@ import {
|
|
|
7
7
|
import React, { CSSProperties, useEffect, useRef } from "react";
|
|
8
8
|
import { CategoryKey } from "../../../../common/entities";
|
|
9
9
|
import { OnFilterFn } from "../../../../hooks/useCategoryFilter";
|
|
10
|
+
import { SELECT_CATEGORY_KEY } from "../../../../providers/exploreState/constants";
|
|
10
11
|
import { TEXT_BODY_SMALL_400 } from "../../../../theme/common/typography";
|
|
11
12
|
import { CheckedIcon } from "../../../common/CustomIcon/components/CheckedIcon/checkedIcon";
|
|
12
13
|
import { UncheckedIcon } from "../../../common/CustomIcon/components/UncheckedIcon/uncheckedIcon";
|
|
@@ -62,9 +63,11 @@ export default function VariableSizeListItem({
|
|
|
62
63
|
disableTypography
|
|
63
64
|
primary={<HighlightedLabel label={label} ranges={labelRanges} />}
|
|
64
65
|
secondary={
|
|
65
|
-
|
|
66
|
-
{
|
|
67
|
-
|
|
66
|
+
categoryKey !== SELECT_CATEGORY_KEY.SAVED_FILTERS && (
|
|
67
|
+
<Typography color="ink.light" variant={TEXT_BODY_SMALL_400}>
|
|
68
|
+
{count}
|
|
69
|
+
</Typography>
|
|
70
|
+
)
|
|
68
71
|
}
|
|
69
72
|
/>
|
|
70
73
|
</ListItemButton>
|
package/src/config/entities.ts
CHANGED
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import { TabProps as MTabProps, Theme, ThemeOptions } from "@mui/material";
|
|
2
2
|
import { ColumnSort } from "@tanstack/react-table";
|
|
3
3
|
import { JSXElementConstructor, ReactNode } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
CategoryKey,
|
|
6
|
+
SelectedFilter,
|
|
7
|
+
SelectedFilterValue,
|
|
8
|
+
} from "../common/entities";
|
|
5
9
|
import { HeroTitle } from "../components/common/Title/title";
|
|
6
10
|
import { FooterProps } from "../components/Layout/components/Footer/footer";
|
|
7
11
|
import { HeaderProps } from "../components/Layout/components/Header/header";
|
|
@@ -56,6 +60,7 @@ export interface BackPageTabConfig extends TabConfig {
|
|
|
56
60
|
export interface CategoryGroupConfig {
|
|
57
61
|
categoryGroups: CategoryGroup[];
|
|
58
62
|
key: string;
|
|
63
|
+
savedFilters?: SavedFilter[];
|
|
59
64
|
}
|
|
60
65
|
|
|
61
66
|
/**
|
|
@@ -302,6 +307,11 @@ type RelatedSearchFunction = (
|
|
|
302
307
|
selectedCategoryValues: SelectedFilterValue | undefined
|
|
303
308
|
) => Promise<RelatedSearchResult | undefined>;
|
|
304
309
|
|
|
310
|
+
export interface SavedFilter {
|
|
311
|
+
filters: SelectedFilter[];
|
|
312
|
+
sort?: ColumnSort;
|
|
313
|
+
title: string;
|
|
314
|
+
}
|
|
305
315
|
/**
|
|
306
316
|
* Filter applied tracking payload
|
|
307
317
|
*/
|
|
@@ -1,10 +1,16 @@
|
|
|
1
1
|
import { ColumnSort } from "@tanstack/react-table";
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
CategoryValueKey,
|
|
4
|
+
SelectCategory,
|
|
5
|
+
SelectCategoryView,
|
|
6
|
+
SelectedFilter,
|
|
7
|
+
} from "../../common/entities";
|
|
3
8
|
import {
|
|
4
9
|
CategoryConfig,
|
|
5
10
|
CategoryGroup,
|
|
6
11
|
CategoryGroupConfig,
|
|
7
12
|
EntityPath,
|
|
13
|
+
SavedFilter,
|
|
8
14
|
} from "../../config/entities";
|
|
9
15
|
|
|
10
16
|
export interface EntityPageState {
|
|
@@ -20,8 +26,11 @@ export interface EntityPageStateMapper {
|
|
|
20
26
|
export interface EntityState {
|
|
21
27
|
categoryConfigs?: CategoryConfig[];
|
|
22
28
|
categoryGroups?: CategoryGroup[];
|
|
23
|
-
categoryViews:
|
|
29
|
+
categoryViews: SelectCategoryView[];
|
|
24
30
|
filterState: SelectedFilter[];
|
|
31
|
+
savedFilterByCategoryValueKey?: SavedFilterByCategoryValueKey;
|
|
32
|
+
savedFilterState: SelectedFilter[];
|
|
33
|
+
savedSelectCategories: SelectCategory[];
|
|
25
34
|
}
|
|
26
35
|
|
|
27
36
|
export type EntityStateByCategoryGroupConfigKey = Map<
|
|
@@ -30,3 +39,12 @@ export type EntityStateByCategoryGroupConfigKey = Map<
|
|
|
30
39
|
>;
|
|
31
40
|
|
|
32
41
|
export type CategoryGroupConfigKey = CategoryGroupConfig["key"];
|
|
42
|
+
|
|
43
|
+
export interface EntityStateSavedFilter extends Omit<SavedFilter, "sort"> {
|
|
44
|
+
sorting?: ColumnSort[];
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
export type SavedFilterByCategoryValueKey = Map<
|
|
48
|
+
CategoryValueKey,
|
|
49
|
+
EntityStateSavedFilter
|
|
50
|
+
>;
|
|
@@ -1,22 +1,86 @@
|
|
|
1
|
-
import { SelectedFilter } from "../../../common/entities";
|
|
1
|
+
import { SelectCategory, SelectedFilter } from "../../../common/entities";
|
|
2
2
|
import { getInitialTableColumnVisibility } from "../../../components/Table/common/utils";
|
|
3
3
|
import {
|
|
4
4
|
CategoryConfig,
|
|
5
5
|
CategoryGroup,
|
|
6
6
|
CategoryGroupConfig,
|
|
7
7
|
EntityConfig,
|
|
8
|
+
SavedFilter,
|
|
8
9
|
SiteConfig,
|
|
9
10
|
} from "../../../config/entities";
|
|
10
11
|
import { getDefaultSorting } from "../../../config/utils";
|
|
11
12
|
import { ExploreState } from "../../exploreState";
|
|
13
|
+
import { SELECT_CATEGORY_KEY } from "../constants";
|
|
12
14
|
import {
|
|
13
15
|
CategoryGroupConfigKey,
|
|
14
16
|
EntityPageStateMapper,
|
|
15
17
|
EntityStateByCategoryGroupConfigKey,
|
|
18
|
+
SavedFilterByCategoryValueKey,
|
|
16
19
|
} from "../entities";
|
|
17
20
|
import { getEntityCategoryGroupConfigKey, getFilterCount } from "../utils";
|
|
18
21
|
import { DEFAULT_ENTITY_STATE, INITIAL_STATE } from "./constants";
|
|
19
22
|
|
|
23
|
+
/**
|
|
24
|
+
* Builds category groups from the given category group config (adds the saved filters category to the category groups).
|
|
25
|
+
* @param categoryGroupConfig - Category group config.
|
|
26
|
+
* @returns category groups with saved filters category.
|
|
27
|
+
*/
|
|
28
|
+
function buildCategoryGroups(
|
|
29
|
+
categoryGroupConfig: CategoryGroupConfig
|
|
30
|
+
): CategoryGroup[] {
|
|
31
|
+
const { categoryGroups, savedFilters } = categoryGroupConfig;
|
|
32
|
+
if (!savedFilters) return categoryGroups;
|
|
33
|
+
const clonedCategoryGroups = [...categoryGroups];
|
|
34
|
+
const savedFiltersCategoryGroup: CategoryGroup = {
|
|
35
|
+
categoryConfigs: [
|
|
36
|
+
{ key: SELECT_CATEGORY_KEY.SAVED_FILTERS, label: "Saved Filters" },
|
|
37
|
+
],
|
|
38
|
+
};
|
|
39
|
+
clonedCategoryGroups.unshift(savedFiltersCategoryGroup);
|
|
40
|
+
return clonedCategoryGroups;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
/**
|
|
44
|
+
* Returns the saved filters as select categories.
|
|
45
|
+
* @param savedFilters - Saved filters.
|
|
46
|
+
* @returns select categories.
|
|
47
|
+
*/
|
|
48
|
+
function buildSavedSelectCategories(
|
|
49
|
+
savedFilters?: SavedFilter[]
|
|
50
|
+
): SelectCategory[] {
|
|
51
|
+
if (!savedFilters) return [];
|
|
52
|
+
return [
|
|
53
|
+
{
|
|
54
|
+
key: SELECT_CATEGORY_KEY.SAVED_FILTERS,
|
|
55
|
+
label: "", // Label is applied in filter hook where it has access to the config.
|
|
56
|
+
values: savedFilters.map(({ title }) => ({
|
|
57
|
+
count: 1,
|
|
58
|
+
key: title,
|
|
59
|
+
label: title,
|
|
60
|
+
selected: false, // Selected state updated in filter hook.
|
|
61
|
+
})),
|
|
62
|
+
},
|
|
63
|
+
];
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
/**
|
|
67
|
+
* Builds saved filter by category value key.
|
|
68
|
+
* @param savedFilters - Saved filters.
|
|
69
|
+
* @returns saved filter by category value key.
|
|
70
|
+
*/
|
|
71
|
+
function buildSavedFilterByCategoryValueKey(
|
|
72
|
+
savedFilters?: SavedFilter[]
|
|
73
|
+
): SavedFilterByCategoryValueKey | undefined {
|
|
74
|
+
if (!savedFilters) return;
|
|
75
|
+
const savedFilterByCategoryValueKey: SavedFilterByCategoryValueKey =
|
|
76
|
+
new Map();
|
|
77
|
+
for (const { filters, sort, title } of savedFilters) {
|
|
78
|
+
const sorting = sort ? [sort] : undefined;
|
|
79
|
+
savedFilterByCategoryValueKey.set(title, { filters, sorting, title });
|
|
80
|
+
}
|
|
81
|
+
return savedFilterByCategoryValueKey;
|
|
82
|
+
}
|
|
83
|
+
|
|
20
84
|
/**
|
|
21
85
|
* Returns entity related configured category group config where entity config takes precedence over site config.
|
|
22
86
|
* @param siteConfig - Site config.
|
|
@@ -109,13 +173,20 @@ function initEntityStateByCategoryGroupConfigKey(
|
|
|
109
173
|
for (const entity of config.entities) {
|
|
110
174
|
const categoryGroupConfig = getEntityCategoryGroupConfig(config, entity);
|
|
111
175
|
if (!categoryGroupConfig) continue;
|
|
112
|
-
const {
|
|
176
|
+
const { key, savedFilters } = categoryGroupConfig;
|
|
113
177
|
if (entityStateByCategoryGroupConfigKey.has(key)) continue;
|
|
178
|
+
const categoryGroups = buildCategoryGroups(categoryGroupConfig);
|
|
179
|
+
const savedSelectCategories: SelectCategory[] =
|
|
180
|
+
buildSavedSelectCategories(savedFilters);
|
|
181
|
+
const savedFilterByCategoryValueKey =
|
|
182
|
+
buildSavedFilterByCategoryValueKey(savedFilters);
|
|
114
183
|
entityStateByCategoryGroupConfigKey.set(key, {
|
|
115
184
|
...DEFAULT_ENTITY_STATE,
|
|
116
185
|
categoryConfigs: flattenCategoryGroups(categoryGroups),
|
|
117
186
|
categoryGroups,
|
|
118
187
|
filterState: key === categoryGroupConfigKey ? filterState : [],
|
|
188
|
+
savedFilterByCategoryValueKey,
|
|
189
|
+
savedSelectCategories,
|
|
119
190
|
});
|
|
120
191
|
}
|
|
121
192
|
return entityStateByCategoryGroupConfigKey;
|
|
@@ -12,6 +12,15 @@ import {
|
|
|
12
12
|
RelatedListItems,
|
|
13
13
|
} from "../../exploreState";
|
|
14
14
|
|
|
15
|
+
/**
|
|
16
|
+
* Apply saved filter payload.
|
|
17
|
+
*/
|
|
18
|
+
export interface ApplySavedFilterPayload {
|
|
19
|
+
categoryKey: CategoryKey;
|
|
20
|
+
selected: boolean;
|
|
21
|
+
selectedValue: CategoryValueKey;
|
|
22
|
+
}
|
|
23
|
+
|
|
15
24
|
/**
|
|
16
25
|
* Process explore response payload.
|
|
17
26
|
*/
|
|
@@ -1,14 +1,52 @@
|
|
|
1
|
-
import {
|
|
2
|
-
import {
|
|
1
|
+
import { ColumnSort } from "@tanstack/react-table";
|
|
2
|
+
import {
|
|
3
|
+
CategoryKey,
|
|
4
|
+
CategoryValueKey,
|
|
5
|
+
SelectedFilter,
|
|
6
|
+
} from "../../common/entities";
|
|
3
7
|
import { ExploreState, PaginationState } from "../exploreState";
|
|
4
8
|
import {
|
|
5
9
|
CategoryGroupConfigKey,
|
|
6
10
|
EntityPageState,
|
|
7
11
|
EntityPageStateMapper,
|
|
8
12
|
EntityState,
|
|
13
|
+
EntityStateSavedFilter,
|
|
9
14
|
} from "./entities";
|
|
10
15
|
import { DEFAULT_ENTITY_STATE } from "./initializer/constants";
|
|
11
16
|
|
|
17
|
+
/**
|
|
18
|
+
* Returns the entity state saved filter state for the given category key.
|
|
19
|
+
* @param categoryKey - Category key.
|
|
20
|
+
* @param selectedValue - Key of category value that has been de/selected.
|
|
21
|
+
* @param selected - True if value is selected, false if de-selected.
|
|
22
|
+
* @returns entity state saved filter state.
|
|
23
|
+
*/
|
|
24
|
+
export function buildEntityStateSavedFilterState(
|
|
25
|
+
categoryKey: CategoryKey,
|
|
26
|
+
selectedValue: CategoryValueKey,
|
|
27
|
+
selected: boolean
|
|
28
|
+
): SelectedFilter[] {
|
|
29
|
+
if (!selected) return [];
|
|
30
|
+
return [{ categoryKey, value: [selectedValue] }];
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Build new set of selected filters on de/select of a "saved filter" filter.
|
|
35
|
+
* @param state - Explore state.
|
|
36
|
+
* @param selectedValue - Key of category value that has been de/selected.
|
|
37
|
+
* @param selected - True if value is selected, false if de-selected.
|
|
38
|
+
* @returns new selected filters.
|
|
39
|
+
*/
|
|
40
|
+
export function buildNextSavedFilterState(
|
|
41
|
+
state: ExploreState,
|
|
42
|
+
selectedValue: CategoryValueKey,
|
|
43
|
+
selected: boolean
|
|
44
|
+
): SelectedFilter[] {
|
|
45
|
+
if (!selected) return []; // Clears all filters on de-select of saved filter.
|
|
46
|
+
const savedFilter = getEntityStateSavedFilter(state, selectedValue);
|
|
47
|
+
return savedFilter?.filters || [];
|
|
48
|
+
}
|
|
49
|
+
|
|
12
50
|
/**
|
|
13
51
|
* Returns the category group config key for the current entity.
|
|
14
52
|
* @param entityPath - Entity path.
|
|
@@ -23,28 +61,17 @@ export function getEntityCategoryGroupConfigKey(
|
|
|
23
61
|
}
|
|
24
62
|
|
|
25
63
|
/**
|
|
26
|
-
* Returns the
|
|
64
|
+
* Returns the entity state for the current entity.
|
|
27
65
|
* @param state - Explore state.
|
|
28
|
-
* @returns category configs.
|
|
29
|
-
*/
|
|
30
|
-
export function getEntityCategoryConfigs(
|
|
31
|
-
state: ExploreState
|
|
32
|
-
): CategoryConfig[] | undefined {
|
|
33
|
-
return getEntityState(
|
|
34
|
-
getEntityCategoryGroupConfigKey(state.tabValue, state.entityPageState),
|
|
35
|
-
state
|
|
36
|
-
).categoryConfigs;
|
|
37
|
-
}
|
|
38
|
-
|
|
39
|
-
/**
|
|
40
|
-
* Returns the entity state for the given category group config key.
|
|
41
66
|
* @param categoryGroupConfigKey - Category group config key.
|
|
42
|
-
* @param state - Explore state.
|
|
43
67
|
* @returns entity state.
|
|
44
68
|
*/
|
|
45
69
|
export function getEntityState(
|
|
46
|
-
|
|
47
|
-
|
|
70
|
+
state: ExploreState,
|
|
71
|
+
categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
|
|
72
|
+
state.tabValue,
|
|
73
|
+
state.entityPageState
|
|
74
|
+
)
|
|
48
75
|
): EntityState {
|
|
49
76
|
return (
|
|
50
77
|
state.entityStateByCategoryGroupConfigKey.get(categoryGroupConfigKey) ||
|
|
@@ -52,6 +79,37 @@ export function getEntityState(
|
|
|
52
79
|
);
|
|
53
80
|
}
|
|
54
81
|
|
|
82
|
+
/**
|
|
83
|
+
* Returns the saved filter/sorting for the given category key.
|
|
84
|
+
* @param state - Explore state.
|
|
85
|
+
* @param categoryValueKey - Category key.
|
|
86
|
+
* @returns saved filter/sorting for the category key.
|
|
87
|
+
*/
|
|
88
|
+
export function getEntityStateSavedFilter(
|
|
89
|
+
state: ExploreState,
|
|
90
|
+
categoryValueKey: CategoryValueKey
|
|
91
|
+
): EntityStateSavedFilter | undefined {
|
|
92
|
+
const entityState = getEntityState(state);
|
|
93
|
+
return entityState.savedFilterByCategoryValueKey?.get(categoryValueKey);
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Returns entity state "saved filter" sorting for the given category value key.
|
|
98
|
+
* @param state - Explore state.
|
|
99
|
+
* @param selectedValue - Key of category value that has been de/selected.
|
|
100
|
+
* @param selected - True if value is selected, false if de-selected.
|
|
101
|
+
* @returns sorting.
|
|
102
|
+
*/
|
|
103
|
+
export function getEntityStateSavedSorting(
|
|
104
|
+
state: ExploreState,
|
|
105
|
+
selectedValue: CategoryValueKey,
|
|
106
|
+
selected: boolean
|
|
107
|
+
): ColumnSort[] | undefined {
|
|
108
|
+
if (!selected) return;
|
|
109
|
+
const savedFilter = getEntityStateSavedFilter(state, selectedValue);
|
|
110
|
+
return savedFilter?.sorting;
|
|
111
|
+
}
|
|
112
|
+
|
|
55
113
|
/**
|
|
56
114
|
* Returns the filter count.
|
|
57
115
|
* @param filterState - Filter state.
|
|
@@ -111,6 +169,32 @@ export function updateEntityPageState(
|
|
|
111
169
|
};
|
|
112
170
|
}
|
|
113
171
|
|
|
172
|
+
/**
|
|
173
|
+
* Updates entity page state sorting for all entities with the same category group config key.
|
|
174
|
+
* @param state - Explore state.
|
|
175
|
+
* @param sorting - Sorting.
|
|
176
|
+
* @returns entity page state.
|
|
177
|
+
*/
|
|
178
|
+
export function updateEntityPageStateSorting(
|
|
179
|
+
state: ExploreState,
|
|
180
|
+
sorting?: EntityPageState["sorting"]
|
|
181
|
+
): EntityPageStateMapper {
|
|
182
|
+
if (!sorting) return state.entityPageState;
|
|
183
|
+
const categoryGroupConfigKey = getEntityCategoryGroupConfigKey(
|
|
184
|
+
state.tabValue,
|
|
185
|
+
state.entityPageState
|
|
186
|
+
);
|
|
187
|
+
return Object.entries(state.entityPageState).reduce(
|
|
188
|
+
(acc, [entityPath, entityPageState]) => {
|
|
189
|
+
if (entityPageState.categoryGroupConfigKey === categoryGroupConfigKey) {
|
|
190
|
+
return { ...acc, [entityPath]: { ...entityPageState, sorting } };
|
|
191
|
+
}
|
|
192
|
+
return { ...acc, [entityPath]: entityPageState };
|
|
193
|
+
},
|
|
194
|
+
{} as EntityPageStateMapper
|
|
195
|
+
);
|
|
196
|
+
}
|
|
197
|
+
|
|
114
198
|
/**
|
|
115
199
|
* Updates entity state by category group config key.
|
|
116
200
|
* @param state - Explore state.
|
|
@@ -125,7 +209,7 @@ export function updateEntityStateByCategoryGroupConfigKey(
|
|
|
125
209
|
state.tabValue,
|
|
126
210
|
state.entityPageState
|
|
127
211
|
);
|
|
128
|
-
const entityState = getEntityState(
|
|
212
|
+
const entityState = getEntityState(state, categoryGroupConfigKey);
|
|
129
213
|
if (entityState) {
|
|
130
214
|
setEntityStateByCategoryGroupConfigKey(categoryGroupConfigKey, state, {
|
|
131
215
|
...entityState,
|