From df5217eea01b6ee3ce03ceede030306d05f19c58 Mon Sep 17 00:00:00 2001 From: bobzel Date: Wed, 4 Sep 2024 17:48:13 -0400 Subject: updated calendars to support times of events and to render evdnts at the correct time. --- src/ClientUtils.ts | 9 ++- src/client/util/DocumentManager.ts | 4 +- src/client/views/nodes/calendarBox/CalendarBox.tsx | 85 ++++++++++++++++------ 3 files changed, 69 insertions(+), 29 deletions(-) (limited to 'src') diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 51ad55c07..d149c2eae 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -651,18 +651,21 @@ export function dateRangeStrToDates(dateStr: string) { // dateStr in yyyy-mm-dd format const dateRangeParts = dateStr.split('|'); // splits into from and to date if (dateRangeParts.length < 2) return { startDate: new Date(), endDate: new Date() }; + return { startDate: new Date(dateRangeParts[0]), endDate: new Date(dateRangeParts[1]) }; const fromParts = dateRangeParts[0].split('-'); const toParts = dateRangeParts[1].split('-'); const fromYear = parseInt(fromParts[0]); const fromMonth = parseInt(fromParts[1]) - 1; - const fromDay = parseInt(fromParts[2]); + const fromDay = parseInt(fromParts[2]?.split('T')[0]); + const fromHour = parseInt(fromParts[2]?.split('T')[1]?.split(':')[0] || '12'); const toYear = parseInt(toParts[0]); const toMonth = parseInt(toParts[1]) - 1; - const toDay = parseInt(toParts[2]); + const toDay = parseInt(toParts[2]?.split('T')[0]); + const toHour = parseInt(fromParts[2]?.split('T')[1]?.split(':')[0] || '12'); - return { startDate: new Date(fromYear, fromMonth, fromDay), endDate: new Date(toYear, toMonth, toDay) }; + return { startDate: new Date(fromYear, fromMonth, fromDay, fromHour), endDate: new Date(toYear, toMonth, toDay, toHour) }; } function replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) { diff --git a/src/client/util/DocumentManager.ts b/src/client/util/DocumentManager.ts index 83b83240e..5ae292760 100644 --- a/src/client/util/DocumentManager.ts +++ b/src/client/util/DocumentManager.ts @@ -269,8 +269,8 @@ export class DocumentManager { if (options.openLocation?.includes(OpenWhere.lightbox)) { // even if we found the document view, if the target is a lightbox, we try to open it in the lightbox to preserve lightbox semantics (eg, there's only one active doc in the lightbox) const target = DocCast(targetDoc.annotationOn, targetDoc); - const contextView = this.getDocumentView(DocCast(target.embedContainer)); - if (contextView?.ComponentView?.addDocTab?.(target, options.openLocation)) { + const compView = this.getDocumentView(DocCast(target.embedContainer))?.ComponentView; + if ((compView?.addDocTab ?? compView?._props.addDocTab)?.(target, options.openLocation)) { await new Promise(waitres => { setTimeout(() => waitres()); }); diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index 0d50f3382..9452cc10a 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -1,4 +1,4 @@ -import { Calendar, EventSourceInput } from '@fullcalendar/core'; +import { Calendar, EventClickArg, EventSourceInput } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import multiMonthPlugin from '@fullcalendar/multimonth'; import timeGrid from '@fullcalendar/timegrid'; @@ -7,11 +7,17 @@ import { observer } from 'mobx-react'; import * as React from 'react'; import { dateRangeStrToDates } from '../../../../ClientUtils'; import { Doc } from '../../../../fields/Doc'; -import { NumCast, StrCast } from '../../../../fields/Types'; +import { BoolCast, NumCast, StrCast } from '../../../../fields/Types'; import { CollectionSubView, SubCollectionViewProps } from '../../collections/CollectionSubView'; import './CalendarBox.scss'; +import { Id } from '../../../../fields/FieldSymbols'; +import { DocServer } from '../../../DocServer'; +import { DocumentView } from '../DocumentView'; +import { OpenWhere } from '../OpenWhere'; +import { DragManager } from '../../../util/DragManager'; +import { DocData } from '../../../../fields/DocSymbols'; -type CalendarView = 'month' | 'multi-month' | 'week'; +type CalendarView = 'multiMonth' | 'dayGridMonth' | 'timeGridWeek' | 'timeGridDay'; @observer export class CalendarBox extends CollectionSubView() { @@ -27,8 +33,10 @@ export class CalendarBox extends CollectionSubView() { } @observable _multiMonth = 0; + isMultiMonth: boolean | undefined; componentDidMount(): void { + this._props.setContentViewBox?.(this); this._eventsDisposer = reaction( () => this.calendarEvents, events => this._calendar?.setOption('events', events), @@ -42,14 +50,14 @@ export class CalendarBox extends CollectionSubView() { @computed get calendarEvents(): EventSourceInput | undefined { return this.childDocs.map(doc => { const { startDate: start, endDate: end } = dateRangeStrToDates(StrCast(doc.date_range)); - return { title: StrCast(doc.title), start, end, + groupId: doc[Id], startEditable: true, endEditable: true, - allDay: false, + allDay: BoolCast(doc.allDay), classNames: ['mother'], // will determine the style editable: true, // subject to change in the future backgroundColor: this.eventToColor(doc), @@ -67,22 +75,40 @@ export class CalendarBox extends CollectionSubView() { // Choose a calendar view based on the date range @computed get calendarViewType(): CalendarView { + if (this.dataDoc[this.fieldKey + '_calendarType']) return StrCast(this.dataDoc[this.fieldKey + '_calendarType']) as CalendarView; + if (this.isMultiMonth) return 'multiMonth'; const { startDate, endDate } = this.dateRangeStrDates; - - if (startDate.getFullYear() !== endDate.getFullYear() || startDate.getMonth() !== endDate.getMonth()) return 'multi-month'; - - if (Math.abs(startDate.getDay() - endDate.getDay()) > 7) return 'month'; - return 'week'; + if (startDate.getFullYear() !== endDate.getFullYear() || startDate.getMonth() !== endDate.getMonth()) return 'multiMonth'; + if (Math.abs(startDate.getDay() - endDate.getDay()) > 7) return 'dayGridMonth'; + return 'timeGridWeek'; } + // TODO: Return a different color based on the event type eventToColor(event: Doc): string { - // TODO: Return a different color based on the event type - console.log(event.title); return 'blue'; } - handleEventClick = (/* arg: EventClickArg */) => { - // TODO: open popover with event description, option to open CalendarManager and change event date, delete event, etc. + internalDocDrop(e: Event, de: DragManager.DropEvent, docDragData: DragManager.DocumentDragData) { + if (!super.onInternalDrop(e, de)) return false; + de.complete.docDragData?.droppedDocuments.forEach(doc => { + const today = new Date().toISOString(); + if (!doc.date_range) doc[DocData].date_range = `${today}|${today}`; + }); + return true; + } + + onInternalDrop = (e: Event, de: DragManager.DropEvent): boolean => { + if (de.complete.docDragData?.droppedDocuments.length) return this.internalDocDrop(e, de, de.complete.docDragData); + return false; + }; + + handleEventClick = (arg: EventClickArg) => { + const doc = DocServer.GetCachedRefField(arg.event._def.groupId ?? ''); + DocumentView.DeselectAll(); + if (doc) { + DocumentView.showDocument(doc, { openLocation: OpenWhere.lightboxAlways }); + arg.jsEvent.stopPropagation(); + } }; // https://fullcalendar.io @@ -94,8 +120,9 @@ export class CalendarBox extends CollectionSubView() { headerToolbar: { left: 'prev,next today', center: 'title', - right: 'multiMonth,dayGridMonth,timeGridWeek,timeGridDay', + right: 'multiMonth dayGridMonth timeGridWeek timeGridDay', }, + initialView: this.calendarViewType === 'multiMonth' ? undefined : this.calendarViewType, initialDate: this.dateRangeStrDates.startDate, navLinks: true, editable: false, @@ -110,19 +137,29 @@ export class CalendarBox extends CollectionSubView() { onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); render() { - this._multiMonth; return (
- setTimeout(() => { - if ((e.nativeEvent.target as HTMLButtonElement)?.className?.includes('multiMonth-button')) { - this._multiMonth = this._multiMonth + 1; - } - }) - )} + onPointerDown={e => + setTimeout( + action(() => { + const cname = (e.nativeEvent.target as HTMLButtonElement)?.className ?? ''; + if (cname.includes('multiMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'multiMonth'; + if (cname.includes('dayGridMonth')) this.dataDoc[this.fieldKey + '_calendarType'] = 'dayGridMonth'; + if (cname.includes('timeGridWeek')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridWeek'; + if (cname.includes('timeGridDay')) this.dataDoc[this.fieldKey + '_calendarType'] = 'timeGridDay'; + }) + ) + } + style={{ + width: this._props.PanelWidth() / this._props.ScreenToLocalTransform().Scale, + height: this._props.PanelHeight() / this._props.ScreenToLocalTransform().Scale, + transform: `scale(${this._props.ScreenToLocalTransform().Scale})`, + transformOrigin: 'top left', + }} ref={r => { + this.createDashEventsTarget(r); this._oldWheel?.removeEventListener('wheel', this.onPassiveWheel); this._oldWheel = r; // prevent wheel events from passively propagating up through containers and prevents containers from preventDefault which would block scrolling -- cgit v1.2.3-70-g09d2