瀏覽代碼

chore: HOC popup window

ascarbek 2 年之前
父節點
當前提交
2d85afe168
共有 17 個文件被更改,包括 140 次插入185 次删除
  1. 6 29
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx
  2. 5 28
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx
  3. 4 26
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx
  4. 11 33
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellOptionPopup.tsx
  5. 12 6
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx
  6. 12 28
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx
  7. 12 14
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx
  8. 3 3
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/LanguageSelectPopup.tsx
  9. 1 1
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupSelect.tsx
  10. 51 0
      frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupWindow.tsx
  11. 7 1
      frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx
  12. 3 3
      frontend/appflowy_tauri/src/appflowy_app/components/board/BoardSettingsPopup.tsx
  13. 2 2
      frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx
  14. 2 2
      frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTitle/GridTitleOptionsPopup.tsx
  15. 3 3
      frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/OptionsPopup.tsx
  16. 3 3
      frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx
  17. 3 3
      frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx

+ 6 - 29
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx

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

+ 5 - 28
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx

@@ -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 useOutsideClick from '$app/components/_shared/useOutsideClick';
+import { PopupWindow } from '$app/components/_shared/PopupWindow';
 
 
 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
-      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'}>
       <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>
   );
   );
 };
 };

+ 4 - 26
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx

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

+ 11 - 33
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellOptionPopup.tsx

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

+ 12 - 6
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx

@@ -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;
-  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 { 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 && (

+ 12 - 28
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx

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

+ 12 - 14
frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx

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

+ 3 - 3
frontend/appflowy_tauri/src/appflowy_app/components/_shared/LanguageSelectPopup.tsx

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

+ 1 - 1
frontend/appflowy_tauri/src/appflowy_app/components/_shared/Popup.tsx → frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupSelect.tsx

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

+ 51 - 0
frontend/appflowy_tauri/src/appflowy_app/components/_shared/PopupWindow.tsx

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

+ 7 - 1
frontend/appflowy_tauri/src/appflowy_app/components/board/BoardCard.tsx

@@ -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'}>

+ 3 - 3
frontend/appflowy_tauri/src/appflowy_app/components/board/BoardSettingsPopup.tsx

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

+ 2 - 2
frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTableHeader/GridTableHeaderItem.tsx

@@ -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>

+ 2 - 2
frontend/appflowy_tauri/src/appflowy_app/components/grid/GridTitle/GridTitleOptionsPopup.tsx

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

+ 3 - 3
frontend/appflowy_tauri/src/appflowy_app/components/layout/HeaderPanel/OptionsPopup.tsx

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

+ 3 - 3
frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NavItemOptionsPopup.tsx

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

+ 3 - 3
frontend/appflowy_tauri/src/appflowy_app/components/layout/NavigationPanel/NewPagePopup.tsx

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