mirror of
https://github.com/twentyhq/twenty.git
synced 2024-12-23 03:51:36 +03:00
Improve aggregate footer cell display (#9124)
Co-authored-by: Jérémy Magrin <jeremy.magrin@gmail.com> Co-authored-by: Charles Bochet <charles@twenty.com>
This commit is contained in:
parent
7d8f895ae9
commit
ed56a68b7c
@ -1,7 +1,6 @@
|
|||||||
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
import { StyledHeaderDropdownButton } from '@/ui/layout/dropdown/components/StyledHeaderDropdownButton';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { AppTooltip, Tag, TooltipDelay } from 'twenty-ui';
|
import { AppTooltip, Tag, TooltipDelay } from 'twenty-ui';
|
||||||
import { formatNumber } from '~/utils/format/number';
|
|
||||||
|
|
||||||
const StyledButton = styled(StyledHeaderDropdownButton)`
|
const StyledButton = styled(StyledHeaderDropdownButton)`
|
||||||
padding: 0;
|
padding: 0;
|
||||||
@ -19,10 +18,7 @@ export const RecordBoardColumnHeaderAggregateDropdownButton = ({
|
|||||||
return (
|
return (
|
||||||
<div id={dropdownId}>
|
<div id={dropdownId}>
|
||||||
<StyledButton>
|
<StyledButton>
|
||||||
<Tag
|
<Tag text={value ? value.toString() : '-'} color={'transparent'} />
|
||||||
text={value ? formatNumber(Number(value)) : '-'}
|
|
||||||
color={'transparent'}
|
|
||||||
/>
|
|
||||||
<AppTooltip
|
<AppTooltip
|
||||||
anchorSelect={`#${dropdownId}`}
|
anchorSelect={`#${dropdownId}`}
|
||||||
content={tooltip}
|
content={tooltip}
|
||||||
|
@ -74,7 +74,7 @@ export const useAggregateRecordsForRecordBoardColumn = () => {
|
|||||||
skip: !isAggregateQueryEnabled,
|
skip: !isAggregateQueryEnabled,
|
||||||
});
|
});
|
||||||
|
|
||||||
const { value, label } = computeAggregateValueAndLabel({
|
const { value, labelWithFieldName } = computeAggregateValueAndLabel({
|
||||||
data,
|
data,
|
||||||
objectMetadataItem,
|
objectMetadataItem,
|
||||||
fieldMetadataId: recordIndexKanbanAggregateOperation?.fieldMetadataId,
|
fieldMetadataId: recordIndexKanbanAggregateOperation?.fieldMetadataId,
|
||||||
@ -84,6 +84,6 @@ export const useAggregateRecordsForRecordBoardColumn = () => {
|
|||||||
|
|
||||||
return {
|
return {
|
||||||
aggregateValue: isAggregateQueryEnabled ? value : recordCount,
|
aggregateValue: isAggregateQueryEnabled ? value : recordCount,
|
||||||
aggregateLabel: isDefined(value) ? label : undefined,
|
aggregateLabel: isDefined(value) ? labelWithFieldName : undefined,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -47,8 +47,81 @@ describe('computeAggregateValueAndLabel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
value: 2,
|
value: '2',
|
||||||
label: 'Sum of amount',
|
label: 'Sum',
|
||||||
|
labelWithFieldName: 'Sum of amount',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle number field as percentage', () => {
|
||||||
|
const mockObjectMetadataWithPercentageField: ObjectMetadataItem = {
|
||||||
|
id: '123',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: MOCK_FIELD_ID,
|
||||||
|
name: 'percentage',
|
||||||
|
type: FieldMetadataType.Number,
|
||||||
|
settings: {
|
||||||
|
type: 'percentage',
|
||||||
|
},
|
||||||
|
} as FieldMetadataItem,
|
||||||
|
],
|
||||||
|
} as ObjectMetadataItem;
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
percentage: {
|
||||||
|
[AGGREGATE_OPERATIONS.avg]: 0.3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeAggregateValueAndLabel({
|
||||||
|
data: mockData,
|
||||||
|
objectMetadataItem: mockObjectMetadataWithPercentageField,
|
||||||
|
fieldMetadataId: MOCK_FIELD_ID,
|
||||||
|
aggregateOperation: AGGREGATE_OPERATIONS.avg,
|
||||||
|
fallbackFieldName: MOCK_KANBAN_FIELD_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
value: '30%',
|
||||||
|
label: 'Average',
|
||||||
|
labelWithFieldName: 'Average of percentage',
|
||||||
|
});
|
||||||
|
});
|
||||||
|
|
||||||
|
it('should handle number field with decimals', () => {
|
||||||
|
const mockObjectMetadataWithDecimalsField: ObjectMetadataItem = {
|
||||||
|
id: '123',
|
||||||
|
fields: [
|
||||||
|
{
|
||||||
|
id: MOCK_FIELD_ID,
|
||||||
|
name: 'decimals',
|
||||||
|
type: FieldMetadataType.Number,
|
||||||
|
settings: {
|
||||||
|
decimals: 2,
|
||||||
|
},
|
||||||
|
} as FieldMetadataItem,
|
||||||
|
],
|
||||||
|
} as ObjectMetadataItem;
|
||||||
|
|
||||||
|
const mockData = {
|
||||||
|
decimals: {
|
||||||
|
[AGGREGATE_OPERATIONS.sum]: 0.009,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
const result = computeAggregateValueAndLabel({
|
||||||
|
data: mockData,
|
||||||
|
objectMetadataItem: mockObjectMetadataWithDecimalsField,
|
||||||
|
fieldMetadataId: MOCK_FIELD_ID,
|
||||||
|
aggregateOperation: AGGREGATE_OPERATIONS.sum,
|
||||||
|
fallbackFieldName: MOCK_KANBAN_FIELD_NAME,
|
||||||
|
});
|
||||||
|
|
||||||
|
expect(result).toEqual({
|
||||||
|
value: '0.01',
|
||||||
|
label: 'Sum',
|
||||||
|
labelWithFieldName: 'Sum of decimals',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
@ -86,8 +159,9 @@ describe('computeAggregateValueAndLabel', () => {
|
|||||||
});
|
});
|
||||||
|
|
||||||
expect(result).toEqual({
|
expect(result).toEqual({
|
||||||
value: undefined,
|
value: '-',
|
||||||
label: 'Sum of amount',
|
label: 'Sum',
|
||||||
|
labelWithFieldName: 'Sum of amount',
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -4,6 +4,8 @@ import { getAggregateOperationLabel } from '@/object-record/record-board/record-
|
|||||||
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
import { AGGREGATE_OPERATIONS } from '@/object-record/record-table/constants/AggregateOperations';
|
||||||
import isEmpty from 'lodash.isempty';
|
import isEmpty from 'lodash.isempty';
|
||||||
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
import { FieldMetadataType } from '~/generated-metadata/graphql';
|
||||||
|
import { formatAmount } from '~/utils/format/formatAmount';
|
||||||
|
import { formatNumber } from '~/utils/format/number';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const computeAggregateValueAndLabel = ({
|
export const computeAggregateValueAndLabel = ({
|
||||||
@ -42,12 +44,33 @@ export const computeAggregateValueAndLabel = ({
|
|||||||
|
|
||||||
const aggregateValue = data[field.name]?.[aggregateOperation];
|
const aggregateValue = data[field.name]?.[aggregateOperation];
|
||||||
|
|
||||||
const value =
|
let value;
|
||||||
isDefined(aggregateValue) && field.type === FieldMetadataType.Currency
|
|
||||||
? Number(aggregateValue) / 1_000_000
|
|
||||||
: aggregateValue;
|
|
||||||
|
|
||||||
const label =
|
if (aggregateOperation === AGGREGATE_OPERATIONS.count) {
|
||||||
|
value = aggregateValue;
|
||||||
|
} else if (!isDefined(aggregateValue)) {
|
||||||
|
value = '-';
|
||||||
|
} else {
|
||||||
|
value = Number(aggregateValue);
|
||||||
|
|
||||||
|
switch (field.type) {
|
||||||
|
case FieldMetadataType.Currency: {
|
||||||
|
value = formatAmount(value / 1_000_000);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
case FieldMetadataType.Number: {
|
||||||
|
const { decimals, type } = field.settings ?? {};
|
||||||
|
value =
|
||||||
|
type === 'percentage'
|
||||||
|
? `${formatNumber(value * 100, decimals)}%`
|
||||||
|
: formatNumber(value, decimals);
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const label = getAggregateOperationLabel(aggregateOperation);
|
||||||
|
const labelWithFieldName =
|
||||||
aggregateOperation === AGGREGATE_OPERATIONS.count
|
aggregateOperation === AGGREGATE_OPERATIONS.count
|
||||||
? `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}`
|
? `${getAggregateOperationLabel(AGGREGATE_OPERATIONS.count)}`
|
||||||
: `${getAggregateOperationLabel(aggregateOperation)} of ${field.name}`;
|
: `${getAggregateOperationLabel(aggregateOperation)} of ${field.name}`;
|
||||||
@ -55,5 +78,6 @@ export const computeAggregateValueAndLabel = ({
|
|||||||
return {
|
return {
|
||||||
value,
|
value,
|
||||||
label,
|
label,
|
||||||
|
labelWithFieldName,
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
@ -4,6 +4,6 @@ import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewCompon
|
|||||||
export const recordIndexRecordGroupHideComponentState =
|
export const recordIndexRecordGroupHideComponentState =
|
||||||
createComponentStateV2<boolean>({
|
createComponentStateV2<boolean>({
|
||||||
key: 'recordIndexRecordGroupHideComponentState',
|
key: 'recordIndexRecordGroupHideComponentState',
|
||||||
defaultValue: true,
|
defaultValue: false,
|
||||||
componentInstanceContext: ViewComponentInstanceContext,
|
componentInstanceContext: ViewComponentInstanceContext,
|
||||||
});
|
});
|
||||||
|
@ -13,7 +13,7 @@ import { RecordTableNoRecordGroupBody } from '@/object-record/record-table/recor
|
|||||||
import { RecordTableNoRecordGroupBodyEffect } from '@/object-record/record-table/record-table-body/components/RecordTableNoRecordGroupBodyEffect';
|
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 { RecordTableRecordGroupBodyEffects } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupBodyEffects';
|
||||||
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
|
import { RecordTableRecordGroupsBody } from '@/object-record/record-table/record-table-body/components/RecordTableRecordGroupsBody';
|
||||||
import { RecordTableFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableFooter';
|
import { RecordTableAggregateFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter';
|
||||||
import { RecordTableHeader } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
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 { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
import { recordTablePendingRecordIdComponentState } from '@/object-record/record-table/states/recordTablePendingRecordIdComponentState';
|
||||||
@ -88,7 +88,7 @@ export const RecordTable = () => {
|
|||||||
<RecordTableEmptyState />
|
<RecordTableEmptyState />
|
||||||
) : (
|
) : (
|
||||||
<>
|
<>
|
||||||
<StyledTable className="entity-table-cell" ref={tableBodyRef}>
|
<StyledTable ref={tableBodyRef}>
|
||||||
<RecordTableHeader />
|
<RecordTableHeader />
|
||||||
{!hasRecordGroups ? (
|
{!hasRecordGroups ? (
|
||||||
<RecordTableNoRecordGroupBody />
|
<RecordTableNoRecordGroupBody />
|
||||||
@ -97,7 +97,7 @@ export const RecordTable = () => {
|
|||||||
)}
|
)}
|
||||||
<RecordTableStickyEffect />
|
<RecordTableStickyEffect />
|
||||||
{isAggregateQueryEnabled && !hasRecordGroups && (
|
{isAggregateQueryEnabled && !hasRecordGroups && (
|
||||||
<RecordTableFooter />
|
<RecordTableAggregateFooter endOfTableSticky />
|
||||||
)}
|
)}
|
||||||
</StyledTable>
|
</StyledTable>
|
||||||
<DragSelect
|
<DragSelect
|
||||||
|
@ -1,7 +1,6 @@
|
|||||||
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
||||||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
||||||
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
import { recordIndexAllRecordIdsComponentSelector } from '@/object-record/record-index/states/selectors/recordIndexAllRecordIdsComponentSelector';
|
||||||
import { RecordTableFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableFooter';
|
|
||||||
import { RecordTablePendingRecordGroupRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRecordGroupRow';
|
import { RecordTablePendingRecordGroupRow } from '@/object-record/record-table/record-table-row/components/RecordTablePendingRecordGroupRow';
|
||||||
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
|
import { RecordTableRow } from '@/object-record/record-table/record-table-row/components/RecordTableRow';
|
||||||
import { RecordTableRecordGroupSectionAddNew } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew';
|
import { RecordTableRecordGroupSectionAddNew } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSectionAddNew';
|
||||||
@ -9,14 +8,10 @@ import { RecordTableRecordGroupSectionLoadMore } from '@/object-record/record-ta
|
|||||||
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
|
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
|
||||||
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
import { useRecoilComponentFamilyValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyValueV2';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
|
||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
import { isDefined } from '~/utils/isDefined';
|
import { isDefined } from '~/utils/isDefined';
|
||||||
|
|
||||||
export const RecordTableRecordGroupRows = () => {
|
export const RecordTableRecordGroupRows = () => {
|
||||||
const isAggregateQueryEnabled = useIsFeatureEnabled(
|
|
||||||
'IS_AGGREGATE_QUERY_ENABLED',
|
|
||||||
);
|
|
||||||
const currentRecordGroupId = useCurrentRecordGroupId();
|
const currentRecordGroupId = useCurrentRecordGroupId();
|
||||||
|
|
||||||
const allRecordIds = useRecoilComponentValueV2(
|
const allRecordIds = useRecoilComponentValueV2(
|
||||||
@ -63,12 +58,6 @@ export const RecordTableRecordGroupRows = () => {
|
|||||||
})}
|
})}
|
||||||
<RecordTablePendingRecordGroupRow />
|
<RecordTablePendingRecordGroupRow />
|
||||||
<RecordTableRecordGroupSectionAddNew />
|
<RecordTableRecordGroupSectionAddNew />
|
||||||
{isAggregateQueryEnabled && (
|
|
||||||
<RecordTableFooter
|
|
||||||
key={currentRecordGroupId}
|
|
||||||
currentRecordGroupId={currentRecordGroupId}
|
|
||||||
/>
|
|
||||||
)}
|
|
||||||
<RecordTableRecordGroupSectionLoadMore />
|
<RecordTableRecordGroupSectionLoadMore />
|
||||||
</>
|
</>
|
||||||
);
|
);
|
||||||
|
@ -15,6 +15,7 @@ export const RecordTableBodyDroppable = ({
|
|||||||
isDropDisabled,
|
isDropDisabled,
|
||||||
}: RecordTableBodyDroppableProps) => {
|
}: RecordTableBodyDroppableProps) => {
|
||||||
const [v4Persistable] = useState(v4());
|
const [v4Persistable] = useState(v4());
|
||||||
|
const recordTableBodyId = `record-table-body${recordGroupId ? '-' + recordGroupId : ''}`;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Droppable
|
<Droppable
|
||||||
@ -23,7 +24,7 @@ export const RecordTableBodyDroppable = ({
|
|||||||
>
|
>
|
||||||
{(provided) => (
|
{(provided) => (
|
||||||
<RecordTableBody
|
<RecordTableBody
|
||||||
id="record-table-body"
|
id={recordTableBodyId}
|
||||||
ref={provided.innerRef}
|
ref={provided.innerRef}
|
||||||
// eslint-disable-next-line react/jsx-props-no-spreading
|
// eslint-disable-next-line react/jsx-props-no-spreading
|
||||||
{...provided.droppableProps}
|
{...provided.droppableProps}
|
||||||
|
@ -6,12 +6,17 @@ import { RecordTableRecordGroupRows } from '@/object-record/record-table/compone
|
|||||||
import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable';
|
import { RecordTableBodyDroppable } from '@/object-record/record-table/record-table-body/components/RecordTableBodyDroppable';
|
||||||
import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading';
|
import { RecordTableBodyLoading } from '@/object-record/record-table/record-table-body/components/RecordTableBodyLoading';
|
||||||
import { RecordTableBodyRecordGroupDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider';
|
import { RecordTableBodyRecordGroupDragDropContextProvider } from '@/object-record/record-table/record-table-body/components/RecordTableBodyRecordGroupDragDropContextProvider';
|
||||||
|
import { RecordTableAggregateFooter } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooter';
|
||||||
import { RecordTableRecordGroupEmptyRow } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupEmptyRow';
|
import { RecordTableRecordGroupEmptyRow } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupEmptyRow';
|
||||||
import { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection';
|
import { RecordTableRecordGroupSection } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupSection';
|
||||||
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
import { isRecordTableInitialLoadingComponentState } from '@/object-record/record-table/states/isRecordTableInitialLoadingComponentState';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { useIsFeatureEnabled } from '@/workspace/hooks/useIsFeatureEnabled';
|
||||||
|
|
||||||
export const RecordTableRecordGroupsBody = () => {
|
export const RecordTableRecordGroupsBody = () => {
|
||||||
|
const isAggregateQueryEnabled = useIsFeatureEnabled(
|
||||||
|
'IS_AGGREGATE_QUERY_ENABLED',
|
||||||
|
);
|
||||||
const allRecordIds = useRecoilComponentValueV2(
|
const allRecordIds = useRecoilComponentValueV2(
|
||||||
recordIndexAllRecordIdsComponentSelector,
|
recordIndexAllRecordIdsComponentSelector,
|
||||||
);
|
);
|
||||||
@ -29,6 +34,7 @@ export const RecordTableRecordGroupsBody = () => {
|
|||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
|
<>
|
||||||
<RecordTableBodyRecordGroupDragDropContextProvider>
|
<RecordTableBodyRecordGroupDragDropContextProvider>
|
||||||
{visibleRecordGroupIds.map((recordGroupId, index) => (
|
{visibleRecordGroupIds.map((recordGroupId, index) => (
|
||||||
<RecordTableRecordGroupBodyContextProvider
|
<RecordTableRecordGroupBodyContextProvider
|
||||||
@ -41,9 +47,16 @@ export const RecordTableRecordGroupsBody = () => {
|
|||||||
<RecordTableRecordGroupSection />
|
<RecordTableRecordGroupSection />
|
||||||
<RecordTableRecordGroupRows />
|
<RecordTableRecordGroupRows />
|
||||||
</RecordTableBodyDroppable>
|
</RecordTableBodyDroppable>
|
||||||
|
{isAggregateQueryEnabled && (
|
||||||
|
<RecordTableAggregateFooter
|
||||||
|
key={recordGroupId}
|
||||||
|
currentRecordGroupId={recordGroupId}
|
||||||
|
/>
|
||||||
|
)}
|
||||||
</RecordGroupContext.Provider>
|
</RecordGroupContext.Provider>
|
||||||
</RecordTableRecordGroupBodyContextProvider>
|
</RecordTableRecordGroupBodyContextProvider>
|
||||||
))}
|
))}
|
||||||
</RecordTableBodyRecordGroupDragDropContextProvider>
|
</RecordTableBodyRecordGroupDragDropContextProvider>
|
||||||
|
</>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
@ -1,17 +1,16 @@
|
|||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { MOBILE_VIEWPORT } from 'twenty-ui';
|
import { MOBILE_VIEWPORT } from 'twenty-ui';
|
||||||
|
|
||||||
import { TABLE_CELL_CHECKBOX_MIN_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellCheckbox';
|
import { RecordTableAggregateFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableAggregateFooterCell';
|
||||||
import { TABLE_CELL_GRIP_WIDTH } from '@/object-record/record-table/record-table-cell/components/RecordTableCellGrip';
|
import { FIRST_TH_WIDTH } from '@/object-record/record-table/record-table-header/components/RecordTableHeader';
|
||||||
import { RecordTableFooterCell } from '@/object-record/record-table/record-table-footer/components/RecordTableFooterCell';
|
|
||||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
|
||||||
const StyledTableFoot = styled.thead`
|
const StyledTableFoot = styled.thead<{ endOfTableSticky?: boolean }>`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
th:nth-of-type(1) {
|
th:nth-of-type(1) {
|
||||||
width: 9px;
|
width: ${FIRST_TH_WIDTH};
|
||||||
left: 0;
|
left: 0;
|
||||||
border-right-color: ${({ theme }) => theme.background.primary};
|
border-right-color: ${({ theme }) => theme.background.primary};
|
||||||
}
|
}
|
||||||
@ -59,31 +58,23 @@ const StyledTableFoot = styled.thead`
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
&.header-sticky {
|
tr {
|
||||||
th {
|
|
||||||
position: sticky;
|
position: sticky;
|
||||||
top: 0;
|
|
||||||
z-index: 5;
|
z-index: 5;
|
||||||
}
|
${({ endOfTableSticky }) => endOfTableSticky && `bottom: 0;`}
|
||||||
}
|
|
||||||
|
|
||||||
&.header-sticky.first-columns-sticky {
|
|
||||||
th:nth-of-type(1),
|
|
||||||
th:nth-of-type(2),
|
|
||||||
th:nth-of-type(3) {
|
|
||||||
z-index: 10;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const StyledDiv = styled.div`
|
const StyledTh = styled.th`
|
||||||
width: calc(${TABLE_CELL_GRIP_WIDTH} + ${TABLE_CELL_CHECKBOX_MIN_WIDTH});
|
background-color: ${({ theme }) => theme.background.primary};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RecordTableFooter = ({
|
export const RecordTableAggregateFooter = ({
|
||||||
currentRecordGroupId,
|
currentRecordGroupId,
|
||||||
|
endOfTableSticky,
|
||||||
}: {
|
}: {
|
||||||
currentRecordGroupId?: string;
|
currentRecordGroupId?: string;
|
||||||
|
endOfTableSticky?: boolean;
|
||||||
}) => {
|
}) => {
|
||||||
const visibleTableColumns = useRecoilComponentValueV2(
|
const visibleTableColumns = useRecoilComponentValueV2(
|
||||||
visibleTableColumnsComponentSelector,
|
visibleTableColumnsComponentSelector,
|
||||||
@ -93,12 +84,13 @@ export const RecordTableFooter = ({
|
|||||||
<StyledTableFoot
|
<StyledTableFoot
|
||||||
id={`record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`}
|
id={`record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`}
|
||||||
data-select-disable
|
data-select-disable
|
||||||
|
endOfTableSticky={endOfTableSticky}
|
||||||
>
|
>
|
||||||
<tr>
|
<tr>
|
||||||
<th />
|
<StyledTh />
|
||||||
<StyledDiv />
|
<StyledTh />
|
||||||
{visibleTableColumns.map((column, index) => (
|
{visibleTableColumns.map((column, index) => (
|
||||||
<RecordTableFooterCell
|
<RecordTableAggregateFooterCell
|
||||||
key={`${column.fieldMetadataId}${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`}
|
key={`${column.fieldMetadataId}${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`}
|
||||||
column={column}
|
column={column}
|
||||||
currentRecordGroupId={currentRecordGroupId}
|
currentRecordGroupId={currentRecordGroupId}
|
@ -2,7 +2,7 @@ import styled from '@emotion/styled';
|
|||||||
import { useMemo } from 'react';
|
import { useMemo } from 'react';
|
||||||
|
|
||||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import { RecordTableColumnFooterWithDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterWithDropdown';
|
import { RecordTableColumnFooterWithDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterWithDropdown';
|
||||||
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
@ -61,7 +61,7 @@ const StyledColumnFootContainer = styled.div`
|
|||||||
width: 100%;
|
width: 100%;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RecordTableFooterCell = ({
|
export const RecordTableAggregateFooterCell = ({
|
||||||
column,
|
column,
|
||||||
isFirstCell = false,
|
isFirstCell = false,
|
||||||
currentRecordGroupId,
|
currentRecordGroupId,
|
@ -14,7 +14,7 @@ import { useMemo } from 'react';
|
|||||||
import { Key } from 'ts-key-enum';
|
import { Key } from 'ts-key-enum';
|
||||||
import { MenuItem } from 'twenty-ui';
|
import { MenuItem } from 'twenty-ui';
|
||||||
|
|
||||||
export const RecordTableColumnFooterDropdown = ({
|
export const RecordTableColumnAggregateFooterDropdown = ({
|
||||||
column,
|
column,
|
||||||
dropdownId,
|
dropdownId,
|
||||||
}: {
|
}: {
|
||||||
@ -30,10 +30,6 @@ export const RecordTableColumnFooterDropdown = ({
|
|||||||
(viewField) => viewField.fieldMetadataId === column.fieldMetadataId,
|
(viewField) => viewField.fieldMetadataId === column.fieldMetadataId,
|
||||||
);
|
);
|
||||||
|
|
||||||
if (!currentViewField) {
|
|
||||||
throw new Error('ViewField not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
useScopedHotkeys(
|
useScopedHotkeys(
|
||||||
[Key.Escape],
|
[Key.Escape],
|
||||||
() => {
|
() => {
|
||||||
@ -56,6 +52,9 @@ export const RecordTableColumnFooterDropdown = ({
|
|||||||
const handleAggregationChange = (
|
const handleAggregationChange = (
|
||||||
aggregateOperation: AGGREGATE_OPERATIONS,
|
aggregateOperation: AGGREGATE_OPERATIONS,
|
||||||
) => {
|
) => {
|
||||||
|
if (!currentViewField) {
|
||||||
|
throw new Error('ViewField not found');
|
||||||
|
}
|
||||||
updateViewFieldRecords([
|
updateViewFieldRecords([
|
||||||
{ ...currentViewField, aggregateOperation: aggregateOperation },
|
{ ...currentViewField, aggregateOperation: aggregateOperation },
|
||||||
]);
|
]);
|
@ -1,12 +1,7 @@
|
|||||||
import { useTheme } from '@emotion/react';
|
import { useTheme } from '@emotion/react';
|
||||||
import styled from '@emotion/styled';
|
import styled from '@emotion/styled';
|
||||||
import { useState } from 'react';
|
import { useState } from 'react';
|
||||||
import {
|
import { IconChevronDown, isDefined } from 'twenty-ui';
|
||||||
AppTooltip,
|
|
||||||
IconChevronDown,
|
|
||||||
isDefined,
|
|
||||||
TooltipDelay,
|
|
||||||
} from 'twenty-ui';
|
|
||||||
|
|
||||||
const StyledCell = styled.div`
|
const StyledCell = styled.div`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
@ -37,6 +32,27 @@ const StyledText = styled.span`
|
|||||||
z-index: 1;
|
z-index: 1;
|
||||||
`;
|
`;
|
||||||
|
|
||||||
|
const StyledValueContainer = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
flex: 1 0 0;
|
||||||
|
gap: 4px;
|
||||||
|
height: 32px;
|
||||||
|
justify-content: flex-end;
|
||||||
|
padding: 8px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledLabel = styled.div`
|
||||||
|
align-items: center;
|
||||||
|
display: flex;
|
||||||
|
gap: 4px;
|
||||||
|
`;
|
||||||
|
|
||||||
|
const StyledValue = styled.div`
|
||||||
|
color: ${({ theme }) => theme.color.gray60};
|
||||||
|
flex: 1 0 0;
|
||||||
|
`;
|
||||||
|
|
||||||
const StyledIcon = styled(IconChevronDown)`
|
const StyledIcon = styled(IconChevronDown)`
|
||||||
align-items: center;
|
align-items: center;
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -46,7 +62,7 @@ const StyledIcon = styled(IconChevronDown)`
|
|||||||
padding-right: ${({ theme }) => theme.spacing(2)};
|
padding-right: ${({ theme }) => theme.spacing(2)};
|
||||||
`;
|
`;
|
||||||
|
|
||||||
export const RecordTableColumnFooterAggregateValue = ({
|
export const RecordTableColumnAggregateFooterValue = ({
|
||||||
dropdownId,
|
dropdownId,
|
||||||
aggregateValue,
|
aggregateValue,
|
||||||
aggregateLabel,
|
aggregateLabel,
|
||||||
@ -70,20 +86,15 @@ export const RecordTableColumnFooterAggregateValue = ({
|
|||||||
<StyledCell>
|
<StyledCell>
|
||||||
{isHovered || isDefined(aggregateValue) || isFirstCell ? (
|
{isHovered || isDefined(aggregateValue) || isFirstCell ? (
|
||||||
<>
|
<>
|
||||||
<StyledText id={sanitizedId}>
|
{isDefined(aggregateValue) ? (
|
||||||
{aggregateValue ?? 'Calculate'}
|
<StyledValueContainer>
|
||||||
</StyledText>
|
<StyledLabel>{aggregateLabel}</StyledLabel>
|
||||||
<StyledIcon fontWeight={'light'} size={theme.icon.size.sm} />
|
<StyledValue>{aggregateValue}</StyledValue>
|
||||||
{isDefined(aggregateValue) && isDefined(aggregateLabel) && (
|
</StyledValueContainer>
|
||||||
<AppTooltip
|
) : (
|
||||||
anchorSelect={`#${sanitizedId}`}
|
<StyledText id={sanitizedId}>Calculate</StyledText>
|
||||||
content={aggregateLabel}
|
|
||||||
noArrow
|
|
||||||
place="top-start"
|
|
||||||
positionStrategy="fixed"
|
|
||||||
delay={TooltipDelay.shortDelay}
|
|
||||||
/>
|
|
||||||
)}
|
)}
|
||||||
|
<StyledIcon fontWeight={'light'} size={theme.icon.size.sm} />
|
||||||
</>
|
</>
|
||||||
) : (
|
) : (
|
||||||
<></>
|
<></>
|
@ -1,6 +1,6 @@
|
|||||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||||
import { RecordTableColumnFooterAggregateValue } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterAggregateValue';
|
import { RecordTableColumnAggregateFooterDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterDropdown';
|
||||||
import { RecordTableColumnFooterDropdown } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnFooterDropdown';
|
import { RecordTableColumnAggregateFooterValue } from '@/object-record/record-table/record-table-footer/components/RecordTableColumnAggregateFooterValue';
|
||||||
import { useAggregateRecordsForRecordTableColumnFooter } from '@/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter';
|
import { useAggregateRecordsForRecordTableColumnFooter } from '@/object-record/record-table/record-table-footer/hooks/useAggregateRecordsForRecordTableColumnFooter';
|
||||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||||
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
import { Dropdown } from '@/ui/layout/dropdown/components/Dropdown';
|
||||||
@ -44,7 +44,7 @@ export const RecordTableColumnFooterWithDropdown = ({
|
|||||||
onClose={handleDropdownClose}
|
onClose={handleDropdownClose}
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
clickableComponent={
|
clickableComponent={
|
||||||
<RecordTableColumnFooterAggregateValue
|
<RecordTableColumnAggregateFooterValue
|
||||||
aggregateLabel={aggregateLabel}
|
aggregateLabel={aggregateLabel}
|
||||||
aggregateValue={aggregateValue}
|
aggregateValue={aggregateValue}
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
@ -52,7 +52,7 @@ export const RecordTableColumnFooterWithDropdown = ({
|
|||||||
/>
|
/>
|
||||||
}
|
}
|
||||||
dropdownComponents={
|
dropdownComponents={
|
||||||
<RecordTableColumnFooterDropdown
|
<RecordTableColumnAggregateFooterDropdown
|
||||||
column={column}
|
column={column}
|
||||||
dropdownId={dropdownId}
|
dropdownId={dropdownId}
|
||||||
/>
|
/>
|
@ -32,13 +32,10 @@ export const useAggregateRecordsForRecordTableColumnFooter = (
|
|||||||
recordIndexViewFilterGroups,
|
recordIndexViewFilterGroups,
|
||||||
);
|
);
|
||||||
|
|
||||||
const viewFieldId = currentViewWithSavedFiltersAndSorts?.viewFields?.find(
|
const viewFieldId =
|
||||||
|
currentViewWithSavedFiltersAndSorts?.viewFields?.find(
|
||||||
(viewField) => viewField.fieldMetadataId === fieldMetadataId,
|
(viewField) => viewField.fieldMetadataId === fieldMetadataId,
|
||||||
)?.id;
|
)?.id ?? '';
|
||||||
|
|
||||||
if (!viewFieldId) {
|
|
||||||
throw new Error('ViewField not found');
|
|
||||||
}
|
|
||||||
|
|
||||||
const aggregateOperationForViewField = useRecoilValue(
|
const aggregateOperationForViewField = useRecoilValue(
|
||||||
aggregateOperationForViewFieldState({ viewFieldId: viewFieldId }),
|
aggregateOperationForViewFieldState({ viewFieldId: viewFieldId }),
|
||||||
|
@ -7,11 +7,13 @@ import { RecordTableHeaderCheckboxColumn } from '@/object-record/record-table/re
|
|||||||
import { RecordTableHeaderDragDropColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderDragDropColumn';
|
import { RecordTableHeaderDragDropColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderDragDropColumn';
|
||||||
import { RecordTableHeaderLastColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn';
|
import { RecordTableHeaderLastColumn } from '@/object-record/record-table/record-table-header/components/RecordTableHeaderLastColumn';
|
||||||
|
|
||||||
|
export const FIRST_TH_WIDTH = '9px';
|
||||||
|
|
||||||
const StyledTableHead = styled.thead`
|
const StyledTableHead = styled.thead`
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
|
|
||||||
th:nth-of-type(1) {
|
th:nth-of-type(1) {
|
||||||
width: 9px;
|
width: ${FIRST_TH_WIDTH};
|
||||||
left: 0;
|
left: 0;
|
||||||
border-right-color: ${({ theme }) => theme.background.primary};
|
border-right-color: ${({ theme }) => theme.background.primary};
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ import { recordGroupDefinitionFamilyState } from '@/object-record/record-group/s
|
|||||||
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
|
import { RecordGroupDefinitionType } from '@/object-record/record-group/types/RecordGroupDefinition';
|
||||||
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
import { recordIndexRecordIdsByGroupComponentFamilyState } from '@/object-record/record-index/states/recordIndexRecordIdsByGroupComponentFamilyState';
|
||||||
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
import { RecordTableTd } from '@/object-record/record-table/record-table-cell/components/RecordTableTd';
|
||||||
|
import { RecordTableRecordGroupStickyEffect } from '@/object-record/record-table/record-table-section/components/RecordTableRecordGroupStickyEffect';
|
||||||
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
|
import { isRecordGroupTableSectionToggledComponentState } from '@/object-record/record-table/record-table-section/states/isRecordGroupTableSectionToggledComponentState';
|
||||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||||
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
|
import { useRecoilComponentFamilyStateV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentFamilyStateV2';
|
||||||
@ -107,6 +108,7 @@ export const RecordTableRecordGroupSection = () => {
|
|||||||
weight="medium"
|
weight="medium"
|
||||||
/>
|
/>
|
||||||
<StyledTotalRow>{recordIdsByGroup.length}</StyledTotalRow>
|
<StyledTotalRow>{recordIdsByGroup.length}</StyledTotalRow>
|
||||||
|
<RecordTableRecordGroupStickyEffect />
|
||||||
</StyledRecordGroupSection>
|
</StyledRecordGroupSection>
|
||||||
<StyledEmptyTd colSpan={visibleColumns.length - 1} />
|
<StyledEmptyTd colSpan={visibleColumns.length - 1} />
|
||||||
<StyledEmptyTd />
|
<StyledEmptyTd />
|
||||||
|
@ -0,0 +1,48 @@
|
|||||||
|
import { useEffect } from 'react';
|
||||||
|
|
||||||
|
import { useCurrentRecordGroupId } from '@/object-record/record-group/hooks/useCurrentRecordGroupId';
|
||||||
|
import { isRecordTableScrolledLeftComponentState } from '@/object-record/record-table/states/isRecordTableScrolledLeftComponentState';
|
||||||
|
import { scrollWrapperScrollLeftComponentState } from '@/ui/utilities/scroll/states/scrollWrapperScrollLeftComponentState';
|
||||||
|
import { useRecoilComponentValueV2 } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValueV2';
|
||||||
|
import { useSetRecoilComponentStateV2 } from '@/ui/utilities/state/component-state/hooks/useSetRecoilComponentStateV2';
|
||||||
|
|
||||||
|
export const RecordTableRecordGroupStickyEffect = () => {
|
||||||
|
const scrollLeft = useRecoilComponentValueV2(
|
||||||
|
scrollWrapperScrollLeftComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const setIsRecordTableScrolledLeft = useSetRecoilComponentStateV2(
|
||||||
|
isRecordTableScrolledLeftComponentState,
|
||||||
|
);
|
||||||
|
|
||||||
|
const currentRecordGroupId = useCurrentRecordGroupId();
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
setIsRecordTableScrolledLeft(scrollLeft === 0);
|
||||||
|
if (scrollLeft > 0) {
|
||||||
|
document
|
||||||
|
.getElementById(
|
||||||
|
`record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`,
|
||||||
|
)
|
||||||
|
?.classList.add('first-columns-sticky');
|
||||||
|
document
|
||||||
|
.getElementById(
|
||||||
|
`record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`,
|
||||||
|
)
|
||||||
|
?.classList.add('first-columns-sticky');
|
||||||
|
} else {
|
||||||
|
document
|
||||||
|
.getElementById(
|
||||||
|
`record-table-footer${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`,
|
||||||
|
)
|
||||||
|
?.classList.remove('first-columns-sticky');
|
||||||
|
document
|
||||||
|
.getElementById(
|
||||||
|
`record-table-body${currentRecordGroupId ? '-' + currentRecordGroupId : ''}`,
|
||||||
|
)
|
||||||
|
?.classList.remove('first-columns-sticky');
|
||||||
|
}
|
||||||
|
}, [currentRecordGroupId, scrollLeft, setIsRecordTableScrolledLeft]);
|
||||||
|
|
||||||
|
return <></>;
|
||||||
|
};
|
@ -19,6 +19,7 @@ export type ContextProviderName =
|
|||||||
| 'test'
|
| 'test'
|
||||||
| 'showPageActivityContainer'
|
| 'showPageActivityContainer'
|
||||||
| 'navigationDrawer'
|
| 'navigationDrawer'
|
||||||
|
| 'aggregateFooterCell'
|
||||||
| 'modalContent';
|
| 'modalContent';
|
||||||
|
|
||||||
const createScrollWrapperContext = (id: string) =>
|
const createScrollWrapperContext = (id: string) =>
|
||||||
@ -52,6 +53,8 @@ export const ShowPageActivityContainerScrollWrapperContext =
|
|||||||
export const NavigationDrawerScrollWrapperContext =
|
export const NavigationDrawerScrollWrapperContext =
|
||||||
createScrollWrapperContext('navigationDrawer');
|
createScrollWrapperContext('navigationDrawer');
|
||||||
export const TestScrollWrapperContext = createScrollWrapperContext('test');
|
export const TestScrollWrapperContext = createScrollWrapperContext('test');
|
||||||
|
export const AggregateFooterCellScrollWrapperContext =
|
||||||
|
createScrollWrapperContext('aggregateFooterCell');
|
||||||
export const ModalContentScrollWrapperContext =
|
export const ModalContentScrollWrapperContext =
|
||||||
createScrollWrapperContext('modalContent');
|
createScrollWrapperContext('modalContent');
|
||||||
|
|
||||||
@ -85,6 +88,8 @@ export const getContextByProviderName = (
|
|||||||
return ShowPageActivityContainerScrollWrapperContext;
|
return ShowPageActivityContainerScrollWrapperContext;
|
||||||
case 'navigationDrawer':
|
case 'navigationDrawer':
|
||||||
return NavigationDrawerScrollWrapperContext;
|
return NavigationDrawerScrollWrapperContext;
|
||||||
|
case 'aggregateFooterCell':
|
||||||
|
return AggregateFooterCellScrollWrapperContext;
|
||||||
case 'modalContent':
|
case 'modalContent':
|
||||||
return ModalContentScrollWrapperContext;
|
return ModalContentScrollWrapperContext;
|
||||||
default:
|
default:
|
||||||
|
@ -20,6 +20,7 @@ import { ObjectMetadataMaps } from 'src/engine/metadata-modules/types/object-met
|
|||||||
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
import { getObjectMetadataMapItemByNameSingular } from 'src/engine/metadata-modules/utils/get-object-metadata-map-item-by-name-singular.util';
|
||||||
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
import { CompositeFieldMetadataType } from 'src/engine/metadata-modules/workspace-migration/factories/composite-column-action.factory';
|
||||||
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
import { isRelationFieldMetadataType } from 'src/engine/utils/is-relation-field-metadata-type.util';
|
||||||
|
import { isDefined } from 'src/utils/is-defined';
|
||||||
import { isPlainObject } from 'src/utils/is-plain-object';
|
import { isPlainObject } from 'src/utils/is-plain-object';
|
||||||
|
|
||||||
export class ObjectRecordsToGraphqlConnectionHelper {
|
export class ObjectRecordsToGraphqlConnectionHelper {
|
||||||
@ -95,7 +96,7 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
|||||||
selectedAggregatedFields: Record<string, AggregationField[]>;
|
selectedAggregatedFields: Record<string, AggregationField[]>;
|
||||||
objectRecordsAggregatedValues: Record<string, any>;
|
objectRecordsAggregatedValues: Record<string, any>;
|
||||||
}) => {
|
}) => {
|
||||||
if (!objectRecordsAggregatedValues) {
|
if (!isDefined(objectRecordsAggregatedValues)) {
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -104,7 +105,7 @@ export class ObjectRecordsToGraphqlConnectionHelper {
|
|||||||
const aggregatedFieldValue =
|
const aggregatedFieldValue =
|
||||||
objectRecordsAggregatedValues[aggregatedFieldName];
|
objectRecordsAggregatedValues[aggregatedFieldName];
|
||||||
|
|
||||||
if (!aggregatedFieldValue) {
|
if (!isDefined(aggregatedFieldValue)) {
|
||||||
return acc;
|
return acc;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Loading…
Reference in New Issue
Block a user