diff options
author | bobzel <zzzman@gmail.com> | 2024-09-04 13:05:17 -0400 |
---|---|---|
committer | bobzel <zzzman@gmail.com> | 2024-09-04 13:05:17 -0400 |
commit | 0b77229000231869695a6211e216d5b1755f53f7 (patch) | |
tree | 70bc7018a861c07a93cb855ce6ac55d449991ee8 | |
parent | 158d501642b0183b286913eb396c396922166435 (diff) |
made calendarBox work
-rw-r--r-- | src/ClientUtils.ts | 3 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCalendarView.tsx | 4 | ||||
-rw-r--r-- | src/client/views/collections/CollectionCardDeckView.scss | 18 | ||||
-rw-r--r-- | src/client/views/nodes/calendarBox/CalendarBox.scss | 18 | ||||
-rw-r--r-- | src/client/views/nodes/calendarBox/CalendarBox.tsx | 172 |
5 files changed, 131 insertions, 84 deletions
diff --git a/src/ClientUtils.ts b/src/ClientUtils.ts index 55801df81..51ad55c07 100644 --- a/src/ClientUtils.ts +++ b/src/ClientUtils.ts @@ -650,6 +650,7 @@ export function DivWidth(ele: HTMLElement | null): number { 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() }; const fromParts = dateRangeParts[0].split('-'); const toParts = dateRangeParts[1].split('-'); @@ -661,7 +662,7 @@ export function dateRangeStrToDates(dateStr: string) { const toMonth = parseInt(toParts[1]) - 1; const toDay = parseInt(toParts[2]); - return [new Date(fromYear, fromMonth, fromDay), new Date(toYear, toMonth, toDay)]; + return { startDate: new Date(fromYear, fromMonth, fromDay), endDate: new Date(toYear, toMonth, toDay) }; } function replaceCanvases(oldDiv: HTMLElement, newDiv: HTMLElement) { diff --git a/src/client/views/collections/CollectionCalendarView.tsx b/src/client/views/collections/CollectionCalendarView.tsx index 9eb16917b..c1f0b314b 100644 --- a/src/client/views/collections/CollectionCalendarView.tsx +++ b/src/client/views/collections/CollectionCalendarView.tsx @@ -38,8 +38,8 @@ export class CollectionCalendarView extends CollectionSubView() { const aDateRangeStr = StrCast(DocListCast(calendarA.data).lastElement()?.date_range); const bDateRangeStr = StrCast(DocListCast(calendarB.data).lastElement()?.date_range); - const [aFromDate, aToDate] = dateRangeStrToDates(aDateRangeStr); - const [bFromDate, bToDate] = dateRangeStrToDates(bDateRangeStr); + const { startDate: aFromDate, endDate: aToDate } = dateRangeStrToDates(aDateRangeStr); + const { startDate: bFromDate, endDate: bToDate } = dateRangeStrToDates(bDateRangeStr); if (aFromDate > bFromDate) { return -1; // a comes first diff --git a/src/client/views/collections/CollectionCardDeckView.scss b/src/client/views/collections/CollectionCardDeckView.scss index a089b248d..cc797d0bd 100644 --- a/src/client/views/collections/CollectionCardDeckView.scss +++ b/src/client/views/collections/CollectionCardDeckView.scss @@ -6,6 +6,15 @@ position: relative; background-color: white; overflow: hidden; + + button { + width: 35px; + height: 35px; + border-radius: 50%; + background-color: $dark-gray; + // border-color: $medium-blue; + margin: 5px; // transform: translateY(-50px); + } } .card-wrapper { @@ -34,15 +43,6 @@ justify-content: start; /* Centers buttons horizontally */ } -button { - width: 35px; - height: 35px; - border-radius: 50%; - background-color: $dark-gray; - // border-color: $medium-blue; - margin: 5px; // transform: translateY(-50px); -} - // button:hover { // transform: translateY(-50px); // } diff --git a/src/client/views/nodes/calendarBox/CalendarBox.scss b/src/client/views/nodes/calendarBox/CalendarBox.scss new file mode 100644 index 000000000..8eee47e42 --- /dev/null +++ b/src/client/views/nodes/calendarBox/CalendarBox.scss @@ -0,0 +1,18 @@ +.calendarBox { + display: flex; + width: 100%; + height: 100%; + .calendarBox-wrapper { + width: 100%; + height: 100%; + .fc-col-header { + width: 100% !important; + } + .fc-daygrid-body { + width: 100% !important; + .fc-scrollgrid-sync-table { + width: 100% !important; + } + } + } +} diff --git a/src/client/views/nodes/calendarBox/CalendarBox.tsx b/src/client/views/nodes/calendarBox/CalendarBox.tsx index bd66941c3..ca35d85b3 100644 --- a/src/client/views/nodes/calendarBox/CalendarBox.tsx +++ b/src/client/views/nodes/calendarBox/CalendarBox.tsx @@ -1,16 +1,17 @@ import { Calendar, EventSourceInput } from '@fullcalendar/core'; import dayGridPlugin from '@fullcalendar/daygrid'; import multiMonthPlugin from '@fullcalendar/multimonth'; -import { makeObservable } from 'mobx'; +import { action, computed, makeObservable, observable, reaction } from 'mobx'; import { observer } from 'mobx-react'; import * as React from 'react'; import { dateRangeStrToDates } from '../../../../ClientUtils'; -import { Doc } from '../../../../fields/Doc'; -import { StrCast } from '../../../../fields/Types'; +import { Doc, DocListCast } from '../../../../fields/Doc'; +import { NumCast, StrCast } from '../../../../fields/Types'; import { DocumentType } from '../../../documents/DocumentTypes'; import { Docs } from '../../../documents/Documents'; import { ViewBoxBaseComponent } from '../../DocComponent'; import { FieldView, FieldViewProps } from '../FieldView'; +import './CalendarBox.scss'; type CalendarView = 'month' | 'multi-month' | 'week'; @@ -20,108 +21,135 @@ export class CalendarBox extends ViewBoxBaseComponent<FieldViewProps>() { return FieldView.LayoutString(CalendarBox, fieldKey); } + _calendarRef: HTMLDivElement | null = null; + _calendar: Calendar | undefined; + _oldWheel: HTMLElement | null = null; + _observer: ResizeObserver | undefined; + constructor(props: FieldViewProps) { super(props); makeObservable(this); } - componentDidMount(): void {} + @observable _multiMonth = 0; - componentWillUnmount(): void {} + componentDidMount(): void { + reaction( + () => this.calendarEvents, + events => { + this._calendar?.setOption('events', events); + }, + { fireImmediately: true } + ); + } - _calendarRef = React.createRef<HTMLElement>(); + @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, + startEditable: true, + endEditable: true, + allDay: false, + classNames: ['mother'], // will determine the style + editable: true, // subject to change in the future + backgroundColor: this.eventToColor(doc), + color: 'white', + extendedProps: { + description: StrCast(doc.description), + }, + }; + }); + } - get dateRangeStr() { - return StrCast(this.Document.date_range); + @computed get dateRangeStrDates() { + return dateRangeStrToDates(StrCast(this.Document.date_range)); } // Choose a calendar view based on the date range - get calendarViewType(): CalendarView { - const [fromDate, toDate] = dateRangeStrToDates(this.dateRangeStr); + @computed get calendarViewType(): CalendarView { + const { startDate, endDate } = this.dateRangeStrDates; - if (fromDate.getFullYear() !== toDate.getFullYear() || fromDate.getMonth() !== toDate.getMonth()) return 'multi-month'; + if (startDate.getFullYear() !== endDate.getFullYear() || startDate.getMonth() !== endDate.getMonth()) return 'multi-month'; - if (Math.abs(fromDate.getDay() - toDate.getDay()) > 7) return 'month'; + if (Math.abs(startDate.getDay() - endDate.getDay()) > 7) return 'month'; return 'week'; } - - get calendarStartDate() { - return this.dateRangeStr.split('|')[0]; - } - - get calendarToDate() { - return this.dateRangeStr.split('|')[1]; + @computed get childDocs() { + return DocListCast(this.dataDoc[this.fieldKey]); } - get childDocs(): Doc[] { - return this.childDocs; // get all sub docs for a calendar - } - - docBackgroundColor(type: string): string { + eventToColor(event: Doc): string { // TODO: Return a different color based on the event type - console.log(type); + console.log(event.title); return 'blue'; } - get calendarEvents(): EventSourceInput | undefined { - if (this.childDocs.length === 0) return undefined; - return this.childDocs.map(doc => { - const docTitle = StrCast(doc.title); - const docDateRange = StrCast(doc.date_range); - const [startDate, endDate] = dateRangeStrToDates(docDateRange); - const docType = doc.type; - const docDescription = doc.description ? StrCast(doc.description) : ''; - - return { - title: docTitle, - start: startDate, - end: endDate, - allDay: false, - classNames: [StrCast(docType)], // will determine the style - editable: false, // subject to change in the future - backgroundColor: this.docBackgroundColor(StrCast(doc.type)), - color: 'white', - extendedProps: { - description: docDescription, - }, - }; - }); - } - handleEventClick = (/* arg: EventClickArg */) => { // TODO: open popover with event description, option to open CalendarManager and change event date, delete event, etc. }; - calendarEl: HTMLElement = document.getElementById('calendar-box-v1')!; - // https://fullcalendar.io - get calendar() { - return new Calendar(this.calendarEl, { - plugins: [this.calendarViewType === 'multi-month' ? multiMonthPlugin : dayGridPlugin], - headerToolbar: { - left: 'prev,next today', - center: 'title', - right: 'dayGridMonth,timeGridWeek,timeGridDay,listWeek', - }, - initialDate: this.calendarStartDate, - navLinks: true, - editable: false, - displayEventTime: false, - displayEventEnd: false, - events: this.calendarEvents, - eventClick: this.handleEventClick, - }); - } + renderCalendar = () => { + const cal = !this._calendarRef + ? null + : (this._calendar = new Calendar(this._calendarRef, { + plugins: [multiMonthPlugin, dayGridPlugin], + headerToolbar: { + left: 'prev,next today', + center: 'title', + right: 'dayGridMonth multiMonth', // timeGridWeek timeGridDay listWeek', + }, + initialDate: this.dateRangeStrDates.startDate, + navLinks: true, + editable: false, + displayEventTime: false, + displayEventEnd: false, + aspectRatio: NumCast(this.Document.width) / NumCast(this.Document.height), + events: this.calendarEvents, + eventClick: this.handleEventClick, + })); + cal?.render(); + }; + onPassiveWheel = (e: WheelEvent) => e.stopPropagation(); render() { + this._multiMonth; return ( - <div className="calendar-box-conatiner"> - <div id="calendar-box-v1" /> + <div + key={this._multiMonth} + className="calendarBox" + onPointerDown={action(e => + setTimeout(() => { + if ((e.nativeEvent.target as HTMLButtonElement)?.className?.includes('multiMonth-button')) { + this._multiMonth = this._multiMonth + 1; + } + }) + )} + ref={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 + r?.addEventListener('wheel', this.onPassiveWheel, { passive: false }); + + if (r) { + this._observer?.disconnect(); + (this._observer = new ResizeObserver(() => { + this._calendar?.setOption('aspectRatio', NumCast(this.Document.width) / NumCast(this.Document.height)); + this._calendar?.updateSize(); + })).observe(r); + this.renderCalendar(); + } + }}> + <div className="calendarBox-wrapper" ref={r => (this._calendarRef = r)} /> </div> ); } } Docs.Prototypes.TemplateMap.set(DocumentType.CALENDAR, { layout: { view: CalendarBox, dataField: 'data' }, - options: { acl: '' }, + options: { acl: '', _layout_nativeDimEditable: true, _layout_reflowHorizontal: true, _layout_reflowVertical: true }, }); |