@truedat/core 5.18.1 → 5.18.3

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@truedat/core",
3
- "version": "5.18.1",
3
+ "version": "5.18.3",
4
4
  "description": "Truedat Web Core",
5
5
  "sideEffects": false,
6
6
  "jsnext:main": "src/index.js",
@@ -117,5 +117,5 @@
117
117
  "react-dom": ">= 16.8.6 < 17",
118
118
  "semantic-ui-react": ">= 2.0.3 < 2.2"
119
119
  },
120
- "gitHead": "95d5a42f5bd259ad53fa66d4fe8cce48be73ee03"
120
+ "gitHead": "ba00f358149f00dcdabb3e3edd883c54c0bbd43a"
121
121
  }
@@ -60,6 +60,7 @@ export const ConfirmModal = ({
60
60
  <Modal.Content>{content}</Modal.Content>
61
61
  )
62
62
  }
63
+ role="dialog"
63
64
  />
64
65
  );
65
66
 
@@ -1,9 +1,16 @@
1
1
  import _ from "lodash/fp";
2
- import React from "react";
2
+ import React, { useState, useEffect } from "react";
3
3
  import PropTypes from "prop-types";
4
4
  import { FormattedMessage } from "react-intl";
5
- import { Label, Icon, Dropdown, Dimmer, Loader } from "semantic-ui-react";
6
- import { accentInsensitivePathOrder } from "../services/sort";
5
+ import {
6
+ Label,
7
+ Icon,
8
+ Dropdown,
9
+ Dimmer,
10
+ Loader,
11
+ Input,
12
+ } from "semantic-ui-react";
13
+ import { accentInsensitivePathOrder, lowerDeburr } from "../services/sort";
7
14
  import FilterItem from "./FilterItem";
8
15
 
9
16
  const removePrefix = _.replace(/^.*\./, "");
@@ -17,55 +24,128 @@ export const FilterDropdown = ({
17
24
  options,
18
25
  removeFilter,
19
26
  toggleFilterValue,
20
- }) => (
21
- <Dropdown
22
- item
23
- floating
24
- scrolling
25
- icon={false}
26
- upward={false}
27
- trigger={
28
- <Label key={filter}>
29
- <FormattedMessage
30
- id={`filters.${filter}`}
31
- defaultMessage={removePrefix(filter)}
32
- />
33
- <Icon
34
- name="delete"
35
- onClick={(e) => {
36
- e.preventDefault();
37
- e.stopPropagation();
38
- removeFilter({ filter });
39
- }}
40
- />
41
- </Label>
42
- }
43
- onOpen={() => openFilter({ filter })}
44
- onClose={() => closeFilter({ filter })}
45
- open={!_.isEmpty(options)}
46
- >
47
- <Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
48
- {options &&
49
- _.flow(
50
- _.sortBy(accentInsensitivePathOrder("text")),
51
- _.map.convert({ cap: false })((option, i) => (
52
- <FilterItem
53
- key={i}
54
- filter={filter}
55
- option={option}
56
- toggleFilterValue={toggleFilterValue}
57
- active={_.includes(_.prop("value")(option))(activeValues)}
58
- />
59
- ))
60
- )(options)}
61
- {loading && (
62
- <Dimmer active inverted>
63
- <Loader size="tiny" />
64
- </Dimmer>
65
- )}
66
- </Dimmer.Dimmable>
67
- </Dropdown>
68
- );
27
+ }) => {
28
+ const [selected, setSelected] = useState();
29
+ const [query, setQuery] = useState();
30
+
31
+ useEffect(() => {
32
+ const activeOptions = _.filter((option) =>
33
+ _.includes(option.value)(activeValues)
34
+ )(options);
35
+
36
+ _.flow(
37
+ _.reduce((acc, option) => [...acc, option], []),
38
+ _.uniq,
39
+ setSelected
40
+ )(activeOptions);
41
+ }, [activeValues, options]);
42
+
43
+ const handleSearch = (e, { value }) => {
44
+ e.preventDefault();
45
+ setQuery(lowerDeburr(value));
46
+ };
47
+
48
+ const toogleSelectedOption = (selectedOption) => {
49
+ const newSelectedOptions = selected.find(
50
+ (s) => s.value === selectedOption.value
51
+ )
52
+ ? selected.filter((s) => s.value !== selectedOption.value)
53
+ : [...selected, selectedOption];
54
+ setSelected(newSelectedOptions);
55
+ toggleFilterValue({ filter, value: selectedOption.value });
56
+ };
57
+
58
+ const match = (name, query) => _.contains(query)(lowerDeburr(name));
59
+ const filterSearch = (all) =>
60
+ query ? _.filter((option) => match(option.text, query))(all) : all;
61
+
62
+ const filteredOptions = filterSearch(options);
63
+
64
+ return (
65
+ <Dropdown
66
+ item
67
+ floating
68
+ scrolling
69
+ icon={false}
70
+ upward={false}
71
+ trigger={
72
+ <Label key={filter}>
73
+ <FormattedMessage
74
+ id={`filters.${filter}`}
75
+ defaultMessage={removePrefix(filter)}
76
+ />
77
+ <Icon
78
+ name="delete"
79
+ onClick={(e) => {
80
+ e.preventDefault();
81
+ e.stopPropagation();
82
+ removeFilter({ filter });
83
+ }}
84
+ />
85
+ </Label>
86
+ }
87
+ onOpen={() => openFilter({ filter })}
88
+ onClose={() => closeFilter({ filter })}
89
+ open={!_.isEmpty(options)}
90
+ >
91
+ <Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
92
+ <>
93
+ {options && options.length >= 8 && (
94
+ <>
95
+ <Input
96
+ icon="search"
97
+ iconPosition="left"
98
+ className="search"
99
+ onKeyDown={(e) => {
100
+ if (e.key === " ") {
101
+ e.stopPropagation();
102
+ }
103
+ }}
104
+ onChange={handleSearch}
105
+ onClick={(e) => {
106
+ e.preventDefault();
107
+ e.stopPropagation();
108
+ }}
109
+ />
110
+ {selected &&
111
+ _.flow(
112
+ _.sortBy(accentInsensitivePathOrder("text")),
113
+ _.map.convert({ cap: false })((option, i) => (
114
+ <FilterItem
115
+ key={i}
116
+ filter={filter}
117
+ option={option}
118
+ toggleFilterValue={toogleSelectedOption}
119
+ active={_.includes(_.prop("value")(option))(activeValues)}
120
+ />
121
+ ))
122
+ )(selected)}
123
+ <Dropdown.Divider />
124
+ </>
125
+ )}
126
+ {filteredOptions &&
127
+ _.flow(
128
+ _.sortBy(accentInsensitivePathOrder("text")),
129
+ _.map.convert({ cap: false })((option, i) => (
130
+ <FilterItem
131
+ key={i}
132
+ filter={filter}
133
+ option={option}
134
+ toggleFilterValue={toggleFilterValue}
135
+ active={_.includes(_.prop("value")(option))(activeValues)}
136
+ />
137
+ ))
138
+ )(filteredOptions)}
139
+ </>
140
+ {loading && (
141
+ <Dimmer active inverted>
142
+ <Loader size="tiny" />
143
+ </Dimmer>
144
+ )}
145
+ </Dimmer.Dimmable>
146
+ </Dropdown>
147
+ );
148
+ };
69
149
 
70
150
  FilterDropdown.propTypes = {
71
151
  activeValues: PropTypes.array,
@@ -14,6 +14,7 @@ import { lowerDeburr } from "../services/sort";
14
14
  import DropdownMenuItem from "./DropdownMenuItem";
15
15
 
16
16
  export const FilterMultilevelDropdown = ({
17
+ name,
17
18
  activeValues,
18
19
  closeFilter,
19
20
  filter,
@@ -130,6 +131,7 @@ export const FilterMultilevelDropdown = ({
130
131
 
131
132
  return (
132
133
  <Dropdown
134
+ name={name || "filterMultilevelDropdown"}
133
135
  item
134
136
  floating
135
137
  icon={false}
@@ -193,6 +195,7 @@ export const FilterMultilevelDropdown = ({
193
195
  };
194
196
 
195
197
  FilterMultilevelDropdown.propTypes = {
198
+ name: PropTypes.string,
196
199
  activeValues: PropTypes.array,
197
200
  closeFilter: PropTypes.func,
198
201
  filter: PropTypes.string,
@@ -72,6 +72,7 @@ const PopulatedHierarchyFilterDropdown = (props) => {
72
72
 
73
73
  return (
74
74
  <FilterMultilevelDropdown
75
+ name="hierarchyFilterDropdown"
75
76
  activeValues={idActiveValues}
76
77
  closeFilter={closeFilter}
77
78
  filter={filter}
@@ -97,7 +98,7 @@ PopulatedHierarchyFilterDropdown.propTypes = {
97
98
 
98
99
  const HierarchyFilterDropdown = (props) =>
99
100
  _.isEmpty(props.options) ? (
100
- <FilterMultilevelDropdown {...props} />
101
+ <FilterMultilevelDropdown {...props} name="hierarchyFilterDropdown" />
101
102
  ) : (
102
103
  <PopulatedHierarchyFilterDropdown {...props} />
103
104
  );
@@ -0,0 +1,155 @@
1
+ import React, { useState } from "react";
2
+ import PropTypes from "prop-types";
3
+
4
+ import _ from "lodash/fp";
5
+ import { useIntl } from "react-intl";
6
+
7
+ import {
8
+ Label,
9
+ Icon,
10
+ Dropdown,
11
+ Dimmer,
12
+ Loader,
13
+ Input,
14
+ } from "semantic-ui-react";
15
+ import { accentInsensitivePathOrder } from "../services/sort";
16
+
17
+ const removePrefix = _.replace(/^.*\./, "");
18
+ const SPACE_CODE = 32;
19
+
20
+ export const SearchFilterDropdown = ({
21
+ query,
22
+ searchCallback,
23
+ placeholder,
24
+ options,
25
+ open,
26
+ loading,
27
+ activeValues,
28
+ closeFilter,
29
+ filter,
30
+ FilterDataLoader,
31
+ loaderProps,
32
+ FilterItem,
33
+ openFilter,
34
+ removeFilter,
35
+ toggleFilterValue,
36
+ searchFilterDispacher,
37
+ }) => {
38
+ const [selectedOptions, setSelectedOptions] = useState([]);
39
+ const { formatMessage } = useIntl();
40
+
41
+ const toogleSelectedOption = (selectedOption) => {
42
+ const newSelectedOptions = selectedOptions.find(
43
+ (s) => s.id === selectedOption.id
44
+ )
45
+ ? selectedOptions.filter((s) => s.id !== selectedOption.id)
46
+ : [...selectedOptions, selectedOption];
47
+ setSelectedOptions(newSelectedOptions);
48
+ toggleFilterValue({ filter, value: selectedOption.id });
49
+ };
50
+
51
+ return (
52
+ <Dropdown
53
+ name="searchFilterDropdown"
54
+ item
55
+ floating
56
+ scrolling
57
+ icon={false}
58
+ upward={false}
59
+ trigger={
60
+ <Label key={filter}>
61
+ {formatMessage({
62
+ id: `filters.${filter}`,
63
+ defaultMessage: removePrefix(filter),
64
+ })}
65
+ <Icon
66
+ name="delete"
67
+ onClick={(e) => {
68
+ e.preventDefault();
69
+ e.stopPropagation();
70
+ removeFilter({ filter });
71
+ }}
72
+ />
73
+ </Label>
74
+ }
75
+ onOpen={() => openFilter({ filter })}
76
+ onClose={(e) => {
77
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/pull/3766
78
+ if (e?.type === "keydown" && e?.keyCode === SPACE_CODE) return;
79
+ closeFilter({ filter });
80
+ }}
81
+ open={open}
82
+ >
83
+ <Dimmer.Dimmable as={Dropdown.Menu} dimmed={loading}>
84
+ <FilterDataLoader {...loaderProps} />
85
+ <Input
86
+ icon="search"
87
+ iconPosition="left"
88
+ placeholder={placeholder}
89
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/issues/1593
90
+ onClick={(e) => e.stopPropagation()}
91
+ value={query}
92
+ onChange={(_e, data) =>
93
+ searchFilterDispacher(searchCallback({ query: data.value }))
94
+ }
95
+ // Next line Fix bug: https://github.com/Semantic-Org/Semantic-UI-React/issues/4374
96
+ onKeyDown={(e) => e.keyCode === SPACE_CODE && e.stopPropagation()}
97
+ />
98
+ {selectedOptions &&
99
+ _.flow(
100
+ _.sortBy(accentInsensitivePathOrder("text")),
101
+ _.map.convert({ cap: false })((option, i) => (
102
+ <FilterItem
103
+ key={i}
104
+ filter={filter}
105
+ option={option}
106
+ toggleFilterValue={toogleSelectedOption}
107
+ active={_.includes(_.prop("id")(option))(activeValues)}
108
+ />
109
+ ))
110
+ )(selectedOptions)}
111
+ <Dropdown.Divider />
112
+ {options &&
113
+ _.flow(
114
+ _.sortBy(accentInsensitivePathOrder("text")),
115
+ _.filter((o) => !_.includes(_.prop("id")(o))(activeValues)),
116
+ _.map.convert({ cap: false })((option, i) => (
117
+ <FilterItem
118
+ key={i}
119
+ filter={filter}
120
+ option={option}
121
+ toggleFilterValue={toogleSelectedOption}
122
+ active={_.includes(_.prop("id")(option))(activeValues)}
123
+ />
124
+ ))
125
+ )(options)}
126
+ {loading && (
127
+ <Dimmer active inverted>
128
+ <Loader size="tiny" />
129
+ </Dimmer>
130
+ )}
131
+ </Dimmer.Dimmable>
132
+ </Dropdown>
133
+ );
134
+ };
135
+
136
+ SearchFilterDropdown.propTypes = {
137
+ query: PropTypes.string,
138
+ searchCallback: PropTypes.func,
139
+ placeholder: PropTypes.string,
140
+ options: PropTypes.object,
141
+ open: PropTypes.bool,
142
+ loading: PropTypes.bool,
143
+ activeValues: PropTypes.array,
144
+ closeFilter: PropTypes.func,
145
+ filter: PropTypes.string,
146
+ FilterDataLoader: PropTypes.node,
147
+ loaderProps: PropTypes.object,
148
+ FilterItem: PropTypes.object,
149
+ openFilter: PropTypes.func,
150
+ removeFilter: PropTypes.func,
151
+ toggleFilterValue: PropTypes.func,
152
+ searchFilterDispacher: PropTypes.func,
153
+ };
154
+
155
+ export default SearchFilterDropdown;
@@ -5,6 +5,7 @@ import { FormattedMessage } from "react-intl";
5
5
  import FilterDropdown from "./FilterDropdown";
6
6
  import FilterMultilevelDropdown from "./FilterMultilevelDropdown";
7
7
  import HierarchyFilterDropdown from "./HierarchyFilterDropdown";
8
+ import SearchFilterDropdown from "./SearchFilterDropdown";
8
9
  import ModalSaveFilter from "./ModalSaveFilter";
9
10
  import UserFilters from "./UserFilters";
10
11
 
@@ -27,6 +28,8 @@ export const SelectedFilters = ({
27
28
  toggleFilterValue,
28
29
  userFilters,
29
30
  userFilterScope,
31
+ searchFiltersPropsMapping,
32
+ searchFilterDispacher,
30
33
  }) => (
31
34
  <>
32
35
  {_.isEmpty(userFilters) ? null : (
@@ -47,10 +50,10 @@ export const SelectedFilters = ({
47
50
  <FormattedMessage id="search.applied_filters" />
48
51
  </div>
49
52
  {selectedFilters.map((filter) => {
53
+ const isSelectedFilter = _.isEqual(filter, selectedFilter);
54
+
50
55
  const filterType = _.prop(filter)(filterTypes);
51
- const options = _.isEqual(filter, selectedFilter)
52
- ? selectedFilterValues
53
- : null;
56
+ const options = isSelectedFilter ? selectedFilterValues : null;
54
57
 
55
58
  const props = {
56
59
  key: filter,
@@ -63,13 +66,24 @@ export const SelectedFilters = ({
63
66
  removeFilter,
64
67
  toggleFilterValue,
65
68
  };
66
- return filterType === "domain" ? (
67
- <FilterMultilevelDropdown {...props} />
68
- ) : filterType === "hierarchy" ? (
69
- <HierarchyFilterDropdown {...props} />
70
- ) : (
71
- <FilterDropdown {...props} />
72
- );
69
+
70
+ switch (filterType) {
71
+ case "domain":
72
+ return <FilterMultilevelDropdown {...props} />;
73
+ case "hierarchy":
74
+ return <HierarchyFilterDropdown {...props} />;
75
+ case "search":
76
+ return (
77
+ <SearchFilterDropdown
78
+ {...props}
79
+ {...searchFiltersPropsMapping[filter]}
80
+ open={isSelectedFilter}
81
+ searchFilterDispacher={searchFilterDispacher}
82
+ />
83
+ );
84
+ default:
85
+ return <FilterDropdown {...props} />;
86
+ }
73
87
  })}
74
88
  <a className="resetFilters" onClick={() => resetFilters()}>
75
89
  <FormattedMessage id="search.clear_filters" />
@@ -106,6 +120,8 @@ SelectedFilters.propTypes = {
106
120
  toggleFilterValue: PropTypes.func,
107
121
  userFilters: PropTypes.array,
108
122
  userFilterScope: PropTypes.string,
123
+ searchFiltersPropsMapping: PropTypes.object,
124
+ searchFilterDispacher: PropTypes.func,
109
125
  };
110
126
 
111
127
  export default SelectedFilters;
@@ -0,0 +1,33 @@
1
+ import _ from "lodash/fp";
2
+ import React from "react";
3
+ import PropTypes from "prop-types";
4
+ import { Icon, Dropdown, Segment } from "semantic-ui-react";
5
+
6
+ const preventDefault = (e, callback) => {
7
+ e && e.preventDefault();
8
+ e && e.stopPropagation();
9
+ callback();
10
+ };
11
+
12
+ export const StructureFilterItem = ({ active, toggleFilterValue, option }) => (
13
+ <Dropdown.Item
14
+ onClick={(e) => preventDefault(e, () => toggleFilterValue(option))}
15
+ active={active}
16
+ >
17
+ <Segment vertical>
18
+ <Icon name={active ? "check square outline" : "square outline"} />
19
+ {_.trim(option.name)}
20
+ <small>{` - (${option.type})`}</small>
21
+ <br />
22
+ <small>{option.path.join(" > ")}</small>
23
+ </Segment>
24
+ </Dropdown.Item>
25
+ );
26
+
27
+ StructureFilterItem.propTypes = {
28
+ active: PropTypes.bool,
29
+ toggleFilterValue: PropTypes.func,
30
+ option: PropTypes.object,
31
+ };
32
+
33
+ export default StructureFilterItem;