@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
JavaScript
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