aboutsummaryrefslogtreecommitdiff
path: root/frontend/src/components/filters
diff options
context:
space:
mode:
authorEmiliano Ciavatta2020-10-16 17:06:05 +0000
committerEmiliano Ciavatta2020-10-16 17:06:05 +0000
commit56f70a72196c777f248038bb2e2e4099e6e1367d (patch)
tree714ad5aed8698dfffbb472b3fa74909acb8cdead /frontend/src/components/filters
parent6204c99e69d1707a79c5e56685b47310106c60b0 (diff)
parent79b8b2fa3e8563c986da8baa3a761f2d4f0c6f47 (diff)
Merge branch 'develop'
Diffstat (limited to 'frontend/src/components/filters')
-rw-r--r--frontend/src/components/filters/AdvancedFilters.js54
-rw-r--r--frontend/src/components/filters/BooleanConnectionsFilter.js83
-rw-r--r--frontend/src/components/filters/ExitSearchFilter.js57
-rw-r--r--frontend/src/components/filters/FiltersDefinitions.js90
-rw-r--r--frontend/src/components/filters/RulesConnectionsFilter.js115
-rw-r--r--frontend/src/components/filters/RulesConnectionsFilter.scss118
-rw-r--r--frontend/src/components/filters/StringConnectionsFilter.js114
7 files changed, 264 insertions, 367 deletions
diff --git a/frontend/src/components/filters/AdvancedFilters.js b/frontend/src/components/filters/AdvancedFilters.js
new file mode 100644
index 0000000..8598185
--- /dev/null
+++ b/frontend/src/components/filters/AdvancedFilters.js
@@ -0,0 +1,54 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
+import {withRouter} from "react-router-dom";
+import dispatcher from "../../dispatcher";
+import {updateParams} from "../../utils";
+import ButtonField from "../fields/ButtonField";
+
+class AdvancedFilters extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ this.urlParams = new URLSearchParams(this.props.location.search);
+
+ this.connectionsFiltersCallback = (payload) => {
+ this.urlParams = updateParams(this.urlParams, payload);
+ const active = ["client_address", "client_port", "min_duration", "max_duration", "min_bytes", "max_bytes"]
+ .some((f) => this.urlParams.has(f));
+ if (this.state.active !== active) {
+ this.setState({active});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
+ }
+
+ render() {
+ return (
+ <ButtonField onClick={this.props.onClick} name="advanced_filters" small active={this.state.active}/>
+ );
+ }
+
+}
+
+export default withRouter(AdvancedFilters);
diff --git a/frontend/src/components/filters/BooleanConnectionsFilter.js b/frontend/src/components/filters/BooleanConnectionsFilter.js
index 4c5a78a..0355167 100644
--- a/frontend/src/components/filters/BooleanConnectionsFilter.js
+++ b/frontend/src/components/filters/BooleanConnectionsFilter.js
@@ -1,64 +1,65 @@
-import React, {Component} from 'react';
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
+import dispatcher from "../../dispatcher";
import CheckField from "../fields/CheckField";
class BooleanConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- filterActive: "false"
- };
-
- this.filterChanged = this.filterChanged.bind(this);
- this.needRedirect = false;
- }
+ state = {
+ filterActive: "false"
+ };
componentDidMount() {
let params = new URLSearchParams(this.props.location.search);
this.setState({filterActive: this.toBoolean(params.get(this.props.filterName)).toString()});
+
+ this.connectionsFiltersCallback = (payload) => {
+ const name = this.props.filterName;
+ if (name in payload && this.state.filterActive !== payload[name]) {
+ this.setState({filterActive: payload[name]});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let externalActive = this.toBoolean(urlParams.get(this.props.filterName));
- let filterActive = this.toBoolean(this.state.filterActive);
- // if the filterActive state is changed by another component (and not by filterChanged func) and
- // the query string is not equals at the filterActive state, update the state of the component
- if (this.toBoolean(prevState.filterActive) === filterActive && filterActive !== externalActive) {
- this.setState({filterActive: externalActive.toString()});
- }
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- toBoolean(value) {
+ toBoolean = (value) => {
return value !== null && value.toLowerCase() === "true";
- }
+ };
- filterChanged() {
- this.needRedirect = true;
- this.setState({filterActive: (!this.toBoolean(this.state.filterActive)).toString()});
- }
+ filterChanged = () => {
+ const newValue = (!this.toBoolean(this.state.filterActive)).toString();
+ const urlParams = {};
+ urlParams[this.props.filterName] = newValue === "true" ? "true" : null;
+ dispatcher.dispatch("connections_filters", urlParams);
+ this.setState({filterActive: newValue});
+ };
render() {
- let redirect = null;
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- if (this.toBoolean(this.state.filterActive)) {
- urlParams.set(this.props.filterName, "true");
- } else {
- urlParams.delete(this.props.filterName);
- }
- redirect = <Redirect push to={`${this.props.location.pathname}?${urlParams}`} />;
-
- this.needRedirect = false;
- }
-
return (
<div className="filter" style={{"width": `${this.props.width}px`}}>
<CheckField checked={this.toBoolean(this.state.filterActive)} name={this.props.filterName}
- onChange={this.filterChanged} />
- {redirect}
+ onChange={this.filterChanged} small/>
</div>
);
}
diff --git a/frontend/src/components/filters/ExitSearchFilter.js b/frontend/src/components/filters/ExitSearchFilter.js
new file mode 100644
index 0000000..0aacfd6
--- /dev/null
+++ b/frontend/src/components/filters/ExitSearchFilter.js
@@ -0,0 +1,57 @@
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
+import {withRouter} from "react-router-dom";
+import dispatcher from "../../dispatcher";
+import CheckField from "../fields/CheckField";
+
+class ExitSearchFilter extends Component {
+
+ state = {};
+
+ componentDidMount() {
+ let params = new URLSearchParams(this.props.location.search);
+ this.setState({performedSearch: params.get("performed_search")});
+
+ this.connectionsFiltersCallback = (payload) => {
+ if (this.state.performedSearch !== payload["performed_search"]) {
+ this.setState({performedSearch: payload["performed_search"]});
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
+ }
+
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
+ }
+
+ render() {
+ return (
+ <>
+ {this.state.performedSearch &&
+ <div className="filter" style={{"width": `${this.props.width}px`}}>
+ <CheckField checked={true} name="exit_search" onChange={() =>
+ dispatcher.dispatch("connections_filters", {"performed_search": null})} small/>
+ </div>}
+ </>
+ );
+ }
+
+}
+
+export default withRouter(ExitSearchFilter);
diff --git a/frontend/src/components/filters/FiltersDefinitions.js b/frontend/src/components/filters/FiltersDefinitions.js
deleted file mode 100644
index 02ccb42..0000000
--- a/frontend/src/components/filters/FiltersDefinitions.js
+++ /dev/null
@@ -1,90 +0,0 @@
-import {
- cleanNumber,
- timestampToTime,
- timeToTimestamp,
- validate24HourTime,
- validateIpAddress,
- validateMin,
- validatePort
-} from "../../utils";
-import StringConnectionsFilter from "./StringConnectionsFilter";
-import React from "react";
-import RulesConnectionsFilter from "./RulesConnectionsFilter";
-import BooleanConnectionsFilter from "./BooleanConnectionsFilter";
-
-export const filtersNames = ["service_port", "matched_rules", "client_address", "client_port",
- "min_duration", "max_duration", "min_bytes", "max_bytes", "started_after",
- "started_before", "closed_after", "closed_before", "marked", "hidden"];
-
-export const filtersDefinitions = {
- service_port: <StringConnectionsFilter filterName="service_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="service_port_filter"
- width={200} />,
- matched_rules: <RulesConnectionsFilter />,
- client_address: <StringConnectionsFilter filterName="client_address"
- defaultFilterValue="all_addresses"
- validateFunc={validateIpAddress}
- key="client_address_filter"
- width={320} />,
- client_port: <StringConnectionsFilter filterName="client_port"
- defaultFilterValue="all_ports"
- replaceFunc={cleanNumber}
- validateFunc={validatePort}
- key="client_port_filter"
- width={200} />,
- min_duration: <StringConnectionsFilter filterName="min_duration"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_duration_filter"
- width={200} />,
- max_duration: <StringConnectionsFilter filterName="max_duration"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_duration_filter"
- width={200} />,
- min_bytes: <StringConnectionsFilter filterName="min_bytes"
- defaultFilterValue="0"
- replaceFunc={cleanNumber}
- validateFunc={validateMin(0)}
- key="min_bytes_filter"
- width={200} />,
- max_bytes: <StringConnectionsFilter filterName="max_bytes"
- defaultFilterValue="∞"
- replaceFunc={cleanNumber}
- key="max_bytes_filter"
- width={200} />,
- started_after: <StringConnectionsFilter filterName="started_after"
- defaultFilterValue="00:00:00"
- validateFunc={validate24HourTime}
- encodeFunc={timeToTimestamp}
- decodeFunc={timestampToTime}
- key="started_after_filter"
- width={200} />,
- started_before: <StringConnectionsFilter filterName="started_before"
- defaultFilterValue="00:00:00"
- validateFunc={validate24HourTime}
- encodeFunc={timeToTimestamp}
- decodeFunc={timestampToTime}
- key="started_before_filter"
- width={200} />,
- closed_after: <StringConnectionsFilter filterName="closed_after"
- defaultFilterValue="00:00:00"
- validateFunc={validate24HourTime}
- encodeFunc={timeToTimestamp}
- decodeFunc={timestampToTime}
- key="closed_after_filter"
- width={200} />,
- closed_before: <StringConnectionsFilter filterName="closed_before"
- defaultFilterValue="00:00:00"
- validateFunc={validate24HourTime}
- encodeFunc={timeToTimestamp}
- decodeFunc={timestampToTime}
- key="closed_before_filter"
- width={200} />,
- marked: <BooleanConnectionsFilter filterName={"marked"} />,
- hidden: <BooleanConnectionsFilter filterName={"hidden"} />
-};
diff --git a/frontend/src/components/filters/RulesConnectionsFilter.js b/frontend/src/components/filters/RulesConnectionsFilter.js
index 8366189..210ee36 100644
--- a/frontend/src/components/filters/RulesConnectionsFilter.js
+++ b/frontend/src/components/filters/RulesConnectionsFilter.js
@@ -1,86 +1,79 @@
-import React, {Component} from 'react';
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
-import './RulesConnectionsFilter.scss';
-import ReactTags from 'react-tag-autocomplete';
import backend from "../../backend";
+import dispatcher from "../../dispatcher";
+import TagField from "../fields/TagField";
-const classNames = require('classnames');
+const classNames = require("classnames");
+const _ = require("lodash");
class RulesConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- mounted: false,
- rules: [],
- activeRules: []
- };
-
- this.needRedirect = false;
- }
+ state = {
+ rules: [],
+ activeRules: []
+ };
componentDidMount() {
- let params = new URLSearchParams(this.props.location.search);
+ const params = new URLSearchParams(this.props.location.search);
let activeRules = params.getAll("matched_rules") || [];
- backend.get("/api/rules").then(res => {
- let rules = res.json.flatMap(rule => rule.enabled ? [{id: rule.id, name: rule.name}] : []);
- activeRules = rules.filter(rule => activeRules.some(id => rule.id === id));
- this.setState({rules, activeRules, mounted: true});
+ backend.get("/api/rules").then((res) => {
+ let rules = res.json.flatMap((rule) => rule.enabled ? [{id: rule.id, name: rule.name}] : []);
+ activeRules = rules.filter((rule) => activeRules.some((id) => rule.id === id));
+ this.setState({rules, activeRules});
});
- }
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let externalRules = urlParams.getAll("matched_rules") || [];
- let activeRules = this.state.activeRules.map(r => r.id);
- let compareRules = (first, second) => first.sort().join(",") === second.sort().join(",");
- if (this.state.mounted &&
- compareRules(prevState.activeRules.map(r => r.id), activeRules) &&
- !compareRules(externalRules, activeRules)) {
- this.setState({activeRules: externalRules.map(id => this.state.rules.find(r => r.id === id))});
- }
+ this.connectionsFiltersCallback = (payload) => {
+ if ("matched_rules" in payload && !_.isEqual(payload["matched_rules"].sort(), this.state.activeRules.sort())) {
+ const newRules = this.state.rules.filter((r) => payload["matched_rules"].includes(r.id));
+ this.setState({
+ activeRules: newRules.map((r) => {
+ return {id: r.id, name: r.name};
+ })
+ });
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- onDelete(i) {
- const activeRules = this.state.activeRules.slice(0);
- activeRules.splice(i, 1);
- this.needRedirect = true;
- this.setState({ activeRules });
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- onAddition(rule) {
- if (!this.state.activeRules.includes(rule)) {
- const activeRules = [].concat(this.state.activeRules, rule);
- this.needRedirect = true;
+ onChange = (activeRules) => {
+ if (!_.isEqual(activeRules.sort(), this.state.activeRules.sort())) {
this.setState({activeRules});
+ dispatcher.dispatch("connections_filters", {"matched_rules": activeRules.map((r) => r.id)});
}
- }
+ };
render() {
- let redirect = null;
-
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- urlParams.delete("matched_rules");
- this.state.activeRules.forEach(rule => urlParams.append("matched_rules", rule.id));
- redirect = <Redirect push to={`${this.props.location.pathname}?${urlParams}`} />;
-
- this.needRedirect = false;
- }
-
return (
- <div className={classNames("filter", "d-inline-block", {"filter-active" : this.state.filterActive === "true"})}>
- <div className="filter-booleanq">
- <ReactTags tags={this.state.activeRules} suggestions={this.state.rules}
- onDelete={this.onDelete.bind(this)} onAddition={this.onAddition.bind(this)}
- minQueryLength={0} placeholderText="rule_name"
- suggestionsFilter={(suggestion, query) =>
- suggestion.name.startsWith(query) && !this.state.activeRules.includes(suggestion)} />
+ <div
+ className={classNames("filter", "d-inline-block", {"filter-active": this.state.filterActive === "true"})}>
+ <div className="filter-rules">
+ <TagField tags={this.state.activeRules} onChange={this.onChange}
+ suggestions={_.differenceWith(this.state.rules, this.state.activeRules, _.isEqual)}
+ minQueryLength={0} name="matched_rules" inline small placeholder="rule_name"/>
</div>
-
- {redirect}
</div>
);
}
diff --git a/frontend/src/components/filters/RulesConnectionsFilter.scss b/frontend/src/components/filters/RulesConnectionsFilter.scss
deleted file mode 100644
index 71efd0d..0000000
--- a/frontend/src/components/filters/RulesConnectionsFilter.scss
+++ /dev/null
@@ -1,118 +0,0 @@
-@import "../../colors";
-
-.react-tags {
- font-size: 12px;
- position: relative;
- z-index: 10;
- padding: 0 6px;
- cursor: text;
- border-radius: 4px;
- background-color: $color-primary-2;
-}
-
-.react-tags.is-focused {
- border-color: #b1b1b1;
-}
-
-.react-tags__selected {
- display: inline;
-}
-
-.react-tags__selected-tag {
- font-size: 11px;
- display: inline-block;
- margin: 0 6px 6px 0;
- padding: 2px 4px;
- color: $color-primary-3;
- border: none;
- border-radius: 2px;
- background: $color-primary-4;
-}
-
-.react-tags__selected-tag::after {
- margin-left: 8px;
- content: "\2715";
- color: $color-primary-3;
-}
-
-.react-tags__selected-tag:hover,
-.react-tags__selected-tag:focus {
- border-color: #b1b1b1;
-}
-
-.react-tags__search {
- display: inline-block;
- max-width: 100%;
- padding: 7px 10px;
-}
-
-@media screen and (min-width: 30em) {
- .react-tags__search {
- position: relative;
- }
-}
-
-.react-tags__search-input {
- font-size: inherit;
- line-height: inherit;
- max-width: 100%;
- margin: 0;
- padding: 0;
- color: $color-primary-4;
- border: 0;
- outline: none;
- background-color: $color-primary-2;
-}
-
-.react-tags__search-input::-ms-clear {
- display: none;
-}
-
-.react-tags__suggestions {
- position: absolute;
- top: 100%;
- left: 0;
- width: 100%;
-}
-
-@media screen and (min-width: 30em) {
- .react-tags__suggestions {
- width: 240px;
- }
-}
-
-.react-tags__suggestions ul {
- font-size: 12px;
- margin: 4px -1px;
- padding: 0;
- list-style: none;
- color: $color-primary-1;
- border-radius: 2px;
- background: $color-primary-4;
-}
-
-.react-tags__suggestions li {
- padding: 3px 5px;
- border-bottom: 1px solid #ddd;
-}
-
-.react-tags__suggestions li mark {
- font-weight: 600;
- text-decoration: underline;
- background: none;
-}
-
-.react-tags__suggestions li:hover {
- cursor: pointer;
- color: $color-primary-4;
- background: $color-primary-0;
-}
-
-.react-tags__suggestions li.is-active {
- background: #b7cfe0;
-}
-
-.react-tags__suggestions li.is-disabled {
- cursor: auto;
- opacity: 0.5;
-}
diff --git a/frontend/src/components/filters/StringConnectionsFilter.js b/frontend/src/components/filters/StringConnectionsFilter.js
index f463593..c5d7075 100644
--- a/frontend/src/components/filters/StringConnectionsFilter.js
+++ b/frontend/src/components/filters/StringConnectionsFilter.js
@@ -1,36 +1,52 @@
-import React, {Component} from 'react';
+/*
+ * This file is part of caronte (https://github.com/eciavatta/caronte).
+ * Copyright (c) 2020 Emiliano Ciavatta.
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 3.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ */
+
+import React, {Component} from "react";
import {withRouter} from "react-router-dom";
-import {Redirect} from "react-router";
+import dispatcher from "../../dispatcher";
import InputField from "../fields/InputField";
class StringConnectionsFilter extends Component {
- constructor(props) {
- super(props);
- this.state = {
- fieldValue: "",
- filterValue: null,
- timeoutHandle: null,
- invalidValue: false
- };
- this.needRedirect = false;
- this.filterChanged = this.filterChanged.bind(this);
- }
+ state = {
+ fieldValue: "",
+ filterValue: null,
+ timeoutHandle: null,
+ invalidValue: false
+ };
componentDidMount() {
let params = new URLSearchParams(this.props.location.search);
this.updateStateFromFilterValue(params.get(this.props.filterName));
+
+ this.connectionsFiltersCallback = (payload) => {
+ const name = this.props.filterName;
+ if (name in payload && this.state.filterValue !== payload[name]) {
+ this.updateStateFromFilterValue(payload[name]);
+ }
+ };
+ dispatcher.register("connections_filters", this.connectionsFiltersCallback);
}
- componentDidUpdate(prevProps, prevState, snapshot) {
- let urlParams = new URLSearchParams(this.props.location.search);
- let filterValue = urlParams.get(this.props.filterName);
- if (prevState.filterValue === this.state.filterValue && this.state.filterValue !== filterValue) {
- this.updateStateFromFilterValue(filterValue);
- }
+ componentWillUnmount() {
+ dispatcher.unregister(this.connectionsFiltersCallback);
}
- updateStateFromFilterValue(filterValue) {
+ updateStateFromFilterValue = (filterValue) => {
if (filterValue !== null) {
let fieldValue = filterValue;
if (typeof this.props.decodeFunc === "function") {
@@ -40,28 +56,28 @@ class StringConnectionsFilter extends Component {
fieldValue = this.props.replaceFunc(fieldValue);
}
if (this.isValueValid(fieldValue)) {
- this.setState({
- fieldValue: fieldValue,
- filterValue: filterValue
- });
+ this.setState({fieldValue, filterValue});
} else {
- this.setState({
- fieldValue: fieldValue,
- invalidValue: true
- });
+ this.setState({fieldValue, invalidValue: true});
}
} else {
this.setState({fieldValue: "", filterValue: null});
}
- }
+ };
- isValueValid(value) {
+ isValueValid = (value) => {
return typeof this.props.validateFunc !== "function" ||
(typeof this.props.validateFunc === "function" && this.props.validateFunc(value));
- }
+ };
- filterChanged(fieldValue) {
- if (this.state.timeoutHandle !== null) {
+ changeFilterValue = (value) => {
+ const urlParams = {};
+ urlParams[this.props.filterName] = value;
+ dispatcher.dispatch("connections_filters", urlParams);
+ };
+
+ filterChanged = (fieldValue) => {
+ if (this.state.timeoutHandle) {
clearTimeout(this.state.timeoutHandle);
}
@@ -70,11 +86,11 @@ class StringConnectionsFilter extends Component {
}
if (fieldValue === "") {
- this.needRedirect = true;
this.setState({fieldValue: "", filterValue: null, invalidValue: false});
- return;
+ return this.changeFilterValue(null);
}
+
if (this.isValueValid(fieldValue)) {
let filterValue = fieldValue;
if (filterValue !== "" && typeof this.props.encodeFunc === "function") {
@@ -82,42 +98,26 @@ class StringConnectionsFilter extends Component {
}
this.setState({
- fieldValue: fieldValue,
+ fieldValue,
timeoutHandle: setTimeout(() => {
- this.needRedirect = true;
- this.setState({filterValue: filterValue});
+ this.setState({filterValue});
+ this.changeFilterValue(filterValue);
}, 500),
invalidValue: false
});
} else {
- this.needRedirect = true;
- this.setState({
- fieldValue: fieldValue,
- invalidValue: true
- });
+ this.setState({fieldValue, invalidValue: true});
}
- }
+ };
render() {
- let redirect = null;
- if (this.needRedirect) {
- let urlParams = new URLSearchParams(this.props.location.search);
- if (this.state.filterValue !== null) {
- urlParams.set(this.props.filterName, this.state.filterValue);
- } else {
- urlParams.delete(this.props.filterName);
- }
- redirect = <Redirect push to={`${this.props.location.pathname}?${urlParams}`} />;
- this.needRedirect = false;
- }
let active = this.state.filterValue !== null;
return (
<div className="filter" style={{"width": `${this.props.width}px`}}>
<InputField active={active} invalid={this.state.invalidValue} name={this.props.filterName}
placeholder={this.props.defaultFilterValue} onChange={this.filterChanged}
- value={this.state.fieldValue} inline={true} small={true} />
- {redirect}
+ value={this.state.fieldValue} inline={this.props.inline} small={this.props.small}/>
</div>
);
}