Create board fields reorder (#2639)

* wip

* fields reorder works but fields are not yet persisted

* fields are persisted

* modify according to comments
This commit is contained in:
bosiraphael 2023-11-22 14:46:18 +01:00 committed by GitHub
parent 532e4342ec
commit 85646a8072
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 90 additions and 8 deletions

View File

@ -1,6 +1,7 @@
import styled from '@emotion/styled'; import styled from '@emotion/styled';
import { BoardContext } from '@/companies/states/contexts/BoardContext'; import { BoardContext } from '@/companies/states/contexts/BoardContext';
import { mapBoardFieldDefinitionsToViewFields } from '@/companies/utils/mapBoardFieldDefinitionsToViewFields';
import { BoardOptionsDropdown } from '@/ui/layout/board/components/BoardOptionsDropdown'; import { BoardOptionsDropdown } from '@/ui/layout/board/components/BoardOptionsDropdown';
import { BoardOptionsDropdownId } from '@/ui/layout/board/components/constants/BoardOptionsDropdownId'; import { BoardOptionsDropdownId } from '@/ui/layout/board/components/constants/BoardOptionsDropdownId';
import { import {
@ -10,6 +11,7 @@ import {
import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar'; import { EntityBoardActionBar } from '@/ui/layout/board/components/EntityBoardActionBar';
import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu'; import { EntityBoardContextMenu } from '@/ui/layout/board/components/EntityBoardContextMenu';
import { ViewBar } from '@/views/components/ViewBar'; import { ViewBar } from '@/views/components/ViewBar';
import { useViewFields } from '@/views/hooks/internal/useViewFields';
import { ViewScope } from '@/views/scopes/ViewScope'; import { ViewScope } from '@/views/scopes/ViewScope';
import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions'; import { opportunitiesBoardOptions } from '~/pages/opportunities/opportunitiesBoardOptions';
@ -36,6 +38,8 @@ export const CompanyBoard = ({
}: CompanyBoardProps) => { }: CompanyBoardProps) => {
const viewScopeId = 'company-board-view'; const viewScopeId = 'company-board-view';
const { persistViewFields } = useViewFields(viewScopeId);
return ( return (
<ViewScope <ViewScope
viewScopeId={viewScopeId} viewScopeId={viewScopeId}
@ -47,6 +51,9 @@ export const CompanyBoard = ({
<BoardContext.Provider <BoardContext.Provider
value={{ value={{
BoardRecoilScopeContext: CompanyBoardRecoilScopeContext, BoardRecoilScopeContext: CompanyBoardRecoilScopeContext,
onFieldsChange: (fields) => {
persistViewFields(mapBoardFieldDefinitionsToViewFields(fields));
},
}} }}
> >
<ViewBar <ViewBar

View File

@ -1,9 +1,13 @@
import { createContext } from 'react'; import { createContext } from 'react';
import { RecoilScopeContext } from '@/types/RecoilScopeContext'; import { RecoilScopeContext } from '@/types/RecoilScopeContext';
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
export const BoardContext = createContext<{ export const BoardContext = createContext<{
BoardRecoilScopeContext: RecoilScopeContext; BoardRecoilScopeContext: RecoilScopeContext;
onFieldsChange: (fields: BoardFieldDefinition<FieldMetadata>[]) => void;
}>({ }>({
BoardRecoilScopeContext: createContext<string | null>(null), BoardRecoilScopeContext: createContext<string | null>(null),
onFieldsChange: () => {},
}); });

View File

@ -0,0 +1,18 @@
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ViewField } from '@/views/types/ViewField';
export const mapBoardFieldDefinitionsToViewFields = (
fieldsDefinitions: BoardFieldDefinition<FieldMetadata>[],
): ViewField[] => {
return fieldsDefinitions.map(
(fieldDefinition): ViewField => ({
id: fieldDefinition.viewFieldId || '',
fieldMetadataId: fieldDefinition.fieldMetadataId,
size: 0,
position: fieldDefinition.position,
isVisible: fieldDefinition.isVisible ?? true,
definition: fieldDefinition,
}),
);
};

View File

@ -1,4 +1,5 @@
import { useContext, useRef, useState } from 'react'; import { useCallback, useContext, useRef, useState } from 'react';
import { OnDragEndResponder } from '@hello-pangea/dnd';
import { useRecoilState, useRecoilValue } from 'recoil'; import { useRecoilState, useRecoilValue } from 'recoil';
import { Key } from 'ts-key-enum'; import { Key } from 'ts-key-enum';
import { v4 } from 'uuid'; import { v4 } from 'uuid';
@ -107,10 +108,26 @@ export const BoardOptionsDropdownContent = ({
setCurrentMenu(menu); setCurrentMenu(menu);
}; };
const { handleFieldVisibilityChange } = useBoardCardFields(); const { handleFieldVisibilityChange, handleFieldsReorder } =
useBoardCardFields();
const { closeDropdown } = useDropdown(); const { closeDropdown } = useDropdown();
const handleReorderField: OnDragEndResponder = useCallback(
(result) => {
if (!result.destination) {
return;
}
const reorderFields = [...visibleBoardCardFields];
const [removed] = reorderFields.splice(result.source.index - 1, 1);
reorderFields.splice(result.destination.index - 1, 0, removed);
handleFieldsReorder(reorderFields);
},
[handleFieldsReorder, visibleBoardCardFields],
);
useScopedHotkeys( useScopedHotkeys(
Key.Escape, Key.Escape,
() => { () => {
@ -209,6 +226,7 @@ export const BoardOptionsDropdownContent = ({
fields={visibleBoardCardFields} fields={visibleBoardCardFields}
onVisibilityChange={handleFieldVisibilityChange} onVisibilityChange={handleFieldVisibilityChange}
isDraggable={true} isDraggable={true}
onDragEnd={handleReorderField}
/> />
)} )}
{hasVisibleFields && hasHiddenFields && <DropdownMenuSeparator />} {hasVisibleFields && hasHiddenFields && <DropdownMenuSeparator />}

View File

@ -1,3 +1,7 @@
import { useCallback } from 'react';
import { savedBoardCardFieldsFamilyState } from '@/ui/layout/board/states/savedBoardCardFieldsFamilyState';
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata'; import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState'; import { useRecoilScopedState } from '@/ui/utilities/recoil-scope/hooks/useRecoilScopedState';
@ -7,13 +11,18 @@ import { boardCardFieldsScopedState } from '../states/boardCardFieldsScopedState
import { useBoardContext } from './useBoardContext'; import { useBoardContext } from './useBoardContext';
export const useBoardCardFields = () => { export const useBoardCardFields = () => {
const { BoardRecoilScopeContext } = useBoardContext(); const { BoardRecoilScopeContext, onFieldsChange } = useBoardContext();
const [, setBoardCardFields] = useRecoilScopedState( const [, setBoardCardFields] = useRecoilScopedState(
boardCardFieldsScopedState, boardCardFieldsScopedState,
BoardRecoilScopeContext, BoardRecoilScopeContext,
); );
const [, setSavedBoardCardFields] = useRecoilScopedState(
savedBoardCardFieldsFamilyState,
BoardRecoilScopeContext,
);
const handleFieldVisibilityChange = ( const handleFieldVisibilityChange = (
field: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>, field: Omit<ColumnDefinition<FieldMetadata>, 'size' | 'position'>,
) => { ) => {
@ -26,5 +35,27 @@ export const useBoardCardFields = () => {
); );
}; };
return { handleFieldVisibilityChange }; const handleFieldsChange = useCallback(
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
setSavedBoardCardFields(fields);
setBoardCardFields(fields);
await onFieldsChange?.(fields);
},
[setBoardCardFields, setSavedBoardCardFields, onFieldsChange],
);
const handleFieldsReorder = useCallback(
async (fields: BoardFieldDefinition<FieldMetadata>[]) => {
const updatedFields = fields.map((column, index) => ({
...column,
position: index,
}));
await handleFieldsChange(updatedFields);
},
[handleFieldsChange],
);
return { handleFieldVisibilityChange, handleFieldsReorder };
}; };

View File

@ -7,7 +7,7 @@ export const visibleBoardCardFieldsScopedSelector = selectorFamily({
get: get:
(scopeId: string) => (scopeId: string) =>
({ get }) => ({ get }) =>
get(boardCardFieldsScopedState(scopeId)).filter( get(boardCardFieldsScopedState(scopeId))
(field) => field.isVisible, .filter((field) => field.isVisible)
), .sort((a, b) => a.position - b.position),
}); });

View File

@ -5,4 +5,5 @@ export type BoardFieldDefinition<T extends FieldMetadata> =
FieldDefinition<T> & { FieldDefinition<T> & {
position: number; position: number;
isVisible?: boolean; isVisible?: boolean;
viewFieldId?: string;
}; };

View File

@ -1,3 +1,4 @@
import { BoardFieldDefinition } from '@/ui/layout/board/types/BoardFieldDefinition';
import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata'; import { FieldMetadata } from '@/ui/object/field/types/FieldMetadata';
import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition'; import { ColumnDefinition } from '@/ui/object/record-table/types/ColumnDefinition';
@ -7,5 +8,7 @@ export type ViewField = {
position: number; position: number;
isVisible: boolean; isVisible: boolean;
size: number; size: number;
definition: ColumnDefinition<FieldMetadata>; definition:
| ColumnDefinition<FieldMetadata>
| BoardFieldDefinition<FieldMetadata>;
}; };