lodash-es#flatMap TypeScript Examples

The following examples show how to use lodash-es#flatMap. You can vote up the ones you like or vote down the ones you don't like, and go to the original project or source file by following the links above each example. You may check out the related API usage on the sidebar.
Example #1
Source File: Select.tsx    From UUI with MIT License 4 votes vote down vote up
BaseSelect = UUIFunctionComponent({
  name: 'Select',
  nodes: SelectNodes,
  propTypes: SelectPropTypes,
}, (props: SelectFeatureProps<boolean | undefined>, { nodes, NodeDataProps }) => {
  const {
    Root, Dropdown, DropdownIcon,
    Activator, Result, Placeholder,
    TagInputContainer,
    ActionBox,
    OptionList, Section, Option,
    SearchList, SearchInput, SearchIcon,
    LoadingSpinner,
    Tag,
  } = nodes

  const finalProps = {
    disabled: props.disabled === undefined ? false : props.disabled,
    searchable: props.searchable === undefined ? false : props.searchable,
    placeholder: props.placeholder || 'select options...',
    searchPlaceholder: props.searchPlaceholder || 'Search options...',
    dropdownPlacement: props.dropdownPlacement === undefined ? 'bottom-start' : props.dropdownPlacement
  }

  const [active, setActive] = useState<boolean>(false)
  const [searchInputValue, setSearchInputValue] = useState('')
  const ref = useRef<any>(null)

  const allOptions = useMemo(() => {
    if (isNormalOptions(props)) return props.options
    if (isSectionedOptions(props)) {
      return flatMap(props.sections, (i) => i.options)
    }
    return []
  }, [props])

  const openDropdown = useCallback(() => {
    setActive(true)
  }, [])

  const closeDropdown = useCallback(() => {
    setActive(false)
    setSearchInputValue('')
  }, [])

  /**
   * ListBox
   */

  const displayResult = useMemo(() => {
    return props.value && props.value.length > 0
      ? (
        <Result>
          {isMultipleValue(props) ? (
            <TagInputContainer>
              {
                props.value &&
                props.value.length > 0 &&
                compact(props.value.map((v) => allOptions?.find((i) => i.value === v)))
                .map((option) => {
                  return (
                    <Tag key={option.key}>{option.label}</Tag>
                  )
                })
              }
            </TagInputContainer>
          ) : (
            allOptions?.find((i) => i.value === props.value)?.label
          )}
        </Result>
      )
      : (
        <Placeholder>{finalProps.placeholder}</Placeholder>
      )
  }, [Placeholder, Result, Tag, TagInputContainer, allOptions, finalProps.placeholder, props])

  const optionListItems = useMemo<ListBoxItem[]>(() => {
    const getOptionData = (i: SelectOption) => ({
      id: i.key,
      content: <Option>{i.content || i.label}</Option>,
    })
    const getSectionData = (i: {
      key: string | number;
      label?: React.ReactNode;
      options: SelectOption[];
    }) => {
      return [
        { id: i.key, content: <Section>{i.label}</Section>, disabled: true },
        ...i.options.map(getOptionData),
      ]
    }

    if (isNormalOptions(props)) {
      return props.options.map(getOptionData) as any[]
    } else if (isSectionedOptions(props)) {
      return flatMap(props.sections.map(getSectionData)) as any[]
    } else {
      return [] as any[]
    }
  }, [Option, Section, props])

  const optionListSelectedIds = useMemo(() => {
    if (!props.value) return []
    if (isMultipleValue(props)) {
      return compact(props.value.map((i) => allOptions && allOptions.find((j) => j.value === i)?.key))
    } else {
      return compact([allOptions && allOptions.find((j) => j.value === props.value)?.key])
    }
  }, [props, allOptions])

  const optionListHandleOnSelect = useCallback((selectedIds: string[]) => {
    if (isMultipleValue(props)) {
      if (selectedIds.length === 0) {
        props.onChange([])
      } else {
        props.onChange(compact(selectedIds.map((i) => allOptions && allOptions.find((j) => j.key === i)?.value)))
      }
    }
    if (isSingleValue(props)) {
      if (selectedIds.length === 0) {
        props.onChange(null)
      } else {
        props.onChange((allOptions && allOptions.find((j) => j.key === selectedIds[0])?.value) || null)
      }
      closeDropdown()
    }
  }, [allOptions, closeDropdown, props])

  const searchListItems = useMemo(() => {
    if (!searchInputValue) return null
    const matchedOptions = searchInOptions(searchInputValue, allOptions, props.onSearch)
    return matchedOptions.map((option) => {
      return {
        id: option.key,
        content: (
          <Option>{option.label}</Option>
        ),
      }
    })
  }, [Option, allOptions, props.onSearch, searchInputValue])

  const searchListSelectedIds = useMemo(() => {
    return optionListSelectedIds.filter((id) => {
      return !!searchListItems?.find((item) => item.id === id)
    })
  }, [optionListSelectedIds, searchListItems])
  const searchListHandleOnSelect = useCallback((selectedId: string) => {
    if (!searchInputValue) return
    const option = allOptions.find((i) => i.key === selectedId)
    if (option) {
      if (isMultipleValue(props)) {
        const newValue = Array.from(props.value || [])
        newValue.push(option.value)
        props.onChange(newValue)
      }
      if (isSingleValue(props)) {
        props.onChange(option.value)
      }
    }
  }, [allOptions, props, searchInputValue])
  const searchListHandleOnUnselect = useCallback((selectedId: string) => {
    if (!searchInputValue) return
    const option = allOptions.find((i) => i.key === selectedId)
    if (option) {
      if (isMultipleValue(props) && props.value) {
        const index = props.value.findIndex((i) => i === selectedId)
        const newValue = Array.from(props.value)
        newValue.splice(index, 1)
        props.onChange(newValue)
      } else {
        props.onChange(null)
      }
    }
  }, [allOptions, props, searchInputValue])

  return (
    <Root
      ref={ref}
      role="select"
      tabIndex={finalProps.disabled ? -1 : 0}
      {...NodeDataProps({
        'disabled': !!finalProps.disabled,
        'active': !!active,
        'loading': !!props.loading,
        'searchable': !!finalProps.searchable,
      })}
      onKeyDown={(event) => {
        if (finalProps.disabled) return;
        switch (event.keyCode) {
          case KeyCode.Enter:
          case KeyCode.SpaceBar:
            if (!active) {
              openDropdown()
            }
            break
          case KeyCode.Escape:
            closeDropdown()
            break
          default:
            // do nothing
        }
      }}
      onFocus={props.onFocus}
      onBlur={props.onBlur}
    >
      <Dropdown
        usePortal={props.usePortal}
        portalContainer={props.portalContainer}
        active={active}
        placement={finalProps.dropdownPlacement}
        referenceElement={ref.current}
        onClickAway={() => {
          if (finalProps.disabled) return;
          closeDropdown()
        }}
        // modifiers for fix dropdown dynamic offset update
        modifiers={[{
          name: "dynamic_offset",
          enabled: true,
          phase: "beforeWrite",
          requires: ["computeStyles"],
          fn: () => { /** */ },
          effect: () => { return () => { /** */ } },
        }]}
        activator={
          <Activator
            onClick={() => {
              if (finalProps.disabled) return;
              if (!active) openDropdown()
              else closeDropdown()
            }}
          >
            {displayResult}
            {props.loading && (
              <LoadingSpinner width={16} height={16} />
            )}
            <DropdownIcon width={20} height={20} svgrProps={{ strokeWidth: 1 }} />
          </Activator>
        }
      >
        <ActionBox>
          {props.searchable && (
            <SearchInput
              value={searchInputValue}
              onChange={(value) => { setSearchInputValue(value) }}
              placeholder={finalProps.searchPlaceholder}
              customize={{
                Root: {
                  extendChildrenBefore: (
                    <SearchIcon />
                  )
                }
              }}
            />
          )}
          {(searchListItems) ? (
            <SearchList
              items={searchListItems}
              selectedIds={searchListSelectedIds}
              onSelect={searchListHandleOnSelect}
              onUnselect={searchListHandleOnUnselect}
              multiple={props.multiple}
            />
          ) : (
            <OptionList
              items={optionListItems}
              disabled={!active}
              selectedIds={optionListSelectedIds}
              onSelected={optionListHandleOnSelect}
              multiple={props.multiple}
            />
          )}
        </ActionBox>
      </Dropdown>
    </Root>
  )
})