Show Header in RecordTable on empty state and show groups in Group By views (#11416)
Closes #11298 --------- Co-authored-by: Félix Malfait <felix@twenty.com>
This commit is contained in:
committed by
GitHub
parent
07b25a2aad
commit
6bc18960c9
@ -1,36 +1,17 @@
|
|||||||
import styled from '@emotion/styled';
|
|
||||||
import { isNonEmptyString } from '@sniptt/guards';
|
import { isNonEmptyString } from '@sniptt/guards';
|
||||||
|
import { useRef } from 'react';
|
||||||
|
|
||||||
import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector';
|
import { hasRecordGroupsComponentSelector } from '@/object-record/record-group/states/selectors/hasRecordGroupsComponentSelector';
|
||||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||||
import { RecordTableStickyBottomEffect } from '@/object-record/record-table/components/RecordTableStickyBottomEffect';
|
import { RecordTableBodyEffectsWrapper } from '@/object-record/record-table/components/RecordTableBodyEffectsWrapper';
|
||||||
import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect';
|
import { RecordTableContent } from '@/object-record/record-table/components/RecordTableContent';
|
||||||
|
import { RecordTableEmpty } from '@/object-record/record-table/components/RecordTableEmpty';
|
||||||
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
import { RECORD_TABLE_CLICK_OUTSIDE_LISTENER_ID } from '@/object-record/record-table/constants/RecordTableClickOutsideListenerId';
|
||||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
|
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect';
|
|
||||||
import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody';
|
|
||||||
import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect';
|
|
||||||
import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects';
|
|
||||||
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
|
|
||||||
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
|
||||||
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
||||||
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
|
||||||
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
import { useClickOutsideListener } from '@/ui/utilities/pointer-event/hooks/useClickOutsideListener';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useRef } from 'react';
|
|
||||||
|
|
||||||
const StyledTable = styled.table`
|
|
||||||
border-radius: ${({ theme }) => theme.border.radius.sm};
|
|
||||||
border-spacing: 0;
|
|
||||||
table-layout: fixed;
|
|
||||||
width: 100%;
|
|
||||||
|
|
||||||
.footer-sticky tr:nth-last-of-type(2) td {
|
|
||||||
border-bottom-color: ${({ theme }) => theme.background.transparent};
|
|
||||||
}
|
|
||||||
`;
|
|
||||||
|
|
||||||
export const RecordTable = () => {
|
export const RecordTable = () => {
|
||||||
const { recordTableId, objectNameSingular } = useRecordTableContextOrThrow();
|
const { recordTableId, objectNameSingular } = useRecordTableContextOrThrow();
|
||||||
@ -56,51 +37,46 @@ export const RecordTable = () => {
|
|||||||
recordTableId,
|
recordTableId,
|
||||||
);
|
);
|
||||||
|
|
||||||
const recordTableIsEmpty =
|
|
||||||
!isRecordTableInitialLoading && allRecordIds.length === 0;
|
|
||||||
|
|
||||||
const { resetTableRowSelection, setRowSelected } = useRecordTable({
|
const { resetTableRowSelection, setRowSelected } = useRecordTable({
|
||||||
recordTableId,
|
recordTableId,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
const recordTableIsEmpty =
|
||||||
|
!isRecordTableInitialLoading && allRecordIds.length === 0;
|
||||||
|
|
||||||
if (!isNonEmptyString(objectNameSingular)) {
|
if (!isNonEmptyString(objectNameSingular)) {
|
||||||
return <></>;
|
return <></>;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
const handleDragSelectionStart = () => {
|
||||||
|
resetTableRowSelection();
|
||||||
|
toggleClickOutsideListener(false);
|
||||||
|
};
|
||||||
|
|
||||||
|
const handleDragSelectionEnd = () => {
|
||||||
|
toggleClickOutsideListener(true);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
{!hasRecordGroups ? (
|
<RecordTableBodyEffectsWrapper
|
||||||
<RecordTableNoRecordGroupBodyEffect />
|
hasRecordGroups={hasRecordGroups}
|
||||||
) : (
|
tableBodyRef={tableBodyRef}
|
||||||
<RecordTableRecordGroupBodyEffects />
|
/>
|
||||||
)}
|
|
||||||
<RecordTableBodyUnselectEffect tableBodyRef={tableBodyRef} />
|
|
||||||
{recordTableIsEmpty ? (
|
{recordTableIsEmpty ? (
|
||||||
<RecordTableEmptyState />
|
<RecordTableEmpty
|
||||||
|
tableBodyRef={tableBodyRef}
|
||||||
|
hasRecordGroups={hasRecordGroups}
|
||||||
|
/>
|
||||||
) : (
|
) : (
|
||||||
<>
|
<RecordTableContent
|
||||||
<StyledTable ref={tableBodyRef}>
|
tableBodyRef={tableBodyRef}
|
||||||
<RecordTableHeader />
|
handleDragSelectionStart={handleDragSelectionStart}
|
||||||
{!hasRecordGroups ? (
|
handleDragSelectionEnd={handleDragSelectionEnd}
|
||||||
<RecordTableNoRecordGroupBody />
|
setRowSelected={setRowSelected}
|
||||||
) : (
|
hasRecordGroups={hasRecordGroups}
|
||||||
<RecordTableRecordGroupsBody />
|
/>
|
||||||
)}
|
|
||||||
<RecordTableStickyEffect />
|
|
||||||
<RecordTableStickyBottomEffect />
|
|
||||||
</StyledTable>
|
|
||||||
<DragSelect
|
|
||||||
dragSelectable={tableBodyRef}
|
|
||||||
onDragSelectionStart={() => {
|
|
||||||
resetTableRowSelection();
|
|
||||||
toggleClickOutsideListener(false);
|
|
||||||
}}
|
|
||||||
onDragSelectionChange={setRowSelected}
|
|
||||||
onDragSelectionEnd={() => {
|
|
||||||
toggleClickOutsideListener(true);
|
|
||||||
}}
|
|
||||||
/>
|
|
||||||
</>
|
|
||||||
)}
|
)}
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
|||||||
@ -0,0 +1,22 @@
|
|||||||
|
import { RecordTableBodyUnselectEffect } from '@/object-record/record-table/record-table-body/components/RecordTableBodyUnselectEffect';
|
||||||
|
import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect';
|
||||||
|
import { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects';
|
||||||
|
|
||||||
|
export interface RecordTableBodyEffectsWrapperProps {
|
||||||
|
hasRecordGroups: boolean;
|
||||||
|
tableBodyRef: React.RefObject<HTMLTableElement>;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecordTableBodyEffectsWrapper = ({
|
||||||
|
hasRecordGroups,
|
||||||
|
tableBodyRef,
|
||||||
|
}: RecordTableBodyEffectsWrapperProps) => (
|
||||||
|
<>
|
||||||
|
{hasRecordGroups ? (
|
||||||
|
<RecordTableRecordGroupBodyEffects />
|
||||||
|
) : (
|
||||||
|
<RecordTableNoRecordGroupBodyEffect />
|
||||||
|
)}
|
||||||
|
<RecordTableBodyUnselectEffect tableBodyRef={tableBodyRef} />
|
||||||
|
</>
|
||||||
|
);
|
||||||
@ -0,0 +1,42 @@
|
|||||||
|
import { RecordTableStickyBottomEffect } from '@/object-record/record-table/components/RecordTableStickyBottomEffect';
|
||||||
|
import { RecordTableStickyEffect } from '@/object-record/record-table/components/RecordTableStickyEffect';
|
||||||
|
import { StyledTable } from '@/object-record/record-table/components/RecordTableStyles';
|
||||||
|
import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBody';
|
||||||
|
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
|
||||||
|
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
||||||
|
import { DragSelect } from '@/ui/utilities/drag-select/components/DragSelect';
|
||||||
|
|
||||||
|
export interface RecordTableContentProps {
|
||||||
|
tableBodyRef: React.RefObject<HTMLTableElement>;
|
||||||
|
handleDragSelectionStart: () => void;
|
||||||
|
handleDragSelectionEnd: () => void;
|
||||||
|
setRowSelected: (rowId: string, selected: boolean) => void;
|
||||||
|
hasRecordGroups: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecordTableContent = ({
|
||||||
|
tableBodyRef,
|
||||||
|
handleDragSelectionStart,
|
||||||
|
handleDragSelectionEnd,
|
||||||
|
setRowSelected,
|
||||||
|
hasRecordGroups,
|
||||||
|
}: RecordTableContentProps) => (
|
||||||
|
<>
|
||||||
|
<StyledTable ref={tableBodyRef}>
|
||||||
|
<RecordTableHeader />
|
||||||
|
{hasRecordGroups ? (
|
||||||
|
<RecordTableRecordGroupsBody />
|
||||||
|
) : (
|
||||||
|
<RecordTableNoRecordGroupBody />
|
||||||
|
)}
|
||||||
|
<RecordTableStickyEffect />
|
||||||
|
<RecordTableStickyBottomEffect />
|
||||||
|
</StyledTable>
|
||||||
|
<DragSelect
|
||||||
|
dragSelectable={tableBodyRef}
|
||||||
|
onDragSelectionStart={handleDragSelectionStart}
|
||||||
|
onDragSelectionChange={setRowSelected}
|
||||||
|
onDragSelectionEnd={handleDragSelectionEnd}
|
||||||
|
/>
|
||||||
|
</>
|
||||||
|
);
|
||||||
@ -0,0 +1,25 @@
|
|||||||
|
import { StyledTable } from '@/object-record/record-table/components/RecordTableStyles';
|
||||||
|
import { RecordTableEmptyState } from '@/object-record/record-table/empty-state/components/RecordTableEmptyState';
|
||||||
|
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
|
||||||
|
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
||||||
|
|
||||||
|
export interface RecordTableEmptyProps {
|
||||||
|
tableBodyRef: React.RefObject<HTMLTableElement>;
|
||||||
|
hasRecordGroups: boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const RecordTableEmpty = ({
|
||||||
|
tableBodyRef,
|
||||||
|
hasRecordGroups,
|
||||||
|
}: RecordTableEmptyProps) => (
|
||||||
|
<>
|
||||||
|
<StyledTable ref={tableBodyRef}>
|
||||||
|
<RecordTableHeader />
|
||||||
|
</StyledTable>
|
||||||
|
{hasRecordGroups ? (
|
||||||
|
<RecordTableRecordGroupsBody />
|
||||||
|
) : (
|
||||||
|
<RecordTableEmptyState />
|
||||||
|
)}
|
||||||
|
</>
|
||||||
|
);
|
||||||
@ -0,0 +1,12 @@
|
|||||||
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
export const StyledTable = styled.table`
|
||||||
|
border-radius: ${({ theme }) => theme.border.radius.sm};
|
||||||
|
border-spacing: 0;
|
||||||
|
table-layout: fixed;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.footer-sticky tr:nth-last-of-type(2) td {
|
||||||
|
border-bottom-color: ${({ theme }) => theme.background.transparent};
|
||||||
|
}
|
||||||
|
`;
|
||||||
@ -1,6 +1,9 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
|
|
||||||
|
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||||
|
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||||
|
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
||||||
import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector';
|
import { allRowsSelectedStatusComponentSelector } from '@/object-record/record-table/states/selectors/allRowsSelectedStatusComponentSelector';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { Checkbox } from 'twenty-ui/input';
|
import { Checkbox } from 'twenty-ui/input';
|
||||||
@ -30,6 +33,21 @@ export const RecordTableHeaderCheckboxColumn = () => {
|
|||||||
allRowsSelectedStatus === 'all' || allRowsSelectedStatus === 'some';
|
allRowsSelectedStatus === 'all' || allRowsSelectedStatus === 'some';
|
||||||
const indeterminate = allRowsSelectedStatus === 'some';
|
const indeterminate = allRowsSelectedStatus === 'some';
|
||||||
|
|
||||||
|
const { recordTableId } = useRecordTableContextOrThrow();
|
||||||
|
|
||||||
|
const isRecordTableInitialLoading = useRecoilComponentValueV2(
|
||||||
|
isRecordTableInitialLoadingComponentState,
|
||||||
|
recordTableId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const allRecordIds = useRecoilComponentValueV2(
|
||||||
|
recordIndexAllRecordIdsComponentSelector,
|
||||||
|
recordTableId,
|
||||||
|
);
|
||||||
|
|
||||||
|
const recordTableIsEmpty =
|
||||||
|
!isRecordTableInitialLoading && allRecordIds.length === 0;
|
||||||
|
|
||||||
const onChange = () => {
|
const onChange = () => {
|
||||||
if (checked) {
|
if (checked) {
|
||||||
setHasUserSelectedAllRows(false);
|
setHasUserSelectedAllRows(false);
|
||||||
@ -48,6 +66,7 @@ export const RecordTableHeaderCheckboxColumn = () => {
|
|||||||
checked={checked}
|
checked={checked}
|
||||||
onChange={onChange}
|
onChange={onChange}
|
||||||
indeterminate={indeterminate}
|
indeterminate={indeterminate}
|
||||||
|
disabled={recordTableIsEmpty}
|
||||||
/>
|
/>
|
||||||
</StyledContainer>
|
</StyledContainer>
|
||||||
</StyledColumnHeaderCell>
|
</StyledColumnHeaderCell>
|
||||||
|
|||||||
Reference in New Issue
Block a user