diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 4dd4a9f63b..f429aa6019 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -34,6 +34,7 @@ "react-i18next": "^12.2.0", "react-redux": "^8.0.5", "react-router-dom": "^6.8.0", + "react-tailwindcss-datepicker": "^1.5.1", "react18-input-otp": "^1.1.2", "redux": "^4.2.1", "rxjs": "^7.8.0", diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx new file mode 100644 index 0000000000..d51c202a91 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/ChangeFieldTypePopup.tsx @@ -0,0 +1,55 @@ +import { FieldType } from '@/services/backend'; +import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; +import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; +import { useEffect, useRef, useState } from 'react'; + +const typesOrder: FieldType[] = [ + FieldType.RichText, + FieldType.Number, + FieldType.DateTime, + FieldType.SingleSelect, + FieldType.MultiSelect, + FieldType.Checkbox, + FieldType.URL, + FieldType.Checklist, +]; + +export const ChangeFieldTypePopup = ({ top, right, onClick }: { top: number; right: number; onClick: () => void }) => { + const ref = useRef(null); + const [adjustedTop, setAdjustedTop] = useState(0); + + 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 ( +
+
+ {typesOrder.map((t, i) => ( + + ))} +
+
+ ); +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx new file mode 100644 index 0000000000..a960a7c6be --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx @@ -0,0 +1,24 @@ +import Picker from 'react-tailwindcss-datepicker'; +import { DateValueType } from 'react-tailwindcss-datepicker/dist/types'; +import { useState } from 'react'; +import { DateCellDataPB } from '@/services/backend'; +import { CellController } from '$app/stores/effects/database/cell/cell_controller'; + +export const EditCellDate = ({ + data, + cellController, +}: { + data?: DateCellDataPB; + cellController: CellController; +}) => { + const [value, setValue] = useState({ + startDate: new Date(), + endDate: new Date(), + }); + + const onChange = (v: DateValueType) => { + console.log(v); + }; + + return ; +}; diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx index 0de7cc38c9..58392b96a0 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellWrapper.tsx @@ -7,33 +7,37 @@ import { useAppSelector } from '$app/stores/store'; import { getBgColor } from '$app/components/_shared/getColor'; import { EditorCheckSvg } from '$app/components/_shared/svg/EditorCheckSvg'; import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; -import { useState } from 'react'; import { EditCellText } from '$app/components/_shared/EditRow/EditCellText'; -import { EditFieldPopup } from '$app/components/_shared/EditRow/EditFieldPopup'; import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; +import { EditCellDate } from '$app/components/_shared/EditRow/EditCellDate'; +import { useRef } from 'react'; export const EditCellWrapper = ({ - viewId, cellIdentifier, cellCache, fieldController, + onEditFieldClick, }: { - viewId: string; cellIdentifier: CellIdentifier; cellCache: CellCache; fieldController: FieldController; + onEditFieldClick: (top: number, right: number) => void; }) => { const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); const databaseStore = useAppSelector((state) => state.database); - const [showFieldEditor, setShowFieldEditor] = useState(false); - const onEditFieldClick = () => { - setShowFieldEditor(true); + const el = useRef(null); + + const onClick = () => { + if (!el.current) return; + const { top, right } = el.current.getBoundingClientRect(); + onEditFieldClick(top, right); }; return (
onEditFieldClick()} + ref={el} + onClick={() => onClick()} className={'relative flex w-[180px] cursor-pointer items-center gap-2 rounded-lg px-3 py-1.5 hover:bg-shade-6'} >
@@ -42,16 +46,6 @@ export const EditCellWrapper = ({ {databaseStore.fields[cellIdentifier.fieldId].title} - {showFieldEditor && cellController && ( - setShowFieldEditor(false)} - fieldInfo={fieldController.getField(cellIdentifier.fieldId)} - > - )}
{(cellIdentifier.fieldType === FieldType.SingleSelect || @@ -72,7 +66,9 @@ export const EditCellWrapper = ({
)} - {cellIdentifier.fieldType === FieldType.DateTime &&
{(data as DateCellDataPB | undefined)?.date}
} + {cellIdentifier.fieldType === FieldType.DateTime && cellController && ( + + )} {(cellIdentifier.fieldType === FieldType.RichText || cellIdentifier.fieldType === FieldType.URL || diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx index bc760a5eec..83879221d2 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditFieldPopup.tsx @@ -1,41 +1,60 @@ import { useEffect, useRef, useState } from 'react'; import useOutsideClick from '$app/components/_shared/useOutsideClick'; import { TrashSvg } from '$app/components/_shared/svg/TrashSvg'; -import { CellController } from '$app/stores/effects/database/cell/cell_controller'; -import { FieldType } from '@/services/backend'; import { FieldTypeIcon } from '$app/components/_shared/EditRow/FieldTypeIcon'; import { FieldTypeName } from '$app/components/_shared/EditRow/FieldTypeName'; import { useTranslation } from 'react-i18next'; import { TypeOptionController } from '$app/stores/effects/database/field/type_option/type_option_controller'; import { Some } from 'ts-results'; import { FieldInfo } from '$app/stores/effects/database/field/field_controller'; +import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; +import { ChangeFieldTypePopup } from '$app/components/_shared/EditRow/ChangeFieldTypePopup'; +import { useAppSelector } from '$app/stores/store'; +import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; export const EditFieldPopup = ({ + top, + right, + cellIdentifier, viewId, - fieldName, onOutsideClick, - fieldType, - cellController, fieldInfo, + // changeFieldTypeClick, }: { + top: number; + right: number; + cellIdentifier: CellIdentifier; viewId: string; - fieldName: string; onOutsideClick?: () => void; - fieldType: FieldType; - cellController: CellController; fieldInfo: FieldInfo | undefined; + // changeFieldTypeClick: (top: number, right: number) => void }) => { + const databaseStore = useAppSelector((state) => state.database); const { t } = useTranslation(''); const ref = useRef(null); + const changeTypeButtonRef = useRef(null); const [name, setName] = useState(''); + + const [adjustedTop, setAdjustedTop] = useState(0); + useOutsideClick(ref, async () => { await save(); onOutsideClick && onOutsideClick(); }); useEffect(() => { - setName(fieldName); - }, [fieldName]); + 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; @@ -44,36 +63,59 @@ export const EditFieldPopup = ({ await controller.setFieldName(name); }; + const onChangeFieldTypeClick = () => { + if (!changeTypeButtonRef.current) return; + const { top: newTop, right: newRight } = changeTypeButtonRef.current.getBoundingClientRect(); + // setChangeFieldTypeTop(newTop); + // setChangeFieldTypeRight(newRight); + // setShowChangeFieldTypePopup(true); + // changeFieldTypeClick(newTop, newRight); + }; + return ( -
-
+
+
setName(e.target.value)} onBlur={() => save()} - className={'border-shades-3 flex-1 rounded border bg-main-selector p-1'} + className={'border-shades-3 flex-1 rounded border bg-main-selector px-2 py-2'} /> + - - + - - - - +
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx index 580cfd61fa..88bac8f41c 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditRow.tsx @@ -5,6 +5,10 @@ import { RowInfo } from '$app/stores/effects/database/row/row_cache'; import { EditCellWrapper } from '$app/components/_shared/EditRow/EditCellWrapper'; import AddSvg from '$app/components/_shared/svg/AddSvg'; import { useTranslation } from 'react-i18next'; +import { EditFieldPopup } from '$app/components/_shared/EditRow/EditFieldPopup'; +import { useState } from 'react'; +import { CellIdentifier } from '$app/stores/effects/database/cell/cell_bd_svc'; +import { ChangeFieldTypePopup } from '$app/components/_shared/EditRow/ChangeFieldTypePopup'; export const EditRow = ({ onClose, @@ -19,26 +23,58 @@ export const EditRow = ({ }) => { const { cells, onNewColumnClick } = useRow(viewId, controller, rowInfo); const { t } = useTranslation(''); + const [showFieldEditor, setShowFieldEditor] = useState(false); + const [editFieldTop, setEditFieldTop] = useState(0); + const [editFieldRight, setEditFieldRight] = useState(0); + const [showChangeFieldTypePopup, setShowChangeFieldTypePopup] = useState(false); + const [changeFieldTypeTop, setChangeFieldTypeTop] = useState(0); + const [changeFieldTypeRight, setChangeFieldTypeRight] = useState(0); + + const onEditFieldClick = (cell: { cellIdentifier: CellIdentifier; fieldId: string }, top: number, right: number) => { + setEditingCell(cell); + setEditFieldTop(top); + setEditFieldRight(right); + setShowFieldEditor(true); + }; + + const [editingCell, setEditingCell] = useState<{ cellIdentifier: CellIdentifier; fieldId: string } | null>(null); return (
-
+
onClose()} className={'absolute top-4 right-4'}>
-
+
{cells.map((cell, cellIndex) => ( onEditFieldClick(cell, top, right)} > ))}
+ {showFieldEditor && editingCell && ( + setShowFieldEditor(false)} + fieldInfo={controller.fieldController.getField(editingCell.cellIdentifier.fieldId)} + > + )} + {showChangeFieldTypePopup && ( + setShowChangeFieldTypePopup(false)} + > + )}