@simplybusiness/mobius 5.11.0 → 5.12.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.
- package/CHANGELOG.md +18 -0
- package/dist/cjs/components/AddressLookup/AddressLookup.js +11 -3
- package/dist/cjs/components/AddressLookup/AddressLookup.js.map +1 -1
- package/dist/cjs/components/Combobox/Combobox.js +30 -6
- package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
- package/dist/cjs/components/Combobox/Listbox.js +8 -5
- package/dist/cjs/components/Combobox/Listbox.js.map +1 -1
- package/dist/cjs/components/Combobox/Option.js +2 -2
- package/dist/cjs/components/Combobox/Option.js.map +1 -1
- package/dist/cjs/components/Combobox/useComboboxOptions.js +10 -4
- package/dist/cjs/components/Combobox/useComboboxOptions.js.map +1 -1
- package/dist/cjs/components/Combobox/utils.js.map +1 -1
- package/dist/cjs/components/TextField/TextField.js +4 -4
- package/dist/cjs/components/TextField/TextField.js.map +1 -1
- package/dist/cjs/components/TextField/adornmentWithClassName.js +2 -2
- package/dist/cjs/components/TextField/adornmentWithClassName.js.map +1 -1
- package/dist/cjs/tsconfig.tsbuildinfo +1 -1
- package/dist/esm/components/AddressLookup/AddressLookup.js +12 -4
- package/dist/esm/components/AddressLookup/AddressLookup.js.map +1 -1
- package/dist/esm/components/AddressLookup/types.js.map +1 -1
- package/dist/esm/components/Combobox/Combobox.js +30 -6
- package/dist/esm/components/Combobox/Combobox.js.map +1 -1
- package/dist/esm/components/Combobox/Listbox.js +8 -5
- package/dist/esm/components/Combobox/Listbox.js.map +1 -1
- package/dist/esm/components/Combobox/Option.js +2 -2
- package/dist/esm/components/Combobox/Option.js.map +1 -1
- package/dist/esm/components/Combobox/types.js.map +1 -1
- package/dist/esm/components/Combobox/useComboboxOptions.js +10 -4
- package/dist/esm/components/Combobox/useComboboxOptions.js.map +1 -1
- package/dist/esm/components/Combobox/utils.js.map +1 -1
- package/dist/esm/components/TextField/TextField.js +4 -4
- package/dist/esm/components/TextField/TextField.js.map +1 -1
- package/dist/esm/components/TextField/adornmentWithClassName.js +2 -2
- package/dist/esm/components/TextField/adornmentWithClassName.js.map +1 -1
- package/dist/types/components/AddressLookup/AddressLookup.d.ts +3 -1
- package/dist/types/components/AddressLookup/types.d.ts +2 -1
- package/dist/types/components/Combobox/Combobox.d.ts +4 -3
- package/dist/types/components/Combobox/Combobox.stories.d.ts +82 -9
- package/dist/types/components/Combobox/Listbox.d.ts +4 -4
- package/dist/types/components/Combobox/Option.d.ts +2 -2
- package/dist/types/components/Combobox/types.d.ts +19 -20
- package/dist/types/components/Combobox/useComboboxOptions.d.ts +6 -5
- package/dist/types/components/Combobox/utils.d.ts +2 -2
- package/dist/types/components/TextField/adornmentWithClassName.d.ts +1 -1
- package/package.json +15 -15
- package/src/components/AddressLookup/AddressLookup.test.tsx +106 -0
- package/src/components/AddressLookup/AddressLookup.tsx +61 -40
- package/src/components/AddressLookup/__mocks__/LoqateAddressLookupService.tsx +0 -2
- package/src/components/AddressLookup/types.tsx +2 -1
- package/src/components/Combobox/Combobox.css +6 -2
- package/src/components/Combobox/Combobox.stories.tsx +38 -16
- package/src/components/Combobox/Combobox.test.tsx +10 -5
- package/src/components/Combobox/Combobox.tsx +225 -202
- package/src/components/Combobox/Listbox.tsx +24 -10
- package/src/components/Combobox/Option.tsx +4 -6
- package/src/components/Combobox/types.tsx +49 -42
- package/src/components/Combobox/useComboboxOptions.test.ts +4 -5
- package/src/components/Combobox/useComboboxOptions.ts +26 -10
- package/src/components/Combobox/utils.test.tsx +10 -12
- package/src/components/Combobox/utils.tsx +6 -6
- package/src/components/TextField/TextField.css +4 -1
- package/src/components/TextField/TextField.stories.tsx +2 -0
- package/src/components/TextField/TextField.tsx +4 -0
- package/src/components/TextField/adornmentWithClassName.ts +6 -1
- package/src/public-whitelist.test.ts +3 -1
- package/src/yarnpath.test.ts +2 -2
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 5.12.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- 9f9cbc5: Save Address selected into state (Questionnaire)
|
|
8
|
+
|
|
9
|
+
### Patch Changes
|
|
10
|
+
|
|
11
|
+
- 4984b57: Add error handling for `AddressLookup` component
|
|
12
|
+
- 1fa9b7e: Package upgrades
|
|
13
|
+
- b3b961b: Fix icon colour in TextField
|
|
14
|
+
|
|
15
|
+
## 5.11.1
|
|
16
|
+
|
|
17
|
+
### Patch Changes
|
|
18
|
+
|
|
19
|
+
- 902cee3: Combobox and AddressLookup behaviour improvements
|
|
20
|
+
|
|
3
21
|
## 5.11.0
|
|
4
22
|
|
|
5
23
|
### Minor Changes
|
|
@@ -32,11 +32,18 @@ function optionsFromResponse({ Items }, addressLookupService) {
|
|
|
32
32
|
}
|
|
33
33
|
}));
|
|
34
34
|
}
|
|
35
|
-
|
|
36
|
-
const [error,
|
|
35
|
+
const AddressLookup = /*#__PURE__*/ (0, _react.forwardRef)(({ addressLookupService, onAddressSelected, onError, errorMessage, ...otherProps }, ref)=>{
|
|
36
|
+
const [error, _setError] = (0, _react.useState)(null);
|
|
37
|
+
const setError = (0, _react.useCallback)((newError)=>{
|
|
38
|
+
if (newError != null) onError === null || onError === void 0 ? void 0 : onError(newError);
|
|
39
|
+
_setError(newError);
|
|
40
|
+
}, [
|
|
41
|
+
onError
|
|
42
|
+
]);
|
|
37
43
|
const asyncOptions = (0, _react.useCallback)(async (searchTerm)=>{
|
|
38
44
|
try {
|
|
39
45
|
const response = await addressLookupService.search(searchTerm);
|
|
46
|
+
setError(null);
|
|
40
47
|
return optionsFromResponse(response, addressLookupService);
|
|
41
48
|
} catch (e) {
|
|
42
49
|
setError(e);
|
|
@@ -54,6 +61,7 @@ function AddressLookup({ addressLookupService, onAddressSelected, errorMessage,
|
|
|
54
61
|
const realErrorMessage = error && "An error occurred" || errorMessage;
|
|
55
62
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)(_Combobox.Combobox, {
|
|
56
63
|
...otherProps,
|
|
64
|
+
ref: ref,
|
|
57
65
|
onSelected: handleSelected,
|
|
58
66
|
asyncOptions: asyncOptions,
|
|
59
67
|
errorMessage: realErrorMessage,
|
|
@@ -61,6 +69,6 @@ function AddressLookup({ addressLookupService, onAddressSelected, errorMessage,
|
|
|
61
69
|
icon: _icons.search
|
|
62
70
|
})
|
|
63
71
|
});
|
|
64
|
-
}
|
|
72
|
+
});
|
|
65
73
|
|
|
66
74
|
//# sourceMappingURL=AddressLookup.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/AddressLookup/AddressLookup.tsx"],"sourcesContent":["import { search } from \"@simplybusiness/icons\";\nimport { useCallback, useState } from \"react\";\nimport { Combobox } from \"../Combobox\";\nimport { Icon } from \"../Icon\";\nimport { LoqateAddressLookupService } from \"./LoqateAddressLookupService\";\nimport type {\n AddressError,\n AddressLookupProps,\n FindAddressResponse,\n FindAddressResult,\n} from \"./types\";\n\nfunction optionsFromResponse(\n { Items }: FindAddressResponse,\n addressLookupService: LoqateAddressLookupService,\n) {\n if (!Items || !Array.isArray(Items)) {\n throw Error(\"No address found\");\n }\n\n if ((Items[0] as unknown as AddressError).Error) {\n throw Error(Items[0].Description);\n }\n\n return (Items as FindAddressResult[]).map(item => ({\n id: item.Id,\n label: `${item.Text}, ${item.Description}`,\n value: item.Text,\n // Add a callback to trigger secondary search\n // if the address type is not \"Address\"\n callback:\n item.Type === \"Address\"\n ? undefined\n : async () => {\n const result = await addressLookupService.findById(item.Id);\n return optionsFromResponse(result, addressLookupService);\n },\n }));\n}\n\nexport
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/AddressLookup/AddressLookup.tsx"],"sourcesContent":["import { search } from \"@simplybusiness/icons\";\nimport { forwardRef, useCallback, useState } from \"react\";\nimport { Combobox } from \"../Combobox\";\nimport type { ComboboxRef, ComboboxElementType } from \"../Combobox\";\nimport { Icon } from \"../Icon\";\nimport { LoqateAddressLookupService } from \"./LoqateAddressLookupService\";\nimport type { ForwardedRefComponent } from \"../../types\";\nimport type {\n AddressError,\n AddressLookupProps,\n FindAddressResponse,\n FindAddressResult,\n} from \"./types\";\n\nfunction optionsFromResponse(\n { Items }: FindAddressResponse,\n addressLookupService: LoqateAddressLookupService,\n) {\n if (!Items || !Array.isArray(Items)) {\n throw Error(\"No address found\");\n }\n\n if ((Items[0] as unknown as AddressError).Error) {\n throw Error(Items[0].Description);\n }\n\n return (Items as FindAddressResult[]).map(item => ({\n id: item.Id,\n label: `${item.Text}, ${item.Description}`,\n value: item.Text,\n // Add a callback to trigger secondary search\n // if the address type is not \"Address\"\n callback:\n item.Type === \"Address\"\n ? undefined\n : async () => {\n const result = await addressLookupService.findById(item.Id);\n return optionsFromResponse(result, addressLookupService);\n },\n }));\n}\n\nexport const AddressLookup: ForwardedRefComponent<\n AddressLookupProps,\n ComboboxElementType\n> = forwardRef(\n (\n {\n addressLookupService,\n onAddressSelected,\n onError,\n errorMessage,\n ...otherProps\n }: AddressLookupProps,\n ref: ComboboxRef,\n ) => {\n const [error, _setError] = useState<Error | null>(null);\n\n const setError = useCallback(\n (newError: Error | null) => {\n if (newError != null) onError?.(newError);\n _setError(newError);\n },\n [onError],\n );\n\n const asyncOptions = useCallback(\n async (searchTerm: string) => {\n try {\n const response = await addressLookupService.search(searchTerm);\n setError(null);\n return optionsFromResponse(response, addressLookupService);\n } catch (e) {\n setError(e as Error);\n return [];\n }\n },\n [addressLookupService, setError],\n );\n\n const handleSelected = (selected: unknown) => {\n setError(null);\n return (\n addressLookupService\n // @ts-expect-error - Fix types\n .get(selected.id)\n .then(onAddressSelected)\n .catch(setError)\n );\n };\n\n const realErrorMessage = (error && \"An error occurred\") || errorMessage;\n\n return (\n <Combobox\n {...otherProps}\n ref={ref}\n onSelected={handleSelected}\n asyncOptions={asyncOptions}\n errorMessage={realErrorMessage}\n icon={<Icon icon={search} />}\n />\n );\n },\n);\n"],"names":["AddressLookup","optionsFromResponse","Items","addressLookupService","Array","isArray","Error","Description","map","item","id","Id","label","Text","value","callback","Type","undefined","result","findById","forwardRef","onAddressSelected","onError","errorMessage","otherProps","ref","error","_setError","useState","setError","useCallback","newError","asyncOptions","searchTerm","response","search","e","handleSelected","selected","get","then","catch","realErrorMessage","Combobox","onSelected","icon","Icon"],"mappings":";;;;+BA0CaA;;;eAAAA;;;;uBA1CU;uBAC2B;0BACzB;sBAEJ;AAUrB,SAASC,oBACP,EAAEC,KAAK,EAAuB,EAC9BC,oBAAgD;IAEhD,IAAI,CAACD,SAAS,CAACE,MAAMC,OAAO,CAACH,QAAQ;QACnC,MAAMI,MAAM;IACd;IAEA,IAAI,AAACJ,KAAK,CAAC,EAAE,CAA6BI,KAAK,EAAE;QAC/C,MAAMA,MAAMJ,KAAK,CAAC,EAAE,CAACK,WAAW;IAClC;IAEA,OAAO,AAACL,MAA8BM,GAAG,CAACC,CAAAA,OAAS,CAAA;YACjDC,IAAID,KAAKE,EAAE;YACXC,OAAO,GAAGH,KAAKI,IAAI,CAAC,EAAE,EAAEJ,KAAKF,WAAW,EAAE;YAC1CO,OAAOL,KAAKI,IAAI;YAChB,6CAA6C;YAC7C,uCAAuC;YACvCE,UACEN,KAAKO,IAAI,KAAK,YACVC,YACA;gBACE,MAAMC,SAAS,MAAMf,qBAAqBgB,QAAQ,CAACV,KAAKE,EAAE;gBAC1D,OAAOV,oBAAoBiB,QAAQf;YACrC;QACR,CAAA;AACF;AAEO,MAAMH,8BAGToB,IAAAA,iBAAU,EACZ,CACE,EACEjB,oBAAoB,EACpBkB,iBAAiB,EACjBC,OAAO,EACPC,YAAY,EACZ,GAAGC,YACgB,EACrBC;IAEA,MAAM,CAACC,OAAOC,UAAU,GAAGC,IAAAA,eAAQ,EAAe;IAElD,MAAMC,WAAWC,IAAAA,kBAAW,EAC1B,CAACC;QACC,IAAIA,YAAY,MAAMT,oBAAAA,8BAAAA,QAAUS;QAChCJ,UAAUI;IACZ,GACA;QAACT;KAAQ;IAGX,MAAMU,eAAeF,IAAAA,kBAAW,EAC9B,OAAOG;QACL,IAAI;YACF,MAAMC,WAAW,MAAM/B,qBAAqBgC,MAAM,CAACF;YACnDJ,SAAS;YACT,OAAO5B,oBAAoBiC,UAAU/B;QACvC,EAAE,OAAOiC,GAAG;YACVP,SAASO;YACT,OAAO,EAAE;QACX;IACF,GACA;QAACjC;QAAsB0B;KAAS;IAGlC,MAAMQ,iBAAiB,CAACC;QACtBT,SAAS;QACT,OACE1B,oBACE,+BAA+B;SAC9BoC,GAAG,CAACD,SAAS5B,EAAE,EACf8B,IAAI,CAACnB,mBACLoB,KAAK,CAACZ;IAEb;IAEA,MAAMa,mBAAmB,AAAChB,SAAS,uBAAwBH;IAE3D,qBACE,qBAACoB,kBAAQ;QACN,GAAGnB,UAAU;QACdC,KAAKA;QACLmB,YAAYP;QACZL,cAAcA;QACdT,cAAcmB;QACdG,oBAAM,qBAACC,UAAI;YAACD,MAAMV,aAAM;;;AAG9B"}
|
|
@@ -23,8 +23,10 @@ function _interop_require_default(obj) {
|
|
|
23
23
|
default: obj
|
|
24
24
|
};
|
|
25
25
|
}
|
|
26
|
-
const
|
|
26
|
+
const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
27
27
|
const { id, defaultValue, options, asyncOptions, delay, minLength, onSelected, className, placeholder, icon, ...otherProps } = props;
|
|
28
|
+
// Avoid re-fetching after selecting an option
|
|
29
|
+
const skipNextDebounceRef = (0, _react.useRef)(false);
|
|
28
30
|
const fallbackRef = (0, _react.useRef)(null);
|
|
29
31
|
const [inputValue, setInputValue] = (0, _react.useState)(defaultValue || "");
|
|
30
32
|
const [isOpen, setIsOpen] = (0, _react.useState)(false);
|
|
@@ -33,7 +35,8 @@ const Combobox = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
33
35
|
asyncOptions,
|
|
34
36
|
inputValue,
|
|
35
37
|
delay,
|
|
36
|
-
minLength
|
|
38
|
+
minLength,
|
|
39
|
+
skipNextDebounceRef
|
|
37
40
|
});
|
|
38
41
|
const { highlightedIndex, highlightedGroupIndex, highlightNextOption, highlightPreviousOption, highlightFirstOption, highlightLastOption, clearHighlight } = (0, _useComboboxHighlight.useComboboxHighlight)(filteredOptions);
|
|
39
42
|
const inputRef = ref || fallbackRef;
|
|
@@ -75,18 +78,35 @@ const Combobox = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
75
78
|
updateFilteredOptions(option.callback());
|
|
76
79
|
return;
|
|
77
80
|
}
|
|
81
|
+
// Prevent re-fetching options after selecting an option
|
|
82
|
+
skipNextDebounceRef.current = true;
|
|
78
83
|
setIsOpen(false);
|
|
79
84
|
setInputValue(value);
|
|
80
85
|
onSelected === null || onSelected === void 0 ? void 0 : onSelected(option);
|
|
81
86
|
};
|
|
82
|
-
|
|
87
|
+
const getFirstOption = ()=>{
|
|
88
|
+
if ((0, _utils.isOptionGroup)(filteredOptions)) {
|
|
89
|
+
var _filteredOptions_;
|
|
90
|
+
return (_filteredOptions_ = filteredOptions[0]) === null || _filteredOptions_ === void 0 ? void 0 : _filteredOptions_.options[0];
|
|
91
|
+
}
|
|
92
|
+
return filteredOptions[0];
|
|
93
|
+
};
|
|
94
|
+
const getHighlightedOption = ()=>{
|
|
83
95
|
if (highlightedIndex === -1) return undefined;
|
|
84
96
|
if ((0, _utils.isOptionGroup)(filteredOptions)) {
|
|
85
97
|
const group = filteredOptions[highlightedGroupIndex];
|
|
86
98
|
return group === null || group === void 0 ? void 0 : group.options[highlightedIndex];
|
|
87
99
|
}
|
|
88
100
|
return filteredOptions[highlightedIndex];
|
|
89
|
-
}
|
|
101
|
+
};
|
|
102
|
+
const getHighlightedOptionId = ()=>{
|
|
103
|
+
const option = getHighlightedOption();
|
|
104
|
+
if (!option) return undefined;
|
|
105
|
+
if ((0, _utils.isOptionGroup)(filteredOptions)) {
|
|
106
|
+
return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;
|
|
107
|
+
}
|
|
108
|
+
return `${listboxId}-option-${highlightedIndex}`;
|
|
109
|
+
};
|
|
90
110
|
const handleKeyDown = (e)=>{
|
|
91
111
|
switch(e.key){
|
|
92
112
|
case "ArrowDown":
|
|
@@ -112,7 +132,10 @@ const Combobox = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
112
132
|
case "Enter":
|
|
113
133
|
e.preventDefault();
|
|
114
134
|
if (isOpen) {
|
|
115
|
-
|
|
135
|
+
const selectedOption = getHighlightedOption() || getFirstOption();
|
|
136
|
+
if (selectedOption) {
|
|
137
|
+
handleOptionSelect(selectedOption);
|
|
138
|
+
}
|
|
116
139
|
}
|
|
117
140
|
break;
|
|
118
141
|
case "Escape":
|
|
@@ -156,7 +179,7 @@ const Combobox = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
156
179
|
"aria-haspopup": "listbox",
|
|
157
180
|
"aria-controls": listboxId,
|
|
158
181
|
"aria-expanded": isOpen,
|
|
159
|
-
"aria-activedescendant": highlightedIndex === -1 ? undefined :
|
|
182
|
+
"aria-activedescendant": highlightedIndex === -1 ? undefined : getHighlightedOptionId(),
|
|
160
183
|
prefixInside: icon,
|
|
161
184
|
ref: inputRef
|
|
162
185
|
}),
|
|
@@ -170,5 +193,6 @@ const Combobox = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
170
193
|
]
|
|
171
194
|
});
|
|
172
195
|
});
|
|
196
|
+
const Combobox = ComboboxInner;
|
|
173
197
|
|
|
174
198
|
//# sourceMappingURL=Combobox.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import classNames from \"classnames/dedupe\";\nimport { forwardRef, useId, useRef, useState } from \"react\";\nimport { useOnUnmount } from \"../../hooks\";\nimport type { ForwardedRefComponent } from \"../../types\";\nimport { TextField } from \"../TextField\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport { Listbox } from \"./Listbox\"; // Import Listbox component\nimport type {\n ComboboxElementType,\n ComboboxOption,\n ComboboxProps,\n ComboboxRef,\n} from \"./types\";\nimport { useComboboxHighlight } from \"./useComboboxHighlight\";\nimport { useComboboxOptions } from \"./useComboboxOptions\";\nimport { getOptionValue, isOptionGroup } from \"./utils\";\n\nexport const Combobox: ForwardedRefComponent<\n ComboboxProps,\n ComboboxElementType\n> = forwardRef((props: ComboboxProps, ref: ComboboxRef) => {\n const {\n id,\n defaultValue,\n options,\n asyncOptions,\n delay,\n minLength,\n onSelected,\n className,\n placeholder,\n icon,\n ...otherProps\n } = props;\n\n const fallbackRef = useRef<HTMLInputElement>(null);\n const [inputValue, setInputValue] = useState(defaultValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const { filteredOptions, updateFilteredOptions, isLoading } =\n useComboboxOptions({\n options,\n asyncOptions,\n inputValue,\n delay,\n minLength,\n });\n const {\n highlightedIndex,\n highlightedGroupIndex,\n highlightNextOption,\n highlightPreviousOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n } = useComboboxHighlight(filteredOptions);\n\n const inputRef = ref || fallbackRef;\n const listboxId = useId();\n const statusId = useId();\n const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const showListbox = isOpen && filteredOptions.length > 0;\n\n const handleFocus = () => {\n if (filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n }\n setIsOpen(true);\n };\n\n const handleBlur = () => {\n blurTimeoutRef.current = setTimeout(() => {\n setIsOpen(false);\n }, 150);\n };\n\n useOnUnmount(() => {\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n }\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n clearHighlight();\n };\n\n const handleOptionSelect = (option: ComboboxOption) => {\n const value = getOptionValue(option);\n if (!value) return;\n\n // TODO: Declare this in the types\n if (\n typeof option === \"object\" &&\n \"callback\" in option &&\n option.callback &&\n typeof option.callback === \"function\"\n ) {\n // @ts-expect-error ref types are hard\n setTimeout(() => inputRef.current.focus(), 0);\n updateFilteredOptions(option.callback());\n return;\n }\n\n setIsOpen(false);\n setInputValue(value);\n onSelected?.(option);\n };\n\n function getHighlightedOption() {\n if (highlightedIndex === -1) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n const group = filteredOptions[highlightedGroupIndex];\n return group?.options[highlightedIndex];\n }\n\n return filteredOptions[highlightedIndex];\n }\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setIsOpen(true);\n highlightNextOption();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setIsOpen(true);\n highlightPreviousOption();\n break;\n case \"Home\":\n e.preventDefault();\n setIsOpen(true);\n highlightFirstOption();\n break;\n case \"End\":\n e.preventDefault();\n setIsOpen(true);\n highlightLastOption();\n break;\n case \"Enter\":\n e.preventDefault();\n if (isOpen) {\n handleOptionSelect(getHighlightedOption()!);\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setIsOpen(false);\n clearHighlight();\n break;\n default:\n // Do nothing\n }\n };\n\n const classes = classNames(\n \"mobius mobius-combobox\",\n {\n \"mobius-combobox--is-expanded\": isOpen,\n \"mobius-combobox--is-loading\": isLoading,\n },\n className,\n );\n\n return (\n <div id={id} data-testid=\"mobius-combobox__wrapper\" className={classes}>\n {isLoading && (\n <VisuallyHidden\n role=\"status\"\n aria-live=\"polite\"\n id={statusId}\n elementType=\"div\"\n className=\"mobius-combobox__status\"\n >\n Loading options\n </VisuallyHidden>\n )}\n <TextField\n {...otherProps}\n className=\"mobius-combobox__input\"\n role=\"combobox\"\n value={inputValue}\n placeholder={placeholder}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n autoComplete=\"off\"\n aria-describedby={isLoading ? statusId : undefined}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-controls={listboxId}\n aria-expanded={isOpen}\n aria-activedescendant={\n highlightedIndex === -1\n ? undefined\n : `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`\n }\n prefixInside={icon}\n ref={inputRef}\n />\n {showListbox && (\n <Listbox\n id={listboxId}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n highlightedGroupIndex={highlightedGroupIndex}\n onOptionSelect={handleOptionSelect}\n />\n )}\n </div>\n );\n});\n"],"names":["Combobox","forwardRef","props","ref","id","defaultValue","options","asyncOptions","delay","minLength","onSelected","className","placeholder","icon","otherProps","fallbackRef","useRef","inputValue","setInputValue","useState","isOpen","setIsOpen","filteredOptions","updateFilteredOptions","isLoading","useComboboxOptions","highlightedIndex","highlightedGroupIndex","highlightNextOption","highlightPreviousOption","highlightFirstOption","highlightLastOption","clearHighlight","useComboboxHighlight","inputRef","listboxId","useId","statusId","blurTimeoutRef","showListbox","length","handleFocus","current","clearTimeout","handleBlur","setTimeout","useOnUnmount","handleInputChange","e","newValue","target","value","handleOptionSelect","option","getOptionValue","callback","focus","getHighlightedOption","undefined","isOptionGroup","group","handleKeyDown","key","preventDefault","classes","classNames","div","data-testid","VisuallyHidden","role","aria-live","elementType","TextField","onFocus","onBlur","onKeyDown","onChange","autoComplete","aria-describedby","aria-autocomplete","aria-haspopup","aria-controls","aria-expanded","aria-activedescendant","prefixInside","Listbox","onOptionSelect"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import classNames from \"classnames/dedupe\";\nimport { forwardRef, useId, useRef, useState } from \"react\";\nimport { useOnUnmount } from \"../../hooks\";\nimport { TextField } from \"../TextField\";\nimport { VisuallyHidden } from \"../VisuallyHidden\";\nimport { Listbox } from \"./Listbox\"; // Import Listbox component\nimport type { ComboboxOption, ComboboxProps, ComboboxRef } from \"./types\";\nimport { useComboboxHighlight } from \"./useComboboxHighlight\";\nimport { useComboboxOptions } from \"./useComboboxOptions\";\nimport { getOptionValue, isOptionGroup } from \"./utils\";\n\nconst ComboboxInner = forwardRef(\n <T extends ComboboxOption>(props: ComboboxProps<T>, ref: ComboboxRef) => {\n const {\n id,\n defaultValue,\n options,\n asyncOptions,\n delay,\n minLength,\n onSelected,\n className,\n placeholder,\n icon,\n ...otherProps\n } = props;\n\n // Avoid re-fetching after selecting an option\n const skipNextDebounceRef = useRef(false);\n const fallbackRef = useRef<HTMLInputElement>(null);\n const [inputValue, setInputValue] = useState(defaultValue || \"\");\n const [isOpen, setIsOpen] = useState(false);\n const { filteredOptions, updateFilteredOptions, isLoading } =\n useComboboxOptions({\n options,\n asyncOptions,\n inputValue,\n delay,\n minLength,\n skipNextDebounceRef,\n });\n const {\n highlightedIndex,\n highlightedGroupIndex,\n highlightNextOption,\n highlightPreviousOption,\n highlightFirstOption,\n highlightLastOption,\n clearHighlight,\n } = useComboboxHighlight(filteredOptions);\n\n const inputRef = ref || fallbackRef;\n const listboxId = useId();\n const statusId = useId();\n const blurTimeoutRef = useRef<NodeJS.Timeout | null>(null);\n const showListbox = isOpen && filteredOptions.length > 0;\n\n const handleFocus = () => {\n if (filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n }\n setIsOpen(true);\n };\n\n const handleBlur = () => {\n blurTimeoutRef.current = setTimeout(() => {\n setIsOpen(false);\n }, 150);\n };\n\n useOnUnmount(() => {\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n }\n });\n\n const handleInputChange = (e: React.ChangeEvent<HTMLInputElement>) => {\n const newValue = e.target.value;\n setInputValue(newValue);\n setIsOpen(true);\n clearHighlight();\n };\n\n const handleOptionSelect = (option: T) => {\n const value = getOptionValue(option);\n if (!value) return;\n\n // TODO: Declare this in the types\n if (\n typeof option === \"object\" &&\n \"callback\" in option &&\n option.callback &&\n typeof option.callback === \"function\"\n ) {\n // @ts-expect-error ref types are hard\n setTimeout(() => inputRef.current.focus(), 0);\n updateFilteredOptions(option.callback());\n return;\n }\n\n // Prevent re-fetching options after selecting an option\n skipNextDebounceRef.current = true;\n\n setIsOpen(false);\n setInputValue(value);\n onSelected?.(option as T);\n };\n\n const getFirstOption = () => {\n if (isOptionGroup(filteredOptions)) {\n return filteredOptions[0]?.options[0];\n }\n\n return filteredOptions[0];\n };\n\n const getHighlightedOption = () => {\n if (highlightedIndex === -1) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n const group = filteredOptions[highlightedGroupIndex];\n return group?.options[highlightedIndex];\n }\n\n return filteredOptions[highlightedIndex];\n };\n\n const getHighlightedOptionId = () => {\n const option = getHighlightedOption();\n if (!option) return undefined;\n\n if (isOptionGroup(filteredOptions)) {\n return `${listboxId}-option-${highlightedGroupIndex}-${highlightedIndex}`;\n }\n\n return `${listboxId}-option-${highlightedIndex}`;\n };\n\n const handleKeyDown = (e: React.KeyboardEvent) => {\n switch (e.key) {\n case \"ArrowDown\":\n e.preventDefault();\n setIsOpen(true);\n highlightNextOption();\n break;\n case \"ArrowUp\":\n e.preventDefault();\n setIsOpen(true);\n highlightPreviousOption();\n break;\n case \"Home\":\n e.preventDefault();\n setIsOpen(true);\n highlightFirstOption();\n break;\n case \"End\":\n e.preventDefault();\n setIsOpen(true);\n highlightLastOption();\n break;\n case \"Enter\":\n e.preventDefault();\n if (isOpen) {\n const selectedOption = getHighlightedOption() || getFirstOption();\n if (selectedOption) {\n handleOptionSelect(selectedOption);\n }\n }\n break;\n case \"Escape\":\n e.preventDefault();\n setIsOpen(false);\n clearHighlight();\n break;\n default:\n // Do nothing\n }\n };\n\n const classes = classNames(\n \"mobius mobius-combobox\",\n {\n \"mobius-combobox--is-expanded\": isOpen,\n \"mobius-combobox--is-loading\": isLoading,\n },\n className,\n );\n\n return (\n <div id={id} data-testid=\"mobius-combobox__wrapper\" className={classes}>\n {isLoading && (\n <VisuallyHidden\n role=\"status\"\n aria-live=\"polite\"\n id={statusId}\n elementType=\"div\"\n className=\"mobius-combobox__status\"\n >\n Loading options\n </VisuallyHidden>\n )}\n <TextField\n {...otherProps}\n className=\"mobius-combobox__input\"\n role=\"combobox\"\n value={inputValue}\n placeholder={placeholder}\n onFocus={handleFocus}\n onBlur={handleBlur}\n onKeyDown={handleKeyDown}\n onChange={handleInputChange}\n autoComplete=\"off\"\n aria-describedby={isLoading ? statusId : undefined}\n aria-autocomplete=\"list\"\n aria-haspopup=\"listbox\"\n aria-controls={listboxId}\n aria-expanded={isOpen}\n aria-activedescendant={\n highlightedIndex === -1 ? undefined : getHighlightedOptionId()\n }\n prefixInside={icon}\n ref={inputRef}\n />\n {showListbox && (\n <Listbox\n id={listboxId}\n options={filteredOptions}\n highlightedIndex={highlightedIndex}\n highlightedGroupIndex={highlightedGroupIndex}\n onOptionSelect={handleOptionSelect}\n />\n )}\n </div>\n );\n },\n);\n\nexport const Combobox = ComboboxInner as <T extends ComboboxOption>(\n props: ComboboxProps<T> & { ref?: ComboboxRef },\n) => JSX.Element;\n"],"names":["Combobox","ComboboxInner","forwardRef","props","ref","id","defaultValue","options","asyncOptions","delay","minLength","onSelected","className","placeholder","icon","otherProps","skipNextDebounceRef","useRef","fallbackRef","inputValue","setInputValue","useState","isOpen","setIsOpen","filteredOptions","updateFilteredOptions","isLoading","useComboboxOptions","highlightedIndex","highlightedGroupIndex","highlightNextOption","highlightPreviousOption","highlightFirstOption","highlightLastOption","clearHighlight","useComboboxHighlight","inputRef","listboxId","useId","statusId","blurTimeoutRef","showListbox","length","handleFocus","current","clearTimeout","handleBlur","setTimeout","useOnUnmount","handleInputChange","e","newValue","target","value","handleOptionSelect","option","getOptionValue","callback","focus","getFirstOption","isOptionGroup","getHighlightedOption","undefined","group","getHighlightedOptionId","handleKeyDown","key","preventDefault","selectedOption","classes","classNames","div","data-testid","VisuallyHidden","role","aria-live","elementType","TextField","onFocus","onBlur","onKeyDown","onChange","autoComplete","aria-describedby","aria-autocomplete","aria-haspopup","aria-controls","aria-expanded","aria-activedescendant","prefixInside","Listbox","onOptionSelect"],"mappings":"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"}
|
|
@@ -19,6 +19,12 @@ function _interop_require_default(obj) {
|
|
|
19
19
|
}
|
|
20
20
|
const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptionSelect })=>{
|
|
21
21
|
const classes = (0, _classnames.default)("mobius-combobox__list");
|
|
22
|
+
function getOptionId(option, groupIndex, index) {
|
|
23
|
+
if (typeof option === "object" && "id" in option && typeof option.id === "string") {
|
|
24
|
+
return option.id;
|
|
25
|
+
}
|
|
26
|
+
return (0, _utils.isOptionGroup)(options) ? `${id}-option-${groupIndex}-${index}` : `${id}-option-${index}`;
|
|
27
|
+
}
|
|
22
28
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
|
|
23
29
|
role: "listbox",
|
|
24
30
|
id: id,
|
|
@@ -36,19 +42,16 @@ const Listbox = ({ id, options, highlightedIndex, highlightedGroupIndex, onOptio
|
|
|
36
42
|
}),
|
|
37
43
|
option.options.map((groupOption, index)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_Option.Option, {
|
|
38
44
|
option: groupOption,
|
|
39
|
-
index: index,
|
|
40
|
-
groupIndex: groupIndex,
|
|
41
45
|
isHighlighted: highlightedIndex === index && highlightedGroupIndex === groupIndex,
|
|
42
46
|
onOptionSelect: onOptionSelect,
|
|
43
|
-
id:
|
|
47
|
+
id: getOptionId(groupOption, groupIndex, index)
|
|
44
48
|
}, `${id}-option-${groupIndex}-${index}`))
|
|
45
49
|
]
|
|
46
50
|
}, option.heading)) : options.map((option, index)=>/*#__PURE__*/ (0, _jsxruntime.jsx)(_Option.Option, {
|
|
47
51
|
option: option,
|
|
48
|
-
index: index,
|
|
49
52
|
isHighlighted: highlightedIndex === index,
|
|
50
53
|
onOptionSelect: onOptionSelect,
|
|
51
|
-
id:
|
|
54
|
+
id: getOptionId(option, 0, index)
|
|
52
55
|
}, index))
|
|
53
56
|
});
|
|
54
57
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Combobox/Listbox.tsx"],"sourcesContent":["import classNames from \"classnames\";\nimport { Option } from \"./Option\";\nimport type { ComboboxOption, ComboboxOptions } from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport type ListboxProps = {\n id: string;\n options: ComboboxOptions
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Combobox/Listbox.tsx"],"sourcesContent":["import classNames from \"classnames\";\nimport { Option } from \"./Option\";\nimport type { ComboboxOption, ComboboxOptions } from \"./types\";\nimport { isOptionGroup } from \"./utils\";\n\nexport type ListboxProps<T extends ComboboxOption> = {\n id: string;\n options: ComboboxOptions<T>;\n highlightedIndex: number;\n highlightedGroupIndex: number;\n onOptionSelect: (option: T) => void;\n};\n\nexport const Listbox = <T extends ComboboxOption>({\n id,\n options,\n highlightedIndex,\n highlightedGroupIndex,\n onOptionSelect,\n}: ListboxProps<T>) => {\n const classes = classNames(\"mobius-combobox__list\");\n\n function getOptionId(\n option: ComboboxOption,\n groupIndex: number,\n index: number,\n ) {\n if (\n typeof option === \"object\" &&\n \"id\" in option &&\n typeof option.id === \"string\"\n ) {\n return option.id;\n }\n return isOptionGroup(options)\n ? `${id}-option-${groupIndex}-${index}`\n : `${id}-option-${index}`;\n }\n\n return (\n <div role=\"listbox\" id={id} className={classes}>\n {isOptionGroup(options)\n ? options.map((option, groupIndex) => (\n <ul\n role=\"group\"\n key={option.heading}\n aria-labelledby={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group\"\n >\n <li\n role=\"presentation\"\n id={`${id}-group-${groupIndex}`}\n className=\"mobius-combobox__group-label\"\n >\n {option.heading}\n </li>\n {option.options.map((groupOption, index) => (\n <Option\n // eslint-disable-next-line react/no-array-index-key\n key={`${id}-option-${groupIndex}-${index}`}\n option={groupOption}\n isHighlighted={\n highlightedIndex === index &&\n highlightedGroupIndex === groupIndex\n }\n onOptionSelect={onOptionSelect}\n id={getOptionId(groupOption, groupIndex, index)}\n />\n ))}\n </ul>\n ))\n : options.map((option, index) => (\n <Option\n // eslint-disable-next-line react/no-array-index-key\n key={index}\n option={option}\n isHighlighted={highlightedIndex === index}\n onOptionSelect={onOptionSelect}\n id={getOptionId(option, 0, index)}\n />\n ))}\n </div>\n );\n};\n"],"names":["Listbox","id","options","highlightedIndex","highlightedGroupIndex","onOptionSelect","classes","classNames","getOptionId","option","groupIndex","index","isOptionGroup","div","role","className","map","ul","aria-labelledby","li","heading","groupOption","Option","isHighlighted"],"mappings":";;;;+BAaaA;;;eAAAA;;;;mEAbU;wBACA;uBAEO;;;;;;AAUvB,MAAMA,UAAU,CAA2B,EAChDC,EAAE,EACFC,OAAO,EACPC,gBAAgB,EAChBC,qBAAqB,EACrBC,cAAc,EACE;IAChB,MAAMC,UAAUC,IAAAA,mBAAU,EAAC;IAE3B,SAASC,YACPC,MAAsB,EACtBC,UAAkB,EAClBC,KAAa;QAEb,IACE,OAAOF,WAAW,YAClB,QAAQA,UACR,OAAOA,OAAOR,EAAE,KAAK,UACrB;YACA,OAAOQ,OAAOR,EAAE;QAClB;QACA,OAAOW,IAAAA,oBAAa,EAACV,WACjB,GAAGD,GAAG,QAAQ,EAAES,WAAW,CAAC,EAAEC,OAAO,GACrC,GAAGV,GAAG,QAAQ,EAAEU,OAAO;IAC7B;IAEA,qBACE,qBAACE;QAAIC,MAAK;QAAUb,IAAIA;QAAIc,WAAWT;kBACpCM,IAAAA,oBAAa,EAACV,WACXA,QAAQc,GAAG,CAAC,CAACP,QAAQC,2BACnB,sBAACO;gBACCH,MAAK;gBAELI,mBAAiB,GAAGjB,GAAG,OAAO,EAAES,YAAY;gBAC5CK,WAAU;;kCAEV,qBAACI;wBACCL,MAAK;wBACLb,IAAI,GAAGA,GAAG,OAAO,EAAES,YAAY;wBAC/BK,WAAU;kCAETN,OAAOW,OAAO;;oBAEhBX,OAAOP,OAAO,CAACc,GAAG,CAAC,CAACK,aAAaV,sBAChC,qBAACW,cAAM;4BAGLb,QAAQY;4BACRE,eACEpB,qBAAqBQ,SACrBP,0BAA0BM;4BAE5BL,gBAAgBA;4BAChBJ,IAAIO,YAAYa,aAAaX,YAAYC;2BAPpC,GAAGV,GAAG,QAAQ,EAAES,WAAW,CAAC,EAAEC,OAAO;;eAdzCF,OAAOW,OAAO,KA0BvBlB,QAAQc,GAAG,CAAC,CAACP,QAAQE,sBACnB,qBAACW,cAAM;gBAGLb,QAAQA;gBACRc,eAAepB,qBAAqBQ;gBACpCN,gBAAgBA;gBAChBJ,IAAIO,YAAYC,QAAQ,GAAGE;eAJtBA;;AASnB"}
|
|
@@ -17,7 +17,7 @@ function _interop_require_default(obj) {
|
|
|
17
17
|
default: obj
|
|
18
18
|
};
|
|
19
19
|
}
|
|
20
|
-
const Option = ({ option,
|
|
20
|
+
const Option = ({ option, isHighlighted, onOptionSelect, id })=>{
|
|
21
21
|
const optionRef = (0, _react.useRef)(null);
|
|
22
22
|
(0, _react.useEffect)(()=>{
|
|
23
23
|
if (isHighlighted && optionRef.current && optionRef.current.scrollIntoView) {
|
|
@@ -31,7 +31,7 @@ const Option = ({ option, index, groupIndex = 0, isHighlighted, onOptionSelect,
|
|
|
31
31
|
return /*#__PURE__*/ (0, _jsxruntime.jsx)("li", {
|
|
32
32
|
ref: optionRef,
|
|
33
33
|
role: "option",
|
|
34
|
-
id:
|
|
34
|
+
id: id,
|
|
35
35
|
"aria-selected": isHighlighted,
|
|
36
36
|
onMouseDown: ()=>onOptionSelect(option),
|
|
37
37
|
className: (0, _dedupe.default)("mobius-combobox__option", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Combobox/Option.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport classNames from \"classnames/dedupe\";\nimport { getOptionValue, getOptionLabel } from \"./utils\";\nimport type { ComboboxOptionProps } from \"./types\";\n\nexport const Option = ({\n option,\n
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Combobox/Option.tsx"],"sourcesContent":["import { useEffect, useRef } from \"react\";\nimport classNames from \"classnames/dedupe\";\nimport { getOptionValue, getOptionLabel } from \"./utils\";\nimport type { ComboboxOption, ComboboxOptionProps } from \"./types\";\n\nexport const Option = <T extends ComboboxOption>({\n option,\n isHighlighted,\n onOptionSelect,\n id,\n}: ComboboxOptionProps<T>) => {\n const optionRef = useRef<HTMLLIElement>(null);\n\n useEffect(() => {\n if (\n isHighlighted &&\n optionRef.current &&\n optionRef.current.scrollIntoView\n ) {\n optionRef.current.scrollIntoView({ block: \"nearest\" });\n }\n }, [isHighlighted]);\n\n return (\n <li\n ref={optionRef}\n role=\"option\"\n key={getOptionValue(option)}\n id={id}\n aria-selected={isHighlighted}\n onMouseDown={() => onOptionSelect(option)}\n className={classNames(\"mobius-combobox__option\", {\n \"mobius-combobox__option--is-highlighted\": isHighlighted,\n })}\n >\n {getOptionLabel(option)}\n </li>\n );\n};\n"],"names":["Option","option","isHighlighted","onOptionSelect","id","optionRef","useRef","useEffect","current","scrollIntoView","block","li","ref","role","aria-selected","onMouseDown","className","classNames","getOptionLabel","getOptionValue"],"mappings":";;;;+BAKaA;;;eAAAA;;;;uBALqB;+DACX;uBACwB;;;;;;AAGxC,MAAMA,SAAS,CAA2B,EAC/CC,MAAM,EACNC,aAAa,EACbC,cAAc,EACdC,EAAE,EACqB;IACvB,MAAMC,YAAYC,IAAAA,aAAM,EAAgB;IAExCC,IAAAA,gBAAS,EAAC;QACR,IACEL,iBACAG,UAAUG,OAAO,IACjBH,UAAUG,OAAO,CAACC,cAAc,EAChC;YACAJ,UAAUG,OAAO,CAACC,cAAc,CAAC;gBAAEC,OAAO;YAAU;QACtD;IACF,GAAG;QAACR;KAAc;IAElB,qBACE,qBAACS;QACCC,KAAKP;QACLQ,MAAK;QAELT,IAAIA;QACJU,iBAAeZ;QACfa,aAAa,IAAMZ,eAAeF;QAClCe,WAAWC,IAAAA,eAAU,EAAC,2BAA2B;YAC/C,2CAA2Cf;QAC7C;kBAECgB,IAAAA,qBAAc,EAACjB;OARXkB,IAAAA,qBAAc,EAAClB;AAW1B"}
|
|
@@ -11,7 +11,7 @@ Object.defineProperty(exports, "useComboboxOptions", {
|
|
|
11
11
|
const _react = require("react");
|
|
12
12
|
const _utils = require("./utils");
|
|
13
13
|
const _hooks = require("../../hooks");
|
|
14
|
-
function useComboboxOptions({ options, asyncOptions, delay = 300, minLength = 3, inputValue = "" }) {
|
|
14
|
+
function useComboboxOptions({ options, asyncOptions, delay = 300, minLength = 3, inputValue = "", skipNextDebounceRef }) {
|
|
15
15
|
const [filteredOptions, setFilteredOptions] = (0, _react.useState)([]);
|
|
16
16
|
const debouncedInputValue = (0, _hooks.useDebouncedValue)(inputValue, // Don't debounce synchronous options
|
|
17
17
|
options ? 0 : delay);
|
|
@@ -34,7 +34,7 @@ function useComboboxOptions({ options, asyncOptions, delay = 300, minLength = 3,
|
|
|
34
34
|
});
|
|
35
35
|
setFilteredOptions(result);
|
|
36
36
|
} else {
|
|
37
|
-
// @ts-expect-error options
|
|
37
|
+
// @ts-expect-error options is erroneously typed as possibly undefined
|
|
38
38
|
setFilteredOptions((0, _utils.filterOptions)(options, debouncedInputValue));
|
|
39
39
|
}
|
|
40
40
|
} catch (e) {
|
|
@@ -47,7 +47,12 @@ function useComboboxOptions({ options, asyncOptions, delay = 300, minLength = 3,
|
|
|
47
47
|
setIsLoading(false);
|
|
48
48
|
}
|
|
49
49
|
};
|
|
50
|
-
|
|
50
|
+
if (!(skipNextDebounceRef === null || skipNextDebounceRef === void 0 ? void 0 : skipNextDebounceRef.current)) {
|
|
51
|
+
fetchOptions();
|
|
52
|
+
} else {
|
|
53
|
+
// eslint-disable-next-line no-param-reassign
|
|
54
|
+
skipNextDebounceRef.current = false;
|
|
55
|
+
}
|
|
51
56
|
return ()=>{
|
|
52
57
|
controller.abort();
|
|
53
58
|
};
|
|
@@ -56,7 +61,8 @@ function useComboboxOptions({ options, asyncOptions, delay = 300, minLength = 3,
|
|
|
56
61
|
options,
|
|
57
62
|
asyncOptions,
|
|
58
63
|
delay,
|
|
59
|
-
minLength
|
|
64
|
+
minLength,
|
|
65
|
+
skipNextDebounceRef
|
|
60
66
|
]);
|
|
61
67
|
function updateFilteredOptions(newOptions) {
|
|
62
68
|
setIsLoading(true);
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Combobox/useComboboxOptions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ComboboxOptions, ComboboxProps } from \"./types\";\nimport { filterOptions } from \"./utils\";\nimport { useDebouncedValue } from \"../../hooks\";\n\nexport type UseComboboxOptionsProps = Pick<\n ComboboxProps
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Combobox/useComboboxOptions.ts"],"sourcesContent":["import { useEffect, useState } from \"react\";\nimport type { ComboboxOption, ComboboxOptions, ComboboxProps } from \"./types\";\nimport { filterOptions } from \"./utils\";\nimport { useDebouncedValue } from \"../../hooks\";\n\nexport type UseComboboxOptionsProps<T extends ComboboxOption> = Pick<\n ComboboxProps<T>,\n \"options\" | \"asyncOptions\" | \"delay\" | \"minLength\"\n> & {\n skipNextDebounceRef?: React.MutableRefObject<boolean>;\n inputValue?: string;\n};\n\nexport function useComboboxOptions<T extends ComboboxOption>({\n options,\n asyncOptions,\n delay = 300,\n minLength = 3,\n inputValue = \"\",\n skipNextDebounceRef,\n}: UseComboboxOptionsProps<T>) {\n const [filteredOptions, setFilteredOptions] = useState<ComboboxOptions<T>>(\n [],\n );\n const debouncedInputValue = useDebouncedValue(\n inputValue,\n // Don't debounce synchronous options\n options ? 0 : delay,\n );\n const [isLoading, setIsLoading] = useState(false);\n const [error, setError] = useState<Error | null>(null);\n\n useEffect(() => {\n const controller = new AbortController();\n const { signal } = controller;\n\n const fetchOptions = async () => {\n setIsLoading(true);\n setError(null);\n try {\n if (asyncOptions) {\n if (debouncedInputValue.length < minLength) {\n setFilteredOptions([]);\n return;\n }\n const result = await asyncOptions(debouncedInputValue, { signal });\n setFilteredOptions(result);\n } else {\n // @ts-expect-error options is erroneously typed as possibly undefined\n setFilteredOptions(filterOptions(options, debouncedInputValue));\n }\n } catch (e: unknown) {\n if (e instanceof DOMException && e.name === \"AbortError\") {\n // Ignore abort errors\n return;\n }\n setError(e as Error);\n } finally {\n setIsLoading(false);\n }\n };\n\n if (!skipNextDebounceRef?.current) {\n fetchOptions();\n } else {\n // eslint-disable-next-line no-param-reassign\n skipNextDebounceRef.current = false;\n }\n\n return () => {\n controller.abort();\n };\n }, [\n debouncedInputValue,\n options,\n asyncOptions,\n delay,\n minLength,\n skipNextDebounceRef,\n ]);\n\n function updateFilteredOptions(newOptions: Promise<ComboboxOptions<T>>) {\n setIsLoading(true);\n return newOptions\n .then(setFilteredOptions)\n .catch(setError)\n .finally(() => setIsLoading(false));\n }\n\n return {\n filteredOptions,\n updateFilteredOptions,\n isLoading,\n error,\n isError: error != null,\n };\n}\n"],"names":["useComboboxOptions","options","asyncOptions","delay","minLength","inputValue","skipNextDebounceRef","filteredOptions","setFilteredOptions","useState","debouncedInputValue","useDebouncedValue","isLoading","setIsLoading","error","setError","useEffect","controller","AbortController","signal","fetchOptions","length","result","filterOptions","e","DOMException","name","current","abort","updateFilteredOptions","newOptions","then","catch","finally","isError"],"mappings":";;;;+BAagBA;;;eAAAA;;;uBAboB;uBAEN;uBACI;AAU3B,SAASA,mBAA6C,EAC3DC,OAAO,EACPC,YAAY,EACZC,QAAQ,GAAG,EACXC,YAAY,CAAC,EACbC,aAAa,EAAE,EACfC,mBAAmB,EACQ;IAC3B,MAAM,CAACC,iBAAiBC,mBAAmB,GAAGC,IAAAA,eAAQ,EACpD,EAAE;IAEJ,MAAMC,sBAAsBC,IAAAA,wBAAiB,EAC3CN,YACA,qCAAqC;IACrCJ,UAAU,IAAIE;IAEhB,MAAM,CAACS,WAAWC,aAAa,GAAGJ,IAAAA,eAAQ,EAAC;IAC3C,MAAM,CAACK,OAAOC,SAAS,GAAGN,IAAAA,eAAQ,EAAe;IAEjDO,IAAAA,gBAAS,EAAC;QACR,MAAMC,aAAa,IAAIC;QACvB,MAAM,EAAEC,MAAM,EAAE,GAAGF;QAEnB,MAAMG,eAAe;YACnBP,aAAa;YACbE,SAAS;YACT,IAAI;gBACF,IAAIb,cAAc;oBAChB,IAAIQ,oBAAoBW,MAAM,GAAGjB,WAAW;wBAC1CI,mBAAmB,EAAE;wBACrB;oBACF;oBACA,MAAMc,SAAS,MAAMpB,aAAaQ,qBAAqB;wBAAES;oBAAO;oBAChEX,mBAAmBc;gBACrB,OAAO;oBACL,sEAAsE;oBACtEd,mBAAmBe,IAAAA,oBAAa,EAACtB,SAASS;gBAC5C;YACF,EAAE,OAAOc,GAAY;gBACnB,IAAIA,aAAaC,gBAAgBD,EAAEE,IAAI,KAAK,cAAc;oBACxD,sBAAsB;oBACtB;gBACF;gBACAX,SAASS;YACX,SAAU;gBACRX,aAAa;YACf;QACF;QAEA,IAAI,EAACP,gCAAAA,0CAAAA,oBAAqBqB,OAAO,GAAE;YACjCP;QACF,OAAO;YACL,6CAA6C;YAC7Cd,oBAAoBqB,OAAO,GAAG;QAChC;QAEA,OAAO;YACLV,WAAWW,KAAK;QAClB;IACF,GAAG;QACDlB;QACAT;QACAC;QACAC;QACAC;QACAE;KACD;IAED,SAASuB,sBAAsBC,UAAuC;QACpEjB,aAAa;QACb,OAAOiB,WACJC,IAAI,CAACvB,oBACLwB,KAAK,CAACjB,UACNkB,OAAO,CAAC,IAAMpB,aAAa;IAChC;IAEA,OAAO;QACLN;QACAsB;QACAjB;QACAE;QACAoB,SAASpB,SAAS;IACpB;AACF"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/Combobox/utils.tsx"],"sourcesContent":["import type {\n ComboboxOption,\n ComboboxOptionGroup,\n ComboboxOptions,\n} from \"./types\";\n\n// FIXME: This might be better handled with Zod\nexport function isOptionGroup(\n options: ComboboxOptions
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/Combobox/utils.tsx"],"sourcesContent":["import type {\n ComboboxOption,\n ComboboxOptionGroup,\n ComboboxOptions,\n} from \"./types\";\n\n// FIXME: This might be better handled with Zod\nexport function isOptionGroup<T extends ComboboxOption>(\n options: ComboboxOptions<T>,\n): options is ComboboxOptionGroup<T>[] {\n return (\n typeof options[0] === \"object\" &&\n \"options\" in options[0] &&\n options[0].options !== undefined &&\n \"heading\" in options[0] &&\n options[0].heading !== undefined\n );\n}\n\nexport const getOptionValue = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.value;\n\nexport const getOptionLabel = (option: ComboboxOption | undefined) =>\n typeof option === \"string\" ? option : option?.label;\n\nexport function filterOptions<T extends ComboboxOption>(\n options: ComboboxOptions<T>,\n inputValue: string,\n): ComboboxOptions<T> {\n if (isOptionGroup(options)) {\n return options\n .map(optionGroup => ({\n ...optionGroup,\n options: optionGroup.options.filter(option =>\n getOptionLabel(option)!\n .toLowerCase()\n .includes(inputValue.toLowerCase()),\n ),\n }))\n .filter(optionGroup => optionGroup.options.length > 0);\n }\n\n return options.filter(option =>\n getOptionLabel(option)!.toLowerCase().includes(inputValue.toLowerCase()),\n );\n}\n\nexport function clamp(value: number, min: number, max: number) {\n return Math.min(Math.max(value, min), max);\n}\n"],"names":["clamp","filterOptions","getOptionLabel","getOptionValue","isOptionGroup","options","undefined","heading","option","value","label","inputValue","map","optionGroup","filter","toLowerCase","includes","length","min","max","Math"],"mappings":";;;;;;;;;;;IA+CgBA,KAAK;eAALA;;IAtBAC,aAAa;eAAbA;;IAHHC,cAAc;eAAdA;;IAHAC,cAAc;eAAdA;;IAZGC,aAAa;eAAbA;;;AAAT,SAASA,cACdC,OAA2B;IAE3B,OACE,OAAOA,OAAO,CAAC,EAAE,KAAK,YACtB,aAAaA,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACA,OAAO,KAAKC,aACvB,aAAaD,OAAO,CAAC,EAAE,IACvBA,OAAO,CAAC,EAAE,CAACE,OAAO,KAAKD;AAE3B;AAEO,MAAMH,iBAAiB,CAACK,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQC,KAAK;AAE9C,MAAMP,iBAAiB,CAACM,SAC7B,OAAOA,WAAW,WAAWA,SAASA,mBAAAA,6BAAAA,OAAQE,KAAK;AAE9C,SAAST,cACdI,OAA2B,EAC3BM,UAAkB;IAElB,IAAIP,cAAcC,UAAU;QAC1B,OAAOA,QACJO,GAAG,CAACC,CAAAA,cAAgB,CAAA;gBACnB,GAAGA,WAAW;gBACdR,SAASQ,YAAYR,OAAO,CAACS,MAAM,CAACN,CAAAA,SAClCN,eAAeM,QACZO,WAAW,GACXC,QAAQ,CAACL,WAAWI,WAAW;YAEtC,CAAA,GACCD,MAAM,CAACD,CAAAA,cAAeA,YAAYR,OAAO,CAACY,MAAM,GAAG;IACxD;IAEA,OAAOZ,QAAQS,MAAM,CAACN,CAAAA,SACpBN,eAAeM,QAASO,WAAW,GAAGC,QAAQ,CAACL,WAAWI,WAAW;AAEzE;AAEO,SAASf,MAAMS,KAAa,EAAES,GAAW,EAAEC,GAAW;IAC3D,OAAOC,KAAKF,GAAG,CAACE,KAAKD,GAAG,CAACV,OAAOS,MAAMC;AACxC"}
|
|
@@ -59,11 +59,11 @@ const TextField = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
59
59
|
/*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
|
|
60
60
|
className: "mobius-text-field__inner-container",
|
|
61
61
|
children: [
|
|
62
|
-
(0, _adornmentWithClassName.adornmentWithClassName)(prefixOutside, "mobius-text-field__prefix-outside"),
|
|
62
|
+
(0, _adornmentWithClassName.adornmentWithClassName)(prefixOutside, labelClasses, "mobius-text-field__prefix-outside"),
|
|
63
63
|
/*#__PURE__*/ (0, _jsxruntime.jsxs)("div", {
|
|
64
64
|
className: inputWrapperClasses,
|
|
65
65
|
children: [
|
|
66
|
-
(0, _adornmentWithClassName.adornmentWithClassName)(prefixInside, "mobius-text-field__prefix-inside"),
|
|
66
|
+
(0, _adornmentWithClassName.adornmentWithClassName)(prefixInside, labelClasses, "mobius-text-field__prefix-inside"),
|
|
67
67
|
/*#__PURE__*/ (0, _jsxruntime.jsx)("input", {
|
|
68
68
|
...otherProps,
|
|
69
69
|
...inputProps,
|
|
@@ -72,10 +72,10 @@ const TextField = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
|
|
|
72
72
|
className: inputClasses,
|
|
73
73
|
readOnly: isReadOnly
|
|
74
74
|
}),
|
|
75
|
-
(0, _adornmentWithClassName.adornmentWithClassName)(suffixInside, "mobius-text-field__suffix-inside")
|
|
75
|
+
(0, _adornmentWithClassName.adornmentWithClassName)(suffixInside, labelClasses, "mobius-text-field__suffix-inside")
|
|
76
76
|
]
|
|
77
77
|
}),
|
|
78
|
-
(0, _adornmentWithClassName.adornmentWithClassName)(suffixOutside, "mobius-text-field__suffix-outside")
|
|
78
|
+
(0, _adornmentWithClassName.adornmentWithClassName)(suffixOutside, labelClasses, "mobius-text-field__suffix-outside")
|
|
79
79
|
]
|
|
80
80
|
}),
|
|
81
81
|
children && /*#__PURE__*/ (0, _jsxruntime.jsx)("div", {
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["\"use client\";\n\nimport classNames from \"classnames/dedupe\";\nimport {\n HTMLInputTypeAttribute,\n ReactElement,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n} from \"react\";\nimport {\n UseTextFieldProps,\n useTextField,\n useValidationClasses,\n} from \"../../hooks\";\nimport { DOMProps, FocusEvents } from \"../../types\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { ErrorMessage } from \"../ErrorMessage\";\nimport { Label } from \"../Label\";\nimport { Stack } from \"../Stack\";\nimport { adornmentWithClassName } from \"./adornmentWithClassName\";\n\nexport type TextFieldElementType = HTMLInputElement;\nexport interface TextFieldProps\n extends DOMProps,\n FocusEvents,\n UseTextFieldProps,\n RefAttributes<TextFieldElementType> {\n className?: string;\n errorMessage?: string;\n children?: ReactNode;\n label?: string;\n type?: Exclude<\n HTMLInputTypeAttribute,\n | \"button\"\n | \"checkbox\"\n | \"color\"\n | \"date\"\n | \"datetime-local\"\n | \"file\"\n | \"image\"\n | \"month\"\n | \"radio\"\n | \"range\"\n | \"reset\"\n | \"submit\"\n | \"week\"\n >;\n prefixInside?: ReactElement;\n prefixOutside?: ReactElement;\n suffixInside?: ReactElement;\n suffixOutside?: ReactElement;\n}\n\nexport type TextFieldRef = Ref<TextFieldElementType>;\n\nconst TextField: ForwardedRefComponent<TextFieldProps, TextFieldElementType> =\n forwardRef((props: TextFieldProps, ref: TextFieldRef) => {\n const {\n isDisabled,\n isReadOnly,\n type = \"text\",\n validationState,\n isInvalid,\n className,\n label,\n errorMessage,\n children,\n isRequired,\n prefixInside,\n prefixOutside,\n suffixInside,\n suffixOutside,\n ...otherProps\n } = props;\n\n const { inputProps, labelProps, errorMessageProps } = useTextField({\n ...props,\n \"aria-errormessage\": errorMessage,\n });\n\n const hidden = type === \"hidden\";\n\n const validationClasses = useValidationClasses({\n validationState,\n isInvalid,\n });\n\n const textfieldClasses = {\n \"--is-disabled\": isDisabled,\n \"--is-required\": typeof isRequired === \"boolean\" && isRequired,\n \"--is-optional\": typeof isRequired === \"boolean\" && !isRequired,\n \"--is-hidden\": hidden,\n [className || \"\"]: true,\n };\n\n const sharedClasses = classNames(validationClasses, textfieldClasses);\n\n const labelClasses = classNames(\n {\n \"--is-disabled\": isDisabled,\n },\n validationClasses,\n );\n\n const containerClasses = classNames(\n \"mobius\",\n \"mobius-text-field\",\n sharedClasses,\n );\n\n const inputClasses = classNames(\n \"mobius\",\n \"mobius-text-field__input\",\n sharedClasses,\n );\n\n const inputWrapperClasses = classNames(\n \"mobius-text-field__input-wrapper\",\n sharedClasses,\n );\n\n return (\n <Stack gap=\"xs\" className={containerClasses}>\n {label && !hidden && (\n <Label {...labelProps} className={labelClasses}>\n {label}\n </Label>\n )}\n <div className=\"mobius-text-field__inner-container\">\n {adornmentWithClassName(\n prefixOutside,\n \"mobius-text-field__prefix-outside\",\n )}\n <div className={inputWrapperClasses}>\n {adornmentWithClassName(\n prefixInside,\n \"mobius-text-field__prefix-inside\",\n )}\n <input\n {...otherProps}\n {...inputProps}\n ref={ref}\n type={type}\n className={inputClasses}\n readOnly={isReadOnly}\n />\n {adornmentWithClassName(\n suffixInside,\n \"mobius-text-field__suffix-inside\",\n )}\n </div>\n {adornmentWithClassName(\n suffixOutside,\n \"mobius-text-field__suffix-outside\",\n )}\n </div>\n {children && (\n <div className=\"mobius-text-field__children\">{children}</div>\n )}\n\n <ErrorMessage {...errorMessageProps} errorMessage={errorMessage} />\n </Stack>\n );\n });\n\nTextField.displayName = \"TextField\";\nexport { TextField };\n"],"names":["TextField","forwardRef","props","ref","isDisabled","isReadOnly","type","validationState","isInvalid","className","label","errorMessage","children","isRequired","prefixInside","prefixOutside","suffixInside","suffixOutside","otherProps","inputProps","labelProps","errorMessageProps","useTextField","hidden","validationClasses","useValidationClasses","textfieldClasses","sharedClasses","classNames","labelClasses","containerClasses","inputClasses","inputWrapperClasses","Stack","gap","Label","div","adornmentWithClassName","input","readOnly","ErrorMessage","displayName"],"mappings":"AAAA;;;;;+
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/TextField/TextField.tsx"],"sourcesContent":["\"use client\";\n\nimport classNames from \"classnames/dedupe\";\nimport {\n HTMLInputTypeAttribute,\n ReactElement,\n ReactNode,\n Ref,\n RefAttributes,\n forwardRef,\n} from \"react\";\nimport {\n UseTextFieldProps,\n useTextField,\n useValidationClasses,\n} from \"../../hooks\";\nimport { DOMProps, FocusEvents } from \"../../types\";\nimport { ForwardedRefComponent } from \"../../types/components\";\nimport { ErrorMessage } from \"../ErrorMessage\";\nimport { Label } from \"../Label\";\nimport { Stack } from \"../Stack\";\nimport { adornmentWithClassName } from \"./adornmentWithClassName\";\n\nexport type TextFieldElementType = HTMLInputElement;\nexport interface TextFieldProps\n extends DOMProps,\n FocusEvents,\n UseTextFieldProps,\n RefAttributes<TextFieldElementType> {\n className?: string;\n errorMessage?: string;\n children?: ReactNode;\n label?: string;\n type?: Exclude<\n HTMLInputTypeAttribute,\n | \"button\"\n | \"checkbox\"\n | \"color\"\n | \"date\"\n | \"datetime-local\"\n | \"file\"\n | \"image\"\n | \"month\"\n | \"radio\"\n | \"range\"\n | \"reset\"\n | \"submit\"\n | \"week\"\n >;\n prefixInside?: ReactElement;\n prefixOutside?: ReactElement;\n suffixInside?: ReactElement;\n suffixOutside?: ReactElement;\n}\n\nexport type TextFieldRef = Ref<TextFieldElementType>;\n\nconst TextField: ForwardedRefComponent<TextFieldProps, TextFieldElementType> =\n forwardRef((props: TextFieldProps, ref: TextFieldRef) => {\n const {\n isDisabled,\n isReadOnly,\n type = \"text\",\n validationState,\n isInvalid,\n className,\n label,\n errorMessage,\n children,\n isRequired,\n prefixInside,\n prefixOutside,\n suffixInside,\n suffixOutside,\n ...otherProps\n } = props;\n\n const { inputProps, labelProps, errorMessageProps } = useTextField({\n ...props,\n \"aria-errormessage\": errorMessage,\n });\n\n const hidden = type === \"hidden\";\n\n const validationClasses = useValidationClasses({\n validationState,\n isInvalid,\n });\n\n const textfieldClasses = {\n \"--is-disabled\": isDisabled,\n \"--is-required\": typeof isRequired === \"boolean\" && isRequired,\n \"--is-optional\": typeof isRequired === \"boolean\" && !isRequired,\n \"--is-hidden\": hidden,\n [className || \"\"]: true,\n };\n\n const sharedClasses = classNames(validationClasses, textfieldClasses);\n\n const labelClasses = classNames(\n {\n \"--is-disabled\": isDisabled,\n },\n validationClasses,\n );\n\n const containerClasses = classNames(\n \"mobius\",\n \"mobius-text-field\",\n sharedClasses,\n );\n\n const inputClasses = classNames(\n \"mobius\",\n \"mobius-text-field__input\",\n sharedClasses,\n );\n\n const inputWrapperClasses = classNames(\n \"mobius-text-field__input-wrapper\",\n sharedClasses,\n );\n\n return (\n <Stack gap=\"xs\" className={containerClasses}>\n {label && !hidden && (\n <Label {...labelProps} className={labelClasses}>\n {label}\n </Label>\n )}\n <div className=\"mobius-text-field__inner-container\">\n {adornmentWithClassName(\n prefixOutside,\n labelClasses,\n \"mobius-text-field__prefix-outside\",\n )}\n <div className={inputWrapperClasses}>\n {adornmentWithClassName(\n prefixInside,\n labelClasses,\n \"mobius-text-field__prefix-inside\",\n )}\n <input\n {...otherProps}\n {...inputProps}\n ref={ref}\n type={type}\n className={inputClasses}\n readOnly={isReadOnly}\n />\n {adornmentWithClassName(\n suffixInside,\n labelClasses,\n \"mobius-text-field__suffix-inside\",\n )}\n </div>\n {adornmentWithClassName(\n suffixOutside,\n labelClasses,\n \"mobius-text-field__suffix-outside\",\n )}\n </div>\n {children && (\n <div className=\"mobius-text-field__children\">{children}</div>\n )}\n\n <ErrorMessage {...errorMessageProps} errorMessage={errorMessage} />\n </Stack>\n );\n });\n\nTextField.displayName = \"TextField\";\nexport { TextField };\n"],"names":["TextField","forwardRef","props","ref","isDisabled","isReadOnly","type","validationState","isInvalid","className","label","errorMessage","children","isRequired","prefixInside","prefixOutside","suffixInside","suffixOutside","otherProps","inputProps","labelProps","errorMessageProps","useTextField","hidden","validationClasses","useValidationClasses","textfieldClasses","sharedClasses","classNames","labelClasses","containerClasses","inputClasses","inputWrapperClasses","Stack","gap","Label","div","adornmentWithClassName","input","readOnly","ErrorMessage","displayName"],"mappings":"AAAA;;;;;+BA4KSA;;;eAAAA;;;;+DA1Kc;uBAQhB;uBAKA;8BAGsB;uBACP;uBACA;wCACiB;;;;;;AAoCvC,MAAMA,0BACJC,IAAAA,iBAAU,EAAC,CAACC,OAAuBC;IACjC,MAAM,EACJC,UAAU,EACVC,UAAU,EACVC,OAAO,MAAM,EACbC,eAAe,EACfC,SAAS,EACTC,SAAS,EACTC,KAAK,EACLC,YAAY,EACZC,QAAQ,EACRC,UAAU,EACVC,YAAY,EACZC,aAAa,EACbC,YAAY,EACZC,aAAa,EACb,GAAGC,YACJ,GAAGhB;IAEJ,MAAM,EAAEiB,UAAU,EAAEC,UAAU,EAAEC,iBAAiB,EAAE,GAAGC,IAAAA,mBAAY,EAAC;QACjE,GAAGpB,KAAK;QACR,qBAAqBS;IACvB;IAEA,MAAMY,SAASjB,SAAS;IAExB,MAAMkB,oBAAoBC,IAAAA,2BAAoB,EAAC;QAC7ClB;QACAC;IACF;IAEA,MAAMkB,mBAAmB;QACvB,iBAAiBtB;QACjB,iBAAiB,OAAOS,eAAe,aAAaA;QACpD,iBAAiB,OAAOA,eAAe,aAAa,CAACA;QACrD,eAAeU;QACf,CAACd,aAAa,GAAG,EAAE;IACrB;IAEA,MAAMkB,gBAAgBC,IAAAA,eAAU,EAACJ,mBAAmBE;IAEpD,MAAMG,eAAeD,IAAAA,eAAU,EAC7B;QACE,iBAAiBxB;IACnB,GACAoB;IAGF,MAAMM,mBAAmBF,IAAAA,eAAU,EACjC,UACA,qBACAD;IAGF,MAAMI,eAAeH,IAAAA,eAAU,EAC7B,UACA,4BACAD;IAGF,MAAMK,sBAAsBJ,IAAAA,eAAU,EACpC,oCACAD;IAGF,qBACE,sBAACM,YAAK;QAACC,KAAI;QAAKzB,WAAWqB;;YACxBpB,SAAS,CAACa,wBACT,qBAACY,YAAK;gBAAE,GAAGf,UAAU;gBAAEX,WAAWoB;0BAC/BnB;;0BAGL,sBAAC0B;gBAAI3B,WAAU;;oBACZ4B,IAAAA,8CAAsB,EACrBtB,eACAc,cACA;kCAEF,sBAACO;wBAAI3B,WAAWuB;;4BACbK,IAAAA,8CAAsB,EACrBvB,cACAe,cACA;0CAEF,qBAACS;gCACE,GAAGpB,UAAU;gCACb,GAAGC,UAAU;gCACdhB,KAAKA;gCACLG,MAAMA;gCACNG,WAAWsB;gCACXQ,UAAUlC;;4BAEXgC,IAAAA,8CAAsB,EACrBrB,cACAa,cACA;;;oBAGHQ,IAAAA,8CAAsB,EACrBpB,eACAY,cACA;;;YAGHjB,0BACC,qBAACwB;gBAAI3B,WAAU;0BAA+BG;;0BAGhD,qBAAC4B,0BAAY;gBAAE,GAAGnB,iBAAiB;gBAAEV,cAAcA;;;;AAGzD;AAEFX,UAAUyC,WAAW,GAAG"}
|
|
@@ -15,10 +15,10 @@ function _interop_require_default(obj) {
|
|
|
15
15
|
default: obj
|
|
16
16
|
};
|
|
17
17
|
}
|
|
18
|
-
const adornmentWithClassName = (component, className)=>{
|
|
18
|
+
const adornmentWithClassName = (component, validationClasses, className)=>{
|
|
19
19
|
if (!component) return null;
|
|
20
20
|
return (0, _react.cloneElement)(component, {
|
|
21
|
-
className: (0, _classnames.default)(component.props.className, className)
|
|
21
|
+
className: (0, _classnames.default)(component.props.className, validationClasses, className)
|
|
22
22
|
});
|
|
23
23
|
};
|
|
24
24
|
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../../../src/components/TextField/adornmentWithClassName.ts"],"sourcesContent":["import classNames from \"classnames\";\nimport { ReactElement, cloneElement } from \"react\";\n\nexport const adornmentWithClassName = (\n component?: ReactElement,\n className?: string,\n) => {\n if (!component) return null;\n\n return cloneElement(component, {\n className: classNames(component.props.className
|
|
1
|
+
{"version":3,"sources":["../../../../src/components/TextField/adornmentWithClassName.ts"],"sourcesContent":["import classNames from \"classnames\";\nimport { ReactElement, cloneElement } from \"react\";\n\nexport const adornmentWithClassName = (\n component?: ReactElement,\n validationClasses?: string,\n className?: string,\n) => {\n if (!component) return null;\n\n return cloneElement(component, {\n className: classNames(\n component.props.className,\n validationClasses,\n className,\n ),\n });\n};\n"],"names":["adornmentWithClassName","component","validationClasses","className","cloneElement","classNames","props"],"mappings":";;;;+BAGaA;;;eAAAA;;;mEAHU;uBACoB;;;;;;AAEpC,MAAMA,yBAAyB,CACpCC,WACAC,mBACAC;IAEA,IAAI,CAACF,WAAW,OAAO;IAEvB,OAAOG,IAAAA,mBAAY,EAACH,WAAW;QAC7BE,WAAWE,IAAAA,mBAAU,EACnBJ,UAAUK,KAAK,CAACH,SAAS,EACzBD,mBACAC;IAEJ;AACF"}
|