diff --git a/packages/twenty-front/src/modules/activities/calendar/components/CalendarDayCardContent.tsx b/packages/twenty-front/src/modules/activities/calendar/components/CalendarDayCardContent.tsx index b32c950e63..94ff654cb6 100644 --- a/packages/twenty-front/src/modules/activities/calendar/components/CalendarDayCardContent.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/components/CalendarDayCardContent.tsx @@ -1,11 +1,10 @@ import { styled } from '@linaria/react'; import { differenceInSeconds, endOfDay, format } from 'date-fns'; -import { useContext } from 'react'; import { CalendarEventRow } from '@/activities/calendar/components/CalendarEventRow'; import { getCalendarEventStartDate } from '@/activities/calendar/utils/getCalendarEventStartDate'; import { CardContent } from 'twenty-ui/surfaces'; -import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; import { type TimelineCalendarEvent } from '~/generated/graphql'; type CalendarDayCardContentProps = { @@ -23,6 +22,17 @@ const StyledCardContentContainer = styled.div` } `; +const StyledDayCardContent = styled(CardContent)` + @keyframes calendarDayEnded { + to { + background-color: ${themeCssVariables.background.primary}; + } + } + + animation: calendarDayEnded calc(var(--t-animation-duration-fast) * 1s) ease + forwards; +`; + const StyledDayContainer = styled.div` text-align: center; width: ${themeCssVariables.spacing[6]}; @@ -54,31 +64,17 @@ export const CalendarDayCardContent = ({ calendarEvents, divider, }: CalendarDayCardContentProps) => { - const { theme } = useContext(ThemeContext); const endOfDayDate = endOfDay(getCalendarEventStartDate(calendarEvents[0])); const dayEndsIn = differenceInSeconds(endOfDayDate, Date.now()); const weekDayLabel = format(endOfDayDate, 'EE'); const monthDayLabel = format(endOfDayDate, 'dd'); - const upcomingDayCardContentVariants = { - upcoming: {}, - ended: { - backgroundColor: theme.background.primary, - }, - }; - return ( - {weekDayLabel} @@ -91,7 +87,7 @@ export const CalendarDayCardContent = ({ ))} - + ); }; diff --git a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventsCard.tsx b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventsCard.tsx index 4661911a0e..dba5317f2b 100644 --- a/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventsCard.tsx +++ b/packages/twenty-front/src/modules/activities/calendar/components/CalendarEventsCard.tsx @@ -20,7 +20,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { Section } from 'twenty-ui/layout'; import { themeCssVariables } from 'twenty-ui/theme-constants'; @@ -97,10 +96,7 @@ export const CalendarEventsCard = () => { if (!firstQueryLoading && !timelineCalendarEvents?.length) { // TODO: change animated placeholder return ( - + diff --git a/packages/twenty-front/src/modules/activities/emails/components/EmptyInboxPlaceholder.tsx b/packages/twenty-front/src/modules/activities/emails/components/EmptyInboxPlaceholder.tsx index 2224ec8351..8b192cb6e1 100644 --- a/packages/twenty-front/src/modules/activities/emails/components/EmptyInboxPlaceholder.tsx +++ b/packages/twenty-front/src/modules/activities/emails/components/EmptyInboxPlaceholder.tsx @@ -8,7 +8,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; export const EmptyInboxPlaceholder = () => { @@ -16,10 +15,7 @@ export const EmptyInboxPlaceholder = () => { const { openComposer, loading } = useComposeEmailForTargetRecord(); return ( - + diff --git a/packages/twenty-front/src/modules/activities/files/components/FilesCard.tsx b/packages/twenty-front/src/modules/activities/files/components/FilesCard.tsx index 0ece6b0dd4..4ec552daf8 100644 --- a/packages/twenty-front/src/modules/activities/files/components/FilesCard.tsx +++ b/packages/twenty-front/src/modules/activities/files/components/FilesCard.tsx @@ -20,7 +20,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { PermissionFlagType } from '~/generated-metadata/graphql'; @@ -103,10 +102,7 @@ export const FilesCard = () => { onUploadFiles={onUploadFiles} /> ) : ( - + diff --git a/packages/twenty-front/src/modules/activities/notes/components/NotesCard.tsx b/packages/twenty-front/src/modules/activities/notes/components/NotesCard.tsx index ecaf1c1822..107d2bbf6a 100644 --- a/packages/twenty-front/src/modules/activities/notes/components/NotesCard.tsx +++ b/packages/twenty-front/src/modules/activities/notes/components/NotesCard.tsx @@ -17,7 +17,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; const StyledNotesContainer = styled.div` @@ -61,10 +60,7 @@ export const NotesCard = () => { if (isNotesEmpty) { return ( - + diff --git a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx index caeb6b3abc..e8dc580da5 100644 --- a/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx +++ b/packages/twenty-front/src/modules/activities/tasks/components/TaskGroups.tsx @@ -20,7 +20,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { AddTaskButton } from './AddTaskButton'; import { TaskList } from './TaskList'; @@ -71,10 +70,7 @@ export const TaskGroups = ({ targetableObject }: TaskGroupsProps) => { if (isTasksEmpty) { return ( - + diff --git a/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineCard.tsx b/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineCard.tsx index d2a5bc8314..0544aa6b82 100644 --- a/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineCard.tsx +++ b/packages/twenty-front/src/modules/activities/timeline-activities/components/TimelineCard.tsx @@ -13,7 +13,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { MOBILE_VIEWPORT, themeCssVariables } from 'twenty-ui/theme-constants'; @@ -63,10 +62,7 @@ export const TimelineCard = () => { if (isTimelineActivitiesEmpty) { const placeholderContent = ( - + diff --git a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx index 24b5928a44..858ca5b667 100644 --- a/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx +++ b/packages/twenty-front/src/modules/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection.tsx @@ -1,6 +1,6 @@ import { styled } from '@linaria/react'; -import { useCallback, useContext } from 'react'; -import { ThemeContext, themeCssVariables } from 'twenty-ui/theme-constants'; +import { useCallback } from 'react'; +import { themeCssVariables } from 'twenty-ui/theme-constants'; import { RecordBoardColumnHeaderAggregateDropdown } from '@/object-record/record-board/record-board-column/components/RecordBoardColumnHeaderAggregateDropdown'; import { visibleRecordFieldsComponentSelector } from '@/object-record/record-field/states/visibleRecordFieldsComponentSelector'; @@ -114,8 +114,6 @@ const StyledRecordTableDragAndDropPlaceholderCell = styled.div` `; export const RecordTableRecordGroupSection = () => { - const { theme } = useContext(ThemeContext); - const currentRecordGroupId = useCurrentRecordGroupId(); const shouldHide = useShouldHideRecordGroup(currentRecordGroupId); @@ -199,8 +197,7 @@ export const RecordTableRecordGroupSection = () => { Icon={IconChevronDown} size="small" accent="secondary" - animate={{ rotate: !isRecordGroupTableSectionToggled ? -90 : 0 }} - transition={{ duration: theme.animation.duration.normal }} + rotate={!isRecordGroupTableSectionToggled ? -90 : 0} /> diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx index 8f4ea5a51f..8ef562d1e3 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/DashboardWidgetPlaceholder.tsx @@ -16,7 +16,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; export const DashboardWidgetPlaceholder = () => { @@ -66,10 +65,7 @@ export const DashboardWidgetPlaceholder = () => { title={t`Add Widget`} isEmpty /> - + diff --git a/packages/twenty-front/src/modules/page-layout/widgets/components/StandaloneWidgetPlaceholder.tsx b/packages/twenty-front/src/modules/page-layout/widgets/components/StandaloneWidgetPlaceholder.tsx index 49c531b071..feae3b227e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/components/StandaloneWidgetPlaceholder.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/components/StandaloneWidgetPlaceholder.tsx @@ -7,7 +7,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; const StyledPlaceholderContainer = styled.div` @@ -24,10 +23,7 @@ const StyledPlaceholderContainer = styled.div` export const StandaloneWidgetPlaceholder = () => { return ( - + diff --git a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidget.tsx index 9be1b191f7..234be9939e 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/field/components/FieldWidget.tsx @@ -35,7 +35,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { FieldDisplayMode } from '~/generated-metadata/graphql'; @@ -76,10 +75,7 @@ export const FieldWidget = ({ widget }: FieldWidgetProps) => { return ( - + diff --git a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx index 2608402fa2..29805fa812 100644 --- a/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx +++ b/packages/twenty-front/src/modules/page-layout/widgets/fields/components/FieldsWidget.tsx @@ -19,7 +19,6 @@ import { AnimatedPlaceholderEmptySubTitle, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, } from 'twenty-ui/feedback'; import { themeCssVariables } from 'twenty-ui/theme-constants'; import { type FieldsConfiguration } from '~/generated-metadata/graphql'; @@ -108,10 +107,7 @@ export const FieldsWidget = ({ widget }: FieldsWidgetProps) => { return ( - + diff --git a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx index c66bf79257..5738edae66 100644 --- a/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx +++ b/packages/twenty-front/src/modules/ui/feedback/snack-bar-manager/components/SnackBar.tsx @@ -8,6 +8,7 @@ import { type ReactNode, useContext, useMemo, + useState, } from 'react'; import { isDefined } from 'twenty-shared/utils'; import { @@ -17,7 +18,7 @@ import { IconX, } from 'twenty-ui/icon'; import { HorizontalSeparator } from 'twenty-ui/layout'; -import { ProgressBar, useProgressAnimation } from 'twenty-ui/feedback'; +import { ProgressBar } from 'twenty-ui/feedback'; import { LightButton, LightIconButton } from 'twenty-ui/input'; import { UndecoratedLink } from 'twenty-ui/navigation'; import { @@ -156,15 +157,8 @@ export const SnackBar = ({ }: SnackBarProps) => { const { i18n, t } = useLingui(); const { theme } = useContext(ThemeContext); - const { animation: progressAnimation, value: progressValue } = - useProgressAnimation({ - autoPlay: isUndefined(overrideProgressValue), - initialValue: isDefined(overrideProgressValue) - ? overrideProgressValue - : 100, - finalValue: 0, - options: { duration, onComplete: onClose }, - }); + const [isPaused, setIsPaused] = useState(false); + const isAutoDismiss = isUndefined(overrideProgressValue); const icon = useMemo(() => { if (isDefined(iconComponent)) { @@ -200,11 +194,11 @@ export const SnackBar = ({ }, [iconComponent, variant, i18n, theme.icon.size.md, theme.snackBar]); const handleMouseEnter = () => { - progressAnimation?.pause(); + setIsPaused(true); }; const handleMouseLeave = () => { - progressAnimation?.play(); + setIsPaused(false); }; const sanitizedMessage = sanitizeMessageToRenderInSnackbar(message); @@ -225,7 +219,10 @@ export const SnackBar = ({ diff --git a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputSchemaBuilder.tsx b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputSchemaBuilder.tsx index fffca3c9c1..84c645f28b 100644 --- a/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputSchemaBuilder.tsx +++ b/packages/twenty-front/src/modules/workflow/workflow-steps/workflow-actions/ai-agent-action/components/WorkflowOutputSchemaBuilder.tsx @@ -194,7 +194,7 @@ export const WorkflowOutputSchemaBuilder = ({ {showRemoveFieldButton && ( & { +import styles from './AnimatedCheckmark.module.scss'; + +export type AnimatedCheckmarkProps = { isAnimating?: boolean; color?: string; duration?: number; @@ -25,16 +24,20 @@ export const AnimatedCheckmark = ({ width={size} height={size} > - ); diff --git a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss index 26232d302a..ef4aba3b87 100644 --- a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss +++ b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss @@ -16,3 +16,17 @@ max-height: 245px; max-width: 245px; } + +.movingImage { + position: absolute; + z-index: 2; + max-width: 130px; + max-height: 130px; + transform: translate(var(--parallax-x, 0), var(--parallax-y, 0)); + transition: transform duration(normal) ease-out; +} + +.movingImageLarge { + max-width: 185px; + max-height: 185px; +} diff --git a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss.d.ts b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss.d.ts index 0cb921e3e9..67b3c4492d 100644 --- a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss.d.ts +++ b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.module.scss.d.ts @@ -2,5 +2,7 @@ declare const classNames: { readonly container: 'container'; readonly backgroundImage: 'backgroundImage'; readonly backgroundImageLarge: 'backgroundImageLarge'; + readonly movingImage: 'movingImage'; + readonly movingImageLarge: 'movingImageLarge'; }; export default classNames; diff --git a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.tsx b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.tsx index 48103dd170..039e2bb806 100644 --- a/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.tsx +++ b/packages/twenty-ui/src/feedback/AnimatedPlaceholder/AnimatedPlaceholder.tsx @@ -1,6 +1,5 @@ import { clsx } from 'clsx'; -import { animate, motion, useMotionValue, useTransform } from 'framer-motion'; -import { useEffect } from 'react'; +import { useEffect, useRef } from 'react'; import { BACKGROUND } from '@ui/feedback/AnimatedPlaceholder/constants/Background'; import { DARK_BACKGROUND } from '@ui/feedback/AnimatedPlaceholder/constants/DarkBackground'; @@ -10,8 +9,7 @@ import { useThemeColorScheme } from '@ui/theme-constants'; import styles from './AnimatedPlaceholder.module.scss'; -const getMovingImageSize = (type: string) => - type === 'error500' || type === 'error404' ? '185px' : '130px'; +const PARALLAX_OFFSET_IN_PX = 2; export type AnimatedPlaceholderType = | keyof typeof BACKGROUND @@ -23,35 +21,35 @@ type AnimatedPlaceholderProps = { export const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => { const colorScheme = useThemeColorScheme(); - - const x = useMotionValue(window.innerWidth / 2); - const y = useMotionValue(window.innerHeight / 2); - - const translateX = useTransform(x, [0, window.innerWidth], [-2, 2]); - const translateY = useTransform(y, [0, window.innerHeight], [-2, 2]); + const movingImageRef = useRef(null); useEffect(() => { + const movingImage = movingImageRef.current; + if (movingImage === null) { + return; + } + + const setParallax = (offsetX: number, offsetY: number) => { + movingImage.style.setProperty('--parallax-x', `${offsetX}px`); + movingImage.style.setProperty('--parallax-y', `${offsetY}px`); + }; + const handleMove = (event: MouseEvent | TouchEvent) => { const clientX = 'touches' in event ? event.touches[0].clientX : event.clientX; const clientY = 'touches' in event ? event.touches[0].clientY : event.clientY; - x.set(clientX); - y.set(clientY); + setParallax( + (clientX / window.innerWidth) * 2 * PARALLAX_OFFSET_IN_PX - + PARALLAX_OFFSET_IN_PX, + (clientY / window.innerHeight) * 2 * PARALLAX_OFFSET_IN_PX - + PARALLAX_OFFSET_IN_PX, + ); }; const handleLeave = () => { - animate(x, window.innerWidth / 2, { - type: 'spring', - stiffness: 100, - damping: 10, - }); - animate(y, window.innerHeight / 2, { - type: 'spring', - stiffness: 100, - damping: 10, - }); + setParallax(0, 0); }; window.addEventListener('mousemove', handleMove); @@ -63,7 +61,9 @@ export const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => { window.removeEventListener('touchmove', handleMove); window.document.removeEventListener('mouseleave', handleLeave); }; - }, [x, y]); + }, []); + + const isLarge = type === 'error500' || type === 'error404'; return (
@@ -72,24 +72,16 @@ export const AnimatedPlaceholder = ({ type }: AnimatedPlaceholderProps) => { alt="" className={clsx( styles.backgroundImage, - (type === 'error500' || type === 'error404') && - styles.backgroundImageLarge, + isLarge && styles.backgroundImageLarge, )} /> -
); diff --git a/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss new file mode 100644 index 0000000000..d1ca0e856e --- /dev/null +++ b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss @@ -0,0 +1,40 @@ +.svg { + animation: circularProgressBarRotate 2s linear infinite; + transform-origin: center; +} + +.circle { + animation: circularProgressBarDash 2s linear infinite; +} + +@keyframes circularProgressBarRotate { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(720deg); + } +} + +@keyframes circularProgressBarDash { + 0% { + stroke-dasharray: 10 90; + } + + 25% { + stroke-dasharray: 20 80; + } + + 50% { + stroke-dasharray: 30 70; + } + + 75% { + stroke-dasharray: 20 80; + } + + 100% { + stroke-dasharray: 10 90; + } +} diff --git a/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss.d.ts b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss.d.ts new file mode 100644 index 0000000000..502ab632b6 --- /dev/null +++ b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.module.scss.d.ts @@ -0,0 +1,5 @@ +declare const classNames: { + readonly svg: 'svg'; + readonly circle: 'circle'; +}; +export default classNames; diff --git a/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.tsx b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.tsx index 9964b49106..d25644e95e 100644 --- a/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.tsx +++ b/packages/twenty-ui/src/feedback/CircularProgressBar/CircularProgressBar.tsx @@ -1,5 +1,4 @@ -import { motion, useAnimation } from 'framer-motion'; -import { useEffect, useMemo } from 'react'; +import styles from './CircularProgressBar.module.scss'; type CircularProgressBarProps = { size?: number; @@ -11,61 +10,18 @@ export const CircularProgressBar = ({ size = 50, barWidth = 5, barColor = 'currentColor', -}: CircularProgressBarProps) => { - const controls = useAnimation(); - - const circumference = useMemo( - () => 2 * Math.PI * (size / 2 - barWidth), - [size, barWidth], - ); - - useEffect(() => { - const animateIndeterminate = async () => { - const baseSegment = Math.max(5, circumference / 10); // Adjusting for smaller values - - // Adjusted sequence based on baseSegment - const dashSequences = [ - `${baseSegment} ${circumference - baseSegment}`, - `${baseSegment * 2} ${circumference - baseSegment * 2}`, - `${baseSegment * 3} ${circumference - baseSegment * 3}`, - `${baseSegment * 2} ${circumference - baseSegment * 2}`, - `${baseSegment} ${circumference - baseSegment}`, - ]; - - await controls.start({ - strokeDasharray: dashSequences, - rotate: [0, 720], - transition: { - strokeDasharray: { - duration: 2, - ease: 'linear', - repeat: Infinity, - repeatType: 'loop', - }, - rotate: { - duration: 2, - ease: 'linear', - repeat: Infinity, - repeatType: 'loop', - }, - }, - }); - }; - - animateIndeterminate(); - }, [circumference, controls]); - - return ( - - - - ); -}; +}: CircularProgressBarProps) => ( + + + +); diff --git a/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.module.scss b/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.module.scss index 1fd5d982c8..3536f67cca 100644 --- a/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.module.scss +++ b/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.module.scss @@ -7,6 +7,17 @@ gap: var(--t-spacing-6); justify-content: center; text-align: center; + animation: emptyPlaceholderFadeIn duration(fast) ease; +} + +@keyframes emptyPlaceholderFadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } } .emptyTextContainer { diff --git a/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.tsx b/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.tsx index 2d5dba330e..6a88376e84 100644 --- a/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.tsx +++ b/packages/twenty-ui/src/feedback/EmptyPlaceholderStyled/EmptyPlaceholderStyled.tsx @@ -1,14 +1,10 @@ import { clsx } from 'clsx'; -import { type HTMLMotionProps, motion } from 'framer-motion'; import { type ComponentPropsWithoutRef, type ReactNode } from 'react'; import { isDefined } from '@ui/utilities/utils/isDefined'; import styles from './EmptyPlaceholderStyled.module.scss'; -type AnimatedPlaceholderEmptyContainerProps = Pick< - HTMLMotionProps<'div'>, - 'initial' | 'animate' | 'transition' -> & { +type AnimatedPlaceholderEmptyContainerProps = { children?: ReactNode; className?: string; width?: number; @@ -18,31 +14,17 @@ export const AnimatedPlaceholderEmptyContainer = ({ children, className, width, - initial, - animate, - transition, }: AnimatedPlaceholderEmptyContainerProps) => { return ( - {children} - + ); }; -export const EMPTY_PLACEHOLDER_TRANSITION_PROPS = { - initial: { opacity: 0 }, - animate: { opacity: 1 }, - transition: { - duration: 0.15, - }, -}; - export const AnimatedPlaceholderEmptyTextContainer = ({ className, ...rest diff --git a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss index cbb0c72a73..7cfb6f3319 100644 --- a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss +++ b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss @@ -1,22 +1,52 @@ .bar { + position: relative; height: var(--t-spacing-2); + width: 100%; background-color: var(--progress-bar-background-color, none); border-radius: 0; overflow: hidden; - width: 100%; &[data-with-border-radius] { border-radius: var(--t-border-radius-xxl); } } -.barFilling { - background-color: var(--progress-bar-color, var(--t-font-color-primary)); - border-radius: 0; +.track { + position: relative; height: 100%; width: 100%; +} + +.indicator { + height: 100%; + background-color: var(--progress-bar-color, var(--t-font-color-primary)); + border-radius: 0; + transition: width duration(normal) linear; &[data-with-border-radius] { border-radius: var(--t-border-radius-md); } + + &[data-nonzero] { + min-width: 12px; + } +} + +.countdown { + animation: progressBarCountdown + var(--progress-bar-countdown-duration, #{duration(normal)}) linear forwards; + + &[data-paused] { + animation-play-state: paused; + } +} + +@keyframes progressBarCountdown { + from { + width: 100%; + } + + to { + width: 0; + } } diff --git a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss.d.ts b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss.d.ts index 9ced34bf10..17931fe96a 100644 --- a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss.d.ts +++ b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.module.scss.d.ts @@ -1,5 +1,7 @@ declare const classNames: { readonly bar: 'bar'; - readonly barFilling: 'barFilling'; + readonly track: 'track'; + readonly indicator: 'indicator'; + readonly countdown: 'countdown'; }; export default classNames; diff --git a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.tsx b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.tsx index 7b0274898e..3d54a09c93 100644 --- a/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.tsx +++ b/packages/twenty-ui/src/feedback/ProgressBar/ProgressBar.tsx @@ -1,3 +1,4 @@ +import { Progress } from '@base-ui/react/progress'; import { clsx } from 'clsx'; import styles from './ProgressBar.module.scss'; @@ -9,16 +10,11 @@ export type ProgressBarProps = { backgroundColor?: string; withBorderRadius?: boolean; ariaLabel?: string; + countdownDurationInMs?: number; + isCountdownPaused?: boolean; + onCountdownComplete?: () => void; }; -export type StyledBarProps = { - className?: string; - backgroundColor?: string; - withBorderRadius?: boolean; -}; - -const MIN_BAR_WIDTH_PX = 12; - export const ProgressBar = ({ value, className, @@ -26,36 +22,39 @@ export const ProgressBar = ({ backgroundColor = 'none', withBorderRadius = false, ariaLabel, -}: ProgressBarProps) => ( -
-
0 ? MIN_BAR_WIDTH_PX : 0, - width: `${Math.ceil(value)}%`, - transition: 'width 0.3s linear', - }} + countdownDurationInMs, + isCountdownPaused = false, + onCountdownComplete, +}: ProgressBarProps) => { + const isCountdown = countdownDurationInMs !== undefined; + + return ( + -
-
-
-); + + 0 && !isCountdown) || undefined} + data-paused={(isCountdown && isCountdownPaused) || undefined} + onAnimationEnd={isCountdown ? onCountdownComplete : undefined} + /> + + + ); +}; diff --git a/packages/twenty-ui/src/feedback/ProgressBar/__stories__/ProgressBar.stories.tsx b/packages/twenty-ui/src/feedback/ProgressBar/__stories__/ProgressBar.stories.tsx index 3b9e37cba1..e79632861f 100644 --- a/packages/twenty-ui/src/feedback/ProgressBar/__stories__/ProgressBar.stories.tsx +++ b/packages/twenty-ui/src/feedback/ProgressBar/__stories__/ProgressBar.stories.tsx @@ -1,5 +1,4 @@ import { type Meta, type StoryObj } from '@storybook/react-vite'; -import { useProgressAnimation } from '@ui/feedback/ProgressBar/hooks/useProgressAnimation'; import { ComponentDecorator } from '@ui/testing/decorators/ComponentDecorator'; import { ProgressBar } from '@ui/feedback/ProgressBar/ProgressBar'; @@ -24,25 +23,16 @@ export const Default: Story = { }, }; -export const Animated: Story = { +export const Countdown: Story = { tags: ['!test'], argTypes: { value: { control: false }, }, - decorators: [ - (Story) => { - const { value } = useProgressAnimation({ - autoPlay: true, - initialValue: 0, - finalValue: 100, - options: { - duration: 10000, - }, - }); - - return ; - }, - ], + args: { + value: 100, + ariaLabel: 'Progress', + countdownDurationInMs: 10000, + }, parameters: { chromatic: { disableSnapshot: true }, }, diff --git a/packages/twenty-ui/src/feedback/ProgressBar/hooks/useProgressAnimation.ts b/packages/twenty-ui/src/feedback/ProgressBar/hooks/useProgressAnimation.ts deleted file mode 100644 index fa99cdbb5b..0000000000 --- a/packages/twenty-ui/src/feedback/ProgressBar/hooks/useProgressAnimation.ts +++ /dev/null @@ -1,57 +0,0 @@ -import { millisecondsToSeconds } from 'date-fns'; -import { - animate, - type AnimationPlaybackControls, - type ValueAnimationTransition, -} from 'framer-motion'; -import { useCallback, useEffect, useState } from 'react'; -import { isDefined } from '@ui/utilities/utils/isDefined'; - -export const useProgressAnimation = ({ - autoPlay = true, - initialValue = 0, - finalValue = 100, - options, -}: { - autoPlay?: boolean; - initialValue?: number; - finalValue?: number; - options?: ValueAnimationTransition; -}) => { - const [animation, setAnimation] = useState< - AnimationPlaybackControls | undefined - >(); - const [value, setValue] = useState(initialValue); - - const startAnimation = useCallback(() => { - if (isDefined(animation)) return; - - const duration = isDefined(options?.duration) - ? millisecondsToSeconds(options.duration) - : undefined; - - setAnimation( - animate(initialValue, finalValue, { - ...options, - duration, - onUpdate: (nextValue) => { - if (value === nextValue) return; - setValue(nextValue); - options?.onUpdate?.(nextValue); - }, - }), - ); - }, [animation, finalValue, initialValue, options, value]); - - useEffect(() => { - if (autoPlay && !animation) { - startAnimation(); - } - }, [animation, autoPlay, startAnimation]); - - return { - animation, - startAnimation, - value, - }; -}; diff --git a/packages/twenty-ui/src/feedback/index.ts b/packages/twenty-ui/src/feedback/index.ts index 50f7aa70de..3e300690e7 100644 --- a/packages/twenty-ui/src/feedback/index.ts +++ b/packages/twenty-ui/src/feedback/index.ts @@ -20,7 +20,6 @@ export { Callout } from './Callout/Callout'; export { CircularProgressBar } from './CircularProgressBar/CircularProgressBar'; export { AnimatedPlaceholderEmptyContainer, - EMPTY_PLACEHOLDER_TRANSITION_PROPS, AnimatedPlaceholderEmptyTextContainer, AnimatedPlaceholderEmptyTitle, AnimatedPlaceholderEmptySubTitle, @@ -35,11 +34,7 @@ export type { InfoAccent, InfoProps } from './Info/Info'; export { Info } from './Info/Info'; export { InlineBanner } from './InlineBanner/InlineBanner'; export { Loader } from './Loader/Loader'; -export { useProgressAnimation } from './ProgressBar/hooks/useProgressAnimation'; -export type { - ProgressBarProps, - StyledBarProps, -} from './ProgressBar/ProgressBar'; +export type { ProgressBarProps } from './ProgressBar/ProgressBar'; export { ProgressBar } from './ProgressBar/ProgressBar'; export type { SidePanelInformationBannerProps } from './SidePanelInformationBanner/SidePanelInformationBanner'; export { SidePanelInformationBanner } from './SidePanelInformationBanner/SidePanelInformationBanner'; diff --git a/packages/twenty-ui/src/input/AnimatedButton/AnimatedButton.tsx b/packages/twenty-ui/src/input/AnimatedButton/AnimatedButton.tsx index 337753d234..b21bb29c5f 100644 --- a/packages/twenty-ui/src/input/AnimatedButton/AnimatedButton.tsx +++ b/packages/twenty-ui/src/input/AnimatedButton/AnimatedButton.tsx @@ -1,5 +1,4 @@ import { clsx } from 'clsx'; -import { type MotionProps, motion } from 'framer-motion'; import React, { useMemo } from 'react'; import { Link } from 'react-router-dom'; @@ -17,13 +16,12 @@ import { import styles from './AnimatedButton.module.scss'; -export type AnimatedButtonProps = ButtonProps & - Pick & { - animatedSvg: React.ReactNode; - soonLabel?: string; - // Renders a square icon-only button (width matches the size-based height). - square?: boolean; - }; +export type AnimatedButtonProps = ButtonProps & { + animatedSvg: React.ReactNode; + soonLabel?: string; + // Renders a square icon-only button (width matches the size-based height). + square?: boolean; +}; type AnimatedButtonDynamicStyles = { background: string; @@ -329,8 +327,6 @@ export const AnimatedButton = ({ dataTestId, hotkeys, ariaLabel, - animate, - transition, dataClickOutsideId, dataGloballyPreventClickOutside, soonLabel = 'Soon', @@ -392,23 +388,11 @@ export const AnimatedButton = ({ data-globally-prevent-click-outside={dataGloballyPreventClickOutside} > {Icon && ( - +
- - )} - {animatedSvg && ( - - {animatedSvg} - +
)} + {animatedSvg &&
{animatedSvg}
} {title} {hotkeys && !isMobile && ( <> diff --git a/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.module.scss b/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.module.scss index 16d06cfb6e..70c39e4398 100644 --- a/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.module.scss +++ b/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.module.scss @@ -76,4 +76,5 @@ align-items: center; display: flex; justify-content: center; + transition: transform duration(normal) ease; } diff --git a/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.tsx b/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.tsx index 6873d1d466..d7defd62b9 100644 --- a/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.tsx +++ b/packages/twenty-ui/src/input/AnimatedLightIconButton/AnimatedLightIconButton.tsx @@ -1,5 +1,4 @@ import { clsx } from 'clsx'; -import { motion, type MotionProps } from 'framer-motion'; import { type ComponentProps, type MouseEvent } from 'react'; import { type IconComponent } from '@ui/icon'; @@ -21,22 +20,21 @@ export type AnimatedLightIconButtonProps = { active?: boolean; disabled?: boolean; focus?: boolean; + rotate?: number; onClick?: (event: MouseEvent) => void; -} & Pick, 'aria-label' | 'title'> & - Pick; +} & Pick, 'aria-label' | 'title'>; export const AnimatedLightIconButton = ({ 'aria-label': ariaLabel, className, testId, - animate, - transition, Icon, active = false, size = 'small', accent = 'secondary', disabled = false, focus = false, + rotate, onClick, title, }: AnimatedLightIconButtonProps) => { @@ -55,10 +53,13 @@ export const AnimatedLightIconButton = ({ className={clsx(styles.button, styles[size], className)} title={title} > - {Icon && ( )} - +
); }; diff --git a/packages/twenty-ui/src/input/AnimatedLightIconButton/__stories__/AnimatedLightIconButton.stories.tsx b/packages/twenty-ui/src/input/AnimatedLightIconButton/__stories__/AnimatedLightIconButton.stories.tsx index 8bc7b9ba4b..20890f0aaa 100644 --- a/packages/twenty-ui/src/input/AnimatedLightIconButton/__stories__/AnimatedLightIconButton.stories.tsx +++ b/packages/twenty-ui/src/input/AnimatedLightIconButton/__stories__/AnimatedLightIconButton.stories.tsx @@ -28,13 +28,9 @@ export const Default: Story = { active: false, focus: false, Icon: IconSearch, - animate: { scale: 1.2 }, - transition: { duration: 0.3 }, }, argTypes: { Icon: { control: false }, - animate: { control: 'object' }, - transition: { control: 'object' }, }, decorators: [ComponentDecorator], }; @@ -43,16 +39,12 @@ export const Catalog: CatalogStory = { args: { title: 'Filter', Icon: IconSearch, - animate: { scale: 1.2 }, - transition: { duration: 0.3 }, }, argTypes: { accent: { control: false }, disabled: { control: false }, active: { control: false }, focus: { control: false }, - animate: { control: 'object' }, - transition: { control: 'object' }, }, parameters: { a11y: A11Y_DEFER_COLOR_CONTRAST, diff --git a/packages/twenty-ui/src/input/ColorSchemeCard/ColorSchemeCard.tsx b/packages/twenty-ui/src/input/ColorSchemeCard/ColorSchemeCard.tsx index a3ad562cb5..d1cf18b1ec 100644 --- a/packages/twenty-ui/src/input/ColorSchemeCard/ColorSchemeCard.tsx +++ b/packages/twenty-ui/src/input/ColorSchemeCard/ColorSchemeCard.tsx @@ -1,7 +1,6 @@ import React from 'react'; import { clsx } from 'clsx'; -import { type AnimationControls } from 'framer-motion'; import { handleClickableElementKeyDown } from '@ui/accessibility/utils/handleClickableElementKeyDown'; import { Checkmark } from '@ui/data-display/Checkmark/Checkmark'; @@ -12,11 +11,8 @@ import { isDefined } from '@ui/utilities/utils/isDefined'; import styles from './ColorSchemeCard.module.scss'; -// controls is no longer used since the hover animation is CSS-only now, but it -// stays in the exported type for backward-compatible public API parity. export type ColorSchemeSegmentProps = { variant: ColorScheme; - controls: AnimationControls; className?: string; } & React.ComponentPropsWithoutRef<'div'>; @@ -27,7 +23,7 @@ const ColorSchemeSegment = ({ onClick, onMouseEnter, onMouseLeave, -}: Omit) => { +}: ColorSchemeSegmentProps) => { const grayScale = variant === 'Dark' ? GRAY_SCALE_DARK : GRAY_SCALE_LIGHT; return ( diff --git a/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.module.scss b/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.module.scss index d32734b929..05d81ca271 100644 --- a/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.module.scss +++ b/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.module.scss @@ -2,4 +2,15 @@ align-items: center; display: flex; justify-content: center; + animation: animatedCircleLoadingSpin duration(slow) ease-in-out infinite; +} + +@keyframes animatedCircleLoadingSpin { + from { + transform: rotate(0deg); + } + + to { + transform: rotate(360deg); + } } diff --git a/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.tsx b/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.tsx index ef8173f6bf..cde11122f3 100644 --- a/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.tsx +++ b/packages/twenty-ui/src/layout/AnimatedCircleLoading/AnimatedCircleLoading.tsx @@ -1,31 +1,7 @@ -import { motion } from 'framer-motion'; -import React from 'react'; - -import { useTheme } from '@ui/theme-constants'; - import styles from './AnimatedCircleLoading.module.scss'; export const AnimatedCircleLoading = ({ children, }: { children: React.ReactNode; -}) => { - const theme = useTheme(); - - return ( - - {children} - - ); -}; +}) =>
{children}
; diff --git a/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss new file mode 100644 index 0000000000..1d08450eaa --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss @@ -0,0 +1,18 @@ +.container { + animation: animatedContainerFadeIn duration(fast) ease; + transition: transform duration(fast) ease; + + &:hover { + transform: scale(1.04); + } +} + +@keyframes animatedContainerFadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} diff --git a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss.d.ts similarity index 78% rename from packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss.d.ts rename to packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss.d.ts index 79778847e0..8df71b4dea 100644 --- a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss.d.ts +++ b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.module.scss.d.ts @@ -1,5 +1,4 @@ declare const classNames: { readonly container: 'container'; - readonly word: 'word'; }; export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.tsx b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.tsx index 581e03cb7b..a21ee14c3d 100644 --- a/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.tsx +++ b/packages/twenty-ui/src/layout/AnimatedContainer/AnimatedContainer.tsx @@ -1,17 +1,7 @@ -import { motion } from 'framer-motion'; -import React from 'react'; +import styles from './AnimatedContainer.module.scss'; export const AnimatedContainer = ({ children, }: { children: React.ReactNode; -}) => ( - - {children} - -); +}) =>
{children}
; diff --git a/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss new file mode 100644 index 0000000000..84d9e73bce --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss @@ -0,0 +1,27 @@ +.fadeIn { + --animated-ease-in-duration: #{duration(normal)}; + + animation: animatedEaseInFadeIn var(--animated-ease-in-duration) linear; + + &[data-duration='instant'] { + --animated-ease-in-duration: #{duration(instant)}; + } + + &[data-duration='fast'] { + --animated-ease-in-duration: #{duration(fast)}; + } + + &[data-duration='slow'] { + --animated-ease-in-duration: #{duration(slow)}; + } +} + +@keyframes animatedEaseInFadeIn { + from { + opacity: 0; + } + + to { + opacity: 1; + } +} diff --git a/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss.d.ts new file mode 100644 index 0000000000..c9a9048a6d --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.module.scss.d.ts @@ -0,0 +1,4 @@ +declare const classNames: { + readonly fadeIn: 'fadeIn'; +}; +export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.tsx b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.tsx index 490a44dc30..5121c86d23 100644 --- a/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.tsx +++ b/packages/twenty-ui/src/layout/AnimatedEaseIn/AnimatedEaseIn.tsx @@ -1,30 +1,17 @@ -import { motion } from 'framer-motion'; import { type AnimationDuration } from '@ui/theme'; -import { useTheme } from '@ui/theme-constants'; -type AnimatedEaseInProps = Omit< - React.ComponentProps, - 'initial' | 'animated' | 'transition' -> & { +import styles from './AnimatedEaseIn.module.scss'; + +type AnimatedEaseInProps = { + children?: React.ReactNode; duration?: AnimationDuration; }; export const AnimatedEaseIn = ({ children, duration = 'normal', -}: AnimatedEaseInProps) => { - const theme = useTheme(); - - const initial = { opacity: 0 }; - const animate = { opacity: 1 }; - const transition = { - ease: 'linear', - duration: theme.animation.duration[duration], - }; - - return ( - - {children} - - ); -}; +}: AnimatedEaseInProps) => ( +
+ {children} +
+); diff --git a/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss new file mode 100644 index 0000000000..ded2fe616b --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss @@ -0,0 +1,37 @@ +.panel { + --animated-ease-in-out-duration: #{duration(normal)}; + + overflow: hidden; + opacity: 1; + height: var(--collapsible-panel-height); + margin-top: var(--animated-ease-in-out-margin-top, 0); + margin-bottom: var(--animated-ease-in-out-margin-bottom, 0); + transition: + height var(--animated-ease-in-out-duration) ease-in-out, + opacity var(--animated-ease-in-out-duration) ease-in-out, + margin var(--animated-ease-in-out-duration) ease-in-out; + + &[data-duration='instant'] { + --animated-ease-in-out-duration: #{duration(instant)}; + } + + &[data-duration='fast'] { + --animated-ease-in-out-duration: #{duration(fast)}; + } + + &[data-duration='slow'] { + --animated-ease-in-out-duration: #{duration(slow)}; + } + + &[data-starting-style] { + height: 0; + opacity: 0; + } + + &[data-ending-style] { + height: 0; + opacity: 0; + margin-top: 0; + margin-bottom: 0; + } +} diff --git a/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss.d.ts new file mode 100644 index 0000000000..25ca51db31 --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.module.scss.d.ts @@ -0,0 +1,4 @@ +declare const classNames: { + readonly panel: 'panel'; +}; +export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.tsx b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.tsx index fd2ffdd636..7f691187f0 100644 --- a/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.tsx +++ b/packages/twenty-ui/src/layout/AnimatedEaseInOut/AnimatedEaseInOut.tsx @@ -1,6 +1,7 @@ -import { AnimatePresence, motion } from 'framer-motion'; +import { Collapsible } from '@base-ui/react/collapsible'; import { type AnimationDuration } from '@ui/theme'; -import { useTheme } from '@ui/theme-constants'; + +import styles from './AnimatedEaseInOut.module.scss'; type AnimatedEaseInOutProps = { isOpen: boolean; @@ -17,30 +18,19 @@ export const AnimatedEaseInOut = ({ marginBottom, marginTop, duration = 'normal', - initial = true, -}: AnimatedEaseInOutProps) => { - const theme = useTheme(); - - return ( - - {isOpen && ( - - {children} - - )} - - ); -}; +}: AnimatedEaseInOutProps) => ( + + + {children} + + +); diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss new file mode 100644 index 0000000000..30589138f4 --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss @@ -0,0 +1,46 @@ +.panel { + --animated-expandable-opacity-duration: #{duration(normal)}; + --animated-expandable-size-duration: #{duration(normal)}; + + opacity: 1; + transition: opacity var(--animated-expandable-opacity-duration) ease-in-out; + + &[data-dimension='height'] { + height: var(--collapsible-panel-height); + transition: + height var(--animated-expandable-size-duration) ease-in-out, + opacity var(--animated-expandable-opacity-duration) ease-in-out; + } + + &[data-dimension='width'] { + width: var(--collapsible-panel-width); + transition: + width var(--animated-expandable-size-duration) ease-in-out, + opacity var(--animated-expandable-opacity-duration) ease-in-out; + } + + &[data-starting-style], + &[data-ending-style] { + opacity: 0; + } + + &[data-dimension='height'][data-starting-style], + &[data-dimension='height'][data-ending-style] { + height: 0; + } + + &[data-dimension='width'][data-starting-style], + &[data-dimension='width'][data-ending-style] { + width: 0; + } +} + +.contained { + overflow: hidden; + display: flex; + flex-direction: column; + + &[data-dimension='height'] { + width: 100%; + } +} diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss.d.ts new file mode 100644 index 0000000000..6972db8604 --- /dev/null +++ b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.module.scss.d.ts @@ -0,0 +1,5 @@ +declare const classNames: { + readonly panel: 'panel'; + readonly contained: 'contained'; +}; +export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.tsx b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.tsx index 57b13a528a..43f7b8580b 100644 --- a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.tsx +++ b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/AnimatedExpandableContainer.tsx @@ -1,16 +1,14 @@ +import { Collapsible } from '@base-ui/react/collapsible'; +import { clsx } from 'clsx'; + import { type AnimationDimension } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDimension'; -import { type AnimationDurationObject } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDurationObject'; import { type AnimationDurations } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDurations'; import { type AnimationMode } from '@ui/layout/AnimatedExpandableContainer/types/AnimationMode'; -import { type AnimationSize } from '@ui/layout/AnimatedExpandableContainer/types/AnimationSize'; -import { getExpandableAnimationConfig } from '@ui/layout/AnimatedExpandableContainer/utils/getExpandableAnimationConfig'; -import { useTheme } from '@ui/theme-constants'; -import { AnimatePresence, motion } from 'framer-motion'; -import { type ReactNode, useRef, useState } from 'react'; -import { isDefined } from '@ui/utilities/utils/isDefined'; + +import styles from './AnimatedExpandableContainer.module.scss'; type AnimatedExpandableContainerProps = { - children: ReactNode; + children: React.ReactNode; isExpanded: boolean; dimension?: AnimationDimension; animationDurations?: AnimationDurations; @@ -24,66 +22,25 @@ export const AnimatedExpandableContainer = ({ isExpanded, dimension = 'height', animationDurations = 'default', - mode = 'scroll-height', containAnimation = true, - initial = true, }: AnimatedExpandableContainerProps) => { - const theme = useTheme(); - const contentRef = useRef(null); - const [size, setSize] = useState(0); - - const normalDuration = theme.animation.duration.normal; - - const actualDurations: AnimationDurationObject = + const durationStyle = animationDurations === 'default' - ? { - opacity: normalDuration, - size: normalDuration, - } - : animationDurations; - - const updateSize = () => { - if ( - mode === 'scroll-height' && - dimension === 'height' && - isDefined(contentRef.current) - ) { - setSize(contentRef.current.scrollHeight); - } - }; - - const motionAnimationVariants = getExpandableAnimationConfig( - isExpanded, - dimension, - actualDurations.opacity, - actualDurations.size, - mode === 'fit-content' ? 'fit-content' : size, - ); + ? undefined + : ({ + '--animated-expandable-opacity-duration': `${animationDurations.opacity}s`, + '--animated-expandable-size-duration': `${animationDurations.size}s`, + } as React.CSSProperties); return ( - - {isExpanded && ( - - {children} - - )} - + + + {children} + + ); }; diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getCommonStyles.ts b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getCommonStyles.ts deleted file mode 100644 index 3a98a4f7c1..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getCommonStyles.ts +++ /dev/null @@ -1,12 +0,0 @@ -import { type AnimationDimension } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDimension'; -import { getTransitionValues } from '@ui/layout/AnimatedExpandableContainer/utils/getTransitionValues'; - -export const getCommonStyles = ( - dimension: AnimationDimension, - opacityDuration: number, - sizeDuration: number, -) => ({ - opacity: 0, - [dimension]: 0, - ...getTransitionValues(dimension, opacityDuration, sizeDuration), -}); diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getExpandableAnimationConfig.ts b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getExpandableAnimationConfig.ts deleted file mode 100644 index 6727db613d..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getExpandableAnimationConfig.ts +++ /dev/null @@ -1,30 +0,0 @@ -import { type AnimationDimension } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDimension'; -import { type AnimationSize } from '@ui/layout/AnimatedExpandableContainer/types/AnimationSize'; -import { getCommonStyles } from '@ui/layout/AnimatedExpandableContainer/utils/getCommonStyles'; -import { getTransitionValues } from '@ui/layout/AnimatedExpandableContainer/utils/getTransitionValues'; - -export const getExpandableAnimationConfig = ( - isExpanded: boolean, - dimension: AnimationDimension, - opacityDuration: number, - sizeDuration: number, - size: AnimationSize, -) => ({ - initial: { - ...getCommonStyles(dimension, opacityDuration, sizeDuration), - }, - animate: { - opacity: 1, - [dimension]: isExpanded - ? size === 'fit-content' - ? 'fit-content' - : dimension === 'width' - ? '100%' - : size - : 0, - ...getTransitionValues(dimension, opacityDuration, sizeDuration), - }, - exit: { - ...getCommonStyles(dimension, opacityDuration, sizeDuration), - }, -}); diff --git a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getTransitionValues.ts b/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getTransitionValues.ts deleted file mode 100644 index abf6d5b654..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedExpandableContainer/utils/getTransitionValues.ts +++ /dev/null @@ -1,18 +0,0 @@ -import { type AnimationDimension } from '@ui/layout/AnimatedExpandableContainer/types/AnimationDimension'; - -export const getTransitionValues = ( - dimension: AnimationDimension, - opacityDuration: number, - sizeDuration: number, -) => ({ - transition: { - opacity: { - duration: opacityDuration, - ease: 'easeInOut', - }, - [dimension]: { - duration: sizeDuration, - ease: 'easeInOut', - }, - }, -}); diff --git a/packages/twenty-ui/src/layout/AnimatedFadeOut/AnimatedFadeOut.tsx b/packages/twenty-ui/src/layout/AnimatedFadeOut/AnimatedFadeOut.tsx deleted file mode 100644 index 44d623735d..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedFadeOut/AnimatedFadeOut.tsx +++ /dev/null @@ -1,42 +0,0 @@ -import { AnimatePresence, motion } from 'framer-motion'; -import { type AnimationDuration } from '@ui/theme'; -import { useTheme } from '@ui/theme-constants'; - -type AnimatedFadeOutProps = { - isOpen: boolean; - children: React.ReactNode; - duration?: AnimationDuration; - marginBottom?: string; - marginTop?: string; -}; - -export const AnimatedFadeOut = ({ - isOpen, - children, - duration = 'normal', - marginBottom, - marginTop, -}: AnimatedFadeOutProps) => { - const theme = useTheme(); - - return ( - - {isOpen && ( - - {children} - - )} - - ); -}; diff --git a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss index dd8851c0af..d6735352e0 100644 --- a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss +++ b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss @@ -11,4 +11,17 @@ inset: 0; justify-content: center; position: absolute; + transition: + opacity duration(fast) ease-in-out, + transform duration(fast) ease-in-out; +} + +.visible { + opacity: 1; + transform: scale(1); +} + +.hidden { + opacity: 0; + transform: scale(0.85); } diff --git a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss.d.ts index defbe83142..1e39e6823d 100644 --- a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss.d.ts +++ b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.module.scss.d.ts @@ -1,5 +1,7 @@ declare const classNames: { readonly container: 'container'; readonly layer: 'layer'; + readonly visible: 'visible'; + readonly hidden: 'hidden'; }; export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.tsx b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.tsx index bf0fa38706..7e59c40d3f 100644 --- a/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.tsx +++ b/packages/twenty-ui/src/layout/AnimatedIconCrossfade/AnimatedIconCrossfade.tsx @@ -1,5 +1,4 @@ -import { motion } from 'framer-motion'; -import React from 'react'; +import { clsx } from 'clsx'; import { type IconComponent } from '@ui/icon/types/IconComponent'; import { useTheme } from '@ui/theme-constants'; @@ -32,34 +31,22 @@ export const AnimatedIconCrossfade = ({ } as React.CSSProperties } > - - - +
- +
); }; diff --git a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss index d32734b929..837dd539cf 100644 --- a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss +++ b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss @@ -1,5 +1,40 @@ .container { + --animated-rotate-duration: #{duration(fast)}; + align-items: center; display: flex; justify-content: center; + animation: animatedRotateIn var(--animated-rotate-duration) ease; + + &[data-duration='instant'] { + --animated-rotate-duration: #{duration(instant)}; + } + + &[data-duration='normal'] { + --animated-rotate-duration: #{duration(normal)}; + } + + &[data-duration='slow'] { + --animated-rotate-duration: #{duration(slow)}; + } +} + +.animateOnHover { + transition: transform duration(fast) ease; + + &:hover { + transform: rotate(45deg); + } +} + +@keyframes animatedRotateIn { + from { + opacity: 0; + transform: rotate(-90deg); + } + + to { + opacity: 1; + transform: rotate(0deg); + } } diff --git a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss.d.ts b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss.d.ts index 8df71b4dea..a453a68ad4 100644 --- a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss.d.ts +++ b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.module.scss.d.ts @@ -1,4 +1,5 @@ declare const classNames: { readonly container: 'container'; + readonly animateOnHover: 'animateOnHover'; }; export default classNames; diff --git a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.tsx b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.tsx index e2ce2a700c..f49f286f71 100644 --- a/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.tsx +++ b/packages/twenty-ui/src/layout/AnimatedRotate/AnimatedRotate.tsx @@ -1,14 +1,12 @@ -import { type HTMLMotionProps, motion } from 'framer-motion'; -import { type AnimationDuration } from '@ui/theme'; -import { useTheme } from '@ui/theme-constants'; +import { clsx } from 'clsx'; import { type JSX } from 'react'; +import { type AnimationDuration } from '@ui/theme'; + import styles from './AnimatedRotate.module.scss'; -type AnimatedRotateProps = Omit< - HTMLMotionProps<'div'>, - 'initial' | 'animate' | 'transition' | 'exit' -> & { +type AnimatedRotateProps = { + children?: React.ReactNode; duration?: AnimationDuration; animateOnHover?: boolean; }; @@ -17,35 +15,11 @@ export const AnimatedRotate = ({ children, duration = 'fast', animateOnHover, -}: AnimatedRotateProps): JSX.Element => { - const theme = useTheme(); - - const initial = { opacity: 0, rotate: -90 }; - const animate = { opacity: 1, rotate: 0 }; - const exit = { opacity: 0, rotate: 90 }; - const transition = { - duration: theme.animation.duration[duration], - }; - - return ( - - {children} - - ); -}; +}: AnimatedRotateProps): JSX.Element => ( +
+ {children} +
+); diff --git a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss b/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss deleted file mode 100644 index 0c3791924f..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.module.scss +++ /dev/null @@ -1,8 +0,0 @@ -.container { - display: flex; - overflow: hidden; -} - -.word { - white-space: pre; -} diff --git a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.tsx b/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.tsx deleted file mode 100644 index 1b82b35bc2..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedTextWord/AnimatedTextWord.tsx +++ /dev/null @@ -1,69 +0,0 @@ -import { motion } from 'framer-motion'; -import React, { useMemo } from 'react'; - -import styles from './AnimatedTextWord.module.scss'; - -type AnimatedTextWordProps = Omit< - React.ComponentProps, - 'children' -> & { - text: string; -}; - -const containerAnimation = { - hidden: { opacity: 0 }, - visible: (i = 1) => ({ - opacity: 1, - transition: { staggerChildren: 0.12, delayChildren: 0.04 * i }, - }), -}; - -const childAnimation = { - visible: { - opacity: 1, - x: 0, - transition: { - type: 'spring', - damping: 12, - stiffness: 100, - }, - }, - hidden: { - opacity: 0, - x: 20, - transition: { - type: 'spring', - damping: 12, - stiffness: 100, - }, - }, -}; - -export const AnimatedTextWord = ({ text = '' }: AnimatedTextWordProps) => { - const words = useMemo(() => { - const words = text.split(' '); - - return words.map((value, index) => - index === words.length - 1 ? value : value + ' ', - ); - }, [text]); - - return ( - - {words.map((word, index) => ( - - {word} - - ))} - - ); -}; diff --git a/packages/twenty-ui/src/layout/AnimatedTranslation/AnimatedTranslation.tsx b/packages/twenty-ui/src/layout/AnimatedTranslation/AnimatedTranslation.tsx deleted file mode 100644 index 934294f0d6..0000000000 --- a/packages/twenty-ui/src/layout/AnimatedTranslation/AnimatedTranslation.tsx +++ /dev/null @@ -1,33 +0,0 @@ -import { motion } from 'framer-motion'; -import { useTheme } from '@ui/theme-constants'; - -type AnimatedTranslationProps = { - children: React.ReactNode; -}; - -export const AnimatedTranslation = ({ children }: AnimatedTranslationProps) => { - const theme = useTheme(); - - return ( - - {children} - - ); -}; diff --git a/packages/twenty-ui/src/layout/index.ts b/packages/twenty-ui/src/layout/index.ts index 198245d635..66006c0105 100644 --- a/packages/twenty-ui/src/layout/index.ts +++ b/packages/twenty-ui/src/layout/index.ts @@ -17,14 +17,8 @@ export type { AnimationDurationObject } from './AnimatedExpandableContainer/type export type { AnimationDurations } from './AnimatedExpandableContainer/types/AnimationDurations'; export type { AnimationMode } from './AnimatedExpandableContainer/types/AnimationMode'; export type { AnimationSize } from './AnimatedExpandableContainer/types/AnimationSize'; -export { getCommonStyles } from './AnimatedExpandableContainer/utils/getCommonStyles'; -export { getExpandableAnimationConfig } from './AnimatedExpandableContainer/utils/getExpandableAnimationConfig'; -export { getTransitionValues } from './AnimatedExpandableContainer/utils/getTransitionValues'; -export { AnimatedFadeOut } from './AnimatedFadeOut/AnimatedFadeOut'; export { AnimatedIconCrossfade } from './AnimatedIconCrossfade/AnimatedIconCrossfade'; export { AnimatedRotate } from './AnimatedRotate/AnimatedRotate'; -export { AnimatedTextWord } from './AnimatedTextWord/AnimatedTextWord'; -export { AnimatedTranslation } from './AnimatedTranslation/AnimatedTranslation'; export { AutogrowWrapper } from './AutogrowWrapper/AutogrowWrapper'; export { HorizontalSeparator } from './HorizontalSeparator/HorizontalSeparator'; export { useResizeHandle } from './ResizeHandle/hooks/useResizeHandle'; diff --git a/packages/twenty-ui/src/surfaces/CardContent/CardContent.tsx b/packages/twenty-ui/src/surfaces/CardContent/CardContent.tsx index 7227520f82..88a181fd07 100644 --- a/packages/twenty-ui/src/surfaces/CardContent/CardContent.tsx +++ b/packages/twenty-ui/src/surfaces/CardContent/CardContent.tsx @@ -1,6 +1,5 @@ import { clsx } from 'clsx'; -import { type HTMLMotionProps, motion } from 'framer-motion'; -import { type ReactNode } from 'react'; +import { type ComponentPropsWithoutRef, type ReactNode } from 'react'; import styles from './CardContent.module.scss'; @@ -10,7 +9,7 @@ type CardContentProps = { divider?: boolean; isClickable?: boolean; hasHoverHighlight?: boolean; -} & Omit, 'theme'>; +} & ComponentPropsWithoutRef<'div'>; export const CardContent = ({ children, @@ -21,7 +20,7 @@ export const CardContent = ({ ...rest }: CardContentProps) => { return ( - {children} - + ); }; diff --git a/yarn.lock b/yarn.lock index 0aa4279552..00e5c4b336 100644 --- a/yarn.lock +++ b/yarn.lock @@ -53629,9 +53629,7 @@ __metadata: "@vitejs/plugin-react-swc": "npm:^4.3.1" "@vitest/browser-playwright": "npm:^4.1.0" clsx: "npm:^2.1.1" - date-fns: "npm:^4.4.0" esbuild: "npm:^0.28.1" - framer-motion: "npm:^11.18.0" glob: "npm:^11.1.0" jest: "npm:29.7.0" jest-environment-jsdom: "npm:30.0.0-beta.3"