Skip to content

Commit 30f0540

Browse files
authored
Merge pull request #105 from netcreateorg/dev-bl/filter-and-or
Feature: Add filters using `&&` or `||` in strings.
2 parents 578f7af + 399a101 commit 30f0540

File tree

10 files changed

+132
-79
lines changed

10 files changed

+132
-79
lines changed

app/view/netcreate/NetCreate.jsx

Lines changed: 32 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ class NetCreate extends UNISYS.Component {
197197
style={{
198198
display: 'flex',
199199
flexFlow: 'row nowrap',
200+
backgroundColor: '#EEE',
200201
width: '100%',
201202
height: '100%',
202203
overflow: 'hidden',
@@ -243,55 +244,46 @@ class NetCreate extends UNISYS.Component {
243244
<NCGraph />
244245
</div>
245246
{/*** RIGHT VIEW COLUMN ***************/}
246-
{layoutFiltersOpen ? (
247-
// OPEN
248-
<div
249-
className="--NetCreate_Column_Filters_Open"
250-
id="right"
251-
style={{
252-
marginTop: '38px',
253-
padding: '0 5px',
254-
backgroundColor: '#6c757d',
255-
borderTopLeftRadius: '10px',
256-
paddingBottom: '25px' // avoid footer
257-
}}
258-
>
259-
<div
260-
style={{
261-
display: 'flex',
262-
flexDirection: 'column',
263-
alignItems: 'end',
264-
height: '100%',
265-
overflow: 'hidden'
266-
}}
267-
>
268-
<Button onClick={this.onFilterBtnClick} style={{ width: '90px' }}>
269-
{FILTER.PANEL_LABEL} &gt;
270-
</Button>
271-
<FiltersPanel />
272-
</div>
273-
</div>
274-
) : (
275-
// CLOSED
247+
<div
248+
className="--NetCreate_Column_Filters_Open"
249+
id="right"
250+
style={{
251+
marginTop: '38px',
252+
padding: '0 5px',
253+
backgroundColor: '#6c757d',
254+
borderTopLeftRadius: layoutFiltersOpen ? '10px' : '0',
255+
paddingBottom: '25px' // avoid footer
256+
}}
257+
>
276258
<div
277-
className="--NetCreate_Column_Filters_Closed"
278-
id="right"
279259
style={{
280-
marginTop: '38px',
281-
paddingTop: '0px',
282-
backgroundColor: '#6c757d',
283-
width: '10px',
284-
height: '100%'
260+
display: 'flex',
261+
flexDirection: 'column',
262+
alignItems: 'end',
263+
width: layoutFiltersOpen ? '100%' : '0',
264+
height: layoutFiltersOpen ? '100%' : 'inherit',
265+
overflow: 'hidden'
285266
}}
286267
>
287268
<Button
288269
onClick={this.onFilterBtnClick}
289-
style={{ width: '90px', float: 'right' }}
270+
style={{
271+
width: '90px',
272+
borderTopLeftRadius: '10px',
273+
paddingBottom: '10px',
274+
backgroundColor: '#6c757d',
275+
border: 'none',
276+
boxShadow: 'none',
277+
position: layoutFiltersOpen ? 'inherit' : 'absolute'
278+
}}
290279
>
291-
&lt; {FILTER.PANEL_LABEL}
280+
{!layoutFiltersOpen && `< `}
281+
{FILTER.PANEL_LABEL}
282+
{layoutFiltersOpen && ` >`}
292283
</Button>
284+
<FiltersPanel hidden={!layoutFiltersOpen} />
293285
</div>
294-
)}
286+
</div>
295287
</div>
296288
<div
297289
className="--NetCreate_Column_Break_Info"

app/view/netcreate/components/NCAutoSuggest.jsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -181,7 +181,7 @@ class NCAutoSuggest extends UNISYS.Component {
181181
))
182182
: undefined;
183183
return (
184-
<div style={{ position: 'relative' }}>
184+
<div style={{ position: 'relative', flexGrow: '1' }}>
185185
<div className="helptop">Click on a node, or type a node name</div>
186186
<input
187187
id={statekey}

app/view/netcreate/components/NCSearch.css

Lines changed: 2 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,6 @@
11
.ncsearch {
2-
display: grid;
3-
grid-template-columns: 1fr;
4-
grid-auto-columns: 100px;
5-
grid-auto-flow: column;
6-
column-gap: 10px;
2+
display: flex;
3+
flex-wrap: wrap;
74
margin-top: 38px; /* match navbar height */
85
margin-bottom: 20px;
96
}

app/view/netcreate/components/filter/FilterEnums.js

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -45,6 +45,7 @@ FILTER.TYPES.NUMBER = 'number';
4545
FILTER.TYPES.SELECT = 'select';
4646
FILTER.TYPES.NODE = 'node'; // edge source / target
4747
FILTER.TYPES.DATE = 'date';
48+
FILTER.TYPES.MARKDOWN = 'markdown';
4849
FILTER.TYPES.HIDDEN = 'hidden';
4950
// Special Edge Keys mapped to node objects
5051
// Used by m_IsEdgeMatchedByFilter to find node labels
@@ -59,12 +60,12 @@ FILTER.OPERATORS.CONTAINS = { key: 'CONTAINS', label: 'contains' };
5960
FILTER.OPERATORS.NOT_CONTAINS = { key: 'NOT_CONTAINS', label: 'does not contain' };
6061
FILTER.OPERATORS.IS_EMPTY = { key: 'IS_EMPTY', label: 'is empty' };
6162
FILTER.OPERATORS.IS_NOT_EMPTY = { key: 'IS_NOT_EMPTY', label: 'is not empty' };
63+
FILTER.OPERATORS.EQ = { key: 'EQ', label: '=' };
64+
FILTER.OPERATORS.NOT_EQ = { key: 'NOT_EQ', label: `\u2260` };
6265
FILTER.OPERATORS.GT = { key: 'GT', label: '>' };
6366
FILTER.OPERATORS.GT_EQ = { key: 'GT_EQ', label: '>=' };
6467
FILTER.OPERATORS.LT = { key: 'LT', label: '<' };
6568
FILTER.OPERATORS.LT_EQ = { key: 'LT_EQ', label: '<=' };
66-
FILTER.OPERATORS.EQ = { key: 'EQ', label: '=' };
67-
FILTER.OPERATORS.NOT_EQ = { key: 'NOT_EQ', label: `\u2260` };
6869

6970
/// MODULE EXPORTS ////////////////////////////////////////////////////////////
7071
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

app/view/netcreate/components/filter/FilterGroup.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,8 +37,9 @@ function FilterGroup({ group, label, filters, filterAction, transparency }) {
3737
</div>
3838
{filters.map(filter => {
3939
switch (filter.type) {
40-
case FILTER.TYPES.STRING:
40+
case FILTER.TYPES.MARKDOWN:
4141
case FILTER.TYPES.NODE:
42+
case FILTER.TYPES.STRING:
4243
return (
4344
<StringFilter
4445
key={filter.id}

app/view/netcreate/components/filter/FiltersPanel.jsx

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,7 @@ class FiltersPanel extends UNISYS.Component {
109109
}
110110

111111
render() {
112+
const { hidden } = this.props;
112113
const { filterAction, focusRange, focusSourceLabel, statsSummary } = this.state;
113114
const defs = [this.state.nodes, this.state.edges];
114115

@@ -148,10 +149,10 @@ class FiltersPanel extends UNISYS.Component {
148149
<div
149150
className="filterPanel"
150151
style={{
152+
display: hidden ? 'none' : 'flex',
151153
overflow: 'hidden',
152154
margin: '6px 0',
153155
padding: '5px',
154-
display: 'flex',
155156
flexDirection: 'column',
156157
backgroundColor: '#EEE',
157158
zIndex: '2000'

app/view/netcreate/components/filter/NumberFilter.js

Lines changed: 35 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -39,6 +39,10 @@
3939
The `id` variable allows us to potentially support multiple search filters
4040
using the same key, e.g. we could have two 'Label' filters.
4141
42+
In order to retain the input selection cursor between state updates, we use
43+
a secondary state `inputval` that retains the cursor position.
44+
45+
4246
\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/
4347

4448
const FILTER = require('./FilterEnums');
@@ -53,12 +57,12 @@ var UDATA = null;
5357
/// - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
5458
const OPERATORS = [
5559
FILTER.OPERATORS.NO_OP,
56-
FILTER.OPERATORS.GT,
57-
FILTER.OPERATORS.GT_EQ,
60+
FILTER.OPERATORS.EQ,
61+
FILTER.OPERATORS.NOT_EQ,
5862
FILTER.OPERATORS.LT,
5963
FILTER.OPERATORS.LT_EQ,
60-
FILTER.OPERATORS.EQ,
61-
FILTER.OPERATORS.NOT_EQ
64+
FILTER.OPERATORS.GT,
65+
FILTER.OPERATORS.GT_EQ
6266
];
6367

6468
/// CLASS DECLARATION /////////////////////////////////////////////////////////
@@ -70,34 +74,49 @@ class NumberFilter extends React.Component {
7074
onChangeHandler
7175
}) {
7276
super();
77+
this.m_ClearFilters = this.m_ClearFilters.bind(this);
7378
this.OnChangeOperator = this.OnChangeOperator.bind(this);
7479
this.OnChangeValue = this.OnChangeValue.bind(this);
7580
this.TriggerChangeHandler = this.TriggerChangeHandler.bind(this);
7681
this.OnSubmit = this.OnSubmit.bind(this);
7782

7883
this.state = {
7984
operator: FILTER.OPERATORS.NO_OP, // Used locally to define result
80-
value: '' // Used locally to define result
85+
inputval: '', // Used to maintain input caret position
86+
value: '' // Used to define the final result
8187
};
8288

8389
/// Initialize UNISYS DATA LINK for REACT
8490
UDATA = UNISYS.NewDataLink(this);
91+
UDATA.HandleMessage('FILTER_CLEAR', this.m_ClearFilters);
92+
}
93+
94+
componentWillUnmount() {
95+
UDATA.UnhandleMessage('FILTER_CLEAR', this.m_ClearFilters);
96+
}
97+
98+
m_ClearFilters() {
99+
this.setState({ inputval: '' });
85100
}
86101

87102
OnChangeOperator(e) {
88103
const newstate = { operator: e.target.value };
89104
// clear value if NO_OP
90-
if (e.target.value === FILTER.OPERATORS.NO_OP.key) newstate.value = '';
105+
if (e.target.value === FILTER.OPERATORS.NO_OP.key) {
106+
newstate.inputval = '';
107+
newstate.value = '';
108+
}
91109
this.setState(newstate, this.TriggerChangeHandler);
92110
}
93111

94-
OnChangeValue(e) {
95-
this.setState(
96-
{
97-
value: Number(e.target.value)
98-
},
99-
this.TriggerChangeHandler
100-
);
112+
OnChangeValue(event) {
113+
const value = Number(event.target.value);
114+
// First update the input field, retaining cursor position
115+
this.setState({ inputval: value }, () => {
116+
// Then send the result
117+
this.setState({ value }, this.TriggerChangeHandler);
118+
});
119+
101120
}
102121

103122
TriggerChangeHandler() {
@@ -126,6 +145,7 @@ class NumberFilter extends React.Component {
126145
}
127146

128147
render() {
148+
const { inputval } = this.state;
129149
const { filterAction } = this.props;
130150
const { id, key, keylabel, operator, value } = this.props.filter;
131151
return (
@@ -154,8 +174,8 @@ class NumberFilter extends React.Component {
154174
))}
155175
</Input>
156176
<Input
157-
type="text"
158-
value={value}
177+
type="number"
178+
value={inputval}
159179
placeholder="..."
160180
style={{ maxWidth: '12em', height: '1.5em', padding: '0' }}
161181
onChange={this.OnChangeValue}

app/view/netcreate/components/filter/README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -35,6 +35,7 @@ The available types are:
3535
* "number"
3636
* "select" -- Drop down menu
3737
* "node" -- Special type for edge "source" and "target" setting.
38+
* "markdown" -- Special string subtype that renders markdown as "string" in view mode
3839

3940
Each type has specific operations associated with it, e.g. "string" supports "contains" and "not contains", whereas "number" supports ">" and "!=" etc.
4041

app/view/netcreate/components/filter/StringFilter.jsx

Lines changed: 29 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,9 @@
3434
The `id` variable allows us to potentially support multiple search filters
3535
using the same key, e.g. we could have two 'Label' filters.
3636
37+
In order to retain the input selection cursor between state updates, we use
38+
a secondary state `inputval` that retains the cursor position.
39+
3740
\*\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ * //////////////////////////////////////*/
3841

3942
const FILTER = require('./FilterEnums');
@@ -63,34 +66,48 @@ class StringFilter extends React.Component {
6366
onChangeHandler
6467
}) {
6568
super();
69+
this.m_ClearFilters = this.m_ClearFilters.bind(this);
6670
this.OnChangeOperator = this.OnChangeOperator.bind(this);
6771
this.OnChangeValue = this.OnChangeValue.bind(this);
6872
this.TriggerChangeHandler = this.TriggerChangeHandler.bind(this);
6973
this.OnSubmit = this.OnSubmit.bind(this);
7074

7175
this.state = {
7276
operator: FILTER.OPERATORS.NO_OP, // Used locally to define result
73-
value: '' // Used locally to define result
77+
inputval: '', // Used to maintain input caret position
78+
value: '' // Used to define the final result
7479
};
7580

7681
/// Initialize UNISYS DATA LINK for REACT
7782
UDATA = UNISYS.NewDataLink(this);
83+
UDATA.HandleMessage('FILTER_CLEAR', this.m_ClearFilters);
84+
}
85+
86+
componentWillUnmount() {
87+
UDATA.UnhandleMessage('FILTER_CLEAR', this.m_ClearFilters);
88+
}
89+
90+
m_ClearFilters() {
91+
this.setState({ inputval: '' });
7892
}
7993

8094
OnChangeOperator(e) {
8195
const newstate = { operator: e.target.value };
8296
// clear value if NO_OP
83-
if (e.target.value === FILTER.OPERATORS.NO_OP.key) newstate.value = '';
97+
if (e.target.value === FILTER.OPERATORS.NO_OP.key) {
98+
newstate.inputval = '';
99+
newstate.value = '';
100+
}
84101
this.setState(newstate, this.TriggerChangeHandler);
85102
}
86103

87-
OnChangeValue(e) {
88-
this.setState(
89-
{
90-
value: e.target.value
91-
},
92-
this.TriggerChangeHandler
93-
);
104+
OnChangeValue(event) {
105+
const value = event.target.value;
106+
// First update the input field, retaining cursor position
107+
this.setState({ inputval: value }, () => {
108+
// Then send the result
109+
this.setState({ value }, this.TriggerChangeHandler);
110+
});
94111
}
95112

96113
TriggerChangeHandler() {
@@ -119,8 +136,10 @@ class StringFilter extends React.Component {
119136
}
120137

121138
render() {
139+
const { inputval } = this.state;
122140
const { filterAction } = this.props;
123141
const { id, key, keylabel, operator, value } = this.props.filter;
142+
124143
return (
125144
<Form inline className="filter-item" key={id} onSubmit={this.OnSubmit}>
126145
{/* FormGroup needs to unset flexFlow or fields will overflow
@@ -145,7 +164,7 @@ class StringFilter extends React.Component {
145164
</Input>
146165
<Input
147166
type="text"
148-
value={value}
167+
value={inputval}
149168
placeholder="..."
150169
style={{ maxWidth: '12em', height: '1.5em' }}
151170
onChange={this.OnChangeValue}

0 commit comments

Comments
 (0)