@bento-core/facet-filter 1.0.1-c3dc.6 → 1.0.1-c3dc.8
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/dist/FacetFilterController.js +9 -3
- package/dist/components/facet/FacetStyle.js +3 -3
- package/dist/components/facet/FacetView.js +7 -7
- package/dist/components/inputs/FilterItems.js +104 -11
- package/dist/components/inputs/slider/InputMinMaxView.js +4 -1
- package/dist/components/inputs/slider/SliderView.js +24 -11
- package/package.json +1 -1
- package/src/FacetFilterController.js +11 -4
- package/src/components/facet/FacetStyle.js +2 -2
- package/src/components/facet/FacetView.js +13 -13
- package/src/components/inputs/FilterItems.js +90 -6
- package/src/components/inputs/slider/InputMinMaxView.js +3 -0
- package/src/components/inputs/slider/SliderView.js +26 -9
|
@@ -61,7 +61,11 @@ const FacetFilterController = props => {
|
|
|
61
61
|
minLowerBound,
|
|
62
62
|
maxUpperBound
|
|
63
63
|
} = sideBar;
|
|
64
|
-
|
|
64
|
+
if (minLowerBound === 0 && maxUpperBound === 0) {
|
|
65
|
+
sideBar.facetValues = [0, 0];
|
|
66
|
+
} else {
|
|
67
|
+
sideBar.facetValues = [minLowerBound, maxUpperBound];
|
|
68
|
+
}
|
|
65
69
|
}
|
|
66
70
|
});
|
|
67
71
|
}
|
|
@@ -126,13 +130,15 @@ const FacetFilterController = props => {
|
|
|
126
130
|
const lowerBound = data[apiForFiltering][ApiLowerBoundName];
|
|
127
131
|
const upperBound = data[apiForFiltering][ApiUpperBoundName];
|
|
128
132
|
if (lowerBound === 0 && upperBound === 0) {
|
|
129
|
-
updateFacet.
|
|
133
|
+
updateFacet.minLowerBound = 0;
|
|
134
|
+
updateFacet.maxUpperBound = 0;
|
|
135
|
+
updateFacet.facetValues = [];
|
|
130
136
|
} else {
|
|
131
137
|
updateFacet.minLowerBound = lowerBound;
|
|
132
138
|
updateFacet.maxUpperBound = upperBound;
|
|
133
139
|
updateFacet.facetValues = [lowerBound, upperBound];
|
|
134
|
-
updateFacet.style = facet.style;
|
|
135
140
|
}
|
|
141
|
+
updateFacet.style = facet.style;
|
|
136
142
|
}
|
|
137
143
|
}
|
|
138
144
|
if (!updateFacet.filterOut) {
|
|
@@ -55,7 +55,9 @@ var _default = () => ({
|
|
|
55
55
|
fontFamily: 'Nunito',
|
|
56
56
|
fontSize: '11px',
|
|
57
57
|
marginRight: '12px',
|
|
58
|
-
marginLeft: '24px'
|
|
58
|
+
marginLeft: '24px',
|
|
59
|
+
display: 'flex',
|
|
60
|
+
alignItems: 'center'
|
|
59
61
|
},
|
|
60
62
|
sortGroupItem: {
|
|
61
63
|
cursor: 'pointer',
|
|
@@ -65,8 +67,6 @@ var _default = () => ({
|
|
|
65
67
|
},
|
|
66
68
|
|
|
67
69
|
NonSortGroup: {
|
|
68
|
-
marginBottom: '5px',
|
|
69
|
-
borderTop: '1px solid #B1B1B1',
|
|
70
70
|
textAlign: 'left',
|
|
71
71
|
paddingLeft: '10px'
|
|
72
72
|
},
|
|
@@ -82,17 +82,13 @@ const FacetView = _ref => {
|
|
|
82
82
|
className: (0, _clsx.default)(classes.subSectionSummaryText, {
|
|
83
83
|
["activeFacet".concat(facet.section)]: isActiveFacet
|
|
84
84
|
})
|
|
85
|
-
}, facet.label)), facetValues.length
|
|
86
|
-
className: classes.NonSortGroup
|
|
87
|
-
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
88
|
-
className: classes.NonSortGroupItem
|
|
89
|
-
}, "No data for this field")), (facet.type === _Types.InputTypes.SLIDER || facetValues.length > 0) && /*#__PURE__*/_react.default.createElement("div", {
|
|
85
|
+
}, facet.label)), (facet.type === _Types.InputTypes.SLIDER || facetValues.length > 0) && /*#__PURE__*/_react.default.createElement("div", {
|
|
90
86
|
className: facet.type === _Types.InputTypes.SLIDER ? classes.sortGroupSlider : classes.sortGroup
|
|
91
87
|
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
92
88
|
className: classes.sortGroupIcon
|
|
93
89
|
}, /*#__PURE__*/_react.default.createElement(_core.Icon, {
|
|
94
90
|
style: {
|
|
95
|
-
fontSize:
|
|
91
|
+
fontSize: 14
|
|
96
92
|
},
|
|
97
93
|
onClick: onClearSection
|
|
98
94
|
}, /*#__PURE__*/_react.default.createElement("img", {
|
|
@@ -100,7 +96,11 @@ const FacetView = _ref => {
|
|
|
100
96
|
height: 12,
|
|
101
97
|
width: 12,
|
|
102
98
|
alt: "clear-icon"
|
|
103
|
-
}))
|
|
99
|
+
})), facetValues.length === 0 && /*#__PURE__*/_react.default.createElement("div", {
|
|
100
|
+
className: classes.NonSortGroup
|
|
101
|
+
}, /*#__PURE__*/_react.default.createElement("span", {
|
|
102
|
+
className: classes.NonSortGroupItem
|
|
103
|
+
}, "No data for this field"))), facet.type === _Types.InputTypes.CHECKBOX && facetValues.length > 0 && /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("span", {
|
|
104
104
|
className: (0, _clsx.default)(classes.sortGroupItem, {
|
|
105
105
|
[classes.highlight]: sortBy === _Sort.sortType.ALPHABET
|
|
106
106
|
}),
|
|
@@ -4,41 +4,128 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
4
4
|
value: true
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
|
-
var _react =
|
|
7
|
+
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _core = require("@material-ui/core");
|
|
8
9
|
var _ReduxCheckbox = _interopRequireDefault(require("./checkbox/ReduxCheckbox"));
|
|
9
10
|
var _ReduxSlider = _interopRequireDefault(require("./slider/ReduxSlider"));
|
|
10
11
|
var _Types = require("./Types");
|
|
11
12
|
var _Sort = require("../../utils/Sort");
|
|
13
|
+
var _FilterItemStyle = _interopRequireDefault(require("./FilterItemStyle"));
|
|
12
14
|
function _interopRequireDefault(obj) { return obj && obj.__esModule ? obj : { default: obj }; }
|
|
15
|
+
function _getRequireWildcardCache(nodeInterop) { if (typeof WeakMap !== "function") return null; var cacheBabelInterop = new WeakMap(); var cacheNodeInterop = new WeakMap(); return (_getRequireWildcardCache = function _getRequireWildcardCache(nodeInterop) { return nodeInterop ? cacheNodeInterop : cacheBabelInterop; })(nodeInterop); }
|
|
16
|
+
function _interopRequireWildcard(obj, nodeInterop) { if (!nodeInterop && obj && obj.__esModule) { return obj; } if (obj === null || typeof obj !== "object" && typeof obj !== "function") { return { default: obj }; } var cache = _getRequireWildcardCache(nodeInterop); if (cache && cache.has(obj)) { return cache.get(obj); } var newObj = {}; var hasPropertyDescriptor = Object.defineProperty && Object.getOwnPropertyDescriptor; for (var key in obj) { if (key !== "default" && Object.prototype.hasOwnProperty.call(obj, key)) { var desc = hasPropertyDescriptor ? Object.getOwnPropertyDescriptor(obj, key) : null; if (desc && (desc.get || desc.set)) { Object.defineProperty(newObj, key, desc); } else { newObj[key] = obj[key]; } } } newObj.default = obj; if (cache) { cache.set(obj, newObj); } return newObj; }
|
|
13
17
|
function ownKeys(object, enumerableOnly) { var keys = Object.keys(object); if (Object.getOwnPropertySymbols) { var symbols = Object.getOwnPropertySymbols(object); enumerableOnly && (symbols = symbols.filter(function (sym) { return Object.getOwnPropertyDescriptor(object, sym).enumerable; })), keys.push.apply(keys, symbols); } return keys; }
|
|
14
18
|
function _objectSpread(target) { for (var i = 1; i < arguments.length; i++) { var source = null != arguments[i] ? arguments[i] : {}; i % 2 ? ownKeys(Object(source), !0).forEach(function (key) { _defineProperty(target, key, source[key]); }) : Object.getOwnPropertyDescriptors ? Object.defineProperties(target, Object.getOwnPropertyDescriptors(source)) : ownKeys(Object(source)).forEach(function (key) { Object.defineProperty(target, key, Object.getOwnPropertyDescriptor(source, key)); }); } return target; }
|
|
15
19
|
function _defineProperty(obj, key, value) { key = _toPropertyKey(key); if (key in obj) { Object.defineProperty(obj, key, { value: value, enumerable: true, configurable: true, writable: true }); } else { obj[key] = value; } return obj; }
|
|
16
20
|
function _toPropertyKey(arg) { var key = _toPrimitive(arg, "string"); return typeof key === "symbol" ? key : String(key); }
|
|
17
21
|
function _toPrimitive(input, hint) { if (typeof input !== "object" || input === null) return input; var prim = input[Symbol.toPrimitive]; if (prim !== undefined) { var res = prim.call(input, hint || "default"); if (typeof res !== "object") return res; throw new TypeError("@@toPrimitive must return a primitive value."); } return (hint === "string" ? String : Number)(input); }
|
|
22
|
+
const INITIAL_ITEM_SIZE = 20;
|
|
18
23
|
const FilterItems = _ref => {
|
|
19
24
|
let {
|
|
20
25
|
facet,
|
|
21
|
-
sortBy
|
|
26
|
+
sortBy = null,
|
|
27
|
+
classes
|
|
22
28
|
} = _ref;
|
|
23
29
|
const {
|
|
24
30
|
type,
|
|
25
31
|
datafield,
|
|
26
32
|
section
|
|
27
33
|
} = facet;
|
|
34
|
+
const [displayCount, setDisplayCount] = (0, _react.useState)(INITIAL_ITEM_SIZE);
|
|
35
|
+
const scrollableRef = (0, _react.useRef)(null);
|
|
28
36
|
const sortFilters = (0, _Sort.sortBySection)(_objectSpread(_objectSpread({}, facet), {}, {
|
|
29
37
|
sortBy
|
|
30
38
|
}));
|
|
39
|
+
|
|
40
|
+
// Memoized scroll handler
|
|
41
|
+
const handleScroll = (0, _react.useCallback)(uncheckedCount => e => {
|
|
42
|
+
if (displayCount < uncheckedCount && uncheckedCount > INITIAL_ITEM_SIZE) {
|
|
43
|
+
const {
|
|
44
|
+
scrollTop,
|
|
45
|
+
scrollHeight,
|
|
46
|
+
clientHeight
|
|
47
|
+
} = e.target;
|
|
48
|
+
if (scrollHeight > clientHeight) {
|
|
49
|
+
const position = Math.ceil(scrollTop / (scrollHeight - clientHeight) * 100);
|
|
50
|
+
if (position >= 90) {
|
|
51
|
+
setDisplayCount(prevCount => prevCount + INITIAL_ITEM_SIZE);
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
}, [displayCount]);
|
|
31
56
|
const filterItems = () => {
|
|
32
57
|
switch (type) {
|
|
33
58
|
case _Types.InputTypes.CHECKBOX:
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
59
|
+
{
|
|
60
|
+
// Only use lazy loading if we have more items than the initial size
|
|
61
|
+
const uncheckedCount = sortFilters.filter(item => !item.isChecked).length;
|
|
62
|
+
if (uncheckedCount <= INITIAL_ITEM_SIZE) {
|
|
63
|
+
// Render all items normally if below threshold
|
|
64
|
+
return sortFilters.map((item, index) => /*#__PURE__*/_react.default.createElement(_ReduxCheckbox.default, {
|
|
65
|
+
key: "all-".concat(item.name, "-").concat(index),
|
|
66
|
+
checkboxItem: _objectSpread(_objectSpread({}, item), {}, {
|
|
67
|
+
index,
|
|
68
|
+
section
|
|
69
|
+
}),
|
|
70
|
+
datafield: datafield,
|
|
71
|
+
facet: facet
|
|
72
|
+
}));
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
// Single pass: separate checked and unchecked items with their indices - O(n) complexity
|
|
76
|
+
const checkedItemsWithIndices = [];
|
|
77
|
+
const uncheckedItemsWithIndices = [];
|
|
78
|
+
sortFilters.forEach((item, originalIndex) => {
|
|
79
|
+
if (item.isChecked) {
|
|
80
|
+
checkedItemsWithIndices.push({
|
|
81
|
+
item,
|
|
82
|
+
originalIndex
|
|
83
|
+
});
|
|
84
|
+
} else {
|
|
85
|
+
uncheckedItemsWithIndices.push({
|
|
86
|
+
item,
|
|
87
|
+
originalIndex
|
|
88
|
+
});
|
|
89
|
+
}
|
|
90
|
+
});
|
|
91
|
+
|
|
92
|
+
// Always show checked items first
|
|
93
|
+
const checkedItems = checkedItemsWithIndices.map(_ref2 => {
|
|
94
|
+
let {
|
|
95
|
+
item,
|
|
96
|
+
originalIndex
|
|
97
|
+
} = _ref2;
|
|
98
|
+
return /*#__PURE__*/_react.default.createElement(_ReduxCheckbox.default, {
|
|
99
|
+
key: "checked-".concat(item.name, "-").concat(originalIndex),
|
|
100
|
+
checkboxItem: _objectSpread(_objectSpread({}, item), {}, {
|
|
101
|
+
index: originalIndex,
|
|
102
|
+
section
|
|
103
|
+
}),
|
|
104
|
+
datafield: datafield,
|
|
105
|
+
facet: facet
|
|
106
|
+
});
|
|
107
|
+
});
|
|
108
|
+
const uncheckedItems = uncheckedItemsWithIndices.slice(0, displayCount).map(_ref3 => {
|
|
109
|
+
let {
|
|
110
|
+
item,
|
|
111
|
+
originalIndex
|
|
112
|
+
} = _ref3;
|
|
113
|
+
return /*#__PURE__*/_react.default.createElement(_ReduxCheckbox.default, {
|
|
114
|
+
key: "unchecked-".concat(item.name, "-").concat(originalIndex),
|
|
115
|
+
checkboxItem: _objectSpread(_objectSpread({}, item), {}, {
|
|
116
|
+
index: originalIndex,
|
|
117
|
+
section
|
|
118
|
+
}),
|
|
119
|
+
datafield: datafield,
|
|
120
|
+
facet: facet
|
|
121
|
+
});
|
|
122
|
+
});
|
|
123
|
+
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, /*#__PURE__*/_react.default.createElement("div", null, checkedItems), /*#__PURE__*/_react.default.createElement("div", {
|
|
124
|
+
ref: scrollableRef,
|
|
125
|
+
className: classes.itemsContainer,
|
|
126
|
+
onScroll: handleScroll(uncheckedItemsWithIndices.length)
|
|
127
|
+
}, uncheckedItems));
|
|
128
|
+
}
|
|
42
129
|
case _Types.InputTypes.SLIDER:
|
|
43
130
|
return /*#__PURE__*/_react.default.createElement(_ReduxSlider.default, {
|
|
44
131
|
facet: facet
|
|
@@ -47,7 +134,13 @@ const FilterItems = _ref => {
|
|
|
47
134
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null);
|
|
48
135
|
}
|
|
49
136
|
};
|
|
137
|
+
(0, _react.useEffect)(() => {
|
|
138
|
+
setDisplayCount(INITIAL_ITEM_SIZE);
|
|
139
|
+
if (scrollableRef.current) {
|
|
140
|
+
scrollableRef.current.scrollTo(0, 0);
|
|
141
|
+
}
|
|
142
|
+
}, [sortBy]);
|
|
50
143
|
return /*#__PURE__*/_react.default.createElement(_react.default.Fragment, null, filterItems());
|
|
51
144
|
};
|
|
52
|
-
var _default = FilterItems;
|
|
145
|
+
var _default = (0, _core.withStyles)(_FilterItemStyle.default)(FilterItems);
|
|
53
146
|
exports.default = _default;
|
|
@@ -18,9 +18,11 @@ function InputMinMaxView(_ref) {
|
|
|
18
18
|
minLowerBound,
|
|
19
19
|
maxUpperBound,
|
|
20
20
|
onInputChange,
|
|
21
|
-
type
|
|
21
|
+
type,
|
|
22
|
+
disabled = false
|
|
22
23
|
} = _ref;
|
|
23
24
|
const handleInputChange = e => {
|
|
25
|
+
if (disabled) return;
|
|
24
26
|
const minMaxRange = [lowerBoundVal, upperBoundVal];
|
|
25
27
|
if (type === _Types.silderTypes.INPUT_MIN) {
|
|
26
28
|
minMaxRange[0] = Number(e.target.value);
|
|
@@ -35,6 +37,7 @@ function InputMinMaxView(_ref) {
|
|
|
35
37
|
id: "slider_".concat(type),
|
|
36
38
|
className: classes["slider_".concat(type)],
|
|
37
39
|
onChange: event => handleInputChange(event),
|
|
40
|
+
disabled: disabled,
|
|
38
41
|
inputProps: {
|
|
39
42
|
step: 1,
|
|
40
43
|
min: minLowerBound,
|
|
@@ -5,6 +5,7 @@ Object.defineProperty(exports, "__esModule", {
|
|
|
5
5
|
});
|
|
6
6
|
exports.default = void 0;
|
|
7
7
|
var _react = _interopRequireWildcard(require("react"));
|
|
8
|
+
var _clsx = _interopRequireDefault(require("clsx"));
|
|
8
9
|
var _core = require("@material-ui/core");
|
|
9
10
|
var _Types = require("../Types");
|
|
10
11
|
var _InputMinMaxView = _interopRequireDefault(require("./InputMinMaxView"));
|
|
@@ -30,11 +31,14 @@ const SliderView = _ref => {
|
|
|
30
31
|
datafield,
|
|
31
32
|
facetValues
|
|
32
33
|
} = facet;
|
|
34
|
+
// Check if bounds are invalid (both are 0)
|
|
35
|
+
const isBoundsInvalid = !facetValues || facetValues.length === 0 || facetValues[0] === 0 && facetValues[1] === 0;
|
|
33
36
|
const lowerBoundValue = facetValues[0];
|
|
34
37
|
const upperBoundValue = facetValues[1];
|
|
35
38
|
|
|
36
39
|
// Determines whether the lower bound and upper bound values are valid
|
|
37
40
|
const isValid = () => {
|
|
41
|
+
if (isBoundsInvalid) return false;
|
|
38
42
|
const checks = [lowerBoundValue <= upperBoundValue, lowerBoundValue >= minLowerBound, upperBoundValue <= maxUpperBound];
|
|
39
43
|
return checks.every(condition => condition === true);
|
|
40
44
|
};
|
|
@@ -74,7 +78,8 @@ const SliderView = _ref => {
|
|
|
74
78
|
minLowerBound: minLowerBound,
|
|
75
79
|
maxUpperBound: maxUpperBound,
|
|
76
80
|
type: _Types.silderTypes.INPUT_MIN,
|
|
77
|
-
onInputChange: handleChangeCommittedSlider
|
|
81
|
+
onInputChange: handleChangeCommittedSlider,
|
|
82
|
+
disabled: isBoundsInvalid
|
|
78
83
|
})), /*#__PURE__*/_react.default.createElement("div", {
|
|
79
84
|
className: classes.maxValue
|
|
80
85
|
}, /*#__PURE__*/_react.default.createElement(_core.Typography, {
|
|
@@ -86,23 +91,31 @@ const SliderView = _ref => {
|
|
|
86
91
|
minLowerBound: minLowerBound,
|
|
87
92
|
maxUpperBound: maxUpperBound,
|
|
88
93
|
type: _Types.silderTypes.INPUT_MAX,
|
|
89
|
-
onInputChange: handleChangeCommittedSlider
|
|
94
|
+
onInputChange: handleChangeCommittedSlider,
|
|
95
|
+
disabled: isBoundsInvalid
|
|
90
96
|
}))), /*#__PURE__*/_react.default.createElement("div", {
|
|
91
97
|
className: classes.slider
|
|
92
98
|
}, /*#__PURE__*/_react.default.createElement(_core.Slider, {
|
|
93
99
|
disableSwap: true,
|
|
100
|
+
disabled: isBoundsInvalid,
|
|
94
101
|
getAriaValueText: valuetext,
|
|
95
|
-
onChange: handleChangeSlider,
|
|
96
|
-
onChangeCommitted: (event, value) => handleChangeCommittedSlider(value),
|
|
97
|
-
value: [...sliderValue],
|
|
102
|
+
onChange: isBoundsInvalid ? undefined : handleChangeSlider,
|
|
103
|
+
onChangeCommitted: isBoundsInvalid ? undefined : (event, value) => handleChangeCommittedSlider(value),
|
|
104
|
+
value: maxUpperBound === 0 ? [0, 0] : [...sliderValue],
|
|
98
105
|
valueLabelDisplay: "auto",
|
|
99
106
|
min: minLowerBound,
|
|
100
|
-
max: maxUpperBound,
|
|
107
|
+
max: maxUpperBound === 0 ? undefined : maxUpperBound,
|
|
101
108
|
classes: {
|
|
102
|
-
colorPrimary: classes.colorPrimary,
|
|
103
|
-
rail: classes.rail,
|
|
104
|
-
thumb:
|
|
105
|
-
|
|
109
|
+
colorPrimary: (0, _clsx.default)("colorPrimary".concat(facet.section), classes.colorPrimary),
|
|
110
|
+
rail: (0, _clsx.default)("rail".concat(facet.section), classes.rail),
|
|
111
|
+
thumb: (0, _clsx.default)("thumb".concat(facet.section), {
|
|
112
|
+
isThumbValid: isValid(),
|
|
113
|
+
invalidThumb: !isValid()
|
|
114
|
+
}),
|
|
115
|
+
track: (0, _clsx.default)("track".concat(facet.section), {
|
|
116
|
+
isTrackValid: isValid(),
|
|
117
|
+
invalidTrack: !isValid()
|
|
118
|
+
})
|
|
106
119
|
}
|
|
107
120
|
})), /*#__PURE__*/_react.default.createElement(_core.Box, {
|
|
108
121
|
className: classes.lowerUpperBound
|
|
@@ -110,7 +123,7 @@ const SliderView = _ref => {
|
|
|
110
123
|
className: classes.lowerBound
|
|
111
124
|
}, minLowerBound.toLocaleString()), /*#__PURE__*/_react.default.createElement(_core.Typography, {
|
|
112
125
|
className: classes.upperBound
|
|
113
|
-
}, maxUpperBound.toLocaleString()))), (sliderValue[0] > minLowerBound || sliderValue[1] < maxUpperBound) && /*#__PURE__*/_react.default.createElement(_core.Typography, {
|
|
126
|
+
}, minLowerBound === 0 && maxUpperBound === 0 ? '-' : maxUpperBound !== 0 ? maxUpperBound.toLocaleString() : '.'))), (sliderValue[0] > minLowerBound || sliderValue[1] < maxUpperBound) && /*#__PURE__*/_react.default.createElement(_core.Typography, {
|
|
114
127
|
className: isValid() ? classes.sliderText : classes.invalidSliderText
|
|
115
128
|
}, sliderValue[0], ' - ', sliderValue[1], "\xA0", quantifier));
|
|
116
129
|
};
|
package/package.json
CHANGED
|
@@ -54,7 +54,11 @@ const FacetFilterController = (props) => {
|
|
|
54
54
|
*/
|
|
55
55
|
if (sideBar.type === InputTypes.SLIDER) {
|
|
56
56
|
const { minLowerBound, maxUpperBound } = sideBar;
|
|
57
|
-
|
|
57
|
+
if (minLowerBound === 0 && maxUpperBound === 0) {
|
|
58
|
+
sideBar.facetValues = [0, 0];
|
|
59
|
+
} else {
|
|
60
|
+
sideBar.facetValues = [minLowerBound, maxUpperBound];
|
|
61
|
+
}
|
|
58
62
|
}
|
|
59
63
|
});
|
|
60
64
|
}
|
|
@@ -65,7 +69,7 @@ const FacetFilterController = (props) => {
|
|
|
65
69
|
const sideBar = {};
|
|
66
70
|
|
|
67
71
|
arr.forEach(({ section, ...item }) => {
|
|
68
|
-
const { isExpanded } =facetSectionConfig[section];
|
|
72
|
+
const { isExpanded } = facetSectionConfig[section];
|
|
69
73
|
if (!sideBar[section]) {
|
|
70
74
|
sideBar[section] = {
|
|
71
75
|
name: section,
|
|
@@ -110,14 +114,17 @@ const FacetFilterController = (props) => {
|
|
|
110
114
|
if (facet.type === InputTypes.SLIDER) {
|
|
111
115
|
const lowerBound = data[apiForFiltering][ApiLowerBoundName];
|
|
112
116
|
const upperBound = data[apiForFiltering][ApiUpperBoundName];
|
|
117
|
+
|
|
113
118
|
if (lowerBound === 0 && upperBound === 0) {
|
|
114
|
-
updateFacet.
|
|
119
|
+
updateFacet.minLowerBound = 0;
|
|
120
|
+
updateFacet.maxUpperBound = 0;
|
|
121
|
+
updateFacet.facetValues = [];
|
|
115
122
|
} else {
|
|
116
123
|
updateFacet.minLowerBound = lowerBound;
|
|
117
124
|
updateFacet.maxUpperBound = upperBound;
|
|
118
125
|
updateFacet.facetValues = [lowerBound, upperBound];
|
|
119
|
-
updateFacet.style = facet.style;
|
|
120
126
|
}
|
|
127
|
+
updateFacet.style = facet.style;
|
|
121
128
|
}
|
|
122
129
|
}
|
|
123
130
|
if (!updateFacet.filterOut) {
|
|
@@ -50,6 +50,8 @@ export default () => ({
|
|
|
50
50
|
fontSize: '11px',
|
|
51
51
|
marginRight: '12px',
|
|
52
52
|
marginLeft: '24px',
|
|
53
|
+
display: 'flex',
|
|
54
|
+
alignItems: 'center',
|
|
53
55
|
},
|
|
54
56
|
sortGroupItem: {
|
|
55
57
|
cursor: 'pointer',
|
|
@@ -58,8 +60,6 @@ export default () => ({
|
|
|
58
60
|
// marginRight: '32px',
|
|
59
61
|
},
|
|
60
62
|
NonSortGroup: {
|
|
61
|
-
marginBottom: '5px',
|
|
62
|
-
borderTop: '1px solid #B1B1B1',
|
|
63
63
|
textAlign: 'left',
|
|
64
64
|
paddingLeft: '10px',
|
|
65
65
|
},
|
|
@@ -89,18 +89,7 @@ const FacetView = ({
|
|
|
89
89
|
</div>
|
|
90
90
|
</CustomAccordionSummary>
|
|
91
91
|
)}
|
|
92
|
-
|
|
93
|
-
(facetValues.length === 0)
|
|
94
|
-
&& (
|
|
95
|
-
<div className={classes.NonSortGroup}>
|
|
96
|
-
<span
|
|
97
|
-
className={classes.NonSortGroupItem}
|
|
98
|
-
>
|
|
99
|
-
No data for this field
|
|
100
|
-
</span>
|
|
101
|
-
</div>
|
|
102
|
-
)
|
|
103
|
-
}
|
|
92
|
+
|
|
104
93
|
{
|
|
105
94
|
(facet.type === InputTypes.SLIDER || facetValues.length > 0)
|
|
106
95
|
&& (
|
|
@@ -110,7 +99,7 @@ const FacetView = ({
|
|
|
110
99
|
>
|
|
111
100
|
<span className={classes.sortGroupIcon}>
|
|
112
101
|
<Icon
|
|
113
|
-
style={{ fontSize:
|
|
102
|
+
style={{ fontSize: 14 }}
|
|
114
103
|
onClick={onClearSection}
|
|
115
104
|
>
|
|
116
105
|
<img
|
|
@@ -120,6 +109,17 @@ const FacetView = ({
|
|
|
120
109
|
alt="clear-icon"
|
|
121
110
|
/>
|
|
122
111
|
</Icon>
|
|
112
|
+
|
|
113
|
+
{facetValues.length === 0
|
|
114
|
+
&& (
|
|
115
|
+
<div className={classes.NonSortGroup}>
|
|
116
|
+
<span
|
|
117
|
+
className={classes.NonSortGroupItem}
|
|
118
|
+
>
|
|
119
|
+
No data for this field
|
|
120
|
+
</span>
|
|
121
|
+
</div>
|
|
122
|
+
)}
|
|
123
123
|
</span>
|
|
124
124
|
{ (facet.type === InputTypes.CHECKBOX && facetValues.length > 0)
|
|
125
125
|
&& (
|
|
@@ -1,28 +1,105 @@
|
|
|
1
1
|
/* eslint-disable react/jsx-wrap-multilines */
|
|
2
2
|
/* eslint-disable react/jsx-indent */
|
|
3
|
-
import React
|
|
3
|
+
import React,
|
|
4
|
+
{
|
|
5
|
+
useEffect, useState, useRef, useCallback,
|
|
6
|
+
} from 'react';
|
|
7
|
+
import { withStyles } from '@material-ui/core';
|
|
4
8
|
import ReduxCheckbox from './checkbox/ReduxCheckbox';
|
|
5
9
|
import ReduxSlider from './slider/ReduxSlider';
|
|
6
10
|
import { InputTypes } from './Types';
|
|
7
11
|
import { sortBySection } from '../../utils/Sort';
|
|
12
|
+
import styles from './FilterItemStyle';
|
|
13
|
+
|
|
14
|
+
const INITIAL_ITEM_SIZE = 20;
|
|
8
15
|
|
|
9
16
|
const FilterItems = ({
|
|
10
17
|
facet,
|
|
11
|
-
sortBy,
|
|
18
|
+
sortBy = null,
|
|
19
|
+
classes,
|
|
12
20
|
}) => {
|
|
13
21
|
const { type, datafield, section } = facet;
|
|
22
|
+
const [displayCount, setDisplayCount] = useState(INITIAL_ITEM_SIZE);
|
|
23
|
+
const scrollableRef = useRef(null);
|
|
14
24
|
const sortFilters = sortBySection({ ...facet, sortBy });
|
|
15
25
|
|
|
26
|
+
// Memoized scroll handler
|
|
27
|
+
const handleScroll = useCallback((uncheckedCount) => (e) => {
|
|
28
|
+
if (displayCount < uncheckedCount && uncheckedCount > INITIAL_ITEM_SIZE) {
|
|
29
|
+
const { scrollTop, scrollHeight, clientHeight } = e.target;
|
|
30
|
+
if (scrollHeight > clientHeight) {
|
|
31
|
+
const position = Math.ceil((scrollTop / (scrollHeight - clientHeight)) * 100);
|
|
32
|
+
if (position >= 90) {
|
|
33
|
+
setDisplayCount((prevCount) => prevCount + INITIAL_ITEM_SIZE);
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}, [displayCount]);
|
|
38
|
+
|
|
16
39
|
const filterItems = () => {
|
|
17
40
|
switch (type) {
|
|
18
|
-
case InputTypes.CHECKBOX:
|
|
19
|
-
|
|
41
|
+
case InputTypes.CHECKBOX: {
|
|
42
|
+
// Only use lazy loading if we have more items than the initial size
|
|
43
|
+
const uncheckedCount = sortFilters.filter((item) => !item.isChecked).length;
|
|
44
|
+
if (uncheckedCount <= INITIAL_ITEM_SIZE) {
|
|
45
|
+
// Render all items normally if below threshold
|
|
46
|
+
return sortFilters.map((item, index) => (
|
|
47
|
+
<ReduxCheckbox
|
|
48
|
+
key={`all-${item.name}-${index}`}
|
|
49
|
+
checkboxItem={{ ...item, index, section }}
|
|
50
|
+
datafield={datafield}
|
|
51
|
+
facet={facet}
|
|
52
|
+
/>
|
|
53
|
+
));
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// Single pass: separate checked and unchecked items with their indices - O(n) complexity
|
|
57
|
+
const checkedItemsWithIndices = [];
|
|
58
|
+
const uncheckedItemsWithIndices = [];
|
|
59
|
+
|
|
60
|
+
sortFilters.forEach((item, originalIndex) => {
|
|
61
|
+
if (item.isChecked) {
|
|
62
|
+
checkedItemsWithIndices.push({ item, originalIndex });
|
|
63
|
+
} else {
|
|
64
|
+
uncheckedItemsWithIndices.push({ item, originalIndex });
|
|
65
|
+
}
|
|
66
|
+
});
|
|
67
|
+
|
|
68
|
+
// Always show checked items first
|
|
69
|
+
const checkedItems = checkedItemsWithIndices.map(({ item, originalIndex }) => (
|
|
20
70
|
<ReduxCheckbox
|
|
21
|
-
|
|
71
|
+
key={`checked-${item.name}-${originalIndex}`}
|
|
72
|
+
checkboxItem={{ ...item, index: originalIndex, section }}
|
|
22
73
|
datafield={datafield}
|
|
23
74
|
facet={facet}
|
|
24
75
|
/>
|
|
25
76
|
));
|
|
77
|
+
const uncheckedItems = uncheckedItemsWithIndices
|
|
78
|
+
.slice(0, displayCount)
|
|
79
|
+
.map(({ item, originalIndex }) => (
|
|
80
|
+
<ReduxCheckbox
|
|
81
|
+
key={`unchecked-${item.name}-${originalIndex}`}
|
|
82
|
+
checkboxItem={{ ...item, index: originalIndex, section }}
|
|
83
|
+
datafield={datafield}
|
|
84
|
+
facet={facet}
|
|
85
|
+
/>
|
|
86
|
+
));
|
|
87
|
+
|
|
88
|
+
return (
|
|
89
|
+
<>
|
|
90
|
+
<div>
|
|
91
|
+
{checkedItems}
|
|
92
|
+
</div>
|
|
93
|
+
<div
|
|
94
|
+
ref={scrollableRef}
|
|
95
|
+
className={classes.itemsContainer}
|
|
96
|
+
onScroll={handleScroll(uncheckedItemsWithIndices.length)}
|
|
97
|
+
>
|
|
98
|
+
{uncheckedItems}
|
|
99
|
+
</div>
|
|
100
|
+
</>
|
|
101
|
+
);
|
|
102
|
+
}
|
|
26
103
|
case InputTypes.SLIDER:
|
|
27
104
|
return (<ReduxSlider facet={facet} />);
|
|
28
105
|
default:
|
|
@@ -30,6 +107,13 @@ const FilterItems = ({
|
|
|
30
107
|
}
|
|
31
108
|
};
|
|
32
109
|
|
|
110
|
+
useEffect(() => {
|
|
111
|
+
setDisplayCount(INITIAL_ITEM_SIZE);
|
|
112
|
+
if (scrollableRef.current) {
|
|
113
|
+
scrollableRef.current.scrollTo(0, 0);
|
|
114
|
+
}
|
|
115
|
+
}, [sortBy]);
|
|
116
|
+
|
|
33
117
|
return (
|
|
34
118
|
<>
|
|
35
119
|
{filterItems()}
|
|
@@ -37,4 +121,4 @@ const FilterItems = ({
|
|
|
37
121
|
);
|
|
38
122
|
};
|
|
39
123
|
|
|
40
|
-
export default FilterItems;
|
|
124
|
+
export default withStyles(styles)(FilterItems);
|
|
@@ -14,8 +14,10 @@ function InputMinMaxView({
|
|
|
14
14
|
maxUpperBound,
|
|
15
15
|
onInputChange,
|
|
16
16
|
type,
|
|
17
|
+
disabled = false,
|
|
17
18
|
}) {
|
|
18
19
|
const handleInputChange = (e) => {
|
|
20
|
+
if (disabled) return;
|
|
19
21
|
const minMaxRange = [lowerBoundVal, upperBoundVal];
|
|
20
22
|
if (type === silderTypes.INPUT_MIN) {
|
|
21
23
|
minMaxRange[0] = Number(e.target.value);
|
|
@@ -32,6 +34,7 @@ function InputMinMaxView({
|
|
|
32
34
|
id={`slider_${type}`}
|
|
33
35
|
className={classes[`slider_${type}`]}
|
|
34
36
|
onChange={(event) => handleInputChange(event)}
|
|
37
|
+
disabled={disabled}
|
|
35
38
|
inputProps={{
|
|
36
39
|
step: 1,
|
|
37
40
|
min: minLowerBound,
|
|
@@ -3,6 +3,7 @@
|
|
|
3
3
|
/* eslint-disable react/jsx-indent */
|
|
4
4
|
/* eslint-disable object-curly-newline */
|
|
5
5
|
import React, { useEffect, useState } from 'react';
|
|
6
|
+
import clsx from 'clsx';
|
|
6
7
|
import { withStyles, Slider, Typography, Box } from '@material-ui/core';
|
|
7
8
|
// import styles from './SliderStyle';
|
|
8
9
|
import { silderTypes } from '../Types';
|
|
@@ -15,11 +16,16 @@ const SliderView = ({
|
|
|
15
16
|
filterState,
|
|
16
17
|
}) => {
|
|
17
18
|
const { minLowerBound, maxUpperBound, quantifier, datafield, facetValues } = facet;
|
|
19
|
+
// Check if bounds are invalid (both are 0)
|
|
20
|
+
const isBoundsInvalid = !facetValues
|
|
21
|
+
|| facetValues.length === 0
|
|
22
|
+
|| (facetValues[0] === 0 && facetValues[1] === 0);
|
|
18
23
|
const lowerBoundValue = facetValues[0];
|
|
19
24
|
const upperBoundValue = facetValues[1];
|
|
20
25
|
|
|
21
26
|
// Determines whether the lower bound and upper bound values are valid
|
|
22
27
|
const isValid = () => {
|
|
28
|
+
if (isBoundsInvalid) return false;
|
|
23
29
|
const checks = [
|
|
24
30
|
lowerBoundValue <= upperBoundValue,
|
|
25
31
|
lowerBoundValue >= minLowerBound,
|
|
@@ -66,6 +72,7 @@ const SliderView = ({
|
|
|
66
72
|
maxUpperBound={maxUpperBound}
|
|
67
73
|
type={silderTypes.INPUT_MIN}
|
|
68
74
|
onInputChange={handleChangeCommittedSlider}
|
|
75
|
+
disabled={isBoundsInvalid}
|
|
69
76
|
/>
|
|
70
77
|
</div>
|
|
71
78
|
<div className={classes.maxValue}>
|
|
@@ -80,6 +87,7 @@ const SliderView = ({
|
|
|
80
87
|
maxUpperBound={maxUpperBound}
|
|
81
88
|
type={silderTypes.INPUT_MAX}
|
|
82
89
|
onInputChange={handleChangeCommittedSlider}
|
|
90
|
+
disabled={isBoundsInvalid}
|
|
83
91
|
/>
|
|
84
92
|
</div>
|
|
85
93
|
</div>
|
|
@@ -87,18 +95,27 @@ const SliderView = ({
|
|
|
87
95
|
{/* Change to red if invalid range */}
|
|
88
96
|
<Slider
|
|
89
97
|
disableSwap
|
|
98
|
+
disabled={isBoundsInvalid}
|
|
90
99
|
getAriaValueText={valuetext}
|
|
91
|
-
onChange={handleChangeSlider}
|
|
92
|
-
onChangeCommitted={
|
|
93
|
-
|
|
100
|
+
onChange={isBoundsInvalid ? undefined : handleChangeSlider}
|
|
101
|
+
onChangeCommitted={
|
|
102
|
+
isBoundsInvalid ? undefined : (event, value) => handleChangeCommittedSlider(value)
|
|
103
|
+
}
|
|
104
|
+
value={maxUpperBound === 0 ? [0, 0] : [...sliderValue]}
|
|
94
105
|
valueLabelDisplay="auto"
|
|
95
106
|
min={minLowerBound}
|
|
96
|
-
max={maxUpperBound}
|
|
107
|
+
max={maxUpperBound === 0 ? undefined : maxUpperBound}
|
|
97
108
|
classes={{
|
|
98
|
-
colorPrimary: classes.colorPrimary,
|
|
99
|
-
rail: classes.rail,
|
|
100
|
-
thumb:
|
|
101
|
-
|
|
109
|
+
colorPrimary: clsx(`colorPrimary${facet.section}`, classes.colorPrimary),
|
|
110
|
+
rail: clsx(`rail${facet.section}`, classes.rail),
|
|
111
|
+
thumb: clsx(`thumb${facet.section}`, {
|
|
112
|
+
isThumbValid: isValid(),
|
|
113
|
+
invalidThumb: !isValid(),
|
|
114
|
+
}),
|
|
115
|
+
track: clsx(`track${facet.section}`, {
|
|
116
|
+
isTrackValid: isValid(),
|
|
117
|
+
invalidTrack: !isValid(),
|
|
118
|
+
}),
|
|
102
119
|
}}
|
|
103
120
|
/>
|
|
104
121
|
</div>
|
|
@@ -107,7 +124,7 @@ const SliderView = ({
|
|
|
107
124
|
{minLowerBound.toLocaleString()}
|
|
108
125
|
</Typography>
|
|
109
126
|
<Typography className={classes.upperBound}>
|
|
110
|
-
{maxUpperBound.toLocaleString()}
|
|
127
|
+
{(minLowerBound === 0 && maxUpperBound === 0) ? '-' : (maxUpperBound !== 0 ? maxUpperBound.toLocaleString() : '.')}
|
|
111
128
|
</Typography>
|
|
112
129
|
</Box>
|
|
113
130
|
</div>
|