feat: display record identifier field as first column in table (#3788)
* feat: display record identifier field as first column in table & forbid hiding and moving record identifier column Closes #3303 * refactor: add availableTableColumnKeysSelectorScopeMap * feat: show plus icon button for label identifier column and dropdown menu for other columns * fix: use label identifier field value in RecordShowPage title * refactor: remove availableColumnKeys selector * refactor: review - compute label identifier logic in mapViewFieldsToColumnDefinitions + remove selectors * fix: several fixes * fix: fix board fields isVisible * fix: fix board fields reordering * fix: more board fields fixes * fix: fix hiddenTableColumnsSelectorScopeMap
This commit is contained in:
@ -26,6 +26,7 @@ import { useScopedHotkeys } from '@/ui/utilities/hotkey/hooks/useScopedHotkeys';
|
||||
import { ViewFieldsVisibilityDropdownSection } from '@/views/components/ViewFieldsVisibilityDropdownSection';
|
||||
import { useViewScopedStates } from '@/views/hooks/internal/useViewScopedStates';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
|
||||
import { useRecordBoardDeprecatedCardFieldsInternal } from '../../hooks/internal/useRecordBoardDeprecatedCardFieldsInternal';
|
||||
import { BoardColumnDefinition } from '../../types/BoardColumnDefinition';
|
||||
@ -113,11 +114,12 @@ export const RecordBoardDeprecatedOptionsDropdownContent = ({
|
||||
return;
|
||||
}
|
||||
|
||||
const reorderFields = [...visibleBoardCardFields];
|
||||
const [removed] = reorderFields.splice(result.source.index - 1, 1);
|
||||
reorderFields.splice(result.destination.index - 1, 0, removed);
|
||||
const reorderedFields = moveArrayItem(visibleBoardCardFields, {
|
||||
fromIndex: result.source.index - 1,
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
handleFieldsReorder(reorderFields);
|
||||
handleFieldsReorder(reorderedFields);
|
||||
},
|
||||
[handleFieldsReorder, visibleBoardCardFields],
|
||||
);
|
||||
@ -217,10 +219,9 @@ export const RecordBoardDeprecatedOptionsDropdownContent = ({
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleBoardCardFields}
|
||||
isVisible={true}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
isDraggable={true}
|
||||
isDraggable
|
||||
onDragEnd={handleReorderField}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
/>
|
||||
)}
|
||||
{hasVisibleFields && hasHiddenFields && <DropdownMenuSeparator />}
|
||||
@ -228,9 +229,8 @@ export const RecordBoardDeprecatedOptionsDropdownContent = ({
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
fields={hiddenBoardCardFields}
|
||||
isVisible={false}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
isDraggable={false}
|
||||
onVisibilityChange={handleFieldVisibilityChange}
|
||||
/>
|
||||
)}
|
||||
</>
|
||||
|
||||
@ -74,25 +74,29 @@ export const RecordIndexContainer = ({
|
||||
const onViewFieldsChange = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(viewFields: ViewField[]) => {
|
||||
setTableColumns(
|
||||
mapViewFieldsToColumnDefinitions(viewFields, columnDefinitions),
|
||||
const newFieldDefinitions = mapViewFieldsToColumnDefinitions({
|
||||
viewFields,
|
||||
columnDefinitions,
|
||||
});
|
||||
|
||||
setTableColumns(newFieldDefinitions);
|
||||
|
||||
const newRecordIndexFieldDefinitions = newFieldDefinitions.filter(
|
||||
(boardField) => !boardField.isLabelIdentifier,
|
||||
);
|
||||
|
||||
const existingRecordIndexFieldDefinitions = snapshot
|
||||
.getLoadable(recordIndexFieldDefinitionsState)
|
||||
.getValue();
|
||||
|
||||
const newFieldDefinitions = mapViewFieldsToColumnDefinitions(
|
||||
viewFields,
|
||||
columnDefinitions,
|
||||
);
|
||||
if (
|
||||
!isDeeplyEqual(
|
||||
existingRecordIndexFieldDefinitions,
|
||||
newFieldDefinitions,
|
||||
newRecordIndexFieldDefinitions,
|
||||
)
|
||||
)
|
||||
set(recordIndexFieldDefinitionsState, newFieldDefinitions);
|
||||
) {
|
||||
set(recordIndexFieldDefinitionsState, newRecordIndexFieldDefinitions);
|
||||
}
|
||||
},
|
||||
[columnDefinitions, setTableColumns],
|
||||
);
|
||||
|
||||
@ -5,7 +5,6 @@ import { useColumnDefinitionsFromFieldMetadata } from '@/object-metadata/hooks/u
|
||||
import { useObjectMetadataItem } from '@/object-metadata/hooks/useObjectMetadataItem';
|
||||
import { useRecordActionBar } from '@/object-record/record-action-bar/hooks/useRecordActionBar';
|
||||
import { useRecordTable } from '@/object-record/record-table/hooks/useRecordTable';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
import { useViewBar } from '@/views/hooks/useViewBar';
|
||||
|
||||
type RecordIndexTableContainerEffectProps = {
|
||||
@ -32,7 +31,7 @@ export const RecordIndexTableContainerEffect = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions, filterDefinitions, sortDefinitions } =
|
||||
const { columnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
const { setEntityCountInCurrentView } = useViewBar({
|
||||
@ -40,18 +39,8 @@ export const RecordIndexTableContainerEffect = ({
|
||||
});
|
||||
|
||||
useEffect(() => {
|
||||
const availableTableColumns = columnDefinitions.filter(
|
||||
filterAvailableTableColumns,
|
||||
);
|
||||
|
||||
setAvailableTableColumns(availableTableColumns);
|
||||
}, [
|
||||
columnDefinitions,
|
||||
objectMetadataItem,
|
||||
sortDefinitions,
|
||||
filterDefinitions,
|
||||
setAvailableTableColumns,
|
||||
]);
|
||||
setAvailableTableColumns(columnDefinitions);
|
||||
}, [columnDefinitions, setAvailableTableColumns]);
|
||||
|
||||
const selectedRowIds = useRecoilValue(getSelectedRowIdsSelector());
|
||||
|
||||
|
||||
@ -159,10 +159,9 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Visible"
|
||||
fields={visibleRecordFields}
|
||||
isVisible={true}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
isDraggable={true}
|
||||
isDraggable
|
||||
onDragEnd={handleReorderFields}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
/>
|
||||
{hiddenRecordFields.length > 0 && (
|
||||
<>
|
||||
@ -170,9 +169,8 @@ export const RecordIndexOptionsDropdownContent = ({
|
||||
<ViewFieldsVisibilityDropdownSection
|
||||
title="Hidden"
|
||||
fields={hiddenRecordFields}
|
||||
isVisible={false}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
isDraggable={false}
|
||||
onVisibilityChange={handleChangeFieldVisibility}
|
||||
/>
|
||||
</>
|
||||
)}
|
||||
|
||||
@ -9,10 +9,12 @@ import { useRecordBoard } from '@/object-record/record-board/hooks/useRecordBoar
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { recordIndexFieldDefinitionsState } from '@/object-record/record-index/states/recordIndexFieldDefinitionsState';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { filterAvailableTableColumns } from '@/object-record/utils/filterAvailableTableColumns';
|
||||
import { useViewFields } from '@/views/hooks/internal/useViewFields';
|
||||
import { useViews } from '@/views/hooks/internal/useViews';
|
||||
import { GraphQLView } from '@/views/types/GraphQLView';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
import { isDeeplyEqual } from '~/utils/isDeeplyEqual';
|
||||
|
||||
type useRecordIndexOptionsForBoardParams = {
|
||||
objectNameSingular: string;
|
||||
@ -40,74 +42,82 @@ export const useRecordIndexOptionsForBoard = ({
|
||||
objectNameSingular,
|
||||
});
|
||||
|
||||
const { columnDefinitions: availableColumnDefinitions } =
|
||||
const { columnDefinitions } =
|
||||
useColumnDefinitionsFromFieldMetadata(objectMetadataItem);
|
||||
|
||||
// Todo replace this with label identifier logic
|
||||
const columnDefinitions = availableColumnDefinitions
|
||||
.filter(
|
||||
(columnDefinition) => columnDefinition.metadata.fieldName !== 'name',
|
||||
)
|
||||
.filter(filterAvailableTableColumns);
|
||||
const availableColumnDefinitions = useMemo(
|
||||
() =>
|
||||
columnDefinitions.filter(({ isLabelIdentifier }) => !isLabelIdentifier),
|
||||
[columnDefinitions],
|
||||
);
|
||||
|
||||
const recordIndexFieldDefinitionsByKey = useMemo(
|
||||
() =>
|
||||
mapArrayToObject(
|
||||
recordIndexFieldDefinitions,
|
||||
({ fieldMetadataId }) => fieldMetadataId,
|
||||
),
|
||||
[recordIndexFieldDefinitions],
|
||||
);
|
||||
|
||||
const visibleBoardFields = useMemo(
|
||||
() =>
|
||||
columnDefinitions.filter((columnDefinition) => {
|
||||
return recordIndexFieldDefinitions.some(
|
||||
(existingRecordFieldDefinition) => {
|
||||
return (
|
||||
columnDefinition.fieldMetadataId ===
|
||||
existingRecordFieldDefinition.fieldMetadataId &&
|
||||
existingRecordFieldDefinition.isVisible
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
[columnDefinitions, recordIndexFieldDefinitions],
|
||||
recordIndexFieldDefinitions
|
||||
.filter((boardField) => boardField.isVisible)
|
||||
.sort(
|
||||
(boardFieldA, boardFieldB) =>
|
||||
boardFieldA.position - boardFieldB.position,
|
||||
),
|
||||
[recordIndexFieldDefinitions],
|
||||
);
|
||||
|
||||
const hiddenBoardFields = useMemo(
|
||||
() =>
|
||||
columnDefinitions.filter((columnDefinition) => {
|
||||
return !recordIndexFieldDefinitions.some(
|
||||
(existingRecordFieldDefinition) => {
|
||||
return (
|
||||
columnDefinition.fieldMetadataId ===
|
||||
existingRecordFieldDefinition.fieldMetadataId &&
|
||||
existingRecordFieldDefinition.isVisible
|
||||
);
|
||||
},
|
||||
);
|
||||
}),
|
||||
[columnDefinitions, recordIndexFieldDefinitions],
|
||||
availableColumnDefinitions
|
||||
.filter(
|
||||
({ fieldMetadataId }) =>
|
||||
!recordIndexFieldDefinitionsByKey[fieldMetadataId]?.isVisible,
|
||||
)
|
||||
.map((availableColumnDefinition) => {
|
||||
const { fieldMetadataId } = availableColumnDefinition;
|
||||
const existingBoardField =
|
||||
recordIndexFieldDefinitionsByKey[fieldMetadataId];
|
||||
|
||||
return {
|
||||
...(existingBoardField || availableColumnDefinition),
|
||||
isVisible: false,
|
||||
};
|
||||
}),
|
||||
[availableColumnDefinitions, recordIndexFieldDefinitionsByKey],
|
||||
);
|
||||
|
||||
const handleReorderBoardFields: OnDragEndResponder = useCallback(
|
||||
(result) => {
|
||||
if (
|
||||
!result.destination ||
|
||||
result.destination.index === 1 ||
|
||||
result.source.index === 1
|
||||
) {
|
||||
if (!result.destination) {
|
||||
return;
|
||||
}
|
||||
|
||||
const reorderFields = [...recordIndexFieldDefinitions];
|
||||
const [removed] = reorderFields.splice(result.source.index - 1, 1);
|
||||
reorderFields.splice(result.destination.index - 1, 0, removed);
|
||||
const reorderedVisibleBoardFields = moveArrayItem(visibleBoardFields, {
|
||||
fromIndex: result.source.index - 1,
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
const updatedFields = reorderFields.map((field, index) => ({
|
||||
...field,
|
||||
position: index,
|
||||
}));
|
||||
if (isDeeplyEqual(visibleBoardFields, reorderedVisibleBoardFields))
|
||||
return;
|
||||
|
||||
const updatedFields = [
|
||||
...reorderedVisibleBoardFields,
|
||||
...hiddenBoardFields,
|
||||
].map((field, index) => ({ ...field, position: index }));
|
||||
|
||||
setRecordIndexFieldDefinitions(updatedFields);
|
||||
persistViewFields(mapBoardFieldDefinitionsToViewFields(updatedFields));
|
||||
},
|
||||
[
|
||||
hiddenBoardFields,
|
||||
persistViewFields,
|
||||
recordIndexFieldDefinitions,
|
||||
setRecordIndexFieldDefinitions,
|
||||
visibleBoardFields,
|
||||
],
|
||||
);
|
||||
|
||||
@ -120,36 +130,43 @@ export const useRecordIndexOptionsForBoard = ({
|
||||
'size' | 'position'
|
||||
>,
|
||||
) => {
|
||||
const isNewViewField = !recordIndexFieldDefinitions.some(
|
||||
(fieldDefinition) =>
|
||||
fieldDefinition.fieldMetadataId ===
|
||||
updatedFieldDefinition.fieldMetadataId,
|
||||
const isNewViewField = !(
|
||||
updatedFieldDefinition.fieldMetadataId in
|
||||
recordIndexFieldDefinitionsByKey
|
||||
);
|
||||
|
||||
let updatedFieldsDefinitions: ColumnDefinition<FieldMetadata>[];
|
||||
|
||||
if (isNewViewField) {
|
||||
const correspondingFieldDefinition = columnDefinitions.find(
|
||||
(availableTableColumn) =>
|
||||
availableTableColumn.fieldMetadataId ===
|
||||
const correspondingFieldDefinition = availableColumnDefinitions.find(
|
||||
(availableColumnDefinition) =>
|
||||
availableColumnDefinition.fieldMetadataId ===
|
||||
updatedFieldDefinition.fieldMetadataId,
|
||||
);
|
||||
|
||||
if (!correspondingFieldDefinition) return;
|
||||
|
||||
const lastVisibleBoardField =
|
||||
visibleBoardFields[visibleBoardFields.length - 1];
|
||||
|
||||
updatedFieldsDefinitions = [
|
||||
...recordIndexFieldDefinitions,
|
||||
{ ...correspondingFieldDefinition, isVisible: true },
|
||||
{
|
||||
...correspondingFieldDefinition,
|
||||
position: lastVisibleBoardField.position + 1,
|
||||
isVisible: true,
|
||||
},
|
||||
];
|
||||
} else {
|
||||
updatedFieldsDefinitions = recordIndexFieldDefinitions.map(
|
||||
(exitingFieldDefinition) =>
|
||||
exitingFieldDefinition.fieldMetadataId ===
|
||||
(existingFieldDefinition) =>
|
||||
existingFieldDefinition.fieldMetadataId ===
|
||||
updatedFieldDefinition.fieldMetadataId
|
||||
? {
|
||||
...exitingFieldDefinition,
|
||||
isVisible: !exitingFieldDefinition.isVisible,
|
||||
...existingFieldDefinition,
|
||||
isVisible: !existingFieldDefinition.isVisible,
|
||||
}
|
||||
: exitingFieldDefinition,
|
||||
: existingFieldDefinition,
|
||||
);
|
||||
}
|
||||
|
||||
@ -160,10 +177,12 @@ export const useRecordIndexOptionsForBoard = ({
|
||||
);
|
||||
},
|
||||
[
|
||||
recordIndexFieldDefinitions,
|
||||
recordIndexFieldDefinitionsByKey,
|
||||
setRecordIndexFieldDefinitions,
|
||||
persistViewFields,
|
||||
columnDefinitions,
|
||||
availableColumnDefinitions,
|
||||
visibleBoardFields,
|
||||
recordIndexFieldDefinitions,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
@ -4,6 +4,7 @@ import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useTableColumns } from '@/object-record/record-table/hooks/useTableColumns';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
|
||||
export const useRecordIndexOptionsForTable = (recordTableId: string) => {
|
||||
const { getHiddenTableColumnsSelector, getVisibleTableColumnsSelector } =
|
||||
@ -26,11 +27,12 @@ export const useRecordIndexOptionsForTable = (recordTableId: string) => {
|
||||
return;
|
||||
}
|
||||
|
||||
const reorderFields = [...visibleTableColumns];
|
||||
const [removed] = reorderFields.splice(result.source.index - 1, 1);
|
||||
reorderFields.splice(result.destination.index - 1, 0, removed);
|
||||
const reorderedFields = moveArrayItem(visibleTableColumns, {
|
||||
fromIndex: result.source.index - 1,
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
handleColumnReorder(reorderFields);
|
||||
handleColumnReorder(reorderedFields);
|
||||
},
|
||||
[visibleTableColumns, handleColumnReorder],
|
||||
);
|
||||
|
||||
@ -9,9 +9,6 @@ import { RecordTableColumnDropdownMenu } from './RecordTableColumnDropdownMenu';
|
||||
|
||||
type ColumnHeadWithDropdownProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
isFirstColumn: boolean;
|
||||
isLastColumn: boolean;
|
||||
primaryColumnKey: string;
|
||||
};
|
||||
|
||||
const StyledDropdown = styled(Dropdown)`
|
||||
@ -21,22 +18,12 @@ const StyledDropdown = styled(Dropdown)`
|
||||
|
||||
export const ColumnHeadWithDropdown = ({
|
||||
column,
|
||||
isFirstColumn,
|
||||
isLastColumn,
|
||||
primaryColumnKey,
|
||||
}: ColumnHeadWithDropdownProps) => {
|
||||
return (
|
||||
<StyledDropdown
|
||||
dropdownId={column.fieldMetadataId + '-header'}
|
||||
clickableComponent={<ColumnHead column={column} />}
|
||||
dropdownComponents={
|
||||
<RecordTableColumnDropdownMenu
|
||||
column={column}
|
||||
isFirstColumn={isFirstColumn}
|
||||
isLastColumn={isLastColumn}
|
||||
primaryColumnKey={primaryColumnKey}
|
||||
/>
|
||||
}
|
||||
dropdownComponents={<RecordTableColumnDropdownMenu column={column} />}
|
||||
dropdownOffset={{ x: -1 }}
|
||||
dropdownPlacement="bottom-start"
|
||||
dropdownHotkeyScope={{ scope: column.fieldMetadataId + '-header' }}
|
||||
|
||||
@ -1,4 +1,7 @@
|
||||
import { useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { IconArrowLeft, IconArrowRight, IconEyeOff } from '@/ui/display/icon';
|
||||
import { DropdownMenuItemsContainer } from '@/ui/layout/dropdown/components/DropdownMenuItemsContainer';
|
||||
import { useDropdown } from '@/ui/layout/dropdown/hooks/useDropdown';
|
||||
@ -9,17 +12,23 @@ import { ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
export type RecordTableColumnDropdownMenuProps = {
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
isFirstColumn: boolean;
|
||||
isLastColumn: boolean;
|
||||
primaryColumnKey: string;
|
||||
};
|
||||
|
||||
export const RecordTableColumnDropdownMenu = ({
|
||||
column,
|
||||
isFirstColumn,
|
||||
isLastColumn,
|
||||
primaryColumnKey,
|
||||
}: RecordTableColumnDropdownMenuProps) => {
|
||||
const { getVisibleTableColumnsSelector } = useRecordTableStates();
|
||||
|
||||
const visibleTableColumns = useRecoilValue(getVisibleTableColumnsSelector());
|
||||
|
||||
const secondVisibleColumn = visibleTableColumns[1];
|
||||
const canMoveLeft =
|
||||
column.fieldMetadataId !== secondVisibleColumn?.fieldMetadataId;
|
||||
|
||||
const lastVisibleColumn = visibleTableColumns[visibleTableColumns.length - 1];
|
||||
const canMoveRight =
|
||||
column.fieldMetadataId !== lastVisibleColumn?.fieldMetadataId;
|
||||
|
||||
const { handleColumnVisibilityChange, handleMoveTableColumn } =
|
||||
useTableColumns();
|
||||
|
||||
@ -27,17 +36,17 @@ export const RecordTableColumnDropdownMenu = ({
|
||||
|
||||
const handleColumnMoveLeft = () => {
|
||||
closeDropdown();
|
||||
if (isFirstColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canMoveLeft) return;
|
||||
|
||||
handleMoveTableColumn('left', column);
|
||||
};
|
||||
|
||||
const handleColumnMoveRight = () => {
|
||||
closeDropdown();
|
||||
if (isLastColumn) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!canMoveRight) return;
|
||||
|
||||
handleMoveTableColumn('right', column);
|
||||
};
|
||||
|
||||
@ -46,18 +55,16 @@ export const RecordTableColumnDropdownMenu = ({
|
||||
handleColumnVisibilityChange(column);
|
||||
};
|
||||
|
||||
return column.fieldMetadataId === primaryColumnKey ? (
|
||||
<></>
|
||||
) : (
|
||||
return (
|
||||
<DropdownMenuItemsContainer>
|
||||
{!isFirstColumn && (
|
||||
{canMoveLeft && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowLeft}
|
||||
onClick={handleColumnMoveLeft}
|
||||
text="Move left"
|
||||
/>
|
||||
)}
|
||||
{!isLastColumn && (
|
||||
{canMoveRight && (
|
||||
<MenuItem
|
||||
LeftIcon={IconArrowRight}
|
||||
onClick={handleColumnMoveRight}
|
||||
|
||||
@ -1,8 +1,9 @@
|
||||
import { useCallback, useState } from 'react';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import styled from '@emotion/styled';
|
||||
import { useRecoilCallback, useRecoilState, useRecoilValue } from 'recoil';
|
||||
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnHead } from '@/object-record/record-table/components/ColumnHead';
|
||||
import { useRecordTableStates } from '@/object-record/record-table/hooks/internal/useRecordTableStates';
|
||||
import { useTableColumns } from '@/object-record/record-table/hooks/useTableColumns';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
@ -10,6 +11,7 @@ import { IconPlus } from '@/ui/display/icon';
|
||||
import { LightIconButton } from '@/ui/input/button/components/LightIconButton';
|
||||
import { useTrackPointer } from '@/ui/utilities/pointer-event/hooks/useTrackPointer';
|
||||
import { getSnapshotValue } from '@/ui/utilities/recoil-scope/utils/getSnapshotValue';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
|
||||
import { ColumnHeadWithDropdown } from './ColumnHeadWithDropdown';
|
||||
|
||||
@ -80,20 +82,19 @@ export const RecordTableHeaderCell = ({
|
||||
column: ColumnDefinition<FieldMetadata>;
|
||||
createRecord: () => void;
|
||||
}) => {
|
||||
const {
|
||||
getResizeFieldOffsetState,
|
||||
getTableColumnsState,
|
||||
getTableColumnsByKeySelector,
|
||||
getVisibleTableColumnsSelector,
|
||||
} = useRecordTableStates();
|
||||
const { getResizeFieldOffsetState, getTableColumnsState } =
|
||||
useRecordTableStates();
|
||||
|
||||
const [resizeFieldOffset, setResizeFieldOffset] = useRecoilState(
|
||||
getResizeFieldOffsetState(),
|
||||
);
|
||||
|
||||
const tableColumns = useRecoilValue(getTableColumnsState());
|
||||
const tableColumnsByKey = useRecoilValue(getTableColumnsByKeySelector());
|
||||
const visibleTableColumns = useRecoilValue(getVisibleTableColumnsSelector());
|
||||
const tableColumnsByKey = useMemo(
|
||||
() =>
|
||||
mapArrayToObject(tableColumns, ({ fieldMetadataId }) => fieldMetadataId),
|
||||
[tableColumns],
|
||||
);
|
||||
|
||||
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
||||
number | null
|
||||
@ -108,10 +109,6 @@ export const RecordTableHeaderCell = ({
|
||||
|
||||
const [iconVisibility, setIconVisibility] = useState(false);
|
||||
|
||||
const primaryColumn = visibleTableColumns.find(
|
||||
(column) => column.position === 0,
|
||||
);
|
||||
|
||||
const handleResizeHandlerMove = useCallback(
|
||||
(positionX: number) => {
|
||||
if (!initialPointerPositionX) return;
|
||||
@ -182,13 +179,12 @@ export const RecordTableHeaderCell = ({
|
||||
onMouseEnter={() => setIconVisibility(true)}
|
||||
onMouseLeave={() => setIconVisibility(false)}
|
||||
>
|
||||
<ColumnHeadWithDropdown
|
||||
column={column}
|
||||
isFirstColumn={column.position === 1}
|
||||
isLastColumn={column.position === visibleTableColumns.length - 1}
|
||||
primaryColumnKey={primaryColumn?.fieldMetadataId || ''}
|
||||
/>
|
||||
{iconVisibility && column.position === 0 && (
|
||||
{column.isLabelIdentifier ? (
|
||||
<ColumnHead column={column} />
|
||||
) : (
|
||||
<ColumnHeadWithDropdown column={column} />
|
||||
)}
|
||||
{iconVisibility && !!column.isLabelIdentifier && (
|
||||
<StyledHeaderIcon>
|
||||
<LightIconButton
|
||||
Icon={IconPlus}
|
||||
|
||||
@ -55,23 +55,21 @@ export const RecordTableRow = ({ recordId, rowIndex }: RecordTableRowProps) => {
|
||||
<StyledTd>
|
||||
<CheckboxCell />
|
||||
</StyledTd>
|
||||
{[...visibleTableColumns]
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position)
|
||||
.map((column, columnIndex) => {
|
||||
return inView ? (
|
||||
<RecordTableCellContext.Provider
|
||||
value={{
|
||||
columnDefinition: column,
|
||||
columnIndex,
|
||||
}}
|
||||
key={column.fieldMetadataId}
|
||||
>
|
||||
<RecordTableCellContainer />
|
||||
</RecordTableCellContext.Provider>
|
||||
) : (
|
||||
<td key={column.fieldMetadataId}></td>
|
||||
);
|
||||
})}
|
||||
{visibleTableColumns.map((column, columnIndex) =>
|
||||
inView ? (
|
||||
<RecordTableCellContext.Provider
|
||||
value={{
|
||||
columnDefinition: column,
|
||||
columnIndex,
|
||||
}}
|
||||
key={column.fieldMetadataId}
|
||||
>
|
||||
<RecordTableCellContainer />
|
||||
</RecordTableCellContext.Provider>
|
||||
) : (
|
||||
<td key={column.fieldMetadataId}></td>
|
||||
),
|
||||
)}
|
||||
<td></td>
|
||||
</tr>
|
||||
</RecordTableRowContext.Provider>
|
||||
|
||||
@ -14,7 +14,6 @@ import { allRowsSelectedStatusSelectorScopeMap } from '@/object-record/record-ta
|
||||
import { hiddenTableColumnsSelectorScopeMap } from '@/object-record/record-table/states/selectors/hiddenTableColumnsSelectorScopeMap';
|
||||
import { numberOfTableColumnsSelectorScopeMap } from '@/object-record/record-table/states/selectors/numberOfTableColumnsSelectorScopeMap';
|
||||
import { selectedRowIdsSelectorScopeMap } from '@/object-record/record-table/states/selectors/selectedRowIdsSelectorScopeMap';
|
||||
import { tableColumnsByKeySelectorScopeMap } from '@/object-record/record-table/states/selectors/tableColumnsByKeySelectorScopeMap';
|
||||
import { visibleTableColumnsSelectorScopeMap } from '@/object-record/record-table/states/selectors/visibleTableColumnsSelectorScopeMap';
|
||||
import { softFocusPositionStateScopeMap } from '@/object-record/record-table/states/softFocusPositionStateScopeMap';
|
||||
import { tableColumnsStateScopeMap } from '@/object-record/record-table/states/tableColumnsStateScopeMap';
|
||||
@ -106,10 +105,6 @@ export const useRecordTableStates = (recordTableId?: string) => {
|
||||
selectedRowIdsSelectorScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
getTableColumnsByKeySelector: getSelectorReadOnly(
|
||||
tableColumnsByKeySelectorScopeMap,
|
||||
scopeId,
|
||||
),
|
||||
getVisibleTableColumnsSelector: getSelectorReadOnly(
|
||||
visibleTableColumnsSelectorScopeMap,
|
||||
scopeId,
|
||||
|
||||
@ -1,7 +1,7 @@
|
||||
import { availableTableColumnsStateScopeMap } from '@/object-record/record-table/states/availableTableColumnsStateScopeMap';
|
||||
import { tableColumnsStateScopeMap } from '@/object-record/record-table/states/tableColumnsStateScopeMap';
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { availableTableColumnsStateScopeMap } from '../availableTableColumnsStateScopeMap';
|
||||
import { tableColumnsStateScopeMap } from '../tableColumnsStateScopeMap';
|
||||
import { mapArrayToObject } from '~/utils/array/mapArrayToObject';
|
||||
|
||||
export const hiddenTableColumnsSelectorScopeMap =
|
||||
createSelectorReadOnlyScopeMap({
|
||||
@ -9,19 +9,30 @@ export const hiddenTableColumnsSelectorScopeMap =
|
||||
get:
|
||||
({ scopeId }) =>
|
||||
({ get }) => {
|
||||
const columns = get(tableColumnsStateScopeMap({ scopeId }));
|
||||
const columnKeys = columns.map(
|
||||
const tableColumns = get(tableColumnsStateScopeMap({ scopeId }));
|
||||
const availableColumns = get(
|
||||
availableTableColumnsStateScopeMap({ scopeId }),
|
||||
);
|
||||
const tableColumnsByKey = mapArrayToObject(
|
||||
tableColumns,
|
||||
({ fieldMetadataId }) => fieldMetadataId,
|
||||
);
|
||||
const otherAvailableColumns = get(
|
||||
availableTableColumnsStateScopeMap({ scopeId }),
|
||||
).filter(
|
||||
({ fieldMetadataId }) => !columnKeys.includes(fieldMetadataId),
|
||||
);
|
||||
|
||||
return [
|
||||
...columns.filter((column) => !column.isVisible),
|
||||
...otherAvailableColumns,
|
||||
];
|
||||
const hiddenColumns = availableColumns
|
||||
.filter(
|
||||
({ fieldMetadataId }) =>
|
||||
!tableColumnsByKey[fieldMetadataId]?.isVisible,
|
||||
)
|
||||
.map((availableColumn) => {
|
||||
const { fieldMetadataId } = availableColumn;
|
||||
const existingTableColumn = tableColumnsByKey[fieldMetadataId];
|
||||
|
||||
return {
|
||||
...(existingTableColumn || availableColumn),
|
||||
isVisible: false,
|
||||
};
|
||||
});
|
||||
|
||||
return hiddenColumns;
|
||||
},
|
||||
});
|
||||
|
||||
@ -1,20 +0,0 @@
|
||||
import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata';
|
||||
import { ColumnDefinition } from '@/object-record/record-table/types/ColumnDefinition';
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { tableColumnsStateScopeMap } from '../tableColumnsStateScopeMap';
|
||||
|
||||
export const tableColumnsByKeySelectorScopeMap = createSelectorReadOnlyScopeMap(
|
||||
{
|
||||
key: 'tableColumnsByKeySelectorScopeMap',
|
||||
get:
|
||||
({ scopeId }) =>
|
||||
({ get }) =>
|
||||
get(tableColumnsStateScopeMap({ scopeId })).reduce<
|
||||
Record<string, ColumnDefinition<FieldMetadata>>
|
||||
>(
|
||||
(result, column) => ({ ...result, [column.fieldMetadataId]: column }),
|
||||
{},
|
||||
),
|
||||
},
|
||||
);
|
||||
@ -1,8 +1,6 @@
|
||||
import { availableTableColumnsStateScopeMap } from '@/object-record/record-table/states/availableTableColumnsStateScopeMap';
|
||||
import { tableColumnsStateScopeMap } from '@/object-record/record-table/states/tableColumnsStateScopeMap';
|
||||
import { createSelectorReadOnlyScopeMap } from '@/ui/utilities/recoil-scope/utils/createSelectorReadOnlyScopeMap';
|
||||
|
||||
import { tableColumnsStateScopeMap } from '../tableColumnsStateScopeMap';
|
||||
|
||||
export const visibleTableColumnsSelectorScopeMap =
|
||||
createSelectorReadOnlyScopeMap({
|
||||
key: 'visibleTableColumnsSelectorScopeMap',
|
||||
@ -10,16 +8,8 @@ export const visibleTableColumnsSelectorScopeMap =
|
||||
({ scopeId }) =>
|
||||
({ get }) => {
|
||||
const columns = get(tableColumnsStateScopeMap({ scopeId }));
|
||||
const availableColumnKeys = get(
|
||||
availableTableColumnsStateScopeMap({ scopeId }),
|
||||
).map(({ fieldMetadataId }) => fieldMetadataId);
|
||||
|
||||
return [...columns]
|
||||
.filter(
|
||||
(column) =>
|
||||
column.isVisible &&
|
||||
availableColumnKeys.includes(column.fieldMetadataId),
|
||||
)
|
||||
.sort((a, b) => a.position - b.position);
|
||||
return columns
|
||||
.filter((column) => column.isVisible)
|
||||
.sort((columnA, columnB) => columnA.position - columnB.position);
|
||||
},
|
||||
});
|
||||
|
||||
@ -4,6 +4,7 @@ import { FieldMetadata } from '@/object-record/record-field/types/FieldMetadata'
|
||||
export type ColumnDefinition<T extends FieldMetadata> = FieldDefinition<T> & {
|
||||
size: number;
|
||||
position: number;
|
||||
isLabelIdentifier?: boolean;
|
||||
isVisible?: boolean;
|
||||
viewFieldId?: string;
|
||||
};
|
||||
|
||||
Reference in New Issue
Block a user