mirror of
https://github.com/AppFlowy-IO/AppFlowy.git
synced 2024-08-30 18:12:39 +00:00
chore: edit cell options wip
This commit is contained in:
parent
bc9b1a5c33
commit
6bbf6873d6
@ -1,4 +1,4 @@
|
|||||||
import { KeyboardEventHandler, useEffect, useRef, useState } from 'react';
|
import { KeyboardEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react';
|
||||||
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
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';
|
||||||
@ -21,6 +21,7 @@ export const CellOptionsPopup = ({
|
|||||||
cellCache,
|
cellCache,
|
||||||
fieldController,
|
fieldController,
|
||||||
onOutsideClick,
|
onOutsideClick,
|
||||||
|
openOptionDetail,
|
||||||
}: {
|
}: {
|
||||||
top: number;
|
top: number;
|
||||||
left: number;
|
left: number;
|
||||||
@ -28,6 +29,7 @@ export const CellOptionsPopup = ({
|
|||||||
cellCache: CellCache;
|
cellCache: CellCache;
|
||||||
fieldController: FieldController;
|
fieldController: FieldController;
|
||||||
onOutsideClick: () => void;
|
onOutsideClick: () => void;
|
||||||
|
openOptionDetail: (_left: number, _top: number) => void;
|
||||||
}) => {
|
}) => {
|
||||||
const ref = useRef<HTMLDivElement>(null);
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
const inputRef = useRef<HTMLInputElement>(null);
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
@ -88,6 +90,19 @@ export const CellOptionsPopup = ({
|
|||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onOptionDetailClick: MouseEventHandler = (e) => {
|
||||||
|
e.stopPropagation();
|
||||||
|
let target = e.target as HTMLElement;
|
||||||
|
|
||||||
|
while (!(target instanceof HTMLButtonElement)) {
|
||||||
|
if (target.parentElement === null) return;
|
||||||
|
target = target.parentElement;
|
||||||
|
}
|
||||||
|
|
||||||
|
const { right: _left, top: _top } = target.getBoundingClientRect();
|
||||||
|
openOptionDetail(_left, _top);
|
||||||
|
};
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
ref={ref}
|
ref={ref}
|
||||||
@ -120,7 +135,7 @@ export const CellOptionsPopup = ({
|
|||||||
<div className={'font-mono text-shade-3'}>{value.length}/30</div>
|
<div className={'font-mono text-shade-3'}>{value.length}/30</div>
|
||||||
</div>
|
</div>
|
||||||
<div className={'-mx-4 h-[1px] bg-shade-6'}></div>
|
<div className={'-mx-4 h-[1px] bg-shade-6'}></div>
|
||||||
<div className={'font-semibold text-shade-3'}>{t('grid.selectOption.panelTitle') || ''}</div>
|
<div className={'font-medium text-shade-3'}>{t('grid.selectOption.panelTitle') || ''}</div>
|
||||||
<div className={'flex flex-col gap-1'}>
|
<div className={'flex flex-col gap-1'}>
|
||||||
{(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map(
|
{(databaseStore.fields[cellIdentifier.fieldId]?.fieldOptions as ISelectOptionType).selectOptions.map(
|
||||||
(option, index) => (
|
(option, index) => (
|
||||||
@ -148,7 +163,7 @@ export const CellOptionsPopup = ({
|
|||||||
<CheckmarkSvg></CheckmarkSvg>
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
</button>
|
</button>
|
||||||
)}
|
)}
|
||||||
<button className={'h-6 w-6 p-1'}>
|
<button onClick={onOptionDetailClick} className={'h-6 w-6 p-1'}>
|
||||||
<Details2Svg></Details2Svg>
|
<Details2Svg></Details2Svg>
|
||||||
</button>
|
</button>
|
||||||
</div>
|
</div>
|
||||||
|
@ -0,0 +1,185 @@
|
|||||||
|
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
|
||||||
|
import { CellCache } from '$app/stores/effects/database/cell/cell_cache';
|
||||||
|
import { FieldController } from '$app/stores/effects/database/field/field_controller';
|
||||||
|
import { KeyboardEventHandler, useEffect, useRef, useState } from 'react';
|
||||||
|
import { useTranslation } from 'react-i18next';
|
||||||
|
import { SelectOptionCellDataPB, SelectOptionColorPB } from '@/services/backend';
|
||||||
|
import { getBgColor } from '$app/components/_shared/getColor';
|
||||||
|
import { CloseSvg } from '$app/components/_shared/svg/CloseSvg';
|
||||||
|
import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc';
|
||||||
|
import useOutsideClick from '$app/components/_shared/useOutsideClick';
|
||||||
|
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
|
||||||
|
import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg';
|
||||||
|
|
||||||
|
export const EditCellOptionPopup = ({
|
||||||
|
left,
|
||||||
|
top,
|
||||||
|
cellIdentifier,
|
||||||
|
cellCache,
|
||||||
|
fieldController,
|
||||||
|
onOutsideClick,
|
||||||
|
}: {
|
||||||
|
left: number;
|
||||||
|
top: number;
|
||||||
|
cellIdentifier: CellIdentifier;
|
||||||
|
cellCache: CellCache;
|
||||||
|
fieldController: FieldController;
|
||||||
|
onOutsideClick: () => void;
|
||||||
|
}) => {
|
||||||
|
const ref = useRef<HTMLDivElement>(null);
|
||||||
|
const inputRef = useRef<HTMLInputElement>(null);
|
||||||
|
const { t } = useTranslation('');
|
||||||
|
const [adjustedTop, setAdjustedTop] = useState(-100);
|
||||||
|
const [value, setValue] = useState('');
|
||||||
|
|
||||||
|
useOutsideClick(ref, async () => {
|
||||||
|
onOutsideClick();
|
||||||
|
});
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (!ref.current) return;
|
||||||
|
const { height } = ref.current.getBoundingClientRect();
|
||||||
|
if (top + height > window.innerHeight) {
|
||||||
|
setAdjustedTop(window.innerHeight - height);
|
||||||
|
} else {
|
||||||
|
setAdjustedTop(top);
|
||||||
|
}
|
||||||
|
}, [ref, window, top, left]);
|
||||||
|
|
||||||
|
const onKeyDown: KeyboardEventHandler = async (e) => {
|
||||||
|
if (e.key === 'Enter' && value.length > 0) {
|
||||||
|
await new SelectOptionCellBackendService(cellIdentifier).createOption({ name: value });
|
||||||
|
setValue('');
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onKeyDownWrapper: KeyboardEventHandler = (e) => {
|
||||||
|
if (e.key === 'Escape') {
|
||||||
|
onOutsideClick();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
const onDeleteOptionClick = () => {
|
||||||
|
console.log('delete option');
|
||||||
|
};
|
||||||
|
|
||||||
|
return (
|
||||||
|
<div
|
||||||
|
ref={ref}
|
||||||
|
onKeyDown={onKeyDownWrapper}
|
||||||
|
className={`fixed z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md transition-opacity duration-300 ${
|
||||||
|
adjustedTop === -100 ? 'opacity-0' : 'opacity-100'
|
||||||
|
}`}
|
||||||
|
style={{ top: `${adjustedTop}px`, left: `${left}px` }}
|
||||||
|
>
|
||||||
|
<div className={'flex flex-col gap-2 p-2'}>
|
||||||
|
<div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
|
||||||
|
<input
|
||||||
|
ref={inputRef}
|
||||||
|
className={'py-2'}
|
||||||
|
value={value}
|
||||||
|
onChange={(e) => setValue(e.target.value)}
|
||||||
|
onKeyDown={onKeyDown}
|
||||||
|
/>
|
||||||
|
<div className={'font-mono text-shade-3'}>{value.length}/30</div>
|
||||||
|
</div>
|
||||||
|
<button
|
||||||
|
onClick={() => onDeleteOptionClick()}
|
||||||
|
className={
|
||||||
|
'flex cursor-pointer items-center gap-2 rounded-lg px-2 py-2 text-main-alert hover:bg-main-secondary'
|
||||||
|
}
|
||||||
|
>
|
||||||
|
<i className={'h-5 w-5'}>
|
||||||
|
<TrashSvg></TrashSvg>
|
||||||
|
</i>
|
||||||
|
<span>{t('grid.selectOption.deleteTag')}</span>
|
||||||
|
</button>
|
||||||
|
<div className={'-mx-4 h-[1px] bg-shade-6'}></div>
|
||||||
|
<div className={'my-2 font-medium text-shade-3'}>{t('grid.selectOption.colorPanelTitle')}</div>
|
||||||
|
<div className={'flex flex-col'}>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Purple)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.purpleColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Pink)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.pinkColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.LightPink)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.lightPinkColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Orange)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.orangeColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Yellow)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.yellowColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Lime)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.limeColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Green)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.greenColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Aqua)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.aquaColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
<div className={'flex cursor-pointer items-center justify-between rounded-lg p-2 hover:bg-main-secondary'}>
|
||||||
|
<div className={'flex items-center gap-2'}>
|
||||||
|
<div className={`h-4 w-4 rounded-full ${getBgColor(SelectOptionColorPB.Blue)}`}></div>
|
||||||
|
<span>{t('grid.selectOption.blueColor')}</span>
|
||||||
|
</div>
|
||||||
|
<i className={'block h-3 w-3'}>
|
||||||
|
<CheckmarkSvg></CheckmarkSvg>
|
||||||
|
</i>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
);
|
||||||
|
};
|
@ -15,6 +15,7 @@ import { FieldType } from '@/services/backend';
|
|||||||
import { CellOptionsPopup } from '$app/components/_shared/EditRow/CellOptionsPopup';
|
import { CellOptionsPopup } from '$app/components/_shared/EditRow/CellOptionsPopup';
|
||||||
import { DatePickerPopup } from '$app/components/_shared/EditRow/DatePickerPopup';
|
import { DatePickerPopup } from '$app/components/_shared/EditRow/DatePickerPopup';
|
||||||
import { DragDropContext, Droppable, OnDragEndResponder } from 'react-beautiful-dnd';
|
import { DragDropContext, Droppable, OnDragEndResponder } from 'react-beautiful-dnd';
|
||||||
|
import { EditCellOptionPopup } from '$app/components/_shared/EditRow/EditCellOptionPopup';
|
||||||
|
|
||||||
export const EditRow = ({
|
export const EditRow = ({
|
||||||
onClose,
|
onClose,
|
||||||
@ -48,6 +49,10 @@ export const EditRow = ({
|
|||||||
const [datePickerTop, setDatePickerTop] = useState(0);
|
const [datePickerTop, setDatePickerTop] = useState(0);
|
||||||
const [datePickerLeft, setDatePickerLeft] = useState(0);
|
const [datePickerLeft, setDatePickerLeft] = useState(0);
|
||||||
|
|
||||||
|
const [showEditCellOption, setShowEditCellOption] = useState(false);
|
||||||
|
const [editCellOptionTop, setEditCellOptionTop] = useState(0);
|
||||||
|
const [editCellOptionLeft, setEditCellOptionLeft] = useState(0);
|
||||||
|
|
||||||
useEffect(() => {
|
useEffect(() => {
|
||||||
setUnveil(true);
|
setUnveil(true);
|
||||||
}, []);
|
}, []);
|
||||||
@ -106,6 +111,12 @@ export const EditRow = ({
|
|||||||
setShowDatePicker(true);
|
setShowDatePicker(true);
|
||||||
};
|
};
|
||||||
|
|
||||||
|
const onOpenOptionDetailClick = (_left: number, _top: number) => {
|
||||||
|
setShowEditCellOption(true);
|
||||||
|
setEditCellOptionLeft(_left);
|
||||||
|
setEditCellOptionTop(_top);
|
||||||
|
};
|
||||||
|
|
||||||
const onDragEnd: OnDragEndResponder = (result) => {
|
const onDragEnd: OnDragEndResponder = (result) => {
|
||||||
if (!result.destination?.index) return;
|
if (!result.destination?.index) return;
|
||||||
void controller.moveField({
|
void controller.moveField({
|
||||||
@ -202,6 +213,7 @@ export const EditRow = ({
|
|||||||
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
fieldController={controller.fieldController}
|
fieldController={controller.fieldController}
|
||||||
onOutsideClick={() => setShowChangeOptionsPopup(false)}
|
onOutsideClick={() => setShowChangeOptionsPopup(false)}
|
||||||
|
openOptionDetail={onOpenOptionDetailClick}
|
||||||
></CellOptionsPopup>
|
></CellOptionsPopup>
|
||||||
)}
|
)}
|
||||||
{showDatePicker && editingCell && (
|
{showDatePicker && editingCell && (
|
||||||
@ -214,6 +226,18 @@ export const EditRow = ({
|
|||||||
onOutsideClick={() => setShowDatePicker(false)}
|
onOutsideClick={() => setShowDatePicker(false)}
|
||||||
></DatePickerPopup>
|
></DatePickerPopup>
|
||||||
)}
|
)}
|
||||||
|
{showEditCellOption && editingCell && (
|
||||||
|
<EditCellOptionPopup
|
||||||
|
top={editCellOptionTop}
|
||||||
|
left={editCellOptionLeft}
|
||||||
|
cellIdentifier={editingCell}
|
||||||
|
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
|
||||||
|
fieldController={controller.fieldController}
|
||||||
|
onOutsideClick={() => {
|
||||||
|
setShowEditCellOption(false);
|
||||||
|
}}
|
||||||
|
></EditCellOptionPopup>
|
||||||
|
)}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
);
|
);
|
||||||
|
Loading…
Reference in New Issue
Block a user