lodash-es#compact TypeScript Examples

The following examples show how to use lodash-es#compact. 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: DateRangePicker.tsx    From UUI with MIT License 4 votes vote down vote up
DateRangePicker = UUIFunctionComponent({
  name: 'DateRangePicker',
  nodes: {
    Root: 'div',
    ConnectIcon: Icons.ArrowRight,
    CalendarIcon: Icons.Calendar,
    Popover: UUIPopover,
    TextField: UUITextField,
    Activator: 'div',
    Container: 'div',
    Toolbar: 'div',
    Main: 'div',
    StartSection: 'div',
    EndSection: 'div',
    YearMonthSelect: UUIYearMonthSelect,
    DateSelect: UUIDateSelect,
    DateTimeShortcut: UUIDateTimeShortcut,
  },
  propTypes: DateRangePickerPropTypes,
}, (props: DateRangePickerFeatureProps, { nodes }) => {
  const {
    Root, ConnectIcon, CalendarIcon, Popover, TextField,
    Activator, Container, Toolbar, Main, StartSection, EndSection,
    YearMonthSelect, DateSelect, DateTimeShortcut,
  } = nodes

  const startInputRef = useRef<HTMLInputElement | null>(null)
  const endInputRef = useRef<HTMLInputElement | null>(null)
  const [whichFocusing, setWhichFocusing] = useState<'start' | 'end'>()
  const [active, setActive] = useState(false)

  const [hoverDate, setHoverDate] = useState<Date>()

  const initialInnerValue = useMemo<DateRangePickerInnerValue>(() => {
    if (props.value === null) {
      return {
        startDate: null,
        endDate: null,
        startInput: '',
        endInput: '',
        startYearMonth: startOfMonth(new Date),
        endYearMonth: add(startOfMonth(new Date), { months: 1 }),
      }
    }
    return {
      startDate: props.value[0],
      endDate: props.value[1],
      startInput: formatDate(props.value[0]),
      endInput: formatDate(props.value[1]),
      startYearMonth: startOfMonth(props.value[0]),
      endYearMonth: isSameMonth(props.value[0], props.value[1]) ? add(startOfMonth(props.value[1]), { months: 1 }) : props.value[1],
    }
  }, [props.value])
  const [innerValue, setInnerValue, resetInnerValue] = usePendingValue<DateRangePickerInnerValue>(initialInnerValue, (value) => {
    if (value.startDate && value.endDate) {
      handleValueOnChange([value.startDate, value.endDate])
      setActive(false)
    }
  }, { resetWhenInitialValueChanged: true })

  const selectedDates = useMemo(() => {
    return compact([innerValue.startDate, innerValue.endDate])
  }, [innerValue.endDate, innerValue.startDate])

  const handleValueOnChange = useCallback((value: [Date, Date] | null) => {
    const sortedValue = value?.sort((i, j) => Number(i) - Number(j)) || null
    props.onChange(sortedValue)
  }, [props])
  /**
   *
   */
  const handleInputOnSubmit = useCallback((type: 'start' | 'end') => {
    if (innerValue.startDate && innerValue.endDate) {
      const originalInput = formatDate(props.value && (type === 'start' ? props.value[0] : props.value[1]))
      const input = type === 'start' ? innerValue.startInput : innerValue.endInput
      if (originalInput === input) return;
      try {
        if (input === '') {
          handleValueOnChange(null)
        } else {
          const result = tryParseDateFromString(input)
          handleValueOnChange(type === 'start' ? [result, innerValue.endDate] : [innerValue.startDate, result])
        }
      } catch {
        resetInnerValue()
      }
    }
  }, [handleValueOnChange, innerValue.endInput, innerValue.endDate, innerValue.startInput, innerValue.startDate, props.value, resetInnerValue])
  /**
   * handle user change year or month in YearMonthSelect.
   */
  const handleStartYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const startYearMonthDate = value
      let endYearMonthDate = oldValue.endYearMonth
      if (!isBefore(startYearMonthDate, endYearMonthDate)) {
        endYearMonthDate = add(startYearMonthDate, { months: 1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  const handleEndYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const endYearMonthDate = value
      let startYearMonthDate = oldValue.startYearMonth
      if (!isAfter(endYearMonthDate, startYearMonthDate)) {
        startYearMonthDate = add(endYearMonthDate, { months: -1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  /**
   * handle user select date in DateSelect.
   */
  const handleDateSelect = useCallback((value: Date) => {
    let shouldSubmit = false
    let newStartValue = innerValue.startDate
    let newEndValue = innerValue.endDate
    if (
      (newStartValue !== null && newEndValue !== null) ||
      (newStartValue === null && newEndValue === null)
    ) {
      if (whichFocusing === 'end') {
        newStartValue = null
        newEndValue = value
      } else {
        newStartValue = value
        newEndValue = null
      }
    } else {
      if (newStartValue === null) newStartValue = value
      if (newEndValue === null) newEndValue = value
      if (isAfter(newStartValue, newEndValue)) {
        const tmp = new Date(newStartValue)
        newStartValue = new Date(newEndValue)
        newEndValue = tmp
      }
      shouldSubmit = true
    }
    setInnerValue((oldValue) => {
      return {
        ...oldValue,
        startDate: newStartValue,
        startInput: formatDate(newStartValue),
        endDate: newEndValue,
        endInput: formatDate(newEndValue),
      }
    }, shouldSubmit)
  }, [innerValue.endDate, innerValue.startDate, setInnerValue, whichFocusing])

  return (
    <Root>
      <Popover
        placement={'bottom-start'}
        active={active}
        onClickAway={() => { setActive(false); resetInnerValue(); }}
        activator={
          <Activator
            onClick={() => {
              setActive(true)
              setTimeout(() => {
                if (whichFocusing === undefined && startInputRef.current) {
                  startInputRef.current.focus()
                }
              }, 0)
            }}
          >
            <TextField
              placeholder={props.startPlaceholder}
              value={innerValue.startInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, startInput: value })) }}
              customize={{
                Input: {
                  ref: startInputRef,
                  onFocus: () => {
                    setWhichFocusing('start')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('start')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('start')
                    }
                  }
                }
              }}
            />
            <ConnectIcon />
            <TextField
              placeholder={props.endPlaceholder}
              value={innerValue.endInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, endInput: value })) }}
              customize={{
                Input: {
                  ref: endInputRef,
                  onFocus: () => {
                    setWhichFocusing('end')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('end')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('end')
                    }
                  }
                }
              }}
            />
            <CalendarIcon />
          </Activator>
        }
      >
        <Container>
          <Toolbar>
            {props.shortcuts && (
              <DateTimeShortcut
                options={props.shortcuts}
                onSelect={(value) => {
                  handleValueOnChange(value)
                  setActive(false)
                }}
              />
            )}
          </Toolbar>
          <Main tabIndex={-1}>
            <StartSection>
              <YearMonthSelect
                value={innerValue.startYearMonth}
                onChange={handleStartYearMonthSelect}
              />
              <DateSelect
                yearMonth={innerValue.startYearMonth}
                selectedDates={selectedDates}
                onSelect={handleDateSelect}
                hoverDate={hoverDate}
                onHoverDateChange={(date) => { setHoverDate(date) }}
              />
            </StartSection>
            <EndSection>
              <YearMonthSelect
                value={innerValue.endYearMonth}
                onChange={handleEndYearMonthSelect}
              />
              <DateSelect
                yearMonth={innerValue.endYearMonth}
                selectedDates={selectedDates}
                onSelect={handleDateSelect}
                hoverDate={hoverDate}
                onHoverDateChange={(date) => { setHoverDate(date) }}
              />
            </EndSection>
          </Main>
        </Container>
      </Popover>
    </Root>
  )
})
Example #2
Source File: DateTimeRangePicker.tsx    From UUI with MIT License 4 votes vote down vote up
DateTimeRangePicker = UUIFunctionComponent({
  name: 'DateTimeRangePicker',
  nodes: {
    Root: 'div',
    ConnectIcon: Icons.ArrowRight,
    CalendarIcon: Icons.Calendar,
    Popover: UUIPopover,
    TextField: UUITextField,
    Activator: 'div',
    Container: 'div',
    Toolbar: 'div',
    Main: 'div',
    StartSection: 'div',
    EndSection: 'div',
    Section: 'div',
    PickerButtons: UUIPickerButtons,
    YearMonthSelect: UUIYearMonthSelect,
    DateSelect: UUIDateSelect,
    TimeSelect: UUITimeSelect,
    DateTimeShortcut: UUIDateTimeShortcut,
  },
  propTypes: DateTimeRangePickerPropTypes,
}, (props: DateTimeRangePickerFeatureProps, { nodes }) => {
  const {
    Root, ConnectIcon, CalendarIcon, Popover, TextField,
    Activator, Container, Main, StartSection, EndSection, Section,
    YearMonthSelect, DateSelect, TimeSelect, DateTimeShortcut, PickerButtons,
  } = nodes

  const startTimeSelectRef = useRef<any | null>(null)
  const endTimeSelectRef = useRef<any | null>(null)
  const startInputRef = useRef<HTMLInputElement | null>(null)
  const endInputRef = useRef<HTMLInputElement | null>(null)
  const [active, setActive] = useState(false)
  const [whichFocusing, setWhichFocusing] = useState<'start' | 'end'>()
  const [hoverDate, setHoverDate] = useState<Date>()

  const initialInnerValue = useMemo(() => {
    if (props.value === null) {
      return {
        startDate: null,
        endDate: null,
        startInput: '',
        endInput: '',
        startYearMonth: startOfMonth(new Date),
        endYearMonth: add(startOfMonth(new Date), { months: 1 }),
      }
    }
    return {
      startDate: props.value[0],
      endDate: props.value[1],
      startInput: formatDateTime(props.value[0]),
      endInput: formatDateTime(props.value[1]),
      startYearMonth: startOfMonth(props.value[0]),
      endYearMonth: isSameMonth(props.value[0], props.value[1]) ? add(startOfMonth(props.value[1]), { months: 1 }) : props.value[1],
    }
  }, [props.value])
  const [innerValue, setInnerValue, resetInnerValue] = usePendingValue<DateTimeRangePickerInnerValue>(initialInnerValue, (value) => {
    if (value.startDate && value.endDate) {
      handleValueOnChange([value.startDate, value.endDate])
      closePopover()
    }
  }, { resetWhenInitialValueChanged: true })

  const selectedDates = useMemo(() => {
    return compact([innerValue.startDate, innerValue.endDate])
  }, [innerValue.endDate, innerValue.startDate])

  const timeSelectScrollToValue = useCallback((type: 'start' | 'end', value: Date, animate?: boolean) => {
    if (type === 'start' && startTimeSelectRef.current) {
      startTimeSelectRef.current.scrollToValue(value, animate)
    }
    if (type === 'end' && endTimeSelectRef.current) {
      endTimeSelectRef.current.scrollToValue(value, animate)
    }
  }, [])
  const openPopover = useCallback(() => { setActive(true) }, [])
  const closePopover = useCallback(() => { setActive(false) }, [])
  const handleValueOnChange = useCallback((value: DateTimeRangePickerValue | null) => {
    const sortedValue = value?.sort((i, j) => Number(i) - Number(j)) || null
    props.onChange(sortedValue)
  }, [props])

  /**
   *
   */
  const handleInputOnSubmit = useCallback((type: 'start' | 'end') => {
    if (innerValue.startDate && innerValue.endDate) {
      const originalInput = formatDateTime(type === 'start' ? innerValue.startDate : innerValue.endDate)
      const input = type === 'start' ? innerValue.startInput : innerValue.endInput
      if (originalInput === input) return;
      try {
        if (input === '') {
          handleValueOnChange(null)
        } else {
          const result = tryParseDateTimeFromString(input)
          handleValueOnChange(type === 'start' ? [result, innerValue.endDate] : [innerValue.startDate, result])
        }
      } catch {
        resetInnerValue()
      }
    }
  }, [handleValueOnChange, innerValue.endInput, innerValue.endDate, innerValue.startInput, innerValue.startDate, resetInnerValue])
    /**
   * handle user change year or month in YearMonthSelect.
   */
  const handleStartYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const startYearMonthDate = value
      let endYearMonthDate = oldValue.endYearMonth
      if (!isBefore(startYearMonthDate, endYearMonthDate)) {
        endYearMonthDate = add(startYearMonthDate, { months: 1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  const handleEndYearMonthSelect = useCallback((value: Date) => {
    setInnerValue((oldValue) => {
      const endYearMonthDate = value
      let startYearMonthDate = oldValue.startYearMonth
      if (!isAfter(endYearMonthDate, startYearMonthDate)) {
        startYearMonthDate = add(endYearMonthDate, { months: -1 })
      }
      return {
        ...oldValue,
        startYearMonth: startYearMonthDate,
        endYearMonth: endYearMonthDate,
      }
    })
  }, [setInnerValue])
  /**
   * handle user select date in DateSelect.
   */
  const handleDateSelect = useCallback((value: Date) => {
    let newStartValue = innerValue.startDate
    let newEndValue = innerValue.endDate
    if (
      (newStartValue !== null && newEndValue !== null) ||
      (newStartValue === null && newEndValue === null)
    ) {
      if (whichFocusing === 'end') {
        newStartValue = null
        newEndValue = value
      } else {
        newStartValue = value
        newEndValue = null
      }
    } else {
      if (newStartValue === null) newStartValue = value
      if (newEndValue === null) newEndValue = value
      if (isAfter(newStartValue, newEndValue)) {
        const tmp = new Date(newStartValue)
        newStartValue = new Date(newEndValue)
        newEndValue = tmp
      }
    }
    setInnerValue((oldValue) => {
      return {
        ...oldValue,
        startDate: newStartValue,
        startInput: formatDateTime(newStartValue),
        endDate: newEndValue,
        endInput: formatDateTime(newEndValue),
      }
    })
  }, [innerValue.endDate, innerValue.startDate, setInnerValue, whichFocusing])
  /**
   * handle user select date in TimeSelect.
   */
  const handleTimeSelect = useCallback((type: 'start' | 'end') => {
    return (value: Date) => {
      setInnerValue((oldValue) => {
        const oldDate = type === 'start' ? oldValue.startDate : oldValue.endDate
        const newDate = set(oldDate || getZeroDate(), {
          hours: value.getHours(),
          minutes: value.getMinutes(),
          seconds: value.getSeconds(),
        })
        const newInput = formatDateTime(newDate)
        return {
          ...oldValue,
          ...(type === 'start' ? {
            startDate: newDate,
            startInput: newInput,
          } : {}),
          ...(type === 'end' ? {
            endDate: newDate,
            endInput: newInput,
          } : {}),
        }
      })
    }
  }, [setInnerValue])

  return (
    <Root>
      <Popover
        placement={'bottom-start'}
        active={active}
        onClickAway={() => {
          resetInnerValue();
          timeSelectScrollToValue('start', props.value ? props.value[0] : getZeroDate(), false)
          timeSelectScrollToValue('end', props.value ? props.value[1] : getZeroDate(), false)
          setTimeout(() => { closePopover() }, 10)
        }}
        activator={
          <Activator
            onClick={() => {
              openPopover()
              setTimeout(() => {
                const focusedElement = ReactHelper.document?.activeElement
                if (startInputRef.current === focusedElement || endInputRef.current === focusedElement) return;
                if (startInputRef.current) {
                  startInputRef.current.focus()
                }
              }, 0)
            }}
          >
            <TextField
              placeholder={props.startPlaceholder}
              value={innerValue.startInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, startInput: value })) }}
              customize={{
                Input: {
                  ref: startInputRef,
                  onFocus: () => {
                    setWhichFocusing('start')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('start')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('start')
                    }
                  }
                }
              }}
            />
            <ConnectIcon />
            <TextField
              placeholder={props.endPlaceholder}
              value={innerValue.endInput}
              onChange={(value) => { setInnerValue((oldValue) => ({ ...oldValue, endInput: value })) }}
              customize={{
                Input: {
                  ref: endInputRef,
                  onFocus: () => {
                    setWhichFocusing('end')
                  },
                  onBlur: () => {
                    setWhichFocusing(undefined)
                    handleInputOnSubmit('end')
                  },
                  onKeyDown: (event) => {
                    if (event.key === 'Enter') {
                      handleInputOnSubmit('end')
                    }
                  }
                }
              }}
            />
            <CalendarIcon />
          </Activator>
        }
      >
        <Container>
          <Main tabIndex={-1}>
            {props.shortcuts && (
              <DateTimeShortcut
                options={props.shortcuts}
                onSelect={(value) => {
                  handleValueOnChange(value)
                  timeSelectScrollToValue('start', value ? value[0] : getZeroDate(), false)
                  timeSelectScrollToValue('end', value ? value[1] : getZeroDate(), false)
                  closePopover()
                }}
              />
            )}
            <StartSection>
              <YearMonthSelect
                value={innerValue.startYearMonth}
                onChange={handleStartYearMonthSelect}
              />
              <Section>
                <DateSelect
                  yearMonth={innerValue.startYearMonth}
                  selectedDates={selectedDates}
                  onSelect={handleDateSelect}
                  hoverDate={hoverDate}
                  onHoverDateChange={(date) => { setHoverDate(date) }}
                />
                <TimeSelect
                  ref={startTimeSelectRef}
                  value={innerValue.startDate || getZeroDate()}
                  onChange={handleTimeSelect('start')}
                />
              </Section>
            </StartSection>
            <EndSection>
              <YearMonthSelect
                value={innerValue.endYearMonth}
                onChange={handleEndYearMonthSelect}
              />
              <Section>
                <DateSelect
                  yearMonth={innerValue.endYearMonth}
                  selectedDates={selectedDates}
                  onSelect={handleDateSelect}
                  hoverDate={hoverDate}
                  onHoverDateChange={(date) => { setHoverDate(date) }}
                />
                <TimeSelect
                  ref={endTimeSelectRef}
                  value={innerValue.endDate || getZeroDate()}
                  onChange={handleTimeSelect('end')}
                />
              </Section>
            </EndSection>
          </Main>
          <PickerButtons
            confirmLabel={props.confirmLabel}
            cancelLabel={props.cancelLabel}
            onCancel={() => {
              resetInnerValue()
              timeSelectScrollToValue('start', props.value ? props.value[0] : getZeroDate(), false)
              timeSelectScrollToValue('end', props.value ? props.value[1] : getZeroDate(), false)
              setTimeout(() => { closePopover() }, 10)
            }}
            onConfirm={() => {
              setInnerValue((value) => value, true)
              if (innerValue.startDate && innerValue.endDate) {
                let data = [innerValue.startDate, innerValue.endDate]
                if (isAfter(innerValue.startDate, innerValue.endDate)) {
                  data = data.reverse()
                }
                timeSelectScrollToValue('start', data[0], false)
                timeSelectScrollToValue('end', data[1], false)
              } else {
                timeSelectScrollToValue('start', getZeroDate(), false)
                timeSelectScrollToValue('end', getZeroDate(), false)
              }
              setTimeout(() => { closePopover() }, 10)
            }}
          />
        </Container>
      </Popover>
    </Root>
  )
})
Example #3
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>
  )
})