Files
twenty/packages/twenty-front/src/modules/ui/field/input/components/RatingInput.tsx
T
Paul Rastoin 4a4e65fe4a [REFACTOR] Twenty UI multi barrel (#11301)
# Introduction
closes https://github.com/twentyhq/core-team-issues/issues/591
Same than for `twenty-shared` made in
https://github.com/twentyhq/twenty/pull/11083.

## TODO
- [x] Manual migrate twenty-website twenty-ui imports

## What's next:
- Generate barrel and migration script factorization within own package
+ tests
- Refactoring using preconstruct ? TimeBox
- Lint circular dependencies
- Lint import from barrel and forbid them

### Preconstruct
We need custom rollup plugins addition, but preconstruct does not expose
its rollup configuration. It might be possible to handle this using the
babel overrides. But was a big tunnel.
We could give it a try afterwards ! ( allowing cjs interop and stuff
like that )
Stuck to vite lib app

Closed related PRs:
- https://github.com/twentyhq/twenty/pull/11294
- https://github.com/twentyhq/twenty/pull/11203
2025-04-03 09:47:55 +00:00

87 lines
2.3 KiB
TypeScript

import { styled } from '@linaria/react';
import { useContext, useState } from 'react';
import { useClearField } from '@/object-record/record-field/hooks/useClearField';
import { RATING_VALUES } from '@/object-record/record-field/meta-types/constants/RatingValues';
import { FieldRatingValue } from '@/object-record/record-field/types/FieldMetadata';
import { IconTwentyStarFilled } from 'twenty-ui/display';
import { THEME_COMMON, ThemeContext } from 'twenty-ui/theme';
const StyledContainer = styled.div`
align-items: center;
display: flex;
`;
const StyledRatingIconContainer = styled.div<{
color: string;
}>`
color: ${({ color }) => color};
display: inline-flex;
`;
type RatingInputProps = {
onChange?: (newValue: FieldRatingValue) => void;
value: FieldRatingValue;
readonly?: boolean;
};
const iconSizeMd = THEME_COMMON.icon.size.md;
export const RatingInput = ({
onChange,
value,
readonly,
}: RatingInputProps) => {
const { theme } = useContext(ThemeContext);
const clearField = useClearField();
const activeColor = theme.font.color.secondary;
const inactiveColor = theme.background.quaternary;
const [hoveredValue, setHoveredValue] = useState<FieldRatingValue>(null);
const currentValue = hoveredValue ?? value;
const selectedIndex =
currentValue !== null ? RATING_VALUES.indexOf(currentValue) : -1;
const canClick = !readonly;
const handleClick = (newValue: FieldRatingValue) => {
if (!canClick) return;
if (newValue === value) {
setHoveredValue(null);
clearField();
} else {
onChange?.(newValue);
}
};
return (
<StyledContainer
role="slider"
aria-label="Rating"
aria-valuemax={RATING_VALUES.length}
aria-valuemin={1}
aria-valuenow={selectedIndex + 1}
tabIndex={0}
>
{RATING_VALUES.map((value, index) => {
const isActive = index <= selectedIndex;
return (
<StyledRatingIconContainer
key={index}
color={isActive ? activeColor : inactiveColor}
onClick={() => handleClick(value)}
onMouseEnter={readonly ? undefined : () => setHoveredValue(value)}
onMouseLeave={readonly ? undefined : () => setHoveredValue(null)}
>
<IconTwentyStarFilled size={iconSizeMd} />
</StyledRatingIconContainer>
);
})}
</StyledContainer>
);
};