index.tsx 1.9 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374
  1. import * as React from 'react';
  2. import flatten from 'lodash/flatten';
  3. import {Result} from './types';
  4. type ChildProps = {
  5. hasAnyResults: boolean;
  6. isLoading: boolean;
  7. results: Result[];
  8. };
  9. type Props = {
  10. children: (props: ChildProps) => React.ReactElement;
  11. params: {orgId: string};
  12. query: string;
  13. sources: React.ComponentType[];
  14. searchOptions?: Fuse.FuseOptions<any>;
  15. };
  16. type SourceResult = {
  17. isLoading: boolean;
  18. results: Result[];
  19. };
  20. class SearchSources extends React.Component<Props> {
  21. // `allSources` will be an array of all result objects from each source
  22. renderResults(allSources: SourceResult[]) {
  23. const {children} = this.props;
  24. // loading means if any result has `isLoading` OR any result is null
  25. const isLoading = !!allSources.find(arg => arg.isLoading || arg.results === null);
  26. const foundResults = isLoading
  27. ? []
  28. : flatten(allSources.map(({results}) => results || [])).sort(
  29. (a, b) => a.score - b.score
  30. );
  31. const hasAnyResults = !!foundResults.length;
  32. return children({
  33. isLoading,
  34. results: foundResults,
  35. hasAnyResults,
  36. });
  37. }
  38. renderSources(sources: Props['sources'], results: SourceResult[], idx: number) {
  39. if (idx >= sources.length) {
  40. return this.renderResults(results);
  41. }
  42. const Source = sources[idx];
  43. return (
  44. <Source {...this.props}>
  45. {(args: SourceResult) => {
  46. // Mutate the array instead of pushing because we don't know how often
  47. // this child function will be called and pushing will cause duplicate
  48. // results to be pushed for all calls down the chain.
  49. results[idx] = args;
  50. return this.renderSources(sources, results, idx + 1);
  51. }}
  52. </Source>
  53. );
  54. }
  55. render() {
  56. return this.renderSources(
  57. this.props.sources,
  58. new Array(this.props.sources.length),
  59. 0
  60. );
  61. }
  62. }
  63. export default SearchSources;