Merge pull request #125 from ONLYOFFICE/hotfix/filter-input-hotfix

Hotfix/filter input hotfix
This commit is contained in:
Alexey Safronov 2020-11-17 11:31:06 +03:00 committed by GitHub
commit d6c58eb49c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
9 changed files with 415 additions and 369 deletions

View File

@ -16,10 +16,12 @@ import find from "lodash/find";
import result from "lodash/result"; import result from "lodash/result";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import { withRouter } from "react-router"; import { withRouter } from "react-router";
import { constants, FilterInput, store, Loaders } from "asc-web-common"; import { constants, FilterInput, store, Loaders, utils } from "asc-web-common";
import isEqual from "lodash/isEqual"; import isEqual from "lodash/isEqual";
import { isMobileOnly } from "react-device-detect"; import { isMobileOnly } from "react-device-detect";
const { withLayoutSize } = utils;
const { const {
getCurrentUser, getCurrentUser,
getSettingsCustomNames, getSettingsCustomNames,
@ -287,20 +289,22 @@ class SectionFilterContent extends React.Component {
this.props.selectedFolderId !== nextProps.selectedFolderId || this.props.selectedFolderId !== nextProps.selectedFolderId ||
this.state.isReady !== nextState.isReady || this.state.isReady !== nextState.isReady ||
this.props.viewAs !== nextProps.viewAs || this.props.viewAs !== nextProps.viewAs ||
this.props.firstLoad !== nextProps.firstLoad this.props.firstLoad !== nextProps.firstLoad ||
this.props.sectionWidth !== nextProps.sectionWidth
); );
} }
render() { render() {
console.log("Filter render"); console.log("Filter render");
const selectedFilterData = this.getSelectedFilterData(); const selectedFilterData = this.getSelectedFilterData();
const { t, language, firstLoad } = this.props; const { t, language, firstLoad, sectionWidth } = this.props;
const filterColumnCount = const filterColumnCount =
window.innerWidth < 500 ? {} : { filterColumnCount: 3 }; window.innerWidth < 500 ? {} : { filterColumnCount: 3 };
return firstLoad ? ( return firstLoad ? (
<Loaders.Filter /> <Loaders.Filter />
) : ( ) : (
<FilterInput <FilterInput
sectionWidth={sectionWidth}
getFilterData={this.getData} getFilterData={this.getData}
getSortData={this.getSortData} getSortData={this.getSortData}
selectedFilterData={selectedFilterData} selectedFilterData={selectedFilterData}
@ -338,4 +342,4 @@ export default connect(mapStateToProps, {
fetchFiles, fetchFiles,
setViewAs, setViewAs,
setIsLoading, setIsLoading,
})(withRouter(withTranslation()(SectionFilterContent))); })(withRouter(withLayoutSize(withTranslation()(SectionFilterContent))));

View File

@ -6,7 +6,7 @@ import result from "lodash/result";
import { withTranslation } from "react-i18next"; import { withTranslation } from "react-i18next";
import { withRouter } from "react-router"; import { withRouter } from "react-router";
import { getFilterByLocation } from "../../../../../helpers/converters"; import { getFilterByLocation } from "../../../../../helpers/converters";
import { store, FilterInput, Loaders } from "asc-web-common"; import { store, FilterInput, Loaders, utils } from "asc-web-common";
import { isMobileOnly } from "react-device-detect"; import { isMobileOnly } from "react-device-detect";
import { getFilter, getGroups } from "../../../../../store/people/selectors"; import { getFilter, getGroups } from "../../../../../store/people/selectors";
const { const {
@ -17,6 +17,8 @@ const {
getIsLoaded, getIsLoaded,
} = store.auth.selectors; } = store.auth.selectors;
const { withLayoutSize } = utils;
const getEmployeeStatus = (filterValues) => { const getEmployeeStatus = (filterValues) => {
const employeeStatus = result( const employeeStatus = result(
find(filterValues, (value) => { find(filterValues, (value) => {
@ -248,9 +250,10 @@ class SectionFilterContent extends React.Component {
render() { render() {
const selectedFilterData = this.getSelectedFilterData(); const selectedFilterData = this.getSelectedFilterData();
const { t, language, isLoaded } = this.props; const { t, language, isLoaded, sectionWidth } = this.props;
return isLoaded ? ( return isLoaded ? (
<FilterInput <FilterInput
sectionWidth={sectionWidth}
getFilterData={this.getData} getFilterData={this.getData}
getSortData={this.getSortData} getSortData={this.getSortData}
selectedFilterData={selectedFilterData} selectedFilterData={selectedFilterData}
@ -282,5 +285,5 @@ function mapStateToProps(state) {
} }
export default connect(mapStateToProps, { fetchPeople })( export default connect(mapStateToProps, { fetchPeople })(
withRouter(withTranslation()(SectionFilterContent)) withRouter(withLayoutSize(withTranslation()(SectionFilterContent)))
); );

View File

@ -1,6 +1,6 @@
{ {
"name": "asc-web-common", "name": "asc-web-common",
"version": "1.0.279", "version": "1.0.275",
"description": "Ascensio System SIA common components and solutions library", "description": "Ascensio System SIA common components and solutions library",
"license": "AGPL-3.0", "license": "AGPL-3.0",
"files": [ "files": [

View File

@ -2,7 +2,6 @@ import React from "react";
import PropTypes from "prop-types"; import PropTypes from "prop-types";
import { SearchInput } from "asc-web-components"; import { SearchInput } from "asc-web-components";
import isEqual from "lodash/isEqual"; import isEqual from "lodash/isEqual";
import throttle from "lodash/throttle";
import FilterBlock from "./sub-components/FilterBlock"; import FilterBlock from "./sub-components/FilterBlock";
import SortComboBox from "./sub-components/SortComboBox"; import SortComboBox from "./sub-components/SortComboBox";
import ViewSelector from "./sub-components/ViewSelector"; import ViewSelector from "./sub-components/ViewSelector";
@ -81,203 +80,122 @@ const convertToInternalData = function (fullDataArray, inputDataArray) {
return filterItems; return filterItems;
}; };
const minWidth = 170;
const filterItemPadding = 60;
const maxFilterItemWidth = 260;
const itemsContainerWidth = 40;
const sectionPaddings = 48;
class FilterInput extends React.Component { class FilterInput extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
this.isResizeUpdate = false; const { selectedFilterData, getSortData, value } = props;
this.minWidth = 190; const { sortDirection, sortId, inputValue } = selectedFilterData;
const sortData = getSortData();
function getDefaultFilterData() { const filterValues = selectedFilterData ? this.getDefaultFilterData() : [];
const filterData = props.getFilterData();
const filterItems = [];
const selectedFilterData = cloneObjectsArray(
props.selectedFilterData.filterValues
);
selectedFilterData.forEach((defaultFilterValue) => {
const filterValue = filterData.find(
(x) =>
x.key ===
defaultFilterValue.key.replace(
defaultFilterValue.group + "_",
""
) && x.group === defaultFilterValue.group
);
let groupLabel = "";
const groupFilterItem = filterData.find(
(x) => x.key === defaultFilterValue.group
);
if (groupFilterItem != undefined) {
groupLabel = groupFilterItem.label;
} else {
const subgroupFilterItem = filterData.find(
(x) => x.subgroup === defaultFilterValue.group
);
if (subgroupFilterItem != undefined) {
groupLabel = subgroupFilterItem.label;
}
}
if (filterValue != undefined) {
defaultFilterValue.key =
defaultFilterValue.group + "_" + defaultFilterValue.key;
defaultFilterValue.label = filterValue.label;
defaultFilterValue.groupLabel = groupLabel;
filterItems.push(defaultFilterValue);
}
});
return filterItems;
}
let filterValues = props.selectedFilterData ? getDefaultFilterData() : [];
filterValues = [
...filterValues,
...this.convertSelectorToInternalData(
props.getFilterData(),
cloneObjectsArray(props.selectedFilterData.filterValues)
),
];
this.state = { this.state = {
sortDirection: sortDirection: sortDirection === "desc" ? true : false,
props.selectedFilterData.sortDirection === "desc" ? true : false,
sortId: sortId:
props sortData.findIndex((x) => x.key === sortId) != -1
.getSortData() ? sortId
.findIndex((x) => x.key === props.selectedFilterData.sortId) != -1 : sortData.length > 0
? props.selectedFilterData.sortId ? sortData[0].key
: props.getSortData().length > 0
? props.getSortData()[0].key
: "", : "",
searchText: props.selectedFilterData.inputValue || props.value, searchText: inputValue || value,
filterValues, filterValues,
openFilterItems: [], openFilterItems: [],
hideFilterItems: [], hiddenFilterItems: [],
needUpdateFilter: false,
}; };
this.searchWrapper = React.createRef(); this.searchWrapper = React.createRef();
this.filterWrapper = React.createRef(); this.filterWrapper = React.createRef();
this.rectComboBoxRef = React.createRef();
this.onClickSortItem = this.onClickSortItem.bind(this);
this.onSortDirectionClick = this.onSortDirectionClick.bind(this);
this.onChangeSortDirection = this.onChangeSortDirection.bind(this);
this.onSearch = this.onSearch.bind(this);
this.onChangeFilter = this.onChangeFilter.bind(this);
this.onSearchChanged = this.onSearchChanged.bind(this);
this.getDefaultSelectedIndex = this.getDefaultSelectedIndex.bind(this);
this.updateFilter = this.updateFilter.bind(this);
this.onClickFilterItem = this.onClickFilterItem.bind(this);
this.getFilterData = this.getFilterData.bind(this);
this.onFilterRender = this.onFilterRender.bind(this);
this.onDeleteFilterItem = this.onDeleteFilterItem.bind(this);
this.clearFilter = this.clearFilter.bind(this);
this.onClickViewSelector = this.onClickViewSelector.bind(this);
this.throttledResize = throttle(this.resize, 300);
} }
componentDidMount() { componentDidMount() {
window.addEventListener("resize", this.throttledResize);
if (this.state.filterValues.length > 0) this.updateFilter(); if (this.state.filterValues.length > 0) this.updateFilter();
} }
componentWillUnmount() {
window.removeEventListener("resize", this.throttledResize);
}
componentDidUpdate(prevProps, prevState) { componentDidUpdate(prevProps, prevState) {
const { selectedFilterData, sectionWidth, getSortData } = this.props;
const { filterValues, searchText } = this.state;
const { sortDirection, sortId, inputValue } = selectedFilterData;
if ( if (
this.props.needForUpdate && this.props.needForUpdate &&
this.props.needForUpdate(prevProps, this.props) this.props.needForUpdate(prevProps, this.props)
) { ) {
let internalFilterData = convertToInternalData( let internalFilterData = convertToInternalData(
this.props.getFilterData(), this.props.getFilterData(),
cloneObjectsArray(this.props.selectedFilterData.filterValues) cloneObjectsArray(selectedFilterData.filterValues)
); );
this.updateFilter(internalFilterData); this.updateFilter(internalFilterData);
} }
if (sectionWidth !== prevProps.sectionWidth) {
this.updateFilter();
}
if (
(!isEqual(selectedFilterData.filterValues, filterValues) ||
inputValue !== searchText) &&
sectionWidth !== prevProps.sectionWidth
) {
const sortData = getSortData();
const filterValues = this.getDefaultFilterData();
this.setState({
sortDirection: sortDirection === "desc" ? true : false,
sortId:
sortData.findIndex((x) => x.key === sortId) != -1
? sortId
: sortData.length > 0
? sortData[0].key
: "",
filterValues: filterValues,
searchText: selectedFilterData.inputValue || "",
});
this.updateFilter(filterValues);
}
if (
!isEqual(
prevProps.selectedFilterData.filterValues,
selectedFilterData.filterValues
) &&
selectedFilterData.filterValues &&
(selectedFilterData.filterValues.length === 0 ||
(selectedFilterData.filterValues.length === 1 &&
selectedFilterData.filterValues[0].key === "null")) &&
!selectedFilterData.inputValue
) {
this.clearFilter();
}
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { const {
selectedFilterData, selectedFilterData,
getFilterData,
getSortData,
value, value,
id, id,
isDisabled, isDisabled,
size, size,
placeholder, placeholder,
sectionWidth,
} = this.props; } = this.props;
if (!isEqual(selectedFilterData, nextProps.selectedFilterData)) {
let internalFilterData = cloneObjectsArray(this.state.filterValues); if (
if (nextProps.selectedFilterData.filterValues) { !isEqual(selectedFilterData, nextProps.selectedFilterData) ||
internalFilterData = convertToInternalData( this.props.viewAs !== nextProps.viewAs ||
getFilterData(), this.props.widthProp !== nextProps.widthProp ||
cloneObjectsArray(nextProps.selectedFilterData.filterValues) sectionWidth !== nextProps.sectionWidth
); ) {
let internalFilterDataSelectors = this.convertSelectorToInternalData(
getFilterData(),
cloneObjectsArray(nextProps.selectedFilterData.filterValues)
);
internalFilterData = internalFilterData.concat(
internalFilterDataSelectors
);
this.updateFilter(internalFilterData);
}
this.setState({
sortDirection:
nextProps.selectedFilterData.sortDirection === "desc" ? true : false,
sortId:
getSortData().findIndex(
(x) => x.key === nextProps.selectedFilterData.sortId
) != -1
? nextProps.selectedFilterData.sortId
: "",
filterValues: internalFilterData,
searchText: nextProps.selectedFilterData.inputValue || value,
});
return true; return true;
} }
if (this.props.viewAs !== nextProps.viewAs) {
return true;
}
if (this.props.isReady !== nextProps.isReady) {
let internalFilterData = cloneObjectsArray(this.state.filterValues);
internalFilterData = convertToInternalData(
getFilterData(),
cloneObjectsArray(nextProps.selectedFilterData.filterValues)
);
let internalFilterDataSelectors = this.convertSelectorToInternalData(
getFilterData(),
cloneObjectsArray(nextProps.selectedFilterData.filterValues)
);
internalFilterData = internalFilterData.concat(
internalFilterDataSelectors
);
this.updateFilter(internalFilterData);
this.setState({
sortDirection:
nextProps.selectedFilterData.sortDirection === "desc" ? true : false,
sortId:
getSortData().findIndex(
(x) => x.key === nextProps.selectedFilterData.sortId
) != -1
? nextProps.selectedFilterData.sortId
: "",
filterValues: internalFilterData,
searchText: nextProps.selectedFilterData.inputValue || value,
});
// return true;
}
if ( if (
id != nextProps.id || id != nextProps.id ||
isDisabled != nextProps.isDisabled || isDisabled != nextProps.isDisabled ||
@ -286,96 +204,41 @@ class FilterInput extends React.Component {
value != nextProps.value value != nextProps.value
) )
return true; return true;
if (this.isResizeUpdate) {
return true;
}
return !isEqual(this.state, nextState); return !isEqual(this.state, nextState);
} }
convertSelectorToInternalData = (filterData, filterValues) => { onChangeSortDirection = (key) => {
const resultValues = [];
filterValues.forEach((item) => {
const isSelector = item.group.includes("filter-author");
if (isSelector) {
const typeSelector = item.key.includes("user")
? "user"
: item.key.includes("group")
? "group"
: null;
const hasUnderscore = item.key.indexOf("_") !== -1;
const key = hasUnderscore
? item.key.slice(0, item.key.indexOf("_"))
: item.key;
const finded = filterData.find(
(x) => x.key === key && x.group === item.group
);
if (!finded) return;
const convertedItem = {
key: item.group + "_" + item.key,
label: finded.label,
group: item.group,
groupLabel: finded.label,
typeSelector,
groupsCaption: finded.groupsCaption,
defaultOptionLabel: finded.defaultOptionLabel,
defaultOption: finded.defaultOption,
defaultSelectLabel: finded.defaultSelectLabel,
selectedItem: finded.selectedItem,
};
resultValues.push(convertedItem);
}
});
return resultValues;
};
resize = () => {
this.isResizeUpdate = true;
this.setState({
filterValues: this.state.filterValues,
openFilterItems: this.state.filterValues,
hideFilterItems: [],
});
};
onChangeSortDirection(key) {
this.onFilter( this.onFilter(
this.state.filterValues, this.state.filterValues,
this.state.sortId, this.state.sortId,
key ? "desc" : "asc" key ? "desc" : "asc"
); );
this.setState({ sortDirection: !!key }); this.setState({ sortDirection: !!key });
} };
onClickViewSelector(item) { onClickViewSelector = (item) => {
const itemId = (item.target && item.target.dataset.for) || item; const itemId = (item.target && item.target.dataset.for) || item;
const viewAs = itemId.indexOf("row") === -1 ? "tile" : "row"; const viewAs = itemId.indexOf("row") === -1 ? "tile" : "row";
this.props.onChangeViewAs(viewAs); this.props.onChangeViewAs(viewAs);
} };
getDefaultSelectedIndex() {
const sortData = this.props.getSortData(); onClickSortItem = (key) => {
if (sortData.length > 0) {
const defaultIndex = sortData.findIndex(
(x) => x.key === this.state.sortId
);
return defaultIndex != -1 ? defaultIndex : 0;
}
return 0;
}
onClickSortItem(key) {
this.setState({ sortId: key }); this.setState({ sortId: key });
this.onFilter( this.onFilter(
this.state.filterValues, this.state.filterValues,
key, key,
this.state.sortDirection ? "desc" : "asc" this.state.sortDirection ? "desc" : "asc"
); );
} };
onSortDirectionClick() { onSortDirectionClick = () => {
this.onFilter( this.onFilter(
this.state.filterValues, this.state.filterValues,
this.state.sortId, this.state.sortId,
!this.state.sortDirection ? "desc" : "asc" !this.state.sortDirection ? "desc" : "asc"
); );
this.setState({ sortDirection: !this.state.sortDirection }); this.setState({ sortDirection: !this.state.sortDirection });
} };
onSearchChanged(value) { onSearchChanged = (value) => {
this.setState({ searchText: value }); this.setState({ searchText: value });
this.onFilter( this.onFilter(
this.state.filterValues, this.state.filterValues,
@ -383,23 +246,93 @@ class FilterInput extends React.Component {
this.state.sortDirection ? "desc" : "asc", this.state.sortDirection ? "desc" : "asc",
value value
); );
} };
onSearch(result) { onSearch = (result) => {
this.onFilter( this.onFilter(
result.filterValues, result.filterValues,
this.state.sortId, this.state.sortId,
this.state.sortDirection ? "desc" : "asc" this.state.sortDirection ? "desc" : "asc"
); );
} };
getFilterData() { getDefaultFilterData = () => {
const _this = this; const { getFilterData, selectedFilterData } = this.props;
const filterData = getFilterData();
const filterItems = [];
const filterValues = cloneObjectsArray(selectedFilterData.filterValues);
for (let item of filterValues) {
const filterValue = filterData.find(
(x) => x.key === item.key && x.group === item.group
);
if (!filterValue) {
const isSelector = item.group.includes("filter-author");
if (isSelector) {
const typeSelector = item.key.includes("user")
? "user"
: item.key.includes("group")
? "group"
: null;
const underlined = item.key.indexOf("_") !== -1;
const key = underlined
? item.key.slice(0, item.key.indexOf("_"))
: item.key;
const filesFilterValue = filterData.find(
(x) => x.key === key && x.group === item.group
);
if (filesFilterValue) {
const convertedItem = {
key: item.group + "_" + item.key,
label: filesFilterValue.label,
group: item.group,
groupLabel: filesFilterValue.label,
typeSelector,
groupsCaption: filesFilterValue.groupsCaption,
defaultOptionLabel: filesFilterValue.defaultOptionLabel,
defaultOption: filesFilterValue.defaultOption,
defaultSelectLabel: filesFilterValue.defaultSelectLabel,
selectedItem: filesFilterValue.selectedItem,
};
filterItems.push(convertedItem);
}
}
}
let groupLabel = "";
const groupFilterItem = filterData.find((x) => x.key === item.group);
if (groupFilterItem) {
groupLabel = groupFilterItem.label;
} else {
const subgroupFilterItem = filterData.find(
(x) => x.subgroup === item.group
);
if (subgroupFilterItem) {
groupLabel = subgroupFilterItem.label;
}
}
if (filterValue) {
item.key = item.group + "_" + item.key;
item.label = filterValue.selectedItem
? filterValue.selectedItem.label
: filterValue.label;
item.groupLabel = groupLabel;
filterItems.push(item);
}
}
return filterItems;
};
getFilterData = () => {
const d = this.props.getFilterData(); const d = this.props.getFilterData();
const result = []; const result = [];
d.forEach((element) => { d.forEach((element) => {
if (!element.inSubgroup) { if (!element.inSubgroup) {
element.onClick = element.onClick =
!element.isSeparator && !element.isHeader && !element.disabled !element.isSeparator && !element.isHeader && !element.disabled
? (e) => _this.props.onClickFilterItem(e, element) ? () => this.onClickFilterItem(element)
: undefined; : undefined;
element.key = element.key =
element.group != element.key element.group != element.key
@ -413,13 +346,13 @@ class FilterInput extends React.Component {
} }
}); });
return result; return result;
} };
clearFilter() { clearFilter = () => {
this.setState({ this.setState({
searchText: "", searchText: "",
filterValues: [], filterValues: [],
openFilterItems: [], openFilterItems: [],
hideFilterItems: [], hiddenFilterItems: [],
}); });
this.onFilter( this.onFilter(
[], [],
@ -427,77 +360,135 @@ class FilterInput extends React.Component {
this.state.sortDirection ? "desc" : "asc", this.state.sortDirection ? "desc" : "asc",
"" ""
); );
} };
updateFilter(inputFilterItems) {
const currentFilterItems = getTextWidth = (text, font) => {
inputFilterItems || cloneObjectsArray(this.state.filterValues); var canvas =
this.getTextWidth.canvas ||
(this.getTextWidth.canvas = document.createElement("canvas"));
var context = canvas.getContext("2d");
context.font = font;
var metrics = context.measureText(text);
return metrics.width;
};
calcHiddenItemWidth = (item) => {
if (!item) return;
let label = this.getItemLabel(item);
const itemWidth =
this.getTextWidth(
item.groupLabel + " " + label,
"bolder 13px sans-serif"
) + filterItemPadding;
return itemWidth;
};
addItems = (searchWidth) => {
const { hiddenFilterItems } = this.state;
if (hiddenFilterItems.length === 0) return 0;
let newSearchWidth = searchWidth;
let numberOfHiddenItems = hiddenFilterItems.length;
for (let i = 0; i < hiddenFilterItems.length; i++) {
let hiddenItemWidth = this.calcHiddenItemWidth(hiddenFilterItems[i]);
if (hiddenItemWidth > maxFilterItemWidth)
hiddenItemWidth = maxFilterItemWidth;
newSearchWidth = newSearchWidth - hiddenItemWidth;
if (newSearchWidth >= minWidth) {
numberOfHiddenItems--;
} else {
break;
}
}
return numberOfHiddenItems;
};
hideItems = (searchWidth, currentFilterItems) => {
const { hiddenFilterItems } = this.state;
let newSearchWidth = searchWidth;
let numberOfHiddenItems = hiddenFilterItems.length;
for (let i = currentFilterItems.length - 1; i >= 0; i--) {
if (currentFilterItems[i].id === "styled-hide-filter") continue;
const filterItemWidth = currentFilterItems[i].getBoundingClientRect()
.width;
newSearchWidth = newSearchWidth + filterItemWidth;
numberOfHiddenItems++;
if (numberOfHiddenItems === 1) newSearchWidth - itemsContainerWidth; // subtract the width of hidden block
if (newSearchWidth > minWidth) break;
}
return numberOfHiddenItems;
};
calcHiddenItems = (searchWidth, currentFilterItems) => {
const { hiddenFilterItems } = this.state;
if (!searchWidth || currentFilterItems.length === 0)
return hiddenFilterItems.length;
const numberOfHiddenItems =
searchWidth < minWidth
? this.hideItems(searchWidth, currentFilterItems)
: this.addItems(searchWidth);
return numberOfHiddenItems;
};
updateFilter = (inputFilterItems) => {
const { sectionWidth } = this.props;
const currentFilterItems = inputFilterItems
? cloneObjectsArray(inputFilterItems)
: cloneObjectsArray(this.state.filterValues);
const fullWidth = this.searchWrapper.current.getBoundingClientRect().width; const fullWidth = this.searchWrapper.current.getBoundingClientRect().width;
const filterWidth = this.filterWrapper.current.getBoundingClientRect() const filterWidth = this.filterWrapper.current.getBoundingClientRect()
.width; .width;
const comboBoxWidth = this.rectComboBoxRef.current.getBoundingClientRect()
.width;
const searchWidth = sectionWidth
? sectionWidth - filterWidth - comboBoxWidth - sectionPaddings
: fullWidth - filterWidth;
const filterArr = Array.from( const filterArr = Array.from(
Array.from(this.filterWrapper.current.children).find( Array.from(this.filterWrapper.current.children).find(
(x) => x.id === "filter-items-container" (x) => x.id === "filter-items-container"
).children ).children
); );
const searchFilterButton = Array.from( const numberOfHiddenItems = this.calcHiddenItems(searchWidth, filterArr);
this.filterWrapper.current.children if (searchWidth !== 0 && currentFilterItems.length > 0) {
).find((x) => x.id != "filter-items-container");
const filterButton = searchFilterButton
? Array.from(searchFilterButton.children)[0]
: null;
if (fullWidth <= this.minWidth && fullWidth > 0) {
this.setState({ this.setState({
openFilterItems: [], openFilterItems: numberOfHiddenItems
hideFilterItems: cloneObjectsArray(currentFilterItems), ? currentFilterItems.slice(
}); 0,
} else if (filterWidth > fullWidth / 2) { currentFilterItems.length - numberOfHiddenItems
let newOpenFilterItems = cloneObjectsArray(currentFilterItems); )
let newHideFilterItems = []; : currentFilterItems.slice(),
hiddenFilterItems: numberOfHiddenItems
let elementsWidth = 0; ? currentFilterItems.slice(-numberOfHiddenItems)
Array.from(filterArr).forEach((element) => { : [],
elementsWidth = elementsWidth + element.getBoundingClientRect().width;
});
if (
filterButton !== null &&
elementsWidth >=
fullWidth / 3 - filterButton.getBoundingClientRect().width
) {
for (let i = 0; i < filterArr.length; i++) {
if (
elementsWidth >
fullWidth / 3 - filterButton.getBoundingClientRect().width
) {
elementsWidth =
elementsWidth - filterArr[i].getBoundingClientRect().width;
const hiddenItem = currentFilterItems.find(
(x) => x.key === filterArr[i].getAttribute("id")
);
if (hiddenItem) newHideFilterItems.push(hiddenItem);
newOpenFilterItems.splice(
newOpenFilterItems.findIndex(
(x) => x.key === filterArr[i].getAttribute("id")
),
1
);
}
}
}
this.setState({
openFilterItems: newOpenFilterItems,
hideFilterItems: newHideFilterItems,
});
} else {
this.setState({
openFilterItems: currentFilterItems.slice(),
hideFilterItems: [],
}); });
} }
} };
onDeleteFilterItem(key) {
getItemLabel = (item) => {
let label = "";
if (item.selectedItem) {
label = item.selectedItem.label
? item.selectedItem.label
: item.defaultSelectLabel;
} else {
label = item.label;
}
return label;
};
onDeleteFilterItem = (key) => {
const currentFilterItems = this.state.filterValues.slice(); const currentFilterItems = this.state.filterValues.slice();
const indexFilterItem = currentFilterItems.findIndex((x) => x.key === key); const indexFilterItem = currentFilterItems.findIndex((x) => x.key === key);
if (indexFilterItem != -1) { if (indexFilterItem != -1) {
@ -506,7 +497,7 @@ class FilterInput extends React.Component {
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
hideFilterItems: [], hiddenFilterItems: [],
}); });
let filterValues = cloneObjectsArray(currentFilterItems); let filterValues = cloneObjectsArray(currentFilterItems);
filterValues = filterValues.map(function (item) { filterValues = filterValues.map(function (item) {
@ -518,8 +509,8 @@ class FilterInput extends React.Component {
this.state.sortId, this.state.sortId,
this.state.sortDirection ? "desc" : "asc" this.state.sortDirection ? "desc" : "asc"
); );
} };
onFilter(filterValues, sortId, sortDirection, searchText) { onFilter = (filterValues, sortId, sortDirection, searchText) => {
let cloneFilterValues = cloneObjectsArray(filterValues); let cloneFilterValues = cloneObjectsArray(filterValues);
cloneFilterValues = cloneFilterValues.map(function (item) { cloneFilterValues = cloneFilterValues.map(function (item) {
item.key = item.key.replace(item.group + "_", ""); item.key = item.key.replace(item.group + "_", "");
@ -531,8 +522,8 @@ class FilterInput extends React.Component {
sortId: sortId, sortId: sortId,
sortDirection: sortDirection, sortDirection: sortDirection,
}); });
} };
onChangeFilter(result) { onChangeFilter = (result) => {
this.setState({ this.setState({
searchText: result.inputValue, searchText: result.inputValue,
filterValues: result.filterValues, filterValues: result.filterValues,
@ -543,24 +534,21 @@ class FilterInput extends React.Component {
this.state.sortDirection ? "desc" : "asc", this.state.sortDirection ? "desc" : "asc",
result.inputValue result.inputValue
); );
} };
onFilterRender() { onFilterRender = () => {
if (this.isResizeUpdate) { this.setState({
this.isResizeUpdate = false; needUpdateFilter: false,
} });
if (this.searchWrapper.current && this.filterWrapper.current) { this.updateFilter();
const fullWidth = this.searchWrapper.current.getBoundingClientRect() };
.width; onClickFilterItem = (event, filterItem) => {
const filterWidth = this.filterWrapper.current.getBoundingClientRect()
.width;
if (fullWidth <= this.minWidth || filterWidth > fullWidth / 2)
this.updateFilter();
}
}
onClickFilterItem(event, filterItem) {
const currentFilterItems = cloneObjectsArray(this.state.filterValues); const currentFilterItems = cloneObjectsArray(this.state.filterValues);
this.setState({
needUpdateFilter: true,
});
if (filterItem.isSelector) { if (filterItem.isSelector) {
const indexFilterItem = currentFilterItems.findIndex( const indexFilterItem = currentFilterItems.findIndex(
(x) => x.group === filterItem.group (x) => x.group === filterItem.group
@ -601,7 +589,7 @@ class FilterInput extends React.Component {
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
hideFilterItems: [], hiddenFilterItems: [],
}); });
if (selectFilterItem.selectedItem.key) { if (selectFilterItem.selectedItem.key) {
@ -647,7 +635,7 @@ class FilterInput extends React.Component {
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
hideFilterItems: [], hiddenFilterItems: [],
}); });
} else if (subgroupItems.length === 1) { } else if (subgroupItems.length === 1) {
const selectFilterItem = { const selectFilterItem = {
@ -678,7 +666,7 @@ class FilterInput extends React.Component {
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
hideFilterItems: [], hiddenFilterItems: [],
}); });
} }
} else { } else {
@ -705,7 +693,7 @@ class FilterInput extends React.Component {
this.setState({ this.setState({
filterValues: currentFilterItems, filterValues: currentFilterItems,
openFilterItems: currentFilterItems, openFilterItems: currentFilterItems,
hideFilterItems: [], hiddenFilterItems: [],
}); });
const clone = cloneObjectsArray( const clone = cloneObjectsArray(
@ -721,7 +709,7 @@ class FilterInput extends React.Component {
this.state.sortDirection ? "desc" : "asc" this.state.sortDirection ? "desc" : "asc"
); );
} }
} };
render() { render() {
/* eslint-disable react/prop-types */ /* eslint-disable react/prop-types */
@ -741,6 +729,7 @@ class FilterInput extends React.Component {
viewAs, viewAs,
contextMenuHeader, contextMenuHeader,
isMobile, isMobile,
sectionWidth,
} = this.props; } = this.props;
/* eslint-enable react/prop-types */ /* eslint-enable react/prop-types */
@ -748,12 +737,13 @@ class FilterInput extends React.Component {
searchText, searchText,
filterValues, filterValues,
openFilterItems, openFilterItems,
hideFilterItems, hiddenFilterItems,
sortId, sortId,
sortDirection, sortDirection,
} = this.state; } = this.state;
// console.log("filter input render, openFilterItems", openFilterItems, 'hideFilterItems', hideFilterItems); const smallSectionWidth = sectionWidth ? sectionWidth < 900 : false;
let iconSize = 30; let iconSize = 30;
switch (size) { switch (size) {
case "base": case "base":
@ -769,13 +759,14 @@ class FilterInput extends React.Component {
} }
return ( return (
<StyledFilterInput <StyledFilterInput
smallSectionWidth={smallSectionWidth}
isMobile={isMobile} isMobile={isMobile}
viewAs={viewAs} viewAs={viewAs}
className={className} className={className}
id={id} id={id}
style={style} style={style}
> >
<div className="styled-search-input" ref={this.searchWrapper}> <div className="styled-search-input test" ref={this.searchWrapper}>
<SearchInput <SearchInput
id={id} id={id}
isDisabled={isDisabled} isDisabled={isDisabled}
@ -796,37 +787,38 @@ class FilterInput extends React.Component {
<FilterBlock <FilterBlock
contextMenuHeader={contextMenuHeader} contextMenuHeader={contextMenuHeader}
openFilterItems={openFilterItems} openFilterItems={openFilterItems}
hideFilterItems={hideFilterItems} hiddenFilterItems={hiddenFilterItems}
iconSize={iconSize} iconSize={iconSize}
getFilterData={getFilterData} getFilterData={getFilterData}
onClickFilterItem={this.onClickFilterItem} onClickFilterItem={this.onClickFilterItem}
onDeleteFilterItem={this.onDeleteFilterItem} onDeleteFilterItem={this.onDeleteFilterItem}
isResizeUpdate={this.isResizeUpdate} onFilterRender={this.onFilterRender}
onRender={this.onFilterRender}
isDisabled={isDisabled} isDisabled={isDisabled}
columnCount={filterColumnCount} columnCount={filterColumnCount}
needUpdateFilter={this.state.needUpdateFilter}
/> />
</div> </div>
</SearchInput> </SearchInput>
</div> </div>
<div ref={this.rectComboBoxRef}>
<SortComboBox <SortComboBox
options={getSortData()} options={getSortData()}
isDisabled={isDisabled} isDisabled={isDisabled}
onChangeSortId={this.onClickSortItem} onChangeSortId={this.onClickSortItem}
onChangeView={this.onClickViewSelector} onChangeView={this.onClickViewSelector}
onChangeSortDirection={this.onChangeSortDirection} onChangeSortDirection={this.onChangeSortDirection}
selectedOption={ selectedOption={
getSortData().length > 0 getSortData().length > 0
? getSortData().find((x) => x.key === sortId) ? getSortData().find((x) => x.key === sortId)
: {} : {}
} }
onButtonClick={this.onSortDirectionClick} onButtonClick={this.onSortDirectionClick}
viewAs={viewAs} viewAs={viewAs}
sortDirection={+sortDirection} sortDirection={+sortDirection}
directionAscLabel={directionAscLabel} directionAscLabel={directionAscLabel}
directionDescLabel={directionDescLabel} directionDescLabel={directionDescLabel}
/> />
</div>
{viewAs && ( {viewAs && (
<ViewSelector <ViewSelector
isDisabled={isDisabled} isDisabled={isDisabled}
@ -853,6 +845,9 @@ FilterInput.protoTypes = {
filterColumnCount: PropTypes.number, filterColumnCount: PropTypes.number,
onChangeViewAs: PropTypes.func, onChangeViewAs: PropTypes.func,
contextMenuHeader: PropTypes.string, contextMenuHeader: PropTypes.string,
sectionWidth: PropTypes.number,
getSortData: PropTypes.func,
value: PropTypes.string,
}; };
FilterInput.defaultProps = { FilterInput.defaultProps = {

View File

@ -107,7 +107,7 @@ const StyledFilterInput = styled.div`
margin-left: 8px; margin-left: 8px;
${(props) => ${(props) =>
props.isMobile && (props.isMobile || props.smallSectionWidth) &&
` `
width: 50px; width: 50px;
.optionalBlock ~ div:first-child{ .optionalBlock ~ div:first-child{

View File

@ -39,6 +39,7 @@ class FilterItem extends React.Component {
isOpen: false, isOpen: false,
isOpenSelector: !isOpenSelector, isOpenSelector: !isOpenSelector,
selectedOption, selectedOption,
needUpdate: false,
}; };
} }
@ -48,7 +49,6 @@ class FilterItem extends React.Component {
if ( if (
selectedItem && selectedItem &&
selectedItem.key !== this.state.selectedOption.key && selectedItem.key !== this.state.selectedOption.key &&
selectedItem.key !== this.state.selectedOption.key &&
selectedItem.key !== prevProps.selectedItem.key selectedItem.key !== prevProps.selectedItem.key
) { ) {
const selectedOption = selectedItem.key const selectedOption = selectedItem.key
@ -67,6 +67,11 @@ class FilterItem extends React.Component {
selectedOption, selectedOption,
}); });
} }
if (this.state.needUpdate) {
this.props.onFilterRender();
this.setNeedUpdate(false);
}
} }
onSelect = (option) => { onSelect = (option) => {
@ -85,6 +90,12 @@ class FilterItem extends React.Component {
toggleCombobox = (e, isOpen) => this.setState({ isOpen }); toggleCombobox = (e, isOpen) => this.setState({ isOpen });
setNeedUpdate = (needUpdate) => {
this.setState({
needUpdate,
});
};
onCancelSelector = (e) => { onCancelSelector = (e) => {
if ( if (
this.state.isOpenSelector && this.state.isOpenSelector &&
@ -260,47 +271,64 @@ class FilterBlock extends React.Component {
constructor(props) { constructor(props) {
super(props); super(props);
const { hideFilterItems, openFilterItems } = props; const { hiddenFilterItems, openFilterItems } = props;
this.state = { this.state = {
hideFilterItems: hideFilterItems || [], hiddenFilterItems: hiddenFilterItems || [],
openFilterItems: openFilterItems || [], openFilterItems: openFilterItems || [],
needUpdate: false,
}; };
this.throttledRender = throttle(this.onRender, 100);
} }
componentDidUpdate() { componentDidMount() {
this.throttledRender(); this.setNeedUpdate(true);
}
componentDidUpdate(prevProps, prevState) {
const { needUpdate } = this.state;
const { needUpdateFilter } = this.props;
if (
(needUpdate || needUpdateFilter) &&
(!isEqual(prevState.openFilterItems, this.state.openFilterItems) ||
!isEqual(prevState.hiddenFilterItems, this.state.hiddenFilterItems))
) {
this.props.onFilterRender();
this.setNeedUpdate(false);
}
} }
shouldComponentUpdate(nextProps, nextState) { shouldComponentUpdate(nextProps, nextState) {
const { hideFilterItems, openFilterItems } = nextProps; const { hiddenFilterItems, openFilterItems } = nextProps;
if (!isEqual(this.props, nextProps)) { if (!isEqual(this.props, nextProps)) {
if ( if (
!isEqual(this.props.hideFilterItems, hideFilterItems) || !isEqual(this.props.hiddenFilterItems, hiddenFilterItems) ||
!isEqual(this.props.openFilterItems, openFilterItems) !isEqual(this.props.openFilterItems, openFilterItems)
) { ) {
this.setState({ this.setState({
hideFilterItems, hiddenFilterItems,
openFilterItems, openFilterItems,
}); });
return false; return false;
} }
return true; return true;
} }
if (this.props.isResizeUpdate) {
return true;
}
return !isEqual(this.state, nextState); return !isEqual(this.state, nextState);
} }
onDeleteFilterItem = (key) => { onDeleteFilterItem = (key) => {
this.props.onDeleteFilterItem(key); this.props.onDeleteFilterItem(key);
this.setNeedUpdate(true);
};
setNeedUpdate = (needUpdate) => {
this.setState({
needUpdate,
});
}; };
getFilterItems = () => { getFilterItems = () => {
const { openFilterItems, hideFilterItems } = this.state; const { openFilterItems, hiddenFilterItems } = this.state;
const _this = this; const _this = this;
let result = []; let result = [];
let openItems = []; let openItems = [];
@ -339,13 +367,14 @@ class FilterBlock extends React.Component {
defaultOption={defaultOption} defaultOption={defaultOption}
defaultSelectLabel={defaultSelectLabel} defaultSelectLabel={defaultSelectLabel}
selectedItem={selectedItem} selectedItem={selectedItem}
onFilterRender={_this.props.onFilterRender}
></FilterItem> ></FilterItem>
); );
}); });
} }
if (hideFilterItems.length > 0) { if (hiddenFilterItems.length > 0) {
let open = false; let open = false;
let hideFilterItemsList = hideFilterItems.map(function (item) { let hideFilterItemsList = hiddenFilterItems.map(function (item) {
const { const {
key, key,
group, group,
@ -379,13 +408,14 @@ class FilterBlock extends React.Component {
defaultOption={defaultOption} defaultOption={defaultOption}
defaultSelectLabel={defaultSelectLabel} defaultSelectLabel={defaultSelectLabel}
selectedItem={selectedItem} selectedItem={selectedItem}
onFilterRender={_this.props.onFilterRender}
></FilterItem> ></FilterItem>
); );
}); });
hideItems.push( hideItems.push(
<HideFilter <HideFilter
key="hide-filter" key="hide-filter"
count={hideFilterItems.length} count={hiddenFilterItems.length}
isDisabled={this.props.isDisabled} isDisabled={this.props.isDisabled}
open={open} open={open}
> >
@ -420,9 +450,6 @@ class FilterBlock extends React.Component {
return result; return result;
}; };
onRender = () => {
this.props.onRender();
};
render() { render() {
const _this = this; const _this = this;
const filterItems = this.getFilterItems(); const filterItems = this.getFilterItems();
@ -453,10 +480,9 @@ class FilterBlock extends React.Component {
} }
FilterBlock.propTypes = { FilterBlock.propTypes = {
getFilterData: PropTypes.func, getFilterData: PropTypes.func,
hideFilterItems: PropTypes.array, hiddenFilterItems: PropTypes.array,
iconSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]), iconSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
isDisabled: PropTypes.bool, isDisabled: PropTypes.bool,
isResizeUpdate: PropTypes.bool,
onDeleteFilterItem: PropTypes.func, onDeleteFilterItem: PropTypes.func,
onRender: PropTypes.func, onRender: PropTypes.func,
openFilterItems: PropTypes.array, openFilterItems: PropTypes.array,

View File

@ -39,6 +39,7 @@ class HideFilter extends React.Component {
className="styled-hide-filter" className="styled-hide-filter"
onClick={this.onClick.bind(this, !popoverOpen)} onClick={this.onClick.bind(this, !popoverOpen)}
ref={this.ref} ref={this.ref}
id="styled-hide-filter"
> >
<StyledHideFilterButton id="PopoverLegacy" isDisabled={isDisabled}> <StyledHideFilterButton id="PopoverLegacy" isDisabled={isDisabled}>
{count} {count}

View File

@ -107,3 +107,5 @@ export function showLoader() {
document.body.classList.add("loading"); document.body.classList.add("loading");
}, 1000); }, 1000);
} }
export { withLayoutSize } from "./withLayoutSize";

View File

@ -0,0 +1,15 @@
import * as React from "react";
import { utils } from "asc-web-components";
const { Consumer } = utils.context;
export function withLayoutSize(Component) {
return function LayoutSizeComponent(props) {
return (
<Consumer>
{(context) => {
return <Component {...props} {...context} />;
}}
</Consumer>
);
};
}