Skip to content

Commit

Permalink
feat: table record group
Browse files Browse the repository at this point in the history
  • Loading branch information
magrinj committed Nov 28, 2024
1 parent 812ed6e commit f9f2d8d
Show file tree
Hide file tree
Showing 14 changed files with 419 additions and 102 deletions.
Original file line number Diff line number Diff line change
@@ -1,12 +1,13 @@
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordGroupIdsComponentState } from '@/object-record/record-group/states/recordGroupIdsComponentState';
import { RecordGroupDefinition } from '@/object-record/record-group/types/RecordGroupDefinition';

import { createComponentSelectorV2 } from '@/ui/utilities/state/component-state/utils/createComponentSelectorV2';
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
import { isDefined } from '~/utils/isDefined';

export const hiddenRecordGroupIdsComponentSelector = createComponentSelectorV2<
string[]
RecordGroupDefinition['id'][]
>({
key: 'hiddenRecordGroupIdsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon
import { isDefined } from '~/utils/isDefined';

export const visibleRecordGroupIdsComponentSelector = createComponentSelectorV2<
string[]
RecordGroupDefinition['id'][]
>({
key: 'visibleRecordGroupIdsComponentSelector',
componentInstanceContext: ViewComponentInstanceContext,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ export const sortRecordGroupDefinitions = (
recordGroupSort: RecordGroupSort,
) => {
const visibleRecordGroups = recordGroupDefinitions.filter(
(boardGroup) => boardGroup.isVisible,
(recordGroup) => recordGroup.isVisible,
);

const compareAlphabetical = (a: string, b: string, reverse = false) => {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,36 +2,63 @@ import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useC
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { recordIndexRowIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRowIdsByGroupComponentFamilyState';
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
import { AnimatePresence, motion } from 'framer-motion';
import { useMemo } from 'react';
import { isDefined } from '~/utils/isDefined';

const MotionRecordTableRow = motion(RecordTableRow);

export const RecordTableRecordGroupRows = () => {
const recordGroupId = useCurrentRecordGroupId();
const currentRecordGroupId = useCurrentRecordGroupId();

const allRowIds = useRecoilComponentValueV2(
recordIndexAllRowIdsComponentState,
);

const recordGroupRowIds = useRecoilComponentFamilyValueV2(
recordIndexRowIdsByGroupComponentFamilyState,
recordGroupId,
currentRecordGroupId,
);

const isRecordGroupTableSectionToggled = useRecoilComponentFamilyValueV2(
isRecordGroupTableSectionToggledComponentState,
currentRecordGroupId,
);

const rowIndexMap = useMemo(
() => new Map(allRowIds.map((id, index) => [id, index])),
[allRowIds],
);

return recordGroupRowIds.map((recordId) => {
const rowIndex = rowIndexMap.get(recordId);
// TODO: Animation is not working, find a way to make it works
const variants = {
hidden: { height: 0, opacity: 0 },
visible: { height: 'auto', opacity: 1 },
};

if (!rowIndex) {
throw new Error(`Row index for record id ${recordId} not found`);
}
return (
<AnimatePresence>
{isRecordGroupTableSectionToggled &&
recordGroupRowIds.map((recordId) => {
const rowIndex = rowIndexMap.get(recordId);

return (
<RecordTableRow key={recordId} recordId={recordId} rowIndex={rowIndex} />
);
});
if (!isDefined(rowIndex)) {
return null;
}

return (
<MotionRecordTableRow
key={recordId}
recordId={recordId}
rowIndex={rowIndex}
isPendingRow={!isRecordGroupTableSectionToggled}
variants={variants}
/>
);
})}
</AnimatePresence>
);
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
import styled from '@emotion/styled';
import { MOBILE_VIEWPORT } from 'twenty-ui';

const StyledTbody = styled.tbody`
&.first-columns-sticky {
td:nth-of-type(1) {
position: sticky;
left: 0;
z-index: 5;
transition: 0.3s ease;
}
td:nth-of-type(2) {
position: sticky;
left: 11px;
z-index: 5;
transition: 0.3s ease;
}
td:nth-of-type(3) {
position: sticky;
left: 43px;
z-index: 5;
transition: 0.3s ease;
@media (max-width: ${MOBILE_VIEWPORT}px) {
& [data-testid='editable-cell-display-mode'] {
[data-testid='tooltip'] {
display: none;
}
[data-testid='chip'] {
gap: 0;
}
}
}
&::after {
content: '';
position: absolute;
top: -1px;
height: calc(100% + 2px);
width: 4px;
right: 0px;
box-shadow: ${({ theme }) => theme.boxShadow.light};
clip-path: inset(0px -4px 0px 0px);
}
}
}
`;

export const RecordTableBody = StyledTbody;
Original file line number Diff line number Diff line change
@@ -1,80 +1,36 @@
import { Theme } from '@emotion/react';
import { RecordTableBody } from '@/object-record/record-table/record-table-body/components/RecordTableBody';
import { Droppable } from '@hello-pangea/dnd';
import { styled } from '@linaria/react';
import { ReactNode, useContext, useState } from 'react';
import { MOBILE_VIEWPORT, ThemeContext } from 'twenty-ui';
import { ReactNode, useState } from 'react';
import { v4 } from 'uuid';

const StyledTbody = styled.tbody<{
theme: Theme;
}>`
&.first-columns-sticky {
td:nth-of-type(1) {
position: sticky;
left: 0;
z-index: 5;
transition: 0.3s ease;
}
td:nth-of-type(2) {
position: sticky;
left: 11px;
z-index: 5;
transition: 0.3s ease;
}
td:nth-of-type(3) {
position: sticky;
left: 43px;
z-index: 5;
transition: 0.3s ease;
@media (max-width: ${MOBILE_VIEWPORT}px) {
& [data-testid='editable-cell-display-mode'] {
[data-testid='tooltip'] {
display: none;
}
[data-testid='chip'] {
gap: 0;
}
}
}
&::after {
content: '';
position: absolute;
top: -1px;
height: calc(100% + 2px);
width: 4px;
right: 0px;
box-shadow: ${({ theme }) => theme.boxShadow.light};
clip-path: inset(0px -4px 0px 0px);
}
}
}
`;
type RecordTableBodyDroppableProps = {
children: ReactNode;
recordGroupId?: string;
isDropDisabled?: boolean;
};

export const RecordTableBodyDroppable = ({
children,
}: {
children: ReactNode;
}) => {
recordGroupId,
isDropDisabled,
}: RecordTableBodyDroppableProps) => {
const [v4Persistable] = useState(v4());

const { theme } = useContext(ThemeContext);

return (
<Droppable droppableId={v4Persistable}>
<Droppable
droppableId={recordGroupId ?? v4Persistable}
isDropDisabled={isDropDisabled}
>
{(provided) => (
<StyledTbody
id="record-table-body"
theme={theme}
<RecordTableBody
id={`record-table-body${recordGroupId ? `-${recordGroupId}` : ''}`}
ref={provided.innerRef}
// eslint-disable-next-line react/jsx-props-no-spreading
{...provided.droppableProps}
>
{children}
{provided.placeholder}
</StyledTbody>
</RecordTableBody>
)}
</Droppable>
);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,108 @@
import { DragDropContext, DropResult } from '@hello-pangea/dnd';
import { ReactNode, useContext } from 'react';
import { useRecoilCallback, useSetRecoilState } from 'recoil';

import { useUpdateOneRecord } from '@/object-record/hooks/useUpdateOneRecord';
import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/states/recordGroupDefinitionFamilyState';
import { recordIndexAllRowIdsComponentState } from '@/object-record/record-index/states/recordIndexAllRowIdsComponentState';
import { RecordTableContext } from '@/object-record/record-table/contexts/RecordTableContext';
import { useComputeNewRowPosition } from '@/object-record/record-table/hooks/useComputeNewRowPosition';
import { isRemoveSortingModalOpenState } from '@/object-record/record-table/states/isRemoveSortingModalOpenState';
import { useRecoilComponentCallbackStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackStateV2';
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
import { useGetCurrentView } from '@/views/hooks/useGetCurrentView';
import { isDefined } from '~/utils/isDefined';

export const RecordTableBodyRecordGroupDragDropContext = ({
children,
}: {
children: ReactNode;
}) => {
const { objectNameSingular, recordTableId, objectMetadataItem } =
useContext(RecordTableContext);

const { updateOneRecord: updateOneRow } = useUpdateOneRecord({
objectNameSingular,
});

const recordIndexAllRowIdsState = useRecoilComponentCallbackStateV2(
recordIndexAllRowIdsComponentState,
);

const { currentViewWithCombinedFiltersAndSorts } =
useGetCurrentView(recordTableId);

const viewSorts = currentViewWithCombinedFiltersAndSorts?.viewSorts || [];

const setIsRemoveSortingModalOpenState = useSetRecoilState(
isRemoveSortingModalOpenState,
);

const computeNewRowPosition = useComputeNewRowPosition();

const handleDragEnd = useRecoilCallback(
({ snapshot }) =>
(result: DropResult) => {
const tableAllRowIds = getSnapshotValue(
snapshot,
recordIndexAllRowIdsState,
);

const recordGroupId = result.destination?.droppableId;

if (!isDefined(recordGroupId)) {
throw new Error('Record group id is not defined');
}

const recordGroup = getSnapshotValue(
snapshot,
recordGroupDefinitionFamilyState(recordGroupId),
);

if (!isDefined(recordGroup)) {
throw new Error('Record group is not defined');
}

const fieldMetadata = objectMetadataItem.fields.find(
(field) => field.id === recordGroup.fieldMetadataId,
);

if (!isDefined(fieldMetadata)) {
throw new Error('Field metadata is not defined');
}

if (viewSorts.length > 0) {
setIsRemoveSortingModalOpenState(true);
return;
}

console.log('DRAP - DROP: ', result, recordGroup);

const computeResult = computeNewRowPosition(result, tableAllRowIds);

if (!isDefined(computeResult)) {
return;
}

updateOneRow({
idToUpdate: computeResult.draggedRecordId,
updateOneRecordInput: {
position: computeResult.newPosition,
[fieldMetadata.name]: recordGroup.value,
},
});
},
[
objectMetadataItem,
computeNewRowPosition,
setIsRemoveSortingModalOpenState,
recordIndexAllRowIdsState,
updateOneRow,
viewSorts.length,
],
);

return (
<DragDropContext onDragEnd={handleDragEnd}>{children}</DragDropContext>
);
};
Loading

0 comments on commit f9f2d8d

Please sign in to comment.