analytica-frontend-lib
Version:
Repositório público dos componentes utilizados nas plataformas da Analytica Ensino
1 lines • 31.4 kB
Source Map (JSON)
{"version":3,"sources":["../src/components/SearchSelect/SearchSelect.tsx"],"sourcesContent":["import {\n useEffect,\n useRef,\n useState,\n useCallback,\n forwardRef,\n useId,\n useMemo,\n KeyboardEvent,\n CSSProperties,\n} from 'react';\nimport { createPortal } from 'react-dom';\nimport { CaretDownIcon } from '@phosphor-icons/react/dist/csr/CaretDown';\nimport { CheckIcon } from '@phosphor-icons/react/dist/csr/Check';\nimport { MagnifyingGlassIcon } from '@phosphor-icons/react/dist/csr/MagnifyingGlass';\nimport { SpinnerGapIcon } from '@phosphor-icons/react/dist/csr/SpinnerGap';\nimport { cn } from '../../utils/utils';\nimport Input from '../Input/Input';\nimport Text from '../Text/Text';\nimport Button from '../Button/Button';\n\n// ============================================================================\n// Types\n// ============================================================================\n\nexport interface SearchSelectOption {\n value: string;\n label: string;\n disabled?: boolean;\n}\n\nexport interface SearchSelectPagination {\n page: number;\n limit?: number;\n totalPages: number;\n hasNext: boolean;\n hasPrev?: boolean;\n total: number;\n}\n\nexport interface SearchSelectProps {\n /** Current selected value */\n value?: string;\n /** Callback when value changes */\n onValueChange?: (value: string) => void;\n /** Options to display */\n options: SearchSelectOption[];\n /** Placeholder text when no value selected */\n placeholder?: string;\n /** Search input placeholder */\n searchPlaceholder?: string;\n /** Label for the select */\n label?: string;\n /** Helper text below the select */\n helperText?: string;\n /** Error message to display */\n errorMessage?: string;\n /** Whether the select is disabled */\n disabled?: boolean;\n /** Whether the select is in loading state */\n loading?: boolean;\n /** Size variant */\n size?: 'small' | 'medium' | 'large';\n /** Visual variant */\n variant?: 'outlined' | 'underlined' | 'rounded';\n /** Additional class name */\n className?: string;\n /** Text to show when no results found */\n emptyText?: string;\n /** Text to show when loading */\n loadingText?: string;\n /** Callback when search query changes (for async search) */\n onSearch?: (query: string) => void;\n /** Debounce delay for search in ms */\n searchDebounce?: number;\n /** Pagination info for infinite scroll */\n pagination?: SearchSelectPagination;\n /** Callback to load more items (can return Promise for auto-reset of loading state) */\n onLoadMore?: () => void | Promise<void>;\n /** Whether loading more items */\n loadingMore?: boolean;\n /** Custom ID */\n id?: string;\n /** Whether to filter options locally (default: true) */\n filterLocally?: boolean;\n}\n\n// ============================================================================\n// Constants\n// ============================================================================\n\nconst SIZE_CLASSES = {\n small: 'text-sm h-8',\n medium: 'text-md h-9',\n large: 'text-lg h-10',\n} as const;\n\nconst PADDING_CLASSES = {\n small: 'px-3 py-1',\n medium: 'px-3 py-2',\n large: 'px-4 py-3',\n} as const;\n\nconst VARIANT_CLASSES = {\n outlined: 'border-2 rounded-lg focus:border-primary-950',\n underlined: 'border-b-2 focus:border-primary-950',\n rounded: 'border-2 rounded-full focus:border-primary-950',\n} as const;\n\n// ============================================================================\n// Hook: useDebounce\n// ============================================================================\n\nfunction useDebounce<T>(value: T, delay: number): T {\n const [debouncedValue, setDebouncedValue] = useState<T>(value);\n\n useEffect(() => {\n const handler = setTimeout(() => {\n setDebouncedValue(value);\n }, delay);\n\n return () => {\n clearTimeout(handler);\n };\n }, [value, delay]);\n\n return debouncedValue;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const SearchSelect = forwardRef<HTMLButtonElement, SearchSelectProps>(\n (\n {\n value,\n onValueChange,\n options,\n placeholder = 'Selecione...',\n searchPlaceholder = 'Buscar...',\n label,\n helperText,\n errorMessage,\n disabled = false,\n loading = false,\n size = 'small',\n variant = 'outlined',\n className,\n emptyText = 'Nenhum resultado encontrado',\n loadingText = 'Carregando...',\n onSearch,\n searchDebounce = 300,\n pagination,\n onLoadMore,\n loadingMore = false,\n id,\n filterLocally = true,\n },\n ref\n ) => {\n const [open, setOpen] = useState(false);\n const [searchQuery, setSearchQuery] = useState('');\n const [highlightedIndex, setHighlightedIndex] = useState(-1);\n\n const triggerRef = useRef<HTMLButtonElement>(null);\n const contentRef = useRef<HTMLDivElement>(null);\n const searchInputRef = useRef<HTMLInputElement>(null);\n const listRef = useRef<HTMLDivElement>(null);\n const isFetchingMoreRef = useRef(false);\n\n const generatedId = useId();\n const selectId = id ?? `search-select-${generatedId}`;\n const listboxId = `${selectId}-listbox`;\n const helperId = `${selectId}-helper`;\n const errorId = `${selectId}-error`;\n\n // Debounced search for async\n const debouncedSearch = useDebounce(searchQuery, searchDebounce);\n\n // Call onSearch when debounced search changes\n useEffect(() => {\n if (onSearch && debouncedSearch !== undefined) {\n onSearch(debouncedSearch);\n }\n }, [debouncedSearch, onSearch]);\n\n // Filter options locally if enabled\n const filteredOptions = useMemo(() => {\n if (!filterLocally || !searchQuery) {\n return options;\n }\n const query = searchQuery.toLowerCase();\n return options.filter((opt) => opt.label.toLowerCase().includes(query));\n }, [options, searchQuery, filterLocally]);\n\n // Clamp highlightedIndex when filteredOptions shrinks\n useEffect(() => {\n if (highlightedIndex >= filteredOptions.length) {\n setHighlightedIndex(Math.max(filteredOptions.length - 1, -1));\n }\n }, [filteredOptions.length, highlightedIndex]);\n\n // Get selected option label\n const selectedLabel = useMemo(() => {\n const selected = options.find((opt) => opt.value === value);\n return selected?.label;\n }, [options, value]);\n\n // Get trigger position for dropdown\n const [triggerRect, setTriggerRect] = useState<DOMRect | null>(null);\n\n const updateTriggerRect = useCallback(() => {\n if (triggerRef.current) {\n setTriggerRect(triggerRef.current.getBoundingClientRect());\n }\n }, []);\n\n // Handle open/close\n const handleToggle = useCallback(() => {\n if (disabled || loading) return;\n const newOpen = !open;\n if (newOpen) {\n updateTriggerRect();\n setSearchQuery('');\n setHighlightedIndex(-1);\n }\n setOpen(newOpen);\n }, [disabled, loading, open, updateTriggerRect]);\n\n // Handle option selection\n const handleSelect = useCallback(\n (optionValue: string) => {\n onValueChange?.(optionValue);\n setOpen(false);\n setSearchQuery('');\n // Return focus to trigger for keyboard/screen-reader users\n requestAnimationFrame(() => {\n triggerRef.current?.focus();\n });\n },\n [onValueChange]\n );\n\n // Focus search input when dropdown opens\n useEffect(() => {\n if (open && searchInputRef.current) {\n setTimeout(() => {\n searchInputRef.current?.focus();\n }, 0);\n }\n }, [open]);\n\n // Handle click outside\n useEffect(() => {\n if (!open) return;\n\n const handleClickOutside = (event: MouseEvent) => {\n const target = event.target as Node;\n const isInsideTrigger = triggerRef.current?.contains(target);\n const isInsideContent = contentRef.current?.contains(target);\n\n if (!isInsideTrigger && !isInsideContent) {\n setOpen(false);\n setSearchQuery('');\n }\n };\n\n document.addEventListener('mousedown', handleClickOutside);\n return () => {\n document.removeEventListener('mousedown', handleClickOutside);\n };\n }, [open]);\n\n // Update position on scroll/resize\n useEffect(() => {\n if (!open) return;\n\n const handleUpdate = () => updateTriggerRect();\n window.addEventListener('scroll', handleUpdate, true);\n window.addEventListener('resize', handleUpdate);\n\n return () => {\n window.removeEventListener('scroll', handleUpdate, true);\n window.removeEventListener('resize', handleUpdate);\n };\n }, [open, updateTriggerRect]);\n\n // Find next enabled index in a given direction\n const findNextEnabledIndex = useCallback(\n (currentIndex: number, direction: 'next' | 'prev'): number => {\n const len = filteredOptions.length;\n if (len === 0) return -1;\n\n // Handle initial state (-1)\n let startIndex: number;\n if (currentIndex < 0) {\n startIndex = direction === 'next' ? -1 : len;\n } else {\n startIndex = currentIndex;\n }\n\n for (let i = 0; i < len; i++) {\n if (direction === 'next') {\n startIndex = (startIndex + 1) % len;\n } else {\n startIndex = (startIndex - 1 + len) % len;\n }\n if (!filteredOptions[startIndex].disabled) {\n return startIndex;\n }\n }\n return -1; // All options are disabled\n },\n [filteredOptions]\n );\n\n // Handle keyboard navigation\n const handleKeyDown = useCallback(\n (e: KeyboardEvent<HTMLInputElement>) => {\n switch (e.key) {\n case 'ArrowDown':\n e.preventDefault();\n setHighlightedIndex((prev) => findNextEnabledIndex(prev, 'next'));\n break;\n case 'ArrowUp':\n e.preventDefault();\n setHighlightedIndex((prev) => findNextEnabledIndex(prev, 'prev'));\n break;\n case 'Enter':\n e.preventDefault();\n if (\n highlightedIndex >= 0 &&\n highlightedIndex < filteredOptions.length &&\n !filteredOptions[highlightedIndex].disabled\n ) {\n handleSelect(filteredOptions[highlightedIndex].value);\n }\n break;\n case 'Escape':\n e.preventDefault();\n setOpen(false);\n setSearchQuery('');\n triggerRef.current?.focus();\n break;\n }\n },\n [filteredOptions, highlightedIndex, handleSelect, findNextEnabledIndex]\n );\n\n // Reset fetching ref when loadingMore changes to false\n useEffect(() => {\n if (!loadingMore) {\n isFetchingMoreRef.current = false;\n }\n }, [loadingMore]);\n\n // Infinite scroll detection\n const handleScroll = useCallback(() => {\n if (\n !listRef.current ||\n !pagination?.hasNext ||\n loadingMore ||\n isFetchingMoreRef.current ||\n !onLoadMore\n )\n return;\n\n const { scrollTop, scrollHeight, clientHeight } = listRef.current;\n const scrollThreshold = 50;\n\n if (scrollHeight - scrollTop - clientHeight < scrollThreshold) {\n isFetchingMoreRef.current = true;\n const result = onLoadMore();\n // Reset ref when Promise completes; for void returns, rely on loadingMore useEffect\n if (result && typeof result.then === 'function') {\n result.finally(() => {\n isFetchingMoreRef.current = false;\n });\n }\n }\n }, [pagination?.hasNext, loadingMore, onLoadMore]);\n\n // Scroll highlighted item into view\n useEffect(() => {\n if (highlightedIndex >= 0 && listRef.current) {\n const items = listRef.current.querySelectorAll('[data-option]');\n const item = items[highlightedIndex] as HTMLElement;\n if (item) {\n item.scrollIntoView({ block: 'nearest' });\n }\n }\n }, [highlightedIndex]);\n\n // Set ref\n const setRefs = useCallback(\n (element: HTMLButtonElement | null) => {\n triggerRef.current = element;\n if (typeof ref === 'function') {\n ref(element);\n } else if (ref) {\n ref.current = element;\n }\n },\n [ref]\n );\n\n const sizeClasses = SIZE_CLASSES[size];\n const paddingClasses = PADDING_CLASSES[size];\n const variantClasses = VARIANT_CLASSES[variant];\n const invalid = !!errorMessage;\n\n // Calculate dropdown position with smart overflow handling\n const getDropdownStyles = useCallback((): CSSProperties => {\n if (!triggerRect) return {};\n\n const gap = 4;\n const dropdownMaxHeight = 300; // max-h-[300px] equivalent\n const viewportHeight = window.innerHeight;\n const spaceBelow = viewportHeight - triggerRect.bottom - gap;\n const spaceAbove = triggerRect.top - gap;\n\n // Determine if dropdown should open above or below\n const openAbove =\n spaceBelow < dropdownMaxHeight && spaceAbove > spaceBelow;\n\n const styles: CSSProperties = {\n position: 'fixed',\n left: triggerRect.left,\n width: triggerRect.width,\n zIndex: 9999,\n maxHeight: openAbove\n ? Math.min(spaceAbove, dropdownMaxHeight)\n : Math.min(spaceBelow, dropdownMaxHeight),\n };\n\n if (openAbove) {\n styles.bottom = viewportHeight - triggerRect.top + gap;\n } else {\n styles.top = triggerRect.bottom + gap;\n }\n\n return styles;\n }, [triggerRect]);\n\n // Render options content based on state\n const renderOptionsContent = () => {\n if (loading) {\n return (\n <div className=\"flex items-center justify-center gap-2 p-4 text-text-500\">\n <SpinnerGapIcon size={18} className=\"animate-spin\" />\n <Text size=\"sm\" className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n );\n }\n\n if (filteredOptions.length === 0) {\n return (\n <Text size=\"sm\" className=\"p-4 text-center text-text-500\">\n {emptyText}\n </Text>\n );\n }\n\n return (\n <>\n {filteredOptions.map((option, index) => {\n const isSelected = option.value === value;\n const isHighlighted = index === highlightedIndex;\n const optionId = `${listboxId}-option-${index}`;\n\n return (\n <div\n key={option.value}\n id={optionId}\n data-option\n role=\"option\"\n aria-selected={isSelected}\n aria-disabled={option.disabled}\n tabIndex={-1}\n onClick={() => {\n if (!option.disabled) {\n handleSelect(option.value);\n }\n }}\n onKeyDown={(e) => {\n if (option.disabled) return;\n if (e.key === 'Enter' || e.key === ' ') {\n e.preventDefault();\n handleSelect(option.value);\n }\n }}\n className={cn(\n 'relative flex items-center gap-2 px-3 py-2.5 cursor-pointer transition-colors text-sm',\n isSelected && 'bg-primary-50',\n isHighlighted && !isSelected && 'bg-background-50',\n option.disabled &&\n 'opacity-50 cursor-not-allowed pointer-events-none',\n !option.disabled &&\n !isSelected &&\n !isHighlighted &&\n 'hover:bg-background-50'\n )}\n >\n <Text size=\"sm\" className=\"flex-1 text-text-700\">\n {option.label}\n </Text>\n {isSelected && (\n <CheckIcon\n size={16}\n className=\"text-primary-700\"\n weight=\"bold\"\n />\n )}\n </div>\n );\n })}\n\n {/* Loading more indicator */}\n {loadingMore && (\n <div className=\"flex items-center justify-center gap-2 p-3 text-text-500 border-t border-border-100\">\n <SpinnerGapIcon size={16} className=\"animate-spin\" />\n <Text size=\"xs\" className=\"text-text-500\">\n Carregando mais...\n </Text>\n </div>\n )}\n\n {/* Pagination info - only show when not filtering locally */}\n {pagination &&\n !loadingMore &&\n pagination.total > 0 &&\n !(filterLocally && searchQuery) && (\n <Text\n size=\"xs\"\n className=\"px-3 py-2 text-text-400 border-t border-border-100 text-center\"\n >\n {options.length} de {pagination.total} itens\n </Text>\n )}\n </>\n );\n };\n\n // Dropdown content\n const dropdownContent = open && triggerRect && (\n <div\n ref={contentRef}\n data-search-select-id={selectId}\n style={getDropdownStyles()}\n className=\"bg-secondary rounded-md border border-border-100 shadow-lg overflow-hidden flex flex-col\"\n >\n {/* Search Input */}\n <div className=\"p-2 border-b border-border-100\">\n <Input\n ref={searchInputRef}\n type=\"text\"\n role=\"combobox\"\n aria-autocomplete=\"list\"\n aria-controls={listboxId}\n aria-activedescendant={\n highlightedIndex >= 0\n ? `${listboxId}-option-${highlightedIndex}`\n : undefined\n }\n value={searchQuery}\n onChange={(e) => setSearchQuery(e.target.value)}\n onKeyDown={handleKeyDown}\n placeholder={searchPlaceholder}\n size=\"small\"\n variant=\"outlined\"\n iconRight={<MagnifyingGlassIcon size={16} />}\n containerClassName=\"w-full\"\n />\n </div>\n\n {/* Options List */}\n <div\n ref={listRef}\n id={listboxId}\n role=\"listbox\"\n aria-label={label || 'Options'}\n onScroll={handleScroll}\n className=\"flex-1 overflow-y-auto\"\n >\n {renderOptionsContent()}\n </div>\n </div>\n );\n\n // Determine which ID to use for aria-describedby\n const getDescribedById = () => {\n if (errorMessage) return errorId;\n if (helperText) return helperId;\n return undefined;\n };\n const describedById = getDescribedById();\n\n return (\n <div className={cn('w-full', className)}>\n {/* Label */}\n {label && (\n <label\n htmlFor={selectId}\n className={cn(\n 'block font-bold text-text-900 mb-1.5',\n size === 'small' && 'text-sm',\n size === 'medium' && 'text-md',\n size === 'large' && 'text-lg'\n )}\n >\n {label}\n </label>\n )}\n\n {/* Trigger Button */}\n <Button\n ref={setRefs}\n id={selectId}\n variant=\"raw\"\n onClick={handleToggle}\n disabled={disabled || loading}\n aria-expanded={open}\n aria-haspopup=\"listbox\"\n aria-controls={open ? listboxId : undefined}\n aria-invalid={!!errorMessage}\n aria-describedby={describedById}\n className={cn(\n 'flex w-full items-center justify-between border-border-300 bg-background',\n sizeClasses,\n paddingClasses,\n variantClasses,\n invalid && 'border-indicator-error',\n disabled || loading\n ? 'cursor-not-allowed opacity-50 text-text-400'\n : 'cursor-pointer hover:bg-background-50 text-text-700'\n )}\n iconRight={\n <CaretDownIcon\n className={cn(\n 'h-4 w-4 opacity-50 transition-transform shrink-0',\n open && 'rotate-180'\n )}\n />\n }\n >\n <div className={cn('truncate', !selectedLabel && 'text-text-500')}>\n {loading ? (\n <div className=\"flex items-center gap-2\">\n <SpinnerGapIcon size={14} className=\"animate-spin\" />\n <Text size=\"sm\" className=\"text-text-500\">\n {loadingText}\n </Text>\n </div>\n ) : (\n selectedLabel || placeholder\n )}\n </div>\n </Button>\n\n {/* Helper Text / Error */}\n {(helperText || errorMessage) && (\n <div className=\"mt-1.5\">\n {helperText && !errorMessage && (\n <Text id={helperId} size=\"sm\" className=\"text-text-500\">\n {helperText}\n </Text>\n )}\n {errorMessage && (\n <Text id={errorId} size=\"sm\" className=\"text-indicator-error\">\n {errorMessage}\n </Text>\n )}\n </div>\n )}\n\n {/* Dropdown Portal */}\n {typeof document !== 'undefined' &&\n createPortal(dropdownContent, document.body)}\n </div>\n );\n }\n);\n\nSearchSelect.displayName = 'SearchSelect';\n\nexport default SearchSelect;\n"],"mappings":"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","names":[]}