eventx-design-system
Version:
Design System do EventX
189 lines (182 loc) • 6.94 kB
JavaScript
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
import { useState, useRef, useEffect } from 'react';
import styled, { css } from 'styled-components';
import { colors, typography, spacing, borderRadius, transitions, shadows, zIndex } from '../tokens/design-tokens';
// Container do select
const SelectContainer = styled.div `
position: relative;
width: ${({ fullWidth }) => fullWidth ? '100%' : 'auto'};
`;
// Trigger do select
const SelectTrigger = styled.button `
display: flex;
align-items: center;
justify-content: space-between;
width: 100%;
background-color: ${colors.neutral[50]};
border: 2px solid ${colors.neutral[300]};
border-radius: ${borderRadius.base};
padding: ${spacing[3]} ${spacing[4]};
font-family: ${typography.fontFamily.secondary};
font-size: ${typography.fontSize.base};
font-weight: ${typography.fontWeight.normal};
color: ${colors.neutral[950]};
cursor: pointer;
transition: all ${transitions.duration[200]} ${transitions.easing.inOut};
text-align: left;
${({ size }) => {
switch (size) {
case 'sm':
return css `
padding: ${spacing[2]} ${spacing[3]};
font-size: ${typography.fontSize.sm};
min-height: 32px;
`;
case 'lg':
return css `
padding: ${spacing[4]} ${spacing[5]};
font-size: ${typography.fontSize.lg};
min-height: 48px;
`;
default:
return css `
padding: ${spacing[3]} ${spacing[4]};
font-size: ${typography.fontSize.base};
min-height: 40px;
`;
}
}}
${({ variant }) => {
switch (variant) {
case 'error':
return css `
border-color: ${colors.semantic.error[500]};
&:focus {
border-color: ${colors.semantic.error[500]};
box-shadow: 0 0 0 3px ${colors.semantic.error[100]};
}
`;
case 'success':
return css `
border-color: ${colors.semantic.success[500]};
&:focus {
border-color: ${colors.semantic.success[500]};
box-shadow: 0 0 0 3px ${colors.semantic.success[100]};
}
`;
default:
return css `
&:hover:not(:disabled) {
border-color: ${colors.neutral[400]};
}
&:focus {
border-color: ${colors.brand.primary[500]};
box-shadow: 0 0 0 3px ${colors.brand.primary[100]};
}
`;
}
}}
${({ disabled }) => disabled &&
css `
background-color: ${colors.neutral[100]};
color: ${colors.neutral[500]};
cursor: not-allowed;
opacity: 0.6;
`}
${({ isOpen }) => isOpen &&
css `
border-color: ${colors.brand.primary[500]};
box-shadow: 0 0 0 3px ${colors.brand.primary[100]};
`}
`;
// Ícone de seta
const ArrowIcon = styled.span `
display: inline-block;
width: 0;
height: 0;
border-left: 4px solid transparent;
border-right: 4px solid transparent;
border-top: 4px solid ${colors.neutral[500]};
margin-left: ${spacing[2]};
transition: transform ${transitions.duration[200]} ${transitions.easing.inOut};
transform: ${({ isOpen }) => (isOpen ? 'rotate(180deg)' : 'rotate(0deg)')};
`;
// Dropdown do select
const SelectDropdown = styled.div `
position: absolute;
top: 100%;
left: 0;
right: 0;
background-color: ${colors.neutral[50]};
border: 1px solid ${colors.neutral[200]};
border-radius: ${borderRadius.base};
box-shadow: ${shadows.lg};
z-index: ${zIndex.dropdown};
max-height: 200px;
overflow-y: auto;
opacity: ${({ isOpen }) => (isOpen ? 1 : 0)};
visibility: ${({ isOpen }) => (isOpen ? 'visible' : 'hidden')};
transform: ${({ isOpen }) => (isOpen ? 'translateY(0)' : 'translateY(-4px)')};
transition: all ${transitions.duration[200]} ${transitions.easing.inOut};
margin-top: ${spacing[1]};
`;
// Opção do select
const SelectOption = styled.div `
padding: ${spacing[2]} ${spacing[4]};
cursor: ${({ disabled }) => (disabled ? 'not-allowed' : 'pointer')};
color: ${({ disabled, isSelected }) => {
if (disabled)
return colors.neutral[400];
if (isSelected)
return colors.brand.primary[950];
return colors.neutral[950];
}};
background-color: ${({ isSelected }) => isSelected ? colors.brand.primary[50] : 'transparent'};
font-family: ${typography.fontFamily.secondary};
font-size: ${typography.fontSize.base};
transition: all ${transitions.duration[150]} ${transitions.easing.inOut};
&:hover:not(:disabled) {
background-color: ${({ isSelected }) => isSelected ? colors.brand.primary[100] : colors.neutral[100]};
}
${({ disabled }) => disabled &&
css `
opacity: 0.5;
`}
`;
// Placeholder
const Placeholder = styled.span `
color: ${colors.neutral[500]};
`;
export const Select = ({ options, value, onChange, placeholder = 'Selecione uma opção', disabled = false, size = 'md', variant = 'default', fullWidth = true, className, ...props }) => {
const [isOpen, setIsOpen] = useState(false);
const containerRef = useRef(null);
const selectedOption = options.find(option => option.value === value);
useEffect(() => {
const handleClickOutside = (event) => {
if (containerRef.current && !containerRef.current.contains(event.target)) {
setIsOpen(false);
}
};
document.addEventListener('mousedown', handleClickOutside);
return () => document.removeEventListener('mousedown', handleClickOutside);
}, []);
const handleTriggerClick = () => {
if (!disabled) {
setIsOpen(!isOpen);
}
};
const handleOptionClick = (option) => {
if (!option.disabled) {
onChange?.(option.value);
setIsOpen(false);
}
};
const handleKeyDown = (event) => {
if (event.key === 'Enter' || event.key === ' ') {
event.preventDefault();
handleTriggerClick();
}
};
return (_jsxs(SelectContainer, { ref: containerRef, fullWidth: fullWidth, className: className, ...props, children: [_jsxs(SelectTrigger, { type: "button", variant: variant, size: size, disabled: disabled, isOpen: isOpen, onClick: handleTriggerClick, onKeyDown: handleKeyDown, "aria-haspopup": "listbox", "aria-expanded": isOpen, "aria-label": placeholder, children: [_jsx("span", { children: selectedOption ? (selectedOption.label) : (_jsx(Placeholder, { children: placeholder })) }), _jsx(ArrowIcon, { isOpen: isOpen })] }), _jsx(SelectDropdown, { isOpen: isOpen, role: "listbox", children: options.map((option) => (_jsx(SelectOption, { isSelected: option.value === value, disabled: option.disabled || false, onClick: () => handleOptionClick(option), role: "option", "aria-selected": option.value === value, children: option.label }, option.value))) })] }));
};
Select.displayName = 'Select';