chore: add column and edit text cell

This commit is contained in:
ascarbek 2023-03-22 19:54:42 +06:00 committed by Mike Abebe
parent 0719f54dec
commit c453d734ab
11 changed files with 155 additions and 24 deletions

View File

@ -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 { CellControllerBuilder } from '$app/stores/effects/database/cell/controller_builder';
import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '$app/../services/backend'; import { DateCellDataPB, SelectOptionCellDataPB, URLCellDataPB } from '$app/../services/backend';
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { CellController } from '$app/stores/effects/database/cell/cell_controller';
export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => { export const useCell = (cellIdentifier: CellIdentifier, cellCache: CellCache, fieldController: FieldController) => {
const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>(); const [data, setData] = useState<DateCellDataPB | URLCellDataPB | SelectOptionCellDataPB | string | undefined>();
const [cellController, setCellController] = useState<CellController<any, any>>();
useEffect(() => { useEffect(() => {
if (!cellIdentifier || !cellCache || !fieldController) return; if (!cellIdentifier || !cellCache || !fieldController) return;
const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController); const builder = new CellControllerBuilder(cellIdentifier, cellCache, fieldController);
const cellController = builder.build(); const c = builder.build();
setCellController(c);
void (async () => { void (async () => {
const cellData = await cellController.getCellData(); const cellData = await c.getCellData();
if (cellData.some) { if (cellData.some) {
setData(cellData.unwrap()); setData(cellData.unwrap());
} }
})(); })();
return () => { return () => {
void cellController.dispose(); void c.dispose();
}; };
}, [cellIdentifier, cellCache, fieldController]); }, [cellIdentifier, cellCache, fieldController]);
return { return {
cellController,
data, data,
}; };
}; };

View File

@ -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 { RowInfo } from '$app/stores/effects/database/row/row_cache';
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
import { useEffect, useState } from 'react'; 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) => { export const useRow = (viewId: string, databaseController: DatabaseController, rowInfo: RowInfo) => {
const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]); const [cells, setCells] = useState<{ fieldId: string; cellIdentifier: CellIdentifier }[]>([]);
@ -38,7 +40,14 @@ export const useRow = (viewId: string, databaseController: DatabaseController, r
})(); })();
}, [rowController]); }, [rowController]);
const onNewColumnClick = async () => {
if (!databaseController) return;
const controller = new TypeOptionController(viewId, None);
await controller.initialize();
};
return { return {
cells: cells, cells,
onNewColumnClick,
}; };
}; };

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -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>
);
};

View File

@ -7,6 +7,7 @@ import { ViewLayoutTypePB } from '@/services/backend';
import { DragDropContext } from 'react-beautiful-dnd'; import { DragDropContext } from 'react-beautiful-dnd';
import { useState } from 'react'; import { useState } from 'react';
import { RowInfo } from '$app/stores/effects/database/row/row_cache'; import { RowInfo } from '$app/stores/effects/database/row/row_cache';
import { EditRow } from '$app/components/board/EditBoardRow/EditRow';
export const Board = ({ viewId }: { viewId: string }) => { export const Board = ({ viewId }: { viewId: string }) => {
const { controller, rows, groups, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutTypePB.Board); const { controller, rows, groups, onNewRowClick, onDragEnd } = useDatabase(viewId, ViewLayoutTypePB.Board);
@ -39,7 +40,7 @@ export const Board = ({ viewId }: { viewId: string }) => {
groups && groups &&
groups.map((group, index) => ( groups.map((group, index) => (
<BoardBlock <BoardBlock
key={index} key={group.groupId}
viewId={viewId} viewId={viewId}
controller={controller} controller={controller}
group={group} group={group}
@ -52,7 +53,14 @@ export const Board = ({ viewId }: { viewId: string }) => {
</div> </div>
</div> </div>
</DragDropContext> </DragDropContext>
{controller && showBoardRow && boardRowInfo && (
<EditRow
onClose={() => setShowBoardRow(false)}
viewId={viewId}
controller={controller}
rowInfo={boardRowInfo}
></EditRow>
)}
</> </>
); );
}; };

View File

@ -51,7 +51,7 @@ export const BoardBlock = ({
viewId={viewId} viewId={viewId}
controller={controller} controller={controller}
index={index} index={index}
key={index} key={row.row.id}
rowInfo={row} rowInfo={row}
onOpenRow={onOpenRow} onOpenRow={onOpenRow}
></BoardCard> ></BoardCard>

View File

@ -14,5 +14,11 @@ export const BoardTextCell = ({
}) => { }) => {
const { data } = useCell(cellIdentifier, cellCache, fieldController); 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>
);
}; };

View File

@ -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>
);
};

View File

@ -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 { useCell } from '$app/components/_shared/database-hooks/useCell';
import { CellCache } from '$app/stores/effects/database/cell/cell_cache'; import { CellCache } from '$app/stores/effects/database/cell/cell_cache';
import { FieldController } from '$app/stores/effects/database/field/field_controller'; 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 { TextTypeSvg } from '$app/components/_shared/svg/TextTypeSvg';
import { NumberTypeSvg } from '$app/components/_shared/svg/NumberTypeSvg'; import { NumberTypeSvg } from '$app/components/_shared/svg/NumberTypeSvg';
import { DateTypeSvg } from '$app/components/_shared/svg/DateTypeSvg'; 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 { ChecklistTypeSvg } from '$app/components/_shared/svg/ChecklistTypeSvg';
import { UrlTypeSvg } from '$app/components/_shared/svg/UrlTypeSvg'; import { UrlTypeSvg } from '$app/components/_shared/svg/UrlTypeSvg';
import { useAppSelector } from '$app/stores/store'; 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, cellIdentifier,
cellCache, cellCache,
fieldController, fieldController,
@ -21,11 +27,19 @@ export const EditBoardCell = ({
cellCache: CellCache; cellCache: CellCache;
fieldController: FieldController; fieldController: FieldController;
}) => { }) => {
const { data } = useCell(cellIdentifier, cellCache, fieldController); const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
const databaseStore = useAppSelector((state) => state.database); const databaseStore = useAppSelector((state) => state.database);
const [showEditField, setShowEditField] = useState(false);
const onEditFieldClick = () => {
setShowEditField(true);
};
return ( return (
<div className={'flex w-full items-center'}> <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'}> <div className={'h-5 w-5 flex-shrink-0'}>
{cellIdentifier.fieldType === FieldType.RichText && <TextTypeSvg></TextTypeSvg>} {cellIdentifier.fieldType === FieldType.RichText && <TextTypeSvg></TextTypeSvg>}
{cellIdentifier.fieldType === FieldType.Number && <NumberTypeSvg></NumberTypeSvg>} {cellIdentifier.fieldType === FieldType.Number && <NumberTypeSvg></NumberTypeSvg>}
@ -34,20 +48,37 @@ export const EditBoardCell = ({
{cellIdentifier.fieldType === FieldType.MultiSelect && <MultiSelectTypeSvg></MultiSelectTypeSvg>} {cellIdentifier.fieldType === FieldType.MultiSelect && <MultiSelectTypeSvg></MultiSelectTypeSvg>}
{cellIdentifier.fieldType === FieldType.Checklist && <ChecklistTypeSvg></ChecklistTypeSvg>} {cellIdentifier.fieldType === FieldType.Checklist && <ChecklistTypeSvg></ChecklistTypeSvg>}
{cellIdentifier.fieldType === FieldType.URL && <UrlTypeSvg></UrlTypeSvg>} {cellIdentifier.fieldType === FieldType.URL && <UrlTypeSvg></UrlTypeSvg>}
{cellIdentifier.fieldType === FieldType.Checkbox && <CheckboxSvg></CheckboxSvg>}
</div> </div>
<span className={'overflow-hidden text-ellipsis whitespace-nowrap'}> <span className={'overflow-hidden text-ellipsis whitespace-nowrap'}>
{databaseStore.fields[cellIdentifier.fieldId].title} {databaseStore.fields[cellIdentifier.fieldId].title}
</span> </span>
</div> </div>
<div className={'flex-1'}> <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.SingleSelect ||
<div> cellIdentifier.fieldType === FieldType.MultiSelect ||
cellIdentifier.fieldType === FieldType.Checklist) && (
<div className={'flex items-center gap-2'}>
{(data as SelectOptionCellDataPB | undefined)?.select_options?.map((option, index) => ( {(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>
)} )}
{<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>
</div> </div>
); );

View File

@ -2,9 +2,10 @@ import { CloseSvg } from '$app/components/_shared/svg/CloseSvg';
import { useRow } from '$app/components/_shared/database-hooks/useRow'; import { useRow } from '$app/components/_shared/database-hooks/useRow';
import { DatabaseController } from '$app/stores/effects/database/database_controller'; import { DatabaseController } from '$app/stores/effects/database/database_controller';
import { RowInfo } from '$app/stores/effects/database/row/row_cache'; 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, onClose,
viewId, viewId,
controller, controller,
@ -15,26 +16,37 @@ export const EditBoardRow = ({
controller: DatabaseController; controller: DatabaseController;
rowInfo: RowInfo; rowInfo: RowInfo;
}) => { }) => {
const { cells } = useRow(viewId, controller, rowInfo); const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo);
return ( return (
<div className={'fixed inset-0 z-20 flex items-center justify-center bg-black/30 backdrop-blur-sm'}> <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'}> <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'}> <button className={'block h-8 w-8 rounded-lg text-shade-2 hover:bg-main-secondary'}>
<CloseSvg></CloseSvg> <CloseSvg></CloseSvg>
</button> </button>
</div> </div>
<div className={'flex flex-col gap-4'}> <div className={'flex flex-1 flex-col gap-4'}>
{cells.map((cell, cellIndex) => ( {cells.map((cell, cellIndex) => (
<EditBoardCell <EditCellWrapper
key={cellIndex} key={cellIndex}
cellIdentifier={cell.cellIdentifier} cellIdentifier={cell.cellIdentifier}
cellCache={controller.databaseViewCache.getRowCache().getCellCache()} cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
fieldController={controller.fieldController} fieldController={controller.fieldController}
></EditBoardCell> ></EditCellWrapper>
))} ))}
</div> </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>
</div> </div>
); );