@bbl-digital/snorre 3.0.3 → 3.0.7

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 (35) hide show
  1. package/dist/bundle.js +208 -100
  2. package/esm/core/Autocomplete/hooks/useAutocomplete.js +242 -0
  3. package/esm/core/Autocomplete/hooks/useHandleDimentions.js +19 -0
  4. package/esm/core/Autocomplete/index.d.js +1 -0
  5. package/esm/core/Autocomplete/index.interfaces.js +1 -0
  6. package/esm/core/Autocomplete/index.js +50 -184
  7. package/esm/core/Autocomplete/styles.js +30 -25
  8. package/esm/core/DatepickerRange/YearMonthForm.js +56 -0
  9. package/esm/core/Radio/styles.js +4 -4
  10. package/lib/core/Autocomplete/hooks/useAutocomplete.d.ts +21 -0
  11. package/lib/core/Autocomplete/hooks/useAutocomplete.d.ts.map +1 -0
  12. package/lib/core/Autocomplete/hooks/useAutocomplete.js +242 -0
  13. package/lib/core/Autocomplete/hooks/useHandleDimentions.d.ts +8 -0
  14. package/lib/core/Autocomplete/hooks/useHandleDimentions.d.ts.map +1 -0
  15. package/lib/core/Autocomplete/hooks/useHandleDimentions.js +19 -0
  16. package/lib/core/Autocomplete/index.d.js +1 -0
  17. package/lib/core/Autocomplete/index.d.ts +2 -74
  18. package/lib/core/Autocomplete/index.d.ts.map +1 -1
  19. package/lib/core/Autocomplete/index.interfaces.d.ts +80 -0
  20. package/lib/core/Autocomplete/index.interfaces.d.ts.map +1 -0
  21. package/lib/core/Autocomplete/index.interfaces.js +1 -0
  22. package/lib/core/Autocomplete/index.js +50 -184
  23. package/lib/core/Autocomplete/styles.d.ts +18 -1
  24. package/lib/core/Autocomplete/styles.d.ts.map +1 -1
  25. package/lib/core/Autocomplete/styles.js +30 -25
  26. package/lib/core/DatepickerRange/YearMonthForm.d.ts +10 -0
  27. package/lib/core/DatepickerRange/YearMonthForm.d.ts.map +1 -0
  28. package/lib/core/DatepickerRange/YearMonthForm.js +56 -0
  29. package/lib/core/Radio/styles.d.ts.map +1 -1
  30. package/lib/core/Radio/styles.js +4 -4
  31. package/package.json +2 -1
  32. package/esm/enums/ModifierKey.js +0 -13
  33. package/lib/enums/ModifierKey.d.ts +0 -12
  34. package/lib/enums/ModifierKey.d.ts.map +0 -1
  35. package/lib/enums/ModifierKey.js +0 -13
@@ -0,0 +1,242 @@
1
+ /* eslint-disable react-hooks/exhaustive-deps */
2
+ import { matchSorter } from 'match-sorter';
3
+ import { useEffect, useMemo, useState } from 'react';
4
+ import { useDebounce } from '../../utils/debounce';
5
+
6
+ const useAutocomplete = props => {
7
+ const [showValues, setShowValues] = useState(!!props.isOpen);
8
+ const [highlightedIndex, setHighlightedIndex] = useState(null);
9
+ const [value, setValueChanged] = useState(props.value ?? '');
10
+ const [inputDirty, setInputDirty] = useState(false);
11
+ const debouncedValue = useDebounce(value, props.debounceDelay ?? 0);
12
+ const [inputValues, setInputValues] = useState(props.values);
13
+
14
+ const handleValueClick = value => {
15
+ props.onSelectItem?.(value);
16
+ setShowValues(false);
17
+ };
18
+
19
+ const clearSelectedItem = () => handleValueClick({
20
+ [props.labelFromValues ?? 'label']: '',
21
+ [props.keyFromValues ?? 'key']: ''
22
+ });
23
+
24
+ const onInputChange = e => {
25
+ setInputDirty(true);
26
+ if (inputValues) setHighlightedIndex(0);
27
+ setValueChanged(e.target.value); // Should not use onChange whenever we have fuzzy search on
28
+
29
+ if (props.fuzzy) return;
30
+ if (props.onChange) props.onChange(e);
31
+ };
32
+
33
+ const handleOnInputClick = () => {
34
+ if (inputValues?.length) setHighlightedIndex(0);
35
+ setShowValues(true);
36
+ };
37
+
38
+ const onFuzzyBlur = e => {
39
+ // If the value of the input is changed, and does not match the value of the values array,
40
+ // we should revert back the input value to the original value
41
+ //What
42
+ // if (!e.target.value.length) clearSelectedItem()
43
+ if (e.target.value === props.value) return; // If target value is the same as a value from the values array, we should set the value for the user
44
+
45
+ if (inputValues?.length) {
46
+ const valueInInputValues = inputValues.find(item => item[props.labelFromValues ?? 'label'].length && item[props.labelFromValues ?? 'label'].toLowerCase() === e.target.value.toLowerCase());
47
+
48
+ if (valueInInputValues) {
49
+ const val = valueInInputValues[props.labelFromValues ?? 'label'];
50
+ setValueChanged(val);
51
+ handleValueClick(valueInInputValues);
52
+ return;
53
+ } // Otherwise we should return to the original input value
54
+
55
+
56
+ setValueChanged(props.value ?? '');
57
+ }
58
+ };
59
+
60
+ const onFuzzyFocus = () => {
61
+ setValueChanged('');
62
+ if (props.values?.length) setShowValues(true);
63
+ };
64
+
65
+ const renderedValues = useMemo(() => {
66
+ if (!props.values?.length) return [];
67
+
68
+ if (props.fuzzy) {
69
+ const fuzzyOptions = {
70
+ keys: [props.labelFromValues ?? 'label']
71
+ };
72
+ setShowValues(Boolean(value));
73
+ return matchSorter(props.values, value ?? '', fuzzyOptions);
74
+ }
75
+
76
+ return props.values;
77
+ }, [value]);
78
+
79
+ const handleEscape = () => {
80
+ setShowValues(false);
81
+ setHighlightedIndex(null);
82
+ };
83
+
84
+ const handleClear = () => {
85
+ clearSelectedItem();
86
+ handleEscape();
87
+ };
88
+
89
+ const handleEnter = e => {
90
+ e.preventDefault();
91
+ e.stopPropagation();
92
+ if (!showValues) return;
93
+
94
+ if (!inputValues?.length) {
95
+ // If there is no autocomplete value in the list, send object with just { value }
96
+ handleValueClick({
97
+ value
98
+ });
99
+ }
100
+
101
+ if (highlightedIndex === null || !inputValues?.length) {
102
+ return;
103
+ }
104
+
105
+ const item = inputValues[highlightedIndex];
106
+ handleValueClick(item);
107
+ setShowValues(false);
108
+ };
109
+
110
+ const handleUp = e => {
111
+ e.preventDefault();
112
+ e.stopPropagation();
113
+ if (!showValues) return;
114
+
115
+ if (highlightedIndex === null || !inputValues?.length) {
116
+ return;
117
+ }
118
+
119
+ const newHighlightIndex = highlightedIndex - 1;
120
+
121
+ if (newHighlightIndex < 0) {
122
+ setHighlightedIndex(null);
123
+ setShowValues(false);
124
+ return;
125
+ }
126
+
127
+ setHighlightedIndex(newHighlightIndex);
128
+ };
129
+
130
+ const handleDown = e => {
131
+ if (!showValues) return;
132
+ e.preventDefault();
133
+ e.stopPropagation();
134
+
135
+ if (highlightedIndex === null || !inputValues?.length) {
136
+ return;
137
+ }
138
+
139
+ const newHighlightIndex = highlightedIndex + 1;
140
+
141
+ if (newHighlightIndex === inputValues.length) {
142
+ setHighlightedIndex(null);
143
+ setShowValues(false);
144
+ return;
145
+ }
146
+
147
+ setHighlightedIndex(newHighlightIndex);
148
+ };
149
+
150
+ const handleCustomOnKeyDown = e => {
151
+ const {
152
+ key
153
+ } = e;
154
+
155
+ if (key === 'Escape') {
156
+ handleEscape();
157
+ return;
158
+ }
159
+
160
+ props.onKeyDown?.(e);
161
+ }; // Makes it possible to navigate using keyboard shortcuts
162
+
163
+
164
+ const handleOnKeyDown = e => {
165
+ const {
166
+ key
167
+ } = e;
168
+
169
+ if (showValues && inputValues?.length) {
170
+ setHighlightedIndex(0);
171
+ } else {
172
+ setHighlightedIndex(null);
173
+ }
174
+
175
+ switch (key) {
176
+ case 'Enter':
177
+ handleEnter(e);
178
+ break;
179
+
180
+ case 'ArrowUp':
181
+ handleUp(e);
182
+ break;
183
+
184
+ case 'ArrowDown':
185
+ case 'Tab':
186
+ handleDown(e);
187
+ break;
188
+
189
+ case 'Escape':
190
+ handleEscape();
191
+ break;
192
+
193
+ default:
194
+ break;
195
+ }
196
+ }; // Handle debounce
197
+
198
+
199
+ useEffect(() => {
200
+ const handleDebounceChange = value => {
201
+ if (props.onDebounceChange && inputDirty) {
202
+ props.onDebounceChange(value);
203
+ }
204
+ };
205
+
206
+ handleDebounceChange(debouncedValue);
207
+ }, [debouncedValue]); // Change local open state if props.isOpen changes
208
+
209
+ useEffect(() => {
210
+ setShowValues(!!props.isOpen);
211
+ }, [props.isOpen]); // Update local values state if props.values changes
212
+
213
+ useEffect(() => {
214
+ setInputValues(props.values);
215
+ }, [props.values]); // Update local value if props.value changes
216
+
217
+ useEffect(() => {
218
+ const value = props.value ?? '';
219
+ setValueChanged(value);
220
+ }, [props.value]);
221
+ useEffect(() => {
222
+ if (!props.openCustomValueInputOnKeyPress) return;
223
+ if (value.length) setShowValues(true);
224
+ }, [value]);
225
+ return {
226
+ setShowValues,
227
+ handleClear,
228
+ handleValueClick,
229
+ onInputChange,
230
+ handleOnKeyDown,
231
+ handleCustomOnKeyDown,
232
+ handleOnInputClick,
233
+ onFuzzyBlur,
234
+ onFuzzyFocus,
235
+ value,
236
+ highlightedIndex,
237
+ showValues,
238
+ renderedValues
239
+ };
240
+ };
241
+
242
+ export default useAutocomplete;
@@ -0,0 +1,19 @@
1
+ import { createRef, useEffect, useState } from 'react';
2
+
3
+ const useHandleDimentions = () => {
4
+ const [height, setHeight] = useState(0);
5
+ const [width, setWidth] = useState(0);
6
+ const dimentionsRef = /*#__PURE__*/createRef();
7
+ useEffect(() => {
8
+ const rect = dimentionsRef.current?.getBoundingClientRect();
9
+ setHeight(rect?.height || 0);
10
+ setWidth(rect?.width || 0); // eslint-disable-next-line react-hooks/exhaustive-deps
11
+ }, [dimentionsRef.current]);
12
+ return {
13
+ height,
14
+ width,
15
+ dimentionsRef
16
+ };
17
+ };
18
+
19
+ export default useHandleDimentions;
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1 @@
1
+ export {};
@@ -1,192 +1,49 @@
1
1
  /** @jsxImportSource @emotion/react */
2
- import React, { createRef, useState } from 'react';
3
- import { styles, ErrorMessage } from './styles';
2
+ import React, { createRef } from 'react';
3
+ import { styles, ErrorMessage, Clear } from './styles';
4
4
  import { useEffect } from 'react';
5
- import { useDebounce } from '../utils/debounce';
6
5
  import IconErrorOutline from '../../icons/General/IconErrorOutline';
7
6
  import Spinner from '../Spinner';
8
- import useHandleOptionsHeight from './utils/useHandleOptionsHeight';
7
+ import useHandleDimentions from './hooks/useHandleDimentions';
9
8
  import Link from '../Link';
9
+ import IconClose from '../../icons/General/IconClose';
10
+ import useAutocomplete from './hooks/useAutocomplete';
10
11
  import { jsx as _jsx } from "@emotion/react/jsx-runtime";
11
12
  import { jsxs as _jsxs } from "@emotion/react/jsx-runtime";
12
13
  import { Fragment as _Fragment } from "@emotion/react/jsx-runtime";
13
14
  const Autocomplete = /*#__PURE__*/React.forwardRef(({
14
- onDebounceChange,
15
- debounceDelay,
16
15
  height,
17
16
  css,
18
17
  ...props
19
18
  }, ref) => {
20
- const {
21
- optionsHeight,
22
- optionsRef
23
- } = useHandleOptionsHeight();
24
19
  const valuesRef = /*#__PURE__*/createRef();
25
- const [showValues, setShowValues] = useState(!!props.isOpen);
26
- const [highlightedIndex, setHighlightedIndex] = useState(null);
27
- const [value, setValueChanged] = useState(props.value ? props.value : '');
28
- const [inputDirty, setInputDirty] = useState(false);
29
- const debouncedValue = useDebounce(value, debounceDelay ? debounceDelay : 0);
30
-
31
- const onInputChange = e => {
32
- setInputDirty(true);
33
- setValueChanged(e.target.value);
34
- if (props.values) setHighlightedIndex(0);
35
- if (props.onChange) props.onChange(e);
36
- };
37
-
38
- const handleOnInputClick = () => {
39
- if (props.values?.length) setHighlightedIndex(0);
40
- setShowValues(true);
41
- };
42
-
43
- const handleValueClick = value => {
44
- setShowValues(false);
45
- props.onSelectItem?.(value);
46
- };
47
-
48
- const handleEnter = e => {
49
- e.preventDefault();
50
- e.stopPropagation();
51
- if (!showValues) return;
52
-
53
- if (!props.values?.length) {
54
- // If there is no autocomplete value in the list, send object with just { value }
55
- props.onSelectItem?.({
56
- value
57
- });
58
- }
59
-
60
- if (highlightedIndex === null || !props.values?.length) {
61
- return;
62
- }
63
-
64
- const item = props.values[highlightedIndex];
65
- props.onSelectItem?.(item);
66
- setShowValues(false);
67
- };
68
-
69
- const handleUp = e => {
70
- e.preventDefault();
71
- e.stopPropagation();
72
- if (!showValues) return;
73
-
74
- if (highlightedIndex === null || !props.values?.length) {
75
- return;
76
- }
77
-
78
- const newHighlightIndex = highlightedIndex - 1;
79
-
80
- if (newHighlightIndex < 0) {
81
- setHighlightedIndex(null);
82
- setShowValues(false);
83
- return;
84
- }
85
-
86
- setHighlightedIndex(newHighlightIndex);
87
- };
88
-
89
- const handleDown = e => {
90
- if (!showValues) return;
91
- e.preventDefault();
92
- e.stopPropagation();
93
-
94
- if (highlightedIndex === null || !props.values?.length) {
95
- return;
96
- }
97
-
98
- const newHighlightIndex = highlightedIndex + 1;
99
-
100
- if (newHighlightIndex === props.values.length) {
101
- setHighlightedIndex(null);
102
- setShowValues(false);
103
- return;
104
- }
105
-
106
- setHighlightedIndex(newHighlightIndex);
107
- };
108
-
109
- const handleEscape = () => {
110
- setShowValues(false);
111
- setHighlightedIndex(null);
112
- };
113
-
114
- const handleCustomOnKeyDown = e => {
115
- const {
116
- key
117
- } = e;
118
-
119
- if (key === 'Escape') {
120
- handleEscape();
121
- return;
122
- }
123
-
124
- props.onKeyDown?.(e);
125
- };
126
-
127
- const handleOnKeyDown = e => {
128
- const {
129
- key
130
- } = e;
131
-
132
- if (showValues && props.values?.length) {
133
- setHighlightedIndex(0);
134
- } else {
135
- setHighlightedIndex(null);
136
- }
137
-
138
- switch (key) {
139
- case 'Enter':
140
- handleEnter(e);
141
- break;
142
-
143
- case 'ArrowUp':
144
- handleUp(e);
145
- break;
146
-
147
- case 'ArrowDown':
148
- case 'Tab':
149
- handleDown(e);
150
- break;
151
-
152
- case 'Escape':
153
- handleEscape();
154
- break;
155
-
156
- default:
157
- break;
158
- }
159
- }; // Handle debounce
160
-
161
-
162
- useEffect(() => {
163
- const handleDebounceChange = value => {
164
- if (onDebounceChange && inputDirty) {
165
- onDebounceChange(value);
166
- }
167
- };
168
-
169
- handleDebounceChange(debouncedValue); // eslint-disable-next-line react-hooks/exhaustive-deps
170
- }, [debouncedValue]);
171
- useEffect(() => {
172
- setShowValues(!!props.isOpen);
173
- }, [props.isOpen]);
174
- useEffect(() => {
175
- const value = props.value ? props.value : '';
176
- setValueChanged(value);
177
- }, [props.value]);
178
- useEffect(() => {
179
- if (!props.openCustomValueInputOnKeyPress) return;
180
- if (value.length) setShowValues(true); // eslint-disable-next-line react-hooks/exhaustive-deps
181
- }, [value]);
20
+ const {
21
+ height: optionsHeight,
22
+ dimentionsRef: optionsRef
23
+ } = useHandleDimentions();
24
+ const {
25
+ width: inputWrapperWidth,
26
+ dimentionsRef: inputWrapperRef
27
+ } = useHandleDimentions();
28
+ const {
29
+ value,
30
+ highlightedIndex,
31
+ showValues,
32
+ renderedValues,
33
+ setShowValues,
34
+ handleClear,
35
+ handleOnInputClick,
36
+ handleCustomOnKeyDown,
37
+ handleOnKeyDown,
38
+ handleValueClick,
39
+ onInputChange,
40
+ onFuzzyBlur,
41
+ onFuzzyFocus
42
+ } = useAutocomplete(props);
182
43
  useEffect(() => {
183
44
  const handleClickOutside = e => {
184
45
  const node = valuesRef.current;
185
-
186
- if (node && node.contains(e.target)) {
187
- return;
188
- }
189
-
46
+ if (node && node.contains(e.target)) return;
190
47
  setShowValues(false);
191
48
  };
192
49
 
@@ -198,10 +55,11 @@ const Autocomplete = /*#__PURE__*/React.forwardRef(({
198
55
 
199
56
  return () => {
200
57
  document.removeEventListener('mousedown', handleClickOutside);
201
- };
58
+ }; // eslint-disable-next-line react-hooks/exhaustive-deps
202
59
  }, [showValues, valuesRef]);
203
60
  return _jsxs(_Fragment, {
204
61
  children: [_jsxs("label", {
62
+ ref: inputWrapperRef,
205
63
  css: theme => [styles.default(theme), (props.invalid || props.invalidMessage) && styles.invalid(theme), props.validation && styles.validation, props.onLabelClick && styles.clickableLabel, height && styles.height(height), css && css],
206
64
  onClick: props.onLabelClick ? e => e.preventDefault() : undefined,
207
65
  children: [_jsxs("span", {
@@ -224,8 +82,8 @@ const Autocomplete = /*#__PURE__*/React.forwardRef(({
224
82
  value: value,
225
83
  disabled: props.disabled,
226
84
  autoFocus: props.focus,
227
- onBlur: props.onBlur,
228
- onFocus: props.onFocus,
85
+ onBlur: props.fuzzy ? onFuzzyBlur : props.onBlur,
86
+ onFocus: props.fuzzy ? onFuzzyFocus : props.onFocus,
229
87
  onChange: onInputChange,
230
88
  onKeyDown: props.onKeyDown ? handleCustomOnKeyDown : handleOnKeyDown,
231
89
  onClick: handleOnInputClick,
@@ -234,6 +92,12 @@ const Autocomplete = /*#__PURE__*/React.forwardRef(({
234
92
  autoComplete: "off",
235
93
  css: theme => [props.disabled && styles.disabled(theme)],
236
94
  children: React.Children.map(props.children, child => child || null)
95
+ }), props.clear && _jsx(Clear, {
96
+ nostyle: true,
97
+ onClick: handleClear,
98
+ children: _jsx(IconClose, {
99
+ size: 14
100
+ })
237
101
  }), props.loading && _jsx(Spinner, {
238
102
  size: 14
239
103
  }), props.actions && _jsx("div", {
@@ -253,17 +117,19 @@ const Autocomplete = /*#__PURE__*/React.forwardRef(({
253
117
  children: props.invalidMessage
254
118
  })]
255
119
  })]
256
- }), (props.values?.length || props.renderCustomValueInput) && !props.loading && showValues && _jsxs("div", {
120
+ }), (Boolean(props.values?.length) || props.renderCustomValueInput) && !props.loading && showValues && _jsxs("div", {
257
121
  ref: valuesRef,
258
- css: () => [!props.renderCustomValueInput && styles.listWrapper, props.dynamicallyPlaceInput && Boolean(optionsHeight) && styles.listWrapperTopPosition(optionsHeight), props.dynamicallyPlaceInput && Boolean(optionsHeight) && props.invalidMessage && styles.listWrapperTopPosition(optionsHeight + 30), props.inputValuesMaxWidth && styles.listWrapperMaxWidth(props.inputValuesMaxWidth)],
259
- children: [props.values?.length && _jsx("ul", {
122
+ css: () => [!props.renderCustomValueInput && styles.listWrapper(inputWrapperWidth), props.dynamicallyPlaceInput && Boolean(optionsHeight) && styles.listWrapperTopPosition(optionsHeight), props.dynamicallyPlaceInput && Boolean(optionsHeight) && props.invalidMessage && styles.listWrapperTopPosition(optionsHeight + 30), props.inputValuesMaxWidth && styles.listWrapperMaxWidth(props.inputValuesMaxWidth)],
123
+ children: [Boolean(renderedValues?.length) && _jsx("ul", {
260
124
  css: theme => [styles.list(theme)],
261
- children: props.values.map((item, index) => _jsx("li", {
262
- tabIndex: 0,
263
- css: index === highlightedIndex ? styles.highlightedItem : null,
264
- onClick: () => handleValueClick(item),
265
- children: props.labelFromValues ? item[props.labelFromValues] : item.label
266
- }, props.keyFromValues ? item[props.keyFromValues] : item.key))
125
+ children: renderedValues.map((item, index) => {
126
+ return _jsx("li", {
127
+ tabIndex: 0,
128
+ css: index === highlightedIndex ? styles.highlightedItem : null,
129
+ onClick: () => handleValueClick(item),
130
+ children: props.labelFromValues ? item[props.labelFromValues] : item.label
131
+ }, props.keyFromValues ? item[props.keyFromValues] : item.key);
132
+ })
267
133
  }), props.renderCustomValueInput && _jsx("div", {
268
134
  children: props.renderCustomValueInput
269
135
  })]