mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: add column and edit text cell
This commit is contained in:
parent
0719f54dec
commit
c453d734ab
@ -4,28 +4,32 @@ import { FieldController } from '$app/stores/effects/database/field/field_contro
|
||||
import { CellControllerBuilder } from '$app/stores/effects/database/cell/controller_builder';
|
||||
import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '$app/../services/backend';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { CellController } from '$app/stores/effects/database/cell/cell_controller';
|
||||
|
||||
export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => {
|
||||
const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>();
|
||||
const [cellController, setCellController] = useState<CellController<any, any>>();
|
||||
|
||||
useEffect(() => {
|
||||
if (!cellIdentifier || !cellCache || !fieldController) return;
|
||||
const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController);
|
||||
const cellController = builder.build();
|
||||
const c = builder.build();
|
||||
setCellController(c);
|
||||
|
||||
void (async () => {
|
||||
const cellData = await cellController.getCellData();
|
||||
const cellData = await c.getCellData();
|
||||
if (cellData.some) {
|
||||
setData(cellData.unwrap());
|
||||
}
|
||||
})();
|
||||
|
||||
return () => {
|
||||
void cellController.dispose();
|
||||
void c.dispose();
|
||||
};
|
||||
}, [cellIdentifier, cellCache, fieldController]);
|
||||
|
||||
return {
|
||||
cellController,
|
||||
data,
|
||||
};
|
||||
};
|
||||
|
@ -3,6 +3,8 @@ import { RowController } from '$app/stores/effects/database/row/row_controller';
|
||||
import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
||||
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
||||
import { useEffect, useState } from 'react';
|
||||
import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller';
|
||||
import { None } from 'ts-results';
|
||||
|
||||
export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => {
|
||||
const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]);
|
||||
@ -38,7 +40,14 @@ export const useRow = (viewId: string, databaseController: DatabaseController, r
|
||||
})();
|
||||
}, [rowController]);
|
||||
|
||||
const onNewColumnClick = async () => {
|
||||
if (!databaseController) return;
|
||||
const controller = new TypeOptionController(viewId, None);
|
||||
await controller.initialize();
|
||||
};
|
||||
|
||||
return {
|
||||
cells: cells,
|
||||
cells,
|
||||
onNewColumnClick,
|
||||
};
|
||||
};
|
||||
|
@ -0,0 +1,13 @@
|
||||
export const CheckboxSvg = () => {
|
||||
return (
|
||||
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<path d='M6.5 8L8.11538 9.5L13.5 4.5' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round' />
|
||||
<path
|
||||
d='M13 8.5V11.8889C13 12.1836 12.8829 12.4662 12.6746 12.6746C12.4662 12.8829 12.1836 13 11.8889 13H4.11111C3.81643 13 3.53381 12.8829 3.32544 12.6746C3.11706 12.4662 3 12.1836 3 11.8889V4.11111C3 3.81643 3.11706 3.53381 3.32544 3.32544C3.53381 3.11706 3.81643 3 4.11111 3H10.2222'
|
||||
stroke='currentColor'
|
||||
strokeLinecap='round'
|
||||
strokeLinejoin='round'
|
||||
/>
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -0,0 +1,8 @@
|
||||
export const EditorCheckSvg = () => {
|
||||
return (
|
||||
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<rect x='2' y='2' width='12' height='12' rx='4' fill='#00BCF0' />
|
||||
<path d='M6 8L7.61538 9.5L10.5 6.5' stroke='white' strokeLinecap='round' strokeLinejoin='round' />
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -0,0 +1,7 @@
|
||||
export const EditorUncheckSvg = () => {
|
||||
return (
|
||||
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
|
||||
<rect x='2.5' y='2.5' width='11' height='11' rx='3.5' stroke='#BDBDBD' />
|
||||
</svg>
|
||||
);
|
||||
};
|
@ -7,6 +7,7 @@ import { ViewLayoutTypePB } from '@/services/backend';
|
||||
import { DragDropContext } from 'react-beautiful-dnd';
|
||||
import { useState } from 'react';
|
||||
import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
||||
import { EditRow } from '$app/components/board/EditBoardRow/EditRow';
|
||||
|
||||
export const Board = ({ viewId }: { viewId: string }) => {
|
||||
const { controller, rows, groups, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutTypePB.Board);
|
||||
@ -39,7 +40,7 @@ export const Board = ({ viewId }: { viewId: string }) => {
|
||||
groups &&
|
||||
groups.map((group, index) => (
|
||||
<BoardBlock
|
||||
key={index}
|
||||
key={group.groupId}
|
||||
viewId={viewId}
|
||||
controller={controller}
|
||||
group={group}
|
||||
@ -52,7 +53,14 @@ export const Board = ({ viewId }: { viewId: string }) => {
|
||||
</div>
|
||||
</div>
|
||||
</DragDropContext>
|
||||
|
||||
{controller && showBoardRow && boardRowInfo && (
|
||||
<EditRow
|
||||
onClose={() => setShowBoardRow(false)}
|
||||
viewId={viewId}
|
||||
controller={controller}
|
||||
rowInfo={boardRowInfo}
|
||||
></EditRow>
|
||||
)}
|
||||
</>
|
||||
);
|
||||
};
|
||||
|
@ -51,7 +51,7 @@ export const BoardBlock = ({
|
||||
viewId={viewId}
|
||||
controller={controller}
|
||||
index={index}
|
||||
key={index}
|
||||
key={row.row.id}
|
||||
rowInfo={row}
|
||||
onOpenRow={onOpenRow}
|
||||
></BoardCard>
|
||||
|
@ -14,5 +14,11 @@ export const BoardTextCell = ({
|
||||
}) => {
|
||||
const { data } = useCell(cellIdentifier, cellCache, fieldController);
|
||||
|
||||
return <div>{(data as string | undefined) || ''}</div>;
|
||||
return (
|
||||
<div>
|
||||
{((data as string | undefined) || '').split('\n').map((line, index) => (
|
||||
<div key={index}>{line}</div>
|
||||
))}
|
||||
</div>
|
||||
);
|
||||
};
|
||||
|
@ -0,0 +1,33 @@
|
||||
import { CellController } from '$app/stores/effects/database/cell/cell_controller';
|
||||
import { useEffect, useState, KeyboardEvent, useMemo } from 'react';
|
||||
|
||||
export const EditCellText = ({ data, cellController }: { data: string; cellController: CellController<any, any> }) => {
|
||||
const [value, setValue] = useState('');
|
||||
const [contentRows, setContentRows] = useState(1);
|
||||
useEffect(() => {
|
||||
setValue(data);
|
||||
}, [data]);
|
||||
|
||||
useEffect(() => {
|
||||
setContentRows(Math.max(1, value.split('\n').length));
|
||||
}, [value]);
|
||||
|
||||
const onTextFieldChange = async (v: string) => {
|
||||
setValue(v);
|
||||
};
|
||||
|
||||
const save = async () => {
|
||||
await cellController?.saveCellData(value);
|
||||
};
|
||||
|
||||
return (
|
||||
<div>
|
||||
<textarea
|
||||
rows={contentRows}
|
||||
value={value}
|
||||
onChange={(e) => onTextFieldChange(e.target.value)}
|
||||
onBlur={() => save()}
|
||||
/>
|
||||
</div>
|
||||
);
|
||||
};
|
@ -2,7 +2,7 @@ import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
||||
import { useCell } from '$app/components/_shared/database-hooks/useCell';
|
||||
import { CellCache } from '$app/stores/effects/database/cell/cell_cache';
|
||||
import { FieldController } from '$app/stores/effects/database/field/field_controller';
|
||||
import { FieldType, SelectOptionCellDataPB } from '@/services/backend';
|
||||
import { DateCellDataPB, FieldType, SelectOptionCellDataPB } from '@/services/backend';
|
||||
import { TextTypeSvg } from '$app/components/_shared/svg/TextTypeSvg';
|
||||
import { NumberTypeSvg } from '$app/components/_shared/svg/NumberTypeSvg';
|
||||
import { DateTypeSvg } from '$app/components/_shared/svg/DateTypeSvg';
|
||||
@ -11,8 +11,14 @@ import { MultiSelectTypeSvg } from '$app/components/_shared/svg/MultiSelectTypeS
|
||||
import { ChecklistTypeSvg } from '$app/components/_shared/svg/ChecklistTypeSvg';
|
||||
import { UrlTypeSvg } from '$app/components/_shared/svg/UrlTypeSvg';
|
||||
import { useAppSelector } from '$app/stores/store';
|
||||
import { getBgColor } from '$app/components/_shared/getColor';
|
||||
import { CheckboxSvg } from '$app/components/_shared/svg/CheckboxSvg';
|
||||
import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg';
|
||||
import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg';
|
||||
import { useState } from 'react';
|
||||
import { EditCellText } from '$app/components/board/EditBoardRow/EditCellText';
|
||||
|
||||
export const EditBoardCell = ({
|
||||
export const EditCellWrapper = ({
|
||||
cellIdentifier,
|
||||
cellCache,
|
||||
fieldController,
|
||||
@ -21,11 +27,19 @@ export const EditBoardCell = ({
|
||||
cellCache: CellCache;
|
||||
fieldController: FieldController;
|
||||
}) => {
|
||||
const { data } = useCell(cellIdentifier, cellCache, fieldController);
|
||||
const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
|
||||
const databaseStore = useAppSelector((state) => state.database);
|
||||
const [showEditField, setShowEditField] = useState(false);
|
||||
const onEditFieldClick = () => {
|
||||
setShowEditField(true);
|
||||
};
|
||||
|
||||
return (
|
||||
<div className={'flex w-full items-center'}>
|
||||
<div className={'flex w-[180px] cursor-pointer items-center gap-2 rounded-lg px-4 py-2 hover:bg-shade-6'}>
|
||||
<div
|
||||
onClick={() => onEditFieldClick()}
|
||||
className={'flex w-[180px] cursor-pointer items-center gap-2 rounded-lg px-4 py-2 hover:bg-shade-6'}
|
||||
>
|
||||
<div className={'h-5 w-5 flex-shrink-0'}>
|
||||
{cellIdentifier.fieldType === FieldType.RichText && <TextTypeSvg></TextTypeSvg>}
|
||||
{cellIdentifier.fieldType === FieldType.Number && <NumberTypeSvg></NumberTypeSvg>}
|
||||
@ -34,20 +48,37 @@ export const EditBoardCell = ({
|
||||
{cellIdentifier.fieldType === FieldType.MultiSelect && <MultiSelectTypeSvg></MultiSelectTypeSvg>}
|
||||
{cellIdentifier.fieldType === FieldType.Checklist && <ChecklistTypeSvg></ChecklistTypeSvg>}
|
||||
{cellIdentifier.fieldType === FieldType.URL && <UrlTypeSvg></UrlTypeSvg>}
|
||||
{cellIdentifier.fieldType === FieldType.Checkbox && <CheckboxSvg></CheckboxSvg>}
|
||||
</div>
|
||||
<span className={'overflow-hidden text-ellipsis whitespace-nowrap'}>
|
||||
{databaseStore.fields[cellIdentifier.fieldId].title}
|
||||
</span>
|
||||
</div>
|
||||
<div className={'flex-1'}>
|
||||
{(cellIdentifier.fieldType === FieldType.SingleSelect || cellIdentifier.fieldType === FieldType.MultiSelect) && (
|
||||
<div>
|
||||
<div className={'flex-1 cursor-pointer rounded-lg px-4 py-2 hover:bg-shade-6'}>
|
||||
{(cellIdentifier.fieldType === FieldType.SingleSelect ||
|
||||
cellIdentifier.fieldType === FieldType.MultiSelect ||
|
||||
cellIdentifier.fieldType === FieldType.Checklist) && (
|
||||
<div className={'flex items-center gap-2'}>
|
||||
{(data as SelectOptionCellDataPB | undefined)?.select_options?.map((option, index) => (
|
||||
<div key={index}>{option?.name || ''}</div>
|
||||
<div className={`${getBgColor(option.color)} rounded px-2 py-0.5 text-xs`} key={index}>
|
||||
{option?.name || ''}
|
||||
</div>
|
||||
)) || ''}
|
||||
</div>
|
||||
)}
|
||||
{<div>{data as string}</div>}
|
||||
|
||||
{cellIdentifier.fieldType === FieldType.Checkbox && (
|
||||
<div className={'h-8 w-8'}>
|
||||
{(data as boolean | undefined) ? <EditorCheckSvg></EditorCheckSvg> : <EditorUncheckSvg></EditorUncheckSvg>}
|
||||
</div>
|
||||
)}
|
||||
|
||||
{cellIdentifier.fieldType === FieldType.DateTime && <div>{(data as DateCellDataPB | undefined)?.date}</div>}
|
||||
|
||||
{(cellIdentifier.fieldType === FieldType.RichText ||
|
||||
cellIdentifier.fieldType === FieldType.URL ||
|
||||
cellIdentifier.fieldType === FieldType.Number) &&
|
||||
cellController && <EditCellText data={data as string} cellController={cellController}></EditCellText>}
|
||||
</div>
|
||||
</div>
|
||||
);
|
@ -2,9 +2,10 @@ import { CloseSvg } from '$app/components/_shared/svg/CloseSvg';
|
||||
import { useRow } from '$app/components/_shared/database-hooks/useRow';
|
||||
import { DatabaseController } from '$app/stores/effects/database/database_controller';
|
||||
import { RowInfo } from '$app/stores/effects/database/row/row_cache';
|
||||
import { EditBoardCell } from '$app/components/board/EditBoardRow/EditBoardCell';
|
||||
import { EditCellWrapper } from '$app/components/board/EditBoardRow/EditCellWrapper';
|
||||
import AddSvg from '$app/components/_shared/svg/AddSvg';
|
||||
|
||||
export const EditBoardRow = ({
|
||||
export const EditRow = ({
|
||||
onClose,
|
||||
viewId,
|
||||
controller,
|
||||
@ -15,26 +16,37 @@ export const EditBoardRow = ({
|
||||
controller: DatabaseController;
|
||||
rowInfo: RowInfo;
|
||||
}) => {
|
||||
const { cells } = useRow(viewId, controller, rowInfo);
|
||||
const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo);
|
||||
|
||||
return (
|
||||
<div className={'fixed inset-0 z-20 flex items-center justify-center bg-black/30 backdrop-blur-sm'}>
|
||||
<div className={'relative flex min-w-[70%] flex-col gap-8 rounded-xl bg-white p-8'}>
|
||||
<div className={'relative flex min-h-[50%] min-w-[70%] flex-col gap-8 rounded-xl bg-white px-8 pb-4 pt-12'}>
|
||||
<div onClick={() => onClose()} className={'absolute top-4 right-4'}>
|
||||
<button className={'block h-8 w-8 rounded-lg text-shade-2 hover:bg-main-secondary'}>
|
||||
<CloseSvg></CloseSvg>
|
||||
</button>
|
||||
</div>
|
||||
<div className={'flex flex-col gap-4'}>
|
||||
<div className={'flex flex-1 flex-col gap-4'}>
|
||||
{cells.map((cell, cellIndex) => (
|
||||
<EditBoardCell
|
||||
<EditCellWrapper
|
||||
key={cellIndex}
|
||||
cellIdentifier={cell.cellIdentifier}
|
||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||
fieldController={controller.fieldController}
|
||||
></EditBoardCell>
|
||||
></EditCellWrapper>
|
||||
))}
|
||||
</div>
|
||||
<div className={'border-t border-shade-6 pt-2'}>
|
||||
<button
|
||||
onClick={() => onNewColumnClick()}
|
||||
className={'flex w-full items-center gap-2 rounded-lg px-4 py-2 hover:bg-shade-6'}
|
||||
>
|
||||
<i className={'h-5 w-5'}>
|
||||
<AddSvg></AddSvg>
|
||||
</i>
|
||||
<span>New Column</span>
|
||||
</button>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
);
|
Loading…
Reference in New Issue
Block a user