<commit_before>import {Modifier} from '@popperjs/core'
import React, {forwardRef, useCallback, useEffect, useState} from 'react'
import {usePopper} from 'react-popper'
import styles from 'part:@sanity/components/selects/searchable-style'
import FaAngleDown from 'part:@sanity/base/angle-down-icon'
import {Layer, useLayer} from 'part:@sanity/components/layer'
import Spinner from 'part:@sanity/components/loading/spinner'
import DefaultTextInput from 'part:@sanity/components/textinputs/default'
import CloseIcon from 'part:@sanity/base/close-icon'
import {useClickOutside} from '../hooks'
import SelectMenu from './SelectMenu'

// @todo
type Item = unknown
type Value = any

interface StatelessSearchableSelectResultsProps {
  highlightIndex: number
  isLoading?: boolean
  items?: Item[]
  onClose: (event: Event) => void
  onSelect: (item: Item) => void
  renderItem: (item: Item) => React.ReactNode
  value?: Value
}

interface StatelessSearchableSelectProps {
  onChange?: (item: Item) => void
  value?: Value
  inputValue?: string
  onInputChange?: (val: string) => void
  onClear?: () => void
  renderItem: (item: Item) => React.ReactNode
  placeholder?: string
  isLoading?: boolean
  isOpen?: boolean
  onOpen?: () => void
  onClose?: (event?: Event) => void
  openItemElement?: (value: Value) => React.ReactNode
  items?: Item[]
  highlightIndex?: number
  onHighlightIndexChange?: (index: number) => void
  isInputSelected?: boolean
  disabled?: boolean
  dropdownPosition?: string
  readOnly?: boolean
  inputId?: string
}

const sameWidthModifier: Modifier<'sameWidth', any> = {
  name: 'sameWidth',
  enabled: true,
  phase: 'beforeWrite',
  requires: ['computeStyles'],
  fn({state}) {
    state.styles.popper.width = `${state.rects.reference.width}px`
  },
}

const StatelessSearchableSelectResults = forwardRef(
  (props: StatelessSearchableSelectResultsProps & React.HTMLProps<HTMLDivElement>, ref) => {
    const {
      highlightIndex,
      isLoading,
      items = [],
      onClose,
      onSelect,
      renderItem: renderItemProp,
      value,
      ...restProps
    } = props
    const layer = useLayer()
    const isTopLayer = layer.depth === layer.size
    const itemsLen = items.length

    const renderItem = useCallback(
      (item: Item) => {
        return <div className={styles.item}>{renderItemProp(item)}</div>
      },
      [renderItemProp]
    )

    const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)

    const setRef = useCallback(
      (el: HTMLDivElement | null) => {
        setRootElement(el)
        if (typeof ref === 'function') ref(el)
        else if (ref) ref.current = el
      },
      [ref]
    )

    useClickOutside(
      useCallback(
        (event: Event) => {
          if (!isTopLayer) return
          onClose(event)
        },
        [isTopLayer, onClose]
      ),
      [rootElement]
    )

    useEffect(() => {
      if (!isTopLayer) return undefined

      const handleGlobalKeyDown = (event: KeyboardEvent) => {
        if (event.key === 'Escape') {
          event.stopPropagation()
          onClose(event as Event)
        }
      }

      window.addEventListener('keydown', handleGlobalKeyDown)

      return () => {
        window.removeEventListener('keydown', handleGlobalKeyDown)
      }
    }, [isTopLayer, onClose])

    return (
      <div {...restProps} ref={setRef} className={styles.popper}>
        <div className={itemsLen === 0 ? styles.listContainerNoResult : styles.listContainer}>
          <div
            className={
              itemsLen === 0 && !isLoading ? styles.noResultText : styles.noResultTextHidden
            }
          >
            No results
          </div>
          {itemsLen > 0 && (
            <SelectMenu
              items={items}
              value={value}
              onSelect={onSelect}
              renderItem={renderItem}
              highlightIndex={highlightIndex}
            />
          )}
        </div>
      </div>
    )
  }
)

StatelessSearchableSelectResults.displayName = 'StatelessSearchableSelectResults'

const StatelessSearchableSelect = forwardRef(
  // eslint-disable-next-line complexity
  (props: StatelessSearchableSelectProps & React.HTMLProps<HTMLInputElement>, ref) => {
    const {
      onClear,
      placeholder,
      isLoading,
      value,
      items = [],
      isOpen,
      highlightIndex = -1,
      isInputSelected,
      inputValue,
      onChange,
      onInputChange,
      onOpen,
      onClose,
      dropdownPosition = 'bottom-start',
      disabled,
      onHighlightIndexChange,
      openItemElement,
      readOnly,
      renderItem: renderItemProp,
      ...rest
    } = props

    const itemsLen = items.length

    const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null)
    const [popperElement, setPopperElement] = useState<HTMLElement | null>(null)
    const popper = usePopper(referenceElement, popperElement, {
      placement: dropdownPosition as any,
      modifiers: [
        {
          name: 'preventOverflow',
          options: {
            altAxis: true,
            padding: 8,
            tether: false,
          },
        },
        sameWidthModifier,
      ],
    })

    const handleSelect = useCallback(
      (item: Item) => {
        if (onChange) onChange(item)
      },
      [onChange]
    )

    const handleClose = useCallback(
      (event?: Event) => {
        if (onClose) onClose(event)
      },
      [onClose]
    )

    const handleArrowClick = useCallback(() => {
      if (isOpen) {
        handleClose()
      } else if (onOpen) onOpen()
    }, [handleClose, isOpen, onOpen])

    const handleArrowKeyPress = useCallback(<commit_msg>[base] Fix issues with popper configuration in SearchableSelect which affected ReferenceInput<commit_after>import {Modifier} from '@popperjs/core'
import React, {forwardRef, useCallback, useEffect, useState} from 'react'
import {usePopper} from 'react-popper'
import styles from 'part:@sanity/components/selects/searchable-style'
import FaAngleDown from 'part:@sanity/base/angle-down-icon'
import {Layer, useLayer} from 'part:@sanity/components/layer'
import Spinner from 'part:@sanity/components/loading/spinner'
import DefaultTextInput from 'part:@sanity/components/textinputs/default'
import CloseIcon from 'part:@sanity/base/close-icon'
import {useBoundaryElement} from '../boundaryElement'
import {useClickOutside} from '../hooks'
import SelectMenu from './SelectMenu'

// @todo
type Item = unknown
type Value = any

interface StatelessSearchableSelectResultsProps {
  highlightIndex: number
  isLoading?: boolean
  items?: Item[]
  onClose: (event: Event) => void
  onSelect: (item: Item) => void
  renderItem: (item: Item) => React.ReactNode
  value?: Value
}

interface StatelessSearchableSelectProps {
  onChange?: (item: Item) => void
  value?: Value
  inputValue?: string
  onInputChange?: (val: string) => void
  onClear?: () => void
  renderItem: (item: Item) => React.ReactNode
  placeholder?: string
  isLoading?: boolean
  isOpen?: boolean
  onOpen?: () => void
  onClose?: (event?: Event) => void
  openItemElement?: (value: Value) => React.ReactNode
  items?: Item[]
  highlightIndex?: number
  onHighlightIndexChange?: (index: number) => void
  isInputSelected?: boolean
  disabled?: boolean
  dropdownPosition?: string
  readOnly?: boolean
  inputId?: string
}

const sameWidthModifier: Modifier<'sameWidth', any> = {
  name: 'sameWidth',
  enabled: true,
  phase: 'beforeWrite',
  requires: ['computeStyles'],
  fn({state}) {
    state.styles.popper.width = `${state.rects.reference.width}px`
  },
}

const StatelessSearchableSelectResults = forwardRef(
  (props: StatelessSearchableSelectResultsProps & React.HTMLProps<HTMLDivElement>, ref) => {
    const {
      highlightIndex,
      isLoading,
      items = [],
      onClose,
      onSelect,
      renderItem: renderItemProp,
      value,
      ...restProps
    } = props
    const layer = useLayer()
    const isTopLayer = layer.depth === layer.size
    const itemsLen = items.length

    const renderItem = useCallback(
      (item: Item) => {
        return <div className={styles.item}>{renderItemProp(item)}</div>
      },
      [renderItemProp]
    )

    const [rootElement, setRootElement] = useState<HTMLDivElement | null>(null)

    const setRef = useCallback(
      (el: HTMLDivElement | null) => {
        setRootElement(el)
        if (typeof ref === 'function') ref(el)
        else if (ref) ref.current = el
      },
      [ref]
    )

    useClickOutside(
      useCallback(
        (event: Event) => {
          if (!isTopLayer) return
          onClose(event)
        },
        [isTopLayer, onClose]
      ),
      [rootElement]
    )

    useEffect(() => {
      if (!isTopLayer) return undefined

      const handleGlobalKeyDown = (event: KeyboardEvent) => {
        if (event.key === 'Escape') {
          event.stopPropagation()
          onClose(event as Event)
        }
      }

      window.addEventListener('keydown', handleGlobalKeyDown)

      return () => {
        window.removeEventListener('keydown', handleGlobalKeyDown)
      }
    }, [isTopLayer, onClose])

    return (
      <div {...restProps} ref={setRef} className={styles.popper}>
        <div className={itemsLen === 0 ? styles.listContainerNoResult : styles.listContainer}>
          <div
            className={
              itemsLen === 0 && !isLoading ? styles.noResultText : styles.noResultTextHidden
            }
          >
            No results
          </div>
          {itemsLen > 0 && (
            <SelectMenu
              items={items}
              value={value}
              onSelect={onSelect}
              renderItem={renderItem}
              highlightIndex={highlightIndex}
            />
          )}
        </div>
      </div>
    )
  }
)

StatelessSearchableSelectResults.displayName = 'StatelessSearchableSelectResults'

const StatelessSearchableSelect = forwardRef(
  // eslint-disable-next-line complexity
  (props: StatelessSearchableSelectProps & React.HTMLProps<HTMLInputElement>, ref) => {
    const {
      onClear,
      placeholder,
      isLoading,
      value,
      items = [],
      isOpen,
      highlightIndex = -1,
      isInputSelected,
      inputValue,
      onChange,
      onInputChange,
      onOpen,
      onClose,
      dropdownPosition = 'bottom-start',
      disabled,
      onHighlightIndexChange,
      openItemElement,
      readOnly,
      renderItem: renderItemProp,
      ...rest
    } = props

    const itemsLen = items.length
    const boundaryElement = useBoundaryElement()
    const [referenceElement, setReferenceElement] = useState<HTMLElement | null>(null)
    const [popperElement, setPopperElement] = useState<HTMLElement | null>(null)

    const popper = usePopper(referenceElement, popperElement, {
      placement: dropdownPosition as any,
      modifiers: [
        {
          name: 'preventOverflow',
          options: {
            rootBoundary: boundaryElement ? undefined : 'viewport',
            boundary: boundaryElement || 'clippingParents',
            padding: 8,
          },
        },
        {
          name: 'flip',
          options: {
            rootBoundary: boundaryElement ? undefined : 'viewport',
            boundary: boundaryElement || 'clippingParents',
            fallbackPlacements: ['top-start', 'bottom-start'],
          },
        },
        sameWidthModifier,
      ],
    })

    const {forceUpdate} = popper

    useEffect(() => {
      if (forceUpdate) setTimeout(forceUpdate, 0)
    }, [itemsLen, forceUpdate])

    const handleSelect = useCallback(
      (item: Item) => {
        if (onChange) onChange(item)
      },
      [onChange]
    )

    const handleClose = useCallback(
      (event?: Event) => {
        if (onClose) onClose(event)
      },
      [onClose]
    )

    const handleArrowClick = useCallback(() => {
      if (isOpen) {
        handleClose()
      } else if (onOpen) onOpen()
    }, [handleClose, isOpen, onOpen])

    const handleArrowKeyPress = useCallback(
{'fraction_non_alphanumeric': 0.11773472429210134, 'fraction_numerical': 0.0009107468123861566, 'mean_word_length': 2.6208033573141485, 'pattern_counts': {'":': 0, '<': 33, '<?xml version=': 0, '>': 80, 'https://': 0, 'lorem ipsum': 0, 'www.': 0, 'xml': 0}, 'pii_count': 0, 'substrings_counts': 0, 'word_list_counts': {'cursed_substrings.json': 0, 'profanity_word_list.json': 0, 'sexual_word_list.json': 0, 'zh_pornsignals.json': 0}}
{'dir': 'git-commits-cleaned', 'id': '5221957', 'n_tokens_mistral': 3488, 'n_tokens_neox': 3468, 'n_words': 788}