Add RecordField abstraction (#13923)
This PR adds a new RecordField abstraction to mimick the behavior of RecordFilter, RecordSort and other similar concepts that allow to have something that is not related directly to views. It is closely following the same pattern with an Effect component to initialize it from views and CRUD hooks to modify it. Although for this concept we save to view fields automatically for every modification. This PR does not implement saving to view fields, it is just a parallel code path that is meant to be built to see if everything behaves the same then we'll remove the old code path at the end by saving record fields instead of the many states for columns, fields definitions, etc.
This commit is contained in:
+34
@@ -0,0 +1,34 @@
|
||||
import { RecordFieldsComponentInstanceContext } from '@/object-record/record-field/states/context/RecordFieldsComponentInstanceContext';
|
||||
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
||||
import { type PropsWithChildren } from 'react';
|
||||
|
||||
export type RecordComponentInstanceContextsWrapperProps = PropsWithChildren<{
|
||||
componentInstanceId: string;
|
||||
}>;
|
||||
|
||||
export const RecordComponentInstanceContextsWrapper = ({
|
||||
componentInstanceId,
|
||||
children,
|
||||
}: RecordComponentInstanceContextsWrapperProps) => {
|
||||
return (
|
||||
<RecordFilterGroupsComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<RecordFiltersComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<RecordSortsComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
<RecordFieldsComponentInstanceContext.Provider
|
||||
value={{ instanceId: componentInstanceId }}
|
||||
>
|
||||
{children}
|
||||
</RecordFieldsComponentInstanceContext.Provider>
|
||||
</RecordSortsComponentInstanceContext.Provider>
|
||||
</RecordFiltersComponentInstanceContext.Provider>
|
||||
</RecordFilterGroupsComponentInstanceContext.Provider>
|
||||
);
|
||||
};
|
||||
+13
-25
@@ -1,14 +1,12 @@
|
||||
import { type Meta, type StoryObj } from '@storybook/react';
|
||||
|
||||
import { objectMetadataItemsState } from '@/object-metadata/states/objectMetadataItemsState';
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { ObjectOptionsDropdownContent } from '@/object-record/object-options-dropdown/components/ObjectOptionsDropdownContent';
|
||||
import { OBJECT_OPTIONS_DROPDOWN_ID } from '@/object-record/object-options-dropdown/constants/ObjectOptionsDropdownId';
|
||||
import { ObjectOptionsDropdownContext } from '@/object-record/object-options-dropdown/states/contexts/ObjectOptionsDropdownContext';
|
||||
import { type ObjectOptionsContentId } from '@/object-record/object-options-dropdown/types/ObjectOptionsContentId';
|
||||
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { RecordIndexContextProvider } from '@/object-record/record-index/contexts/RecordIndexContext';
|
||||
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
||||
import { RecordTableComponentInstanceContext } from '@/object-record/record-table/states/context/RecordTableComponentInstanceContext';
|
||||
import { DropdownContent } from '@/ui/layout/dropdown/components/DropdownContent';
|
||||
import { ViewComponentInstanceContext } from '@/views/states/contexts/ViewComponentInstanceContext';
|
||||
@@ -42,30 +40,20 @@ const meta: Meta<typeof ObjectOptionsDropdownContent> = {
|
||||
}, [setObjectMetadataItems]);
|
||||
|
||||
return (
|
||||
<RecordFilterGroupsComponentInstanceContext.Provider
|
||||
value={{ instanceId }}
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
componentInstanceId={instanceId}
|
||||
>
|
||||
<RecordFiltersComponentInstanceContext.Provider
|
||||
value={{ instanceId }}
|
||||
>
|
||||
<RecordSortsComponentInstanceContext.Provider
|
||||
value={{ instanceId }}
|
||||
>
|
||||
<RecordTableComponentInstanceContext.Provider
|
||||
value={{ instanceId }}
|
||||
<RecordTableComponentInstanceContext.Provider value={{ instanceId }}>
|
||||
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
<ViewComponentInstanceContext.Provider value={{ instanceId }}>
|
||||
<MemoryRouter
|
||||
initialEntries={['/one', '/two', { pathname: '/three' }]}
|
||||
initialIndex={1}
|
||||
>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
</ViewComponentInstanceContext.Provider>
|
||||
</RecordTableComponentInstanceContext.Provider>
|
||||
</RecordSortsComponentInstanceContext.Provider>
|
||||
</RecordFiltersComponentInstanceContext.Provider>
|
||||
</RecordFilterGroupsComponentInstanceContext.Provider>
|
||||
<Story />
|
||||
</MemoryRouter>
|
||||
</ViewComponentInstanceContext.Provider>
|
||||
</RecordTableComponentInstanceContext.Provider>
|
||||
</RecordComponentInstanceContextsWrapper>
|
||||
);
|
||||
},
|
||||
ContextStoreDecorator,
|
||||
|
||||
+9
-1
@@ -1,6 +1,7 @@
|
||||
import { type OnDragEndResponder } from '@hello-pangea/dnd';
|
||||
import { useCallback } from 'react';
|
||||
|
||||
import { useReorderRecordFields } from '@/object-record/record-field/hooks/useReorderRecordFields';
|
||||
import { useTableColumns } from '@/object-record/record-table/hooks/useTableColumns';
|
||||
import { hiddenTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/hiddenTableColumnsComponentSelector';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
@@ -24,6 +25,8 @@ export const useObjectOptionsForTable = (
|
||||
{ recordTableId, objectMetadataId },
|
||||
);
|
||||
|
||||
const { reorderRecordFields } = useReorderRecordFields();
|
||||
|
||||
const handleReorderColumns: OnDragEndResponder = useCallback(
|
||||
async (result) => {
|
||||
if (
|
||||
@@ -39,9 +42,14 @@ export const useObjectOptionsForTable = (
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
reorderRecordFields({
|
||||
fromIndex: result.source.index - 1,
|
||||
toIndex: result.destination.index - 1,
|
||||
});
|
||||
|
||||
handleColumnReorder(reorderedFields);
|
||||
},
|
||||
[visibleTableColumns, handleColumnReorder],
|
||||
[visibleTableColumns, handleColumnReorder, reorderRecordFields],
|
||||
);
|
||||
|
||||
return {
|
||||
|
||||
+70
@@ -0,0 +1,70 @@
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const useMoveRecordField = () => {
|
||||
const currentRecordFieldsCallbackState = useRecoilComponentCallbackState(
|
||||
currentRecordFieldsComponentState,
|
||||
);
|
||||
|
||||
const moveRecordField = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
({
|
||||
direction,
|
||||
fieldMetadataItemIdToMove,
|
||||
}: {
|
||||
direction: 'before' | 'after';
|
||||
fieldMetadataItemIdToMove: string;
|
||||
}) => {
|
||||
const currentRecordFields = snapshot
|
||||
.getLoadable(currentRecordFieldsCallbackState)
|
||||
.getValue();
|
||||
|
||||
const indexOfRecordFieldToMove = currentRecordFields.findIndex(
|
||||
(recordField) =>
|
||||
recordField.fieldMetadataItemId === fieldMetadataItemIdToMove,
|
||||
);
|
||||
|
||||
if (indexOfRecordFieldToMove === -1) {
|
||||
return;
|
||||
}
|
||||
|
||||
const newRecordFields = [...currentRecordFields];
|
||||
|
||||
const targetArrayIndex =
|
||||
direction === 'before'
|
||||
? indexOfRecordFieldToMove - 1
|
||||
: indexOfRecordFieldToMove + 1;
|
||||
|
||||
const targetArraySize = newRecordFields.length - 1;
|
||||
|
||||
if (
|
||||
indexOfRecordFieldToMove >= 0 &&
|
||||
targetArrayIndex >= 0 &&
|
||||
indexOfRecordFieldToMove <= targetArraySize &&
|
||||
targetArrayIndex <= targetArraySize
|
||||
) {
|
||||
const currentRecordField = newRecordFields[indexOfRecordFieldToMove];
|
||||
const targetRecordField = newRecordFields[targetArrayIndex];
|
||||
|
||||
const targetRecordFieldNewPosition = currentRecordField.position;
|
||||
const currentRecordFieldNewPosition = targetRecordField.position;
|
||||
|
||||
newRecordFields[indexOfRecordFieldToMove] = {
|
||||
...newRecordFields[indexOfRecordFieldToMove],
|
||||
position: currentRecordFieldNewPosition,
|
||||
};
|
||||
|
||||
newRecordFields[targetRecordFieldNewPosition] = {
|
||||
...newRecordFields[targetRecordFieldNewPosition],
|
||||
position: targetRecordFieldNewPosition,
|
||||
};
|
||||
|
||||
set(currentRecordFieldsCallbackState, newRecordFields);
|
||||
}
|
||||
},
|
||||
[currentRecordFieldsCallbackState],
|
||||
);
|
||||
|
||||
return { moveRecordField };
|
||||
};
|
||||
+39
@@ -0,0 +1,39 @@
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
import { moveArrayItem } from '~/utils/array/moveArrayItem';
|
||||
|
||||
export const useReorderRecordFields = () => {
|
||||
const currentRecordFieldsCallbackState = useRecoilComponentCallbackState(
|
||||
currentRecordFieldsComponentState,
|
||||
);
|
||||
|
||||
const reorderRecordFields = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
({ fromIndex, toIndex }: { fromIndex: number; toIndex: number }) => {
|
||||
const currentRecordFields = snapshot
|
||||
.getLoadable(currentRecordFieldsCallbackState)
|
||||
.getValue();
|
||||
|
||||
const reorderedRecordFields = moveArrayItem(currentRecordFields, {
|
||||
fromIndex,
|
||||
toIndex,
|
||||
});
|
||||
|
||||
const reorderedRecordFieldsWithNewPosition =
|
||||
reorderedRecordFields.map<RecordField>((recordField, index) => ({
|
||||
...recordField,
|
||||
position: index,
|
||||
}));
|
||||
|
||||
set(
|
||||
currentRecordFieldsCallbackState,
|
||||
reorderedRecordFieldsWithNewPosition,
|
||||
);
|
||||
},
|
||||
[currentRecordFieldsCallbackState],
|
||||
);
|
||||
|
||||
return { reorderRecordFields };
|
||||
};
|
||||
+58
@@ -0,0 +1,58 @@
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const useUpdateRecordField = () => {
|
||||
const currentRecordFieldsCallbackState = useRecoilComponentCallbackState(
|
||||
currentRecordFieldsComponentState,
|
||||
);
|
||||
|
||||
const updateRecordField = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(
|
||||
fieldMetadataItemId: string,
|
||||
partialRecordField: Partial<
|
||||
Pick<RecordField, 'isVisible' | 'size' | 'position'>
|
||||
>,
|
||||
) => {
|
||||
const currentRecordFields = getSnapshotValue(
|
||||
snapshot,
|
||||
currentRecordFieldsCallbackState,
|
||||
);
|
||||
|
||||
const foundRecordFieldInCurrentRecordFields = currentRecordFields.some(
|
||||
(existingRecordField) =>
|
||||
existingRecordField.fieldMetadataItemId === fieldMetadataItemId,
|
||||
);
|
||||
|
||||
if (!foundRecordFieldInCurrentRecordFields) {
|
||||
throw new Error(
|
||||
`Cannot find record field to update with field metadata item id : ${fieldMetadataItemId}`,
|
||||
);
|
||||
} else {
|
||||
set(currentRecordFieldsCallbackState, (currentRecordFields) => {
|
||||
const newCurrentRecordFields = [...currentRecordFields];
|
||||
|
||||
const indexOfRecordFieldToUpdate = newCurrentRecordFields.findIndex(
|
||||
(existingRecordField) =>
|
||||
existingRecordField.fieldMetadataItemId === fieldMetadataItemId,
|
||||
);
|
||||
|
||||
newCurrentRecordFields[indexOfRecordFieldToUpdate] = {
|
||||
...newCurrentRecordFields[indexOfRecordFieldToUpdate],
|
||||
...partialRecordField,
|
||||
};
|
||||
|
||||
return newCurrentRecordFields;
|
||||
});
|
||||
}
|
||||
},
|
||||
[currentRecordFieldsCallbackState],
|
||||
);
|
||||
|
||||
return {
|
||||
updateRecordField,
|
||||
};
|
||||
};
|
||||
+53
@@ -0,0 +1,53 @@
|
||||
import { currentRecordFieldsComponentState } from '@/object-record/record-field/states/currentRecordFieldsComponentState';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { useRecoilComponentCallbackState } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentCallbackState';
|
||||
import { getSnapshotValue } from '@/ui/utilities/state/utils/getSnapshotValue';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
export const useUpsertRecordField = () => {
|
||||
const currentRecordFieldsCallbackState = useRecoilComponentCallbackState(
|
||||
currentRecordFieldsComponentState,
|
||||
);
|
||||
|
||||
const upsertRecordField = useRecoilCallback(
|
||||
({ set, snapshot }) =>
|
||||
(recordFieldToUpsert: RecordField) => {
|
||||
const currentRecordFields = getSnapshotValue(
|
||||
snapshot,
|
||||
currentRecordFieldsCallbackState,
|
||||
);
|
||||
|
||||
const foundRecordFieldInCurrentRecordFields = currentRecordFields.some(
|
||||
(existingRecordField) =>
|
||||
existingRecordField.id === recordFieldToUpsert.id,
|
||||
);
|
||||
|
||||
if (!foundRecordFieldInCurrentRecordFields) {
|
||||
set(currentRecordFieldsCallbackState, [
|
||||
...currentRecordFields,
|
||||
recordFieldToUpsert,
|
||||
]);
|
||||
} else {
|
||||
set(currentRecordFieldsCallbackState, (currentRecordFields) => {
|
||||
const newCurrentRecordFields = [...currentRecordFields];
|
||||
|
||||
const indexOfRecordFieldToUpdate = newCurrentRecordFields.findIndex(
|
||||
(existingRecordField) =>
|
||||
existingRecordField.id === recordFieldToUpsert.id,
|
||||
);
|
||||
|
||||
newCurrentRecordFields[indexOfRecordFieldToUpdate] = {
|
||||
...recordFieldToUpsert,
|
||||
};
|
||||
|
||||
return newCurrentRecordFields;
|
||||
});
|
||||
}
|
||||
},
|
||||
[currentRecordFieldsCallbackState],
|
||||
);
|
||||
|
||||
return {
|
||||
upsertRecordField,
|
||||
};
|
||||
};
|
||||
+4
@@ -0,0 +1,4 @@
|
||||
import { createComponentInstanceContext } from '@/ui/utilities/state/component-state/utils/createComponentInstanceContext';
|
||||
|
||||
export const RecordFieldsComponentInstanceContext =
|
||||
createComponentInstanceContext();
|
||||
+11
@@ -0,0 +1,11 @@
|
||||
import { RecordFieldsComponentInstanceContext } from '@/object-record/record-field/states/context/RecordFieldsComponentInstanceContext';
|
||||
import { type RecordField } from '@/object-record/record-field/types/RecordField';
|
||||
import { createComponentState } from '@/ui/utilities/state/component-state/utils/createComponentState';
|
||||
|
||||
export const currentRecordFieldsComponentState = createComponentState<
|
||||
RecordField[]
|
||||
>({
|
||||
key: 'currentRecordFieldsComponentState',
|
||||
defaultValue: [],
|
||||
componentInstanceContext: RecordFieldsComponentInstanceContext,
|
||||
});
|
||||
@@ -0,0 +1,10 @@
|
||||
import { type AggregateOperations } from '~/generated/graphql';
|
||||
|
||||
export type RecordField = {
|
||||
id: string;
|
||||
fieldMetadataItemId: string;
|
||||
position: number;
|
||||
isVisible: boolean;
|
||||
size: number;
|
||||
aggregateOperation?: AggregateOperations | null;
|
||||
};
|
||||
+18
-28
@@ -3,17 +3,15 @@ import { RecordIndexContextProvider } from '@/object-record/record-index/context
|
||||
import { ActionMenuComponentInstanceContext } from '@/action-menu/states/contexts/ActionMenuComponentInstanceContext';
|
||||
import { getActionMenuIdFromRecordIndexId } from '@/action-menu/utils/getActionMenuIdFromRecordIndexId';
|
||||
import { getObjectPermissionsForObject } from '@/object-metadata/utils/getObjectPermissionsForObject';
|
||||
import { RecordComponentInstanceContextsWrapper } from '@/object-record/components/RecordComponentInstanceContextsWrapper';
|
||||
import { useObjectPermissions } from '@/object-record/hooks/useObjectPermissions';
|
||||
import { lastShowPageRecordIdState } from '@/object-record/record-field/ui/states/lastShowPageRecordId';
|
||||
import { RecordFilterGroupsComponentInstanceContext } from '@/object-record/record-filter-group/states/context/RecordFilterGroupsComponentInstanceContext';
|
||||
import { RecordFiltersComponentInstanceContext } from '@/object-record/record-filter/states/context/RecordFiltersComponentInstanceContext';
|
||||
import { RecordIndexContainer } from '@/object-record/record-index/components/RecordIndexContainer';
|
||||
import { RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect } from '@/object-record/record-index/components/RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect';
|
||||
import { RecordIndexLoadBaseOnContextStoreEffect } from '@/object-record/record-index/components/RecordIndexLoadBaseOnContextStoreEffect';
|
||||
import { RecordIndexPageHeader } from '@/object-record/record-index/components/RecordIndexPageHeader';
|
||||
import { useHandleIndexIdentifierClick } from '@/object-record/record-index/hooks/useHandleIndexIdentifierClick';
|
||||
import { useRecordIndexIdFromCurrentContextStore } from '@/object-record/record-index/hooks/useRecordIndexIdFromCurrentContextStore';
|
||||
import { RecordSortsComponentInstanceContext } from '@/object-record/record-sort/states/context/RecordSortsComponentInstanceContext';
|
||||
import { PageBody } from '@/ui/layout/page/components/PageBody';
|
||||
import { RECORD_INDEX_DRAG_SELECT_BOUNDARY_CLASS } from '@/ui/utilities/drag-select/constants/RecordIndecDragSelectBoundaryClass';
|
||||
import { PageTitle } from '@/ui/utilities/page-title/components/PageTitle';
|
||||
@@ -74,34 +72,26 @@ export const RecordIndexContainerGater = () => {
|
||||
<ViewComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordIndexId }}
|
||||
>
|
||||
<RecordFilterGroupsComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordIndexId }}
|
||||
<RecordComponentInstanceContextsWrapper
|
||||
componentInstanceId={recordIndexId}
|
||||
>
|
||||
<RecordFiltersComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordIndexId }}
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
||||
}}
|
||||
>
|
||||
<RecordSortsComponentInstanceContext.Provider
|
||||
value={{ instanceId: recordIndexId }}
|
||||
>
|
||||
<ActionMenuComponentInstanceContext.Provider
|
||||
value={{
|
||||
instanceId: getActionMenuIdFromRecordIndexId(recordIndexId),
|
||||
}}
|
||||
<PageTitle title={objectMetadataItem.labelPlural} />
|
||||
<RecordIndexPageHeader />
|
||||
<PageBody>
|
||||
<StyledIndexContainer
|
||||
className={RECORD_INDEX_DRAG_SELECT_BOUNDARY_CLASS}
|
||||
>
|
||||
<PageTitle title={objectMetadataItem.labelPlural} />
|
||||
<RecordIndexPageHeader />
|
||||
<PageBody>
|
||||
<StyledIndexContainer
|
||||
className={RECORD_INDEX_DRAG_SELECT_BOUNDARY_CLASS}
|
||||
>
|
||||
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect />
|
||||
<RecordIndexContainer />
|
||||
</StyledIndexContainer>
|
||||
</PageBody>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecordSortsComponentInstanceContext.Provider>
|
||||
</RecordFiltersComponentInstanceContext.Provider>
|
||||
</RecordFilterGroupsComponentInstanceContext.Provider>
|
||||
<RecordIndexContainerContextStoreNumberOfSelectedRecordsEffect />
|
||||
<RecordIndexContainer />
|
||||
</StyledIndexContainer>
|
||||
</PageBody>
|
||||
</ActionMenuComponentInstanceContext.Provider>
|
||||
</RecordComponentInstanceContextsWrapper>
|
||||
<RecordIndexLoadBaseOnContextStoreEffect />
|
||||
</ViewComponentInstanceContext.Provider>
|
||||
</RecordIndexContextProvider>
|
||||
|
||||
+1
@@ -5,6 +5,7 @@ import { type ColumnDefinition } from '@/object-record/record-table/types/Column
|
||||
|
||||
// TODO: see how we can better abstract this and set the correct interaction between view and table
|
||||
// but for now it allows to have a cleaner API globally.
|
||||
// TODO: should be solved with new RecordField abstraction
|
||||
export const useHandleColumnsChange = () => {
|
||||
const { saveColumnsToView } = useSaveColumnsToView();
|
||||
const { setTableColumns } = useSetTableColumns();
|
||||
|
||||
@@ -4,11 +4,15 @@ import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldM
|
||||
import { useUnfocusRecordTableCell } from '@/object-record/record-table/record-table-cell/hooks/useUnfocusRecordTableCell';
|
||||
import { useMoveViewColumns } from '@/views/hooks/useMoveViewColumns';
|
||||
|
||||
import { useMoveRecordField } from '@/object-record/record-field/hooks/useMoveRecordField';
|
||||
import { useUpdateRecordField } from '@/object-record/record-field/hooks/useUpdateRecordField';
|
||||
import { useUpsertRecordField } from '@/object-record/record-field/hooks/useUpsertRecordField';
|
||||
import { useHandleColumnsChange } from '@/object-record/record-table/hooks/useHandleColumnsChange';
|
||||
import { availableTableColumnsComponentState } from '@/object-record/record-table/states/availableTableColumnsComponentState';
|
||||
import { visibleTableColumnsComponentSelector } from '@/object-record/record-table/states/selectors/visibleTableColumnsComponentSelector';
|
||||
import { tableColumnsComponentState } from '@/object-record/record-table/states/tableColumnsComponentState';
|
||||
import { useRecoilComponentValue } from '@/ui/utilities/state/component-state/hooks/useRecoilComponentValue';
|
||||
import { v4 } from 'uuid';
|
||||
import { type ColumnDefinition } from '../types/ColumnDefinition';
|
||||
|
||||
type useRecordTableProps = {
|
||||
@@ -40,6 +44,9 @@ export const useTableColumns = ({
|
||||
|
||||
const { handleColumnsChange } = useHandleColumnsChange();
|
||||
|
||||
const { updateRecordField } = useUpdateRecordField();
|
||||
const { upsertRecordField } = useUpsertRecordField();
|
||||
|
||||
const handleColumnVisibilityChange = useCallback(
|
||||
async (
|
||||
viewField: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>,
|
||||
@@ -68,6 +75,14 @@ export const useTableColumns = ({
|
||||
{ ...newColumn, isVisible: true, position: lastPosition + 1 },
|
||||
];
|
||||
|
||||
upsertRecordField({
|
||||
id: v4(),
|
||||
fieldMetadataItemId: viewField.fieldMetadataId,
|
||||
size: 100,
|
||||
isVisible: true,
|
||||
position: lastPosition + 1,
|
||||
});
|
||||
|
||||
await handleColumnsChange({
|
||||
columns: nextColumns,
|
||||
objectMetadataId,
|
||||
@@ -80,6 +95,10 @@ export const useTableColumns = ({
|
||||
: previousColumn,
|
||||
);
|
||||
|
||||
updateRecordField(viewField.fieldMetadataId, {
|
||||
isVisible: !viewField.isVisible,
|
||||
});
|
||||
|
||||
await handleColumnsChange({
|
||||
columns: nextColumns,
|
||||
objectMetadataId,
|
||||
@@ -88,15 +107,19 @@ export const useTableColumns = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
upsertRecordField,
|
||||
tableColumns,
|
||||
availableTableColumns,
|
||||
handleColumnsChange,
|
||||
visibleTableColumns,
|
||||
objectMetadataId,
|
||||
recordTableId,
|
||||
updateRecordField,
|
||||
],
|
||||
);
|
||||
|
||||
const { moveRecordField } = useMoveRecordField();
|
||||
|
||||
const handleMoveTableColumn = useCallback(
|
||||
async (
|
||||
direction: 'left' | 'right',
|
||||
@@ -115,6 +138,11 @@ export const useTableColumns = ({
|
||||
visibleTableColumns,
|
||||
);
|
||||
|
||||
moveRecordField({
|
||||
direction: direction === 'left' ? 'before' : 'after',
|
||||
fieldMetadataItemIdToMove: column.fieldMetadataId,
|
||||
});
|
||||
|
||||
await handleColumnsChange({
|
||||
columns,
|
||||
objectMetadataId,
|
||||
@@ -128,6 +156,7 @@ export const useTableColumns = ({
|
||||
handleColumnsChange,
|
||||
objectMetadataId,
|
||||
recordTableId,
|
||||
moveRecordField,
|
||||
],
|
||||
);
|
||||
|
||||
|
||||
+25
-12
@@ -2,6 +2,7 @@ import styled from '@emotion/styled';
|
||||
import { useCallback, useMemo, useState } from 'react';
|
||||
import { useRecoilCallback } from 'recoil';
|
||||
|
||||
import { useUpdateRecordField } from '@/object-record/record-field/hooks/useUpdateRecordField';
|
||||
import { isObjectReadOnly } from '@/object-record/record-field/ui/hooks/read-only/utils/isObjectReadOnly';
|
||||
import { type FieldMetadata } from '@/object-record/record-field/ui/types/FieldMetadata';
|
||||
import { useRecordTableContextOrThrow } from '@/object-record/record-table/contexts/RecordTableContext';
|
||||
@@ -135,13 +136,17 @@ export const RecordTableHeaderCell = ({
|
||||
const [initialPointerPositionX, setInitialPointerPositionX] = useState<
|
||||
number | null
|
||||
>(null);
|
||||
const [resizedFieldKey, setResizedFieldKey] = useState<string | null>(null);
|
||||
const [resizedFieldMetadataItemId, setResizedFieldMetadataItemId] = useState<
|
||||
string | null
|
||||
>(null);
|
||||
|
||||
const { handleColumnsChange } = useTableColumns({
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
recordTableId,
|
||||
});
|
||||
|
||||
const { updateRecordField } = useUpdateRecordField();
|
||||
|
||||
const handleResizeHandlerStart = useCallback<PointerEventListener>(
|
||||
({ x }) => {
|
||||
setInitialPointerPositionX(x);
|
||||
@@ -162,7 +167,7 @@ export const RecordTableHeaderCell = ({
|
||||
const handleResizeHandlerEnd = useRecoilCallback(
|
||||
({ snapshot, set }) =>
|
||||
async () => {
|
||||
if (!resizedFieldKey) return;
|
||||
if (!resizedFieldMetadataItemId) return;
|
||||
|
||||
const resizeFieldOffset = getSnapshotValue(
|
||||
snapshot,
|
||||
@@ -171,22 +176,27 @@ export const RecordTableHeaderCell = ({
|
||||
|
||||
const nextWidth = Math.round(
|
||||
Math.max(
|
||||
tableColumnsByKey[resizedFieldKey].size + resizeFieldOffset,
|
||||
tableColumnsByKey[resizedFieldMetadataItemId].size +
|
||||
resizeFieldOffset,
|
||||
COLUMN_MIN_WIDTH,
|
||||
),
|
||||
);
|
||||
|
||||
set(resizeFieldOffsetState, 0);
|
||||
setInitialPointerPositionX(null);
|
||||
setResizedFieldKey(null);
|
||||
setResizedFieldMetadataItemId(null);
|
||||
|
||||
if (nextWidth !== tableColumnsByKey[resizedFieldKey].size) {
|
||||
if (nextWidth !== tableColumnsByKey[resizedFieldMetadataItemId].size) {
|
||||
const nextColumns = tableColumns.map((column) =>
|
||||
column.fieldMetadataId === resizedFieldKey
|
||||
column.fieldMetadataId === resizedFieldMetadataItemId
|
||||
? { ...column, size: nextWidth }
|
||||
: column,
|
||||
);
|
||||
|
||||
updateRecordField(resizedFieldMetadataItemId, {
|
||||
size: nextWidth,
|
||||
});
|
||||
|
||||
await handleColumnsChange({
|
||||
columns: nextColumns,
|
||||
objectMetadataId: objectMetadataItem.id,
|
||||
@@ -195,19 +205,20 @@ export const RecordTableHeaderCell = ({
|
||||
}
|
||||
},
|
||||
[
|
||||
resizedFieldKey,
|
||||
resizedFieldMetadataItemId,
|
||||
resizeFieldOffsetState,
|
||||
tableColumnsByKey,
|
||||
setResizedFieldKey,
|
||||
setResizedFieldMetadataItemId,
|
||||
tableColumns,
|
||||
handleColumnsChange,
|
||||
objectMetadataItem,
|
||||
recordTableId,
|
||||
updateRecordField,
|
||||
],
|
||||
);
|
||||
|
||||
useTrackPointer({
|
||||
shouldTrackPointer: resizedFieldKey !== null,
|
||||
shouldTrackPointer: resizedFieldMetadataItemId !== null,
|
||||
onMouseDown: handleResizeHandlerStart,
|
||||
onMouseMove: handleResizeHandlerMove,
|
||||
onMouseUp: handleResizeHandlerEnd,
|
||||
@@ -251,10 +262,12 @@ export const RecordTableHeaderCell = ({
|
||||
return (
|
||||
<StyledColumnHeaderCell
|
||||
key={column.fieldMetadataId}
|
||||
isResizing={resizedFieldKey === column.fieldMetadataId}
|
||||
isResizing={resizedFieldMetadataItemId === column.fieldMetadataId}
|
||||
columnWidth={Math.max(
|
||||
tableColumnsByKey[column.fieldMetadataId].size +
|
||||
(resizedFieldKey === column.fieldMetadataId ? resizeFieldOffset : 0) +
|
||||
(resizedFieldMetadataItemId === column.fieldMetadataId
|
||||
? resizeFieldOffset
|
||||
: 0) +
|
||||
24,
|
||||
COLUMN_MIN_WIDTH,
|
||||
)}
|
||||
@@ -286,7 +299,7 @@ export const RecordTableHeaderCell = ({
|
||||
className="cursor-col-resize"
|
||||
role="separator"
|
||||
onPointerDown={() => {
|
||||
setResizedFieldKey(column.fieldMetadataId);
|
||||
setResizedFieldMetadataItemId(column.fieldMetadataId);
|
||||
}}
|
||||
/>
|
||||
)}
|
||||
|
||||
Reference in New Issue
Block a user