chore: date picker

This commit is contained in:
ascarbek 2023-03-30 03:29:23 +06:00
parent b9e49f109d
commit 03eaf0b4cb
10 changed files with 310 additions and 16 deletions

View File

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

View File

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

View File

@ -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<HTMLDivElement>(null);
const [adjustedTop, setAdjustedTop] = useState(-100);
// const [value, setValue] = useState();
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(() => {
// 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 (
<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` }}
>
<div className={'px-2'}>
<Calendar onChange={(d) => onChange(d)} value={selectedDate} />
</div>
<hr className={'-mx-2 my-4 border-shade-6'} />
<div className={'flex items-center justify-between px-4'}>
<div className={'flex items-center gap-2'}>
<i className={'h-4 w-4'}>
<ClockSvg></ClockSvg>
</i>
<span>{t('grid.field.includeTime')}</span>
</div>
<i className={'h-5 w-5'}>
<EditorUncheckSvg></EditorUncheckSvg>
</i>
</div>
<hr className={'-mx-2 my-4 border-shade-6'} />
<div className={'flex items-center justify-between px-4 pb-2'}>
<span>
{t('grid.field.dateFormat')} & {t('grid.field.timeFormat')}
</span>
<i className={'h-5 w-5'}>
<MoreSvg></MoreSvg>
</i>
</div>
</div>
);
};

View File

@ -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<any, any>;
onEditClick: (left: number, top: number) => void;
}) => {
const [value, setValue] = useState<DateValueType>({
startDate: new Date(),
endDate: new Date(),
});
const ref = useRef<HTMLDivElement>(null);
const onChange = (v: DateValueType) => {
console.log(v);
const onClick = () => {
if (!ref.current) return;
const { left, top } = ref.current.getBoundingClientRect();
onEditClick(left, top);
};
return (
<div className={'px-4 py-2'}>
<Picker value={value} onChange={onChange} useRange={false} asSingle={true}></Picker>;
<div ref={ref} onClick={() => onClick()} className={'px-4 py-2'}>
{data?.date || ''}
</div>
);
};

View File

@ -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 && (
<EditCellDate data={data as DateCellDataPB | undefined} cellController={cellController}></EditCellDate>
<EditCellDate
data={data as DateCellDataPB | undefined}
onEditClick={onEditDateClick}
cellController={cellController}
></EditCellDate>
)}
{cellIdentifier.fieldType === FieldType.Number && cellController && (

View File

@ -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 (
<div
className={`fixed inset-0 z-10 flex items-center justify-center bg-black/30 backdrop-blur-sm transition-opacity duration-300 ${
@ -115,6 +127,7 @@ export const EditRow = ({
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)}
></EditCellWrapper>
))}
</div>
@ -160,6 +173,16 @@ export const EditRow = ({
onOutsideClick={() => setShowChangeOptionsPopup(false)}
></CellOptionsPopup>
)}
{showDatePicker && editingCell && (
<DatePickerPopup
top={datePickerTop}
left={datePickerLeft}
cellIdentifier={editingCell}
cellCache={controller.databaseViewCache.getRowCache().getCellCache()}
fieldController={controller.fieldController}
onOutsideClick={() => setShowDatePicker(false)}
></DatePickerPopup>
)}
</div>
</div>
);

View File

@ -0,0 +1,15 @@
export const ClockSvg = () => {
return (
<svg width='100%' height='100%' viewBox='0 0 16 16' fill='none' xmlns='http://www.w3.org/2000/svg'>
<path
d='M8 13C10.7614 13 13 10.7614 13 8C13 5.23858 10.7614 3 8 3C5.23858 3 3 5.23858 3 8C3 10.7614 5.23858 13 8 13Z'
stroke='currentColor'
strokeLinecap='round'
strokeLinejoin='round'
/>
<path d='M8 5V8L10 9' stroke='currentColor' strokeLinecap='round' strokeLinejoin='round' />
<path d='M11.5 2.5L13.5 4.5' stroke='currentColor' strokeLinecap='round' />
<path d='M4.5 2.5L2.5 4.5' stroke='currentColor' strokeLinecap='round' />
</svg>
);
};

View File

@ -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(<App />);

View File

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

View File

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