diff --git a/frontend/appflowy_tauri/package.json b/frontend/appflowy_tauri/package.json index 6ba0da114f..6f9ba16df5 100644 --- a/frontend/appflowy_tauri/package.json +++ b/frontend/appflowy_tauri/package.json @@ -23,6 +23,7 @@ "@slate-yjs/core": "^0.3.1", "@tanstack/react-virtual": "3.0.0-beta.54", "@tauri-apps/api": "^1.2.0", + "dayjs": "^1.11.7", "events": "^3.3.0", "google-protobuf": "^3.21.2", "i18next": "^22.4.10", @@ -33,12 +34,12 @@ "protoc-gen-ts": "^0.8.5", "react": "^18.2.0", "react-beautiful-dnd": "^13.1.1", + "react-calendar": "^4.1.0", "react-dom": "^18.2.0", "react-error-boundary": "^3.1.4", "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", @@ -46,8 +47,8 @@ "slate-react": "^0.91.9", "ts-results": "^3.3.0", "utf8": "^3.0.0", - "yjs": "^13.5.51", - "y-indexeddb": "^9.0.9" + "y-indexeddb": "^9.0.9", + "yjs": "^13.5.51" }, "devDependencies": { "@tauri-apps/cli": "^1.2.2", diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx index 96108ee1fd..0a2b1cf09b 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/CellOptionsPopup.tsx @@ -39,8 +39,8 @@ export const CellOptionsPopup = ({ useEffect(() => { if (!ref.current) return; const { height } = ref.current.getBoundingClientRect(); - if (top + height > window.innerHeight) { - setAdjustedTop(window.innerHeight - height); + if (top + height + 40 > window.innerHeight) { + setAdjustedTop(window.innerHeight - height - 40); } else { setAdjustedTop(top); } diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx new file mode 100644 index 0000000000..b346caf73f --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/DatePickerPopup.tsx @@ -0,0 +1,99 @@ +import { useEffect, useRef, 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'; +import { MoreSvg } from '$app/components/_shared/svg/MoreSvg'; +import { CheckboxSvg } from '$app/components/_shared/svg/CheckboxSvg'; +import { EditorUncheckSvg } from '$app/components/_shared/svg/EditorUncheckSvg'; +import { useCell } from '$app/components/_shared/database-hooks/useCell'; +import { DateCellDataPB } from '@/services/backend'; + +export const DatePickerPopup = ({ + left, + top, + cellIdentifier, + cellCache, + fieldController, + onOutsideClick, +}: { + left: number; + top: number; + cellIdentifier: CellIdentifier; + cellCache: CellCache; + fieldController: FieldController; + onOutsideClick: () => void; +}) => { + const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); + const ref = useRef(null); + const [adjustedTop, setAdjustedTop] = useState(-100); + // const [value, setValue] = useState(); + const { t } = useTranslation(''); + const [selectedDate, setSelectedDate] = useState(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(() => { + // console.log((data as DateCellDataPB).date); + // setSelectedDate(new Date((data as DateCellDataPB).date)); + }, [data]); + + const onChange = (v: Date | null | (Date | null)[]) => { + if (v instanceof Date) { + console.log(dayjs(v).format('YYYY-MM-DD')); + setSelectedDate(v); + // void cellController?.saveCellData(new DateCellDataPB({ date: dayjs(v).format('YYYY-MM-DD') })); + } + }; + + return ( +
+
+ onChange(d)} value={selectedDate} /> +
+
+
+
+ + + + {t('grid.field.includeTime')} +
+ + + +
+
+
+ + {t('grid.field.dateFormat')} & {t('grid.field.timeFormat')} + + + + +
+
+ ); +}; 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 index 6f24c50ebe..896adf38c6 100644 --- a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/EditRow/EditCellDate.tsx @@ -1,28 +1,29 @@ import Picker from 'react-tailwindcss-datepicker'; import { DateValueType } from 'react-tailwindcss-datepicker/dist/types'; -import { useState } from 'react'; +import { useRef, useState } from 'react'; import { DateCellDataPB } from '@/services/backend'; import { CellController } from '$app/stores/effects/database/cell/cell_controller'; export const EditCellDate = ({ data, cellController, + onEditClick, }: { data?: DateCellDataPB; cellController: CellController; + onEditClick: (left: number, top: number) => void; }) => { - const [value, setValue] = useState({ - startDate: new Date(), - endDate: new Date(), - }); + const ref = useRef(null); - const onChange = (v: DateValueType) => { - console.log(v); + const onClick = () => { + if (!ref.current) return; + const { left, top } = ref.current.getBoundingClientRect(); + onEditClick(left, top); }; return ( -
- ; +
onClick()} className={'px-4 py-2'}> + {data?.date || ''}
); }; 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 c094f9f26c..7e8f2b03f1 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 @@ -19,12 +19,14 @@ export const EditCellWrapper = ({ fieldController, onEditFieldClick, onEditOptionsClick, + onEditDateClick, }: { cellIdentifier: CellIdentifier; cellCache: CellCache; fieldController: FieldController; onEditFieldClick: (top: number, right: number) => void; onEditOptionsClick: (left: number, top: number) => void; + onEditDateClick: (left: number, top: number) => void; }) => { const { data, cellController } = useCell(cellIdentifier, cellCache, fieldController); const databaseStore = useAppSelector((state) => state.database); @@ -66,7 +68,11 @@ export const EditCellWrapper = ({ )} {cellIdentifier.fieldType === FieldType.DateTime && cellController && ( - + )} {cellIdentifier.fieldType === FieldType.Number && cellController && ( 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 96452fa93c..4103935e50 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 @@ -14,6 +14,7 @@ import { Some } from 'ts-results'; import { FieldType } from '@/services/backend'; import { CellOptions } from '$app/components/_shared/EditRow/CellOptions'; import { CellOptionsPopup } from '$app/components/_shared/EditRow/CellOptionsPopup'; +import { DatePickerPopup } from '$app/components/_shared/EditRow/DatePickerPopup'; export const EditRow = ({ onClose, @@ -43,6 +44,10 @@ export const EditRow = ({ const [changeOptionsTop, setChangeOptionsTop] = useState(0); const [changeOptionsLeft, setChangeOptionsLeft] = useState(0); + const [showDatePicker, setShowDatePicker] = useState(false); + const [datePickerTop, setDatePickerTop] = useState(0); + const [datePickerLeft, setDatePickerLeft] = useState(0); + useEffect(() => { setUnveil(true); }, []); @@ -94,6 +99,13 @@ export const EditRow = ({ setShowChangeOptionsPopup(true); }; + const onEditDateClick = async (cellIdentifier: CellIdentifier, left: number, top: number) => { + setEditingCell(cellIdentifier); + setDatePickerLeft(left); + setDatePickerTop(top); + setShowDatePicker(true); + }; + return (
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)} > ))}
@@ -160,6 +173,16 @@ export const EditRow = ({ onOutsideClick={() => setShowChangeOptionsPopup(false)} > )} + {showDatePicker && editingCell && ( + setShowDatePicker(false)} + > + )}
); diff --git a/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx new file mode 100644 index 0000000000..b66f7bfe18 --- /dev/null +++ b/frontend/appflowy_tauri/src/appflowy_app/components/_shared/svg/ClockSvg.tsx @@ -0,0 +1,15 @@ +export const ClockSvg = () => { + return ( + + + + + + + ); +}; diff --git a/frontend/appflowy_tauri/src/main.tsx b/frontend/appflowy_tauri/src/main.tsx index e53dc96c43..e2779406de 100644 --- a/frontend/appflowy_tauri/src/main.tsx +++ b/frontend/appflowy_tauri/src/main.tsx @@ -4,5 +4,6 @@ import App from './appflowy_app/App'; import './styles/tailwind.css'; import './styles/font.css'; import './styles/template.css'; +import './styles/Calendar.css'; ReactDOM.createRoot(document.getElementById('root') as HTMLElement).render(); diff --git a/frontend/appflowy_tauri/src/styles/Calendar.css b/frontend/appflowy_tauri/src/styles/Calendar.css new file mode 100644 index 0000000000..b7f3f4647b --- /dev/null +++ b/frontend/appflowy_tauri/src/styles/Calendar.css @@ -0,0 +1,143 @@ +.react-calendar { + width: 250px; + max-width: 100%; + background: white; + line-height: 1.125em; +} + +.react-calendar--doubleView { + width: 700px; +} + +.react-calendar--doubleView .react-calendar__viewContainer { + display: flex; + margin: -0.5em; +} + +.react-calendar--doubleView .react-calendar__viewContainer > * { + width: 50%; + margin: 0.5em; +} + +.react-calendar, +.react-calendar *, +.react-calendar *:before, +.react-calendar *:after { + -moz-box-sizing: border-box; + -webkit-box-sizing: border-box; + box-sizing: border-box; +} + +.react-calendar button { + margin: 0; + border: 0; + outline: none; +} + +.react-calendar button:enabled:hover { + cursor: pointer; +} + +.react-calendar__navigation { + display: flex; + height: 44px; + margin-bottom: 1em; +} + +.react-calendar__navigation button { + min-width: 44px; + background: none; +} + +.react-calendar__navigation button:disabled { + background-color: #f0f0f0; +} + +.react-calendar__navigation button:enabled:hover, +.react-calendar__navigation button:enabled:focus { + background-color: #e6e6e6; +} + +.react-calendar__month-view__weekdays { + @apply mb-2 text-center text-xs uppercase text-shade-3; +} + +.react-calendar__month-view__weekdays abbr { + @apply no-underline; +} + +.react-calendar__month-view__weekdays__weekday { + padding: 0.5em; +} + +.react-calendar__month-view__weekNumbers .react-calendar__tile { + display: flex; + align-items: center; + justify-content: center; + font-size: 0.75em; + font-weight: 400; +} + +.react-calendar__month-view__days__day--weekend { + @apply text-main-alert; +} + +.react-calendar__month-view__days__day--neighboringMonth { + /*color: #757575;*/ + @apply text-shade-4; +} + +.react-calendar__year-view .react-calendar__tile, +.react-calendar__decade-view .react-calendar__tile, +.react-calendar__century-view .react-calendar__tile { + padding: 2em 0.5em; +} + +.react-calendar__tile { + max-width: 100%; + /*padding: 10px 6.6667px;*/ + background: none; + text-align: center; + line-height: 16px; + @apply p-1; +} + +.react-calendar__tile:disabled { + background-color: #f0f0f0; +} + +.react-calendar__tile:enabled:hover, +.react-calendar__tile:enabled:focus { + background-color: #e6e6e6; +} + +.react-calendar__tile--now { + @apply rounded bg-shade-6; +} + +.react-calendar__tile--now:enabled:hover, +.react-calendar__tile--now:enabled:focus { + @apply rounded bg-shade-6; +} + +.react-calendar__tile--hasActive { + background: #76baff; +} + +.react-calendar__tile--hasActive:enabled:hover, +.react-calendar__tile--hasActive:enabled:focus { + background: #a9d4ff; +} + +.react-calendar__tile--active { + @apply rounded bg-main-accent text-white; +} + +.react-calendar__tile--active:enabled:hover, +.react-calendar__tile--active:enabled:focus { + background: #1087ff; +} + +.react-calendar--selectRange .react-calendar__tile--hover { + background-color: #e6e6e6; +} diff --git a/frontend/appflowy_tauri/tailwind.config.cjs b/frontend/appflowy_tauri/tailwind.config.cjs index 1857a9b9f3..b2ba17c424 100644 --- a/frontend/appflowy_tauri/tailwind.config.cjs +++ b/frontend/appflowy_tauri/tailwind.config.cjs @@ -1,6 +1,11 @@ /** @type {import('tailwindcss').Config} */ module.exports = { - content: ['./index.html', './src/**/*.{js,ts,jsx,tsx}'], + content: [ + './index.html', + './src/**/*.{js,ts,jsx,tsx}', + './node_modules/react-tailwindcss-datepicker/dist/index.esm.js', + ], + darkMode: 'class', theme: { extend: { colors: {