@backstage/plugin-search-react 1.7.10 → 1.7.11-next.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 +13 -0
  2. package/alpha/package.json +1 -1
  3. package/dist/alpha.esm.js +1 -10
  4. package/dist/alpha.esm.js.map +1 -1
  5. package/dist/api.esm.js +16 -0
  6. package/dist/api.esm.js.map +1 -0
  7. package/dist/components/DefaultResultListItem/DefaultResultListItem.esm.js +70 -0
  8. package/dist/components/DefaultResultListItem/DefaultResultListItem.esm.js.map +1 -0
  9. package/dist/components/HighlightedSearchResultText/HighlightedSearchResultText.esm.js +23 -0
  10. package/dist/components/HighlightedSearchResultText/HighlightedSearchResultText.esm.js.map +1 -0
  11. package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js +107 -0
  12. package/dist/components/SearchAutocomplete/SearchAutocomplete.esm.js.map +1 -0
  13. package/dist/components/SearchAutocomplete/SearchAutocompleteDefaultOption.esm.js +27 -0
  14. package/dist/components/SearchAutocomplete/SearchAutocompleteDefaultOption.esm.js.map +1 -0
  15. package/dist/components/SearchBar/SearchBar.esm.js +161 -0
  16. package/dist/components/SearchBar/SearchBar.esm.js.map +1 -0
  17. package/dist/components/SearchFilter/SearchFilter.Autocomplete.esm.js +72 -0
  18. package/dist/components/SearchFilter/SearchFilter.Autocomplete.esm.js.map +1 -0
  19. package/dist/components/SearchFilter/SearchFilter.esm.js +157 -0
  20. package/dist/components/SearchFilter/SearchFilter.esm.js.map +1 -0
  21. package/dist/components/SearchFilter/hooks.esm.js +52 -0
  22. package/dist/components/SearchFilter/hooks.esm.js.map +1 -0
  23. package/dist/components/SearchPagination/SearchPagination.esm.js +82 -0
  24. package/dist/components/SearchPagination/SearchPagination.esm.js.map +1 -0
  25. package/dist/components/SearchResult/SearchResult.esm.js +70 -0
  26. package/dist/components/SearchResult/SearchResult.esm.js.map +1 -0
  27. package/dist/components/SearchResultGroup/SearchResultGroup.esm.js +259 -0
  28. package/dist/components/SearchResultGroup/SearchResultGroup.esm.js.map +1 -0
  29. package/dist/components/SearchResultList/SearchResultList.esm.js +67 -0
  30. package/dist/components/SearchResultList/SearchResultList.esm.js.map +1 -0
  31. package/dist/components/SearchResultPager/SearchResultPager.esm.js +44 -0
  32. package/dist/components/SearchResultPager/SearchResultPager.esm.js.map +1 -0
  33. package/dist/context/SearchContext.esm.js +126 -0
  34. package/dist/context/SearchContext.esm.js.map +1 -0
  35. package/dist/extensions.esm.js +123 -0
  36. package/dist/extensions.esm.js.map +1 -0
  37. package/dist/index.esm.js +15 -1153
  38. package/dist/index.esm.js.map +1 -1
  39. package/package.json +8 -8
  40. package/dist/esm/extensions-COgIMweE.esm.js +0 -205
  41. package/dist/esm/extensions-COgIMweE.esm.js.map +0 -1
package/dist/index.esm.js CHANGED
@@ -1,1154 +1,16 @@
1
- import { createApiRef, useApi, configApiRef, AnalyticsContext, useAnalytics } from '@backstage/core-plugin-api';
2
- import { S as SearchResultListItemExtensions, u as useSearchResultListItemExtensions, H as HigherOrderDefaultResultListItem } from './esm/extensions-COgIMweE.esm.js';
3
- export { b as HighlightedSearchResultText, a as SearchResultListItemExtension, c as createSearchResultListItemExtension } from './esm/extensions-COgIMweE.esm.js';
4
- import IconButton from '@material-ui/core/IconButton';
5
- import InputAdornment from '@material-ui/core/InputAdornment';
6
- import TextField from '@material-ui/core/TextField';
7
- import Button from '@material-ui/core/Button';
8
- import SearchIcon from '@material-ui/icons/Search';
9
- import React, { useContext, useState, useCallback, useEffect, forwardRef, useRef, useMemo } from 'react';
10
- import useDebounce from 'react-use/esm/useDebounce';
11
- import { isEqual, isFunction } from 'lodash';
12
- import useAsync from 'react-use/esm/useAsync';
13
- import usePrevious from 'react-use/esm/usePrevious';
14
- import { createVersionedContext, createVersionedValueMap } from '@backstage/version-bridge';
15
- import CircularProgress from '@material-ui/core/CircularProgress';
16
- import { makeStyles } from '@material-ui/core/styles';
17
- import Autocomplete from '@material-ui/lab/Autocomplete';
18
- import ListItemIcon from '@material-ui/core/ListItemIcon';
19
- import ListItemText from '@material-ui/core/ListItemText';
20
- import FormControl from '@material-ui/core/FormControl';
21
- import FormControlLabel from '@material-ui/core/FormControlLabel';
22
- import InputLabel from '@material-ui/core/InputLabel';
23
- import Checkbox from '@material-ui/core/Checkbox';
24
- import Select from '@material-ui/core/Select';
25
- import MenuItem from '@material-ui/core/MenuItem';
26
- import FormLabel from '@material-ui/core/FormLabel';
27
- import Typography from '@material-ui/core/Typography';
28
- import Chip from '@material-ui/core/Chip';
29
- import useAsyncFn from 'react-use/esm/useAsyncFn';
30
- import TablePagination from '@material-ui/core/TablePagination';
31
- import { Progress, ResponseErrorPanel, EmptyState, Link } from '@backstage/core-components';
32
- import List from '@material-ui/core/List';
33
- import qs from 'qs';
34
- import ListSubheader from '@material-ui/core/ListSubheader';
35
- import Menu from '@material-ui/core/Menu';
36
- import InputBase from '@material-ui/core/InputBase';
37
- import AddIcon from '@material-ui/icons/Add';
38
- import ArrowRightIcon from '@material-ui/icons/ArrowForwardIos';
39
- import ArrowBackIosIcon from '@material-ui/icons/ArrowBackIos';
40
- import '@material-ui/core/ListItem';
41
- import '@material-ui/core/Box';
42
-
43
- const searchApiRef = createApiRef({
44
- id: "plugin.search.queryservice"
45
- });
46
- class MockSearchApi {
47
- constructor(mockedResults) {
48
- this.mockedResults = mockedResults;
49
- }
50
- query() {
51
- return Promise.resolve(this.mockedResults || { results: [] });
52
- }
53
- }
54
-
55
- const SearchContext = createVersionedContext("search-context");
56
- const useSearch = () => {
57
- const context = useContext(SearchContext);
58
- if (!context) {
59
- throw new Error("useSearch must be used within a SearchContextProvider");
60
- }
61
- const value = context.atVersion(1);
62
- if (!value) {
63
- throw new Error("No SearchContext v1 found");
64
- }
65
- return value;
66
- };
67
- const useSearchContextCheck = () => {
68
- const context = useContext(SearchContext);
69
- return context !== void 0;
70
- };
71
- const defaultInitialSearchState = {
72
- term: "",
73
- types: [],
74
- filters: {},
75
- pageLimit: void 0,
76
- pageCursor: void 0
77
- };
78
- const useSearchContextValue = (initialValue = defaultInitialSearchState) => {
79
- var _a, _b, _c, _d;
80
- const searchApi = useApi(searchApiRef);
81
- const analytics = useAnalytics();
82
- const [term, setTerm] = useState(initialValue.term);
83
- const [types, setTypes] = useState(initialValue.types);
84
- const [filters, setFilters] = useState(initialValue.filters);
85
- const [pageLimit, setPageLimit] = useState(
86
- initialValue.pageLimit
87
- );
88
- const [pageCursor, setPageCursor] = useState(
89
- initialValue.pageCursor
90
- );
91
- const prevTerm = usePrevious(term);
92
- const prevFilters = usePrevious(filters);
93
- const result = useAsync(async () => {
94
- var _a2, _b2;
95
- const resultSet = await searchApi.query({
96
- term,
97
- types,
98
- filters,
99
- pageLimit,
100
- pageCursor
101
- });
102
- if (term) {
103
- analytics.captureEvent("search", term, {
104
- value: (_b2 = (_a2 = result.value) == null ? void 0 : _a2.numberOfResults) != null ? _b2 : void 0
105
- });
106
- }
107
- return resultSet;
108
- }, [term, types, filters, pageLimit, pageCursor]);
109
- const hasNextPage = !result.loading && !result.error && ((_a = result.value) == null ? void 0 : _a.nextPageCursor);
110
- const hasPreviousPage = !result.loading && !result.error && ((_b = result.value) == null ? void 0 : _b.previousPageCursor);
111
- const fetchNextPage = useCallback(() => {
112
- var _a2;
113
- setPageCursor((_a2 = result.value) == null ? void 0 : _a2.nextPageCursor);
114
- }, [(_c = result.value) == null ? void 0 : _c.nextPageCursor]);
115
- const fetchPreviousPage = useCallback(() => {
116
- var _a2;
117
- setPageCursor((_a2 = result.value) == null ? void 0 : _a2.previousPageCursor);
118
- }, [(_d = result.value) == null ? void 0 : _d.previousPageCursor]);
119
- useEffect(() => {
120
- if (prevTerm !== void 0 && term !== prevTerm) {
121
- setPageCursor(void 0);
122
- }
123
- }, [term, prevTerm, setPageCursor]);
124
- useEffect(() => {
125
- if (prevFilters !== void 0 && !isEqual(filters, prevFilters)) {
126
- setPageCursor(void 0);
127
- }
128
- }, [filters, prevFilters, setPageCursor]);
129
- const value = {
130
- result,
131
- term,
132
- setTerm,
133
- types,
134
- setTypes,
135
- filters,
136
- setFilters,
137
- pageLimit,
138
- setPageLimit,
139
- pageCursor,
140
- setPageCursor,
141
- fetchNextPage: hasNextPage ? fetchNextPage : void 0,
142
- fetchPreviousPage: hasPreviousPage ? fetchPreviousPage : void 0
143
- };
144
- return value;
145
- };
146
- const LocalSearchContext = (props) => {
147
- const { initialState, children } = props;
148
- const value = useSearchContextValue(initialState);
149
- return /* @__PURE__ */ React.createElement(
150
- AnalyticsContext,
151
- {
152
- attributes: { searchTypes: value.types.sort().join(",") }
153
- },
154
- /* @__PURE__ */ React.createElement(SearchContext.Provider, { value: createVersionedValueMap({ 1: value }) }, children)
155
- );
156
- };
157
- const SearchContextProvider = (props) => {
158
- const { initialState, inheritParentContextIfAvailable, children } = props;
159
- const hasParentContext = useSearchContextCheck();
160
- const configApi = useApi(configApiRef);
161
- const propsInitialSearchState = initialState != null ? initialState : {};
162
- const configInitialSearchState = configApi.has("search.query.pageLimit") ? { pageLimit: configApi.getNumber("search.query.pageLimit") } : {};
163
- const searchContextInitialState = {
164
- ...defaultInitialSearchState,
165
- ...propsInitialSearchState,
166
- ...configInitialSearchState
167
- };
168
- return hasParentContext && inheritParentContextIfAvailable ? /* @__PURE__ */ React.createElement(React.Fragment, null, children) : /* @__PURE__ */ React.createElement(LocalSearchContext, { initialState: searchContextInitialState }, children);
169
- };
170
-
171
- function withContext$1(Component) {
172
- return forwardRef((props, ref) => /* @__PURE__ */ React.createElement(SearchContextProvider, { inheritParentContextIfAvailable: true }, /* @__PURE__ */ React.createElement(Component, { ...props, ref })));
173
- }
174
- const SearchBarBase = withContext$1(
175
- forwardRef((props, ref) => {
176
- const {
177
- onChange,
178
- onKeyDown = () => {
179
- },
180
- onClear = () => {
181
- },
182
- onSubmit = () => {
183
- },
184
- debounceTime = 200,
185
- clearButton = true,
186
- fullWidth = true,
187
- value: defaultValue,
188
- label,
189
- placeholder,
190
- inputProps = {},
191
- InputProps = {},
192
- endAdornment,
193
- ...rest
194
- } = props;
195
- const configApi = useApi(configApiRef);
196
- const [value, setValue] = useState("");
197
- const forwardedValueRef = useRef("");
198
- useEffect(() => {
199
- setValue((prevValue) => {
200
- if (prevValue === forwardedValueRef.current) {
201
- return String(defaultValue);
202
- }
203
- return prevValue;
204
- });
205
- }, [defaultValue, forwardedValueRef]);
206
- useDebounce(
207
- () => {
208
- forwardedValueRef.current = value;
209
- onChange(value);
210
- },
211
- debounceTime,
212
- [value]
213
- );
214
- const handleChange = useCallback(
215
- (e) => {
216
- setValue(e.target.value);
217
- },
218
- [setValue]
219
- );
220
- const handleKeyDown = useCallback(
221
- (e) => {
222
- if (onKeyDown)
223
- onKeyDown(e);
224
- if (onSubmit && e.key === "Enter") {
225
- onSubmit();
226
- }
227
- },
228
- [onKeyDown, onSubmit]
229
- );
230
- const handleClear = useCallback(() => {
231
- forwardedValueRef.current = "";
232
- onChange("");
233
- setValue("");
234
- if (onClear) {
235
- onClear();
236
- }
237
- }, [onChange, onClear]);
238
- const ariaLabel = label ? void 0 : "Search";
239
- const inputPlaceholder = placeholder != null ? placeholder : `Search in ${configApi.getOptionalString("app.title") || "Backstage"}`;
240
- const startAdornment = /* @__PURE__ */ React.createElement(InputAdornment, { position: "start" }, /* @__PURE__ */ React.createElement(IconButton, { "aria-label": "Query", size: "small", disabled: true }, /* @__PURE__ */ React.createElement(SearchIcon, null)));
241
- const clearButtonEndAdornment = /* @__PURE__ */ React.createElement(InputAdornment, { position: "end" }, /* @__PURE__ */ React.createElement(
242
- Button,
243
- {
244
- "aria-label": "Clear",
245
- size: "small",
246
- onClick: handleClear,
247
- onKeyDown: (event) => {
248
- if (event.key === "Enter") {
249
- event.stopPropagation();
250
- }
251
- }
252
- },
253
- "Clear"
254
- ));
255
- return /* @__PURE__ */ React.createElement(
256
- TextField,
257
- {
258
- id: "search-bar-text-field",
259
- "data-testid": "search-bar-next",
260
- variant: "outlined",
261
- margin: "normal",
262
- inputRef: ref,
263
- value,
264
- label,
265
- placeholder: inputPlaceholder,
266
- InputProps: {
267
- startAdornment,
268
- endAdornment: clearButton ? clearButtonEndAdornment : endAdornment,
269
- ...InputProps
270
- },
271
- inputProps: {
272
- "aria-label": ariaLabel,
273
- ...inputProps
274
- },
275
- fullWidth,
276
- onChange: handleChange,
277
- onKeyDown: handleKeyDown,
278
- ...rest
279
- }
280
- );
281
- })
282
- );
283
- const SearchBar = withContext$1(
284
- forwardRef((props, ref) => {
285
- const { value: initialValue = "", onChange, ...rest } = props;
286
- const { term, setTerm } = useSearch();
287
- useEffect(() => {
288
- if (initialValue) {
289
- setTerm(String(initialValue));
290
- }
291
- }, [initialValue, setTerm]);
292
- const handleChange = useCallback(
293
- (newValue) => {
294
- if (onChange) {
295
- onChange(newValue);
296
- } else {
297
- setTerm(newValue);
298
- }
299
- },
300
- [onChange, setTerm]
301
- );
302
- return /* @__PURE__ */ React.createElement(
303
- AnalyticsContext,
304
- {
305
- attributes: { pluginId: "search", extension: "SearchBar" }
306
- },
307
- /* @__PURE__ */ React.createElement(
308
- SearchBarBase,
309
- {
310
- ...rest,
311
- ref,
312
- value: term,
313
- onChange: handleChange
314
- }
315
- )
316
- );
317
- })
318
- );
319
-
320
- const useStyles$3 = makeStyles((theme) => ({
321
- loading: {
322
- right: theme.spacing(1),
323
- position: "absolute"
324
- }
325
- }));
326
- const withContext = (Component) => {
327
- return (props) => /* @__PURE__ */ React.createElement(SearchContextProvider, { inheritParentContextIfAvailable: true }, /* @__PURE__ */ React.createElement(Component, { ...props }));
328
- };
329
- const SearchAutocompleteLoadingAdornment = () => {
330
- const classes = useStyles$3();
331
- return /* @__PURE__ */ React.createElement(
332
- CircularProgress,
333
- {
334
- className: classes.loading,
335
- "data-testid": "search-autocomplete-progressbar",
336
- color: "inherit",
337
- size: 20
338
- }
339
- );
340
- };
341
- const SearchAutocomplete = withContext(
342
- function SearchAutocompleteComponent(props) {
343
- const {
344
- loading,
345
- value,
346
- onChange = () => {
347
- },
348
- options = [],
349
- getOptionLabel = (option) => String(option),
350
- inputPlaceholder,
351
- inputDebounceTime,
352
- freeSolo = true,
353
- fullWidth = true,
354
- clearOnBlur = false,
355
- "data-testid": dataTestId = "search-autocomplete",
356
- ...rest
357
- } = props;
358
- const { setTerm } = useSearch();
359
- const getInputValue = useCallback(
360
- (option) => {
361
- if (!option)
362
- return "";
363
- if (typeof option === "string")
364
- return option;
365
- return getOptionLabel(option);
366
- },
367
- [getOptionLabel]
368
- );
369
- const inputValue = useMemo(
370
- () => getInputValue(value),
371
- [value, getInputValue]
372
- );
373
- const handleChange = useCallback(
374
- (event, option, reason, details) => {
375
- setTerm(getInputValue(option));
376
- onChange(event, option, reason, details);
377
- },
378
- [getInputValue, setTerm, onChange]
379
- );
380
- const renderInput = useCallback(
381
- ({
382
- InputProps: { ref, className, endAdornment },
383
- InputLabelProps,
384
- ...params
385
- }) => /* @__PURE__ */ React.createElement(
386
- SearchBar,
387
- {
388
- ...params,
389
- ref,
390
- clearButton: false,
391
- value: inputValue,
392
- placeholder: inputPlaceholder,
393
- debounceTime: inputDebounceTime,
394
- endAdornment: loading ? /* @__PURE__ */ React.createElement(SearchAutocompleteLoadingAdornment, null) : endAdornment,
395
- InputProps: { className }
396
- }
397
- ),
398
- [loading, inputValue, inputPlaceholder, inputDebounceTime]
399
- );
400
- return /* @__PURE__ */ React.createElement(
401
- Autocomplete,
402
- {
403
- ...rest,
404
- "data-testid": dataTestId,
405
- value,
406
- onChange: handleChange,
407
- options,
408
- getOptionLabel,
409
- renderInput,
410
- freeSolo,
411
- fullWidth,
412
- clearOnBlur
413
- }
414
- );
415
- }
416
- );
417
-
418
- const SearchAutocompleteDefaultOption = (props) => {
419
- const {
420
- icon,
421
- primaryText,
422
- primaryTextTypographyProps,
423
- secondaryText,
424
- secondaryTextTypographyProps,
425
- disableTextTypography
426
- } = props;
427
- return /* @__PURE__ */ React.createElement(React.Fragment, null, icon ? /* @__PURE__ */ React.createElement(ListItemIcon, null, icon) : null, /* @__PURE__ */ React.createElement(
428
- ListItemText,
429
- {
430
- primary: primaryText,
431
- primaryTypographyProps: primaryTextTypographyProps,
432
- secondary: secondaryText,
433
- secondaryTypographyProps: secondaryTextTypographyProps,
434
- disableTypography: disableTextTypography
435
- }
436
- ));
437
- };
438
-
439
- const useAsyncFilterValues = (fn, inputValue, defaultValues = [], debounce = 250) => {
440
- const valuesMemo = useRef({});
441
- const definiteFn = fn || (() => Promise.resolve([]));
442
- const [state, callback] = useAsyncFn(definiteFn, [inputValue], {
443
- loading: true
444
- });
445
- useDebounce(
446
- () => {
447
- if (valuesMemo.current[inputValue] === void 0) {
448
- valuesMemo.current[inputValue] = callback(inputValue).then((values) => {
449
- valuesMemo.current[inputValue] = values;
450
- return values;
451
- });
452
- }
453
- },
454
- debounce,
455
- [callback, inputValue]
456
- );
457
- if (defaultValues.length) {
458
- return {
459
- loading: false,
460
- value: defaultValues
461
- };
462
- }
463
- const possibleValue = valuesMemo.current[inputValue];
464
- if (Array.isArray(possibleValue)) {
465
- return {
466
- loading: false,
467
- value: possibleValue
468
- };
469
- }
470
- return state;
471
- };
472
- const useDefaultFilterValue = (name, defaultValue) => {
473
- const { setFilters } = useSearch();
474
- useEffect(() => {
475
- if (defaultValue && [defaultValue].flat().length > 0) {
476
- setFilters((prevFilters) => ({
477
- ...prevFilters,
478
- [name]: defaultValue
479
- }));
480
- }
481
- }, []);
482
- };
483
-
484
- const AutocompleteFilter = (props) => {
485
- const {
486
- className,
487
- defaultValue,
488
- name,
489
- values: givenValues,
490
- valuesDebounceMs,
491
- label,
492
- filterSelectedOptions,
493
- limitTags,
494
- multiple
495
- } = props;
496
- const [inputValue, setInputValue] = useState("");
497
- useDefaultFilterValue(name, defaultValue);
498
- const asyncValues = typeof givenValues === "function" ? givenValues : void 0;
499
- const defaultValues = typeof givenValues === "function" ? void 0 : givenValues;
500
- const { value: values, loading } = useAsyncFilterValues(
501
- asyncValues,
502
- inputValue,
503
- defaultValues,
504
- valuesDebounceMs
505
- );
506
- const { filters, setFilters } = useSearch();
507
- const filterValue = filters[name] || (multiple ? [] : null);
508
- const handleChange = (_, newValue) => {
509
- setFilters((prevState) => {
510
- const { [name]: filter, ...others } = prevState;
511
- if (newValue) {
512
- return { ...others, [name]: newValue };
513
- }
514
- return { ...others };
515
- });
516
- };
517
- const renderInput = (params) => /* @__PURE__ */ React.createElement(
518
- TextField,
519
- {
520
- ...params,
521
- name: "search",
522
- variant: "outlined",
523
- label,
524
- fullWidth: true
525
- }
526
- );
527
- const renderTags = (tagValue, getTagProps) => tagValue.map((option, index) => /* @__PURE__ */ React.createElement(Chip, { label: option, color: "primary", ...getTagProps({ index }) }));
528
- return /* @__PURE__ */ React.createElement(
529
- Autocomplete,
530
- {
531
- filterSelectedOptions,
532
- limitTags,
533
- multiple,
534
- className,
535
- id: `${multiple ? "multi-" : ""}select-filter-${name}--select`,
536
- options: values || [],
537
- loading,
538
- value: filterValue,
539
- onChange: handleChange,
540
- onInputChange: (_, newValue) => setInputValue(newValue),
541
- renderInput,
542
- renderTags
543
- }
544
- );
545
- };
546
-
547
- const useStyles$2 = makeStyles({
548
- label: {
549
- textTransform: "capitalize"
550
- },
551
- checkboxWrapper: {
552
- display: "flex",
553
- alignItems: "center",
554
- width: "100%"
555
- },
556
- textWrapper: {
557
- overflow: "hidden",
558
- textOverflow: "ellipsis",
559
- whiteSpace: "nowrap"
560
- }
561
- });
562
- const CheckboxFilter = (props) => {
563
- const {
564
- className,
565
- defaultValue,
566
- label,
567
- name,
568
- values: givenValues = [],
569
- valuesDebounceMs
570
- } = props;
571
- const classes = useStyles$2();
572
- const { filters, setFilters } = useSearch();
573
- useDefaultFilterValue(name, defaultValue);
574
- const asyncValues = typeof givenValues === "function" ? givenValues : void 0;
575
- const defaultValues = typeof givenValues === "function" ? void 0 : givenValues;
576
- const { value: values = [], loading } = useAsyncFilterValues(
577
- asyncValues,
578
- "",
579
- defaultValues,
580
- valuesDebounceMs
581
- );
582
- const handleChange = (e) => {
583
- const {
584
- target: { value, checked }
585
- } = e;
586
- setFilters((prevFilters) => {
587
- const { [name]: filter, ...others } = prevFilters;
588
- const rest = (filter || []).filter((i) => i !== value);
589
- const items = checked ? [...rest, value] : rest;
590
- return items.length ? { ...others, [name]: items } : others;
591
- });
592
- };
593
- return /* @__PURE__ */ React.createElement(
594
- FormControl,
595
- {
596
- className,
597
- disabled: loading,
598
- fullWidth: true,
599
- "data-testid": "search-checkboxfilter-next"
600
- },
601
- label ? /* @__PURE__ */ React.createElement(FormLabel, { className: classes.label }, label) : null,
602
- values.map((value) => {
603
- var _a;
604
- return /* @__PURE__ */ React.createElement(
605
- FormControlLabel,
606
- {
607
- key: value,
608
- classes: {
609
- root: classes.checkboxWrapper,
610
- label: classes.textWrapper
611
- },
612
- label: value,
613
- control: /* @__PURE__ */ React.createElement(
614
- Checkbox,
615
- {
616
- color: "primary",
617
- inputProps: { "aria-labelledby": value },
618
- value,
619
- name: value,
620
- onChange: handleChange,
621
- checked: ((_a = filters[name]) != null ? _a : []).includes(value)
622
- }
623
- )
624
- }
625
- );
626
- })
627
- );
628
- };
629
- const SelectFilter = (props) => {
630
- const {
631
- className,
632
- defaultValue,
633
- label,
634
- name,
635
- values: givenValues,
636
- valuesDebounceMs
637
- } = props;
638
- const classes = useStyles$2();
639
- useDefaultFilterValue(name, defaultValue);
640
- const asyncValues = typeof givenValues === "function" ? givenValues : void 0;
641
- const defaultValues = typeof givenValues === "function" ? void 0 : givenValues;
642
- const { value: values = [], loading } = useAsyncFilterValues(
643
- asyncValues,
644
- "",
645
- defaultValues,
646
- valuesDebounceMs
647
- );
648
- const { filters, setFilters } = useSearch();
649
- const handleChange = (e) => {
650
- const {
651
- target: { value }
652
- } = e;
653
- setFilters((prevFilters) => {
654
- const { [name]: filter, ...others } = prevFilters;
655
- return value ? { ...others, [name]: value } : others;
656
- });
657
- };
658
- return /* @__PURE__ */ React.createElement(
659
- FormControl,
660
- {
661
- disabled: loading,
662
- className,
663
- variant: "filled",
664
- fullWidth: true,
665
- "data-testid": "search-selectfilter-next"
666
- },
667
- label ? /* @__PURE__ */ React.createElement(InputLabel, { className: classes.label, margin: "dense" }, label) : null,
668
- /* @__PURE__ */ React.createElement(
669
- Select,
670
- {
671
- variant: "outlined",
672
- value: filters[name] || "",
673
- onChange: handleChange
674
- },
675
- /* @__PURE__ */ React.createElement(MenuItem, { value: "" }, /* @__PURE__ */ React.createElement("em", null, "All")),
676
- values.map((value) => /* @__PURE__ */ React.createElement(MenuItem, { key: value, value }, /* @__PURE__ */ React.createElement(Typography, { variant: "inherit", noWrap: true }, value)))
677
- )
678
- );
679
- };
680
- const SearchFilter = (props) => {
681
- const { component: Element, ...elementProps } = props;
682
- return /* @__PURE__ */ React.createElement(Element, { ...elementProps });
683
- };
684
- SearchFilter.Checkbox = (props) => /* @__PURE__ */ React.createElement(SearchFilter, { ...props, component: CheckboxFilter });
685
- SearchFilter.Select = (props) => /* @__PURE__ */ React.createElement(SearchFilter, { ...props, component: SelectFilter });
686
- SearchFilter.Autocomplete = (props) => /* @__PURE__ */ React.createElement(SearchFilter, { ...props, component: AutocompleteFilter });
687
-
688
- const encodePageCursor = (pageCursor) => {
689
- return Buffer.from(pageCursor.toString(), "utf-8").toString("base64");
690
- };
691
- const decodePageCursor = (pageCursor) => {
692
- if (!pageCursor)
693
- return 0;
694
- return Number(Buffer.from(pageCursor, "base64").toString("utf-8"));
695
- };
696
- const SearchPaginationBase = (props) => {
697
- const {
698
- total: count = -1,
699
- cursor: pageCursor,
700
- hasNextPage,
701
- onCursorChange: onPageCursorChange,
702
- limit: rowsPerPage = 25,
703
- limitLabel: labelRowsPerPage = "Results per page:",
704
- limitText: labelDisplayedRows = ({ from, to }) => count > 0 ? `of ${count}` : `${from}-${to}`,
705
- limitOptions: rowsPerPageOptions,
706
- onLimitChange: onPageLimitChange,
707
- ...rest
708
- } = props;
709
- const page = useMemo(() => decodePageCursor(pageCursor), [pageCursor]);
710
- const handlePageChange = useCallback(
711
- (_, newValue) => {
712
- onPageCursorChange == null ? void 0 : onPageCursorChange(encodePageCursor(newValue));
713
- },
714
- [onPageCursorChange]
715
- );
716
- const handleRowsPerPageChange = useCallback(
717
- (e) => {
718
- const newValue = e.target.value;
719
- onPageLimitChange == null ? void 0 : onPageLimitChange(parseInt(newValue, 10));
720
- },
721
- [onPageLimitChange]
722
- );
723
- return /* @__PURE__ */ React.createElement(
724
- TablePagination,
725
- {
726
- ...rest,
727
- component: "div",
728
- count,
729
- page,
730
- nextIconButtonProps: {
731
- ...hasNextPage !== void 0 && { disabled: !hasNextPage }
732
- },
733
- onPageChange: handlePageChange,
734
- rowsPerPage,
735
- labelRowsPerPage,
736
- labelDisplayedRows,
737
- rowsPerPageOptions,
738
- onRowsPerPageChange: handleRowsPerPageChange
739
- }
740
- );
741
- };
742
- const SearchPagination = (props) => {
743
- const { pageLimit, setPageLimit, pageCursor, setPageCursor, fetchNextPage } = useSearch();
744
- const handlePageLimitChange = useCallback(
745
- (newPageLimit) => {
746
- setPageLimit(newPageLimit);
747
- setPageCursor(void 0);
748
- },
749
- [setPageLimit, setPageCursor]
750
- );
751
- return /* @__PURE__ */ React.createElement(
752
- SearchPaginationBase,
753
- {
754
- ...props,
755
- hasNextPage: !!fetchNextPage,
756
- limit: pageLimit,
757
- onLimitChange: handlePageLimitChange,
758
- cursor: pageCursor,
759
- onCursorChange: setPageCursor
760
- }
761
- );
762
- };
763
-
764
- const SearchResultContext = (props) => {
765
- const { children } = props;
766
- const context = useSearch();
767
- const { result: state, ...query } = context;
768
- return children(state, query);
769
- };
770
- const SearchResultApi = (props) => {
771
- const { query, children } = props;
772
- const searchApi = useApi(searchApiRef);
773
- const state = useAsync(() => {
774
- const { term = "", types = [], filters = {}, ...rest } = query;
775
- return searchApi.query({ ...rest, term, types, filters });
776
- }, [query]);
777
- return children(state, query);
778
- };
779
- const SearchResultState = (props) => {
780
- const { query, children } = props;
781
- return query ? /* @__PURE__ */ React.createElement(SearchResultApi, { query }, children) : /* @__PURE__ */ React.createElement(SearchResultContext, null, children);
782
- };
783
- const SearchResultComponent = (props) => {
784
- const {
785
- query,
786
- children,
787
- noResultsComponent = /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "Sorry, no results were found" }),
788
- ...rest
789
- } = props;
790
- return /* @__PURE__ */ React.createElement(SearchResultState, { query }, ({ loading, error, value }) => {
791
- if (loading) {
792
- return /* @__PURE__ */ React.createElement(Progress, null);
793
- }
794
- if (error) {
795
- return /* @__PURE__ */ React.createElement(
796
- ResponseErrorPanel,
797
- {
798
- title: "Error encountered while fetching search results",
799
- error
800
- }
801
- );
802
- }
803
- if (!(value == null ? void 0 : value.results.length)) {
804
- return noResultsComponent;
805
- }
806
- if (isFunction(children)) {
807
- return children(value);
808
- }
809
- return /* @__PURE__ */ React.createElement(SearchResultListItemExtensions, { ...rest, results: value.results }, children);
810
- });
811
- };
812
- const SearchResult = (props) => /* @__PURE__ */ React.createElement(
813
- AnalyticsContext,
814
- {
815
- attributes: {
816
- pluginId: "search",
817
- extension: "SearchResult"
818
- }
819
- },
820
- /* @__PURE__ */ React.createElement(SearchResultComponent, { ...props })
821
- );
822
-
823
- const SearchResultListLayout = (props) => {
824
- const {
825
- error,
826
- loading,
827
- resultItems,
828
- renderResultItem = (resultItem) => /* @__PURE__ */ React.createElement(
829
- HigherOrderDefaultResultListItem,
830
- {
831
- key: resultItem.document.location,
832
- result: resultItem.document
833
- }
834
- ),
835
- disableRenderingWithNoResults,
836
- noResultsComponent = disableRenderingWithNoResults ? null : /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "Sorry, no results were found" }),
837
- ...rest
838
- } = props;
839
- if (loading) {
840
- return /* @__PURE__ */ React.createElement(Progress, null);
841
- }
842
- if (error) {
843
- return /* @__PURE__ */ React.createElement(
844
- ResponseErrorPanel,
845
- {
846
- title: "Error encountered while fetching search results",
847
- error
848
- }
849
- );
850
- }
851
- if (!(resultItems == null ? void 0 : resultItems.length)) {
852
- return /* @__PURE__ */ React.createElement(React.Fragment, null, noResultsComponent);
853
- }
854
- return /* @__PURE__ */ React.createElement(List, { ...rest }, resultItems.map(renderResultItem));
855
- };
856
- const SearchResultList = (props) => {
857
- const { query, renderResultItem, children, ...rest } = props;
858
- const defaultRenderResultItem = useSearchResultListItemExtensions(children);
859
- return /* @__PURE__ */ React.createElement(
860
- AnalyticsContext,
861
- {
862
- attributes: {
863
- pluginId: "search",
864
- extension: "SearchResultList"
865
- }
866
- },
867
- /* @__PURE__ */ React.createElement(SearchResultState, { query }, ({ loading, error, value }) => /* @__PURE__ */ React.createElement(
868
- SearchResultListLayout,
869
- {
870
- ...rest,
871
- error,
872
- loading,
873
- resultItems: value == null ? void 0 : value.results,
874
- renderResultItem: renderResultItem != null ? renderResultItem : defaultRenderResultItem
875
- }
876
- ))
877
- );
878
- };
879
-
880
- const useStyles$1 = makeStyles((theme) => ({
881
- listSubheader: {
882
- display: "flex",
883
- alignItems: "center"
884
- },
885
- listSubheaderName: {
886
- marginLeft: theme.spacing(1),
887
- textTransform: "uppercase"
888
- },
889
- listSubheaderChip: {
890
- color: theme.palette.text.secondary,
891
- margin: theme.spacing(0, 0, 0, 1.5)
892
- },
893
- listSubheaderFilter: {
894
- display: "flex",
895
- color: theme.palette.text.secondary,
896
- margin: theme.spacing(0, 0, 0, 1.5)
897
- },
898
- listSubheaderLink: {
899
- marginLeft: "auto",
900
- display: "flex",
901
- alignItems: "center"
902
- },
903
- listSubheaderLinkIcon: {
904
- fontSize: "inherit",
905
- marginLeft: theme.spacing(0.5)
906
- }
907
- }));
908
- const SearchResultGroupFilterFieldLayout = (props) => {
909
- const classes = useStyles$1();
910
- const { label, children, ...rest } = props;
911
- return /* @__PURE__ */ React.createElement(
912
- Chip,
913
- {
914
- ...rest,
915
- className: classes.listSubheaderFilter,
916
- variant: "outlined",
917
- label: /* @__PURE__ */ React.createElement(React.Fragment, null, label, ": ", children)
918
- }
919
- );
920
- };
921
- const NullIcon = () => null;
922
- const useSearchResultGroupTextFilterStyles = makeStyles((theme) => ({
923
- root: {
924
- fontSize: "inherit",
925
- "&:focus": {
926
- outline: "none",
927
- background: theme.palette.common.white
928
- },
929
- "&:not(:focus)": {
930
- cursor: "pointer",
931
- color: theme.palette.primary.main,
932
- "&:hover": {
933
- textDecoration: "underline"
934
- }
935
- }
936
- }
937
- }));
938
- const SearchResultGroupTextFilterField = (props) => {
939
- const classes = useSearchResultGroupTextFilterStyles();
940
- const { label, value = "None", onChange, onDelete } = props;
941
- const handleChange = useCallback(
942
- (e) => {
943
- onChange(e.target.value);
944
- },
945
- [onChange]
946
- );
947
- return /* @__PURE__ */ React.createElement(SearchResultGroupFilterFieldLayout, { label, onDelete }, /* @__PURE__ */ React.createElement(
948
- Typography,
949
- {
950
- role: "textbox",
951
- component: "span",
952
- className: classes.root,
953
- onChange: handleChange,
954
- contentEditable: true,
955
- suppressContentEditableWarning: true
956
- },
957
- value == null ? void 0 : value.toString()
958
- ));
959
- };
960
- const useSearchResultGroupSelectFilterStyles = makeStyles((theme) => ({
961
- root: {
962
- fontSize: "inherit",
963
- "&:not(:focus)": {
964
- cursor: "pointer",
965
- color: theme.palette.primary.main,
966
- "&:hover": {
967
- textDecoration: "underline"
968
- }
969
- },
970
- "&:focus": {
971
- outline: "none"
972
- },
973
- "&>div:first-child": {
974
- padding: 0
975
- }
976
- }
977
- }));
978
- const SearchResultGroupSelectFilterField = (props) => {
979
- const classes = useSearchResultGroupSelectFilterStyles();
980
- const { label, value = "none", onChange, onDelete, children } = props;
981
- const handleChange = useCallback(
982
- (e) => {
983
- onChange(e.target.value);
984
- },
985
- [onChange]
986
- );
987
- return /* @__PURE__ */ React.createElement(SearchResultGroupFilterFieldLayout, { label, onDelete }, /* @__PURE__ */ React.createElement(
988
- Select,
989
- {
990
- className: classes.root,
991
- value,
992
- onChange: handleChange,
993
- input: /* @__PURE__ */ React.createElement(InputBase, null),
994
- IconComponent: NullIcon
995
- },
996
- /* @__PURE__ */ React.createElement(MenuItem, { value: "none" }, "None"),
997
- children
998
- ));
999
- };
1000
- function SearchResultGroupLayout(props) {
1001
- const classes = useStyles$1();
1002
- const [anchorEl, setAnchorEl] = useState(null);
1003
- const {
1004
- error,
1005
- loading,
1006
- icon,
1007
- title,
1008
- titleProps = {},
1009
- link = /* @__PURE__ */ React.createElement(React.Fragment, null, "See all", /* @__PURE__ */ React.createElement(ArrowRightIcon, { className: classes.listSubheaderLinkIcon })),
1010
- linkProps = {},
1011
- filterOptions,
1012
- renderFilterOption = (filterOption) => /* @__PURE__ */ React.createElement(MenuItem, { key: String(filterOption), value: String(filterOption) }, String(filterOption)),
1013
- filterFields,
1014
- renderFilterField,
1015
- resultItems,
1016
- renderResultItem = (resultItem) => /* @__PURE__ */ React.createElement(
1017
- HigherOrderDefaultResultListItem,
1018
- {
1019
- key: resultItem.document.location,
1020
- result: resultItem.document
1021
- }
1022
- ),
1023
- disableRenderingWithNoResults,
1024
- noResultsComponent = disableRenderingWithNoResults ? null : /* @__PURE__ */ React.createElement(EmptyState, { missing: "data", title: "Sorry, no results were found" }),
1025
- ...rest
1026
- } = props;
1027
- const handleClick = useCallback((e) => {
1028
- setAnchorEl(e.currentTarget);
1029
- }, []);
1030
- const handleClose = useCallback(() => {
1031
- setAnchorEl(null);
1032
- }, []);
1033
- if (loading) {
1034
- return /* @__PURE__ */ React.createElement(Progress, null);
1035
- }
1036
- if (error) {
1037
- return /* @__PURE__ */ React.createElement(
1038
- ResponseErrorPanel,
1039
- {
1040
- title: "Error encountered while fetching search results",
1041
- error
1042
- }
1043
- );
1044
- }
1045
- if (!(resultItems == null ? void 0 : resultItems.length)) {
1046
- return /* @__PURE__ */ React.createElement(React.Fragment, null, noResultsComponent);
1047
- }
1048
- return /* @__PURE__ */ React.createElement(List, { ...rest }, /* @__PURE__ */ React.createElement(ListSubheader, { className: classes.listSubheader }, icon, /* @__PURE__ */ React.createElement(
1049
- Typography,
1050
- {
1051
- className: classes.listSubheaderName,
1052
- component: "strong",
1053
- ...titleProps
1054
- },
1055
- title
1056
- ), filterOptions ? /* @__PURE__ */ React.createElement(
1057
- Chip,
1058
- {
1059
- className: classes.listSubheaderChip,
1060
- component: "button",
1061
- icon: /* @__PURE__ */ React.createElement(AddIcon, null),
1062
- variant: "outlined",
1063
- label: "Add filter",
1064
- "aria-controls": "filters-menu",
1065
- "aria-haspopup": "true",
1066
- onClick: handleClick
1067
- }
1068
- ) : null, filterOptions ? /* @__PURE__ */ React.createElement(
1069
- Menu,
1070
- {
1071
- id: "filters-menu",
1072
- anchorEl,
1073
- open: Boolean(anchorEl),
1074
- onClose: handleClose,
1075
- onClick: handleClose,
1076
- keepMounted: true
1077
- },
1078
- filterOptions.map(renderFilterOption)
1079
- ) : null, filterFields == null ? void 0 : filterFields.map(
1080
- (filterField) => {
1081
- var _a;
1082
- return (_a = renderFilterField == null ? void 0 : renderFilterField(filterField)) != null ? _a : null;
1083
- }
1084
- ), /* @__PURE__ */ React.createElement(Link, { className: classes.listSubheaderLink, to: "/search", ...linkProps }, link)), resultItems.map(renderResultItem));
1085
- }
1086
- function SearchResultGroup(props) {
1087
- const { query, children, renderResultItem, linkProps = {}, ...rest } = props;
1088
- const defaultRenderResultItem = useSearchResultListItemExtensions(children);
1089
- return /* @__PURE__ */ React.createElement(
1090
- AnalyticsContext,
1091
- {
1092
- attributes: {
1093
- pluginId: "search",
1094
- extension: "SearchResultGroup"
1095
- }
1096
- },
1097
- /* @__PURE__ */ React.createElement(SearchResultState, { query }, ({ loading, error, value }, { term, types, pageCursor, filters = {} }) => {
1098
- const to = `/search?${qs.stringify(
1099
- { term, types, filters, pageCursor, query: term },
1100
- { arrayFormat: "brackets" }
1101
- )}`;
1102
- return /* @__PURE__ */ React.createElement(
1103
- SearchResultGroupLayout,
1104
- {
1105
- ...rest,
1106
- error,
1107
- loading,
1108
- linkProps: { to, ...linkProps },
1109
- filterFields: Object.keys(filters),
1110
- resultItems: value == null ? void 0 : value.results,
1111
- renderResultItem: renderResultItem != null ? renderResultItem : defaultRenderResultItem
1112
- }
1113
- );
1114
- })
1115
- );
1116
- }
1117
-
1118
- const useStyles = makeStyles((theme) => ({
1119
- root: {
1120
- display: "flex",
1121
- justifyContent: "space-between",
1122
- gap: theme.spacing(2),
1123
- margin: theme.spacing(2, 0)
1124
- }
1125
- }));
1126
- const SearchResultPager = () => {
1127
- const { fetchNextPage, fetchPreviousPage } = useSearch();
1128
- const classes = useStyles();
1129
- if (!fetchNextPage && !fetchPreviousPage) {
1130
- return /* @__PURE__ */ React.createElement(React.Fragment, null);
1131
- }
1132
- return /* @__PURE__ */ React.createElement("nav", { "aria-label": "pagination navigation", className: classes.root }, /* @__PURE__ */ React.createElement(
1133
- Button,
1134
- {
1135
- "aria-label": "previous page",
1136
- disabled: !fetchPreviousPage,
1137
- onClick: fetchPreviousPage,
1138
- startIcon: /* @__PURE__ */ React.createElement(ArrowBackIosIcon, null)
1139
- },
1140
- "Previous"
1141
- ), /* @__PURE__ */ React.createElement(
1142
- Button,
1143
- {
1144
- "aria-label": "next page",
1145
- disabled: !fetchNextPage,
1146
- onClick: fetchNextPage,
1147
- endIcon: /* @__PURE__ */ React.createElement(ArrowRightIcon, null)
1148
- },
1149
- "Next"
1150
- ));
1151
- };
1152
-
1153
- export { AutocompleteFilter, CheckboxFilter, HigherOrderDefaultResultListItem as DefaultResultListItem, MockSearchApi, SearchAutocomplete, SearchAutocompleteDefaultOption, SearchBar, SearchBarBase, SearchContextProvider, SearchFilter, SearchPagination, SearchPaginationBase, SearchResult, SearchResultApi, SearchResultComponent, SearchResultContext, SearchResultGroup, SearchResultGroupFilterFieldLayout, SearchResultGroupLayout, SearchResultGroupSelectFilterField, SearchResultGroupTextFilterField, SearchResultList, SearchResultListItemExtensions, SearchResultListLayout, SearchResultPager, SearchResultState, SelectFilter, searchApiRef, useSearch, useSearchContextCheck, useSearchResultListItemExtensions };
1
+ export { MockSearchApi, searchApiRef } from './api.esm.js';
2
+ export { SearchResultListItemExtension, SearchResultListItemExtensions, createSearchResultListItemExtension, useSearchResultListItemExtensions } from './extensions.esm.js';
3
+ export { HighlightedSearchResultText } from './components/HighlightedSearchResultText/HighlightedSearchResultText.esm.js';
4
+ export { SearchBar, SearchBarBase } from './components/SearchBar/SearchBar.esm.js';
5
+ export { SearchAutocomplete } from './components/SearchAutocomplete/SearchAutocomplete.esm.js';
6
+ export { SearchAutocompleteDefaultOption } from './components/SearchAutocomplete/SearchAutocompleteDefaultOption.esm.js';
7
+ export { CheckboxFilter, SearchFilter, SelectFilter } from './components/SearchFilter/SearchFilter.esm.js';
8
+ export { AutocompleteFilter } from './components/SearchFilter/SearchFilter.Autocomplete.esm.js';
9
+ export { SearchPagination, SearchPaginationBase } from './components/SearchPagination/SearchPagination.esm.js';
10
+ export { SearchResult, SearchResultApi, SearchResultComponent, SearchResultContext, SearchResultState } from './components/SearchResult/SearchResult.esm.js';
11
+ export { SearchResultList, SearchResultListLayout } from './components/SearchResultList/SearchResultList.esm.js';
12
+ export { SearchResultGroup, SearchResultGroupFilterFieldLayout, SearchResultGroupLayout, SearchResultGroupSelectFilterField, SearchResultGroupTextFilterField } from './components/SearchResultGroup/SearchResultGroup.esm.js';
13
+ export { SearchResultPager } from './components/SearchResultPager/SearchResultPager.esm.js';
14
+ export { DefaultResultListItem } from './components/DefaultResultListItem/DefaultResultListItem.esm.js';
15
+ export { SearchContextProvider, useSearch, useSearchContextCheck } from './context/SearchContext.esm.js';
1154
16
  //# sourceMappingURL=index.esm.js.map