UNPKG

@unicity/design-system

Version:

A comprehensive React component library built on Material-UI with advanced theming capabilities including neumorphism design support

134 lines 7.69 kB
import { createElement as _createElement } from "react"; import { jsx as _jsx, Fragment as _Fragment, jsxs as _jsxs } from "react/jsx-runtime"; import React from 'react'; import { Autocomplete as MuiAutocomplete, TextField, Chip, Avatar, ListItem, ListItemText, ListItemAvatar, CircularProgress, Paper, } from '@mui/material'; import { Search, Check } from '@mui/icons-material'; import { styled } from '@mui/material/styles'; const StyledAutocomplete = styled(MuiAutocomplete, { shouldForwardProp: (prop) => prop !== 'variant', })(({ theme, variant = 'default' }) => ({ '& .MuiAutocomplete-inputRoot': { ...(variant === 'outlined' && { borderRadius: `calc(${theme.shape.borderRadius}px * 1.5)`, }), ...(variant === 'filled' && { backgroundColor: theme.palette.grey[100], '&:hover': { backgroundColor: theme.palette.grey[200], }, '&.Mui-focused': { backgroundColor: theme.palette.background.paper, }, }), }, '& .MuiAutocomplete-option': { padding: theme.spacing(1, 2), '&[aria-selected="true"]': { backgroundColor: theme.palette.primary.main + '10', }, '&.Mui-focused': { backgroundColor: theme.palette.action.hover, }, }, '& .MuiAutocomplete-popupIndicator': { color: theme.palette.text.secondary, }, '& .MuiAutocomplete-clearIndicator': { color: theme.palette.text.secondary, }, })); const CustomListItem = styled(ListItem)(({ theme }) => ({ padding: theme.spacing(1, 2), margin: 0, '&:hover': { backgroundColor: theme.palette.action.hover, }, '&[aria-selected="true"]': { backgroundColor: theme.palette.primary.main + '10', '&:hover': { backgroundColor: theme.palette.primary.main + '20', }, }, })); const HighlightedText = styled('span')(({ theme }) => ({ fontWeight: 600, color: theme.palette.primary.main, })); const highlightText = (text, highlight) => { if (!highlight) return text; const parts = text.split(new RegExp(`(${highlight})`, 'gi')); return parts.map((part, index) => part.toLowerCase() === highlight.toLowerCase() ? (_jsx(HighlightedText, { children: part }, index)) : (part)); }; const getOptionLabel = (option) => { if (typeof option === 'string') return option; return option.label; }; const isOptionEqualToValue = (option, value) => { return option.id === value.id; }; export const Autocomplete = ({ options, value, onChange, label, placeholder, helperText, error = false, required = false, variant = 'default', size = 'medium', showAvatars = false, showDescriptions = false, highlightMatches = true, groupByCategory = false, loadingText = 'Loading...', noOptionsText = 'No options', allowCreate = false, onCreateOption, chipProps = {}, maxSelections, showSelectedIndicator = true, filterOptions, loadOptions, multiple = false, loading = false, ...props }) => { const [inputValue, setInputValue] = React.useState(''); const [asyncOptions, setAsyncOptions] = React.useState([]); const [asyncLoading, setAsyncLoading] = React.useState(false); // Handle async options loading React.useEffect(() => { if (loadOptions && inputValue) { setAsyncLoading(true); const timeoutId = setTimeout(async () => { try { const newOptions = await loadOptions(inputValue); setAsyncOptions(newOptions); } catch (error) { console.error('Error loading options:', error); } finally { setAsyncLoading(false); } }, 300); // Debounce return () => clearTimeout(timeoutId); } }, [inputValue, loadOptions]); const processedOptions = loadOptions ? asyncOptions : options; const handleChange = (event, newValue) => { // Handle max selections for multiple if (multiple && maxSelections && Array.isArray(newValue) && newValue.length > maxSelections) { return; } // Handle create option if (allowCreate && typeof newValue === 'string' && onCreateOption) { const createdOption = onCreateOption(newValue); onChange?.(multiple ? [...(value || []), createdOption] : createdOption); return; } onChange?.(newValue); }; const renderOption = (props, option, { inputValue: searchValue }) => { const isSelected = multiple ? value?.some(v => isOptionEqualToValue(option, v)) : value && isOptionEqualToValue(option, value); return (_createElement(CustomListItem, { ...props, key: option.id }, showAvatars && (_jsx(ListItemAvatar, { children: _jsx(Avatar, { src: option.avatar, sx: { width: 32, height: 32 }, children: option.icon || option.label.charAt(0) }) })), _jsx(ListItemText, { primary: highlightMatches ? highlightText(option.label, searchValue) : option.label, secondary: showDescriptions && option.description ? (highlightMatches ? highlightText(option.description, searchValue) : option.description) : undefined }), showSelectedIndicator && isSelected && (_jsx(Check, { color: "primary", fontSize: "small" })))); }; const renderTags = (tagValue, getTagProps) => { const displayTags = maxSelections && tagValue.length > maxSelections ? tagValue.slice(0, maxSelections) : tagValue; return (_jsxs(_Fragment, { children: [displayTags.map((option, index) => (_createElement(Chip, { ...getTagProps({ index }), key: option.id, label: option.label, size: size === 'small' ? 'small' : 'medium', avatar: showAvatars && option.avatar ? _jsx(Avatar, { src: option.avatar }) : undefined, ...chipProps }))), maxSelections && tagValue.length > maxSelections && (_jsx(Chip, { label: `+${tagValue.length - maxSelections} more`, size: size === 'small' ? 'small' : 'medium', variant: "outlined" }))] })); }; const groupBy = groupByCategory ? (option) => option.category || 'Other' : undefined; return (_jsx(StyledAutocomplete, { options: processedOptions, value: value, onChange: handleChange, inputValue: inputValue, onInputChange: (event, newInputValue) => setInputValue(newInputValue), getOptionLabel: getOptionLabel, isOptionEqualToValue: isOptionEqualToValue, renderOption: renderOption, renderTags: multiple ? renderTags : undefined, groupBy: groupBy, filterOptions: filterOptions, multiple: multiple, loading: loading || asyncLoading, loadingText: loadingText, noOptionsText: noOptionsText, freeSolo: allowCreate, variant: variant, size: size, ...props, renderInput: (params) => (_jsx(TextField, { ...params, label: label, placeholder: placeholder, helperText: helperText, error: error, required: required, variant: variant === 'default' ? 'outlined' : variant, size: size, InputProps: { ...params.InputProps, startAdornment: (_jsxs(_Fragment, { children: [_jsx(Search, { color: "action", sx: { mr: 1 } }), params.InputProps.startAdornment] })), endAdornment: (_jsxs(_Fragment, { children: [(loading || asyncLoading) ? _jsx(CircularProgress, { color: "inherit", size: 20 }) : null, params.InputProps.endAdornment] })), } })), PaperComponent: ({ children, ...paperProps }) => (_jsx(Paper, { ...paperProps, elevation: 8, children: children })) })); }; //# sourceMappingURL=Autocomplete.js.map