chore: HOC popup window

This commit is contained in:
ascarbek 2023-04-09 20:13:53 +06:00
parent 44585079b6
commit 2d85afe168
17 changed files with 140 additions and 185 deletions

View File

@ -1,4 +1,4 @@
import { KeyboardEventHandler, MouseEventHandler, useEffect, useRef, useState } from 'react'; import { KeyboardEventHandler, 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';
@ -9,10 +9,10 @@ import { useTranslation } from 'react-i18next';
import { Details2Svg } from '$app/components/_shared/svg/Details2Svg'; import { Details2Svg } from '$app/components/_shared/svg/Details2Svg';
import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg';
import { CloseSvg } from '$app/components/_shared/svg/CloseSvg'; import { CloseSvg } from '$app/components/_shared/svg/CloseSvg';
import useOutsideClick from '$app/components/_shared/useOutsideClick';
import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc';
import { useAppSelector } from '$app/stores/store'; import { useAppSelector } from '$app/stores/store';
import { ISelectOption, ISelectOptionType } from '$app/stores/reducers/database/slice'; import { ISelectOption, ISelectOptionType } from '$app/stores/reducers/database/slice';
import { PopupWindow } from '$app/components/_shared/PopupWindow';
export const CellOptionsPopup = ({ export const CellOptionsPopup = ({
top, top,
@ -31,28 +31,12 @@ export const CellOptionsPopup = ({
onOutsideClick: () => void; onOutsideClick: () => void;
openOptionDetail: (_left: number, _top: number, _select_option: SelectOptionPB) => void; openOptionDetail: (_left: number, _top: number, _select_option: SelectOptionPB) => void;
}) => { }) => {
const ref = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const { t } = useTranslation(''); const { t } = useTranslation('');
const [adjustedTop, setAdjustedTop] = useState(-100);
const [value, setValue] = useState(''); const [value, setValue] = useState('');
const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); const { data } = useCell(cellIdentifier, cellCache, fieldController);
const databaseStore = useAppSelector((state) => state.database); const databaseStore = useAppSelector((state) => state.database);
useEffect(() => {
if (!ref.current) return;
const { height } = ref.current.getBoundingClientRect();
if (top + height + 40 > window.innerHeight) {
setAdjustedTop(window.innerHeight - height - 40);
} else {
setAdjustedTop(top);
}
}, [ref, window, top, left]);
useOutsideClick(ref, async () => {
onOutsideClick();
});
useEffect(() => { useEffect(() => {
if (inputRef?.current) { if (inputRef?.current) {
inputRef.current.focus(); inputRef.current.focus();
@ -106,15 +90,8 @@ export const CellOptionsPopup = ({
}; };
return ( return (
<div <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
ref={ref} <div onKeyDown={onKeyDownWrapper} className={'flex flex-col gap-2 p-2'}>
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 + 40}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 '}> <div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
<div className={'flex flex-wrap items-center gap-2 text-black'}> <div className={'flex flex-wrap items-center gap-2 text-black'}>
{(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => ( {(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => (
@ -174,6 +151,6 @@ export const CellOptionsPopup = ({
)} )}
</div> </div>
</div> </div>
</div> </PopupWindow>
); );
}; };

View File

@ -1,8 +1,7 @@
import { FieldType } from '@/services/backend'; import { FieldType } from '@/services/backend';
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName';
import { useEffect, useMemo, useRef, useState } from 'react'; import { PopupWindow } from '$app/components/_shared/PopupWindow';
import useOutsideClick from '$app/components/_shared/useOutsideClick';
const typesOrder: FieldType[] = [ const typesOrder: FieldType[] = [
FieldType.RichText, FieldType.RichText,
@ -17,39 +16,17 @@ const typesOrder: FieldType[] = [
export const ChangeFieldTypePopup = ({ export const ChangeFieldTypePopup = ({
top, top,
right, left,
onClick, onClick,
onOutsideClick, onOutsideClick,
}: { }: {
top: number; top: number;
right: number; left: number;
onClick: (newType: FieldType) => void; onClick: (newType: FieldType) => void;
onOutsideClick: () => void; onOutsideClick: () => void;
}) => { }) => {
const ref = useRef<HTMLDivElement>(null);
const [adjustedTop, setAdjustedTop] = useState(-100);
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, right]);
return ( return (
<div <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
ref={ref}
className={`fixed z-10 rounded-lg bg-white p-2 text-xs shadow-md transition-opacity duration-300 ${
adjustedTop === -100 ? 'opacity-0' : 'opacity-100'
}`}
style={{ top: `${adjustedTop}px`, left: `${right + 30}px` }}
>
<div className={'flex flex-col'}> <div className={'flex flex-col'}>
{typesOrder.map((t, i) => ( {typesOrder.map((t, i) => (
<button <button
@ -66,6 +43,6 @@ export const ChangeFieldTypePopup = ({
</button> </button>
))} ))}
</div> </div>
</div> </PopupWindow>
); );
}; };

View File

@ -1,9 +1,8 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useState } from 'react';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
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 useOutsideClick from '$app/components/_shared/useOutsideClick';
import Calendar from 'react-calendar'; import Calendar from 'react-calendar';
import dayjs from 'dayjs'; import dayjs from 'dayjs';
import { ClockSvg } from '$app/components/_shared/svg/ClockSvg'; import { ClockSvg } from '$app/components/_shared/svg/ClockSvg';
@ -12,6 +11,7 @@ import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg';
import { useCell } from '$app/components/_shared/database-hooks/useCell'; import { useCell } from '$app/components/_shared/database-hooks/useCell';
import { CalendarData } from '$app/stores/effects/database/cell/controller_builder'; import { CalendarData } from '$app/stores/effects/database/cell/controller_builder';
import { DateCellDataPB } from '@/services/backend'; import { DateCellDataPB } from '@/services/backend';
import { PopupWindow } from '$app/components/_shared/PopupWindow';
export const DatePickerPopup = ({ export const DatePickerPopup = ({
left, left,
@ -29,25 +29,9 @@ export const DatePickerPopup = ({
onOutsideClick: () => void; onOutsideClick: () => void;
}) => { }) => {
const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
const ref = useRef<HTMLDivElement>(null);
const [adjustedTop, setAdjustedTop] = useState(-100);
const { t } = useTranslation(''); const { t } = useTranslation('');
const [selectedDate, setSelectedDate] = useState<Date>(new Date()); const [selectedDate, setSelectedDate] = useState<Date>(new Date());
useEffect(() => {
if (!ref.current) return;
const { height } = ref.current.getBoundingClientRect();
if (top + height + 40 > window.innerHeight) {
setAdjustedTop(top - height - 40);
} else {
setAdjustedTop(top);
}
}, [ref, window, top, left]);
useOutsideClick(ref, async () => {
onOutsideClick();
});
useEffect(() => { useEffect(() => {
const date_pb = data as DateCellDataPB | undefined; const date_pb = data as DateCellDataPB | undefined;
if (!date_pb || !date_pb?.date.length) return; if (!date_pb || !date_pb?.date.length) return;
@ -65,13 +49,7 @@ export const DatePickerPopup = ({
}; };
return ( return (
<div <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
ref={ref}
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 + 40}px`, left: `${left}px` }}
>
<div className={'px-2'}> <div className={'px-2'}>
<Calendar onChange={(d) => onChange(d)} value={selectedDate} /> <Calendar onChange={(d) => onChange(d)} value={selectedDate} />
</div> </div>
@ -96,6 +74,6 @@ export const DatePickerPopup = ({
<MoreSvg></MoreSvg> <MoreSvg></MoreSvg>
</i> </i>
</div> </div>
</div> </PopupWindow>
); );
}; };

View File

@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next';
import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend'; import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend';
import { getBgColor } from '$app/components/_shared/getColor'; import { getBgColor } from '$app/components/_shared/getColor';
import { SelectOptionCellBackendService } from '$app/stores/effects/database/cell/select_option_bd_svc'; 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 { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg'; import { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg';
import { PopupWindow } from '$app/components/_shared/PopupWindow';
export const EditCellOptionPopup = ({ export const EditCellOptionPopup = ({
left, left,
@ -21,33 +21,10 @@ export const EditCellOptionPopup = ({
editingSelectOption: SelectOptionPB; editingSelectOption: SelectOptionPB;
onOutsideClick: () => void; onOutsideClick: () => void;
}) => { }) => {
const ref = useRef<HTMLDivElement>(null);
const inputRef = useRef<HTMLInputElement>(null); const inputRef = useRef<HTMLInputElement>(null);
const { t } = useTranslation(''); const { t } = useTranslation('');
const [adjustedTop, setAdjustedTop] = useState(-100);
const [adjustedLeft, setAdjustedLeft] = useState(-100);
const [value, setValue] = useState(''); const [value, setValue] = useState('');
useOutsideClick(ref, async () => {
await onBlur();
onOutsideClick();
});
useEffect(() => {
if (!ref.current) return;
const { height, width } = ref.current.getBoundingClientRect();
if (top + height > window.innerHeight) {
setAdjustedTop(window.innerHeight - height);
} else {
setAdjustedTop(top);
}
if (left + width > window.innerWidth) {
setAdjustedLeft(window.innerWidth - width);
} else {
setAdjustedLeft(left);
}
}, [ref, window, top, left]);
useEffect(() => { useEffect(() => {
setValue(editingSelectOption.name); setValue(editingSelectOption.name);
}, [editingSelectOption]); }, [editingSelectOption]);
@ -93,15 +70,16 @@ export const EditCellOptionPopup = ({
}; };
return ( return (
<div <PopupWindow
ref={ref} className={'p-2 text-xs'}
onKeyDown={onKeyDownWrapper} onOutsideClick={async () => {
className={`fixed z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md transition-opacity duration-300 ${ await onBlur();
adjustedTop === -100 && adjustedLeft === -100 ? 'opacity-0' : 'opacity-100' onOutsideClick();
}`} }}
style={{ top: `${adjustedTop}px`, left: `${adjustedLeft}px` }} left={left}
top={top}
> >
<div className={'flex flex-col gap-2 p-2'}> <div onKeyDown={onKeyDownWrapper} 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 '}> <div className={'border-shades-3 flex flex-1 items-center gap-2 rounded border bg-main-selector px-2 '}>
<input <input
ref={inputRef} ref={inputRef}
@ -255,6 +233,6 @@ export const EditCellOptionPopup = ({
</div> </div>
</div> </div>
</div> </div>
</div> </PopupWindow>
); );
}; };

View File

@ -27,9 +27,9 @@ export const EditCellWrapper = ({
cellIdentifier: CellIdentifier; cellIdentifier: CellIdentifier;
cellCache: CellCache; cellCache: CellCache;
fieldController: FieldController; fieldController: FieldController;
onEditFieldClick: (top: number, right: number) => void; onEditFieldClick: (cell: CellIdentifier, left: number, top: number) => void;
onEditOptionsClick: (left: number, top: number) => void; onEditOptionsClick: (cell: CellIdentifier, left: number, top: number) => void;
onEditDateClick: (left: number, top: number) => void; onEditDateClick: (cell: CellIdentifier, left: number, top: number) => void;
}) => { }) => {
const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
const databaseStore = useAppSelector((state) => state.database); const databaseStore = useAppSelector((state) => state.database);
@ -38,7 +38,7 @@ export const EditCellWrapper = ({
const onClick = () => { const onClick = () => {
if (!el.current) return; if (!el.current) return;
const { top, right } = el.current.getBoundingClientRect(); const { top, right } = el.current.getBoundingClientRect();
onEditFieldClick(top, right); onEditFieldClick(cellIdentifier, right, top);
}; };
return ( return (
@ -69,7 +69,10 @@ export const EditCellWrapper = ({
cellIdentifier.fieldType === FieldType.MultiSelect || cellIdentifier.fieldType === FieldType.MultiSelect ||
cellIdentifier.fieldType === FieldType.Checklist) && cellIdentifier.fieldType === FieldType.Checklist) &&
cellController && ( cellController && (
<CellOptions data={data as SelectOptionCellDataPB} onEditClick={onEditOptionsClick}></CellOptions> <CellOptions
data={data as SelectOptionCellDataPB}
onEditClick={(left, top) => onEditOptionsClick(cellIdentifier, left, top)}
></CellOptions>
)} )}
{cellIdentifier.fieldType === FieldType.Checkbox && cellController && ( {cellIdentifier.fieldType === FieldType.Checkbox && cellController && (
@ -80,7 +83,10 @@ export const EditCellWrapper = ({
)} )}
{cellIdentifier.fieldType === FieldType.DateTime && ( {cellIdentifier.fieldType === FieldType.DateTime && (
<EditCellDate data={data as DateCellDataPB} onEditClick={onEditDateClick}></EditCellDate> <EditCellDate
data={data as DateCellDataPB}
onEditClick={(left, top) => onEditDateClick(cellIdentifier, left, top)}
></EditCellDate>
)} )}
{cellIdentifier.fieldType === FieldType.Number && cellController && ( {cellIdentifier.fieldType === FieldType.Number && cellController && (

View File

@ -1,5 +1,4 @@
import { useEffect, useRef, useState } from 'react'; import { useEffect, useRef, useState } from 'react';
import useOutsideClick from '$app/components/_shared/useOutsideClick';
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName';
@ -10,10 +9,11 @@ import { FieldInfo } from '$app/stores/effects/database/field/field_controller';
import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; import { MoreSvg } from '$app/components/_shared/svg/MoreSvg';
import { useAppSelector } from '$app/stores/store'; import { useAppSelector } from '$app/stores/store';
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
import { PopupWindow } from '$app/components/_shared/PopupWindow';
export const EditFieldPopup = ({ export const EditFieldPopup = ({
top, top,
right, left,
cellIdentifier, cellIdentifier,
viewId, viewId,
onOutsideClick, onOutsideClick,
@ -21,7 +21,7 @@ export const EditFieldPopup = ({
changeFieldTypeClick, changeFieldTypeClick,
}: { }: {
top: number; top: number;
right: number; left: number;
cellIdentifier: CellIdentifier; cellIdentifier: CellIdentifier;
viewId: string; viewId: string;
onOutsideClick: () => void; onOutsideClick: () => void;
@ -30,31 +30,13 @@ export const EditFieldPopup = ({
}) => { }) => {
const databaseStore = useAppSelector((state) => state.database); const databaseStore = useAppSelector((state) => state.database);
const { t } = useTranslation(''); const { t } = useTranslation('');
const ref = useRef<HTMLDivElement>(null);
const changeTypeButtonRef = useRef<HTMLDivElement>(null); const changeTypeButtonRef = useRef<HTMLDivElement>(null);
const [name, setName] = useState(''); const [name, setName] = useState('');
const [adjustedTop, setAdjustedTop] = useState(-100);
useOutsideClick(ref, async () => {
await save();
onOutsideClick();
});
useEffect(() => { useEffect(() => {
setName(databaseStore.fields[cellIdentifier.fieldId].title); setName(databaseStore.fields[cellIdentifier.fieldId].title);
}, [databaseStore, cellIdentifier]); }, [databaseStore, cellIdentifier]);
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, right]);
const save = async () => { const save = async () => {
if (!fieldInfo) return; if (!fieldInfo) return;
const controller = new TypeOptionController(viewId, Some(fieldInfo)); const controller = new TypeOptionController(viewId, Some(fieldInfo));
@ -78,12 +60,14 @@ export const EditFieldPopup = ({
}; };
return ( return (
<div <PopupWindow
ref={ref} className={'px-2 py-2 text-xs'}
className={`fixed z-10 rounded-lg bg-white px-2 py-2 text-xs shadow-md transition-opacity duration-300 ${ onOutsideClick={async () => {
adjustedTop === -100 ? 'opacity-0' : 'opacity-100' await save();
}`} onOutsideClick();
style={{ top: `${adjustedTop}px`, left: `${right + 10}px` }} }}
left={left}
top={top}
> >
<div className={'flex flex-col gap-2 p-2'}> <div className={'flex flex-col gap-2 p-2'}>
<input <input
@ -125,6 +109,6 @@ export const EditFieldPopup = ({
</i> </i>
</div> </div>
</div> </div>
</div> </PopupWindow>
); );
}; };

View File

@ -35,11 +35,11 @@ export const EditRow = ({
const [editingCell, setEditingCell] = useState<CellIdentifier | null>(null); const [editingCell, setEditingCell] = useState<CellIdentifier | null>(null);
const [showFieldEditor, setShowFieldEditor] = useState(false); const [showFieldEditor, setShowFieldEditor] = useState(false);
const [editFieldTop, setEditFieldTop] = useState(0); const [editFieldTop, setEditFieldTop] = useState(0);
const [editFieldRight, setEditFieldRight] = useState(0); const [editFieldLeft, setEditFieldLeft] = useState(0);
const [showChangeFieldTypePopup, setShowChangeFieldTypePopup] = useState(false); const [showChangeFieldTypePopup, setShowChangeFieldTypePopup] = useState(false);
const [changeFieldTypeTop, setChangeFieldTypeTop] = useState(0); const [changeFieldTypeTop, setChangeFieldTypeTop] = useState(0);
const [changeFieldTypeRight, setChangeFieldTypeRight] = useState(0); const [changeFieldTypeLeft, setChangeFieldTypeLeft] = useState(0);
const [showChangeOptionsPopup, setShowChangeOptionsPopup] = useState(false); const [showChangeOptionsPopup, setShowChangeOptionsPopup] = useState(false);
const [changeOptionsTop, setChangeOptionsTop] = useState(0); const [changeOptionsTop, setChangeOptionsTop] = useState(0);
@ -66,10 +66,10 @@ export const EditRow = ({
}, 300); }, 300);
}; };
const onEditFieldClick = (cellIdentifier: CellIdentifier, top: number, right: number) => { const onEditFieldClick = (cellIdentifier: CellIdentifier, left: number, top: number) => {
setEditingCell(cellIdentifier); setEditingCell(cellIdentifier);
setEditFieldTop(top); setEditFieldTop(top);
setEditFieldRight(right); setEditFieldLeft(left + 10);
setShowFieldEditor(true); setShowFieldEditor(true);
}; };
@ -81,7 +81,7 @@ export const EditRow = ({
const onChangeFieldTypeClick = (buttonTop: number, buttonRight: number) => { const onChangeFieldTypeClick = (buttonTop: number, buttonRight: number) => {
setChangeFieldTypeTop(buttonTop); setChangeFieldTypeTop(buttonTop);
setChangeFieldTypeRight(buttonRight); setChangeFieldTypeLeft(buttonRight + 30);
setShowChangeFieldTypePopup(true); setShowChangeFieldTypePopup(true);
}; };
@ -102,14 +102,14 @@ export const EditRow = ({
const onEditOptionsClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => { const onEditOptionsClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => {
setEditingCell(cellIdentifier); setEditingCell(cellIdentifier);
setChangeOptionsLeft(left); setChangeOptionsLeft(left);
setChangeOptionsTop(top); setChangeOptionsTop(top + 40);
setShowChangeOptionsPopup(true); setShowChangeOptionsPopup(true);
}; };
const onEditDateClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => { const onEditDateClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => {
setEditingCell(cellIdentifier); setEditingCell(cellIdentifier);
setDatePickerLeft(left); setDatePickerLeft(left);
setDatePickerTop(top); setDatePickerTop(top + 40);
setShowDatePicker(true); setShowDatePicker(true);
}; };
@ -165,11 +165,9 @@ export const EditRow = ({
cellIdentifier={cell.cellIdentifier} cellIdentifier={cell.cellIdentifier}
cellCache={controller.databaseViewCache.getRowCache().getCellCache()} cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
fieldController={controller.fieldController} fieldController={controller.fieldController}
onEditFieldClick={(top: number, right: number) => onEditFieldClick(cell.cellIdentifier, top, right)} onEditFieldClick={onEditFieldClick}
onEditOptionsClick={(left: number, top: number) => onEditOptionsClick={onEditOptionsClick}
onEditOptionsClick(cell.cellIdentifier, left, top) onEditDateClick={onEditDateClick}
}
onEditDateClick={(left: number, top: number) => onEditDateClick(cell.cellIdentifier, left, top)}
></EditCellWrapper> ></EditCellWrapper>
))} ))}
</div> </div>
@ -192,7 +190,7 @@ export const EditRow = ({
{showFieldEditor && editingCell && ( {showFieldEditor && editingCell && (
<EditFieldPopup <EditFieldPopup
top={editFieldTop} top={editFieldTop}
right={editFieldRight} left={editFieldLeft}
cellIdentifier={editingCell} cellIdentifier={editingCell}
viewId={viewId} viewId={viewId}
onOutsideClick={onOutsideEditFieldClick} onOutsideClick={onOutsideEditFieldClick}
@ -203,7 +201,7 @@ export const EditRow = ({
{showChangeFieldTypePopup && ( {showChangeFieldTypePopup && (
<ChangeFieldTypePopup <ChangeFieldTypePopup
top={changeFieldTypeTop} top={changeFieldTypeTop}
right={changeFieldTypeRight} left={changeFieldTypeLeft}
onClick={(newType) => changeFieldType(newType)} onClick={(newType) => changeFieldType(newType)}
onOutsideClick={() => setShowChangeFieldTypePopup(false)} onOutsideClick={() => setShowChangeFieldTypePopup(false)}
></ChangeFieldTypePopup> ></ChangeFieldTypePopup>

View File

@ -1,4 +1,4 @@
import { IPopupItem, Popup } from './Popup'; import { IPopupItem, PopupSelect } from './PopupSelect';
import i18n from 'i18next'; import i18n from 'i18next';
const supportedLanguages: { key: string; title: string }[] = [ const supportedLanguages: { key: string; title: string }[] = [
@ -37,11 +37,11 @@ export const LanguageSelectPopup = ({ onClose }: { onClose: () => void }) => {
icon: <></>, icon: <></>,
})); }));
return ( return (
<Popup <PopupSelect
items={items} items={items}
className={'absolute top-full right-0 z-10 w-[200px]'} className={'absolute top-full right-0 z-10 w-[200px]'}
onOutsideClick={onClose} onOutsideClick={onClose}
columns={2} columns={2}
></Popup> ></PopupSelect>
); );
}; };

View File

@ -7,7 +7,7 @@ export interface IPopupItem {
onClick: () => void; onClick: () => void;
} }
export const Popup = ({ export const PopupSelect = ({
items, items,
className = '', className = '',
onOutsideClick, onOutsideClick,

View File

@ -0,0 +1,51 @@
import { ReactNode, useEffect, useRef, useState } from 'react';
import useOutsideClick from '$app/components/_shared/useOutsideClick';
export const PopupWindow = ({
children,
className,
onOutsideClick,
left,
top,
}: {
children: ReactNode;
className: string;
onOutsideClick: () => void;
left: number;
top: number;
}) => {
const ref = useRef<HTMLDivElement>(null);
useOutsideClick(ref, onOutsideClick);
const [adjustedTop, setAdjustedTop] = useState(-100);
const [adjustedLeft, setAdjustedLeft] = useState(-100);
useEffect(() => {
if (!ref.current) return;
const { height, width } = ref.current.getBoundingClientRect();
if (top + height > window.innerHeight) {
setAdjustedTop(window.innerHeight - height);
} else {
setAdjustedTop(top);
}
if (left + width > window.innerWidth) {
setAdjustedLeft(window.innerWidth - width);
} else {
setAdjustedLeft(left);
}
}, [ref, left, top, window]);
return (
<div
ref={ref}
className={
'fixed z-10 rounded-lg bg-white shadow-md transition-opacity duration-300 ' +
(adjustedTop === -100 && adjustedLeft === -100 ? 'opacity-0 ' : 'opacity-100 ') +
(className || '')
}
style={{ top: `${adjustedTop}px`, left: `${adjustedLeft}px` }}
>
{children}
</div>
);
};

View File

@ -4,6 +4,7 @@ import { useRow } from '../_shared/database-hooks/useRow';
import { DatabaseController } from '$app/stores/effects/database/database_controller'; import { DatabaseController } from '$app/stores/effects/database/database_controller';
import { BoardCell } from './BoardCell'; import { BoardCell } from './BoardCell';
import { Draggable } from 'react-beautiful-dnd'; import { Draggable } from 'react-beautiful-dnd';
import { MouseEventHandler } from 'react';
export const BoardCard = ({ export const BoardCard = ({
index, index,
@ -22,6 +23,11 @@ export const BoardCard = ({
}) => { }) => {
const { cells } = useRow(viewId, controller, rowInfo); const { cells } = useRow(viewId, controller, rowInfo);
const onDetailClick: MouseEventHandler = (e) => {
e.stopPropagation();
// onOpenRow(rowInfo);
};
return ( return (
<Draggable draggableId={rowInfo.row.id} index={index}> <Draggable draggableId={rowInfo.row.id} index={index}>
{(provided) => ( {(provided) => (
@ -32,7 +38,7 @@ export const BoardCard = ({
onClick={() => onOpenRow(rowInfo)} onClick={() => onOpenRow(rowInfo)}
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 `} 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 `}
> >
<button className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-surface-2'}> <button onClick={onDetailClick} className={'absolute right-4 top-2.5 h-5 w-5 rounded hover:bg-surface-2'}>
<Details2Svg></Details2Svg> <Details2Svg></Details2Svg>
</button> </button>
<div className={'flex flex-col gap-3'}> <div className={'flex flex-col gap-3'}>

View File

@ -1,6 +1,6 @@
import { useEffect, useState } from 'react'; import { useEffect, useState } from 'react';
import { PropertiesSvg } from '$app/components/_shared/svg/PropertiesSvg'; import { PropertiesSvg } from '$app/components/_shared/svg/PropertiesSvg';
import { IPopupItem, Popup } from '$app/components/_shared/Popup'; import { IPopupItem, PopupSelect } from '$app/components/_shared/PopupSelect';
import { useTranslation } from 'react-i18next'; import { useTranslation } from 'react-i18next';
import { GroupByFieldSvg } from '$app/components/_shared/svg/GroupByFieldSvg'; import { GroupByFieldSvg } from '$app/components/_shared/svg/GroupByFieldSvg';
@ -39,10 +39,10 @@ export const BoardSettingsPopup = ({
}, [t]); }, [t]);
return ( return (
<Popup <PopupSelect
onOutsideClick={() => hidePopup()} onOutsideClick={() => hidePopup()}
items={settingsItems} items={settingsItems}
className={'absolute top-full left-full z-10 text-xs'} className={'absolute top-full left-full z-10 text-xs'}
></Popup> ></PopupSelect>
); );
}; };

View File

@ -88,7 +88,7 @@ export const GridTableHeaderItem = ({
{showFieldEditor && editingField && ( {showFieldEditor && editingField && (
<EditFieldPopup <EditFieldPopup
top={editFieldTop} top={editFieldTop}
right={editFieldRight} left={editFieldRight}
cellIdentifier={ cellIdentifier={
{ {
fieldId: editingField.fieldId, fieldId: editingField.fieldId,
@ -112,7 +112,7 @@ export const GridTableHeaderItem = ({
{showChangeFieldTypePopup && ( {showChangeFieldTypePopup && (
<ChangeFieldTypePopup <ChangeFieldTypePopup
top={changeFieldTypeTop} top={changeFieldTypeTop}
right={changeFieldTypeRight} left={changeFieldTypeRight}
onClick={(newType) => changeFieldType(newType)} onClick={(newType) => changeFieldType(newType)}
onOutsideClick={() => setShowChangeFieldTypePopup(false)} onOutsideClick={() => setShowChangeFieldTypePopup(false)}
></ChangeFieldTypePopup> ></ChangeFieldTypePopup>

View File

@ -1,4 +1,4 @@
import { IPopupItem, Popup } from '../../_shared/Popup'; import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
import { FilterSvg } from '../../_shared/svg/FilterSvg'; import { FilterSvg } from '../../_shared/svg/FilterSvg';
import { GroupBySvg } from '../../_shared/svg/GroupBySvg'; import { GroupBySvg } from '../../_shared/svg/GroupBySvg';
import { PropertiesSvg } from '../../_shared/svg/PropertiesSvg'; import { PropertiesSvg } from '../../_shared/svg/PropertiesSvg';
@ -51,5 +51,5 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
title: 'Group by', title: 'Group by',
}, },
]; ];
return <Popup items={items} className={'absolute top-full z-10 w-fit'} onOutsideClick={onClose} />; return <PopupSelect items={items} className={'absolute top-full z-10 w-fit'} onOutsideClick={onClose} />;
}; };

View File

@ -1,4 +1,4 @@
import { IPopupItem, Popup } from '../../_shared/Popup'; import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
import { LogoutSvg } from '../../_shared/svg/LogoutSvg'; import { LogoutSvg } from '../../_shared/svg/LogoutSvg';
export const OptionsPopup = ({ onSignOutClick, onClose }: { onSignOutClick: () => void; onClose: () => void }) => { export const OptionsPopup = ({ onSignOutClick, onClose }: { onSignOutClick: () => void; onClose: () => void }) => {
@ -14,10 +14,10 @@ export const OptionsPopup = ({ onSignOutClick, onClose }: { onSignOutClick: () =
}, },
]; ];
return ( return (
<Popup <PopupSelect
className={'absolute top-[50px] right-[30px] z-10 whitespace-nowrap'} className={'absolute top-[50px] right-[30px] z-10 whitespace-nowrap'}
items={items} items={items}
onOutsideClick={onClose} onOutsideClick={onClose}
></Popup> ></PopupSelect>
); );
}; };

View File

@ -1,4 +1,4 @@
import { IPopupItem, Popup } from '../../_shared/Popup'; import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
import { EditSvg } from '../../_shared/svg/EditSvg'; import { EditSvg } from '../../_shared/svg/EditSvg';
import { TrashSvg } from '../../_shared/svg/TrashSvg'; import { TrashSvg } from '../../_shared/svg/TrashSvg';
import { CopySvg } from '../../_shared/svg/CopySvg'; import { CopySvg } from '../../_shared/svg/CopySvg';
@ -47,11 +47,11 @@ export const NavItemOptionsPopup = ({
]; ];
return ( return (
<Popup <PopupSelect
onOutsideClick={() => onClose && onClose()} onOutsideClick={() => onClose && onClose()}
items={items} items={items}
className={`absolute right-0`} className={`absolute right-0`}
style={{ top: `${top}px` }} style={{ top: `${top}px` }}
></Popup> ></PopupSelect>
); );
}; };

View File

@ -1,4 +1,4 @@
import { IPopupItem, Popup } from '../../_shared/Popup'; import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
import { DocumentSvg } from '../../_shared/svg/DocumentSvg'; import { DocumentSvg } from '../../_shared/svg/DocumentSvg';
import { BoardSvg } from '../../_shared/svg/BoardSvg'; import { BoardSvg } from '../../_shared/svg/BoardSvg';
import { GridSvg } from '../../_shared/svg/GridSvg'; import { GridSvg } from '../../_shared/svg/GridSvg';
@ -47,11 +47,11 @@ export const NewPagePopup = ({
]; ];
return ( return (
<Popup <PopupSelect
onOutsideClick={() => onClose && onClose()} onOutsideClick={() => onClose && onClose()}
items={items} items={items}
className={'absolute right-0'} className={'absolute right-0'}
style={{ top: `${top}px` }} style={{ top: `${top}px` }}
></Popup> ></PopupSelect>
); );
}; };