Introduce RecordCard (#14497)

<img width="1919" height="956" alt="image"
src="https://github.com/user-attachments/assets/5c55af89-49b1-4173-8b52-2f02b849bc5c"
/>
This commit is contained in:
Charles Bochet
2025-09-15 13:49:36 +02:00
committed by GitHub
parent 77b6a5b29d
commit 302dd01333
5 changed files with 115 additions and 64 deletions
@@ -17,6 +17,7 @@ import { RecordBoardCardHeader } from '@/object-record/record-board/record-board
import { RECORD_BOARD_CARD_CLICK_OUTSIDE_ID } from '@/object-record/record-board/record-board-card/constants/RecordBoardCardClickOutsideId';
import { RecordBoardCardComponentInstanceContext } from '@/object-record/record-board/record-board-card/states/contexts/RecordBoardCardComponentInstanceContext';
import { RecordBoardComponentInstanceContext } from '@/object-record/record-board/states/contexts/RecordBoardComponentInstanceContext';
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
import { useOpenRecordFromIndexView } from '@/object-record/record-index/hooks/useOpenRecordFromIndexView';
import { useOpenDropdown } from '@/ui/layout/dropdown/hooks/useOpenDropdown';
import { useScrollWrapperHTMLElement } from '@/ui/utilities/scroll/hooks/useScrollWrapperHTMLElement';
@@ -31,66 +32,6 @@ import { InView, useInView } from 'react-intersection-observer';
import { AnimatedEaseInOut } from 'twenty-ui/utilities';
import { useDebouncedCallback } from 'use-debounce';
const StyledBoardCard = styled.div<{
isDragging?: boolean;
isSecondaryDragged?: boolean;
isPrimaryMultiDrag?: boolean;
}>`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
cursor: pointer;
${({ isSecondaryDragged }) =>
isSecondaryDragged &&
`
opacity: 0.3;
`}
&[data-selected='true'] {
background-color: ${({ theme }) => theme.accent.quaternary};
}
&[data-focused='true'] {
background-color: ${({ theme }) => theme.background.tertiary};
}
&[data-active='true'] {
background-color: ${({ theme }) => theme.accent.quaternary};
border: 1px solid ${({ theme }) => theme.adaptiveColors.blue3};
}
&:hover {
border: 1px solid ${({ theme }) => theme.border.color.strong};
&[data-active='true'] {
border: 1px solid ${({ theme }) => theme.adaptiveColors.blue3};
}
}
.checkbox-container {
transition: all ease-in-out 160ms;
opacity: 0;
}
&[data-selected='true'] .checkbox-container {
opacity: 1;
}
&:hover .checkbox-container {
opacity: 1;
}
.compact-icon-container {
transition: all ease-in-out 160ms;
opacity: 0;
}
&:hover .compact-icon-container {
opacity: 1;
}
`;
const StyledCardContainer = styled.div<{ isPrimaryMultiDrag?: boolean }>`
position: relative;
${({ isPrimaryMultiDrag }) =>
@@ -243,7 +184,7 @@ export const RecordBoardCard = () => {
/>
))}
<StyledBoardCard
<RecordCard
ref={cardRef}
data-selected={isCurrentCardSelected}
data-focused={isCurrentCardFocused}
@@ -263,7 +204,7 @@ export const RecordBoardCard = () => {
>
<RecordBoardCardBody />
</AnimatedEaseInOut>
</StyledBoardCard>
</RecordCard>
</StyledCardContainer>
<RecordBoardCardCellHoveredPortal />
<RecordBoardCardCellEditModePortal />
@@ -0,0 +1,28 @@
import { RecordCard } from '@/object-record/record-card/components/RecordCard';
import { recordStoreFamilyState } from '@/object-record/record-store/states/recordStoreFamilyState';
import styled from '@emotion/styled';
import { useRecoilValue } from 'recoil';
const StyledContainer = styled.div`
display: flex;
`;
const StyledRecordCard = styled(RecordCard)`
margin-bottom: ${({ theme }) => theme.spacing(1)};
`;
type RecordCalendarMonthBodyCardProps = {
recordId: string;
};
export const RecordCalendarMonthBodyCard = ({
recordId,
}: RecordCalendarMonthBodyCardProps) => {
const record = useRecoilValue(recordStoreFamilyState(recordId));
return (
<StyledContainer>
<StyledRecordCard>{record?.id}</StyledRecordCard>
</StyledContainer>
);
};
@@ -1,3 +1,4 @@
import { RecordCalendarMonthBodyCard } from '@/object-record/record-calendar/month/components/RecordCalendarMonthBodyCard';
import { calendarDayRecordIdsComponentFamilySelector } from '@/object-record/record-calendar/states/selectors/calendarDayRecordsComponentFamilySelector';
import { useRecoilComponentFamilyValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValue';
import styled from '@emotion/styled';
@@ -5,6 +6,20 @@ import styled from '@emotion/styled';
const StyledContainer = styled.div`
display: flex;
width: calc(100% / 7);
flex-direction: column;
min-height: 122px;
`;
const StyledDayHeader = styled.div`
display: flex;
width: 100%;
text-align: right;
justify-content: flex-end;
`;
const StyledCardsContainer = styled.div`
display: flex;
flex-direction: column;
`;
type RecordCalendarMonthBodyDayProps = {
@@ -21,7 +36,12 @@ export const RecordCalendarMonthBodyDay = ({
return (
<StyledContainer>
{day.getDate()}: {recordIds.length}
<StyledDayHeader>{day.getDate()}</StyledDayHeader>
<StyledCardsContainer>
{recordIds.slice(0, 5).map((recordId) => (
<RecordCalendarMonthBodyCard key={recordId} recordId={recordId} />
))}
</StyledCardsContainer>
</StyledContainer>
);
};
@@ -5,7 +5,6 @@ import { eachDayOfInterval, endOfWeek } from 'date-fns';
const StyledContainer = styled.div`
display: flex;
min-height: 122px;
`;
type RecordCalendarMonthBodyWeekProps = {
@@ -0,0 +1,63 @@
import styled from '@emotion/styled';
const StyledBoardCard = styled.div<{
isDragging?: boolean;
isSecondaryDragged?: boolean;
isPrimaryMultiDrag?: boolean;
}>`
background-color: ${({ theme }) => theme.background.secondary};
border: 1px solid ${({ theme }) => theme.border.color.medium};
border-radius: ${({ theme }) => theme.border.radius.sm};
color: ${({ theme }) => theme.font.color.primary};
cursor: pointer;
${({ isSecondaryDragged }) =>
isSecondaryDragged &&
`
opacity: 0.3;
`}
&[data-selected='true'] {
background-color: ${({ theme }) => theme.accent.quaternary};
}
&[data-focused='true'] {
background-color: ${({ theme }) => theme.background.tertiary};
}
&[data-active='true'] {
background-color: ${({ theme }) => theme.accent.quaternary};
border: 1px solid ${({ theme }) => theme.adaptiveColors.blue3};
}
&:hover {
border: 1px solid ${({ theme }) => theme.border.color.strong};
&[data-active='true'] {
border: 1px solid ${({ theme }) => theme.adaptiveColors.blue3};
}
}
.checkbox-container {
transition: all ease-in-out 160ms;
opacity: 0;
}
&[data-selected='true'] .checkbox-container {
opacity: 1;
}
&:hover .checkbox-container {
opacity: 1;
}
.compact-icon-container {
transition: all ease-in-out 160ms;
opacity: 0;
}
&:hover .compact-icon-container {
opacity: 1;
}
`;
export { StyledBoardCard as RecordCard };