draft-components 1.8.0 → 1.10.0

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.
@@ -14,14 +14,22 @@ const keyboardKeys = require('../../lib/keyboard-keys.cjs');
14
14
  const helpers = require('../../lib/helpers.cjs');
15
15
  const reactHelpers = require('../../lib/react-helpers.cjs');
16
16
 
17
- function FilteredSearch({ className, placeholder = 'Search and filter', applyButtonLabel = 'Apply', cancelButtonLabel = 'Cancel', clearButtonAccessibleName = 'Clear', removeFilterButtonAccessibleName = 'Remove filter', filtersConfig, filters, onChangeFilters, onMouseDown, ...props }) {
17
+ function FilteredSearch({ filters: appliedFilters, className, placeholder = 'Search and filter', applyButtonLabel = 'Apply', cancelButtonLabel = 'Cancel', clearButtonAccessibleName = 'Clear', removeFilterButtonAccessibleName = 'Remove filter', filtersConfig, onChangeFilters, onMouseDown, ...props }) {
18
18
  const containerRef = react.useRef(null);
19
+ const [newFilter, setNewFilter] = react.useState(null);
19
20
  const [query, setQuery] = react.useState('');
20
21
  const [hasFocus, setHasFocus] = react.useState(false);
21
22
  const [expanded, setExpanded] = react.useState(false);
22
23
  const [selectedId, setSelectedId] = react.useState('');
23
24
  const [activeField, setActiveField] = react.useState('');
24
25
  const { textBoxId, listBoxId, getOptionId } = useComboboxIds.useComboboxIds();
26
+ let filters;
27
+ if (newFilter) {
28
+ filters = [...appliedFilters, newFilter];
29
+ }
30
+ else {
31
+ filters = appliedFilters;
32
+ }
25
33
  const filtersConfigMap = react.useMemo(() => {
26
34
  const map = new Map();
27
35
  for (const config of filtersConfig) {
@@ -36,20 +44,16 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
36
44
  }
37
45
  return textBox;
38
46
  };
39
- const addFilter = (config) => {
40
- const filter = createFilter(config);
41
- const textBoxElement = getTextBoxElement();
42
- onChangeFilters([...filters, filter]);
43
- textBoxElement.blur();
44
- setQuery('');
45
- setActiveField(filter.field);
47
+ const addFilter = (filter) => {
48
+ const newFilters = [...appliedFilters, filter];
49
+ onChangeFilters(newFilters);
46
50
  };
47
- const changeFilter = (changedFilter) => {
48
- const newFilters = filters.map((filter) => (filter.field === changedFilter.field ? changedFilter : filter));
51
+ const updateFilter = (updatedFilter) => {
52
+ const newFilters = appliedFilters.map((filter) => (filter.field === updatedFilter.field ? updatedFilter : filter));
49
53
  onChangeFilters(newFilters);
50
54
  };
51
- const removeFilter = (filterToRemove) => {
52
- const newFilters = filters.filter((filter) => (filter.field !== filterToRemove.field));
55
+ const removeFilter = (removedFilter) => {
56
+ const newFilters = appliedFilters.filter((filter) => (filter.field !== removedFilter.field));
53
57
  onChangeFilters(newFilters);
54
58
  };
55
59
  const onFilterEditStarted = (filter) => {
@@ -58,12 +62,23 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
58
62
  const onFilterEditCanceled = (filter) => {
59
63
  setActiveField('');
60
64
  if (filter.isEmpty()) {
61
- removeFilter(filter);
65
+ if (newFilter && filter.field === newFilter.field) {
66
+ setNewFilter(null);
67
+ }
68
+ else {
69
+ removeFilter(filter);
70
+ }
62
71
  getTextBoxElement().focus();
63
72
  }
64
73
  };
65
74
  const onFilterChanged = (filter) => {
66
- changeFilter(filter);
75
+ if (newFilter && filter.field === newFilter.field) {
76
+ addFilter(filter);
77
+ setNewFilter(null);
78
+ }
79
+ else {
80
+ updateFilter(filter);
81
+ }
67
82
  setActiveField('');
68
83
  };
69
84
  const renderedFilters = [];
@@ -73,6 +88,14 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
73
88
  fieldsWithAppliedFilters.add(field);
74
89
  renderedFilters.push(jsxRuntime.jsx(filterItem.FilterItem, { filter: filter, isEditing: activeField === field, onEditStart: onFilterEditStarted, onEditCancel: onFilterEditCanceled, onChange: onFilterChanged, onRemove: removeFilter }, field));
75
90
  }
91
+ const onOptionSelected = (config) => {
92
+ const filter = createFilter(config);
93
+ const textBoxElement = getTextBoxElement();
94
+ setQuery('');
95
+ setNewFilter(filter);
96
+ setActiveField(filter.field);
97
+ textBoxElement.blur();
98
+ };
76
99
  const onOptionHovered = (event) => {
77
100
  const listItemElement = event.currentTarget;
78
101
  setSelectedId(listItemElement.id);
@@ -83,7 +106,7 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
83
106
  const listItemElement = event.currentTarget;
84
107
  const config = filtersConfigMap.get(listItemElement.id);
85
108
  if (config) {
86
- addFilter(config);
109
+ onOptionSelected(config);
87
110
  }
88
111
  };
89
112
  const renderedOptions = [];
@@ -127,7 +150,7 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
127
150
  else if (key === keyboardKeys.KeyboardKeys.Enter) {
128
151
  const config = filtersConfigMap.get(selectedId);
129
152
  if (config) {
130
- addFilter(config);
153
+ onOptionSelected(config);
131
154
  isHandled = true;
132
155
  }
133
156
  }
@@ -15,7 +15,7 @@ function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onR
15
15
  const translations = useTranslations.useTranslations();
16
16
  const [filterOperator, setFilterOperator] = react.useState(filter.operator);
17
17
  const [filterValue, setFilterValue] = react.useState(filter.value);
18
- const { label, values, operators, operatorSelectAccessibleName, valueFormatter: formatValue = defaultValueFormatter, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
18
+ const { label, values, operators, operatorSelectAccessibleName, valueFormatter: formatValue = defaultValueFormatter, valuesFormatter: formatValues = defaultValuesFormatter, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
19
19
  const cancelEdit = () => {
20
20
  onEditCancel(filter);
21
21
  };
@@ -44,23 +44,16 @@ function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onR
44
44
  }
45
45
  onChange(changedFilter);
46
46
  };
47
- const anchor = (jsxRuntime.jsxs(filterToken.FilterToken, { isHighlighted: isEditing, onClickLabel: startEdit, onClickCloseButton: onClickCloseButton, children: [label, filter.value.length > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["\u00A0", jsxRuntime.jsx("span", { children: formatOperator(filter.operator) }), "\u00A0", jsxRuntime.jsx("b", { children: formatFilterValue(filter.value) })] })) : null] }));
47
+ const anchor = (jsxRuntime.jsxs(filterToken.FilterToken, { isHighlighted: isEditing, onClickLabel: startEdit, onClickCloseButton: onClickCloseButton, children: [label, filter.value.length > 0 ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["\u00A0", jsxRuntime.jsx("span", { children: formatOperator(filter.operator) }), "\u00A0", jsxRuntime.jsx("b", { children: formatValues(filter.value) })] })) : null] }));
48
48
  return (jsxRuntime.jsx(popover.Popover, { className: "dc-filter-popover", anchor: anchor, anchorGap: 2, transitionDurationMs: 0, isOpen: isEditing, onClose: cancelEdit, children: jsxRuntime.jsxs("form", { className: "dc-filter-form", onSubmit: onSubmit, children: [jsxRuntime.jsx(filterOperatorSelect.FilterOperatorSelect, { className: "dc-filter-form__operator", accessibleName: operatorSelectAccessibleName, label: label, values: operators, value: filterOperator, onChange: setFilterOperator, formatValue: formatOperator }), jsxRuntime.jsx(filterValueList.FilterValueList, { className: "dc-filter-form__value-list", values: values, checkedValues: filterValue, onChangeCheckedValues: setFilterValue, formatValue: formatValue }), jsxRuntime.jsxs("div", { className: "dc-filter-form__buttons", children: [jsxRuntime.jsx(button.Button, { variant: "plain", type: "button", onClick: cancelEdit, children: translations.cancelButton }), jsxRuntime.jsx(button.Button, { appearance: "primary", type: "submit", disabled: filterValue.length < 1, children: translations.applyButton })] })] }) }));
49
49
  }
50
50
  StringSetFilterItem.defaultValueFormatter = defaultValueFormatter;
51
+ StringSetFilterItem.defaultValuesFormatter = defaultValuesFormatter;
51
52
  StringSetFilterItem.defaultOperatorFormatter = defaultOperatorFormatter;
52
- StringSetFilterItem.formatFilterValue = formatFilterValue;
53
53
  function defaultValueFormatter(value) {
54
54
  return value[0].toUpperCase() + value.slice(1);
55
55
  }
56
- function defaultOperatorFormatter(operator) {
57
- const messages = {
58
- [stringSetFilter.StringSetFilter.Operators.in]: 'is',
59
- [stringSetFilter.StringSetFilter.Operators.notIn]: 'is not',
60
- };
61
- return messages[operator];
62
- }
63
- function formatFilterValue(values) {
56
+ function defaultValuesFormatter(values) {
64
57
  const list = values.map(defaultValueFormatter);
65
58
  if (list.length <= 1) {
66
59
  return list.toString();
@@ -73,5 +66,12 @@ function formatFilterValue(values) {
73
66
  }
74
67
  return list.slice(0, 2).join(', ') + `, and ${list.length - 2} more`;
75
68
  }
69
+ function defaultOperatorFormatter(operator) {
70
+ const messages = {
71
+ [stringSetFilter.StringSetFilter.Operators.in]: 'is',
72
+ [stringSetFilter.StringSetFilter.Operators.notIn]: 'is not',
73
+ };
74
+ return messages[operator];
75
+ }
76
76
 
77
77
  exports.StringSetFilterItem = StringSetFilterItem;
@@ -12,14 +12,22 @@ import { KeyboardKeys } from '../../lib/keyboard-keys.js';
12
12
  import { exhaustiveCheck } from '../../lib/helpers.js';
13
13
  import { classNames } from '../../lib/react-helpers.js';
14
14
 
15
- function FilteredSearch({ className, placeholder = 'Search and filter', applyButtonLabel = 'Apply', cancelButtonLabel = 'Cancel', clearButtonAccessibleName = 'Clear', removeFilterButtonAccessibleName = 'Remove filter', filtersConfig, filters, onChangeFilters, onMouseDown, ...props }) {
15
+ function FilteredSearch({ filters: appliedFilters, className, placeholder = 'Search and filter', applyButtonLabel = 'Apply', cancelButtonLabel = 'Cancel', clearButtonAccessibleName = 'Clear', removeFilterButtonAccessibleName = 'Remove filter', filtersConfig, onChangeFilters, onMouseDown, ...props }) {
16
16
  const containerRef = useRef(null);
17
+ const [newFilter, setNewFilter] = useState(null);
17
18
  const [query, setQuery] = useState('');
18
19
  const [hasFocus, setHasFocus] = useState(false);
19
20
  const [expanded, setExpanded] = useState(false);
20
21
  const [selectedId, setSelectedId] = useState('');
21
22
  const [activeField, setActiveField] = useState('');
22
23
  const { textBoxId, listBoxId, getOptionId } = useComboboxIds();
24
+ let filters;
25
+ if (newFilter) {
26
+ filters = [...appliedFilters, newFilter];
27
+ }
28
+ else {
29
+ filters = appliedFilters;
30
+ }
23
31
  const filtersConfigMap = useMemo(() => {
24
32
  const map = new Map();
25
33
  for (const config of filtersConfig) {
@@ -34,20 +42,16 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
34
42
  }
35
43
  return textBox;
36
44
  };
37
- const addFilter = (config) => {
38
- const filter = createFilter(config);
39
- const textBoxElement = getTextBoxElement();
40
- onChangeFilters([...filters, filter]);
41
- textBoxElement.blur();
42
- setQuery('');
43
- setActiveField(filter.field);
45
+ const addFilter = (filter) => {
46
+ const newFilters = [...appliedFilters, filter];
47
+ onChangeFilters(newFilters);
44
48
  };
45
- const changeFilter = (changedFilter) => {
46
- const newFilters = filters.map((filter) => (filter.field === changedFilter.field ? changedFilter : filter));
49
+ const updateFilter = (updatedFilter) => {
50
+ const newFilters = appliedFilters.map((filter) => (filter.field === updatedFilter.field ? updatedFilter : filter));
47
51
  onChangeFilters(newFilters);
48
52
  };
49
- const removeFilter = (filterToRemove) => {
50
- const newFilters = filters.filter((filter) => (filter.field !== filterToRemove.field));
53
+ const removeFilter = (removedFilter) => {
54
+ const newFilters = appliedFilters.filter((filter) => (filter.field !== removedFilter.field));
51
55
  onChangeFilters(newFilters);
52
56
  };
53
57
  const onFilterEditStarted = (filter) => {
@@ -56,12 +60,23 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
56
60
  const onFilterEditCanceled = (filter) => {
57
61
  setActiveField('');
58
62
  if (filter.isEmpty()) {
59
- removeFilter(filter);
63
+ if (newFilter && filter.field === newFilter.field) {
64
+ setNewFilter(null);
65
+ }
66
+ else {
67
+ removeFilter(filter);
68
+ }
60
69
  getTextBoxElement().focus();
61
70
  }
62
71
  };
63
72
  const onFilterChanged = (filter) => {
64
- changeFilter(filter);
73
+ if (newFilter && filter.field === newFilter.field) {
74
+ addFilter(filter);
75
+ setNewFilter(null);
76
+ }
77
+ else {
78
+ updateFilter(filter);
79
+ }
65
80
  setActiveField('');
66
81
  };
67
82
  const renderedFilters = [];
@@ -71,6 +86,14 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
71
86
  fieldsWithAppliedFilters.add(field);
72
87
  renderedFilters.push(jsx(FilterItem, { filter: filter, isEditing: activeField === field, onEditStart: onFilterEditStarted, onEditCancel: onFilterEditCanceled, onChange: onFilterChanged, onRemove: removeFilter }, field));
73
88
  }
89
+ const onOptionSelected = (config) => {
90
+ const filter = createFilter(config);
91
+ const textBoxElement = getTextBoxElement();
92
+ setQuery('');
93
+ setNewFilter(filter);
94
+ setActiveField(filter.field);
95
+ textBoxElement.blur();
96
+ };
74
97
  const onOptionHovered = (event) => {
75
98
  const listItemElement = event.currentTarget;
76
99
  setSelectedId(listItemElement.id);
@@ -81,7 +104,7 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
81
104
  const listItemElement = event.currentTarget;
82
105
  const config = filtersConfigMap.get(listItemElement.id);
83
106
  if (config) {
84
- addFilter(config);
107
+ onOptionSelected(config);
85
108
  }
86
109
  };
87
110
  const renderedOptions = [];
@@ -125,7 +148,7 @@ function FilteredSearch({ className, placeholder = 'Search and filter', applyBut
125
148
  else if (key === KeyboardKeys.Enter) {
126
149
  const config = filtersConfigMap.get(selectedId);
127
150
  if (config) {
128
- addFilter(config);
151
+ onOptionSelected(config);
129
152
  isHandled = true;
130
153
  }
131
154
  }
@@ -13,7 +13,7 @@ function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onR
13
13
  const translations = useTranslations();
14
14
  const [filterOperator, setFilterOperator] = useState(filter.operator);
15
15
  const [filterValue, setFilterValue] = useState(filter.value);
16
- const { label, values, operators, operatorSelectAccessibleName, valueFormatter: formatValue = defaultValueFormatter, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
16
+ const { label, values, operators, operatorSelectAccessibleName, valueFormatter: formatValue = defaultValueFormatter, valuesFormatter: formatValues = defaultValuesFormatter, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
17
17
  const cancelEdit = () => {
18
18
  onEditCancel(filter);
19
19
  };
@@ -42,23 +42,16 @@ function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onR
42
42
  }
43
43
  onChange(changedFilter);
44
44
  };
45
- const anchor = (jsxs(FilterToken, { isHighlighted: isEditing, onClickLabel: startEdit, onClickCloseButton: onClickCloseButton, children: [label, filter.value.length > 0 ? (jsxs(Fragment, { children: ["\u00A0", jsx("span", { children: formatOperator(filter.operator) }), "\u00A0", jsx("b", { children: formatFilterValue(filter.value) })] })) : null] }));
45
+ const anchor = (jsxs(FilterToken, { isHighlighted: isEditing, onClickLabel: startEdit, onClickCloseButton: onClickCloseButton, children: [label, filter.value.length > 0 ? (jsxs(Fragment, { children: ["\u00A0", jsx("span", { children: formatOperator(filter.operator) }), "\u00A0", jsx("b", { children: formatValues(filter.value) })] })) : null] }));
46
46
  return (jsx(Popover, { className: "dc-filter-popover", anchor: anchor, anchorGap: 2, transitionDurationMs: 0, isOpen: isEditing, onClose: cancelEdit, children: jsxs("form", { className: "dc-filter-form", onSubmit: onSubmit, children: [jsx(FilterOperatorSelect, { className: "dc-filter-form__operator", accessibleName: operatorSelectAccessibleName, label: label, values: operators, value: filterOperator, onChange: setFilterOperator, formatValue: formatOperator }), jsx(FilterValueList, { className: "dc-filter-form__value-list", values: values, checkedValues: filterValue, onChangeCheckedValues: setFilterValue, formatValue: formatValue }), jsxs("div", { className: "dc-filter-form__buttons", children: [jsx(Button, { variant: "plain", type: "button", onClick: cancelEdit, children: translations.cancelButton }), jsx(Button, { appearance: "primary", type: "submit", disabled: filterValue.length < 1, children: translations.applyButton })] })] }) }));
47
47
  }
48
48
  StringSetFilterItem.defaultValueFormatter = defaultValueFormatter;
49
+ StringSetFilterItem.defaultValuesFormatter = defaultValuesFormatter;
49
50
  StringSetFilterItem.defaultOperatorFormatter = defaultOperatorFormatter;
50
- StringSetFilterItem.formatFilterValue = formatFilterValue;
51
51
  function defaultValueFormatter(value) {
52
52
  return value[0].toUpperCase() + value.slice(1);
53
53
  }
54
- function defaultOperatorFormatter(operator) {
55
- const messages = {
56
- [StringSetFilter.Operators.in]: 'is',
57
- [StringSetFilter.Operators.notIn]: 'is not',
58
- };
59
- return messages[operator];
60
- }
61
- function formatFilterValue(values) {
54
+ function defaultValuesFormatter(values) {
62
55
  const list = values.map(defaultValueFormatter);
63
56
  if (list.length <= 1) {
64
57
  return list.toString();
@@ -71,5 +64,12 @@ function formatFilterValue(values) {
71
64
  }
72
65
  return list.slice(0, 2).join(', ') + `, and ${list.length - 2} more`;
73
66
  }
67
+ function defaultOperatorFormatter(operator) {
68
+ const messages = {
69
+ [StringSetFilter.Operators.in]: 'is',
70
+ [StringSetFilter.Operators.notIn]: 'is not',
71
+ };
72
+ return messages[operator];
73
+ }
74
74
 
75
75
  export { StringSetFilterItem };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "draft-components",
3
- "version": "1.8.0",
3
+ "version": "1.10.0",
4
4
  "description": "The React based UI components library.",
5
5
  "type": "module",
6
6
  "exports": {
@@ -12,4 +12,4 @@ export type FilteredSearchProps = FilteredSearchBaseProps & {
12
12
  filters: Filter[];
13
13
  onChangeFilters: (filters: Filter[]) => void;
14
14
  };
15
- export declare function FilteredSearch({ className, placeholder, applyButtonLabel, cancelButtonLabel, clearButtonAccessibleName, removeFilterButtonAccessibleName, filtersConfig, filters, onChangeFilters, onMouseDown, ...props }: FilteredSearchProps): import("react/jsx-runtime").JSX.Element;
15
+ export declare function FilteredSearch({ filters: appliedFilters, className, placeholder, applyButtonLabel, cancelButtonLabel, clearButtonAccessibleName, removeFilterButtonAccessibleName, filtersConfig, onChangeFilters, onMouseDown, ...props }: FilteredSearchProps): import("react/jsx-runtime").JSX.Element;
@@ -1,4 +1,4 @@
1
1
  export { FilteredSearch, type FilteredSearchProps, type FilteredSearchBaseProps, type FilteredSearchHTMLProps, } from './filtered-search';
2
2
  export { type Filter, type FilterConfig, } from './types';
3
- export { StringFilter, type StringFilterState, type StringFilterConfig, } from './model/string-filter';
4
- export { StringSetFilter, type StringSetFilterState, type StringSetFilterConfig, } from './model/string-set-filter';
3
+ export { StringFilter, type StringFilterState, type StringFilterConfig, type StringFilterOperator, } from './model/string-filter';
4
+ export { StringSetFilter, type StringSetFilterState, type StringSetFilterConfig, type StringSetFilterOperator, } from './model/string-set-filter';
@@ -12,6 +12,7 @@ export type StringSetFilterConfig = {
12
12
  label: string;
13
13
  values: string[];
14
14
  valueFormatter?: (value: string) => string;
15
+ valuesFormatter?: (values: string[]) => string;
15
16
  operatorSelectAccessibleName?: string;
16
17
  operators: StringSetFilterOperator[];
17
18
  operatorFormatter?: (operator: StringSetFilterOperator) => string;
@@ -10,6 +10,6 @@ export type StringSetFilterItemProps = {
10
10
  export declare function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onRemove, onChange, }: StringSetFilterItemProps): import("react/jsx-runtime").JSX.Element;
11
11
  export declare namespace StringSetFilterItem {
12
12
  var defaultValueFormatter: (value: string) => string;
13
+ var defaultValuesFormatter: (values: string[]) => string;
13
14
  var defaultOperatorFormatter: (operator: StringSetFilterOperator) => string;
14
- var formatFilterValue: (values: string[]) => string;
15
15
  }