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