mirror of
				https://github.com/AppFlowy-IO/AppFlowy.git
				synced 2025-11-04 12:03:28 +00:00 
			
		
		
		
	chore: HOC popup window
This commit is contained in:
		
							parent
							
								
									44585079b6
								
							
						
					
					
						commit
						2d85afe168
					
				@ -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 { useCell } from '$app/components/_shared/database-hooks/useCell';
 | 
			
		||||
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 { CheckmarkSvg } from '$app/components/_shared/svg/CheckmarkSvg';
 | 
			
		||||
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 { useAppSelector } from '$app/stores/store';
 | 
			
		||||
import { ISelectOption, ISelectOptionType } from '$app/stores/reducers/database/slice';
 | 
			
		||||
import { PopupWindow } from '$app/components/_shared/PopupWindow';
 | 
			
		||||
 | 
			
		||||
export const CellOptionsPopup = ({
 | 
			
		||||
  top,
 | 
			
		||||
@ -31,28 +31,12 @@ export const CellOptionsPopup = ({
 | 
			
		||||
  onOutsideClick: () => void;
 | 
			
		||||
  openOptionDetail: (_left: number, _top: number, _select_option: SelectOptionPB) => void;
 | 
			
		||||
}) => {
 | 
			
		||||
  const ref = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const inputRef = useRef<HTMLInputElement>(null);
 | 
			
		||||
  const { t } = useTranslation('');
 | 
			
		||||
  const [adjustedTop, setAdjustedTop] = useState(-100);
 | 
			
		||||
  const [value, setValue] = useState('');
 | 
			
		||||
  const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
 | 
			
		||||
  const { data } = useCell(cellIdentifier, cellCache, fieldController);
 | 
			
		||||
  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(() => {
 | 
			
		||||
    if (inputRef?.current) {
 | 
			
		||||
      inputRef.current.focus();
 | 
			
		||||
@ -106,15 +90,8 @@ export const CellOptionsPopup = ({
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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 + 40}px`, left: `${left}px` }}
 | 
			
		||||
    >
 | 
			
		||||
      <div className={'flex flex-col gap-2 p-2'}>
 | 
			
		||||
    <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
 | 
			
		||||
      <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={'flex flex-wrap items-center gap-2 text-black'}>
 | 
			
		||||
            {(data as SelectOptionCellDataPB)?.select_options?.map((option, index) => (
 | 
			
		||||
@ -174,6 +151,6 @@ export const CellOptionsPopup = ({
 | 
			
		||||
          )}
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </PopupWindow>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,8 +1,7 @@
 | 
			
		||||
import { FieldType } from '@/services/backend';
 | 
			
		||||
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
 | 
			
		||||
import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName';
 | 
			
		||||
import { useEffect, useMemo, useRef, useState } from 'react';
 | 
			
		||||
import useOutsideClick from '$app/components/_shared/useOutsideClick';
 | 
			
		||||
import { PopupWindow } from '$app/components/_shared/PopupWindow';
 | 
			
		||||
 | 
			
		||||
const typesOrder: FieldType[] = [
 | 
			
		||||
  FieldType.RichText,
 | 
			
		||||
@ -17,39 +16,17 @@ const typesOrder: FieldType[] = [
 | 
			
		||||
 | 
			
		||||
export const ChangeFieldTypePopup = ({
 | 
			
		||||
  top,
 | 
			
		||||
  right,
 | 
			
		||||
  left,
 | 
			
		||||
  onClick,
 | 
			
		||||
  onOutsideClick,
 | 
			
		||||
}: {
 | 
			
		||||
  top: number;
 | 
			
		||||
  right: number;
 | 
			
		||||
  left: number;
 | 
			
		||||
  onClick: (newType: FieldType) => 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 (
 | 
			
		||||
    <div
 | 
			
		||||
      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` }}
 | 
			
		||||
    >
 | 
			
		||||
    <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
 | 
			
		||||
      <div className={'flex flex-col'}>
 | 
			
		||||
        {typesOrder.map((t, i) => (
 | 
			
		||||
          <button
 | 
			
		||||
@ -66,6 +43,6 @@ export const ChangeFieldTypePopup = ({
 | 
			
		||||
          </button>
 | 
			
		||||
        ))}
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </PopupWindow>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,9 +1,8 @@
 | 
			
		||||
import { useEffect, useRef, useState } from 'react';
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
import { useTranslation } from 'react-i18next';
 | 
			
		||||
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 useOutsideClick from '$app/components/_shared/useOutsideClick';
 | 
			
		||||
import Calendar from 'react-calendar';
 | 
			
		||||
import dayjs from 'dayjs';
 | 
			
		||||
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 { CalendarData } from '$app/stores/effects/database/cell/controller_builder';
 | 
			
		||||
import { DateCellDataPB } from '@/services/backend';
 | 
			
		||||
import { PopupWindow } from '$app/components/_shared/PopupWindow';
 | 
			
		||||
 | 
			
		||||
export const DatePickerPopup = ({
 | 
			
		||||
  left,
 | 
			
		||||
@ -29,25 +29,9 @@ export const DatePickerPopup = ({
 | 
			
		||||
  onOutsideClick: () => void;
 | 
			
		||||
}) => {
 | 
			
		||||
  const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
 | 
			
		||||
  const ref = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const [adjustedTop, setAdjustedTop] = useState(-100);
 | 
			
		||||
  const { t } = useTranslation('');
 | 
			
		||||
  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(() => {
 | 
			
		||||
    const date_pb = data as DateCellDataPB | undefined;
 | 
			
		||||
    if (!date_pb || !date_pb?.date.length) return;
 | 
			
		||||
@ -65,13 +49,7 @@ export const DatePickerPopup = ({
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      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` }}
 | 
			
		||||
    >
 | 
			
		||||
    <PopupWindow className={'p-2 text-xs'} onOutsideClick={onOutsideClick} left={left} top={top}>
 | 
			
		||||
      <div className={'px-2'}>
 | 
			
		||||
        <Calendar onChange={(d) => onChange(d)} value={selectedDate} />
 | 
			
		||||
      </div>
 | 
			
		||||
@ -96,6 +74,6 @@ export const DatePickerPopup = ({
 | 
			
		||||
          <MoreSvg></MoreSvg>
 | 
			
		||||
        </i>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </PopupWindow>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -4,9 +4,9 @@ import { useTranslation } from 'react-i18next';
 | 
			
		||||
import { SelectOptionColorPB, SelectOptionPB } from '@/services/backend';
 | 
			
		||||
import { getBgColor } from '$app/components/_shared/getColor';
 | 
			
		||||
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';
 | 
			
		||||
import { PopupWindow } from '$app/components/_shared/PopupWindow';
 | 
			
		||||
 | 
			
		||||
export const EditCellOptionPopup = ({
 | 
			
		||||
  left,
 | 
			
		||||
@ -21,33 +21,10 @@ export const EditCellOptionPopup = ({
 | 
			
		||||
  editingSelectOption: SelectOptionPB;
 | 
			
		||||
  onOutsideClick: () => void;
 | 
			
		||||
}) => {
 | 
			
		||||
  const ref = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const inputRef = useRef<HTMLInputElement>(null);
 | 
			
		||||
  const { t } = useTranslation('');
 | 
			
		||||
  const [adjustedTop, setAdjustedTop] = useState(-100);
 | 
			
		||||
  const [adjustedLeft, setAdjustedLeft] = useState(-100);
 | 
			
		||||
  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(() => {
 | 
			
		||||
    setValue(editingSelectOption.name);
 | 
			
		||||
  }, [editingSelectOption]);
 | 
			
		||||
@ -93,15 +70,16 @@ export const EditCellOptionPopup = ({
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  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 && adjustedLeft === -100 ? 'opacity-0' : 'opacity-100'
 | 
			
		||||
      }`}
 | 
			
		||||
      style={{ top: `${adjustedTop}px`, left: `${adjustedLeft}px` }}
 | 
			
		||||
    <PopupWindow
 | 
			
		||||
      className={'p-2 text-xs'}
 | 
			
		||||
      onOutsideClick={async () => {
 | 
			
		||||
        await onBlur();
 | 
			
		||||
        onOutsideClick();
 | 
			
		||||
      }}
 | 
			
		||||
      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 '}>
 | 
			
		||||
          <input
 | 
			
		||||
            ref={inputRef}
 | 
			
		||||
@ -255,6 +233,6 @@ export const EditCellOptionPopup = ({
 | 
			
		||||
          </div>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </PopupWindow>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -27,9 +27,9 @@ export const EditCellWrapper = ({
 | 
			
		||||
  cellIdentifier: CellIdentifier;
 | 
			
		||||
  cellCache: CellCache;
 | 
			
		||||
  fieldController: FieldController;
 | 
			
		||||
  onEditFieldClick: (top: number, right: number) => void;
 | 
			
		||||
  onEditOptionsClick: (left: number, top: number) => void;
 | 
			
		||||
  onEditDateClick: (left: number, top: number) => void;
 | 
			
		||||
  onEditFieldClick: (cell: CellIdentifier, left: number, top: number) => void;
 | 
			
		||||
  onEditOptionsClick: (cell: CellIdentifier, left: number, top: number) => void;
 | 
			
		||||
  onEditDateClick: (cell: CellIdentifier, left: number, top: number) => void;
 | 
			
		||||
}) => {
 | 
			
		||||
  const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController);
 | 
			
		||||
  const databaseStore = useAppSelector((state) => state.database);
 | 
			
		||||
@ -38,7 +38,7 @@ export const EditCellWrapper = ({
 | 
			
		||||
  const onClick = () => {
 | 
			
		||||
    if (!el.current) return;
 | 
			
		||||
    const { top, right } = el.current.getBoundingClientRect();
 | 
			
		||||
    onEditFieldClick(top, right);
 | 
			
		||||
    onEditFieldClick(cellIdentifier, right, top);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
@ -69,7 +69,10 @@ export const EditCellWrapper = ({
 | 
			
		||||
              cellIdentifier.fieldType === FieldType.MultiSelect ||
 | 
			
		||||
              cellIdentifier.fieldType === FieldType.Checklist) &&
 | 
			
		||||
              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 && (
 | 
			
		||||
@ -80,7 +83,10 @@ export const EditCellWrapper = ({
 | 
			
		||||
            )}
 | 
			
		||||
 | 
			
		||||
            {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 && (
 | 
			
		||||
 | 
			
		||||
@ -1,5 +1,4 @@
 | 
			
		||||
import { useEffect, useRef, useState } from 'react';
 | 
			
		||||
import useOutsideClick from '$app/components/_shared/useOutsideClick';
 | 
			
		||||
import { TrashSvg } from '$app/components/_shared/svg/TrashSvg';
 | 
			
		||||
import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon';
 | 
			
		||||
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 { useAppSelector } from '$app/stores/store';
 | 
			
		||||
import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc';
 | 
			
		||||
import { PopupWindow } from '$app/components/_shared/PopupWindow';
 | 
			
		||||
 | 
			
		||||
export const EditFieldPopup = ({
 | 
			
		||||
  top,
 | 
			
		||||
  right,
 | 
			
		||||
  left,
 | 
			
		||||
  cellIdentifier,
 | 
			
		||||
  viewId,
 | 
			
		||||
  onOutsideClick,
 | 
			
		||||
@ -21,7 +21,7 @@ export const EditFieldPopup = ({
 | 
			
		||||
  changeFieldTypeClick,
 | 
			
		||||
}: {
 | 
			
		||||
  top: number;
 | 
			
		||||
  right: number;
 | 
			
		||||
  left: number;
 | 
			
		||||
  cellIdentifier: CellIdentifier;
 | 
			
		||||
  viewId: string;
 | 
			
		||||
  onOutsideClick: () => void;
 | 
			
		||||
@ -30,31 +30,13 @@ export const EditFieldPopup = ({
 | 
			
		||||
}) => {
 | 
			
		||||
  const databaseStore = useAppSelector((state) => state.database);
 | 
			
		||||
  const { t } = useTranslation('');
 | 
			
		||||
  const ref = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const changeTypeButtonRef = useRef<HTMLDivElement>(null);
 | 
			
		||||
  const [name, setName] = useState('');
 | 
			
		||||
 | 
			
		||||
  const [adjustedTop, setAdjustedTop] = useState(-100);
 | 
			
		||||
 | 
			
		||||
  useOutsideClick(ref, async () => {
 | 
			
		||||
    await save();
 | 
			
		||||
    onOutsideClick();
 | 
			
		||||
  });
 | 
			
		||||
 | 
			
		||||
  useEffect(() => {
 | 
			
		||||
    setName(databaseStore.fields[cellIdentifier.fieldId].title);
 | 
			
		||||
  }, [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 () => {
 | 
			
		||||
    if (!fieldInfo) return;
 | 
			
		||||
    const controller = new TypeOptionController(viewId, Some(fieldInfo));
 | 
			
		||||
@ -78,12 +60,14 @@ export const EditFieldPopup = ({
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <div
 | 
			
		||||
      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}px`, left: `${right + 10}px` }}
 | 
			
		||||
    <PopupWindow
 | 
			
		||||
      className={'px-2 py-2 text-xs'}
 | 
			
		||||
      onOutsideClick={async () => {
 | 
			
		||||
        await save();
 | 
			
		||||
        onOutsideClick();
 | 
			
		||||
      }}
 | 
			
		||||
      left={left}
 | 
			
		||||
      top={top}
 | 
			
		||||
    >
 | 
			
		||||
      <div className={'flex flex-col gap-2 p-2'}>
 | 
			
		||||
        <input
 | 
			
		||||
@ -125,6 +109,6 @@ export const EditFieldPopup = ({
 | 
			
		||||
          </i>
 | 
			
		||||
        </div>
 | 
			
		||||
      </div>
 | 
			
		||||
    </div>
 | 
			
		||||
    </PopupWindow>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -35,11 +35,11 @@ export const EditRow = ({
 | 
			
		||||
  const [editingCell, setEditingCell] = useState<CellIdentifier | null>(null);
 | 
			
		||||
  const [showFieldEditor, setShowFieldEditor] = useState(false);
 | 
			
		||||
  const [editFieldTop, setEditFieldTop] = useState(0);
 | 
			
		||||
  const [editFieldRight, setEditFieldRight] = useState(0);
 | 
			
		||||
  const [editFieldLeft, setEditFieldLeft] = useState(0);
 | 
			
		||||
 | 
			
		||||
  const [showChangeFieldTypePopup, setShowChangeFieldTypePopup] = useState(false);
 | 
			
		||||
  const [changeFieldTypeTop, setChangeFieldTypeTop] = useState(0);
 | 
			
		||||
  const [changeFieldTypeRight, setChangeFieldTypeRight] = useState(0);
 | 
			
		||||
  const [changeFieldTypeLeft, setChangeFieldTypeLeft] = useState(0);
 | 
			
		||||
 | 
			
		||||
  const [showChangeOptionsPopup, setShowChangeOptionsPopup] = useState(false);
 | 
			
		||||
  const [changeOptionsTop, setChangeOptionsTop] = useState(0);
 | 
			
		||||
@ -66,10 +66,10 @@ export const EditRow = ({
 | 
			
		||||
    }, 300);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onEditFieldClick = (cellIdentifier: CellIdentifier, top: number, right: number) => {
 | 
			
		||||
  const onEditFieldClick = (cellIdentifier: CellIdentifier, left: number, top: number) => {
 | 
			
		||||
    setEditingCell(cellIdentifier);
 | 
			
		||||
    setEditFieldTop(top);
 | 
			
		||||
    setEditFieldRight(right);
 | 
			
		||||
    setEditFieldLeft(left + 10);
 | 
			
		||||
    setShowFieldEditor(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -81,7 +81,7 @@ export const EditRow = ({
 | 
			
		||||
 | 
			
		||||
  const onChangeFieldTypeClick = (buttonTop: number, buttonRight: number) => {
 | 
			
		||||
    setChangeFieldTypeTop(buttonTop);
 | 
			
		||||
    setChangeFieldTypeRight(buttonRight);
 | 
			
		||||
    setChangeFieldTypeLeft(buttonRight + 30);
 | 
			
		||||
    setShowChangeFieldTypePopup(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -102,14 +102,14 @@ export const EditRow = ({
 | 
			
		||||
  const onEditOptionsClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => {
 | 
			
		||||
    setEditingCell(cellIdentifier);
 | 
			
		||||
    setChangeOptionsLeft(left);
 | 
			
		||||
    setChangeOptionsTop(top);
 | 
			
		||||
    setChangeOptionsTop(top + 40);
 | 
			
		||||
    setShowChangeOptionsPopup(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  const onEditDateClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => {
 | 
			
		||||
    setEditingCell(cellIdentifier);
 | 
			
		||||
    setDatePickerLeft(left);
 | 
			
		||||
    setDatePickerTop(top);
 | 
			
		||||
    setDatePickerTop(top + 40);
 | 
			
		||||
    setShowDatePicker(true);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
@ -165,11 +165,9 @@ export const EditRow = ({
 | 
			
		||||
                    cellIdentifier={cell.cellIdentifier}
 | 
			
		||||
                    cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
 | 
			
		||||
                    fieldController={controller.fieldController}
 | 
			
		||||
                    onEditFieldClick={(top: number, right: number) => onEditFieldClick(cell.cellIdentifier, top, right)}
 | 
			
		||||
                    onEditOptionsClick={(left: number, top: number) =>
 | 
			
		||||
                      onEditOptionsClick(cell.cellIdentifier, left, top)
 | 
			
		||||
                    }
 | 
			
		||||
                    onEditDateClick={(left: number, top: number) => onEditDateClick(cell.cellIdentifier, left, top)}
 | 
			
		||||
                    onEditFieldClick={onEditFieldClick}
 | 
			
		||||
                    onEditOptionsClick={onEditOptionsClick}
 | 
			
		||||
                    onEditDateClick={onEditDateClick}
 | 
			
		||||
                  ></EditCellWrapper>
 | 
			
		||||
                ))}
 | 
			
		||||
              </div>
 | 
			
		||||
@ -192,7 +190,7 @@ export const EditRow = ({
 | 
			
		||||
        {showFieldEditor && editingCell && (
 | 
			
		||||
          <EditFieldPopup
 | 
			
		||||
            top={editFieldTop}
 | 
			
		||||
            right={editFieldRight}
 | 
			
		||||
            left={editFieldLeft}
 | 
			
		||||
            cellIdentifier={editingCell}
 | 
			
		||||
            viewId={viewId}
 | 
			
		||||
            onOutsideClick={onOutsideEditFieldClick}
 | 
			
		||||
@ -203,7 +201,7 @@ export const EditRow = ({
 | 
			
		||||
        {showChangeFieldTypePopup && (
 | 
			
		||||
          <ChangeFieldTypePopup
 | 
			
		||||
            top={changeFieldTypeTop}
 | 
			
		||||
            right={changeFieldTypeRight}
 | 
			
		||||
            left={changeFieldTypeLeft}
 | 
			
		||||
            onClick={(newType) => changeFieldType(newType)}
 | 
			
		||||
            onOutsideClick={() => setShowChangeFieldTypePopup(false)}
 | 
			
		||||
          ></ChangeFieldTypePopup>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IPopupItem, Popup } from './Popup';
 | 
			
		||||
import { IPopupItem, PopupSelect } from './PopupSelect';
 | 
			
		||||
import i18n from 'i18next';
 | 
			
		||||
 | 
			
		||||
const supportedLanguages: { key: string; title: string }[] = [
 | 
			
		||||
@ -37,11 +37,11 @@ export const LanguageSelectPopup = ({ onClose }: { onClose: () => void }) => {
 | 
			
		||||
    icon: <></>,
 | 
			
		||||
  }));
 | 
			
		||||
  return (
 | 
			
		||||
    <Popup
 | 
			
		||||
    <PopupSelect
 | 
			
		||||
      items={items}
 | 
			
		||||
      className={'absolute top-full right-0 z-10 w-[200px]'}
 | 
			
		||||
      onOutsideClick={onClose}
 | 
			
		||||
      columns={2}
 | 
			
		||||
    ></Popup>
 | 
			
		||||
    ></PopupSelect>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -7,7 +7,7 @@ export interface IPopupItem {
 | 
			
		||||
  onClick: () => void;
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
export const Popup = ({
 | 
			
		||||
export const PopupSelect = ({
 | 
			
		||||
  items,
 | 
			
		||||
  className = '',
 | 
			
		||||
  onOutsideClick,
 | 
			
		||||
@ -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>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
@ -4,6 +4,7 @@ import { useRow } from '../_shared/database-hooks/useRow';
 | 
			
		||||
import { DatabaseController } from '$app/stores/effects/database/database_controller';
 | 
			
		||||
import { BoardCell } from './BoardCell';
 | 
			
		||||
import { Draggable } from 'react-beautiful-dnd';
 | 
			
		||||
import { MouseEventHandler } from 'react';
 | 
			
		||||
 | 
			
		||||
export const BoardCard = ({
 | 
			
		||||
  index,
 | 
			
		||||
@ -22,6 +23,11 @@ export const BoardCard = ({
 | 
			
		||||
}) => {
 | 
			
		||||
  const { cells } = useRow(viewId, controller, rowInfo);
 | 
			
		||||
 | 
			
		||||
  const onDetailClick: MouseEventHandler = (e) => {
 | 
			
		||||
    e.stopPropagation();
 | 
			
		||||
    // onOpenRow(rowInfo);
 | 
			
		||||
  };
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Draggable draggableId={rowInfo.row.id} index={index}>
 | 
			
		||||
      {(provided) => (
 | 
			
		||||
@ -32,7 +38,7 @@ export const BoardCard = ({
 | 
			
		||||
          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 `}
 | 
			
		||||
        >
 | 
			
		||||
          <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>
 | 
			
		||||
          </button>
 | 
			
		||||
          <div className={'flex flex-col gap-3'}>
 | 
			
		||||
 | 
			
		||||
@ -1,6 +1,6 @@
 | 
			
		||||
import { useEffect, useState } from 'react';
 | 
			
		||||
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 { GroupByFieldSvg } from '$app/components/_shared/svg/GroupByFieldSvg';
 | 
			
		||||
 | 
			
		||||
@ -39,10 +39,10 @@ export const BoardSettingsPopup = ({
 | 
			
		||||
  }, [t]);
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Popup
 | 
			
		||||
    <PopupSelect
 | 
			
		||||
      onOutsideClick={() => hidePopup()}
 | 
			
		||||
      items={settingsItems}
 | 
			
		||||
      className={'absolute top-full left-full z-10 text-xs'}
 | 
			
		||||
    ></Popup>
 | 
			
		||||
    ></PopupSelect>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -88,7 +88,7 @@ export const GridTableHeaderItem = ({
 | 
			
		||||
        {showFieldEditor && editingField && (
 | 
			
		||||
          <EditFieldPopup
 | 
			
		||||
            top={editFieldTop}
 | 
			
		||||
            right={editFieldRight}
 | 
			
		||||
            left={editFieldRight}
 | 
			
		||||
            cellIdentifier={
 | 
			
		||||
              {
 | 
			
		||||
                fieldId: editingField.fieldId,
 | 
			
		||||
@ -112,7 +112,7 @@ export const GridTableHeaderItem = ({
 | 
			
		||||
        {showChangeFieldTypePopup && (
 | 
			
		||||
          <ChangeFieldTypePopup
 | 
			
		||||
            top={changeFieldTypeTop}
 | 
			
		||||
            right={changeFieldTypeRight}
 | 
			
		||||
            left={changeFieldTypeRight}
 | 
			
		||||
            onClick={(newType) => changeFieldType(newType)}
 | 
			
		||||
            onOutsideClick={() => setShowChangeFieldTypePopup(false)}
 | 
			
		||||
          ></ChangeFieldTypePopup>
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IPopupItem, Popup } from '../../_shared/Popup';
 | 
			
		||||
import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
 | 
			
		||||
import { FilterSvg } from '../../_shared/svg/FilterSvg';
 | 
			
		||||
import { GroupBySvg } from '../../_shared/svg/GroupBySvg';
 | 
			
		||||
import { PropertiesSvg } from '../../_shared/svg/PropertiesSvg';
 | 
			
		||||
@ -51,5 +51,5 @@ export const GridTitleOptionsPopup = ({ onClose }: { onClose?: () => void }) =>
 | 
			
		||||
      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} />;
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IPopupItem, Popup } from '../../_shared/Popup';
 | 
			
		||||
import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
 | 
			
		||||
import { LogoutSvg } from '../../_shared/svg/LogoutSvg';
 | 
			
		||||
 | 
			
		||||
export const OptionsPopup = ({ onSignOutClick, onClose }: { onSignOutClick: () => void; onClose: () => void }) => {
 | 
			
		||||
@ -14,10 +14,10 @@ export const OptionsPopup = ({ onSignOutClick, onClose }: { onSignOutClick: () =
 | 
			
		||||
    },
 | 
			
		||||
  ];
 | 
			
		||||
  return (
 | 
			
		||||
    <Popup
 | 
			
		||||
    <PopupSelect
 | 
			
		||||
      className={'absolute top-[50px] right-[30px] z-10 whitespace-nowrap'}
 | 
			
		||||
      items={items}
 | 
			
		||||
      onOutsideClick={onClose}
 | 
			
		||||
    ></Popup>
 | 
			
		||||
    ></PopupSelect>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IPopupItem, Popup } from '../../_shared/Popup';
 | 
			
		||||
import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
 | 
			
		||||
import { EditSvg } from '../../_shared/svg/EditSvg';
 | 
			
		||||
import { TrashSvg } from '../../_shared/svg/TrashSvg';
 | 
			
		||||
import { CopySvg } from '../../_shared/svg/CopySvg';
 | 
			
		||||
@ -47,11 +47,11 @@ export const NavItemOptionsPopup = ({
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Popup
 | 
			
		||||
    <PopupSelect
 | 
			
		||||
      onOutsideClick={() => onClose && onClose()}
 | 
			
		||||
      items={items}
 | 
			
		||||
      className={`absolute right-0`}
 | 
			
		||||
      style={{ top: `${top}px` }}
 | 
			
		||||
    ></Popup>
 | 
			
		||||
    ></PopupSelect>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
@ -1,4 +1,4 @@
 | 
			
		||||
import { IPopupItem, Popup } from '../../_shared/Popup';
 | 
			
		||||
import { IPopupItem, PopupSelect } from '../../_shared/PopupSelect';
 | 
			
		||||
import { DocumentSvg } from '../../_shared/svg/DocumentSvg';
 | 
			
		||||
import { BoardSvg } from '../../_shared/svg/BoardSvg';
 | 
			
		||||
import { GridSvg } from '../../_shared/svg/GridSvg';
 | 
			
		||||
@ -47,11 +47,11 @@ export const NewPagePopup = ({
 | 
			
		||||
  ];
 | 
			
		||||
 | 
			
		||||
  return (
 | 
			
		||||
    <Popup
 | 
			
		||||
    <PopupSelect
 | 
			
		||||
      onOutsideClick={() => onClose && onClose()}
 | 
			
		||||
      items={items}
 | 
			
		||||
      className={'absolute right-0'}
 | 
			
		||||
      style={{ top: `${top}px` }}
 | 
			
		||||
    ></Popup>
 | 
			
		||||
    ></PopupSelect>
 | 
			
		||||
  );
 | 
			
		||||
};
 | 
			
		||||
 | 
			
		||||
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user