Merge branch 'develop' into refactoring/files-thirdparty
This commit is contained in:
commit
93410996f0
@ -16,10 +16,12 @@ import find from "lodash/find";
|
||||
import result from "lodash/result";
|
||||
import { withTranslation } from "react-i18next";
|
||||
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 { isMobileOnly } from "react-device-detect";
|
||||
|
||||
const { withLayoutSize } = utils;
|
||||
|
||||
const {
|
||||
getCurrentUser,
|
||||
getSettingsCustomNames,
|
||||
@ -287,20 +289,22 @@ class SectionFilterContent extends React.Component {
|
||||
this.props.selectedFolderId !== nextProps.selectedFolderId ||
|
||||
this.state.isReady !== nextState.isReady ||
|
||||
this.props.viewAs !== nextProps.viewAs ||
|
||||
this.props.firstLoad !== nextProps.firstLoad
|
||||
this.props.firstLoad !== nextProps.firstLoad ||
|
||||
this.props.sectionWidth !== nextProps.sectionWidth
|
||||
);
|
||||
}
|
||||
|
||||
render() {
|
||||
console.log("Filter render");
|
||||
const selectedFilterData = this.getSelectedFilterData();
|
||||
const { t, language, firstLoad } = this.props;
|
||||
const { t, language, firstLoad, sectionWidth } = this.props;
|
||||
const filterColumnCount =
|
||||
window.innerWidth < 500 ? {} : { filterColumnCount: 3 };
|
||||
return firstLoad ? (
|
||||
<Loaders.Filter />
|
||||
) : (
|
||||
<FilterInput
|
||||
sectionWidth={sectionWidth}
|
||||
getFilterData={this.getData}
|
||||
getSortData={this.getSortData}
|
||||
selectedFilterData={selectedFilterData}
|
||||
@ -338,4 +342,4 @@ export default connect(mapStateToProps, {
|
||||
fetchFiles,
|
||||
setViewAs,
|
||||
setIsLoading,
|
||||
})(withRouter(withTranslation()(SectionFilterContent)));
|
||||
})(withRouter(withLayoutSize(withTranslation()(SectionFilterContent))));
|
||||
|
@ -32,7 +32,7 @@ using ASC.Files.Thirdparty;
|
||||
|
||||
namespace ASC.Files.Core
|
||||
{
|
||||
[Scope(typeof(CachedProviderAccountDao), Additional = typeof(ProviderAccountDaoExtension))]
|
||||
[Scope(typeof(ProviderAccountDao), Additional = typeof(ProviderAccountDaoExtension))]
|
||||
public interface IProviderDao
|
||||
{
|
||||
IProviderInfo GetProviderInfo(int linkId);
|
||||
|
@ -19,7 +19,7 @@ namespace ASC.Files.Core.EF
|
||||
|
||||
public override object[] GetKeys()
|
||||
{
|
||||
return new object[] { Id };
|
||||
return new object[] { HashId };
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ namespace ASC.Files.Thirdparty.Box
|
||||
{
|
||||
internal abstract class BoxDaoBase : ThirdPartyProviderDao<BoxProviderInfo>
|
||||
{
|
||||
public override string Id { get => "box"; }
|
||||
protected override string Id { get => "box"; }
|
||||
|
||||
public BoxDaoBase(
|
||||
IServiceProvider serviceProvider,
|
||||
|
@ -183,7 +183,7 @@ namespace ASC.Files.Thirdparty.Box
|
||||
|
||||
internal BoxStorage CreateStorage(OAuth20Token token, int id)
|
||||
{
|
||||
if (Storage != null) return Storage;
|
||||
if (Storage != null && Storage.IsOpened) return Storage;
|
||||
|
||||
var boxStorage = new BoxStorage();
|
||||
CheckToken(token, id);
|
||||
@ -199,7 +199,7 @@ namespace ASC.Files.Thirdparty.Box
|
||||
{
|
||||
token = OAuth20TokenHelper.RefreshToken<BoxLoginProvider>(ConsumerFactory, token);
|
||||
|
||||
var dbDao = ServiceProvider.GetService<CachedProviderAccountDao>();
|
||||
var dbDao = ServiceProvider.GetService<ProviderAccountDao>();
|
||||
dbDao.UpdateProviderInfo(id, new AuthData(token: token.ToJson()));
|
||||
}
|
||||
}
|
||||
|
@ -1,125 +0,0 @@
|
||||
/*
|
||||
*
|
||||
* (c) Copyright Ascensio System Limited 2010-2018
|
||||
*
|
||||
* This program is freeware. You can redistribute it and/or modify it under the terms of the GNU
|
||||
* General Public License (GPL) version 3 as published by the Free Software Foundation (https://www.gnu.org/copyleft/gpl.html).
|
||||
* In accordance with Section 7(a) of the GNU GPL its Section 15 shall be amended to the effect that
|
||||
* Ascensio System SIA expressly excludes the warranty of non-infringement of any third-party rights.
|
||||
*
|
||||
* THIS PROGRAM IS DISTRIBUTED WITHOUT ANY WARRANTY; WITHOUT EVEN THE IMPLIED WARRANTY OF MERCHANTABILITY OR
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. For more details, see GNU GPL at https://www.gnu.org/copyleft/gpl.html
|
||||
*
|
||||
* You can contact Ascensio System SIA by email at sales@onlyoffice.com
|
||||
*
|
||||
* The interactive user interfaces in modified source and object code versions of ONLYOFFICE must display
|
||||
* Appropriate Legal Notices, as required under Section 5 of the GNU GPL version 3.
|
||||
*
|
||||
* Pursuant to Section 7 § 3(b) of the GNU GPL you must retain the original ONLYOFFICE logo which contains
|
||||
* relevant author attributions when distributing the software. If the display of the logo in its graphic
|
||||
* form is not reasonably feasible for technical reasons, you must include the words "Powered by ONLYOFFICE"
|
||||
* in every copy of the program you distribute.
|
||||
* Pursuant to Section 7 § 3(e) we decline to grant you any rights under trademark law for use of our trademarks.
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
using System;
|
||||
using System.Collections.Concurrent;
|
||||
using System.Globalization;
|
||||
|
||||
using ASC.Common;
|
||||
using ASC.Common.Caching;
|
||||
using ASC.Common.Logging;
|
||||
using ASC.Core;
|
||||
using ASC.Core.Common.Configuration;
|
||||
using ASC.Core.Common.EF;
|
||||
using ASC.Core.Tenants;
|
||||
using ASC.Files.Core;
|
||||
using ASC.Files.Core.EF;
|
||||
using ASC.Security.Cryptography;
|
||||
|
||||
using Microsoft.Extensions.Options;
|
||||
|
||||
namespace ASC.Files.Thirdparty
|
||||
{
|
||||
[Singletone]
|
||||
internal class CachedProviderAccountDaoNotify
|
||||
{
|
||||
public ConcurrentDictionary<string, IProviderInfo> Cache { get; private set; }
|
||||
internal ICacheNotify<ProviderAccountCacheItem> CacheNotify { get; set; }
|
||||
|
||||
public CachedProviderAccountDaoNotify(ICacheNotify<ProviderAccountCacheItem> cacheNotify)
|
||||
{
|
||||
Cache = new ConcurrentDictionary<string, IProviderInfo>();
|
||||
CacheNotify = cacheNotify;
|
||||
cacheNotify.Subscribe((i) => RemoveFromCache(i.Key), CacheNotifyAction.Any);
|
||||
}
|
||||
private void RemoveFromCache(string key)
|
||||
{
|
||||
Cache.TryRemove(key, out _);
|
||||
}
|
||||
}
|
||||
|
||||
[Scope]
|
||||
internal class CachedProviderAccountDao : ProviderAccountDao
|
||||
{
|
||||
private readonly ConcurrentDictionary<string, IProviderInfo> cache;
|
||||
private readonly ICacheNotify<ProviderAccountCacheItem> cacheNotify;
|
||||
|
||||
private string _rootKey { get => TenantID.ToString(CultureInfo.InvariantCulture); }
|
||||
|
||||
public CachedProviderAccountDao(
|
||||
IServiceProvider serviceProvider,
|
||||
TenantUtil tenantUtil,
|
||||
TenantManager tenantManager,
|
||||
InstanceCrypto instanceCrypto,
|
||||
SecurityContext securityContext,
|
||||
ConsumerFactory consumerFactory,
|
||||
DbContextManager<FilesDbContext> dbContextManager,
|
||||
IOptionsMonitor<ILog> options,
|
||||
CachedProviderAccountDaoNotify cachedProviderAccountDaoNotify)
|
||||
: base(serviceProvider, tenantUtil, tenantManager, instanceCrypto, securityContext, consumerFactory, dbContextManager, options)
|
||||
{
|
||||
cache = cachedProviderAccountDaoNotify.Cache;
|
||||
cacheNotify = cachedProviderAccountDaoNotify.CacheNotify;
|
||||
}
|
||||
|
||||
public override IProviderInfo GetProviderInfo(int linkId)
|
||||
{
|
||||
var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture);
|
||||
if (!cache.TryGetValue(key, out var value))
|
||||
{
|
||||
value = base.GetProviderInfo(linkId);
|
||||
cache.TryAdd(key, value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
public override void RemoveProviderInfo(int linkId)
|
||||
{
|
||||
base.RemoveProviderInfo(linkId);
|
||||
|
||||
var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture);
|
||||
cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Any);
|
||||
}
|
||||
|
||||
public override int UpdateProviderInfo(int linkId, string customerTitle, AuthData authData, FolderType folderType, Guid? userId = null)
|
||||
{
|
||||
var result = base.UpdateProviderInfo(linkId, customerTitle, authData, folderType, userId);
|
||||
|
||||
var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture);
|
||||
cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Any);
|
||||
return result;
|
||||
}
|
||||
|
||||
public override int UpdateProviderInfo(int linkId, AuthData authData)
|
||||
{
|
||||
var result = base.UpdateProviderInfo(linkId, authData);
|
||||
|
||||
var key = _rootKey + linkId.ToString(CultureInfo.InvariantCulture);
|
||||
cacheNotify.Publish(new ProviderAccountCacheItem { Key = key }, CacheNotifyAction.Any);
|
||||
return result;
|
||||
}
|
||||
}
|
||||
}
|
@ -48,7 +48,7 @@ namespace ASC.Files.Thirdparty.Dropbox
|
||||
{
|
||||
internal abstract class DropboxDaoBase : ThirdPartyProviderDao<DropboxProviderInfo>
|
||||
{
|
||||
public override string Id { get => "dropbox"; }
|
||||
protected override string Id { get => "dropbox"; }
|
||||
|
||||
public DropboxDaoBase(IServiceProvider serviceProvider, UserManager userManager, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FilesDbContext> dbContextManager, SetupInfo setupInfo, IOptionsMonitor<ILog> monitor, FileUtility fileUtility) : base(serviceProvider, userManager, tenantManager, tenantUtil, dbContextManager, setupInfo, monitor, fileUtility)
|
||||
{
|
||||
|
@ -162,7 +162,7 @@ namespace ASC.Files.Thirdparty.Dropbox
|
||||
|
||||
public DropboxStorage CreateStorage(OAuth20Token token)
|
||||
{
|
||||
if (Storage != null) return Storage;
|
||||
if (Storage != null && Storage.IsOpened) return Storage;
|
||||
|
||||
var dropboxStorage = new DropboxStorage();
|
||||
dropboxStorage.Open(token);
|
||||
|
@ -49,7 +49,7 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
{
|
||||
internal abstract class GoogleDriveDaoBase : ThirdPartyProviderDao<GoogleDriveProviderInfo>
|
||||
{
|
||||
public override string Id { get => "drive"; }
|
||||
protected override string Id { get => "drive"; }
|
||||
|
||||
public GoogleDriveDaoBase(IServiceProvider serviceProvider, UserManager userManager, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FilesDbContext> dbContextManager, SetupInfo setupInfo, IOptionsMonitor<ILog> monitor, FileUtility fileUtility) : base(serviceProvider, userManager, tenantManager, tenantUtil, dbContextManager, setupInfo, monitor, fileUtility)
|
||||
{
|
||||
|
@ -196,7 +196,7 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
|
||||
public GoogleDriveStorage CreateStorage(OAuth20Token token, int id)
|
||||
{
|
||||
if (Storage != null) return Storage;
|
||||
if (Storage != null && Storage.IsOpened) return Storage;
|
||||
|
||||
var driveStorage = ServiceProvider.GetService<GoogleDriveStorage>();
|
||||
|
||||
@ -213,7 +213,7 @@ namespace ASC.Files.Thirdparty.GoogleDrive
|
||||
{
|
||||
token = OAuth20TokenHelper.RefreshToken<GoogleLoginProvider>(ConsumerFactory, token);
|
||||
|
||||
var dbDao = ServiceProvider.GetService<CachedProviderAccountDao>();
|
||||
var dbDao = ServiceProvider.GetService<ProviderAccountDao>();
|
||||
var authData = new AuthData(token: token.ToJson());
|
||||
dbDao.UpdateProviderInfo(id, authData);
|
||||
}
|
||||
|
@ -188,14 +188,14 @@ namespace ASC.Files.Thirdparty
|
||||
protected TenantUtil TenantUtil { get; }
|
||||
protected FilesDbContext FilesDbContext { get; }
|
||||
protected SetupInfo SetupInfo { get; }
|
||||
public ILog Log { get; }
|
||||
protected ILog Log { get; }
|
||||
protected FileUtility FileUtility { get; }
|
||||
|
||||
public RegexDaoSelectorBase<T> DaoSelector { get; set; }
|
||||
public T ProviderInfo { get; set; }
|
||||
public string PathPrefix { get; private set; }
|
||||
protected RegexDaoSelectorBase<T> DaoSelector { get; set; }
|
||||
protected T ProviderInfo { get; set; }
|
||||
protected string PathPrefix { get; private set; }
|
||||
|
||||
public abstract string Id { get; }
|
||||
protected abstract string Id { get; }
|
||||
|
||||
public ThirdPartyProviderDao(
|
||||
IServiceProvider serviceProvider,
|
||||
|
@ -47,7 +47,7 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
{
|
||||
internal abstract class OneDriveDaoBase : ThirdPartyProviderDao<OneDriveProviderInfo>
|
||||
{
|
||||
public override string Id { get => "onedrive"; }
|
||||
protected override string Id { get => "onedrive"; }
|
||||
|
||||
public OneDriveDaoBase(IServiceProvider serviceProvider, UserManager userManager, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FilesDbContext> dbContextManager, SetupInfo setupInfo, IOptionsMonitor<ILog> monitor, FileUtility fileUtility) : base(serviceProvider, userManager, tenantManager, tenantUtil, dbContextManager, setupInfo, monitor, fileUtility)
|
||||
{
|
||||
|
@ -157,7 +157,7 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
|
||||
public OneDriveStorage CreateStorage(OAuth20Token token, int id)
|
||||
{
|
||||
if (Storage != null) return Storage;
|
||||
if (Storage != null && Storage.IsOpened) return Storage;
|
||||
|
||||
var onedriveStorage = ServiceProvider.GetService<OneDriveStorage>();
|
||||
|
||||
@ -174,7 +174,7 @@ namespace ASC.Files.Thirdparty.OneDrive
|
||||
{
|
||||
token = OAuth20TokenHelper.RefreshToken<OneDriveLoginProvider>(ConsumerFactory, token);
|
||||
|
||||
var dbDao = ServiceProvider.GetService<CachedProviderAccountDao>();
|
||||
var dbDao = ServiceProvider.GetService<ProviderAccountDao>();
|
||||
var authData = new AuthData(token: token.ToJson());
|
||||
dbDao.UpdateProviderInfo(id, authData);
|
||||
}
|
||||
|
@ -149,7 +149,7 @@ namespace ASC.Files.Thirdparty
|
||||
|
||||
public void RenameProvider(T provider, string newTitle)
|
||||
{
|
||||
var dbDao = ServiceProvider.GetService<CachedProviderAccountDao>();
|
||||
var dbDao = ServiceProvider.GetService<ProviderAccountDao>();
|
||||
dbDao.UpdateProviderInfo(provider.ID, newTitle, null, provider.RootFolderType);
|
||||
provider.UpdateTitle(newTitle); //This will update cached version too
|
||||
}
|
||||
|
@ -45,7 +45,7 @@ namespace ASC.Files.Thirdparty.SharePoint
|
||||
{
|
||||
internal class SharePointDaoBase : ThirdPartyProviderDao<SharePointProviderInfo>
|
||||
{
|
||||
public override string Id { get => "spoint"; }
|
||||
protected override string Id { get => "spoint"; }
|
||||
|
||||
public SharePointDaoBase(IServiceProvider serviceProvider, UserManager userManager, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FilesDbContext> dbContextManager, SetupInfo setupInfo, IOptionsMonitor<ILog> monitor, FileUtility fileUtility) : base(serviceProvider, userManager, tenantManager, tenantUtil, dbContextManager, setupInfo, monitor, fileUtility)
|
||||
{
|
||||
|
@ -50,7 +50,7 @@ namespace ASC.Files.Thirdparty.Sharpbox
|
||||
{
|
||||
internal abstract class SharpBoxDaoBase : ThirdPartyProviderDao<SharpBoxProviderInfo>
|
||||
{
|
||||
public override string Id { get => "sbox"; }
|
||||
protected override string Id { get => "sbox"; }
|
||||
|
||||
public SharpBoxDaoBase(IServiceProvider serviceProvider, UserManager userManager, TenantManager tenantManager, TenantUtil tenantUtil, DbContextManager<FilesDbContext> dbContextManager, SetupInfo setupInfo, IOptionsMonitor<ILog> monitor, FileUtility fileUtility) : base(serviceProvider, userManager, tenantManager, tenantUtil, dbContextManager, setupInfo, monitor, fileUtility)
|
||||
{
|
||||
|
@ -6,7 +6,7 @@ import result from "lodash/result";
|
||||
import { withTranslation } from "react-i18next";
|
||||
import { withRouter } from "react-router";
|
||||
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 { getFilter, getGroups } from "../../../../../store/people/selectors";
|
||||
const {
|
||||
@ -17,6 +17,8 @@ const {
|
||||
getIsLoaded,
|
||||
} = store.auth.selectors;
|
||||
|
||||
const { withLayoutSize } = utils;
|
||||
|
||||
const getEmployeeStatus = (filterValues) => {
|
||||
const employeeStatus = result(
|
||||
find(filterValues, (value) => {
|
||||
@ -248,9 +250,10 @@ class SectionFilterContent extends React.Component {
|
||||
|
||||
render() {
|
||||
const selectedFilterData = this.getSelectedFilterData();
|
||||
const { t, language, isLoaded } = this.props;
|
||||
const { t, language, isLoaded, sectionWidth } = this.props;
|
||||
return isLoaded ? (
|
||||
<FilterInput
|
||||
sectionWidth={sectionWidth}
|
||||
getFilterData={this.getData}
|
||||
getSortData={this.getSortData}
|
||||
selectedFilterData={selectedFilterData}
|
||||
@ -282,5 +285,5 @@ function mapStateToProps(state) {
|
||||
}
|
||||
|
||||
export default connect(mapStateToProps, { fetchPeople })(
|
||||
withRouter(withTranslation()(SectionFilterContent))
|
||||
withRouter(withLayoutSize(withTranslation()(SectionFilterContent)))
|
||||
);
|
||||
|
@ -864,21 +864,8 @@ namespace ASC.Employee.Core.Controllers
|
||||
return new ThumbnailsDataWrapper(user.ID, UserPhotoManager);
|
||||
}
|
||||
|
||||
|
||||
[Create("{userid}/photo")]
|
||||
public FileUploadResult UploadMemberPhotoFromBody(string userid, [FromBody]IFormCollection model)
|
||||
{
|
||||
return UploadMemberPhoto(userid, model);
|
||||
}
|
||||
|
||||
[Create("{userid}/photo")]
|
||||
[Consumes("application/x-www-form-urlencoded")]
|
||||
public FileUploadResult UploadMemberPhotoFromForm(string userid, [FromForm] IFormCollection model)
|
||||
{
|
||||
return UploadMemberPhoto(userid, model);
|
||||
}
|
||||
|
||||
private FileUploadResult UploadMemberPhoto(string userid, IFormCollection model)
|
||||
public FileUploadResult UploadMemberPhoto(string userid, IFormCollection model)
|
||||
{
|
||||
var result = new People.Models.FileUploadResult();
|
||||
var autosave = bool.Parse(model["Autosave"]);
|
||||
|
@ -1,6 +1,6 @@
|
||||
{
|
||||
"name": "asc-web-common",
|
||||
"version": "1.0.279",
|
||||
"version": "1.0.275",
|
||||
"description": "Ascensio System SIA common components and solutions library",
|
||||
"license": "AGPL-3.0",
|
||||
"files": [
|
||||
|
@ -2,7 +2,6 @@ import React from "react";
|
||||
import PropTypes from "prop-types";
|
||||
import { SearchInput } from "asc-web-components";
|
||||
import isEqual from "lodash/isEqual";
|
||||
import throttle from "lodash/throttle";
|
||||
import FilterBlock from "./sub-components/FilterBlock";
|
||||
import SortComboBox from "./sub-components/SortComboBox";
|
||||
import ViewSelector from "./sub-components/ViewSelector";
|
||||
@ -81,203 +80,122 @@ const convertToInternalData = function (fullDataArray, inputDataArray) {
|
||||
return filterItems;
|
||||
};
|
||||
|
||||
const minWidth = 170;
|
||||
const filterItemPadding = 60;
|
||||
const maxFilterItemWidth = 260;
|
||||
const itemsContainerWidth = 40;
|
||||
const sectionPaddings = 48;
|
||||
|
||||
class FilterInput extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.isResizeUpdate = false;
|
||||
this.minWidth = 190;
|
||||
const { selectedFilterData, getSortData, value } = props;
|
||||
const { sortDirection, sortId, inputValue } = selectedFilterData;
|
||||
const sortData = getSortData();
|
||||
|
||||
function 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)
|
||||
),
|
||||
];
|
||||
const filterValues = selectedFilterData ? this.getDefaultFilterData() : [];
|
||||
|
||||
this.state = {
|
||||
sortDirection:
|
||||
props.selectedFilterData.sortDirection === "desc" ? true : false,
|
||||
sortDirection: sortDirection === "desc" ? true : false,
|
||||
sortId:
|
||||
props
|
||||
.getSortData()
|
||||
.findIndex((x) => x.key === props.selectedFilterData.sortId) != -1
|
||||
? props.selectedFilterData.sortId
|
||||
: props.getSortData().length > 0
|
||||
? props.getSortData()[0].key
|
||||
sortData.findIndex((x) => x.key === sortId) != -1
|
||||
? sortId
|
||||
: sortData.length > 0
|
||||
? sortData[0].key
|
||||
: "",
|
||||
searchText: props.selectedFilterData.inputValue || props.value,
|
||||
searchText: inputValue || value,
|
||||
|
||||
filterValues,
|
||||
openFilterItems: [],
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
needUpdateFilter: false,
|
||||
};
|
||||
|
||||
this.searchWrapper = React.createRef();
|
||||
this.filterWrapper = 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);
|
||||
this.rectComboBoxRef = React.createRef();
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
window.addEventListener("resize", this.throttledResize);
|
||||
if (this.state.filterValues.length > 0) this.updateFilter();
|
||||
}
|
||||
componentWillUnmount() {
|
||||
window.removeEventListener("resize", this.throttledResize);
|
||||
}
|
||||
|
||||
componentDidUpdate(prevProps, prevState) {
|
||||
const { selectedFilterData, sectionWidth, getSortData } = this.props;
|
||||
const { filterValues, searchText } = this.state;
|
||||
const { sortDirection, sortId, inputValue } = selectedFilterData;
|
||||
|
||||
if (
|
||||
this.props.needForUpdate &&
|
||||
this.props.needForUpdate(prevProps, this.props)
|
||||
) {
|
||||
let internalFilterData = convertToInternalData(
|
||||
this.props.getFilterData(),
|
||||
cloneObjectsArray(this.props.selectedFilterData.filterValues)
|
||||
cloneObjectsArray(selectedFilterData.filterValues)
|
||||
);
|
||||
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) {
|
||||
const {
|
||||
selectedFilterData,
|
||||
getFilterData,
|
||||
getSortData,
|
||||
value,
|
||||
id,
|
||||
isDisabled,
|
||||
size,
|
||||
placeholder,
|
||||
sectionWidth,
|
||||
} = this.props;
|
||||
if (!isEqual(selectedFilterData, nextProps.selectedFilterData)) {
|
||||
let internalFilterData = cloneObjectsArray(this.state.filterValues);
|
||||
if (nextProps.selectedFilterData.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,
|
||||
});
|
||||
|
||||
if (
|
||||
!isEqual(selectedFilterData, nextProps.selectedFilterData) ||
|
||||
this.props.viewAs !== nextProps.viewAs ||
|
||||
this.props.widthProp !== nextProps.widthProp ||
|
||||
sectionWidth !== nextProps.sectionWidth
|
||||
) {
|
||||
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 (
|
||||
id != nextProps.id ||
|
||||
isDisabled != nextProps.isDisabled ||
|
||||
@ -286,96 +204,41 @@ class FilterInput extends React.Component {
|
||||
value != nextProps.value
|
||||
)
|
||||
return true;
|
||||
if (this.isResizeUpdate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isEqual(this.state, nextState);
|
||||
}
|
||||
|
||||
convertSelectorToInternalData = (filterData, filterValues) => {
|
||||
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) {
|
||||
onChangeSortDirection = (key) => {
|
||||
this.onFilter(
|
||||
this.state.filterValues,
|
||||
this.state.sortId,
|
||||
key ? "desc" : "asc"
|
||||
);
|
||||
this.setState({ sortDirection: !!key });
|
||||
}
|
||||
onClickViewSelector(item) {
|
||||
};
|
||||
onClickViewSelector = (item) => {
|
||||
const itemId = (item.target && item.target.dataset.for) || item;
|
||||
const viewAs = itemId.indexOf("row") === -1 ? "tile" : "row";
|
||||
this.props.onChangeViewAs(viewAs);
|
||||
}
|
||||
getDefaultSelectedIndex() {
|
||||
const sortData = this.props.getSortData();
|
||||
if (sortData.length > 0) {
|
||||
const defaultIndex = sortData.findIndex(
|
||||
(x) => x.key === this.state.sortId
|
||||
);
|
||||
return defaultIndex != -1 ? defaultIndex : 0;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
onClickSortItem(key) {
|
||||
};
|
||||
|
||||
onClickSortItem = (key) => {
|
||||
this.setState({ sortId: key });
|
||||
this.onFilter(
|
||||
this.state.filterValues,
|
||||
key,
|
||||
this.state.sortDirection ? "desc" : "asc"
|
||||
);
|
||||
}
|
||||
onSortDirectionClick() {
|
||||
};
|
||||
onSortDirectionClick = () => {
|
||||
this.onFilter(
|
||||
this.state.filterValues,
|
||||
this.state.sortId,
|
||||
!this.state.sortDirection ? "desc" : "asc"
|
||||
);
|
||||
this.setState({ sortDirection: !this.state.sortDirection });
|
||||
}
|
||||
onSearchChanged(value) {
|
||||
};
|
||||
onSearchChanged = (value) => {
|
||||
this.setState({ searchText: value });
|
||||
this.onFilter(
|
||||
this.state.filterValues,
|
||||
@ -383,23 +246,93 @@ class FilterInput extends React.Component {
|
||||
this.state.sortDirection ? "desc" : "asc",
|
||||
value
|
||||
);
|
||||
}
|
||||
onSearch(result) {
|
||||
};
|
||||
onSearch = (result) => {
|
||||
this.onFilter(
|
||||
result.filterValues,
|
||||
this.state.sortId,
|
||||
this.state.sortDirection ? "desc" : "asc"
|
||||
);
|
||||
}
|
||||
getFilterData() {
|
||||
const _this = this;
|
||||
};
|
||||
getDefaultFilterData = () => {
|
||||
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 result = [];
|
||||
d.forEach((element) => {
|
||||
if (!element.inSubgroup) {
|
||||
element.onClick =
|
||||
!element.isSeparator && !element.isHeader && !element.disabled
|
||||
? (e) => _this.props.onClickFilterItem(e, element)
|
||||
? () => this.onClickFilterItem(element)
|
||||
: undefined;
|
||||
element.key =
|
||||
element.group != element.key
|
||||
@ -413,13 +346,13 @@ class FilterInput extends React.Component {
|
||||
}
|
||||
});
|
||||
return result;
|
||||
}
|
||||
clearFilter() {
|
||||
};
|
||||
clearFilter = () => {
|
||||
this.setState({
|
||||
searchText: "",
|
||||
filterValues: [],
|
||||
openFilterItems: [],
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
this.onFilter(
|
||||
[],
|
||||
@ -427,77 +360,135 @@ class FilterInput extends React.Component {
|
||||
this.state.sortDirection ? "desc" : "asc",
|
||||
""
|
||||
);
|
||||
}
|
||||
updateFilter(inputFilterItems) {
|
||||
const currentFilterItems =
|
||||
inputFilterItems || cloneObjectsArray(this.state.filterValues);
|
||||
};
|
||||
|
||||
getTextWidth = (text, font) => {
|
||||
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 filterWidth = this.filterWrapper.current.getBoundingClientRect()
|
||||
.width;
|
||||
const comboBoxWidth = this.rectComboBoxRef.current.getBoundingClientRect()
|
||||
.width;
|
||||
|
||||
const searchWidth = sectionWidth
|
||||
? sectionWidth - filterWidth - comboBoxWidth - sectionPaddings
|
||||
: fullWidth - filterWidth;
|
||||
|
||||
const filterArr = Array.from(
|
||||
Array.from(this.filterWrapper.current.children).find(
|
||||
(x) => x.id === "filter-items-container"
|
||||
).children
|
||||
);
|
||||
const searchFilterButton = Array.from(
|
||||
this.filterWrapper.current.children
|
||||
).find((x) => x.id != "filter-items-container");
|
||||
|
||||
const filterButton = searchFilterButton
|
||||
? Array.from(searchFilterButton.children)[0]
|
||||
: null;
|
||||
|
||||
if (fullWidth <= this.minWidth && fullWidth > 0) {
|
||||
const numberOfHiddenItems = this.calcHiddenItems(searchWidth, filterArr);
|
||||
if (searchWidth !== 0 && currentFilterItems.length > 0) {
|
||||
this.setState({
|
||||
openFilterItems: [],
|
||||
hideFilterItems: cloneObjectsArray(currentFilterItems),
|
||||
});
|
||||
} else if (filterWidth > fullWidth / 2) {
|
||||
let newOpenFilterItems = cloneObjectsArray(currentFilterItems);
|
||||
let newHideFilterItems = [];
|
||||
|
||||
let elementsWidth = 0;
|
||||
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: [],
|
||||
openFilterItems: numberOfHiddenItems
|
||||
? currentFilterItems.slice(
|
||||
0,
|
||||
currentFilterItems.length - numberOfHiddenItems
|
||||
)
|
||||
: currentFilterItems.slice(),
|
||||
hiddenFilterItems: numberOfHiddenItems
|
||||
? currentFilterItems.slice(-numberOfHiddenItems)
|
||||
: [],
|
||||
});
|
||||
}
|
||||
}
|
||||
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 indexFilterItem = currentFilterItems.findIndex((x) => x.key === key);
|
||||
if (indexFilterItem != -1) {
|
||||
@ -506,7 +497,7 @@ class FilterInput extends React.Component {
|
||||
this.setState({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
let filterValues = cloneObjectsArray(currentFilterItems);
|
||||
filterValues = filterValues.map(function (item) {
|
||||
@ -518,8 +509,8 @@ class FilterInput extends React.Component {
|
||||
this.state.sortId,
|
||||
this.state.sortDirection ? "desc" : "asc"
|
||||
);
|
||||
}
|
||||
onFilter(filterValues, sortId, sortDirection, searchText) {
|
||||
};
|
||||
onFilter = (filterValues, sortId, sortDirection, searchText) => {
|
||||
let cloneFilterValues = cloneObjectsArray(filterValues);
|
||||
cloneFilterValues = cloneFilterValues.map(function (item) {
|
||||
item.key = item.key.replace(item.group + "_", "");
|
||||
@ -531,8 +522,8 @@ class FilterInput extends React.Component {
|
||||
sortId: sortId,
|
||||
sortDirection: sortDirection,
|
||||
});
|
||||
}
|
||||
onChangeFilter(result) {
|
||||
};
|
||||
onChangeFilter = (result) => {
|
||||
this.setState({
|
||||
searchText: result.inputValue,
|
||||
filterValues: result.filterValues,
|
||||
@ -543,24 +534,21 @@ class FilterInput extends React.Component {
|
||||
this.state.sortDirection ? "desc" : "asc",
|
||||
result.inputValue
|
||||
);
|
||||
}
|
||||
onFilterRender() {
|
||||
if (this.isResizeUpdate) {
|
||||
this.isResizeUpdate = false;
|
||||
}
|
||||
};
|
||||
onFilterRender = () => {
|
||||
this.setState({
|
||||
needUpdateFilter: false,
|
||||
});
|
||||
|
||||
if (this.searchWrapper.current && this.filterWrapper.current) {
|
||||
const fullWidth = this.searchWrapper.current.getBoundingClientRect()
|
||||
.width;
|
||||
const filterWidth = this.filterWrapper.current.getBoundingClientRect()
|
||||
.width;
|
||||
if (fullWidth <= this.minWidth || filterWidth > fullWidth / 2)
|
||||
this.updateFilter();
|
||||
}
|
||||
}
|
||||
onClickFilterItem(event, filterItem) {
|
||||
this.updateFilter();
|
||||
};
|
||||
onClickFilterItem = (event, filterItem) => {
|
||||
const currentFilterItems = cloneObjectsArray(this.state.filterValues);
|
||||
|
||||
this.setState({
|
||||
needUpdateFilter: true,
|
||||
});
|
||||
|
||||
if (filterItem.isSelector) {
|
||||
const indexFilterItem = currentFilterItems.findIndex(
|
||||
(x) => x.group === filterItem.group
|
||||
@ -601,7 +589,7 @@ class FilterInput extends React.Component {
|
||||
this.setState({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
|
||||
if (selectFilterItem.selectedItem.key) {
|
||||
@ -647,7 +635,7 @@ class FilterInput extends React.Component {
|
||||
this.setState({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
} else if (subgroupItems.length === 1) {
|
||||
const selectFilterItem = {
|
||||
@ -678,7 +666,7 @@ class FilterInput extends React.Component {
|
||||
this.setState({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
}
|
||||
} else {
|
||||
@ -705,7 +693,7 @@ class FilterInput extends React.Component {
|
||||
this.setState({
|
||||
filterValues: currentFilterItems,
|
||||
openFilterItems: currentFilterItems,
|
||||
hideFilterItems: [],
|
||||
hiddenFilterItems: [],
|
||||
});
|
||||
|
||||
const clone = cloneObjectsArray(
|
||||
@ -721,7 +709,7 @@ class FilterInput extends React.Component {
|
||||
this.state.sortDirection ? "desc" : "asc"
|
||||
);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
render() {
|
||||
/* eslint-disable react/prop-types */
|
||||
@ -741,6 +729,7 @@ class FilterInput extends React.Component {
|
||||
viewAs,
|
||||
contextMenuHeader,
|
||||
isMobile,
|
||||
sectionWidth,
|
||||
} = this.props;
|
||||
/* eslint-enable react/prop-types */
|
||||
|
||||
@ -748,12 +737,13 @@ class FilterInput extends React.Component {
|
||||
searchText,
|
||||
filterValues,
|
||||
openFilterItems,
|
||||
hideFilterItems,
|
||||
hiddenFilterItems,
|
||||
sortId,
|
||||
sortDirection,
|
||||
} = this.state;
|
||||
|
||||
// console.log("filter input render, openFilterItems", openFilterItems, 'hideFilterItems', hideFilterItems);
|
||||
const smallSectionWidth = sectionWidth ? sectionWidth < 900 : false;
|
||||
|
||||
let iconSize = 30;
|
||||
switch (size) {
|
||||
case "base":
|
||||
@ -769,13 +759,14 @@ class FilterInput extends React.Component {
|
||||
}
|
||||
return (
|
||||
<StyledFilterInput
|
||||
smallSectionWidth={smallSectionWidth}
|
||||
isMobile={isMobile}
|
||||
viewAs={viewAs}
|
||||
className={className}
|
||||
id={id}
|
||||
style={style}
|
||||
>
|
||||
<div className="styled-search-input" ref={this.searchWrapper}>
|
||||
<div className="styled-search-input test" ref={this.searchWrapper}>
|
||||
<SearchInput
|
||||
id={id}
|
||||
isDisabled={isDisabled}
|
||||
@ -796,37 +787,38 @@ class FilterInput extends React.Component {
|
||||
<FilterBlock
|
||||
contextMenuHeader={contextMenuHeader}
|
||||
openFilterItems={openFilterItems}
|
||||
hideFilterItems={hideFilterItems}
|
||||
hiddenFilterItems={hiddenFilterItems}
|
||||
iconSize={iconSize}
|
||||
getFilterData={getFilterData}
|
||||
onClickFilterItem={this.onClickFilterItem}
|
||||
onDeleteFilterItem={this.onDeleteFilterItem}
|
||||
isResizeUpdate={this.isResizeUpdate}
|
||||
onRender={this.onFilterRender}
|
||||
onFilterRender={this.onFilterRender}
|
||||
isDisabled={isDisabled}
|
||||
columnCount={filterColumnCount}
|
||||
needUpdateFilter={this.state.needUpdateFilter}
|
||||
/>
|
||||
</div>
|
||||
</SearchInput>
|
||||
</div>
|
||||
|
||||
<SortComboBox
|
||||
options={getSortData()}
|
||||
isDisabled={isDisabled}
|
||||
onChangeSortId={this.onClickSortItem}
|
||||
onChangeView={this.onClickViewSelector}
|
||||
onChangeSortDirection={this.onChangeSortDirection}
|
||||
selectedOption={
|
||||
getSortData().length > 0
|
||||
? getSortData().find((x) => x.key === sortId)
|
||||
: {}
|
||||
}
|
||||
onButtonClick={this.onSortDirectionClick}
|
||||
viewAs={viewAs}
|
||||
sortDirection={+sortDirection}
|
||||
directionAscLabel={directionAscLabel}
|
||||
directionDescLabel={directionDescLabel}
|
||||
/>
|
||||
<div ref={this.rectComboBoxRef}>
|
||||
<SortComboBox
|
||||
options={getSortData()}
|
||||
isDisabled={isDisabled}
|
||||
onChangeSortId={this.onClickSortItem}
|
||||
onChangeView={this.onClickViewSelector}
|
||||
onChangeSortDirection={this.onChangeSortDirection}
|
||||
selectedOption={
|
||||
getSortData().length > 0
|
||||
? getSortData().find((x) => x.key === sortId)
|
||||
: {}
|
||||
}
|
||||
onButtonClick={this.onSortDirectionClick}
|
||||
viewAs={viewAs}
|
||||
sortDirection={+sortDirection}
|
||||
directionAscLabel={directionAscLabel}
|
||||
directionDescLabel={directionDescLabel}
|
||||
/>
|
||||
</div>
|
||||
{viewAs && (
|
||||
<ViewSelector
|
||||
isDisabled={isDisabled}
|
||||
@ -853,6 +845,9 @@ FilterInput.protoTypes = {
|
||||
filterColumnCount: PropTypes.number,
|
||||
onChangeViewAs: PropTypes.func,
|
||||
contextMenuHeader: PropTypes.string,
|
||||
sectionWidth: PropTypes.number,
|
||||
getSortData: PropTypes.func,
|
||||
value: PropTypes.string,
|
||||
};
|
||||
|
||||
FilterInput.defaultProps = {
|
||||
|
@ -107,7 +107,7 @@ const StyledFilterInput = styled.div`
|
||||
margin-left: 8px;
|
||||
|
||||
${(props) =>
|
||||
props.isMobile &&
|
||||
(props.isMobile || props.smallSectionWidth) &&
|
||||
`
|
||||
width: 50px;
|
||||
.optionalBlock ~ div:first-child{
|
||||
|
@ -39,6 +39,7 @@ class FilterItem extends React.Component {
|
||||
isOpen: false,
|
||||
isOpenSelector: !isOpenSelector,
|
||||
selectedOption,
|
||||
needUpdate: false,
|
||||
};
|
||||
}
|
||||
|
||||
@ -48,7 +49,6 @@ class FilterItem extends React.Component {
|
||||
if (
|
||||
selectedItem &&
|
||||
selectedItem.key !== this.state.selectedOption.key &&
|
||||
selectedItem.key !== this.state.selectedOption.key &&
|
||||
selectedItem.key !== prevProps.selectedItem.key
|
||||
) {
|
||||
const selectedOption = selectedItem.key
|
||||
@ -67,6 +67,11 @@ class FilterItem extends React.Component {
|
||||
selectedOption,
|
||||
});
|
||||
}
|
||||
|
||||
if (this.state.needUpdate) {
|
||||
this.props.onFilterRender();
|
||||
this.setNeedUpdate(false);
|
||||
}
|
||||
}
|
||||
|
||||
onSelect = (option) => {
|
||||
@ -85,6 +90,12 @@ class FilterItem extends React.Component {
|
||||
|
||||
toggleCombobox = (e, isOpen) => this.setState({ isOpen });
|
||||
|
||||
setNeedUpdate = (needUpdate) => {
|
||||
this.setState({
|
||||
needUpdate,
|
||||
});
|
||||
};
|
||||
|
||||
onCancelSelector = (e) => {
|
||||
if (
|
||||
this.state.isOpenSelector &&
|
||||
@ -260,47 +271,64 @@ class FilterBlock extends React.Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
const { hideFilterItems, openFilterItems } = props;
|
||||
const { hiddenFilterItems, openFilterItems } = props;
|
||||
|
||||
this.state = {
|
||||
hideFilterItems: hideFilterItems || [],
|
||||
hiddenFilterItems: hiddenFilterItems || [],
|
||||
openFilterItems: openFilterItems || [],
|
||||
needUpdate: false,
|
||||
};
|
||||
|
||||
this.throttledRender = throttle(this.onRender, 100);
|
||||
}
|
||||
|
||||
componentDidUpdate() {
|
||||
this.throttledRender();
|
||||
componentDidMount() {
|
||||
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) {
|
||||
const { hideFilterItems, openFilterItems } = nextProps;
|
||||
const { hiddenFilterItems, openFilterItems } = nextProps;
|
||||
|
||||
if (!isEqual(this.props, nextProps)) {
|
||||
if (
|
||||
!isEqual(this.props.hideFilterItems, hideFilterItems) ||
|
||||
!isEqual(this.props.hiddenFilterItems, hiddenFilterItems) ||
|
||||
!isEqual(this.props.openFilterItems, openFilterItems)
|
||||
) {
|
||||
this.setState({
|
||||
hideFilterItems,
|
||||
hiddenFilterItems,
|
||||
openFilterItems,
|
||||
});
|
||||
return false;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
if (this.props.isResizeUpdate) {
|
||||
return true;
|
||||
}
|
||||
|
||||
return !isEqual(this.state, nextState);
|
||||
}
|
||||
|
||||
onDeleteFilterItem = (key) => {
|
||||
this.props.onDeleteFilterItem(key);
|
||||
this.setNeedUpdate(true);
|
||||
};
|
||||
|
||||
setNeedUpdate = (needUpdate) => {
|
||||
this.setState({
|
||||
needUpdate,
|
||||
});
|
||||
};
|
||||
getFilterItems = () => {
|
||||
const { openFilterItems, hideFilterItems } = this.state;
|
||||
const { openFilterItems, hiddenFilterItems } = this.state;
|
||||
const _this = this;
|
||||
let result = [];
|
||||
let openItems = [];
|
||||
@ -339,13 +367,14 @@ class FilterBlock extends React.Component {
|
||||
defaultOption={defaultOption}
|
||||
defaultSelectLabel={defaultSelectLabel}
|
||||
selectedItem={selectedItem}
|
||||
onFilterRender={_this.props.onFilterRender}
|
||||
></FilterItem>
|
||||
);
|
||||
});
|
||||
}
|
||||
if (hideFilterItems.length > 0) {
|
||||
if (hiddenFilterItems.length > 0) {
|
||||
let open = false;
|
||||
let hideFilterItemsList = hideFilterItems.map(function (item) {
|
||||
let hideFilterItemsList = hiddenFilterItems.map(function (item) {
|
||||
const {
|
||||
key,
|
||||
group,
|
||||
@ -379,13 +408,14 @@ class FilterBlock extends React.Component {
|
||||
defaultOption={defaultOption}
|
||||
defaultSelectLabel={defaultSelectLabel}
|
||||
selectedItem={selectedItem}
|
||||
onFilterRender={_this.props.onFilterRender}
|
||||
></FilterItem>
|
||||
);
|
||||
});
|
||||
hideItems.push(
|
||||
<HideFilter
|
||||
key="hide-filter"
|
||||
count={hideFilterItems.length}
|
||||
count={hiddenFilterItems.length}
|
||||
isDisabled={this.props.isDisabled}
|
||||
open={open}
|
||||
>
|
||||
@ -420,9 +450,6 @@ class FilterBlock extends React.Component {
|
||||
return result;
|
||||
};
|
||||
|
||||
onRender = () => {
|
||||
this.props.onRender();
|
||||
};
|
||||
render() {
|
||||
const _this = this;
|
||||
const filterItems = this.getFilterItems();
|
||||
@ -453,10 +480,9 @@ class FilterBlock extends React.Component {
|
||||
}
|
||||
FilterBlock.propTypes = {
|
||||
getFilterData: PropTypes.func,
|
||||
hideFilterItems: PropTypes.array,
|
||||
hiddenFilterItems: PropTypes.array,
|
||||
iconSize: PropTypes.oneOfType([PropTypes.number, PropTypes.string]),
|
||||
isDisabled: PropTypes.bool,
|
||||
isResizeUpdate: PropTypes.bool,
|
||||
onDeleteFilterItem: PropTypes.func,
|
||||
onRender: PropTypes.func,
|
||||
openFilterItems: PropTypes.array,
|
||||
|
@ -39,6 +39,7 @@ class HideFilter extends React.Component {
|
||||
className="styled-hide-filter"
|
||||
onClick={this.onClick.bind(this, !popoverOpen)}
|
||||
ref={this.ref}
|
||||
id="styled-hide-filter"
|
||||
>
|
||||
<StyledHideFilterButton id="PopoverLegacy" isDisabled={isDisabled}>
|
||||
{count}
|
||||
|
@ -107,3 +107,5 @@ export function showLoader() {
|
||||
document.body.classList.add("loading");
|
||||
}, 1000);
|
||||
}
|
||||
|
||||
export { withLayoutSize } from "./withLayoutSize";
|
||||
|
15
web/ASC.Web.Common/src/utils/withLayoutSize.js
Normal file
15
web/ASC.Web.Common/src/utils/withLayoutSize.js
Normal 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>
|
||||
);
|
||||
};
|
||||
}
|
Loading…
Reference in New Issue
Block a user