@open-pioneer/search 1.4.0-dev.20260727093741 → 1.5.0-dev.20260910103330

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 CHANGED
@@ -1,16 +1,32 @@
1
1
  # @open-pioneer/search
2
2
 
3
- ## 1.4.0-dev.20260727093741
3
+ ## 1.5.0-dev.20260910103330
4
+
5
+ ### Minor Changes
6
+
7
+ - f790fda: Update to Chakra 3.37.0
8
+
9
+ ### Patch Changes
10
+
11
+ - 3203815: Update code to match new react linting rules from oxlint.
12
+ - 09ec7c7: Updated dependencies
13
+
14
+ ## 1.4.0
4
15
 
5
16
  ### Minor Changes
6
17
 
7
18
  - c30396d: Update Chakra to 3.36.1
19
+ - d862003: Update to trails core-packages 4.7.0
8
20
  - 0b9143d: Add `searchAndSelect` to perform programmatic searches, update search results in the UI, and automatically select the first matching result.
9
21
  The method returns the selected `SearchResult` (and its `SearchSource`) or `undefined` when no match is found.
10
22
  - 0b9143d: The `onSelect` callback now receives an additional property `trigger` (`"user"` or `"api-select"`) support different reactions based on the way a result was selected:
11
23
 
12
- - `user` indicates that event was triggered by the end user
13
- - `api-select` indicates that the selection was made via the API (i.e. `searchAndSelect`).
24
+ - `user` indicates that event was triggered by the end user
25
+ - `api-select` indicates that the selection was made via the API (i.e. `searchAndSelect`).
26
+
27
+ ### Patch Changes
28
+
29
+ - c16a401: Migrated from eslint to oxlint and from prettier to oxfmt.
14
30
 
15
31
  ## 1.3.0
16
32
 
@@ -19,8 +35,8 @@
19
35
  - 9b5d5f3: Support for new common container props (role, aria-\*, data-\* and css)
20
36
  - d05a3b8: Refactor user interface: now based on chakra's combobox instead of react-select.
21
37
 
22
- NOTE: This causes changes to many internal CSS classes and attributes.
23
- The UX of the control should be the same.
38
+ NOTE: This causes changes to many internal CSS classes and attributes.
39
+ The UX of the control should be the same.
24
40
 
25
41
  - d54ccfd: Update to Chakra UI 3.35.0
26
42
  - 206b397: Update to trails core packages 4.6.0
@@ -57,8 +73,8 @@
57
73
 
58
74
  - 0485014: Introduce search API and `resetInput` method (see [PR](https://github.com/open-pioneer/trails-openlayers-base-packages/pull/493)).
59
75
 
60
- The search API allows programmatic access to the search component.
61
- Currently, it provides a method to reset the search input field.
76
+ The search API allows programmatic access to the search component.
77
+ Currently, it provides a method to reset the search input field.
62
78
 
63
79
  - 2732052: Icons have been changed to unify the appearance of the components. Preferably, Lucide react-icons are used.
64
80
  - 2abcaaf: Update to chakra-ui 3.28.0
@@ -81,16 +97,16 @@
81
97
 
82
98
  - 9376a74: Ensure that icons and other decorative elements are hidden from the screen reader using the `aria-hidden="true"` attribute.
83
99
 
84
- The easiest way to do that is to wrap icons into chakra's `<Icon />` component, for example:
100
+ The easiest way to do that is to wrap icons into chakra's `<Icon />` component, for example:
85
101
 
86
- ```tsx
87
- import { Icon } from "@chakra-ui/react";
88
- import { FiX } from "react-icons/fi";
102
+ ```tsx
103
+ import { Icon } from "@chakra-ui/react";
104
+ import { FiX } from "react-icons/fi";
89
105
 
90
- <Icon>
91
- <FiX />
92
- </Icon>;
93
- ```
106
+ <Icon>
107
+ <FiX />
108
+ </Icon>;
109
+ ```
94
110
 
95
111
  ## 0.10.0
96
112
 
@@ -99,17 +115,17 @@
99
115
  - 193068a: Deprecate the `mapId` property on React components.
100
116
  Use the `MapModel` directly instead to pass a reference to the map.
101
117
 
102
- Example:
118
+ Example:
103
119
 
104
- ```tsx
105
- // Default map for entire component tree
106
- <DefaultMapProvider map={mapModel}>
107
- <Toc />
108
- </DefaultMapProvider>
120
+ ```tsx
121
+ // Default map for entire component tree
122
+ <DefaultMapProvider map={mapModel}>
123
+ <Toc />
124
+ </DefaultMapProvider>
109
125
 
110
- // Map for specific component
111
- <Toc map={mapModel} />
112
- ```
126
+ // Map for specific component
127
+ <Toc map={mapModel} />
128
+ ```
113
129
 
114
130
  ### Patch Changes
115
131
 
@@ -133,9 +149,9 @@
133
149
  ### Minor Changes
134
150
 
135
151
  - 2fa8020: Update trails core package dependencies.
136
- - Also updates Chakra UI to the latest 2.x version and Chakra React Select to version 5.
137
- - Removes any obsolete references to `@chakra-ui/system`.
138
- This dependency seems to be no longer required and may lead to duplicate packages in your dependency tree.
152
+ - Also updates Chakra UI to the latest 2.x version and Chakra React Select to version 5.
153
+ - Removes any obsolete references to `@chakra-ui/system`.
154
+ This dependency seems to be no longer required and may lead to duplicate packages in your dependency tree.
139
155
 
140
156
  ### Patch Changes
141
157
 
@@ -154,7 +170,7 @@
154
170
  - 583f1d6: The `mapId` or `map` properties are now optional on individual components.
155
171
  You can use the `DefaultMapProvider` to configure an implicit default value.
156
172
 
157
- Note that configuring _neither_ a default _nor_ an explicit `map` or `mapId` will trigger a runtime error.
173
+ Note that configuring _neither_ a default _nor_ an explicit `map` or `mapId` will trigger a runtime error.
158
174
 
159
175
  - 583f1d6: All UI components in this project now accept the `mapId` (a `string`) _or_ the `map` (a `MapModel`) directly.
160
176
  - a8b3449: Switch to a new versioning strategy.
@@ -1 +1 @@
1
- {"version":3,"file":"SearchController.js","sources":["SearchController.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError, throwAbortError } from \"@open-pioneer/core\";\nimport { MapModel } from \"@open-pioneer/map\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { SearchResult, SearchSource } from \"../api\";\n\nconst LOG = createLogger(sourceId);\n\n/**\n * Group of suggestions returned from one source.\n */\nexport interface ResultGroup {\n label: string;\n source: SearchSource;\n results: SearchResult[];\n}\n\nconst DEFAULT_SEARCH_TYPING_DELAY = 200;\nconst DEFAULT_MAX_RESULTS_PER_SOURCE = 5;\n\nexport class SearchController {\n #mapModel: MapModel;\n\n /**\n * Search sources defined by the developer.\n */\n #sources: SearchSource[] = [];\n\n /**\n * Limits the number of results per source.\n */\n #maxResultsPerSource: number = DEFAULT_MAX_RESULTS_PER_SOURCE;\n\n /**\n * The timeout in millis.\n */\n #searchTypingDelay: number = DEFAULT_SEARCH_TYPING_DELAY;\n\n /**\n * Cancel or abort a previous request.\n */\n #abortController: AbortController | undefined;\n\n constructor(mapModel: MapModel, sources: SearchSource[]) {\n this.#mapModel = mapModel;\n this.#sources = sources;\n }\n\n destroy() {\n this.#abortController?.abort();\n this.#abortController = undefined;\n }\n\n async search(searchTerm: string): Promise<ResultGroup[]> {\n this.#abortController?.abort();\n this.#abortController = undefined;\n if (!searchTerm) {\n return [];\n }\n\n const abort = (this.#abortController = new AbortController());\n try {\n await waitForTimeOut(abort.signal, this.#searchTypingDelay);\n if (abort.signal.aborted) {\n LOG.debug(`search canceled with ${searchTerm}`);\n throwAbortError();\n }\n const settledSearches = await Promise.all(\n this.#sources.map((source) => this.#searchSource(source, searchTerm, abort.signal))\n );\n return settledSearches.filter((s): s is ResultGroup => s != null);\n } finally {\n if (this.#abortController === abort) {\n this.#abortController = undefined;\n }\n }\n }\n\n async #searchSource(\n source: SearchSource,\n searchTerm: string,\n signal: AbortSignal\n ): Promise<ResultGroup | undefined> {\n const label = source.label;\n const projection = this.#mapModel.projection;\n try {\n const maxResults = this.#maxResultsPerSource;\n let results = await source.search(searchTerm, {\n maxResults,\n signal,\n mapProjection: projection,\n map: this.#mapModel\n });\n if (results.length > maxResults) {\n results = results.slice(0, maxResults);\n }\n return { label, source, results };\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(`search for source ${label} failed`, e);\n }\n return undefined;\n }\n }\n\n get searchTypingDelay(): number {\n return this.#searchTypingDelay;\n }\n\n set searchTypingDelay(value: number | undefined) {\n this.#searchTypingDelay = value ?? DEFAULT_SEARCH_TYPING_DELAY;\n }\n\n get maxResultsPerSource(): number {\n return this.#maxResultsPerSource;\n }\n\n set maxResultsPerSource(value: number | undefined) {\n this.#maxResultsPerSource = value ?? DEFAULT_MAX_RESULTS_PER_SOURCE;\n }\n\n get sources() {\n return this.#sources;\n }\n}\n\n/**\n * wait for timeouts millis or until signal is aborted, whatever happens first\n */\nasync function waitForTimeOut(signal: AbortSignal, timeoutMillis: number) {\n if (signal.aborted) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n const done = () => {\n signal.removeEventListener(\"abort\", done);\n clearTimeout(timeoutId);\n resolve();\n };\n\n signal.addEventListener(\"abort\", done);\n const timeoutId = setTimeout(done, timeoutMillis);\n });\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"SearchController.js","sources":["SearchController.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { createLogger, isAbortError, throwAbortError } from \"@open-pioneer/core\";\nimport { MapModel } from \"@open-pioneer/map\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { SearchResult, SearchSource } from \"../api\";\n\nconst LOG = createLogger(sourceId);\n\n/**\n * Group of suggestions returned from one source.\n */\nexport interface ResultGroup {\n label: string;\n source: SearchSource;\n results: SearchResult[];\n}\n\nconst DEFAULT_SEARCH_TYPING_DELAY = 200;\nconst DEFAULT_MAX_RESULTS_PER_SOURCE = 5;\n\nexport class SearchController {\n #mapModel: MapModel;\n\n /**\n * Search sources defined by the developer.\n */\n #sources: SearchSource[] = [];\n\n /**\n * Limits the number of results per source.\n */\n #maxResultsPerSource: number = DEFAULT_MAX_RESULTS_PER_SOURCE;\n\n /**\n * The timeout in millis.\n */\n #searchTypingDelay: number = DEFAULT_SEARCH_TYPING_DELAY;\n\n /**\n * Cancel or abort a previous request.\n */\n #abortController: AbortController | undefined;\n\n constructor(mapModel: MapModel, sources: SearchSource[]) {\n this.#mapModel = mapModel;\n this.#sources = sources;\n }\n\n destroy() {\n this.#abortController?.abort();\n this.#abortController = undefined;\n }\n\n async search(searchTerm: string): Promise<ResultGroup[]> {\n this.#abortController?.abort();\n this.#abortController = undefined;\n if (!searchTerm) {\n return [];\n }\n\n const abort = (this.#abortController = new AbortController());\n try {\n await waitForTimeOut(abort.signal, this.#searchTypingDelay);\n if (abort.signal.aborted) {\n LOG.debug(`search canceled with ${searchTerm}`);\n throwAbortError();\n }\n const settledSearches = await Promise.all(\n this.#sources.map((source) => this.#searchSource(source, searchTerm, abort.signal))\n );\n return settledSearches.filter((s): s is ResultGroup => s != null);\n } finally {\n if (this.#abortController === abort) {\n this.#abortController = undefined;\n }\n }\n }\n\n async #searchSource(\n source: SearchSource,\n searchTerm: string,\n signal: AbortSignal\n ): Promise<ResultGroup | undefined> {\n const label = source.label;\n const projection = this.#mapModel.projection;\n try {\n const maxResults = this.#maxResultsPerSource;\n let results = await source.search(searchTerm, {\n maxResults,\n signal,\n mapProjection: projection,\n map: this.#mapModel\n });\n if (results.length > maxResults) {\n results = results.slice(0, maxResults);\n }\n return { label, source, results };\n } catch (e) {\n if (!isAbortError(e)) {\n LOG.error(`search for source ${label} failed`, e);\n }\n return undefined;\n }\n }\n\n get searchTypingDelay(): number {\n return this.#searchTypingDelay;\n }\n\n set searchTypingDelay(value: number | undefined) {\n this.#searchTypingDelay = value ?? DEFAULT_SEARCH_TYPING_DELAY;\n }\n\n get maxResultsPerSource(): number {\n return this.#maxResultsPerSource;\n }\n\n set maxResultsPerSource(value: number | undefined) {\n this.#maxResultsPerSource = value ?? DEFAULT_MAX_RESULTS_PER_SOURCE;\n }\n\n get sources() {\n return this.#sources;\n }\n}\n\n/**\n * wait for timeouts millis or until signal is aborted, whatever happens first\n */\nasync function waitForTimeOut(signal: AbortSignal, timeoutMillis: number) {\n if (signal.aborted) {\n return;\n }\n\n await new Promise<void>((resolve) => {\n const done = () => {\n signal.removeEventListener(\"abort\", done);\n clearTimeout(timeoutId);\n resolve();\n };\n\n signal.addEventListener(\"abort\", done);\n const timeoutId = setTimeout(done, timeoutMillis);\n });\n}\n"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "type": "module",
3
3
  "name": "@open-pioneer/search",
4
- "version": "1.4.0-dev.20260727093741",
4
+ "version": "1.5.0-dev.20260910103330",
5
5
  "description": "This package provides a UI component to perform a search on given search sources.",
6
6
  "keywords": [
7
7
  "open-pioneer-trails"
@@ -14,15 +14,19 @@
14
14
  "directory": "src/packages/search"
15
15
  },
16
16
  "dependencies": {
17
- "@chakra-ui/react": "^3.36.1",
18
- "@open-pioneer/chakra-snippets": "4.7.0-dev.20260727091735",
19
- "@open-pioneer/core": "4.7.0-dev.20260727091735",
20
- "@open-pioneer/react-utils": "4.7.0-dev.20260727091735",
21
- "@open-pioneer/runtime": "4.7.0-dev.20260727091735",
22
- "ol": "^10.9.0",
17
+ "@chakra-ui/react": "^3.37.0",
18
+ "@open-pioneer/chakra-snippets": "4.8.0-dev.20260910101405",
19
+ "@open-pioneer/core": "4.8.0-dev.20260910101405",
20
+ "@open-pioneer/map": "1.5.0-dev.20260910103330",
21
+ "@open-pioneer/react-utils": "4.8.0-dev.20260910101405",
22
+ "@open-pioneer/runtime": "4.8.0-dev.20260910101405",
23
+ "ol": "^10.10.0",
23
24
  "react": "^19.2.8",
24
- "react-icons": "^5.7.0",
25
- "@open-pioneer/map": "1.4.0-dev.20260727093741"
25
+ "react-icons": "^5.7.0"
26
+ },
27
+ "publishConfig": {
28
+ "directory": "dist",
29
+ "linkDirectory": false
26
30
  },
27
31
  "exports": {
28
32
  "./package.json": "./package.json",
package/ui/Search.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"Search.js","sources":["Search.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Box } from \"@chakra-ui/react\";\nimport { MapModelProps, useMapModelValue } from \"@open-pioneer/map\";\nimport { CommonComponentProps, useCommonComponentProps, useEvent } from \"@open-pioneer/react-utils\";\nimport { FC, useEffect, useRef } from \"react\";\nimport {\n SearchApi,\n SearchClearEvent,\n SearchDisposedEvent,\n SearchReadyEvent,\n SearchSelectEvent,\n SearchSource,\n SelectResult\n} from \"../api\";\nimport { SearchApiImpl } from \"./SearchApiImpl\";\nimport { SearchInput } from \"./SearchInput\";\nimport { useSearchState } from \"./useSearchState\";\n\n/**\n * Properties supported by the {@link Search} component.\n */\nexport interface SearchProps extends CommonComponentProps, MapModelProps {\n /**\n * Data sources to be searched on.\n */\n sources: SearchSource[];\n\n /**\n * Typing delay (in milliseconds) before the async search query starts after the user types in the search term.\n * Defaults to `200`.\n */\n searchTypingDelay?: number;\n\n /**\n * The maximum number of results shown per group.\n * Defaults to `5`.\n */\n maxResultsPerGroup?: number;\n\n /**\n * The placeholder text shown in the search input field when it is empty.\n * Defaults to a generic (and localized) hint.\n */\n placeholder?: string;\n\n /**\n * This event handler will be called when a search result has been selected.\n */\n onSelect?: (event: SearchSelectEvent) => void;\n\n /**\n * This event handler will be called when the search input has been cleared.\n */\n onClear?: (event: SearchClearEvent) => void;\n\n /**\n * Callback that is triggered once when the search is initialized.\n * The search API can be accessed by the `api` property of the {@link SearchReadyEvent}.\n */\n onReady?: (event: SearchReadyEvent) => void;\n\n /**\n * Callback that is triggered once when the search is disposed and unmounted.\n */\n onDisposed?: (event: SearchDisposedEvent) => void;\n}\n\n/**\n * A component that allows the user to search a given set of {@link SearchSource | SearchSources}.\n */\nexport const Search: FC<SearchProps> = (props) => {\n const {\n sources,\n searchTypingDelay,\n maxResultsPerGroup,\n placeholder,\n onSelect,\n onClear,\n onReady,\n onDisposed\n } = props;\n const { containerProps } = useCommonComponentProps(\"search\", props);\n const map = useMapModelValue(props);\n const { input, results, onInputChanged, onOptionConfirmed, selectedOption, searchAndSelect } =\n useSearchState(sources, searchTypingDelay, maxResultsPerGroup, map, onSelect);\n\n // api trigger hooks\n useSearchApi(onReady, onDisposed, onInputChanged, onClear, searchAndSelect);\n\n return (\n <Box {...containerProps} width={\"100%\"}>\n <SearchInput\n input={input}\n selectedOption={selectedOption}\n results={results}\n placeholder={placeholder}\n onClear={onClear}\n onSelect={onSelect}\n onInputChanged={onInputChanged}\n onOptionConfirmed={onOptionConfirmed}\n />\n </Box>\n );\n};\n\n// Note: `clearInput` and `setInputValue` must be stable because only the initial value is used to construct the API instance at this time.\nfunction useSearchApi(\n onReady: ((event: SearchReadyEvent) => void) | undefined,\n onDisposed: ((event: SearchDisposedEvent) => void) | undefined,\n onInputChanged: (newValue: string) => void,\n onClear: ((event: SearchClearEvent) => void) | undefined,\n searchAndSelect: (inputValue: string) => Promise<SelectResult | undefined>\n) {\n const clearInput = useEvent(() => {\n onInputChanged(\"\");\n onClear?.({ trigger: \"api-reset\" });\n });\n\n const apiRef = useRef<SearchApi>(null);\n if (!apiRef.current) {\n // NOTE: The API object is only created once.\n // This is why the functions must currently be stable!\n apiRef.current = new SearchApiImpl(clearInput, onInputChanged, searchAndSelect);\n }\n\n const api = apiRef.current;\n\n const readyTrigger = useEvent(() => {\n onReady?.({\n api\n });\n });\n\n const disposeTrigger = useEvent(() => {\n onDisposed?.({});\n });\n\n // Trigger ready / dispose on mount / unmount, but if the callbacks change.\n // useEvent() returns a stable function reference.\n useEffect(() => {\n readyTrigger();\n return disposeTrigger;\n }, [readyTrigger, disposeTrigger]);\n}\n"],"names":[],"mappings":";;;;;;;;;AAuEO,MAAM,MAAA,GAA0B,CAAC,KAAA,KAAU;AAC9C,EAAA,MAAM;AAAA,IACF,OAAA;AAAA,IACA,iBAAA;AAAA,IACA,kBAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACJ,GAAI,KAAA;AACJ,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,uBAAA,CAAwB,UAAU,KAAK,CAAA;AAClE,EAAA,MAAM,GAAA,GAAM,iBAAiB,KAAK,CAAA;AAClC,EAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAS,cAAA,EAAgB,iBAAA,EAAmB,cAAA,EAAgB,eAAA,EAAgB,GACvF,cAAA,CAAe,OAAA,EAAS,iBAAA,EAAmB,kBAAA,EAAoB,KAAK,QAAQ,CAAA;AAGhF,EAAA,YAAA,CAAa,OAAA,EAAS,UAAA,EAAY,cAAA,EAAgB,OAAA,EAAS,eAAe,CAAA;AAE1E,EAAA,uBACI,GAAA,CAAC,GAAA,EAAA,EAAK,GAAG,cAAA,EAAgB,OAAO,MAAA,EAC5B,QAAA,kBAAA,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACG,KAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA;AAAA,MACA,WAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA;AAAA,GACJ,EACJ,CAAA;AAER;AAGA,SAAS,YAAA,CACL,OAAA,EACA,UAAA,EACA,cAAA,EACA,SACA,eAAA,EACF;AACE,EAAA,MAAM,UAAA,GAAa,SAAS,MAAM;AAC9B,IAAA,cAAA,CAAe,EAAE,CAAA;AACjB,IAAA,OAAA,GAAU,EAAE,OAAA,EAAS,WAAA,EAAa,CAAA;AAAA,EACtC,CAAC,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,OAAkB,IAAI,CAAA;AACrC,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AAGjB,IAAA,MAAA,CAAO,OAAA,GAAU,IAAI,aAAA,CAAc,UAAA,EAAY,gBAAgB,eAAe,CAAA;AAAA,EAClF;AAEA,EAAA,MAAM,MAAM,MAAA,CAAO,OAAA;AAEnB,EAAA,MAAM,YAAA,GAAe,SAAS,MAAM;AAChC,IAAA,OAAA,GAAU;AAAA,MACN;AAAA,KACH,CAAA;AAAA,EACL,CAAC,CAAA;AAED,EAAA,MAAM,cAAA,GAAiB,SAAS,MAAM;AAClC,IAAA,UAAA,GAAa,EAAE,CAAA;AAAA,EACnB,CAAC,CAAA;AAID,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,YAAA,EAAa;AACb,IAAA,OAAO,cAAA;AAAA,EACX,CAAA,EAAG,CAAC,YAAA,EAAc,cAAc,CAAC,CAAA;AACrC;;;;"}
1
+ {"version":3,"file":"Search.js","sources":["Search.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Box } from \"@chakra-ui/react\";\nimport { MapModelProps, useMapModelValue } from \"@open-pioneer/map\";\nimport { CommonComponentProps, useCommonComponentProps, useEvent } from \"@open-pioneer/react-utils\";\nimport { FC, useEffect, useRef } from \"react\";\nimport {\n SearchApi,\n SearchClearEvent,\n SearchDisposedEvent,\n SearchReadyEvent,\n SearchSelectEvent,\n SearchSource,\n SelectResult\n} from \"../api\";\nimport { SearchApiImpl } from \"./SearchApiImpl\";\nimport { SearchInput } from \"./SearchInput\";\nimport { useSearchState } from \"./useSearchState\";\n\n/**\n * Properties supported by the {@link Search} component.\n */\nexport interface SearchProps extends CommonComponentProps, MapModelProps {\n /**\n * Data sources to be searched on.\n */\n sources: SearchSource[];\n\n /**\n * Typing delay (in milliseconds) before the async search query starts after the user types in the search term.\n * Defaults to `200`.\n */\n searchTypingDelay?: number;\n\n /**\n * The maximum number of results shown per group.\n * Defaults to `5`.\n */\n maxResultsPerGroup?: number;\n\n /**\n * The placeholder text shown in the search input field when it is empty.\n * Defaults to a generic (and localized) hint.\n */\n placeholder?: string;\n\n /**\n * This event handler will be called when a search result has been selected.\n */\n onSelect?: (event: SearchSelectEvent) => void;\n\n /**\n * This event handler will be called when the search input has been cleared.\n */\n onClear?: (event: SearchClearEvent) => void;\n\n /**\n * Callback that is triggered once when the search is initialized.\n * The search API can be accessed by the `api` property of the {@link SearchReadyEvent}.\n */\n onReady?: (event: SearchReadyEvent) => void;\n\n /**\n * Callback that is triggered once when the search is disposed and unmounted.\n */\n onDisposed?: (event: SearchDisposedEvent) => void;\n}\n\n/**\n * A component that allows the user to search a given set of {@link SearchSource | SearchSources}.\n */\nexport const Search: FC<SearchProps> = (props) => {\n const {\n sources,\n searchTypingDelay,\n maxResultsPerGroup,\n placeholder,\n onSelect,\n onClear,\n onReady,\n onDisposed\n } = props;\n const { containerProps } = useCommonComponentProps(\"search\", props);\n const map = useMapModelValue(props);\n const { input, results, onInputChanged, onOptionConfirmed, selectedOption, searchAndSelect } =\n useSearchState(sources, searchTypingDelay, maxResultsPerGroup, map, onSelect);\n\n // api trigger hooks\n useSearchApi(onReady, onDisposed, onInputChanged, onClear, searchAndSelect);\n\n return (\n <Box {...containerProps} width={\"100%\"}>\n <SearchInput\n input={input}\n selectedOption={selectedOption}\n results={results}\n placeholder={placeholder}\n onClear={onClear}\n onSelect={onSelect}\n onInputChanged={onInputChanged}\n onOptionConfirmed={onOptionConfirmed}\n />\n </Box>\n );\n};\n\n// Note: `clearInput` and `setInputValue` must be stable because only the initial value is used to construct the API instance at this time.\n// oxlint-disable-next-line max-params\nfunction useSearchApi(\n onReady: ((event: SearchReadyEvent) => void) | undefined,\n onDisposed: ((event: SearchDisposedEvent) => void) | undefined,\n onInputChanged: (newValue: string) => void,\n onClear: ((event: SearchClearEvent) => void) | undefined,\n searchAndSelect: (inputValue: string) => Promise<SelectResult | undefined>\n) {\n const clearInput = useEvent(() => {\n onInputChanged(\"\");\n onClear?.({ trigger: \"api-reset\" });\n });\n\n const apiRef = useRef<SearchApi>(null);\n // oxlint-disable-next-line react/refs\n if (!apiRef.current) {\n // NOTE: The API object is only created once.\n // This is why the functions must currently be stable!\n apiRef.current = new SearchApiImpl(clearInput, onInputChanged, searchAndSelect);\n }\n\n const api = apiRef.current;\n\n const readyTrigger = useEvent(() => {\n onReady?.({\n api\n });\n });\n\n const disposeTrigger = useEvent(() => {\n onDisposed?.({});\n });\n\n // Trigger ready / dispose on mount / unmount, but if the callbacks change.\n // useEvent() returns a stable function reference.\n useEffect(() => {\n readyTrigger();\n return disposeTrigger;\n }, [readyTrigger, disposeTrigger]);\n}\n"],"names":[],"mappings":";;;;;;;;;AAwEO,MAAM,MAAA,GAA0B,CAAC,KAAA,KAAU;AAC9C,EAAA,MAAM;AAAA,IACF,OAAA;AAAA,IACA,iBAAA;AAAA,IACA,kBAAA;AAAA,IACA,WAAA;AAAA,IACA,QAAA;AAAA,IACA,OAAA;AAAA,IACA,OAAA;AAAA,IACA;AAAA,GACJ,GAAI,KAAA;AACJ,EAAA,MAAM,EAAE,cAAA,EAAe,GAAI,uBAAA,CAAwB,UAAU,KAAK,CAAA;AAClE,EAAA,MAAM,GAAA,GAAM,iBAAiB,KAAK,CAAA;AAClC,EAAA,MAAM,EAAE,KAAA,EAAO,OAAA,EAAS,cAAA,EAAgB,iBAAA,EAAmB,cAAA,EAAgB,eAAA,EAAgB,GACvF,cAAA,CAAe,OAAA,EAAS,iBAAA,EAAmB,kBAAA,EAAoB,KAAK,QAAQ,CAAA;AAGhF,EAAA,YAAA,CAAa,OAAA,EAAS,UAAA,EAAY,cAAA,EAAgB,OAAA,EAAS,eAAe,CAAA;AAE1E,EAAA,uBACI,GAAA,CAAC,GAAA,EAAA,EAAK,GAAG,cAAA,EAAgB,OAAO,MAAA,EAC5B,QAAA,kBAAA,GAAA;AAAA,IAAC,WAAA;AAAA,IAAA;AAAA,MACG,KAAA;AAAA,MACA,cAAA;AAAA,MACA,OAAA;AAAA,MACA,WAAA;AAAA,MACA,OAAA;AAAA,MACA,QAAA;AAAA,MACA,cAAA;AAAA,MACA;AAAA;AAAA,GACJ,EACJ,CAAA;AAER;AAIA,SAAS,YAAA,CACL,OAAA,EACA,UAAA,EACA,cAAA,EACA,SACA,eAAA,EACF;AACE,EAAA,MAAM,UAAA,GAAa,SAAS,MAAM;AAC9B,IAAA,cAAA,CAAe,EAAE,CAAA;AACjB,IAAA,OAAA,GAAU,EAAE,OAAA,EAAS,WAAA,EAAa,CAAA;AAAA,EACtC,CAAC,CAAA;AAED,EAAA,MAAM,MAAA,GAAS,OAAkB,IAAI,CAAA;AAErC,EAAA,IAAI,CAAC,OAAO,OAAA,EAAS;AAGjB,IAAA,MAAA,CAAO,OAAA,GAAU,IAAI,aAAA,CAAc,UAAA,EAAY,gBAAgB,eAAe,CAAA;AAAA,EAClF;AAEA,EAAA,MAAM,MAAM,MAAA,CAAO,OAAA;AAEnB,EAAA,MAAM,YAAA,GAAe,SAAS,MAAM;AAChC,IAAA,OAAA,GAAU;AAAA,MACN;AAAA,KACH,CAAA;AAAA,EACL,CAAC,CAAA;AAED,EAAA,MAAM,cAAA,GAAiB,SAAS,MAAM;AAClC,IAAA,UAAA,GAAa,EAAE,CAAA;AAAA,EACnB,CAAC,CAAA;AAID,EAAA,SAAA,CAAU,MAAM;AACZ,IAAA,YAAA,EAAa;AACb,IAAA,OAAO,cAAA;AAAA,EACX,CAAA,EAAG,CAAC,YAAA,EAAc,cAAc,CAAC,CAAA;AACrC;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"SearchApiImpl.js","sources":["SearchApiImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { SearchApi, SearchClearTrigger, SelectResult } from \"../api\";\n\nexport class SearchApiImpl implements SearchApi {\n #clearInput: (trigger: SearchClearTrigger) => void;\n #setInputValue: (newValue: string) => void;\n #searchAndSelect: (input: string) => Promise<SelectResult | undefined>;\n\n constructor(\n clearInput: (trigger: SearchClearTrigger) => void,\n setInputValue: (newValue: string) => void,\n searchAndSelect: (inputValue: string) => Promise<SelectResult | undefined>\n ) {\n this.#clearInput = clearInput;\n this.#setInputValue = setInputValue;\n this.#searchAndSelect = searchAndSelect;\n }\n\n resetInput() {\n this.#clearInput(\"api-reset\");\n }\n\n setInputValue(inputValue: string) {\n this.#setInputValue(inputValue);\n }\n\n async searchAndSelect(inputValue: string) {\n return this.#searchAndSelect(inputValue);\n }\n}\n"],"names":[],"mappings":"AAIO,MAAM,aAAA,CAAmC;AAAA,EAC5C,WAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EAEA,WAAA,CACI,UAAA,EACA,aAAA,EACA,eAAA,EACF;AACE,IAAA,IAAA,CAAK,WAAA,GAAc,UAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,aAAA;AACtB,IAAA,IAAA,CAAK,gBAAA,GAAmB,eAAA;AAAA,EAC5B;AAAA,EAEA,UAAA,GAAa;AACT,IAAA,IAAA,CAAK,YAAY,WAAW,CAAA;AAAA,EAChC;AAAA,EAEA,cAAc,UAAA,EAAoB;AAC9B,IAAA,IAAA,CAAK,eAAe,UAAU,CAAA;AAAA,EAClC;AAAA,EAEA,MAAM,gBAAgB,UAAA,EAAoB;AACtC,IAAA,OAAO,IAAA,CAAK,iBAAiB,UAAU,CAAA;AAAA,EAC3C;AACJ;;;;"}
1
+ {"version":3,"file":"SearchApiImpl.js","sources":["SearchApiImpl.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { SearchApi, SearchClearTrigger, SelectResult } from \"../api\";\n\nexport class SearchApiImpl implements SearchApi {\n #clearInput: (trigger: SearchClearTrigger) => void;\n #setInputValue: (newValue: string) => void;\n #searchAndSelect: (input: string) => Promise<SelectResult | undefined>;\n\n constructor(\n clearInput: (trigger: SearchClearTrigger) => void,\n setInputValue: (newValue: string) => void,\n searchAndSelect: (inputValue: string) => Promise<SelectResult | undefined>\n ) {\n this.#clearInput = clearInput;\n this.#setInputValue = setInputValue;\n this.#searchAndSelect = searchAndSelect;\n }\n\n resetInput() {\n this.#clearInput(\"api-reset\");\n }\n\n setInputValue(inputValue: string) {\n this.#setInputValue(inputValue);\n }\n\n async searchAndSelect(inputValue: string) {\n return this.#searchAndSelect(inputValue);\n }\n}\n"],"names":[],"mappings":"AAKO,MAAM,aAAA,CAAmC;AAAA,EAC5C,WAAA;AAAA,EACA,cAAA;AAAA,EACA,gBAAA;AAAA,EAEA,WAAA,CACI,UAAA,EACA,aAAA,EACA,eAAA,EACF;AACE,IAAA,IAAA,CAAK,WAAA,GAAc,UAAA;AACnB,IAAA,IAAA,CAAK,cAAA,GAAiB,aAAA;AACtB,IAAA,IAAA,CAAK,gBAAA,GAAmB,eAAA;AAAA,EAC5B;AAAA,EAEA,UAAA,GAAa;AACT,IAAA,IAAA,CAAK,YAAY,WAAW,CAAA;AAAA,EAChC;AAAA,EAEA,cAAc,UAAA,EAAoB;AAC9B,IAAA,IAAA,CAAK,eAAe,UAAU,CAAA;AAAA,EAClC;AAAA,EAEA,MAAM,gBAAgB,UAAA,EAAoB;AACtC,IAAA,OAAO,IAAA,CAAK,iBAAiB,UAAU,CAAA;AAAA,EAC3C;AACJ;;;;"}
@@ -1 +1 @@
1
- {"version":3,"file":"SearchInput.js","sources":["SearchInput.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport {\n CloseButton,\n Combobox,\n ComboboxInputValueChangeDetails,\n ComboboxValueChangeDetails,\n createListCollection,\n Icon,\n InputGroup,\n Portal,\n Spinner,\n VisuallyHidden\n} from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { memo, UIEvent, useMemo, useRef } from \"react\";\nimport { LuSearch } from \"react-icons/lu\";\nimport { SearchClearEvent, SearchSelectEvent } from \"../api\";\nimport { SearchResults } from \"./SearchResults\";\nimport { SearchOption, SearchResultsState } from \"./useSearchState\";\n\nexport interface SearchInputProps {\n input: string;\n selectedOption?: SearchOption;\n results: SearchResultsState;\n placeholder?: string;\n\n onClear?: (event: SearchClearEvent) => void;\n onSelect?: (event: SearchSelectEvent) => void;\n onInputChanged: (newInput: string) => void;\n onOptionConfirmed: (option: SearchOption) => void;\n}\n\nexport function SearchInput(props: SearchInputProps) {\n const {\n input,\n selectedOption,\n results,\n placeholder,\n onClear,\n onSelect,\n onInputChanged,\n onOptionConfirmed\n } = props;\n const intl = useIntl();\n const controlRef = useRef<HTMLInputElement>(null);\n\n // Create the collection for the combobox and keep it synced with search results.\n const collection = useSearchCollection(results);\n\n // Event hooks for handling input changes and selecting options\n const { handleInputChange, handleSelectChange } = useSearchHandlers(\n onInputChanged,\n onOptionConfirmed,\n onSelect\n );\n\n return (\n <Combobox.Root\n collection={collection}\n onInputValueChange={(e) => {\n handleInputChange(e);\n }}\n onValueChange={(e) => {\n handleSelectChange(e);\n }}\n inputValue={input}\n value={selectedOption?.value ? [selectedOption.value] : []}\n className=\"search-combobox-component\"\n aria-label={intl.formatMessage({ id: \"ariaLabel.search\" })}\n placeholder={placeholder ?? intl.formatMessage({ id: \"searchPlaceholder\" })}\n openOnClick={input.length > 0}\n closeOnSelect={true}\n lazyMount={true}\n unmountOnExit={true}\n selectionBehavior=\"preserve\"\n >\n <AccessibleBoxHelper search={results} />\n <Combobox.Control>\n <InputGroup\n startElement={\n <Icon className={\"search-icon\"} size=\"md\">\n <LuSearch />\n </Icon>\n }\n >\n <Combobox.Input ref={controlRef} />\n </InputGroup>\n <Combobox.IndicatorGroup>\n {results.kind === \"loading\" ? (\n <Spinner size=\"xs\" borderWidth=\"1px\" />\n ) : input.length ? (\n <ClearIndicator\n clearValue={() => {\n onInputChanged(\"\");\n onClear?.({ trigger: \"user\" });\n controlRef.current?.focus();\n }}\n />\n ) : null}\n </Combobox.IndicatorGroup>\n </Combobox.Control>\n\n <Portal>\n <Combobox.Positioner>\n <SearchResults collection={collection} input={input} results={results} />\n </Combobox.Positioner>\n </Portal>\n </Combobox.Root>\n );\n}\n\n/**\n * Report loading status for screen readers.\n */\nconst AccessibleBoxHelper = memo(function AccessibleBoxHelper(props: {\n search: SearchResultsState;\n}) {\n const { search } = props;\n const intl = useIntl();\n\n let content;\n if (search.kind === \"loading\") {\n content = intl.formatMessage({ id: \"loadingText\" });\n } else if (search.kind === \"ready\") {\n content = intl.formatMessage({ id: \"resultLoaded\" });\n }\n\n return <VisuallyHidden aria-live=\"polite\">{content}</VisuallyHidden>;\n});\n\nconst ClearIndicator = memo(function ClearIndicator(props: { clearValue: () => void }) {\n const intl = useIntl();\n const clearButtonLabel = intl.formatMessage({\n id: \"ariaLabel.clearButton\"\n });\n const clickHandler = (e: UIEvent) => {\n e.preventDefault();\n e.stopPropagation();\n props.clearValue();\n };\n\n return (\n <Tooltip content={clearButtonLabel}>\n <CloseButton\n variant=\"ghost\"\n mr=\"-10px\"\n size=\"sm\"\n aria-label={clearButtonLabel}\n onClick={clickHandler}\n onTouchEnd={clickHandler}\n />\n </Tooltip>\n );\n});\n\nfunction useSearchCollection(results: SearchResultsState) {\n return useMemo(() => {\n if (results.kind === \"ready\") {\n const options = results.results.flatMap((group) => group.options);\n return createListCollection({\n items: options,\n groupBy: (item) => item.group.id,\n itemToString: (item) => item?.label || \"\",\n itemToValue: (item) => item?.value || \"\"\n });\n }\n return createListCollection<SearchOption>({ items: [] });\n }, [results]);\n}\n\nfunction useSearchHandlers(\n onInputChanged: (newValue: string) => void,\n onOptionConfirmed: (option: SearchOption) => void,\n onSelect: ((event: SearchSelectEvent) => void) | undefined\n) {\n const handleInputChange = useEvent((e: ComboboxInputValueChangeDetails) => {\n // Only update the input if the user actually typed something.\n // This keeps the input content if the user focuses another element or if the menu is closed.\n if (e.reason === \"input-change\" || e.reason === \"interact-outside\") {\n onInputChanged(e.inputValue);\n }\n });\n\n const handleSelectChange = useEvent((e: ComboboxValueChangeDetails<SearchOption>) => {\n const selectedItem = e.items.length ? e.items[0] : null;\n if (!selectedItem) {\n return;\n }\n onOptionConfirmed(selectedItem);\n onSelect?.({\n source: selectedItem.source,\n result: selectedItem.result,\n trigger: \"user\"\n });\n });\n\n return { handleInputChange, handleSelectChange };\n}\n"],"names":["AccessibleBoxHelper","ClearIndicator"],"mappings":"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"}
1
+ {"version":3,"file":"SearchInput.js","sources":["SearchInput.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport {\n CloseButton,\n Combobox,\n ComboboxInputValueChangeDetails,\n ComboboxValueChangeDetails,\n createListCollection,\n Icon,\n InputGroup,\n Portal,\n Spinner,\n VisuallyHidden\n} from \"@chakra-ui/react\";\nimport { Tooltip } from \"@open-pioneer/chakra-snippets/tooltip\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { memo, UIEvent, useMemo, useRef } from \"react\";\nimport { LuSearch } from \"react-icons/lu\";\nimport { SearchClearEvent, SearchSelectEvent } from \"../api\";\nimport { SearchResults } from \"./SearchResults\";\nimport { SearchOption, SearchResultsState } from \"./useSearchState\";\n\nexport interface SearchInputProps {\n input: string;\n selectedOption?: SearchOption;\n results: SearchResultsState;\n placeholder?: string;\n\n onClear?: (event: SearchClearEvent) => void;\n onSelect?: (event: SearchSelectEvent) => void;\n onInputChanged: (newInput: string) => void;\n onOptionConfirmed: (option: SearchOption) => void;\n}\n\nexport function SearchInput(props: SearchInputProps) {\n const {\n input,\n selectedOption,\n results,\n placeholder,\n onClear,\n onSelect,\n onInputChanged,\n onOptionConfirmed\n } = props;\n const intl = useIntl();\n const controlRef = useRef<HTMLInputElement>(null);\n\n // Create the collection for the combobox and keep it synced with search results.\n const collection = useSearchCollection(results);\n\n // Event hooks for handling input changes and selecting options\n const { handleInputChange, handleSelectChange } = useSearchHandlers(\n onInputChanged,\n onOptionConfirmed,\n onSelect\n );\n\n return (\n <Combobox.Root\n collection={collection}\n onInputValueChange={(e) => {\n handleInputChange(e);\n }}\n onValueChange={(e) => {\n handleSelectChange(e);\n }}\n inputValue={input}\n value={selectedOption?.value ? [selectedOption.value] : []}\n className=\"search-combobox-component\"\n aria-label={intl.formatMessage({ id: \"ariaLabel.search\" })}\n placeholder={placeholder ?? intl.formatMessage({ id: \"searchPlaceholder\" })}\n openOnClick={input.length > 0}\n closeOnSelect={true}\n lazyMount={true}\n unmountOnExit={true}\n selectionBehavior=\"preserve\"\n >\n <AccessibleBoxHelper search={results} />\n <Combobox.Control>\n <InputGroup\n startElement={\n <Icon className={\"search-icon\"} size=\"md\">\n <LuSearch />\n </Icon>\n }\n >\n <Combobox.Input ref={controlRef} />\n </InputGroup>\n <Combobox.IndicatorGroup>\n {results.kind === \"loading\" ? (\n <Spinner size=\"xs\" borderWidth=\"1px\" />\n ) : input.length ? (\n <ClearIndicator\n clearValue={() => {\n onInputChanged(\"\");\n onClear?.({ trigger: \"user\" });\n controlRef.current?.focus();\n }}\n />\n ) : null}\n </Combobox.IndicatorGroup>\n </Combobox.Control>\n\n <Portal>\n <Combobox.Positioner>\n <SearchResults collection={collection} input={input} results={results} />\n </Combobox.Positioner>\n </Portal>\n </Combobox.Root>\n );\n}\n\n/**\n * Report loading status for screen readers.\n */\nconst AccessibleBoxHelper = memo(function AccessibleBoxHelper(props: {\n search: SearchResultsState;\n}) {\n const { search } = props;\n const intl = useIntl();\n\n let content;\n if (search.kind === \"loading\") {\n content = intl.formatMessage({ id: \"loadingText\" });\n } else if (search.kind === \"ready\") {\n content = intl.formatMessage({ id: \"resultLoaded\" });\n }\n\n return <VisuallyHidden aria-live=\"polite\">{content}</VisuallyHidden>;\n});\n\nconst ClearIndicator = memo(function ClearIndicator(props: { clearValue: () => void }) {\n const intl = useIntl();\n const clearButtonLabel = intl.formatMessage({\n id: \"ariaLabel.clearButton\"\n });\n const clickHandler = (e: UIEvent) => {\n e.preventDefault();\n e.stopPropagation();\n props.clearValue();\n };\n\n return (\n <Tooltip content={clearButtonLabel}>\n <CloseButton\n variant=\"ghost\"\n mr=\"-10px\"\n size=\"sm\"\n aria-label={clearButtonLabel}\n onClick={clickHandler}\n onTouchEnd={clickHandler}\n />\n </Tooltip>\n );\n});\n\nfunction useSearchCollection(results: SearchResultsState) {\n return useMemo(() => {\n if (results.kind === \"ready\") {\n const options = results.results.flatMap((group) => group.options);\n return createListCollection({\n items: options,\n groupBy: (item) => item.group.id,\n itemToString: (item) => item?.label || \"\",\n itemToValue: (item) => item?.value || \"\"\n });\n }\n return createListCollection<SearchOption>({ items: [] });\n }, [results]);\n}\n\nfunction useSearchHandlers(\n onInputChanged: (newValue: string) => void,\n onOptionConfirmed: (option: SearchOption) => void,\n onSelect: ((event: SearchSelectEvent) => void) | undefined\n) {\n const handleInputChange = useEvent((e: ComboboxInputValueChangeDetails) => {\n // Only update the input if the user actually typed something.\n // This keeps the input content if the user focuses another element or if the menu is closed.\n if (e.reason === \"input-change\" || e.reason === \"interact-outside\") {\n onInputChanged(e.inputValue);\n }\n });\n\n const handleSelectChange = useEvent((e: ComboboxValueChangeDetails<SearchOption>) => {\n const selectedItem = e.items.length ? e.items[0] : null;\n if (!selectedItem) {\n return;\n }\n onOptionConfirmed(selectedItem);\n onSelect?.({\n source: selectedItem.source,\n result: selectedItem.result,\n trigger: \"user\"\n });\n });\n\n return { handleInputChange, handleSelectChange };\n}\n"],"names":["AccessibleBoxHelper","ClearIndicator"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { jsxs, jsx } from 'react/jsx-runtime';
2
2
  import { Combobox, Highlight, HStack, Span } from '@chakra-ui/react';
3
- import { memo } from 'react';
4
3
  import { useIntl } from '../_virtual/hooks.js';
4
+ import { memo } from 'react';
5
5
 
6
6
  const SearchResults = memo(function SearchResults2(props) {
7
7
  const { collection, input, results } = props;
@@ -23,7 +23,7 @@ const SearchResults = memo(function SearchResults2(props) {
23
23
  });
24
24
  const SearchResultList = memo(function SearchResults3(props) {
25
25
  const { collection, input, results } = props;
26
- return collection.group().map(([groupId, groupOptions], key) => {
26
+ return collection.group().map(([groupId, groupOptions]) => {
27
27
  return /* @__PURE__ */ jsxs(Combobox.ItemGroup, { children: [
28
28
  /* @__PURE__ */ jsx(
29
29
  Combobox.ItemGroupLabel,
@@ -34,11 +34,11 @@ const SearchResultList = memo(function SearchResults3(props) {
34
34
  },
35
35
  groupId
36
36
  ),
37
- groupOptions.map((searchResult, key2) => {
37
+ groupOptions.map((option) => {
38
38
  return /* @__PURE__ */ jsx(
39
39
  Combobox.Item,
40
40
  {
41
- item: searchResult,
41
+ item: option,
42
42
  css: {
43
43
  _checked: {
44
44
  backgroundColor: "colorPalette.500",
@@ -48,12 +48,12 @@ const SearchResultList = memo(function SearchResults3(props) {
48
48
  backgroundColor: "colorPalette.50"
49
49
  }
50
50
  },
51
- children: /* @__PURE__ */ jsx(Combobox.ItemText, { children: /* @__PURE__ */ jsx(Highlight, { ignoreCase: true, query: input, styles: { fontWeight: "bold" }, children: searchResult.label }) })
51
+ children: /* @__PURE__ */ jsx(Combobox.ItemText, { children: /* @__PURE__ */ jsx(Highlight, { ignoreCase: true, query: input, styles: { fontWeight: "bold" }, children: option.label }) })
52
52
  },
53
- key2
53
+ option.result.id
54
54
  );
55
55
  })
56
- ] }, key);
56
+ ] }, groupId);
57
57
  });
58
58
  });
59
59
  const FallbackContent = memo(function FallbackContent2(props) {
@@ -1 +1 @@
1
- {"version":3,"file":"SearchResults.js","sources":["SearchResults.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { Combobox, Highlight, HStack, ListCollection, Span } from \"@chakra-ui/react\";\nimport { memo } from \"react\";\nimport { SearchOption, SearchResultsState } from \"./useSearchState\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\n\nexport interface SearchResultsProps {\n collection: ListCollection<SearchOption>;\n input: string;\n results: SearchResultsState;\n}\n\nexport const SearchResults = memo(function SearchResults(props: SearchResultsProps) {\n const { collection, input, results } = props;\n return (\n <Combobox.Content\n className=\"search-component-menu\"\n minW=\"sm\"\n maxH=\"300px\"\n overflowY=\"auto\"\n overflowX=\"hidden\"\n visibility={input.length ? \"visible\" : \"hidden\"}\n >\n <FallbackContent results={results} />\n <SearchResultList collection={collection} input={input} results={results} />\n </Combobox.Content>\n );\n});\n\nconst SearchResultList = memo(function SearchResults(props: SearchResultsProps) {\n const { collection, input, results } = props;\n return collection.group().map(([groupId, groupOptions], key) => {\n return (\n <Combobox.ItemGroup key={key}>\n <Combobox.ItemGroupLabel\n key={groupId}\n backgroundColor=\"colorPalette.100\"\n visibility={results.kind === \"loading\" ? \"hidden\" : \"visible\"}\n >\n {groupOptions[0]?.group.label}\n </Combobox.ItemGroupLabel>\n {groupOptions.map((searchResult, key) => {\n return (\n <Combobox.Item\n key={key}\n item={searchResult}\n css={{\n _checked: {\n backgroundColor: \"colorPalette.500\",\n color: \"white\"\n },\n \"&:hover:not([data-state=checked])\": {\n backgroundColor: \"colorPalette.50\"\n }\n }}\n >\n <Combobox.ItemText>\n <Highlight ignoreCase query={input} styles={{ fontWeight: \"bold\" }}>\n {searchResult.label}\n </Highlight>\n </Combobox.ItemText>\n </Combobox.Item>\n );\n })}\n </Combobox.ItemGroup>\n );\n });\n});\n\n/**\n * Show loading label or fallback message when no results are found.\n */\nconst FallbackContent = memo(function FallbackContent(props: { results: SearchResultsState }) {\n const { results } = props;\n const intl = useIntl();\n\n let content;\n if (results.kind === \"loading\") {\n content = intl.formatMessage({ id: \"loadingText\" });\n } else {\n content = intl.formatMessage({ id: \"noOptionsText\" });\n }\n\n return (\n <Combobox.Empty padding=\"0\">\n <HStack p=\"2\" justifyContent=\"center\">\n <Span>{content}</Span>\n </HStack>\n </Combobox.Empty>\n );\n});\n"],"names":["SearchResults","key","FallbackContent"],"mappings":"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"}
1
+ {"version":3,"file":"SearchResults.js","sources":["SearchResults.tsx"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { Combobox, Highlight, HStack, ListCollection, Span } from \"@chakra-ui/react\";\nimport { useIntl } from \"open-pioneer:react-hooks\";\nimport { memo } from \"react\";\nimport { SearchOption, SearchResultsState } from \"./useSearchState\";\n\nexport interface SearchResultsProps {\n collection: ListCollection<SearchOption>;\n input: string;\n results: SearchResultsState;\n}\n\nexport const SearchResults = memo(function SearchResults(props: SearchResultsProps) {\n const { collection, input, results } = props;\n return (\n <Combobox.Content\n className=\"search-component-menu\"\n minW=\"sm\"\n maxH=\"300px\"\n overflowY=\"auto\"\n overflowX=\"hidden\"\n visibility={input.length ? \"visible\" : \"hidden\"}\n >\n <FallbackContent results={results} />\n <SearchResultList collection={collection} input={input} results={results} />\n </Combobox.Content>\n );\n});\n\nconst SearchResultList = memo(function SearchResults(props: SearchResultsProps) {\n const { collection, input, results } = props;\n return collection.group().map(([groupId, groupOptions]) => {\n return (\n <Combobox.ItemGroup key={groupId}>\n <Combobox.ItemGroupLabel\n key={groupId}\n backgroundColor=\"colorPalette.100\"\n visibility={results.kind === \"loading\" ? \"hidden\" : \"visible\"}\n >\n {groupOptions[0]?.group.label}\n </Combobox.ItemGroupLabel>\n {groupOptions.map((option) => {\n return (\n <Combobox.Item\n key={option.result.id}\n item={option}\n css={{\n _checked: {\n backgroundColor: \"colorPalette.500\",\n color: \"white\"\n },\n \"&:hover:not([data-state=checked])\": {\n backgroundColor: \"colorPalette.50\"\n }\n }}\n >\n <Combobox.ItemText>\n <Highlight ignoreCase query={input} styles={{ fontWeight: \"bold\" }}>\n {option.label}\n </Highlight>\n </Combobox.ItemText>\n </Combobox.Item>\n );\n })}\n </Combobox.ItemGroup>\n );\n });\n});\n\n/**\n * Show loading label or fallback message when no results are found.\n */\nconst FallbackContent = memo(function FallbackContent(props: { results: SearchResultsState }) {\n const { results } = props;\n const intl = useIntl();\n\n let content;\n if (results.kind === \"loading\") {\n content = intl.formatMessage({ id: \"loadingText\" });\n } else {\n content = intl.formatMessage({ id: \"noOptionsText\" });\n }\n\n return (\n <Combobox.Empty padding=\"0\">\n <HStack p=\"2\" justifyContent=\"center\">\n <Span>{content}</Span>\n </HStack>\n </Combobox.Empty>\n );\n});\n"],"names":["SearchResults","FallbackContent"],"mappings":"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"}
@@ -1,7 +1,7 @@
1
1
  import { createLogger, isAbortError } from '@open-pioneer/core';
2
2
  import { useEvent } from '@open-pioneer/react-utils';
3
- import { useReducer, useRef, useCallback, useState, useEffect } from 'react';
4
3
  import { sourceId$1 as sourceId } from '../_virtual/source-info.js';
4
+ import { useReducer, useRef, useCallback, useState, useEffect } from 'react';
5
5
  import { SearchController } from '../model/SearchController.js';
6
6
 
7
7
  const LOG = createLogger(sourceId);
@@ -1 +1 @@
1
- {"version":3,"file":"useSearchState.js","sources":["useSearchState.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport { MapModel } from \"@open-pioneer/map\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { useCallback, useEffect, useReducer, useRef, useState } from \"react\";\nimport { SearchResult, SearchSource, SelectResult } from \"../api\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { SearchController, ResultGroup } from \"../model/SearchController\";\nimport { SearchProps } from \"./Search\";\n\nconst LOG = createLogger(sourceId);\n\nexport interface SearchOptionGroup {\n /** Unique id of this group. */\n id: string;\n\n /** Display text shown in menu. */\n label: string;\n\n /** Set of options that belong to this group. */\n options: SearchOption[];\n}\n\nexport interface SearchOption {\n /** Unique value for this option. */\n value: string;\n\n /** Display text shown in menu. */\n label: string;\n\n /** Search source that returned the suggestion. */\n source: SearchSource;\n\n /** Links to the parent group. */\n group: SearchOptionGroup;\n\n /** The raw result from the search source. */\n result: SearchResult;\n}\n\nexport interface SearchState {\n input: string;\n results: SearchResultsState;\n selectedOption: SearchOption | undefined;\n\n // NOTE: Stable functions\n onOptionConfirmed: (newOption: SearchOption) => void;\n onInputChanged: (newInput: string) => void;\n\n searchAndSelect: (query: string) => Promise<SelectResult | undefined>;\n}\n\nexport type SearchResultsReady = {\n kind: \"ready\";\n results: SearchOptionGroup[];\n};\n\nexport type SearchResultsLoading = {\n kind: \"loading\";\n};\n\nexport type SearchResultsState = SearchResultsReady | SearchResultsLoading;\n\n/**\n * Keeps track of the current input text, active searches and their results.\n *\n * NOTE: it would be great to merge this state handling with the search controller\n * in a future revision.\n */\nexport function useSearchState(\n sources: SearchSource[],\n searchTypingDelay: number | undefined,\n maxResultsPerGroup: number | undefined,\n map: MapModel,\n onSelect: SearchProps[\"onSelect\"] | undefined // TODO: refactor --> model\n): SearchState {\n interface FullSearchState {\n query: string;\n selectedOption: SearchOption | undefined;\n search: SearchResultsState;\n }\n\n const getId = useSearchSourceId();\n const controller = useController(sources, searchTypingDelay, maxResultsPerGroup, map);\n\n type Action =\n | { kind: \"input\"; query: string }\n | { kind: \"select-option\"; option: SearchOption }\n | { kind: \"load-results\" }\n | { kind: \"accept-results\"; results: SearchOptionGroup[] };\n\n const [state, dispatch] = useReducer(\n (current: FullSearchState, action: Action): FullSearchState => {\n switch (action.kind) {\n case \"input\":\n return {\n ...current,\n query: action.query,\n selectedOption: undefined\n };\n case \"select-option\":\n return {\n ...current,\n selectedOption: action.option,\n query: action.option.label\n };\n case \"load-results\":\n return {\n ...current,\n search: {\n kind: \"loading\"\n }\n };\n case \"accept-results\":\n return {\n ...current,\n search: {\n kind: \"ready\",\n results: action.results\n }\n };\n }\n },\n undefined,\n (): FullSearchState => ({\n query: \"\",\n selectedOption: undefined,\n search: {\n kind: \"ready\",\n results: []\n }\n })\n );\n\n // Stores the promise for the current search.\n // Any results from outdated searches are ignored.\n const currentSearch = useRef<Promise<unknown>>(undefined);\n const startSearch = useEvent((query: string) => {\n if (!controller) {\n currentSearch.current = undefined;\n dispatch({ kind: \"accept-results\", results: [] });\n return;\n }\n\n LOG.isDebug() && LOG.debug(`Starting new search for query ${JSON.stringify(query)}.`);\n dispatch({ kind: \"load-results\" });\n const promise = (currentSearch.current = search(controller, query, getId).then(\n (results) => {\n // Check if this job is still current\n if (currentSearch.current === promise) {\n dispatch({ kind: \"accept-results\", results });\n }\n }\n ));\n });\n\n /**\n * Searches for the specified query and automatically selects the first matching result.\n *\n * If the search controller is unavailable or the search returns no results,\n * the promise resolves to `undefined`.\n *\n * When a match is found, this function:\n * - Executes the search using the provided query.\n * - Updates the UI with the returned search results.\n * - Selects the first available search option and notifies the component's callback function (if any).\n *\n * @param query - The search query to execute.\n * @returns A promise that resolves to the selected {@link SearchResult} and its source,\n * or `undefined` if no result is found or the search cannot be performed.\n */\n const searchAndSelect = useEvent(async (query: string): Promise<SelectResult | undefined> => {\n if (!controller) {\n return undefined;\n }\n\n const groups = await search(controller, query, getId);\n const firstOption = groups.flatMap((g) => g.options)[0];\n if (!firstOption) {\n return undefined;\n }\n\n const { result, source } = firstOption;\n\n // update UI\n dispatch({\n kind: \"accept-results\",\n results: groups\n });\n dispatch({\n kind: \"select-option\",\n option: firstOption\n });\n\n // notify component callback\n onSelect?.({ result, source, trigger: \"api-select\" });\n return { result, source };\n });\n\n // Called when the user confirms a search result\n const onOptionConfirmed = useEvent((option: SearchOption) => {\n // Do not start a new search when the user confirms a result\n dispatch({ kind: \"select-option\", option });\n });\n\n // Called when a user types into the input field\n const onInputChanged = useEvent((newValue: string) => {\n // Trigger a new search if the user changes the query by typing\n dispatch({ kind: \"input\", query: newValue });\n startSearch(newValue);\n });\n\n return {\n input: state.query,\n results: state.search,\n selectedOption: state.selectedOption,\n onOptionConfirmed,\n onInputChanged,\n searchAndSelect\n };\n}\n\nasync function search(\n controller: SearchController,\n query: string,\n getId: GetSearchSourceId\n): Promise<SearchOptionGroup[]> {\n let suggestions: ResultGroup[];\n try {\n suggestions = await controller.search(query);\n } catch (error) {\n if (!isAbortError(error)) {\n LOG.error(`Search failed`, error);\n }\n suggestions = [];\n }\n return mapSuggestions(suggestions, getId);\n}\n\n/**\n * Creates a controller to search on the given sources.\n */\nfunction useController(\n sources: SearchSource[],\n searchTypingDelay: number | undefined,\n maxResultsPerGroup: number | undefined,\n map: MapModel\n) {\n const [controller, setController] = useState<SearchController | undefined>(undefined);\n useEffect(() => {\n const controller = new SearchController(map, sources);\n setController(controller);\n return () => {\n controller.destroy();\n setController(undefined);\n };\n }, [map, sources]);\n\n useEffect(() => {\n if (controller) {\n controller.searchTypingDelay = searchTypingDelay;\n }\n }, [controller, searchTypingDelay]);\n useEffect(() => {\n if (controller) {\n controller.maxResultsPerSource = maxResultsPerGroup;\n }\n }, [controller, maxResultsPerGroup]);\n return controller;\n}\n\n// Maps results from search source to objects that the UI can work with.\nfunction mapSuggestions(\n resultGroups: ResultGroup[],\n getId: GetSearchSourceId\n): SearchOptionGroup[] {\n return resultGroups.map((resultGroup): SearchOptionGroup => {\n const groupId = getId(resultGroup.source);\n const optionGroup: SearchOptionGroup = {\n id: groupId,\n label: resultGroup.label,\n options: []\n };\n\n for (const result of resultGroup.results) {\n optionGroup.options.push({\n // unique (in this component)\n value: `${groupId}-${result.id}`,\n label: result.label,\n group: optionGroup,\n source: resultGroup.source,\n result: result\n });\n }\n return optionGroup;\n });\n}\n\ntype GetSearchSourceId = (source: SearchSource) => string;\n\nfunction useSearchSourceId(): GetSearchSourceId {\n const sourceIds = useRef<WeakMap<SearchSource, string>>(undefined);\n const counter = useRef(0);\n if (!sourceIds.current) {\n sourceIds.current = new WeakMap();\n }\n\n return useCallback((searchSource: SearchSource) => {\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n const ids = sourceIds.current!;\n if (!ids.has(searchSource)) {\n ids.set(searchSource, `source-${counter.current++}`);\n }\n // eslint-disable-next-line @typescript-eslint/no-non-null-assertion\n return ids.get(searchSource)!;\n }, []);\n}\n"],"names":["controller"],"mappings":"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"}
1
+ {"version":3,"file":"useSearchState.js","sources":["useSearchState.ts"],"sourcesContent":["// SPDX-FileCopyrightText: 2023-2025 Open Pioneer project (https://github.com/open-pioneer)\n// SPDX-License-Identifier: Apache-2.0\n\nimport { createLogger, isAbortError } from \"@open-pioneer/core\";\nimport { MapModel } from \"@open-pioneer/map\";\nimport { useEvent } from \"@open-pioneer/react-utils\";\nimport { sourceId } from \"open-pioneer:source-info\";\nimport { useCallback, useEffect, useReducer, useRef, useState } from \"react\";\nimport { SearchResult, SearchSource, SelectResult } from \"../api\";\nimport { SearchController, ResultGroup } from \"../model/SearchController\";\nimport { SearchProps } from \"./Search\";\n\nconst LOG = createLogger(sourceId);\n\nexport interface SearchOptionGroup {\n /** Unique id of this group. */\n id: string;\n\n /** Display text shown in menu. */\n label: string;\n\n /** Set of options that belong to this group. */\n options: SearchOption[];\n}\n\nexport interface SearchOption {\n /** Unique value for this option. */\n value: string;\n\n /** Display text shown in menu. */\n label: string;\n\n /** Search source that returned the suggestion. */\n source: SearchSource;\n\n /** Links to the parent group. */\n group: SearchOptionGroup;\n\n /** The raw result from the search source. */\n result: SearchResult;\n}\n\nexport interface SearchState {\n input: string;\n results: SearchResultsState;\n selectedOption: SearchOption | undefined;\n\n // NOTE: Stable functions\n onOptionConfirmed: (newOption: SearchOption) => void;\n onInputChanged: (newInput: string) => void;\n\n searchAndSelect: (query: string) => Promise<SelectResult | undefined>;\n}\n\nexport type SearchResultsReady = {\n kind: \"ready\";\n results: SearchOptionGroup[];\n};\n\nexport type SearchResultsLoading = {\n kind: \"loading\";\n};\n\nexport type SearchResultsState = SearchResultsReady | SearchResultsLoading;\n\n/**\n * Keeps track of the current input text, active searches and their results.\n *\n * NOTE: it would be great to merge this state handling with the search controller\n * in a future revision.\n */\n// oxlint-disable-next-line max-params\nexport function useSearchState(\n sources: SearchSource[],\n searchTypingDelay: number | undefined,\n maxResultsPerGroup: number | undefined,\n map: MapModel,\n onSelect: SearchProps[\"onSelect\"] | undefined // TODO: refactor --> model\n): SearchState {\n interface FullSearchState {\n query: string;\n selectedOption: SearchOption | undefined;\n search: SearchResultsState;\n }\n\n const getId = useSearchSourceId();\n const controller = useController(sources, searchTypingDelay, maxResultsPerGroup, map);\n\n type Action =\n | { kind: \"input\"; query: string }\n | { kind: \"select-option\"; option: SearchOption }\n | { kind: \"load-results\" }\n | { kind: \"accept-results\"; results: SearchOptionGroup[] };\n\n const [state, dispatch] = useReducer(\n (current: FullSearchState, action: Action): FullSearchState => {\n switch (action.kind) {\n case \"input\":\n return {\n ...current,\n query: action.query,\n selectedOption: undefined\n };\n case \"select-option\":\n return {\n ...current,\n selectedOption: action.option,\n query: action.option.label\n };\n case \"load-results\":\n return {\n ...current,\n search: {\n kind: \"loading\"\n }\n };\n case \"accept-results\":\n return {\n ...current,\n search: {\n kind: \"ready\",\n results: action.results\n }\n };\n }\n },\n undefined,\n (): FullSearchState => ({\n query: \"\",\n selectedOption: undefined,\n search: {\n kind: \"ready\",\n results: []\n }\n })\n );\n\n // Stores the promise for the current search.\n // Any results from outdated searches are ignored.\n const currentSearch = useRef<Promise<unknown>>(undefined);\n const startSearch = useEvent((query: string) => {\n if (!controller) {\n currentSearch.current = undefined;\n dispatch({ kind: \"accept-results\", results: [] });\n return;\n }\n\n LOG.isDebug() && LOG.debug(`Starting new search for query ${JSON.stringify(query)}.`);\n dispatch({ kind: \"load-results\" });\n const promise = (currentSearch.current = search(controller, query, getId).then(\n (results) => {\n // Check if this job is still current\n if (currentSearch.current === promise) {\n dispatch({ kind: \"accept-results\", results });\n }\n }\n ));\n });\n\n /**\n * Searches for the specified query and automatically selects the first matching result.\n *\n * If the search controller is unavailable or the search returns no results,\n * the promise resolves to `undefined`.\n *\n * When a match is found, this function:\n * - Executes the search using the provided query.\n * - Updates the UI with the returned search results.\n * - Selects the first available search option and notifies the component's callback function (if any).\n *\n * @param query - The search query to execute.\n * @returns A promise that resolves to the selected {@link SearchResult} and its source,\n * or `undefined` if no result is found or the search cannot be performed.\n */\n const searchAndSelect = useEvent(async (query: string): Promise<SelectResult | undefined> => {\n if (!controller) {\n return undefined;\n }\n\n const groups = await search(controller, query, getId);\n const firstOption = groups.flatMap((g) => g.options)[0];\n if (!firstOption) {\n return undefined;\n }\n\n const { result, source } = firstOption;\n\n // update UI\n dispatch({\n kind: \"accept-results\",\n results: groups\n });\n dispatch({\n kind: \"select-option\",\n option: firstOption\n });\n\n // notify component callback\n onSelect?.({ result, source, trigger: \"api-select\" });\n return { result, source };\n });\n\n // Called when the user confirms a search result\n const onOptionConfirmed = useEvent((option: SearchOption) => {\n // Do not start a new search when the user confirms a result\n dispatch({ kind: \"select-option\", option });\n });\n\n // Called when a user types into the input field\n const onInputChanged = useEvent((newValue: string) => {\n // Trigger a new search if the user changes the query by typing\n dispatch({ kind: \"input\", query: newValue });\n startSearch(newValue);\n });\n\n return {\n input: state.query,\n results: state.search,\n selectedOption: state.selectedOption,\n onOptionConfirmed,\n onInputChanged,\n searchAndSelect\n };\n}\n\nasync function search(\n controller: SearchController,\n query: string,\n getId: GetSearchSourceId\n): Promise<SearchOptionGroup[]> {\n let suggestions: ResultGroup[];\n try {\n suggestions = await controller.search(query);\n } catch (error) {\n if (!isAbortError(error)) {\n LOG.error(`Search failed`, error);\n }\n suggestions = [];\n }\n return mapSuggestions(suggestions, getId);\n}\n\n/**\n * Creates a controller to search on the given sources.\n */\nfunction useController(\n sources: SearchSource[],\n searchTypingDelay: number | undefined,\n maxResultsPerGroup: number | undefined,\n map: MapModel\n) {\n const [controller, setController] = useState<SearchController | undefined>(undefined);\n useEffect(() => {\n const controller = new SearchController(map, sources);\n setController(controller);\n return () => {\n controller.destroy();\n setController(undefined);\n };\n }, [map, sources]);\n\n useEffect(() => {\n if (controller) {\n controller.searchTypingDelay = searchTypingDelay;\n }\n }, [controller, searchTypingDelay]);\n useEffect(() => {\n if (controller) {\n controller.maxResultsPerSource = maxResultsPerGroup;\n }\n }, [controller, maxResultsPerGroup]);\n return controller;\n}\n\n// Maps results from search source to objects that the UI can work with.\nfunction mapSuggestions(\n resultGroups: ResultGroup[],\n getId: GetSearchSourceId\n): SearchOptionGroup[] {\n return resultGroups.map((resultGroup): SearchOptionGroup => {\n const groupId = getId(resultGroup.source);\n const optionGroup: SearchOptionGroup = {\n id: groupId,\n label: resultGroup.label,\n options: []\n };\n\n for (const result of resultGroup.results) {\n optionGroup.options.push({\n // unique (in this component)\n value: `${groupId}-${result.id}`,\n label: result.label,\n group: optionGroup,\n source: resultGroup.source,\n result: result\n });\n }\n return optionGroup;\n });\n}\n\ntype GetSearchSourceId = (source: SearchSource) => string;\n\nfunction useSearchSourceId(): GetSearchSourceId {\n const sourceIds = useRef<WeakMap<SearchSource, string>>(undefined);\n const counter = useRef(0);\n // oxlint-disable-next-line react/refs\n if (!sourceIds.current) {\n sourceIds.current = new WeakMap();\n }\n\n return useCallback((searchSource: SearchSource) => {\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n const ids = sourceIds.current!;\n if (!ids.has(searchSource)) {\n ids.set(searchSource, `source-${counter.current++}`);\n }\n // oxlint-disable-next-line @typescript-eslint/no-non-null-assertion\n return ids.get(searchSource)!;\n }, []);\n}\n"],"names":["controller"],"mappings":"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"}