Commit 3747f392 authored by James Long's avatar James Long Committed by Commit Bot

[Lorenz] Modify filter list to use checkboxes

As part of UX discussions, the filter list is now a list of nodes that
can be used in the filter. Whether a node is actually in the filter or
not is toggled by its checkbox. "Select all" and "Deselect all" buttons
have been added to make checkbox manipulation easier.

A node enters the filter list through the filter input or the node
details panel. It always enters in the checked state. A node exits the
filter list through the user manually clicking the x beside it. Once in
the filter list, the node's filter visibility can be toggled by a
checkbox beside it or through the node details panel.

Checkbox information is also encoded in the URL.

Bug: 1111836
Change-Id: If92d64a9bdcfc93d6b86d1a335b795a5ab628c01
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/2333066
Commit-Queue: James Long <yjlong@google.com>
Reviewed-by: default avatarMohamed Heikal <mheikal@chromium.org>
Reviewed-by: default avatarSamuel Huang <huangs@chromium.org>
Cr-Commit-Position: refs/heads/master@{#795184}
parent e861f454
...@@ -16,48 +16,88 @@ const GraphEdgeColor = { ...@@ -16,48 +16,88 @@ const GraphEdgeColor = {
}; };
/** /**
* A container representing the visualization's node filter. Nodes included in * Underlying data for node filtering. The UI shows a "filter list" that
* the filter are allowed to be displayed on the graph. * displays nodes of interest, and each can be toggled on/off using a checkbox.
* Each node is classified as:
* 1. Ignored: Not in filter list; hidden in visualizer.
* 2. Unchecked: In filter list; hidden in visualizer.
* 3. Checked (Selected): In filter list; shown in visualizer.
*/ */
class NodeFilterData { class NodeFilterData {
constructor() {
/** /**
* Vue does not currently support reactivity on ES6 Sets. (Planned addition * @typedef {Object} NodeFilterEntry An entry in the filter list.
* for 3.0 https://github.com/vuejs/vue/issues/2410#issuecomment-434990853). * @property {string} name The name of the node to be filtered.
* For performance, we maintain a Set for lookups when filtering nodes/edges * @property {boolean} checked Whether the node is checked (selected). If
* and expose an Array to the UI for reactivity. We sync the data in these * true, then the node is shown in the visualizer.
* two structures manually.
*/ */
constructor() {
/** @public {!Set<string>} */ /**
this.nodeSet = new Set(); * List of filter list entries, i.e., nodes in unchecked or checked state.
/** @public {!Array<string>} */ * @public {!Array<!NodeFilterEntry>)
this.nodeList = []; */
this.filterList = [];
} }
/** /**
* Adds a node to the node set + array. * Finds a node in the filter list, creating and adding one if necessary.
* @param {string} nodeName The name of the node to add. * @param {string} nodeName The name of the node to find.
* @return {!NodeFilterEntry} The node's entry in the filter list.
*/ */
addNode(nodeName) { addOrFindNode(nodeName) {
if (!this.nodeSet.has(nodeName)) { const foundIndex = this.filterList.findIndex(
this.nodeSet.add(nodeName); filterEntry => filterEntry.name === nodeName);
this.nodeList.push(nodeName); if (foundIndex >= 0) {
return this.filterList[foundIndex];
} }
const entryToAdd = {
name: nodeName,
checked: true,
};
this.filterList.push(entryToAdd);
return entryToAdd;
} }
/** /**
* Removes a node from the node set + array. * Removes a node from the filter list (i.e., set state to ignored) if it
* exists.
* @param {string} nodeName The name of the node to remove. * @param {string} nodeName The name of the node to remove.
*/ */
removeNode(nodeName) { removeNode(nodeName) {
const deleted = this.nodeSet.delete(nodeName); const deleteIndex = this.filterList.findIndex(
if (deleted) { filterEntry => filterEntry.name === nodeName);
const deleteIndex = this.nodeList.indexOf(nodeName); if (deleteIndex >= 0) {
// TODO(yjlong): If order turns out to be unimportant, just swap the // TODO(yjlong): If order turns out to be unimportant, just swap the
// last element and the deleted element, then pop. // last element and the deleted element, then pop.
this.nodeList.splice(deleteIndex, 1); this.filterList.splice(deleteIndex, 1);
} }
} }
/**
* Sets all nodes in the filter list to checked.
*/
checkAll() {
for (const filterEntry of this.filterList) {
filterEntry.checked = true;
}
}
/**
* Sets all nodes in the filter list to unchecked.
*/
uncheckAll() {
for (const filterEntry of this.filterList) {
filterEntry.checked = false;
}
}
/**
* @return {!Set<string>} A set of nodes that are checked in the filter.
*/
getSelectedNodeSet() {
return new Set(this.filterList.filter(filterEntry => filterEntry.checked)
.map(filterEntry => filterEntry.name));
}
} }
/** Data store containing graph display-related settings. */ /** Data store containing graph display-related settings. */
...@@ -89,9 +129,12 @@ class DisplaySettingsData { ...@@ -89,9 +129,12 @@ class DisplaySettingsData {
urlProcessor.append( urlProcessor.append(
URL_PARAM_KEYS.COLOR_ONLY_ON_HOVER, this.colorOnlyOnHover); URL_PARAM_KEYS.COLOR_ONLY_ON_HOVER, this.colorOnlyOnHover);
urlProcessor.append(URL_PARAM_KEYS.EDGE_COLOR, this.graphEdgeColor); urlProcessor.append(URL_PARAM_KEYS.EDGE_COLOR, this.graphEdgeColor);
if (this.nodeFilterData.nodeList.length > 0) { if (this.nodeFilterData.filterList.length > 0) {
urlProcessor.appendArray( urlProcessor.appendArray(URL_PARAM_KEYS.FILTER_NAMES,
URL_PARAM_KEYS.FILTER, this.nodeFilterData.nodeList); this.nodeFilterData.filterList.map(filterEntry => filterEntry.name));
urlProcessor.appendArray(URL_PARAM_KEYS.FILTER_CHECKED,
this.nodeFilterData.filterList.map(
filterEntry => filterEntry.checked));
} }
} }
...@@ -110,8 +153,20 @@ class DisplaySettingsData { ...@@ -110,8 +153,20 @@ class DisplaySettingsData {
URL_PARAM_KEYS.COLOR_ONLY_ON_HOVER, this.colorOnlyOnHover); URL_PARAM_KEYS.COLOR_ONLY_ON_HOVER, this.colorOnlyOnHover);
this.graphEdgeColor = urlProcessor.getString( this.graphEdgeColor = urlProcessor.getString(
URL_PARAM_KEYS.EDGE_COLOR, this.graphEdgeColor); URL_PARAM_KEYS.EDGE_COLOR, this.graphEdgeColor);
for (const filterItem of urlProcessor.getArray(URL_PARAM_KEYS.FILTER, [])) {
this.nodeFilterData.addNode(filterItem); const filterNames = urlProcessor.getArray(URL_PARAM_KEYS.FILTER_NAMES, []);
const filterChecked = urlProcessor.getArray(
URL_PARAM_KEYS.FILTER_CHECKED, []);
for (const [filterIdx, filterName] of filterNames.entries()) {
const filterEntry = this.nodeFilterData.addOrFindNode(filterName);
// If there is no corresponding entry in `filterChecked` (e.g., if the
// checked param is empty), use true as a default value.
if (filterIdx < filterChecked.length) {
const filterElemChecked = (filterChecked[filterIdx] === 'true');
filterEntry.checked = filterElemChecked;
} else {
filterEntry.checked = true;
}
} }
} }
} }
......
...@@ -14,7 +14,8 @@ const PagePathName = { ...@@ -14,7 +14,8 @@ const PagePathName = {
// Keys for identifying URL params. // Keys for identifying URL params.
const URL_PARAM_KEYS = { const URL_PARAM_KEYS = {
// Common keys: // Common keys:
FILTER: 'f', FILTER_NAMES: 'fn',
FILTER_CHECKED: 'fc',
INBOUND_DEPTH: 'ibd', INBOUND_DEPTH: 'ibd',
OUTBOUND_DEPTH: 'obd', OUTBOUND_DEPTH: 'obd',
CURVE_EDGES: 'ce', CURVE_EDGES: 'ce',
......
...@@ -7,10 +7,12 @@ ...@@ -7,10 +7,12 @@
<div id="page-controls"> <div id="page-controls">
<GraphFilterInput <GraphFilterInput
:node-ids="pageModel.getNodeIds()" :node-ids="pageModel.getNodeIds()"
@[CUSTOM_EVENTS.FILTER_SUBMITTED]="addNodeToFilter"/> @[CUSTOM_EVENTS.FILTER_SUBMITTED]="filterAddOrCheckNode"/>
<GraphFilterItems <GraphFilterItems
:node-filter-data="displaySettingsData.nodeFilterData" :node-filter-data="displaySettingsData.nodeFilterData"
@[CUSTOM_EVENTS.FILTER_ELEMENT_CLICKED]="removeNodeFromFilter"/> @[CUSTOM_EVENTS.FILTER_REMOVE]="filterRemoveNode"
@[CUSTOM_EVENTS.FILTER_CHECK_ALL]="filterCheckAll"
@[CUSTOM_EVENTS.FILTER_UNCHECK_ALL]="filterUncheckAll"/>
<NumericInput <NumericInput
description="Change inbound (blue) depth:" description="Change inbound (blue) depth:"
input-id="inbound-input" input-id="inbound-input"
...@@ -37,8 +39,8 @@ ...@@ -37,8 +39,8 @@
:selected-hull-display.sync="displaySettingsData.hullDisplay"/> :selected-hull-display.sync="displaySettingsData.hullDisplay"/>
<GraphSelectedNodeDetails <GraphSelectedNodeDetails
:selected-node-details-data="pageModel.selectedNodeDetailsData" :selected-node-details-data="pageModel.selectedNodeDetailsData"
@[CUSTOM_EVENTS.ADD_TO_FILTER_CLICKED]="addNodeToFilter" @[CUSTOM_EVENTS.DETAILS_CHECK_NODE]="filterAddOrCheckNode"
@[CUSTOM_EVENTS.REMOVE_FROM_FILTER_CLICKED]="removeNodeFromFilter"/> @[CUSTOM_EVENTS.DETAILS_UNCHECK_NODE]="filterUncheckNode"/>
<ClassDetailsPanel <ClassDetailsPanel
:selected-class="pageModel.selectedNodeDetailsData.selectedNode"/> :selected-class="pageModel.selectedNodeDetailsData.selectedNode"/>
</div> </div>
...@@ -143,13 +145,13 @@ const ClassGraphPage = { ...@@ -143,13 +145,13 @@ const ClassGraphPage = {
const pageUrlProcessor = new UrlProcessor(pageUrl.searchParams); const pageUrlProcessor = new UrlProcessor(pageUrl.searchParams);
this.displaySettingsData.readUrlProcessor(pageUrlProcessor); this.displaySettingsData.readUrlProcessor(pageUrlProcessor);
if (this.displaySettingsData.nodeFilterData.nodeList.length === 0) { if (this.displaySettingsData.nodeFilterData.filterList.length === 0) {
// TODO(yjlong): This is test data. Remove this when no longer needed. // TODO(yjlong): This is test data. Remove this when no longer needed.
this.addNodesToFilter([ [
'org.chromium.chrome.browser.tabmodel.AsyncTabParams', 'org.chromium.chrome.browser.tabmodel.AsyncTabParams',
'org.chromium.chrome.browser.ActivityTabProvider', 'org.chromium.chrome.browser.ActivityTabProvider',
'org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver', 'org.chromium.chrome.browser.tabmodel.TabModelSelectorTabModelObserver',
]); ].forEach(nodeName => this.filterAddOrCheckNode(nodeName));
} }
}, },
methods: { methods: {
...@@ -160,25 +162,22 @@ const ClassGraphPage = { ...@@ -160,25 +162,22 @@ const ClassGraphPage = {
const pageUrl = urlProcessor.getUrl(document.URL, PagePathName.CLASS); const pageUrl = urlProcessor.getUrl(document.URL, PagePathName.CLASS);
history.replaceState(null, '', pageUrl); history.replaceState(null, '', pageUrl);
}, },
/** filterRemoveNode: function(nodeName) {
* @param {string} nodeName The node to add. this.displaySettingsData.nodeFilterData.removeNode(nodeName);
*/
addNodeToFilter: function(nodeName) {
this.displaySettingsData.nodeFilterData.addNode(nodeName);
}, },
/** filterAddOrCheckNode: function(nodeName) {
* @param {!Array<string>} nodeNames The nodes to add. this.displaySettingsData.nodeFilterData.addOrFindNode(
*/ nodeName).checked = true;
addNodesToFilter: function(nodeNames) {
for (const nodeName of nodeNames) {
this.displaySettingsData.nodeFilterData.addNode(nodeName);
}
}, },
/** filterUncheckNode: function(nodeName) {
* @param {string} nodeName The node to remove. this.displaySettingsData.nodeFilterData.addOrFindNode(
*/ nodeName).checked = false;
removeNodeFromFilter: function(nodeName) { },
this.displaySettingsData.nodeFilterData.removeNode(nodeName); filterCheckAll: function() {
this.displaySettingsData.nodeFilterData.checkAll();
},
filterUncheckAll: function() {
this.displaySettingsData.nodeFilterData.uncheckAll();
}, },
/** /**
* @param {number} depth The new inbound depth. * @param {number} depth The new inbound depth.
......
...@@ -3,14 +3,31 @@ ...@@ -3,14 +3,31 @@
found in the LICENSE file. --> found in the LICENSE file. -->
<template> <template>
<ul class="filter-items"> <div id="filter-items-container">
<div id="controls">
<button @click="checkAll">
Check All
</button>
<button @click="uncheckAll">
Uncheck All
</button>
</div>
<ul id="filter-list">
<li <li
v-for="node in nodeList" v-for="node in filterList"
:key="node.id" :key="node.name">
@click="removeFilter"> <div class="filter-list-item">
{{ node }} <div @click="removeFromFilter(node.name)">
x
</div>
<input
v-model="node.checked"
type="checkbox">
<div>{{ node.name }}</div>
</div>
</li> </li>
</ul> </ul>
</div>
</template> </template>
<script> <script>
...@@ -25,9 +42,14 @@ const GraphFilterItems = { ...@@ -25,9 +42,14 @@ const GraphFilterItems = {
return this.nodeFilterData; return this.nodeFilterData;
}, },
methods: { methods: {
removeFilter: function(e) { removeFromFilter: function(nodeName) {
const filterText = e.target.textContent; this.$emit(CUSTOM_EVENTS.FILTER_REMOVE, nodeName);
this.$emit(CUSTOM_EVENTS.FILTER_ELEMENT_CLICKED, filterText.trim()); },
checkAll: function() {
this.$emit(CUSTOM_EVENTS.FILTER_CHECK_ALL);
},
uncheckAll: function() {
this.$emit(CUSTOM_EVENTS.FILTER_UNCHECK_ALL);
}, },
}, },
}; };
...@@ -36,10 +58,31 @@ export default GraphFilterItems; ...@@ -36,10 +58,31 @@ export default GraphFilterItems;
</script> </script>
<style scoped> <style scoped>
.filter-items { ul {
overflow: hidden; list-style-type: none;
overflow-y: scroll; }
min-width: 100px;
#filter-items-container {
display: flex;
flex-direction: column;
margin-right: 20px; margin-right: 20px;
min-width: 100px;
}
#filter-list {
margin: 0;
overflow-x: hidden;
overflow-y: scroll;
padding: 0;
}
#controls {
display: flex;
flex-direction: row;
}
.filter-list-item {
display: flex;
flex-direction: row;
} }
</style> </style>
...@@ -16,13 +16,13 @@ ...@@ -16,13 +16,13 @@
</ul> </ul>
<button <button
v-if="selectedNode.visualizationState.selectedByFilter" v-if="selectedNode.visualizationState.selectedByFilter"
@click="removeSelectedFromFilter"> @click="uncheckNodeInFilter">
Remove from filter Uncheck in filter
</button> </button>
<button <button
v-else v-else
@click="addSelectedToFilter"> @click="checkNodeInFilter">
Add to filter Add/check in filter
</button> </button>
</template> </template>
<div v-else> <div v-else>
...@@ -43,12 +43,11 @@ const GraphSelectedNodeDetails = { ...@@ -43,12 +43,11 @@ const GraphSelectedNodeDetails = {
return this.selectedNodeDetailsData; return this.selectedNodeDetailsData;
}, },
methods: { methods: {
addSelectedToFilter: function() { checkNodeInFilter: function(check) {
this.$emit(CUSTOM_EVENTS.ADD_TO_FILTER_CLICKED, this.selectedNode.id); this.$emit(CUSTOM_EVENTS.DETAILS_CHECK_NODE, this.selectedNode.id);
}, },
removeSelectedFromFilter: function() { uncheckNodeInFilter: function(check) {
this.$emit( this.$emit(CUSTOM_EVENTS.DETAILS_UNCHECK_NODE, this.selectedNode.id);
CUSTOM_EVENTS.REMOVE_FROM_FILTER_CLICKED, this.selectedNode.id);
}, },
}, },
}; };
......
...@@ -42,7 +42,7 @@ const GraphVisualization = { ...@@ -42,7 +42,7 @@ const GraphVisualization = {
graphUpdateTriggers: { graphUpdateTriggers: {
handler: function() { handler: function() {
const d3Data = this.pageModel.graphModel.getDataForD3( const d3Data = this.pageModel.graphModel.getDataForD3(
this.displaySettingsData.nodeFilterData.nodeSet, this.displaySettingsData.nodeFilterData.getSelectedNodeSet(),
this.displaySettingsData.inboundDepth, this.displaySettingsData.inboundDepth,
this.displaySettingsData.outboundDepth, this.displaySettingsData.outboundDepth,
); );
......
...@@ -19,7 +19,7 @@ const LinkToGraph = { ...@@ -19,7 +19,7 @@ const LinkToGraph = {
computed: { computed: {
url: function() { url: function() {
const urlProcessor = UrlProcessor.createForOutput(); const urlProcessor = UrlProcessor.createForOutput();
urlProcessor.appendArray(URL_PARAM_KEYS.FILTER, this.filter); urlProcessor.appendArray(URL_PARAM_KEYS.FILTER_NAMES, this.filter);
return urlProcessor.getUrl(document.URL, this.graphType); return urlProcessor.getUrl(document.URL, this.graphType);
}, },
}, },
......
...@@ -7,10 +7,12 @@ ...@@ -7,10 +7,12 @@
<div id="page-controls"> <div id="page-controls">
<GraphFilterInput <GraphFilterInput
:node-ids="pageModel.getNodeIds()" :node-ids="pageModel.getNodeIds()"
@[CUSTOM_EVENTS.FILTER_SUBMITTED]="addNodeToFilter"/> @[CUSTOM_EVENTS.FILTER_SUBMITTED]="filterAddOrCheckNode"/>
<GraphFilterItems <GraphFilterItems
:node-filter-data="displaySettingsData.nodeFilterData" :node-filter-data="displaySettingsData.nodeFilterData"
@[CUSTOM_EVENTS.FILTER_ELEMENT_CLICKED]="removeNodeFromFilter"/> @[CUSTOM_EVENTS.FILTER_REMOVE]="filterRemoveNode"
@[CUSTOM_EVENTS.FILTER_CHECK_ALL]="filterCheckAll"
@[CUSTOM_EVENTS.FILTER_UNCHECK_ALL]="filterUncheckAll"/>
<NumericInput <NumericInput
description="Change inbound (blue) depth:" description="Change inbound (blue) depth:"
input-id="inbound-input" input-id="inbound-input"
...@@ -24,7 +26,7 @@ ...@@ -24,7 +26,7 @@
<GraphVisualization <GraphVisualization
:graph-update-triggers="[ :graph-update-triggers="[
displaySettingsData, displaySettingsData,
displaySettingsData.nodeFilterData.nodeList, displaySettingsData.nodeFilterData.filterList,
]" ]"
:page-model="pageModel" :page-model="pageModel"
:display-settings-data="displaySettingsData" :display-settings-data="displaySettingsData"
...@@ -33,13 +35,11 @@ ...@@ -33,13 +35,11 @@
<GraphDisplaySettings <GraphDisplaySettings
:display-settings-data="displaySettingsData"/> :display-settings-data="displaySettingsData"/>
<GraphSelectedNodeDetails <GraphSelectedNodeDetails
:selected-node-details-data=" :selected-node-details-data="pageModel.selectedNodeDetailsData"
pageModel.selectedNodeDetailsData" @[CUSTOM_EVENTS.DETAILS_CHECK_NODE]="filterAddOrCheckNode"
@[CUSTOM_EVENTS.ADD_TO_FILTER_CLICKED]="addNodeToFilter" @[CUSTOM_EVENTS.DETAILS_UNCHECK_NODE]="filterUncheckNode"/>
@[CUSTOM_EVENTS.REMOVE_FROM_FILTER_CLICKED]="removeNodeFromFilter"/>
<PackageDetailsPanel <PackageDetailsPanel
:selected-package=" :selected-package="pageModel.selectedNodeDetailsData.selectedNode"/>
pageModel.selectedNodeDetailsData.selectedNode"/>
</div> </div>
</div> </div>
</div> </div>
...@@ -119,15 +119,15 @@ const PackageGraphPage = { ...@@ -119,15 +119,15 @@ const PackageGraphPage = {
const pageUrlProcessor = new UrlProcessor(pageUrl.searchParams); const pageUrlProcessor = new UrlProcessor(pageUrl.searchParams);
this.displaySettingsData.readUrlProcessor(pageUrlProcessor); this.displaySettingsData.readUrlProcessor(pageUrlProcessor);
if (this.displaySettingsData.nodeFilterData.nodeList.length === 0) { if (this.displaySettingsData.nodeFilterData.filterList.length === 0) {
// TODO(yjlong): This is test data. Remove this when no longer needed. // TODO(yjlong): This is test data. Remove this when no longer needed.
this.addNodesToFilter([ [
'org.chromium.base', 'org.chromium.base',
'org.chromium.chrome.browser.gsa', 'org.chromium.chrome.browser.gsa',
'org.chromium.chrome.browser.omaha', 'org.chromium.chrome.browser.omaha',
'org.chromium.chrome.browser.media', 'org.chromium.chrome.browser.media',
'org.chromium.ui.base', 'org.chromium.ui.base',
]); ].forEach(nodeName => this.filterAddOrCheckNode(nodeName));
} }
}, },
methods: { methods: {
...@@ -138,25 +138,22 @@ const PackageGraphPage = { ...@@ -138,25 +138,22 @@ const PackageGraphPage = {
const pageUrl = urlProcessor.getUrl(document.URL, PagePathName.PACKAGE); const pageUrl = urlProcessor.getUrl(document.URL, PagePathName.PACKAGE);
history.replaceState(null, '', pageUrl); history.replaceState(null, '', pageUrl);
}, },
/** filterRemoveNode: function(nodeName) {
* @param {string} nodeName The node to add. this.displaySettingsData.nodeFilterData.removeNode(nodeName);
*/
addNodeToFilter: function(nodeName) {
this.displaySettingsData.nodeFilterData.addNode(nodeName);
}, },
/** filterAddOrCheckNode: function(nodeName) {
* @param {!Array<string>} nodeNames The nodes to add. this.displaySettingsData.nodeFilterData.addOrFindNode(
*/ nodeName).checked = true;
addNodesToFilter: function(nodeNames) {
for (const nodeName of nodeNames) {
this.displaySettingsData.nodeFilterData.addNode(nodeName);
}
}, },
/** filterUncheckNode: function(nodeName) {
* @param {string} nodeName The node to remove. this.displaySettingsData.nodeFilterData.addOrFindNode(
*/ nodeName).checked = false;
removeNodeFromFilter: function(nodeName) { },
this.displaySettingsData.nodeFilterData.removeNode(nodeName); filterCheckAll: function() {
this.displaySettingsData.nodeFilterData.checkAll();
},
filterUncheckAll: function() {
this.displaySettingsData.nodeFilterData.uncheckAll();
}, },
/** /**
* @param {number} depth The new inbound depth. * @param {number} depth The new inbound depth.
......
...@@ -5,11 +5,13 @@ ...@@ -5,11 +5,13 @@
// A collection of names for the custom events to be emitted by the page's // A collection of names for the custom events to be emitted by the page's
// components (in vue_components/). // components (in vue_components/).
const CUSTOM_EVENTS = { const CUSTOM_EVENTS = {
ADD_TO_FILTER_CLICKED: 'add-to-filter-clicked', DETAILS_CHECK_NODE: 'details-check-node',
FILTER_ELEMENT_CLICKED: 'filter-element-clicked', DETAILS_UNCHECK_NODE: 'details-uncheck-node',
FILTER_REMOVE: 'filter-remove',
FILTER_CHECK_ALL: 'filter-check-all',
FILTER_UNCHECK_ALL: 'filter-uncheck-all',
FILTER_SUBMITTED: 'filter-submitted', FILTER_SUBMITTED: 'filter-submitted',
NODE_CLICKED: 'node-clicked', NODE_CLICKED: 'node-clicked',
REMOVE_FROM_FILTER_CLICKED: 'remove-from-filter-clicked',
}; };
export { export {
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment