@simplybusiness/mobius 5.12.0 → 5.13.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 (41) hide show
  1. package/CHANGELOG.md +17 -0
  2. package/dist/cjs/components/AddressLookup/AddressLookup.js +2 -1
  3. package/dist/cjs/components/AddressLookup/AddressLookup.js.map +1 -1
  4. package/dist/cjs/components/AddressLookup/LoqateAddressLookupError.js +32 -0
  5. package/dist/cjs/components/AddressLookup/LoqateAddressLookupError.js.map +1 -0
  6. package/dist/cjs/components/AddressLookup/LoqateAddressLookupService.js +11 -6
  7. package/dist/cjs/components/AddressLookup/LoqateAddressLookupService.js.map +1 -1
  8. package/dist/cjs/components/AddressLookup/utils.js +15 -0
  9. package/dist/cjs/components/AddressLookup/utils.js.map +1 -0
  10. package/dist/cjs/components/Combobox/Combobox.js +18 -7
  11. package/dist/cjs/components/Combobox/Combobox.js.map +1 -1
  12. package/dist/cjs/tsconfig.tsbuildinfo +1 -1
  13. package/dist/esm/components/AddressLookup/AddressLookup.js +2 -1
  14. package/dist/esm/components/AddressLookup/AddressLookup.js.map +1 -1
  15. package/dist/esm/components/AddressLookup/LoqateAddressLookupError.js +22 -0
  16. package/dist/esm/components/AddressLookup/LoqateAddressLookupError.js.map +1 -0
  17. package/dist/esm/components/AddressLookup/LoqateAddressLookupService.js +11 -6
  18. package/dist/esm/components/AddressLookup/LoqateAddressLookupService.js.map +1 -1
  19. package/dist/esm/components/AddressLookup/types.js.map +1 -1
  20. package/dist/esm/components/AddressLookup/utils.js +5 -0
  21. package/dist/esm/components/AddressLookup/utils.js.map +1 -0
  22. package/dist/esm/components/Combobox/Combobox.js +19 -8
  23. package/dist/esm/components/Combobox/Combobox.js.map +1 -1
  24. package/dist/types/components/AddressLookup/LoqateAddressLookupError.d.ts +5 -0
  25. package/dist/types/components/AddressLookup/LoqateAddressLookupError.test.d.ts +1 -0
  26. package/dist/types/components/AddressLookup/LoqateAddressLookupService.d.ts +4 -3
  27. package/dist/types/components/AddressLookup/__mocks__/LoqateAddressLookupService.d.ts +4 -3
  28. package/dist/types/components/AddressLookup/types.d.ts +24 -6
  29. package/dist/types/components/AddressLookup/utils.d.ts +2 -0
  30. package/package.json +2 -2
  31. package/src/components/AddressLookup/AddressLookup.test.tsx +19 -6
  32. package/src/components/AddressLookup/AddressLookup.tsx +6 -6
  33. package/src/components/AddressLookup/LoqateAddressLookupError.test.tsx +31 -0
  34. package/src/components/AddressLookup/LoqateAddressLookupError.tsx +11 -0
  35. package/src/components/AddressLookup/LoqateAddressLookupService.test.tsx +24 -4
  36. package/src/components/AddressLookup/LoqateAddressLookupService.tsx +23 -13
  37. package/src/components/AddressLookup/__mocks__/LoqateAddressLookupService.tsx +5 -4
  38. package/src/components/AddressLookup/types.tsx +25 -7
  39. package/src/components/AddressLookup/utils.ts +7 -0
  40. package/src/components/Combobox/Combobox.test.tsx +78 -0
  41. package/src/components/Combobox/Combobox.tsx +25 -9
package/CHANGELOG.md CHANGED
@@ -1,5 +1,22 @@
1
1
  # Changelog
2
2
 
3
+ ## 5.13.0
4
+
5
+ ### Minor Changes
6
+
7
+ - 1e26027: Add event handlers and auto-select on blur
8
+
9
+ ### Patch Changes
10
+
11
+ - 757a953: Improve `AddressLookup` types
12
+
13
+ ## 5.12.1
14
+
15
+ ### Patch Changes
16
+
17
+ - Updated dependencies [45acef6]
18
+ - @simplybusiness/icons@4.17.0
19
+
3
20
  ## 5.12.0
4
21
 
5
22
  ### Minor Changes
@@ -13,11 +13,12 @@ const _icons = require("@simplybusiness/icons");
13
13
  const _react = require("react");
14
14
  const _Combobox = require("../Combobox");
15
15
  const _Icon = require("../Icon");
16
+ const _utils = require("./utils");
16
17
  function optionsFromResponse({ Items }, addressLookupService) {
17
18
  if (!Items || !Array.isArray(Items)) {
18
19
  throw Error("No address found");
19
20
  }
20
- if (Items[0].Error) {
21
+ if ((0, _utils.isLoqateErrorResponse)(Items)) {
21
22
  throw Error(Items[0].Description);
22
23
  }
23
24
  return Items.map((item)=>({
@@ -1 +1 @@
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"}
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 AddressLookupProps,\n LoqateSearchResponse,\n LoqateSearchResultItem,\n} from \"./types\";\nimport { isLoqateErrorResponse } from \"./utils\";\n\nfunction optionsFromResponse(\n { Items }: LoqateSearchResponse,\n addressLookupService: LoqateAddressLookupService,\n) {\n if (!Items || !Array.isArray(Items)) {\n throw Error(\"No address found\");\n }\n\n if (isLoqateErrorResponse(Items)) {\n throw Error(Items[0].Description);\n }\n\n return (Items as LoqateSearchResultItem[]).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","isLoqateErrorResponse","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;uBAQiB;AAEtC,SAASC,oBACP,EAAEC,KAAK,EAAwB,EAC/BC,oBAAgD;IAEhD,IAAI,CAACD,SAAS,CAACE,MAAMC,OAAO,CAACH,QAAQ;QACnC,MAAMI,MAAM;IACd;IAEA,IAAIC,IAAAA,4BAAqB,EAACL,QAAQ;QAChC,MAAMI,MAAMJ,KAAK,CAAC,EAAE,CAACM,WAAW;IAClC;IAEA,OAAO,AAACN,MAAmCO,GAAG,CAACC,CAAAA,OAAS,CAAA;YACtDC,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,MAAMhB,qBAAqBiB,QAAQ,CAACV,KAAKE,EAAE;gBAC1D,OAAOX,oBAAoBkB,QAAQhB;YACrC;QACR,CAAA;AACF;AAEO,MAAMH,8BAGTqB,IAAAA,iBAAU,EACZ,CACE,EACElB,oBAAoB,EACpBmB,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,MAAMhC,qBAAqBiC,MAAM,CAACF;YACnDJ,SAAS;YACT,OAAO7B,oBAAoBkC,UAAUhC;QACvC,EAAE,OAAOkC,GAAG;YACVP,SAASO;YACT,OAAO,EAAE;QACX;IACF,GACA;QAAClC;QAAsB2B;KAAS;IAGlC,MAAMQ,iBAAiB,CAACC;QACtBT,SAAS;QACT,OACE3B,oBACE,+BAA+B;SAC9BqC,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"}
@@ -0,0 +1,32 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "LoqateAddressLookupError", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return LoqateAddressLookupError;
9
+ }
10
+ });
11
+ function _define_property(obj, key, value) {
12
+ if (key in obj) {
13
+ Object.defineProperty(obj, key, {
14
+ value: value,
15
+ enumerable: true,
16
+ configurable: true,
17
+ writable: true
18
+ });
19
+ } else {
20
+ obj[key] = value;
21
+ }
22
+ return obj;
23
+ }
24
+ class LoqateAddressLookupError extends Error {
25
+ constructor(response){
26
+ super(response.Description), _define_property(this, "response", void 0);
27
+ this.name = "LoqateAddressLookupError";
28
+ this.response = response;
29
+ }
30
+ }
31
+
32
+ //# sourceMappingURL=LoqateAddressLookupError.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/AddressLookup/LoqateAddressLookupError.tsx"],"sourcesContent":["import { LoqateErrorItem } from \"./types\";\n\nexport class LoqateAddressLookupError extends Error {\n response: LoqateErrorItem;\n\n constructor(response: LoqateErrorItem) {\n super(response.Description);\n this.name = \"LoqateAddressLookupError\";\n this.response = response;\n }\n}\n"],"names":["LoqateAddressLookupError","Error","constructor","response","Description","name"],"mappings":";;;;+BAEaA;;;eAAAA;;;;;;;;;;;;;;;;AAAN,MAAMA,iCAAiCC;IAG5CC,YAAYC,QAAyB,CAAE;QACrC,KAAK,CAACA,SAASC,WAAW,GAH5BD,uBAAAA,YAAAA,KAAAA;QAIE,IAAI,CAACE,IAAI,GAAG;QACZ,IAAI,CAACF,QAAQ,GAAGA;IAClB;AACF"}
@@ -8,6 +8,7 @@ Object.defineProperty(exports, "LoqateAddressLookupService", {
8
8
  return LoqateAddressLookupService;
9
9
  }
10
10
  });
11
+ const _LoqateAddressLookupError = require("./LoqateAddressLookupError");
11
12
  function _check_private_redeclaration(obj, privateCollection) {
12
13
  if (privateCollection.has(obj)) {
13
14
  throw new TypeError("Cannot initialize the same private elements twice on an object");
@@ -58,22 +59,26 @@ var /**
58
59
  */ _apiKey = /*#__PURE__*/ new WeakMap();
59
60
  class LoqateAddressLookupService {
60
61
  fetchFromApi(url) {
61
- return fetch(`${_class_private_field_get(this, _baseUrl)}${url}`).then((response)=>response.json());
62
+ return fetch(`${_class_private_field_get(this, _baseUrl)}${url}`).then((response)=>response.json()).then((json)=>{
63
+ var _json_Items;
64
+ if ((_json_Items = json.Items) === null || _json_Items === void 0 ? void 0 : _json_Items.some((item)=>item.Error)) {
65
+ throw new _LoqateAddressLookupError.LoqateAddressLookupError(json);
66
+ }
67
+ return json;
68
+ });
62
69
  }
63
- // TODO: Fix type
64
70
  search(searchTerm) {
65
71
  const url = `${LOQATE_FIND_URL}?Key=${_class_private_field_get(this, _apiKey)}&Text=${searchTerm}`;
66
72
  return this.fetchFromApi(url);
67
73
  }
68
- // TODO: Fix type
69
74
  findById(id) {
70
75
  const url = `${LOQATE_FIND_URL}?Key=${_class_private_field_get(this, _apiKey)}&Container=${id}`;
71
76
  return this.fetchFromApi(url);
72
77
  }
73
- // TODO: Fix type
74
- get(id) {
78
+ async get(id) {
75
79
  const url = `${LOQATE_RETRIEVE_URL}?Key=${_class_private_field_get(this, _apiKey)}&Id=${id}`;
76
- return this.fetchFromApi(url);
80
+ const response = await this.fetchFromApi(url);
81
+ return response.Items[0];
77
82
  }
78
83
  constructor({ baseUrl, apiKey }){
79
84
  _class_private_field_init(this, _baseUrl, {
@@ -1 +1 @@
1
- {"version":3,"sources":["../../../../src/components/AddressLookup/LoqateAddressLookupService.tsx"],"sourcesContent":["const LOQATE_BASE_URL = \"https://api.addressy.com/Capture/Interactive\";\nconst LOQATE_FIND_URL = \"/Find/v1.00/json3.ws\";\nconst LOQATE_RETRIEVE_URL = \"/Retrieve/v1.2/json3.ws\";\n\nexport class LoqateAddressLookupService {\n /**\n * Base URL for the Loqate API\n */\n #baseUrl: string;\n\n /**\n * API key for the Loqate API\n */\n #apiKey?: string;\n\n constructor({ baseUrl, apiKey }: { baseUrl?: string; apiKey?: string }) {\n this.#apiKey = apiKey;\n this.#baseUrl = baseUrl || LOQATE_BASE_URL;\n }\n\n private fetchFromApi<TResponse = unknown>(url: string): Promise<unknown> {\n return fetch(`${this.#baseUrl}${url}`).then(response =>\n response.json(),\n ) as Promise<TResponse>;\n }\n\n // TODO: Fix type\n search(searchTerm: string): Promise<any> {\n const url = `${LOQATE_FIND_URL}?Key=${this.#apiKey}&Text=${searchTerm}`;\n return this.fetchFromApi(url);\n }\n\n // TODO: Fix type\n findById(id: string): Promise<any> {\n const url = `${LOQATE_FIND_URL}?Key=${this.#apiKey}&Container=${id}`;\n return this.fetchFromApi(url);\n }\n\n // TODO: Fix type\n get(id: string): Promise<any> {\n const url = `${LOQATE_RETRIEVE_URL}?Key=${this.#apiKey}&Id=${id}`;\n return this.fetchFromApi(url);\n }\n}\n"],"names":["LoqateAddressLookupService","LOQATE_BASE_URL","LOQATE_FIND_URL","LOQATE_RETRIEVE_URL","fetchFromApi","url","fetch","then","response","json","search","searchTerm","findById","id","get","constructor","baseUrl","apiKey"],"mappings":";;;;+BAIaA;;;eAAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAJb,MAAMC,kBAAkB;AACxB,MAAMC,kBAAkB;AACxB,MAAMC,sBAAsB;IAG1B;;GAEC,GACD,wCAEA;;GAEC,GACD;AATK,MAAMH;IAgBHI,aAAkCC,GAAW,EAAoB;QACvE,OAAOC,MAAM,4BAAG,IAAI,EAAC,YAAWD,KAAK,EAAEE,IAAI,CAACC,CAAAA,WAC1CA,SAASC,IAAI;IAEjB;IAEA,iBAAiB;IACjBC,OAAOC,UAAkB,EAAgB;QACvC,MAAMN,MAAM,GAAGH,gBAAgB,KAAK,2BAAE,IAAI,EAAC,SAAQ,MAAM,EAAES,YAAY;QACvE,OAAO,IAAI,CAACP,YAAY,CAACC;IAC3B;IAEA,iBAAiB;IACjBO,SAASC,EAAU,EAAgB;QACjC,MAAMR,MAAM,GAAGH,gBAAgB,KAAK,2BAAE,IAAI,EAAC,SAAQ,WAAW,EAAEW,IAAI;QACpE,OAAO,IAAI,CAACT,YAAY,CAACC;IAC3B;IAEA,iBAAiB;IACjBS,IAAID,EAAU,EAAgB;QAC5B,MAAMR,MAAM,GAAGF,oBAAoB,KAAK,2BAAE,IAAI,EAAC,SAAQ,IAAI,EAAEU,IAAI;QACjE,OAAO,IAAI,CAACT,YAAY,CAACC;IAC3B;IA3BAU,YAAY,EAAEC,OAAO,EAAEC,MAAM,EAAyC,CAAE;QAPxE,gCAAA;;mBAAA,KAAA;;QAKA,gCAAA;;mBAAA,KAAA;;uCAGO,SAAUA;uCACV,UAAWD,WAAWf;IAC7B;AAyBF"}
1
+ {"version":3,"sources":["../../../../src/components/AddressLookup/LoqateAddressLookupService.tsx"],"sourcesContent":["import { LoqateAddressLookupError } from \"./LoqateAddressLookupError\";\nimport type {\n LoqateSearchResponse,\n LoqateAddressDetailsResponse,\n LoqateAddressDetailsItem,\n} from \"./types\";\n\nconst LOQATE_BASE_URL = \"https://api.addressy.com/Capture/Interactive\";\nconst LOQATE_FIND_URL = \"/Find/v1.00/json3.ws\";\nconst LOQATE_RETRIEVE_URL = \"/Retrieve/v1.2/json3.ws\";\n\nexport class LoqateAddressLookupService {\n /**\n * Base URL for the Loqate API\n */\n #baseUrl: string;\n\n /**\n * API key for the Loqate API\n */\n #apiKey?: string;\n\n constructor({ baseUrl, apiKey }: { baseUrl?: string; apiKey?: string }) {\n this.#apiKey = apiKey;\n this.#baseUrl = baseUrl || LOQATE_BASE_URL;\n }\n\n private fetchFromApi<TResponse = unknown>(url: string): Promise<TResponse> {\n return fetch(`${this.#baseUrl}${url}`)\n .then(response => response.json())\n .then(json => {\n if (json.Items?.some((item: any) => item.Error)) {\n throw new LoqateAddressLookupError(json);\n }\n return json;\n });\n }\n\n search(searchTerm: string): Promise<LoqateSearchResponse> {\n const url = `${LOQATE_FIND_URL}?Key=${this.#apiKey}&Text=${searchTerm}`;\n return this.fetchFromApi<LoqateSearchResponse>(url);\n }\n\n findById(id: string): Promise<LoqateSearchResponse> {\n const url = `${LOQATE_FIND_URL}?Key=${this.#apiKey}&Container=${id}`;\n return this.fetchFromApi<LoqateSearchResponse>(url);\n }\n\n async get(id: string): Promise<LoqateAddressDetailsItem> {\n const url = `${LOQATE_RETRIEVE_URL}?Key=${this.#apiKey}&Id=${id}`;\n const response = await this.fetchFromApi<LoqateAddressDetailsResponse>(url);\n return response.Items[0];\n }\n}\n"],"names":["LoqateAddressLookupService","LOQATE_BASE_URL","LOQATE_FIND_URL","LOQATE_RETRIEVE_URL","fetchFromApi","url","fetch","then","response","json","Items","some","item","Error","LoqateAddressLookupError","search","searchTerm","findById","id","get","constructor","baseUrl","apiKey"],"mappings":";;;;+BAWaA;;;eAAAA;;;0CAX4B;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;AAOzC,MAAMC,kBAAkB;AACxB,MAAMC,kBAAkB;AACxB,MAAMC,sBAAsB;IAG1B;;GAEC,GACD,wCAEA;;GAEC,GACD;AATK,MAAMH;IAgBHI,aAAkCC,GAAW,EAAsB;QACzE,OAAOC,MAAM,4BAAG,IAAI,EAAC,YAAWD,KAAK,EAClCE,IAAI,CAACC,CAAAA,WAAYA,SAASC,IAAI,IAC9BF,IAAI,CAACE,CAAAA;gBACAA;YAAJ,KAAIA,cAAAA,KAAKC,KAAK,cAAVD,kCAAAA,YAAYE,IAAI,CAAC,CAACC,OAAcA,KAAKC,KAAK,GAAG;gBAC/C,MAAM,IAAIC,kDAAwB,CAACL;YACrC;YACA,OAAOA;QACT;IACJ;IAEAM,OAAOC,UAAkB,EAAiC;QACxD,MAAMX,MAAM,GAAGH,gBAAgB,KAAK,2BAAE,IAAI,EAAC,SAAQ,MAAM,EAAEc,YAAY;QACvE,OAAO,IAAI,CAACZ,YAAY,CAAuBC;IACjD;IAEAY,SAASC,EAAU,EAAiC;QAClD,MAAMb,MAAM,GAAGH,gBAAgB,KAAK,2BAAE,IAAI,EAAC,SAAQ,WAAW,EAAEgB,IAAI;QACpE,OAAO,IAAI,CAACd,YAAY,CAAuBC;IACjD;IAEA,MAAMc,IAAID,EAAU,EAAqC;QACvD,MAAMb,MAAM,GAAGF,oBAAoB,KAAK,2BAAE,IAAI,EAAC,SAAQ,IAAI,EAAEe,IAAI;QACjE,MAAMV,WAAW,MAAM,IAAI,CAACJ,YAAY,CAA+BC;QACvE,OAAOG,SAASE,KAAK,CAAC,EAAE;IAC1B;IA9BAU,YAAY,EAAEC,OAAO,EAAEC,MAAM,EAAyC,CAAE;QAPxE,gCAAA;;mBAAA,KAAA;;QAKA,gCAAA;;mBAAA,KAAA;;uCAGO,SAAUA;uCACV,UAAWD,WAAWpB;IAC7B;AA4BF"}
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", {
3
+ value: true
4
+ });
5
+ Object.defineProperty(exports, "isLoqateErrorResponse", {
6
+ enumerable: true,
7
+ get: function() {
8
+ return isLoqateErrorResponse;
9
+ }
10
+ });
11
+ function isLoqateErrorResponse(response) {
12
+ return Array.isArray(response) && response.some((item)=>"Error" in item);
13
+ }
14
+
15
+ //# sourceMappingURL=utils.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"sources":["../../../../src/components/AddressLookup/utils.ts"],"sourcesContent":["import type { LoqateErrorResponse } from \"./types\";\n\nexport function isLoqateErrorResponse(\n response: any,\n): response is LoqateErrorResponse {\n return Array.isArray(response) && response.some(item => \"Error\" in item);\n}\n"],"names":["isLoqateErrorResponse","response","Array","isArray","some","item"],"mappings":";;;;+BAEgBA;;;eAAAA;;;AAAT,SAASA,sBACdC,QAAa;IAEb,OAAOC,MAAMC,OAAO,CAACF,aAAaA,SAASG,IAAI,CAACC,CAAAA,OAAQ,WAAWA;AACrE"}
@@ -24,7 +24,7 @@ function _interop_require_default(obj) {
24
24
  };
25
25
  }
26
26
  const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
27
- const { id, defaultValue, options, asyncOptions, delay, minLength, onSelected, className, placeholder, icon, ...otherProps } = props;
27
+ const { id, defaultValue, options, asyncOptions, delay, minLength, onSelected, className, placeholder, icon, onBlur, onFocus, onChange, ...otherProps } = props;
28
28
  // Avoid re-fetching after selecting an option
29
29
  const skipNextDebounceRef = (0, _react.useRef)(false);
30
30
  const fallbackRef = (0, _react.useRef)(null);
@@ -44,18 +44,14 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
44
44
  const statusId = (0, _react.useId)();
45
45
  const blurTimeoutRef = (0, _react.useRef)(null);
46
46
  const showListbox = isOpen && filteredOptions.length > 0;
47
- const handleFocus = ()=>{
47
+ const handleFocus = (e)=>{
48
48
  if (filteredOptions.length === 0) return;
49
49
  if (blurTimeoutRef.current) {
50
50
  clearTimeout(blurTimeoutRef.current);
51
51
  blurTimeoutRef.current = null;
52
52
  }
53
53
  setIsOpen(true);
54
- };
55
- const handleBlur = ()=>{
56
- blurTimeoutRef.current = setTimeout(()=>{
57
- setIsOpen(false);
58
- }, 150);
54
+ onFocus === null || onFocus === void 0 ? void 0 : onFocus(e);
59
55
  };
60
56
  (0, _hooks.useOnUnmount)(()=>{
61
57
  if (blurTimeoutRef.current) {
@@ -67,6 +63,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
67
63
  setInputValue(newValue);
68
64
  setIsOpen(true);
69
65
  clearHighlight();
66
+ onChange === null || onChange === void 0 ? void 0 : onChange(e);
70
67
  };
71
68
  const handleOptionSelect = (option)=>{
72
69
  const value = (0, _utils.getOptionValue)(option);
@@ -107,6 +104,19 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
107
104
  }
108
105
  return `${listboxId}-option-${highlightedIndex}`;
109
106
  };
107
+ const handleBlur = (e)=>{
108
+ // Force selection if user has matched an entry
109
+ const typedText = inputValue.trim().toLowerCase();
110
+ const highlightedOption = getHighlightedOption();
111
+ const label = (0, _utils.getOptionLabel)(highlightedOption);
112
+ if (typedText === (label === null || label === void 0 ? void 0 : label.toLowerCase())) {
113
+ handleOptionSelect(highlightedOption);
114
+ }
115
+ blurTimeoutRef.current = setTimeout(()=>{
116
+ setIsOpen(false);
117
+ }, 150);
118
+ onBlur === null || onBlur === void 0 ? void 0 : onBlur(e);
119
+ };
110
120
  const handleKeyDown = (e)=>{
111
121
  switch(e.key){
112
122
  case "ArrowDown":
@@ -140,6 +150,7 @@ const ComboboxInner = /*#__PURE__*/ (0, _react.forwardRef)((props, ref)=>{
140
150
  break;
141
151
  case "Escape":
142
152
  e.preventDefault();
153
+ setInputValue("");
143
154
  setIsOpen(false);
144
155
  clearHighlight();
145
156
  break;
@@ -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 { 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":";;;;+BA+OaA;;;eAAAA;;;;+DA/OU;uBAC6B;uBACvB;2BACH;gCACK;yBACP;sCAEa;oCACF;uBACW;;;;;;AAE9C,MAAMC,8BAAgBC,IAAAA,iBAAU,EAC9B,CAA2BC,OAAyBC;IAClD,MAAM,EACJC,EAAE,EACFC,YAAY,EACZC,OAAO,EACPC,YAAY,EACZC,KAAK,EACLC,SAAS,EACTC,UAAU,EACVC,SAAS,EACTC,WAAW,EACXC,IAAI,EACJ,GAAGC,YACJ,GAAGZ;IAEJ,8CAA8C;IAC9C,MAAMa,sBAAsBC,IAAAA,aAAM,EAAC;IACnC,MAAMC,cAAcD,IAAAA,aAAM,EAAmB;IAC7C,MAAM,CAACE,YAAYC,cAAc,GAAGC,IAAAA,eAAQ,EAACf,gBAAgB;IAC7D,MAAM,CAACgB,QAAQC,UAAU,GAAGF,IAAAA,eAAQ,EAAC;IACrC,MAAM,EAAEG,eAAe,EAAEC,qBAAqB,EAAEC,SAAS,EAAE,GACzDC,IAAAA,sCAAkB,EAAC;QACjBpB;QACAC;QACAW;QACAV;QACAC;QACAM;IACF;IACF,MAAM,EACJY,gBAAgB,EAChBC,qBAAqB,EACrBC,mBAAmB,EACnBC,uBAAuB,EACvBC,oBAAoB,EACpBC,mBAAmB,EACnBC,cAAc,EACf,GAAGC,IAAAA,0CAAoB,EAACX;IAEzB,MAAMY,WAAWhC,OAAOc;IACxB,MAAMmB,YAAYC,IAAAA,YAAK;IACvB,MAAMC,WAAWD,IAAAA,YAAK;IACtB,MAAME,iBAAiBvB,IAAAA,aAAM,EAAwB;IACrD,MAAMwB,cAAcnB,UAAUE,gBAAgBkB,MAAM,GAAG;IAEvD,MAAMC,cAAc;QAClB,IAAInB,gBAAgBkB,MAAM,KAAK,GAAG;QAClC,IAAIF,eAAeI,OAAO,EAAE;YAC1BC,aAAaL,eAAeI,OAAO;YACnCJ,eAAeI,OAAO,GAAG;QAC3B;QACArB,UAAU;IACZ;IAEA,MAAMuB,aAAa;QACjBN,eAAeI,OAAO,GAAGG,WAAW;YAClCxB,UAAU;QACZ,GAAG;IACL;IAEAyB,IAAAA,mBAAY,EAAC;QACX,IAAIR,eAAeI,OAAO,EAAE;YAC1BC,aAAaL,eAAeI,OAAO;QACrC;IACF;IAEA,MAAMK,oBAAoB,CAACC;QACzB,MAAMC,WAAWD,EAAEE,MAAM,CAACC,KAAK;QAC/BjC,cAAc+B;QACd5B,UAAU;QACVW;IACF;IAEA,MAAMoB,qBAAqB,CAACC;QAC1B,MAAMF,QAAQG,IAAAA,qBAAc,EAACD;QAC7B,IAAI,CAACF,OAAO;QAEZ,kCAAkC;QAClC,IACE,OAAOE,WAAW,YAClB,cAAcA,UACdA,OAAOE,QAAQ,IACf,OAAOF,OAAOE,QAAQ,KAAK,YAC3B;YACA,sCAAsC;YACtCV,WAAW,IAAMX,SAASQ,OAAO,CAACc,KAAK,IAAI;YAC3CjC,sBAAsB8B,OAAOE,QAAQ;YACrC;QACF;QAEA,wDAAwD;QACxDzC,oBAAoB4B,OAAO,GAAG;QAE9BrB,UAAU;QACVH,cAAciC;QACd1C,uBAAAA,iCAAAA,WAAa4C;IACf;IAEA,MAAMI,iBAAiB;QACrB,IAAIC,IAAAA,oBAAa,EAACpC,kBAAkB;gBAC3BA;YAAP,QAAOA,oBAAAA,eAAe,CAAC,EAAE,cAAlBA,wCAAAA,kBAAoBjB,OAAO,CAAC,EAAE;QACvC;QAEA,OAAOiB,eAAe,CAAC,EAAE;IAC3B;IAEA,MAAMqC,uBAAuB;QAC3B,IAAIjC,qBAAqB,CAAC,GAAG,OAAOkC;QAEpC,IAAIF,IAAAA,oBAAa,EAACpC,kBAAkB;YAClC,MAAMuC,QAAQvC,eAAe,CAACK,sBAAsB;YACpD,OAAOkC,kBAAAA,4BAAAA,MAAOxD,OAAO,CAACqB,iBAAiB;QACzC;QAEA,OAAOJ,eAAe,CAACI,iBAAiB;IAC1C;IAEA,MAAMoC,yBAAyB;QAC7B,MAAMT,SAASM;QACf,IAAI,CAACN,QAAQ,OAAOO;QAEpB,IAAIF,IAAAA,oBAAa,EAACpC,kBAAkB;YAClC,OAAO,GAAGa,UAAU,QAAQ,EAAER,sBAAsB,CAAC,EAAED,kBAAkB;QAC3E;QAEA,OAAO,GAAGS,UAAU,QAAQ,EAAET,kBAAkB;IAClD;IAEA,MAAMqC,gBAAgB,CAACf;QACrB,OAAQA,EAAEgB,GAAG;YACX,KAAK;gBACHhB,EAAEiB,cAAc;gBAChB5C,UAAU;gBACVO;gBACA;YACF,KAAK;gBACHoB,EAAEiB,cAAc;gBAChB5C,UAAU;gBACVQ;gBACA;YACF,KAAK;gBACHmB,EAAEiB,cAAc;gBAChB5C,UAAU;gBACVS;gBACA;YACF,KAAK;gBACHkB,EAAEiB,cAAc;gBAChB5C,UAAU;gBACVU;gBACA;YACF,KAAK;gBACHiB,EAAEiB,cAAc;gBAChB,IAAI7C,QAAQ;oBACV,MAAM8C,iBAAiBP,0BAA0BF;oBACjD,IAAIS,gBAAgB;wBAClBd,mBAAmBc;oBACrB;gBACF;gBACA;YACF,KAAK;gBACHlB,EAAEiB,cAAc;gBAChB5C,UAAU;gBACVW;gBACA;YACF;QAEF;IACF;IAEA,MAAMmC,UAAUC,IAAAA,eAAU,EACxB,0BACA;QACE,gCAAgChD;QAChC,+BAA+BI;IACjC,GACAd;IAGF,qBACE,sBAAC2D;QAAIlE,IAAIA;QAAImE,eAAY;QAA2B5D,WAAWyD;;YAC5D3C,2BACC,qBAAC+C,8BAAc;gBACbC,MAAK;gBACLC,aAAU;gBACVtE,IAAIkC;gBACJqC,aAAY;gBACZhE,WAAU;0BACX;;0BAIH,qBAACiE,oBAAS;gBACP,GAAG9D,UAAU;gBACdH,WAAU;gBACV8D,MAAK;gBACLrB,OAAOlC;gBACPN,aAAaA;gBACbiE,SAASnC;gBACToC,QAAQjC;gBACRkC,WAAWf;gBACXgB,UAAUhC;gBACViC,cAAa;gBACbC,oBAAkBzD,YAAYa,WAAWuB;gBACzCsB,qBAAkB;gBAClBC,iBAAc;gBACdC,iBAAejD;gBACfkD,iBAAejE;gBACfkE,yBACE5D,qBAAqB,CAAC,IAAIkC,YAAYE;gBAExCyB,cAAc3E;gBACdV,KAAKgC;;YAENK,6BACC,qBAACiD,gBAAO;gBACNrF,IAAIgC;gBACJ9B,SAASiB;gBACTI,kBAAkBA;gBAClBC,uBAAuBA;gBACvB8D,gBAAgBrC;;;;AAK1B;AAGK,MAAMtD,WAAWC"}
1
+ {"version":3,"sources":["../../../../src/components/Combobox/Combobox.tsx"],"sourcesContent":["import classNames from \"classnames/dedupe\";\nimport { FocusEvent, 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 { getOptionLabel, 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 onBlur,\n onFocus,\n onChange,\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 = (e: FocusEvent) => {\n if (filteredOptions.length === 0) return;\n if (blurTimeoutRef.current) {\n clearTimeout(blurTimeoutRef.current);\n blurTimeoutRef.current = null;\n }\n setIsOpen(true);\n onFocus?.(e);\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 onChange?.(e);\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 handleBlur = (e: FocusEvent<Element, Element>) => {\n // Force selection if user has matched an entry\n const typedText = inputValue.trim().toLowerCase();\n const highlightedOption = getHighlightedOption();\n const label = getOptionLabel(highlightedOption);\n\n if (typedText === label?.toLowerCase()) {\n handleOptionSelect(highlightedOption as T);\n }\n\n blurTimeoutRef.current = setTimeout(() => {\n setIsOpen(false);\n }, 150);\n onBlur?.(e);\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 setInputValue(\"\");\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","onBlur","onFocus","onChange","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","e","current","clearTimeout","useOnUnmount","handleInputChange","newValue","target","value","handleOptionSelect","option","getOptionValue","callback","setTimeout","focus","getFirstOption","isOptionGroup","getHighlightedOption","undefined","group","getHighlightedOptionId","handleBlur","typedText","trim","toLowerCase","highlightedOption","label","getOptionLabel","handleKeyDown","key","preventDefault","selectedOption","classes","classNames","div","data-testid","VisuallyHidden","role","aria-live","elementType","TextField","onKeyDown","autoComplete","aria-describedby","aria-autocomplete","aria-haspopup","aria-controls","aria-expanded","aria-activedescendant","prefixInside","Listbox","onOptionSelect"],"mappings":"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"}