All Downloads are FREE. Search and download functionalities are using the official Maven repository.

web.lib.components.extensions-picker.extensions-picker.tsx Maven / Gradle / Ivy

There is a newer version: 37
Show newest version
import * as React from 'react';
import { useHotkeys } from 'react-hotkeys-hook';
import { useAnalytics } from '../../core/analytics';
import { InputProps } from '../../core/types';
import {
  debouncedComputeResults,
  FilterResult, ProcessedExtensions,
  processExtensionsValues
} from './extensions-utils';
import { QuarkusProject } from '../api/model';
import './extensions-picker.scss';
import { ExtensionRow } from './extension-row';
import { ExtensionSearchBar } from './extension-search-bar';
import { Button } from 'react-bootstrap';
import { FaCaretDown } from 'react-icons/fa';
import { SearchResultsInfo } from './search-results-info';

export interface ExtensionEntry {
  id: string;
  name: string;
  version: string;
  keywords: string[];
  tags: string[];
  description?: string;
  shortName?: string;
  category: string;
  order: number;
  default: boolean;
  guide?: string;
  bom?: string
  platform: boolean;
}

export interface TagEntry {
  name: string;
  href?: string;
  description?: string;
  color?: string;
  hide?: boolean;
}

export interface ExtensionsPickerValue {
  extensions: ExtensionEntry[];
}

const REDUCED_SIZE = 100;

interface ExtensionsPickerProps extends InputProps {
  entries: ExtensionEntry[];
  tagsDef: TagEntry[];
  placeholder: string;
  buildTool: string;
  project?: QuarkusProject;

  filter: string;
  setFilter: React.Dispatch>;

  filterFunction?(d: ExtensionEntry): boolean;
}

const hotkeysOptions = {
  ignoreEventWhen: (e) => {
    const el = (e.target) as any | undefined;
    if (!el) {
      return true;
    }
    const tagName = el && el.tagName;
    return el.id === 'extensions-search-input' || !(tagName === 'INPUT' || tagName === 'SELECT' || tagName === 'TEXTAREA');
  },
  enableOnFormTags: [ 'INPUT' ] as 'INPUT'[]
};

export const ExtensionsPicker = (props: ExtensionsPickerProps) => {
  const { filter } = props;
  const [ processedExtensions, setProcessedExtensions ] = React.useState(undefined);
  const [ keyboardActivated, setKeyBoardActivated ] = React.useState(-1);
  const [ showAll, setShowAll ] = React.useState(false);
  const [ result, setResult ] = React.useState();
  const analytics = useAnalytics();
  const context = { element: 'extension-picker' };

  function setFilter(filter: string) {
    setKeyBoardActivated(-1);
    setShowAll(false);
    props.setFilter(filter);
  }
  const extensions = props.value.extensions || [];

  const entrySet = new Set(extensions.map(e => e.id));
  const entriesById: Map = new Map(props.entries.map(item => [ item.id, item ]));

  React.useEffect(() => {
    setProcessedExtensions(processExtensionsValues(props.entries));
  }, [ props.entries, setProcessedExtensions ]);

  React.useEffect(() => {
    debouncedComputeResults(analytics, filter, props.entries, processedExtensions, setResult);
  }, [ filter, processedExtensions, props.entries, setShowAll, setResult ]);

  const allEntries = result?.effective || [];
  const entries = showAll ? allEntries : allEntries.slice(0, REDUCED_SIZE)
  const add = (index: number, type: string) => {
    const id =entries[index].id;
    entrySet.add(id);
    props.onChange({ extensions: Array.from(entrySet).map(e => entriesById.get(e)!) });
    analytics.event('Select extension', { extension: id, type, ...context });
    if (keyboardActivated >= 0) {
      setKeyBoardActivated(index);
    }
  };

  const remove = (id: string, type: string) => {
    entrySet.delete(id);
    props.onChange({ extensions: Array.from(entrySet).map(e => entriesById.get(e)!) });
    analytics.event('Unselect extension', { extension: id, type, ...context });
  };

  const flip = (index: number, origin: string) => {
    if (!entries[index] || entries[index].default) {
      return;
    }
    if (entrySet.has(entries[index].id)) {
      remove(entries[index].id, origin);
    } else {
      add(index, origin);
    }
  };


  useHotkeys('esc', () => setKeyBoardActivated(-1), hotkeysOptions);
  useHotkeys('up', () => setKeyBoardActivated((prev) => Math.max(0, prev - 1)), hotkeysOptions);
  useHotkeys('down', () => setKeyBoardActivated((prev) => Math.min(entries.length - 1, prev + 1)), hotkeysOptions, [ entries ]);
  useHotkeys('space', (event) => {
    if (keyboardActivated >= 0) {
      event.preventDefault();
      flip(keyboardActivated, 'Keyboard');
    }
  }, hotkeysOptions, [ entries, keyboardActivated ]);

  let currentCat: string | undefined;

  return (
    
{entries.map((ex, i) => { const ext = ( flip(i, 'List')} buildTool={props.buildTool} pickerLayout={true} /> ); if (!result.filtered && (!currentCat || currentCat !== ex.category)) { currentCat = ex.category; return (
{currentCat}
{ext}
); } return ext; })}
{!showAll && allEntries.length > 100 && }
); };




© 2015 - 2025 Weber Informatics LLC | Privacy Policy