draft-components 1.6.0 → 1.7.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.
Files changed (174) hide show
  1. package/cjs/components/dialog/dialog.cjs +1 -1
  2. package/cjs/components/filtered-search/filter-item.cjs +23 -0
  3. package/cjs/components/filtered-search/filter-operator-select.cjs +17 -0
  4. package/cjs/components/filtered-search/filter-token.cjs +17 -0
  5. package/cjs/components/filtered-search/filter-value-list.cjs +20 -0
  6. package/cjs/components/filtered-search/filtered-search.cjs +202 -0
  7. package/cjs/components/filtered-search/icons.cjs +21 -0
  8. package/cjs/components/filtered-search/model/abstract-filter.cjs +6 -0
  9. package/cjs/components/filtered-search/model/string-filter.cjs +46 -0
  10. package/cjs/components/filtered-search/model/string-set-filter.cjs +44 -0
  11. package/cjs/components/filtered-search/string-filter-input.cjs +17 -0
  12. package/cjs/components/filtered-search/string-filter-item.cjs +68 -0
  13. package/cjs/components/filtered-search/string-set-filter-item.cjs +77 -0
  14. package/cjs/components/filtered-search/use-combobox-ids.cjs +17 -0
  15. package/cjs/components/filtered-search/use-translations.cjs +24 -0
  16. package/cjs/components/index.cjs +12 -0
  17. package/cjs/components/popover/popover.cjs +3 -4
  18. package/cjs/components/slide-over/slide-over-body.cjs +10 -0
  19. package/cjs/components/slide-over/slide-over-context.cjs +24 -0
  20. package/cjs/components/slide-over/slide-over-header.cjs +21 -0
  21. package/cjs/components/slide-over/slide-over.cjs +106 -0
  22. package/cjs/hooks/index.cjs +2 -2
  23. package/cjs/hooks/use-disable-body-scroll.cjs +3 -1
  24. package/cjs/hooks/use-esc-key-down.cjs +3 -1
  25. package/cjs/hooks/use-focus-trap.cjs +3 -1
  26. package/cjs/index.cjs +16 -2
  27. package/cjs/lib/helpers.cjs +5 -1
  28. package/cjs/lib/index.cjs +2 -0
  29. package/cjs/lib/keyboard-keys.cjs +1 -0
  30. package/cjs/lib/react-helpers.cjs +8 -0
  31. package/css/draft-components.css +340 -2
  32. package/css/draft-components.dark.css +65 -40
  33. package/esm/components/dialog/dialog.js +1 -1
  34. package/esm/components/filtered-search/filter-item.js +21 -0
  35. package/esm/components/filtered-search/filter-operator-select.js +15 -0
  36. package/esm/components/filtered-search/filter-token.js +15 -0
  37. package/esm/components/filtered-search/filter-value-list.js +18 -0
  38. package/esm/components/filtered-search/filtered-search.js +200 -0
  39. package/esm/components/filtered-search/icons.js +16 -0
  40. package/esm/components/filtered-search/model/abstract-filter.js +4 -0
  41. package/esm/components/filtered-search/model/string-filter.js +44 -0
  42. package/esm/components/filtered-search/model/string-set-filter.js +42 -0
  43. package/esm/components/filtered-search/string-filter-input.js +15 -0
  44. package/esm/components/filtered-search/string-filter-item.js +66 -0
  45. package/esm/components/filtered-search/string-set-filter-item.js +75 -0
  46. package/esm/components/filtered-search/use-combobox-ids.js +15 -0
  47. package/esm/components/filtered-search/use-translations.js +21 -0
  48. package/esm/components/index.js +6 -0
  49. package/esm/components/popover/popover.js +3 -4
  50. package/esm/components/slide-over/slide-over-body.js +8 -0
  51. package/esm/components/slide-over/slide-over-context.js +21 -0
  52. package/esm/components/slide-over/slide-over-header.js +19 -0
  53. package/esm/components/slide-over/slide-over.js +104 -0
  54. package/esm/hooks/index.js +1 -1
  55. package/esm/hooks/use-disable-body-scroll.js +3 -1
  56. package/esm/hooks/use-esc-key-down.js +3 -1
  57. package/esm/hooks/use-focus-trap.js +3 -1
  58. package/esm/index.js +9 -3
  59. package/esm/lib/helpers.js +5 -2
  60. package/esm/lib/index.js +2 -2
  61. package/esm/lib/keyboard-keys.js +1 -0
  62. package/esm/lib/react-helpers.js +8 -1
  63. package/package.json +1 -1
  64. package/types/components/alert/alert.d.ts +1 -1
  65. package/types/components/avatar/avatar.d.ts +1 -1
  66. package/types/components/avatar-group/avatar-group.d.ts +1 -1
  67. package/types/components/badge/badge.d.ts +1 -1
  68. package/types/components/breadcrumbs/breadcrumbs-context.d.ts +1 -1
  69. package/types/components/breadcrumbs/breadcrumbs-item.d.ts +1 -1
  70. package/types/components/breadcrumbs/breadcrumbs.d.ts +1 -1
  71. package/types/components/button/button.d.ts +1 -1
  72. package/types/components/button/icon-button.d.ts +2 -2
  73. package/types/components/button-group/button-group.d.ts +1 -1
  74. package/types/components/caption/caption.d.ts +1 -1
  75. package/types/components/caption/icons.d.ts +1 -1
  76. package/types/components/checkbox/checkbox.d.ts +1 -1
  77. package/types/components/color-picker/color-picker-button.d.ts +1 -1
  78. package/types/components/color-picker/color-picker.d.ts +1 -1
  79. package/types/components/date-picker/calendar-day.d.ts +1 -1
  80. package/types/components/date-picker/calendar-grid-head.d.ts +1 -1
  81. package/types/components/date-picker/calendar-grid.d.ts +1 -1
  82. package/types/components/date-picker/calendar.d.ts +3 -3
  83. package/types/components/date-picker/date-picker.d.ts +3 -3
  84. package/types/components/date-picker/date-range-picker.d.ts +4 -4
  85. package/types/components/date-picker/date-range.d.ts +1 -1
  86. package/types/components/date-picker/icons.d.ts +1 -1
  87. package/types/components/date-picker-popover/date-picker-popover.d.ts +3 -3
  88. package/types/components/date-range-picker-popover/date-range-picker-popover-footer.d.ts +1 -1
  89. package/types/components/date-range-picker-popover/date-range-picker-popover-presets.d.ts +1 -1
  90. package/types/components/date-range-picker-popover/date-range-picker-popover.d.ts +4 -4
  91. package/types/components/date-range-picker-popover/types.d.ts +1 -1
  92. package/types/components/dialog/dialog-body.d.ts +1 -1
  93. package/types/components/dialog/dialog-context.d.ts +1 -1
  94. package/types/components/dialog/dialog-footer.d.ts +1 -1
  95. package/types/components/dialog/dialog-header.d.ts +1 -1
  96. package/types/components/dialog/dialog.d.ts +1 -1
  97. package/types/components/dialog/x-mark-icon.d.ts +1 -1
  98. package/types/components/empty-state/empty-state.d.ts +1 -1
  99. package/types/components/filter-buttons/filter-button.d.ts +1 -1
  100. package/types/components/filter-buttons/filter-buttons.d.ts +1 -1
  101. package/types/components/filtered-search/filter-item.d.ts +10 -0
  102. package/types/components/filtered-search/filter-operator-select.d.ts +10 -0
  103. package/types/components/filtered-search/filter-token.d.ts +9 -0
  104. package/types/components/filtered-search/filter-value-list.d.ts +8 -0
  105. package/types/components/filtered-search/filtered-search.d.ts +15 -0
  106. package/types/components/filtered-search/icons.d.ts +5 -0
  107. package/types/components/filtered-search/index.d.ts +4 -0
  108. package/types/components/filtered-search/model/abstract-filter.d.ts +6 -0
  109. package/types/components/filtered-search/model/string-filter.d.ts +47 -0
  110. package/types/components/filtered-search/model/string-set-filter.d.ts +41 -0
  111. package/types/components/filtered-search/model/validation-result.d.ts +6 -0
  112. package/types/components/filtered-search/string-filter-input.d.ts +9 -0
  113. package/types/components/filtered-search/string-filter-item.d.ts +13 -0
  114. package/types/components/filtered-search/string-set-filter-item.d.ts +15 -0
  115. package/types/components/filtered-search/types.d.ts +4 -0
  116. package/types/components/filtered-search/use-combobox-ids.d.ts +5 -0
  117. package/types/components/filtered-search/use-translations.d.ts +13 -0
  118. package/types/components/form-field/form-field.d.ts +1 -1
  119. package/types/components/index.d.ts +2 -0
  120. package/types/components/label/label.d.ts +1 -1
  121. package/types/components/menu/menu-item.d.ts +1 -1
  122. package/types/components/menu/menu-separator.d.ts +1 -1
  123. package/types/components/menu/menu.d.ts +3 -3
  124. package/types/components/nav-list/nav-list-item.d.ts +1 -1
  125. package/types/components/nav-list/nav-list-title.d.ts +1 -1
  126. package/types/components/nav-list/nav-list.d.ts +1 -1
  127. package/types/components/password-input/icons.d.ts +1 -1
  128. package/types/components/password-input/password-input.d.ts +1 -1
  129. package/types/components/popover/popover.d.ts +3 -1
  130. package/types/components/portal/portal-context.d.ts +1 -1
  131. package/types/components/portal/portal.d.ts +1 -1
  132. package/types/components/positioner/calc-position.d.ts +1 -1
  133. package/types/components/positioner/positioner.d.ts +2 -2
  134. package/types/components/radio/radio.d.ts +1 -1
  135. package/types/components/segmented-control/segmented-control-button.d.ts +1 -1
  136. package/types/components/segmented-control/segmented-control.d.ts +1 -1
  137. package/types/components/select/select.d.ts +1 -1
  138. package/types/components/selection-control/selection-control.d.ts +1 -1
  139. package/types/components/slide-over/index.d.ts +4 -0
  140. package/types/components/slide-over/slide-over-body.d.ts +3 -0
  141. package/types/components/slide-over/slide-over-context.d.ts +14 -0
  142. package/types/components/slide-over/slide-over-header.d.ts +11 -0
  143. package/types/components/slide-over/slide-over.d.ts +15 -0
  144. package/types/components/slide-over/types.d.ts +1 -0
  145. package/types/components/slider/slider-tick-marks.d.ts +1 -1
  146. package/types/components/slider/slider.d.ts +2 -2
  147. package/types/components/spinner/spinner.d.ts +1 -1
  148. package/types/components/switch/switch.d.ts +1 -1
  149. package/types/components/table/table-body.d.ts +1 -1
  150. package/types/components/table/table-cell.d.ts +1 -1
  151. package/types/components/table/table-container.d.ts +1 -1
  152. package/types/components/table/table-head-cell.d.ts +1 -1
  153. package/types/components/table/table-head.d.ts +1 -1
  154. package/types/components/table/table-row.d.ts +1 -1
  155. package/types/components/table/table.d.ts +1 -1
  156. package/types/components/tabs/tab-list.d.ts +1 -1
  157. package/types/components/tabs/tab-panel.d.ts +2 -2
  158. package/types/components/tabs/tab.d.ts +1 -1
  159. package/types/components/tabs/tabs-context.d.ts +2 -2
  160. package/types/components/tabs/tabs.d.ts +2 -2
  161. package/types/components/tag/tag.d.ts +1 -1
  162. package/types/components/text-input/text-input.d.ts +1 -1
  163. package/types/components/textarea/textarea.d.ts +1 -1
  164. package/types/components/toast/toast-button.d.ts +1 -1
  165. package/types/components/toast/toast.d.ts +1 -1
  166. package/types/components/toaster/toaster.d.ts +1 -1
  167. package/types/components/tooltip/tooltip.d.ts +2 -2
  168. package/types/hooks/index.d.ts +1 -1
  169. package/types/hooks/use-disable-body-scroll.d.ts +1 -1
  170. package/types/hooks/use-esc-key-down.d.ts +1 -1
  171. package/types/hooks/use-focus-trap.d.ts +2 -2
  172. package/types/lib/helpers.d.ts +1 -0
  173. package/types/lib/keyboard-keys.d.ts +1 -0
  174. package/types/lib/react-helpers.d.ts +2 -1
@@ -3,10 +3,10 @@
3
3
  const jsxRuntime = require('react/jsx-runtime');
4
4
  const react = require('react');
5
5
  const reactHelpers = require('../../lib/react-helpers.cjs');
6
- const useMountTransition = require('../../hooks/use-mount-transition.cjs');
7
6
  const useDisableBodyScroll = require('../../hooks/use-disable-body-scroll.cjs');
8
7
  const useEscKeyDown = require('../../hooks/use-esc-key-down.cjs');
9
8
  const useFocusTrap = require('../../hooks/use-focus-trap.cjs');
9
+ const useMountTransition = require('../../hooks/use-mount-transition.cjs');
10
10
  const portal = require('../portal/portal.cjs');
11
11
  const dialogContext = require('./dialog-context.cjs');
12
12
 
@@ -0,0 +1,23 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const stringFilter = require('./model/string-filter.cjs');
5
+ const stringSetFilter = require('./model/string-set-filter.cjs');
6
+ const helpers = require('../../lib/helpers.cjs');
7
+ require('react');
8
+ const stringFilterItem = require('./string-filter-item.cjs');
9
+ const stringSetFilterItem = require('./string-set-filter-item.cjs');
10
+
11
+ function FilterItem({ filter, isEditing, onEditStart, onEditCancel, onRemove, onChange, }) {
12
+ const filterType = filter.type;
13
+ switch (filterType) {
14
+ case stringFilter.StringFilter.Type:
15
+ return (jsxRuntime.jsx(stringFilterItem.StringFilterItem, { filter: filter, isEditing: isEditing, onEditStart: onEditStart, onEditCancel: onEditCancel, onRemove: onRemove, onChange: onChange }));
16
+ case stringSetFilter.StringSetFilter.Type:
17
+ return (jsxRuntime.jsx(stringSetFilterItem.StringSetFilterItem, { filter: filter, isEditing: isEditing, onEditStart: onEditStart, onEditCancel: onEditCancel, onRemove: onRemove, onChange: onChange }));
18
+ default:
19
+ helpers.exhaustiveCheck(filterType, `Unable to render filter type ${filterType}.`);
20
+ }
21
+ }
22
+
23
+ exports.FilterItem = FilterItem;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const reactHelpers = require('../../lib/react-helpers.cjs');
5
+ const select = require('../select/select.cjs');
6
+
7
+ function FilterOperatorSelect({ accessibleName, className, label, values, value, onChange, formatValue, }) {
8
+ const shouldRenderSelect = values.length > 1;
9
+ const onValueChanged = (newValue) => {
10
+ onChange(newValue);
11
+ };
12
+ return (jsxRuntime.jsxs("div", { className: reactHelpers.classNames('dc-filter-operator-select', className), children: [jsxRuntime.jsx("span", { children: shouldRenderSelect
13
+ ? label
14
+ : `${label} ${formatValue(value)}` }), shouldRenderSelect && (jsxRuntime.jsx(select.Select, { size: "sm", "data-testid": "filter-operator-select", "aria-label": accessibleName, value: value, onChangeValue: onValueChanged, children: values.map((value) => (jsxRuntime.jsx("option", { value: value, children: formatValue(value) }, value))) }))] }));
15
+ }
16
+
17
+ exports.FilterOperatorSelect = FilterOperatorSelect;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const react = require('react');
5
+ const reactHelpers = require('../../lib/react-helpers.cjs');
6
+ const icons = require('./icons.cjs');
7
+ const useTranslations = require('./use-translations.cjs');
8
+
9
+ const FilterToken = react.forwardRef(function FilterToken({ className, isHighlighted, children, onClickLabel, onClickCloseButton, }, ref) {
10
+ const { removeFilterButton } = useTranslations.useTranslations();
11
+ return (jsxRuntime.jsxs("div", { ref: ref, "data-testid": "filter-token", className: reactHelpers.classNames(className, {
12
+ 'dc-filter-token': true,
13
+ 'dc-filter-token_highlighted': isHighlighted,
14
+ }), children: [jsxRuntime.jsx("button", { className: "dc-filter-token__label", onClick: onClickLabel, children: children }), jsxRuntime.jsx("button", { className: "dc-filter-token__button", type: "button", "aria-label": removeFilterButton, onClick: onClickCloseButton, children: jsxRuntime.jsx(icons.XMarkIcon, {}) })] }));
15
+ });
16
+
17
+ exports.FilterToken = FilterToken;
@@ -0,0 +1,20 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const reactHelpers = require('../../lib/react-helpers.cjs');
5
+ const selectionControl = require('../selection-control/selection-control.cjs');
6
+ const checkbox = require('../checkbox/checkbox.cjs');
7
+
8
+ function FilterValueList({ className, values, checkedValues, onChangeCheckedValues, formatValue, }) {
9
+ const onChange = (event) => {
10
+ const checkboxElement = event.currentTarget;
11
+ const value = checkboxElement.value;
12
+ const checked = checkboxElement.checked;
13
+ onChangeCheckedValues(checked
14
+ ? [...checkedValues, value]
15
+ : checkedValues.filter((prevValue) => prevValue !== value));
16
+ };
17
+ return (jsxRuntime.jsx("ul", { className: reactHelpers.classNames('dc-filter-value-list', className), children: values.map((value, index) => (jsxRuntime.jsx("li", { children: jsxRuntime.jsx(selectionControl.SelectionControl, { label: formatValue(value), children: jsxRuntime.jsx(checkbox.Checkbox, { autoFocus: index === 0, value: value, checked: checkedValues.includes(value), onChange: onChange }) }) }, value))) }));
18
+ }
19
+
20
+ exports.FilterValueList = FilterValueList;
@@ -0,0 +1,202 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const react = require('react');
5
+ const stringFilter = require('./model/string-filter.cjs');
6
+ const stringSetFilter = require('./model/string-set-filter.cjs');
7
+ const useTranslations = require('./use-translations.cjs');
8
+ const useComboboxIds = require('./use-combobox-ids.cjs');
9
+ require('../button/button.cjs');
10
+ const iconButton = require('../button/icon-button.cjs');
11
+ const filterItem = require('./filter-item.cjs');
12
+ const icons = require('./icons.cjs');
13
+ const keyboardKeys = require('../../lib/keyboard-keys.cjs');
14
+ const helpers = require('../../lib/helpers.cjs');
15
+ const reactHelpers = require('../../lib/react-helpers.cjs');
16
+
17
+ function FilteredSearch({ className, placeholder = 'Search and filter', applyButtonLabel = 'Apply', cancelButtonLabel = 'Cancel', clearButtonAccessibleName = 'Clear', removeFilterButtonAccessibleName = 'Remove filter', filtersConfig, filters, onChange, onMouseDown, ...props }) {
18
+ const containerRef = react.useRef(null);
19
+ const [query, setQuery] = react.useState('');
20
+ const [hasFocus, setHasFocus] = react.useState(false);
21
+ const [expanded, setExpanded] = react.useState(false);
22
+ const [selectedId, setSelectedId] = react.useState('');
23
+ const [activeField, setActiveField] = react.useState('');
24
+ const { textBoxId, listBoxId, getOptionId } = useComboboxIds.useComboboxIds();
25
+ const filtersConfigMap = react.useMemo(() => {
26
+ const map = new Map();
27
+ for (const config of filtersConfig) {
28
+ map.set(getOptionId(config.field), config);
29
+ }
30
+ return map;
31
+ }, [filtersConfig, getOptionId]);
32
+ const getTextBoxElement = () => {
33
+ const textBox = document.getElementById(textBoxId);
34
+ if (!(textBox instanceof HTMLInputElement)) {
35
+ throw new Error(`Unable to find input element with ID ${textBoxId}.`);
36
+ }
37
+ return textBox;
38
+ };
39
+ const addFilter = (config) => {
40
+ const filter = createFilter(config);
41
+ const textBoxElement = getTextBoxElement();
42
+ onChange([...filters, filter]);
43
+ textBoxElement.blur();
44
+ setQuery('');
45
+ setActiveField(filter.field);
46
+ };
47
+ const changeFilter = (changedFilter) => {
48
+ const newFilters = filters.map((filter) => (filter.field === changedFilter.field ? changedFilter : filter));
49
+ onChange(newFilters);
50
+ };
51
+ const removeFilter = (filterToRemove) => {
52
+ const newFilters = filters.filter((filter) => (filter.field !== filterToRemove.field));
53
+ onChange(newFilters);
54
+ };
55
+ const onFilterEditStarted = (filter) => {
56
+ setActiveField(filter.field);
57
+ };
58
+ const onFilterEditCanceled = (filter) => {
59
+ setActiveField('');
60
+ if (filter.isEmpty()) {
61
+ removeFilter(filter);
62
+ getTextBoxElement().focus();
63
+ }
64
+ };
65
+ const onFilterChanged = (filter) => {
66
+ changeFilter(filter);
67
+ setActiveField('');
68
+ };
69
+ const renderedFilters = [];
70
+ const fieldsWithAppliedFilters = new Set();
71
+ for (const filter of filters) {
72
+ const field = filter.field;
73
+ fieldsWithAppliedFilters.add(field);
74
+ renderedFilters.push(jsxRuntime.jsx(filterItem.FilterItem, { filter: filter, isEditing: activeField === field, onEditStart: onFilterEditStarted, onEditCancel: onFilterEditCanceled, onChange: onFilterChanged, onRemove: removeFilter }, field));
75
+ }
76
+ const onOptionHovered = (event) => {
77
+ const listItemElement = event.currentTarget;
78
+ setSelectedId(listItemElement.id);
79
+ };
80
+ const onOptionPressed = (event) => {
81
+ event.preventDefault();
82
+ event.stopPropagation();
83
+ const listItemElement = event.currentTarget;
84
+ const config = filtersConfigMap.get(listItemElement.id);
85
+ if (config) {
86
+ addFilter(config);
87
+ }
88
+ };
89
+ const renderedOptions = [];
90
+ const search = query.toLowerCase();
91
+ for (const [id, config] of filtersConfigMap) {
92
+ if (fieldsWithAppliedFilters.has(config.field)) {
93
+ continue;
94
+ }
95
+ if (!config.label.toLowerCase().includes(search)) {
96
+ continue;
97
+ }
98
+ renderedOptions.push(jsxRuntime.jsx("li", { id: id, role: "option", "data-field": config.field, "aria-selected": selectedId === id, onMouseEnter: onOptionHovered, onMouseDown: onOptionPressed, children: config.label }, id));
99
+ }
100
+ const onInputFocused = () => {
101
+ setHasFocus(true);
102
+ setExpanded(true);
103
+ };
104
+ const onInputBlurred = () => {
105
+ setExpanded(false);
106
+ setHasFocus(false);
107
+ setQuery('');
108
+ setSelectedId('');
109
+ };
110
+ const onInputKeyPressed = (event) => {
111
+ const key = event.key;
112
+ const inputElement = event.currentTarget;
113
+ const optionIds = renderedOptions.map((opt) => opt.props.id);
114
+ const firstIndex = 0;
115
+ const lastIndex = optionIds.length - 1;
116
+ const selectedIdIndex = optionIds.findIndex((id) => id === selectedId);
117
+ let isHandled = false;
118
+ let nextIdIndex = selectedIdIndex;
119
+ if (key === keyboardKeys.KeyboardKeys.ArrowDown) {
120
+ nextIdIndex = selectedIdIndex + 1;
121
+ isHandled = true;
122
+ }
123
+ else if (key === keyboardKeys.KeyboardKeys.ArrowUp) {
124
+ nextIdIndex = selectedIdIndex - 1;
125
+ isHandled = true;
126
+ }
127
+ else if (key === keyboardKeys.KeyboardKeys.Enter) {
128
+ const config = filtersConfigMap.get(selectedId);
129
+ if (config) {
130
+ addFilter(config);
131
+ isHandled = true;
132
+ }
133
+ }
134
+ else if (key === keyboardKeys.KeyboardKeys.Backspace) {
135
+ if (query === '' && filters.length > 0) {
136
+ onChange(filters.slice(0, -1));
137
+ isHandled = true;
138
+ }
139
+ }
140
+ else if (key === keyboardKeys.KeyboardKeys.Escape) {
141
+ inputElement.blur();
142
+ isHandled = true;
143
+ }
144
+ if (nextIdIndex !== selectedIdIndex) {
145
+ if (nextIdIndex < firstIndex) {
146
+ nextIdIndex = lastIndex;
147
+ }
148
+ if (nextIdIndex > lastIndex) {
149
+ nextIdIndex = firstIndex;
150
+ }
151
+ setSelectedId(optionIds[nextIdIndex]);
152
+ }
153
+ else if (renderedOptions.length === 0) {
154
+ setSelectedId('');
155
+ }
156
+ if (isHandled) {
157
+ event.preventDefault();
158
+ event.stopPropagation();
159
+ }
160
+ };
161
+ const onInputChanged = (event) => {
162
+ setQuery(event.target.value);
163
+ };
164
+ const onClearButtonPressed = (event) => {
165
+ event.stopPropagation();
166
+ onChange([]);
167
+ };
168
+ const onContainerPressed = (event) => {
169
+ if (event.currentTarget === event.target) {
170
+ const textBoxElement = getTextBoxElement();
171
+ textBoxElement.focus();
172
+ event.stopPropagation();
173
+ event.preventDefault();
174
+ }
175
+ if (typeof onMouseDown === 'function') {
176
+ onMouseDown(event);
177
+ }
178
+ };
179
+ return (jsxRuntime.jsx(useTranslations.TranslationsProvider, { applyButton: applyButtonLabel, cancelButton: cancelButtonLabel, removeFilterButton: removeFilterButtonAccessibleName, children: jsxRuntime.jsxs("div", { ref: containerRef, "data-testid": "combobox-container", className: reactHelpers.classNames(className, {
180
+ 'dc-filtered-search': true,
181
+ 'dc-filtered-search_has_focus': hasFocus,
182
+ }), onMouseDown: onContainerPressed, ...props, children: [jsxRuntime.jsx(icons.MagnifyingGlassIcon, { className: "dc-filtered-search__icon" }), jsxRuntime.jsxs("div", { className: "dc-filtered-search__filters", children: [renderedFilters, jsxRuntime.jsx("input", { className: "dc-filtered-search__input", id: textBoxId, placeholder: placeholder, type: "text", role: "combobox", "aria-autocomplete": "list", "aria-expanded": expanded, "aria-controls": listBoxId, "aria-activedescendant": selectedId, value: query, onFocus: onInputFocused, onBlur: onInputBlurred, onKeyDown: onInputKeyPressed, onChange: onInputChanged })] }), jsxRuntime.jsx(iconButton.IconButton, { className: "dc-filtered-search__clear-button", "aria-label": clearButtonAccessibleName, icon: jsxRuntime.jsx(icons.TrashIcon, {}), size: "sm", variant: "plain", appearance: "primary", onClick: onClearButtonPressed }), expanded && renderedOptions.length > 0 && (jsxRuntime.jsx("ul", { className: "dc-filtered-search__list-box", id: listBoxId, role: "listbox", children: renderedOptions }))] }) }));
183
+ }
184
+ function createFilter(config) {
185
+ const filterType = config.type;
186
+ switch (filterType) {
187
+ case stringFilter.StringFilter.Type:
188
+ return new stringFilter.StringFilter(config, {
189
+ value: '',
190
+ operator: config.operators[0] || stringFilter.StringFilter.Operators.equal,
191
+ });
192
+ case stringSetFilter.StringSetFilter.Type:
193
+ return new stringSetFilter.StringSetFilter(config, {
194
+ value: [],
195
+ operator: config.operators[0] || stringSetFilter.StringSetFilter.Operators.in,
196
+ });
197
+ default:
198
+ helpers.exhaustiveCheck(filterType, `Unable to create a filter with ${filterType} type.`);
199
+ }
200
+ }
201
+
202
+ exports.FilteredSearch = FilteredSearch;
@@ -0,0 +1,21 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+
5
+ function MagnifyingGlassIcon(props) {
6
+ return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 1.5, ...props, children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M21 21l-5.197-5.197m0 0A7.5 7.5 0 105.196 5.196a7.5 7.5 0 0010.607 10.607z" }) }));
7
+ }
8
+ function TrashIcon(props) {
9
+ return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: 18, height: 18, fill: "none", stroke: "currentColor", strokeWidth: 1.5, ...props, children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M14.74 9l-.346 9m-4.788 0L9.26 9m9.968-3.21c.342.052.682.107 1.022.166m-1.022-.165L18.16 19.673a2.25 2.25 0 01-2.244 2.077H8.084a2.25 2.25 0 01-2.244-2.077L4.772 5.79m14.456 0a48.108 48.108 0 00-3.478-.397m-12 .562c.34-.059.68-.114 1.022-.165m0 0a48.11 48.11 0 013.478-.397m7.5 0v-.916c0-1.18-.91-2.164-2.09-2.201a51.964 51.964 0 00-3.32 0c-1.18.037-2.09 1.022-2.09 2.201v.916m7.5 0a48.667 48.667 0 00-7.5 0" }) }));
10
+ }
11
+ function XMarkIcon(props) {
12
+ return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 24 24", width: 16, height: 16, fill: "none", stroke: "currentColor", strokeWidth: 1.5, ...props, children: jsxRuntime.jsx("path", { strokeLinecap: "round", strokeLinejoin: "round", d: "M6 18L18 6M6 6l12 12" }) }));
13
+ }
14
+ function ArrowReturnRight(props) {
15
+ return (jsxRuntime.jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 16 16", width: 16, height: 16, fill: "currentColor", ...props, children: jsxRuntime.jsx("path", { fillRule: "evenodd", d: "M1.5 1.5A.5.5 0 0 0 1 2v4.8a2.5 2.5 0 0 0 2.5 2.5h9.793l-3.347 3.346a.5.5 0 0 0 .708.708l4.2-4.2a.5.5 0 0 0 0-.708l-4-4a.5.5 0 0 0-.708.708L13.293 8.3H3.5A1.5 1.5 0 0 1 2 6.8V2a.5.5 0 0 0-.5-.5z" }) }));
16
+ }
17
+
18
+ exports.ArrowReturnRight = ArrowReturnRight;
19
+ exports.MagnifyingGlassIcon = MagnifyingGlassIcon;
20
+ exports.TrashIcon = TrashIcon;
21
+ exports.XMarkIcon = XMarkIcon;
@@ -0,0 +1,6 @@
1
+ 'use strict';
2
+
3
+ class AbstractFilter {
4
+ }
5
+
6
+ exports.AbstractFilter = AbstractFilter;
@@ -0,0 +1,46 @@
1
+ 'use strict';
2
+
3
+ const abstractFilter = require('./abstract-filter.cjs');
4
+
5
+ const TYPE = 'STRING';
6
+ const OPERATORS = {
7
+ equal: 'EQUAL',
8
+ notEqual: 'NOT_EQUAL',
9
+ contain: 'CONTAIN',
10
+ notContain: 'NOT_CONTAIN',
11
+ };
12
+ class StringFilter extends abstractFilter.AbstractFilter {
13
+ constructor(config, state) {
14
+ super();
15
+ this.config = config;
16
+ this.state = state;
17
+ }
18
+ get type() {
19
+ return this.config.type;
20
+ }
21
+ get field() {
22
+ return this.config.field;
23
+ }
24
+ get label() {
25
+ return this.config.label;
26
+ }
27
+ get value() {
28
+ return this.state.value;
29
+ }
30
+ get operator() {
31
+ return this.state.operator;
32
+ }
33
+ setValue(value) {
34
+ return new StringFilter(this.config, { ...this.state, value });
35
+ }
36
+ setOperator(operator) {
37
+ return new StringFilter(this.config, { ...this.state, operator });
38
+ }
39
+ isEmpty() {
40
+ return this.value === '';
41
+ }
42
+ }
43
+ StringFilter.Type = TYPE;
44
+ StringFilter.Operators = OPERATORS;
45
+
46
+ exports.StringFilter = StringFilter;
@@ -0,0 +1,44 @@
1
+ 'use strict';
2
+
3
+ const abstractFilter = require('./abstract-filter.cjs');
4
+
5
+ const TYPE = 'STRING_SET';
6
+ const OPERATORS = {
7
+ in: 'IN',
8
+ notIn: 'NOT_IN',
9
+ };
10
+ class StringSetFilter extends abstractFilter.AbstractFilter {
11
+ constructor(config, state) {
12
+ super();
13
+ this.config = config;
14
+ this.state = state;
15
+ }
16
+ get type() {
17
+ return this.config.type;
18
+ }
19
+ get field() {
20
+ return this.config.field;
21
+ }
22
+ get label() {
23
+ return this.config.label;
24
+ }
25
+ get value() {
26
+ return this.state.value;
27
+ }
28
+ get operator() {
29
+ return this.state.operator;
30
+ }
31
+ setValue(value) {
32
+ return new StringSetFilter(this.config, { ...this.state, value });
33
+ }
34
+ setOperator(operator) {
35
+ return new StringSetFilter(this.config, { ...this.state, operator });
36
+ }
37
+ isEmpty() {
38
+ return this.value.length === 0;
39
+ }
40
+ }
41
+ StringSetFilter.Type = TYPE;
42
+ StringSetFilter.Operators = OPERATORS;
43
+
44
+ exports.StringSetFilter = StringSetFilter;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const reactHelpers = require('../../lib/react-helpers.cjs');
5
+ const icons = require('./icons.cjs');
6
+ const textInput = require('../text-input/text-input.cjs');
7
+ const caption = require('../caption/caption.cjs');
8
+ const react = require('react');
9
+
10
+ function StringFilterInput({ className, accessibleName, placeholder, error, value, onChangeValue, }) {
11
+ const id = react.useId();
12
+ const alertId = `${id}alert`;
13
+ const hasError = Boolean(error);
14
+ return (jsxRuntime.jsxs("div", { className: reactHelpers.classNames('dc-string-filter-input', className), children: [jsxRuntime.jsx(icons.ArrowReturnRight, {}), jsxRuntime.jsx(textInput.TextInput, { "data-testid": "string-filter-input", size: "sm", isBlock: true, "aria-label": accessibleName, "aria-describedby": hasError ? alertId : '', placeholder: placeholder, type: "text", required: true, autoFocus: true, value: value, hasError: hasError, onChangeValue: onChangeValue }), hasError && (jsxRuntime.jsx(caption.Caption, { id: alertId, className: "dc-string-filter-input__error", appearance: "error", showIcon: true, role: "alert", children: error }))] }));
15
+ }
16
+
17
+ exports.StringFilterInput = StringFilterInput;
@@ -0,0 +1,68 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const stringFilter = require('./model/string-filter.cjs');
5
+ const react = require('react');
6
+ const useTranslations = require('./use-translations.cjs');
7
+ const popover = require('../popover/popover.cjs');
8
+ const filterToken = require('./filter-token.cjs');
9
+ const filterOperatorSelect = require('./filter-operator-select.cjs');
10
+ const stringFilterInput = require('./string-filter-input.cjs');
11
+ const button = require('../button/button.cjs');
12
+ require('../button/icon-button.cjs');
13
+
14
+ function StringFilterItem({ filter, isEditing, onEditStart, onEditCancel, onRemove, onChange, }) {
15
+ const translations = useTranslations.useTranslations();
16
+ const [filterOperator, setFilterOperator] = react.useState(filter.operator);
17
+ const [filterValue, setFilterValue] = react.useState(filter.value);
18
+ const [error, setError] = react.useState('');
19
+ const { label, operators, valueInputAccessibleName, valueInputPlaceholder, operatorSelectAccessibleName, valueValidator, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
20
+ const cancelEdit = () => {
21
+ onEditCancel(filter);
22
+ };
23
+ const startEdit = () => {
24
+ if (isEditing) {
25
+ cancelEdit();
26
+ }
27
+ else {
28
+ setFilterOperator(filter.operator);
29
+ setFilterValue(filter.value);
30
+ onEditStart(filter);
31
+ }
32
+ };
33
+ const onClickCloseButton = () => {
34
+ onRemove(filter);
35
+ };
36
+ const onSubmit = (event) => {
37
+ event.preventDefault();
38
+ event.stopPropagation();
39
+ if (typeof valueValidator === 'function') {
40
+ const result = valueValidator(filterValue);
41
+ if (!result.valid) {
42
+ return setError(result.error);
43
+ }
44
+ setError('');
45
+ }
46
+ if (filterOperator !== filter.operator) {
47
+ filter = filter.setOperator(filterOperator);
48
+ }
49
+ if (filterValue !== filter.value) {
50
+ filter = filter.setValue(filterValue);
51
+ }
52
+ onChange(filter);
53
+ };
54
+ const anchor = (jsxRuntime.jsxs(filterToken.FilterToken, { isHighlighted: isEditing, onClickLabel: startEdit, onClickCloseButton: onClickCloseButton, children: [label, filter.value ? (jsxRuntime.jsxs(jsxRuntime.Fragment, { children: ["\u00A0", jsxRuntime.jsx("span", { children: formatOperator(filter.operator) }), "\u00A0", jsxRuntime.jsx("b", { children: filter.value })] })) : null] }));
55
+ 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(stringFilterInput.StringFilterInput, { placeholder: valueInputPlaceholder, accessibleName: valueInputAccessibleName, error: error, value: filterValue, onChangeValue: setFilterValue }), 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, children: translations.applyButton })] })] }) }));
56
+ }
57
+ StringFilterItem.defaultOperatorFormatter = defaultOperatorFormatter;
58
+ function defaultOperatorFormatter(operator) {
59
+ const messages = {
60
+ [stringFilter.StringFilter.Operators.equal]: 'is',
61
+ [stringFilter.StringFilter.Operators.notEqual]: 'is not',
62
+ [stringFilter.StringFilter.Operators.contain]: 'contains',
63
+ [stringFilter.StringFilter.Operators.notContain]: "doesn't contain",
64
+ };
65
+ return messages[operator];
66
+ }
67
+
68
+ exports.StringFilterItem = StringFilterItem;
@@ -0,0 +1,77 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const stringSetFilter = require('./model/string-set-filter.cjs');
5
+ const react = require('react');
6
+ const useTranslations = require('./use-translations.cjs');
7
+ const popover = require('../popover/popover.cjs');
8
+ const filterToken = require('./filter-token.cjs');
9
+ const filterOperatorSelect = require('./filter-operator-select.cjs');
10
+ const filterValueList = require('./filter-value-list.cjs');
11
+ const button = require('../button/button.cjs');
12
+ require('../button/icon-button.cjs');
13
+
14
+ function StringSetFilterItem({ filter, isEditing, onEditStart, onEditCancel, onRemove, onChange, }) {
15
+ const translations = useTranslations.useTranslations();
16
+ const [filterOperator, setFilterOperator] = react.useState(filter.operator);
17
+ const [filterValue, setFilterValue] = react.useState(filter.value);
18
+ const { label, values, operators, operatorSelectAccessibleName, valueFormatter: formatValue = defaultValueFormatter, operatorFormatter: formatOperator = defaultOperatorFormatter, } = filter.config;
19
+ const cancelEdit = () => {
20
+ onEditCancel(filter);
21
+ };
22
+ const startEdit = () => {
23
+ if (isEditing) {
24
+ cancelEdit();
25
+ }
26
+ else {
27
+ setFilterOperator(filter.operator);
28
+ setFilterValue(filter.value);
29
+ onEditStart(filter);
30
+ }
31
+ };
32
+ const onClickCloseButton = () => {
33
+ onRemove(filter);
34
+ };
35
+ const onSubmit = (event) => {
36
+ event.preventDefault();
37
+ event.stopPropagation();
38
+ let changedFilter = filter;
39
+ if (filterOperator !== filter.operator) {
40
+ changedFilter = changedFilter.setOperator(filterOperator);
41
+ }
42
+ if (filterValue !== filter.value) {
43
+ changedFilter = changedFilter.setValue(filterValue);
44
+ }
45
+ onChange(changedFilter);
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] }));
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
+ }
50
+ StringSetFilterItem.defaultValueFormatter = defaultValueFormatter;
51
+ StringSetFilterItem.defaultOperatorFormatter = defaultOperatorFormatter;
52
+ StringSetFilterItem.formatFilterValue = formatFilterValue;
53
+ function defaultValueFormatter(value) {
54
+ return value[0].toUpperCase() + value.slice(1);
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) {
64
+ const list = values.map(defaultValueFormatter);
65
+ if (list.length <= 1) {
66
+ return list.toString();
67
+ }
68
+ if (list.length <= 2) {
69
+ return list.join(' or ');
70
+ }
71
+ if (list.length <= 3) {
72
+ return list.slice(0, -1).join(', ') + ', or ' + list.slice(-1);
73
+ }
74
+ return list.slice(0, 2).join(', ') + `, and ${list.length - 2} more`;
75
+ }
76
+
77
+ exports.StringSetFilterItem = StringSetFilterItem;
@@ -0,0 +1,17 @@
1
+ 'use strict';
2
+
3
+ const react = require('react');
4
+
5
+ function useComboboxIds() {
6
+ const id = react.useId();
7
+ const textBoxId = `${id}textBox`;
8
+ const listBoxId = `${id}listBox`;
9
+ const getOptionId = react.useCallback((key) => `${id}option[${key}]`, [id]);
10
+ return {
11
+ textBoxId,
12
+ listBoxId,
13
+ getOptionId,
14
+ };
15
+ }
16
+
17
+ exports.useComboboxIds = useComboboxIds;
@@ -0,0 +1,24 @@
1
+ 'use strict';
2
+
3
+ const jsxRuntime = require('react/jsx-runtime');
4
+ const react = require('react');
5
+
6
+ const Context = react.createContext(null);
7
+ function useTranslations() {
8
+ const ctx = react.useContext(Context);
9
+ if (ctx == null) {
10
+ throw new Error('useTranslations must be used within TranslationsProvider');
11
+ }
12
+ return ctx;
13
+ }
14
+ function TranslationsProvider({ applyButton, cancelButton, removeFilterButton, children, }) {
15
+ const translations = react.useMemo(() => ({
16
+ applyButton,
17
+ cancelButton,
18
+ removeFilterButton,
19
+ }), [applyButton, cancelButton, removeFilterButton]);
20
+ return (jsxRuntime.jsx(Context.Provider, { value: translations, children: children }));
21
+ }
22
+
23
+ exports.TranslationsProvider = TranslationsProvider;
24
+ exports.useTranslations = useTranslations;