mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: get board groups and rows
This commit is contained in:
parent
a9c8bad599
commit
c80e77ab16
@ -20,9 +20,9 @@ export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fi
|
|||||||
// ignore the return value, because we are using the subscription
|
// ignore the return value, because we are using the subscription
|
||||||
void cellController.getCellData();
|
void cellController.getCellData();
|
||||||
|
|
||||||
// dispose the cell controller when the component is unmounted
|
|
||||||
return () => {
|
return () => {
|
||||||
void cellController.dispose();
|
// dispose is causing an error
|
||||||
|
// void cellController.dispose();
|
||||||
};
|
};
|
||||||
}, []);
|
}, []);
|
||||||
|
|
||||||
|
@ -1,30 +1,26 @@
|
|||||||
import { useEffect, useState } from 'react';
|
import { useEffect, useState } from 'react';
|
||||||
import { DatabaseController } from '../../../stores/effects/database/database_controller';
|
import { DatabaseController } from '../../../stores/effects/database/database_controller';
|
||||||
import {
|
import { databaseActions, DatabaseFieldMap, IDatabaseColumn } from '../../../stores/reducers/database/slice';
|
||||||
databaseActions,
|
import { useAppDispatch } from '../../../stores/store';
|
||||||
DatabaseFieldMap,
|
|
||||||
IDatabaseColumn,
|
|
||||||
IDatabaseRow,
|
|
||||||
} from '../../../stores/reducers/database/slice';
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../../stores/store';
|
|
||||||
import loadField from './loadField';
|
import loadField from './loadField';
|
||||||
import { FieldInfo } from '../../../stores/effects/database/field/field_controller';
|
import { FieldInfo } from '../../../stores/effects/database/field/field_controller';
|
||||||
import { RowInfo } from '../../../stores/effects/database/row/row_cache';
|
import { RowInfo } from '../../../stores/effects/database/row/row_cache';
|
||||||
|
import { ViewLayoutTypePB } from '@/services/backend';
|
||||||
|
import { DatabaseGroupController } from '$app/stores/effects/database/group/group_controller';
|
||||||
|
|
||||||
export const useDatabase = (viewId: string) => {
|
export const useDatabase = (viewId: string, type?: ViewLayoutTypePB) => {
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
const databaseStore = useAppSelector((state) => state.database);
|
|
||||||
const boardStore = useAppSelector((state) => state.board);
|
|
||||||
const [controller, setController] = useState<DatabaseController>();
|
const [controller, setController] = useState<DatabaseController>();
|
||||||
const [rows, setRows] = useState<readonly RowInfo[]>([]);
|
const [rows, setRows] = useState<readonly RowInfo[]>([]);
|
||||||
|
const [groups, setGroups] = useState<readonly DatabaseGroupController[]>([]);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
if (!viewId.length) return;
|
if (!viewId.length) return;
|
||||||
const c = new DatabaseController(viewId);
|
const c = new DatabaseController(viewId);
|
||||||
setController(c);
|
setController(c);
|
||||||
|
|
||||||
// on unmount dispose the controller
|
// dispose is causing an error
|
||||||
return () => void c.dispose();
|
// return () => void c.dispose();
|
||||||
}, [viewId]);
|
}, [viewId]);
|
||||||
|
|
||||||
const loadFields = async (fieldInfos: readonly FieldInfo[]) => {
|
const loadFields = async (fieldInfos: readonly FieldInfo[]) => {
|
||||||
@ -45,7 +41,6 @@ export const useDatabase = (viewId: string) => {
|
|||||||
|
|
||||||
dispatch(databaseActions.updateFields({ fields }));
|
dispatch(databaseActions.updateFields({ fields }));
|
||||||
dispatch(databaseActions.updateColumns({ columns }));
|
dispatch(databaseActions.updateColumns({ columns }));
|
||||||
console.log(fields, columns);
|
|
||||||
};
|
};
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
@ -61,8 +56,12 @@ export const useDatabase = (viewId: string) => {
|
|||||||
},
|
},
|
||||||
});
|
});
|
||||||
await controller.open();
|
await controller.open();
|
||||||
|
|
||||||
|
if (type === ViewLayoutTypePB.Board) {
|
||||||
|
setGroups(controller.groups.value);
|
||||||
|
}
|
||||||
})();
|
})();
|
||||||
}, [controller]);
|
}, [controller]);
|
||||||
|
|
||||||
return { loadFields, controller, rows };
|
return { loadFields, controller, rows, groups };
|
||||||
};
|
};
|
||||||
|
@ -1,55 +0,0 @@
|
|||||||
import { useEffect, useState } from 'react';
|
|
||||||
import { useAppDispatch, useAppSelector } from '../../stores/store';
|
|
||||||
import { boardActions } from '../../stores/reducers/board/slice';
|
|
||||||
import { ISelectOption, ISelectOptionType } from '../../stores/reducers/database/slice';
|
|
||||||
|
|
||||||
export const useBoard = () => {
|
|
||||||
const dispatch = useAppDispatch();
|
|
||||||
const groupingFieldId = useAppSelector((state) => state.board);
|
|
||||||
const database = useAppSelector((state) => state.database);
|
|
||||||
const [title, setTitle] = useState('');
|
|
||||||
const [boardColumns, setBoardColumns] = useState<ISelectOption[]>([]);
|
|
||||||
const [movingRowId, setMovingRowId] = useState<string | undefined>(undefined);
|
|
||||||
const [ghostLocation, setGhostLocation] = useState<{ column: number; row: number }>({ column: 0, row: 0 });
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
setTitle(database.title);
|
|
||||||
if (database.fields[groupingFieldId]) {
|
|
||||||
setBoardColumns(
|
|
||||||
(database.fields[groupingFieldId].fieldOptions as ISelectOptionType | undefined)?.selectOptions || []
|
|
||||||
);
|
|
||||||
}
|
|
||||||
}, [database, groupingFieldId]);
|
|
||||||
|
|
||||||
const changeGroupingField = (fieldId: string) => {
|
|
||||||
dispatch(
|
|
||||||
boardActions.setGroupingFieldId({
|
|
||||||
fieldId,
|
|
||||||
})
|
|
||||||
);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onGhostItemMove = (columnIndex: number, rowIndex: number) => {
|
|
||||||
setGhostLocation({ column: columnIndex, row: rowIndex });
|
|
||||||
};
|
|
||||||
|
|
||||||
const startMove = (rowId: string) => {
|
|
||||||
setMovingRowId(rowId);
|
|
||||||
};
|
|
||||||
|
|
||||||
const endMove = () => {
|
|
||||||
setMovingRowId(undefined);
|
|
||||||
};
|
|
||||||
|
|
||||||
return {
|
|
||||||
title,
|
|
||||||
boardColumns,
|
|
||||||
groupingFieldId,
|
|
||||||
changeGroupingField,
|
|
||||||
startMove,
|
|
||||||
endMove,
|
|
||||||
onGhostItemMove,
|
|
||||||
movingRowId,
|
|
||||||
ghostLocation,
|
|
||||||
};
|
|
||||||
};
|
|
@ -2,29 +2,17 @@ import { SettingsSvg } from '../_shared/svg/SettingsSvg';
|
|||||||
import { SearchInput } from '../_shared/SearchInput';
|
import { SearchInput } from '../_shared/SearchInput';
|
||||||
import { BoardBlock } from './BoardBlock';
|
import { BoardBlock } from './BoardBlock';
|
||||||
import { NewBoardBlock } from './NewBoardBlock';
|
import { NewBoardBlock } from './NewBoardBlock';
|
||||||
import { useBoard } from './Board.hooks';
|
|
||||||
import { useDatabase } from '../_shared/database-hooks/useDatabase';
|
import { useDatabase } from '../_shared/database-hooks/useDatabase';
|
||||||
|
import { ViewLayoutTypePB } from '@/services/backend';
|
||||||
|
|
||||||
export const Board = ({ viewId }: { viewId: string }) => {
|
export const Board = ({ viewId }: { viewId: string }) => {
|
||||||
const { controller, rows } = useDatabase(viewId);
|
const { controller, rows, groups } = useDatabase(viewId, ViewLayoutTypePB.Board);
|
||||||
|
|
||||||
const {
|
|
||||||
title,
|
|
||||||
boardColumns,
|
|
||||||
groupingFieldId,
|
|
||||||
changeGroupingField,
|
|
||||||
startMove,
|
|
||||||
endMove,
|
|
||||||
onGhostItemMove,
|
|
||||||
movingRowId,
|
|
||||||
ghostLocation,
|
|
||||||
} = useBoard();
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className='flex w-full items-center justify-between'>
|
<div className='flex w-full items-center justify-between'>
|
||||||
<div className={'flex items-center text-xl font-semibold'}>
|
<div className={'flex items-center text-xl font-semibold'}>
|
||||||
<div>{title}</div>
|
<div>{'Kanban'}</div>
|
||||||
<button className={'ml-2 h-5 w-5'}>
|
<button className={'ml-2 h-5 w-5'}>
|
||||||
<SettingsSvg></SettingsSvg>
|
<SettingsSvg></SettingsSvg>
|
||||||
</button>
|
</button>
|
||||||
@ -37,16 +25,15 @@ export const Board = ({ viewId }: { viewId: string }) => {
|
|||||||
<div className={'relative w-full flex-1 overflow-auto'}>
|
<div className={'relative w-full flex-1 overflow-auto'}>
|
||||||
<div className={'absolute flex h-full flex-shrink-0 items-start justify-start gap-4'}>
|
<div className={'absolute flex h-full flex-shrink-0 items-start justify-start gap-4'}>
|
||||||
{controller &&
|
{controller &&
|
||||||
boardColumns?.map((column, index) => (
|
groups &&
|
||||||
|
groups.map((group, index) => (
|
||||||
<BoardBlock
|
<BoardBlock
|
||||||
|
key={index}
|
||||||
viewId={viewId}
|
viewId={viewId}
|
||||||
controller={controller}
|
controller={controller}
|
||||||
key={index}
|
rows={group.rows}
|
||||||
title={column.title}
|
title={group.name}
|
||||||
rows={rows}
|
allRows={rows}
|
||||||
groupingFieldId={groupingFieldId}
|
|
||||||
startMove={startMove}
|
|
||||||
endMove={endMove}
|
|
||||||
/>
|
/>
|
||||||
))}
|
))}
|
||||||
|
|
||||||
|
@ -3,23 +3,20 @@ import AddSvg from '../_shared/svg/AddSvg';
|
|||||||
import { BoardCard } from './BoardCard';
|
import { BoardCard } from './BoardCard';
|
||||||
import { RowInfo } from '../../stores/effects/database/row/row_cache';
|
import { RowInfo } from '../../stores/effects/database/row/row_cache';
|
||||||
import { DatabaseController } from '../../stores/effects/database/database_controller';
|
import { DatabaseController } from '../../stores/effects/database/database_controller';
|
||||||
|
import { RowPB } from '@/services/backend';
|
||||||
|
|
||||||
export const BoardBlock = ({
|
export const BoardBlock = ({
|
||||||
viewId,
|
viewId,
|
||||||
controller,
|
controller,
|
||||||
title,
|
title,
|
||||||
groupingFieldId,
|
|
||||||
rows,
|
rows,
|
||||||
startMove,
|
allRows,
|
||||||
endMove,
|
|
||||||
}: {
|
}: {
|
||||||
viewId: string;
|
viewId: string;
|
||||||
controller: DatabaseController;
|
controller: DatabaseController;
|
||||||
title: string;
|
title: string;
|
||||||
groupingFieldId: string;
|
rows: RowPB[];
|
||||||
rows: readonly RowInfo[];
|
allRows: readonly RowInfo[];
|
||||||
startMove: (id: string) => void;
|
|
||||||
endMove: () => void;
|
|
||||||
}) => {
|
}) => {
|
||||||
return (
|
return (
|
||||||
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-surface-1'}>
|
<div className={'flex h-full w-[250px] flex-col rounded-lg bg-surface-1'}>
|
||||||
@ -38,17 +35,14 @@ export const BoardBlock = ({
|
|||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'flex flex-1 flex-col gap-1 overflow-auto px-2'}>
|
<div className={'flex flex-1 flex-col gap-1 overflow-auto px-2'}>
|
||||||
{rows.map((row, index) => (
|
{rows.map((row_pb, index) => {
|
||||||
<BoardCard
|
const row = allRows.find((r) => r.row.id === row_pb.id);
|
||||||
viewId={viewId}
|
return row ? (
|
||||||
controller={controller}
|
<BoardCard viewId={viewId} controller={controller} key={index} rowInfo={row}></BoardCard>
|
||||||
key={index}
|
) : (
|
||||||
groupingFieldId={groupingFieldId}
|
<span key={index}></span>
|
||||||
row={row}
|
);
|
||||||
startMove={() => startMove(row.row.id)}
|
})}
|
||||||
endMove={() => endMove()}
|
|
||||||
></BoardCard>
|
|
||||||
))}
|
|
||||||
</div>
|
</div>
|
||||||
<div className={'p-2'}>
|
<div className={'p-2'}>
|
||||||
<button className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-surface-2'}>
|
<button className={'flex w-full items-center gap-2 rounded-lg px-2 py-2 hover:bg-surface-2'}>
|
||||||
|
@ -1,121 +1,38 @@
|
|||||||
import { DatabaseFieldMap, IDatabaseColumn, IDatabaseRow } from '../../stores/reducers/database/slice';
|
|
||||||
import { Details2Svg } from '../_shared/svg/Details2Svg';
|
import { Details2Svg } from '../_shared/svg/Details2Svg';
|
||||||
import { FieldType } from '../../../services/backend';
|
|
||||||
import { getBgColor } from '../_shared/getColor';
|
|
||||||
import { MouseEventHandler, useEffect, useRef, useState } from 'react';
|
|
||||||
import { RowInfo } from '../../stores/effects/database/row/row_cache';
|
import { RowInfo } from '../../stores/effects/database/row/row_cache';
|
||||||
import { useRow } from '../_shared/database-hooks/useRow';
|
import { useRow } from '../_shared/database-hooks/useRow';
|
||||||
import { DatabaseController } from '../../stores/effects/database/database_controller';
|
import { DatabaseController } from '../../stores/effects/database/database_controller';
|
||||||
import { useAppSelector } from '../../stores/store';
|
|
||||||
import { BoardCell } from './BoardCell';
|
import { BoardCell } from './BoardCell';
|
||||||
|
|
||||||
export const BoardCard = ({
|
export const BoardCard = ({
|
||||||
viewId,
|
viewId,
|
||||||
controller,
|
controller,
|
||||||
groupingFieldId,
|
rowInfo,
|
||||||
// fields,
|
|
||||||
// columns,
|
|
||||||
row,
|
|
||||||
startMove,
|
|
||||||
endMove,
|
|
||||||
}: {
|
}: {
|
||||||
viewId: string;
|
viewId: string;
|
||||||
controller: DatabaseController;
|
controller: DatabaseController;
|
||||||
groupingFieldId: string;
|
rowInfo: RowInfo;
|
||||||
// fields: DatabaseFieldMap;
|
|
||||||
// columns: IDatabaseColumn[];
|
|
||||||
row: RowInfo;
|
|
||||||
startMove: () => void;
|
|
||||||
endMove: () => void;
|
|
||||||
}) => {
|
}) => {
|
||||||
const { cells } = useRow(viewId, controller, row);
|
const { cells } = useRow(viewId, controller, rowInfo);
|
||||||
|
|
||||||
const databaseStore = useAppSelector((state) => state.database);
|
|
||||||
const [isMoving, setIsMoving] = useState(false);
|
|
||||||
const [isDown, setIsDown] = useState(false);
|
|
||||||
const [ghostWidth, setGhostWidth] = useState(0);
|
|
||||||
const [ghostHeight, setGhostHeight] = useState(0);
|
|
||||||
const [ghostLeft, setGhostLeft] = useState(0);
|
|
||||||
const [ghostTop, setGhostTop] = useState(0);
|
|
||||||
const el = useRef<HTMLDivElement>(null);
|
|
||||||
|
|
||||||
useEffect(() => {
|
|
||||||
if (el.current?.getBoundingClientRect && isMoving) {
|
|
||||||
const { left, top, width, height } = el.current.getBoundingClientRect();
|
|
||||||
setGhostWidth(width);
|
|
||||||
setGhostHeight(height);
|
|
||||||
setGhostLeft(left);
|
|
||||||
setGhostTop(top);
|
|
||||||
|
|
||||||
startMove();
|
|
||||||
|
|
||||||
const gEl = document.getElementById('ghost-block');
|
|
||||||
if (gEl?.innerHTML) {
|
|
||||||
gEl.innerHTML = el.current.innerHTML;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}, [el, isMoving]);
|
|
||||||
|
|
||||||
const onMouseMove: MouseEventHandler<HTMLDivElement> = (e) => {
|
|
||||||
setGhostLeft(ghostLeft + e.movementX);
|
|
||||||
setGhostTop(ghostTop + e.movementY);
|
|
||||||
};
|
|
||||||
|
|
||||||
const onMouseUp: MouseEventHandler<HTMLDivElement> = (e) => {
|
|
||||||
setIsMoving(false);
|
|
||||||
endMove();
|
|
||||||
};
|
|
||||||
|
|
||||||
const dragStart = () => {
|
|
||||||
if (isDown) {
|
|
||||||
setIsMoving(true);
|
|
||||||
setIsDown(false);
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<>
|
<div
|
||||||
<div
|
onClick={() => console.log('on click')}
|
||||||
ref={el}
|
className={`relative cursor-pointer select-none rounded-lg border border-shade-6 bg-white px-3 py-2 transition-transform duration-100 hover:bg-main-selector `}
|
||||||
onMouseDown={() => setIsDown(true)}
|
>
|
||||||
onMouseMove={dragStart}
|
<button className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-surface-2'}>
|
||||||
onMouseUp={() => setIsDown(false)}
|
<Details2Svg></Details2Svg>
|
||||||
onClick={() => console.log('on click')}
|
</button>
|
||||||
className={`relative cursor-pointer select-none rounded-lg border border-shade-6 bg-white px-3 py-2 transition-transform duration-100 hover:bg-main-selector `}
|
<div className={'flex flex-col gap-3'}>
|
||||||
>
|
{cells.map((cell, index) => (
|
||||||
<button className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-surface-2'}>
|
<BoardCell
|
||||||
<Details2Svg></Details2Svg>
|
key={index}
|
||||||
</button>
|
cellIdentifier={cell.cellIdentifier}
|
||||||
<div className={'flex flex-col gap-3'}>
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
{cells.map((cell, index) => (
|
fieldController={controller.fieldController}
|
||||||
<BoardCell
|
></BoardCell>
|
||||||
key={index}
|
))}
|
||||||
cellIdentifier={cell.cellIdentifier}
|
|
||||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
|
||||||
fieldController={controller.fieldController}
|
|
||||||
></BoardCell>
|
|
||||||
))}
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
{isMoving && (
|
</div>
|
||||||
<div
|
|
||||||
onMouseMove={onMouseMove}
|
|
||||||
onMouseUp={onMouseUp}
|
|
||||||
onMouseLeave={onMouseUp}
|
|
||||||
id={'ghost-block'}
|
|
||||||
className={
|
|
||||||
'fixed z-10 rotate-6 scale-105 cursor-pointer select-none rounded-lg border border-shade-6 bg-white px-3 py-2'
|
|
||||||
}
|
|
||||||
style={{
|
|
||||||
width: `${ghostWidth}px`,
|
|
||||||
height: `${ghostHeight}px`,
|
|
||||||
left: `${ghostLeft}px`,
|
|
||||||
top: `${ghostTop}px`,
|
|
||||||
}}
|
|
||||||
>
|
|
||||||
|
|
||||||
</div>
|
|
||||||
)}
|
|
||||||
</>
|
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
Loading…
Reference in New Issue
Block a user