Skip to content

Commit 3b81e81

Browse files
authored
fix(deps): migrate FullCalendar to v7 (#10890)
2 parents d7f0582 + 9862cdb commit 3b81e81

23 files changed

Lines changed: 480 additions & 342 deletions

apps/client/package.json

Lines changed: 4 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -18,13 +18,7 @@
1818
},
1919
"dependencies": {
2020
"@excalidraw/excalidraw": "0.18.1",
21-
"@fullcalendar/core": "6.1.21",
22-
"@fullcalendar/daygrid": "6.1.21",
23-
"@fullcalendar/interaction": "6.1.21",
24-
"@fullcalendar/list": "6.1.21",
25-
"@fullcalendar/multimonth": "6.1.21",
26-
"@fullcalendar/rrule": "6.1.21",
27-
"@fullcalendar/timegrid": "6.1.21",
21+
"@fullcalendar/rrule": "7.0.2",
2822
"@mermaid-js/layout-elk": "0.2.2",
2923
"@popperjs/core": "2.11.8",
3024
"@preact/signals": "2.10.1",
@@ -52,6 +46,7 @@
5246
"dompurify": "3.4.12",
5347
"draggabilly": "3.0.0",
5448
"force-graph": "1.51.4",
49+
"fullcalendar": "7.0.2",
5550
"gridstack": "13.0.2",
5651
"htmldiff-js": "1.0.5",
5752
"i18next": "26.3.6",
@@ -74,7 +69,8 @@
7469
"reveal.js": "6.0.1",
7570
"rrule": "2.8.1",
7671
"svg-pan-zoom": "3.6.2",
77-
"tabulator-tables": "6.5.2"
72+
"tabulator-tables": "6.5.2",
73+
"temporal-polyfill": "1.0.3"
7874
},
7975
"devDependencies": {
8076
"@ckeditor/ckeditor5-inspector": "5.0.3",

apps/client/src/stylesheets/style.css

Lines changed: 0 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -2946,14 +2946,6 @@ iframe.print-iframe {
29462946
overflow: hidden;
29472947
}
29482948

2949-
/* Calendar collection */
2950-
2951-
.calendar-view a.fc-timegrid-event,
2952-
.calendar-view a.fc-daygrid-event {
2953-
/* Workaround: set font weight only if the theme-next is not active */
2954-
font-weight: var(--root-background, 800);
2955-
}
2956-
29572949
@media (max-width: 991px) {
29582950
body.mobile {
29592951
.split-note-container-widget {

apps/client/src/widgets/collections/board/index.css

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@
1414
height: 100%;
1515
display: flex;
1616
gap: 1em;
17-
padding: 4px var(--content-margin-inline);
17+
padding: var(--content-margin-inline);
1818
align-items: flex-start;
1919
overflow-x: auto;
2020
}

apps/client/src/widgets/collections/calendar/EventPopover.tsx

Lines changed: 9 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,7 +18,7 @@ import EventDatesEditor from "./EventDatesEditor";
1818
import { EventFieldList } from "./EventField";
1919
import { useEventLabelOmissions } from "./hooks";
2020
import RecurrenceEditor from "./RecurrenceEditor";
21-
import { AnchorPoint } from "./selection";
21+
import { AnchorPoint, narrowAnchorRect } from "./selection";
2222

2323
/**
2424
* The whole of an event: the note's title, its own fields — when it happens, how it repeats — its
@@ -118,7 +118,9 @@ function EventPopoverShell({ note, anchorRect, updateKey, parentNote, isEditable
118118
// A press on another chip is not a dismissal but a switch: the click behind it re-points
119119
// this popover at that event (see onEventClick), which would otherwise have to tear the
120120
// popover down on the press and build it again on the click.
121-
keepOpenSelector=".fc-event"
121+
// Named by the attribute eventDidMount stamps on every chip rather than by a
122+
// FullCalendar class: v7 emits hashed class names, so `.fc-event` no longer exists.
123+
keepOpenSelector="[data-event-note-id]"
122124
onDismiss={onClose}
123125
>
124126
<EventDetails note={note} parentNote={parentNote} isEditable={isEditable} onClose={onClose} onFollowLink={onFollowLink} />
@@ -288,9 +290,12 @@ function EventDetailsBody({ note, parentNote, isEditable, onClose }: {
288290
* row, so the one under the click is the one pointed at, falling back to the first drawn, and to
289291
* the bare click point where no chip is on the grid at all. Read afresh on every reposition (see
290292
* Popover), so the popover follows the chip through scrolls and redraws.
293+
*
294+
* A chip too wide to be stood beside — an event spanning the whole week, which is drawn the width
295+
* of the grid — is narrowed to the click within it (see {@link narrowAnchorRect}).
291296
*/
292297
function eventAnchorRect(container: HTMLElement | null, noteId: string, point: AnchorPoint | null): DOMRect {
293-
const chips = container?.querySelectorAll<HTMLElement>(`.fc-event[data-event-note-id="${CSS.escape(noteId)}"]`);
298+
const chips = container?.querySelectorAll<HTMLElement>(`[data-event-note-id="${CSS.escape(noteId)}"]`);
294299

295300
if (chips?.length) {
296301
let pick: HTMLElement | undefined;
@@ -303,7 +308,7 @@ function eventAnchorRect(container: HTMLElement | null, noteId: string, point: A
303308
}
304309
}
305310
}
306-
return (pick ?? chips[0]).getBoundingClientRect();
311+
return narrowAnchorRect((pick ?? chips[0]).getBoundingClientRect(), point);
307312
}
308313

309314
return new DOMRect(point?.x ?? 0, point?.y ?? 0, 0, 0);

apps/client/src/widgets/collections/calendar/GhostPopover.tsx

Lines changed: 13 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ import Button from "../../react/Button";
99
import FormTextBox from "../../react/FormTextBox";
1010
import Modal from "../../react/Modal";
1111
import Popover from "../../react/Popover";
12-
import { AnchorPoint, EventDraft } from "./selection";
12+
import { AnchorPoint, EventDraft, narrowAnchorRect } from "./selection";
1313

1414
/**
1515
* The ghost: the form an event is decided in before its note exists. It asks for the one thing the
@@ -165,13 +165,19 @@ export default function GhostPopover({ draft, anchor, container, onCommit, onCan
165165

166166
/**
167167
* Where the ghost stands: beside the range's own shading, read back off the grid. FullCalendar
168-
* draws the selection as one `.fc-highlight` per row it crosses, so the piece under the mouseup is
169-
* the one pointed at — where the drag ended is where the eye is — falling back to the last piece
170-
* drawn, and to the bare point where no shading is on the grid at all. Read afresh on every
171-
* reposition (see Popover), so the ghost follows the shading through scrolls and redraws.
168+
* draws the selection as one piece per row it crosses, so the piece under the mouseup is the one
169+
* pointed at — where the drag ended is where the eye is — falling back to the last piece drawn, and
170+
* to the bare point where no shading is on the grid at all. Read afresh on every reposition (see
171+
* Popover), so the ghost follows the shading through scrolls and redraws.
172+
*
173+
* A piece too wide to be stood beside — a range dragged across a whole week — is narrowed to the
174+
* end of the drag within it (see {@link narrowAnchorRect}).
175+
*
176+
* The pieces answer to the name the calendar gives them through `highlightClass`, FullCalendar
177+
* having no class of its own left to go by (see the calendar view).
172178
*/
173179
function ghostAnchorRect(container: HTMLElement | null, point: AnchorPoint | null): DOMRect {
174-
const highlights = container?.querySelectorAll<HTMLElement>(".fc-highlight");
180+
const highlights = container?.querySelectorAll<HTMLElement>(".calendar-highlight");
175181

176182
if (highlights?.length) {
177183
let pick: HTMLElement | undefined;
@@ -184,7 +190,7 @@ function ghostAnchorRect(container: HTMLElement | null, point: AnchorPoint | nul
184190
}
185191
}
186192
}
187-
return (pick ?? highlights[highlights.length - 1]).getBoundingClientRect();
193+
return narrowAnchorRect((pick ?? highlights[highlights.length - 1]).getBoundingClientRect(), point);
188194
}
189195

190196
return new DOMRect(point?.x ?? 0, point?.y ?? 0, 0, 0);

apps/client/src/widgets/collections/calendar/calendar.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import { useEffect, useLayoutEffect, useRef } from "preact/hooks";
2-
import { CalendarOptions, Calendar as FullCalendar, PluginDef } from "@fullcalendar/core";
2+
import { CalendarOptions, Calendar as FullCalendar } from "fullcalendar";
33
import { RefObject } from "preact";
44

55
export interface CalendarProps extends CalendarOptions {

apps/client/src/widgets/collections/calendar/event_builder.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
1-
import { EventInput, EventSourceFuncArg, EventSourceInput } from "@fullcalendar/core/index.js";
21
import { dayjs } from "@triliumnext/commons";
32
import clsx from "clsx";
3+
import { EventInput, EventSourceFuncInfo, EventSourceInput } from "fullcalendar";
44
import * as rruleLib from 'rrule';
55

66
import FNote from "../../../entities/fnote";
@@ -48,7 +48,7 @@ export async function buildEvents(noteIds: string[]) {
4848
return events.flat();
4949
}
5050

51-
export async function buildEventsForCalendar(note: FNote, e: EventSourceFuncArg) {
51+
export async function buildEventsForCalendar(note: FNote, e: EventSourceFuncInfo) {
5252
const events: EventInput[] = [];
5353

5454
// Gather all the required date note IDs.
Lines changed: 57 additions & 105 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,4 @@
11
.calendar-view {
2-
--fc-event-border-color: var(--calendar-coll-event-text-color, var(--main-text-color));
3-
--fc-event-bg-color: var(--calendar-coll-event-background-color, var(--accented-background-color));
4-
--fc-event-text-color: var(--calendar-coll-event-text-color, var(--main-text-color));
5-
--fc-event-selected-overlay-color: transparent;
6-
--fc-today-bg-color: var(--calendar-coll-today-background-color, var(--more-accented-background-color));
7-
82
overflow: hidden;
93
position: relative;
104
outline: 0;
@@ -13,7 +7,6 @@
137
padding: 0;
148
display: flex;
159
flex-direction: column;
16-
padding-bottom: 16px;
1710
box-sizing: border-box;
1811

1912
@media (max-width: 991px) {
@@ -26,11 +19,6 @@
2619
}
2720
}
2821

29-
.calendar-view a,
30-
:root .calendar-view a.fc-daygrid-event:hover {
31-
color: var(--fc-event-text-color);
32-
}
33-
3422
.search-result-widget-content .calendar-view {
3523
position: absolute;
3624
top: 0;
@@ -41,19 +29,12 @@
4129

4230
.calendar-container {
4331
height: 100%;
44-
--fc-page-bg-color: var(--main-background-color);
45-
--fc-border-color: var(--main-border-color);
46-
--fc-neutral-bg-color: var(--launcher-pane-background-color);
47-
--fc-list-event-hover-bg-color: var(--left-pane-item-hover-background);
48-
padding: 0 var(--content-margin-inline);
4932
}
5033

51-
.calendar-container .fc-list-sticky .fc-list-day > * {
52-
z-index: 50;
53-
}
54-
55-
.calendar-container .fc-button {
56-
padding: 0.2em 0.5em;
34+
/* Stacked beneath the title, the chip's own container being a column wherever these are shown at
35+
all (see hasRoomForAttributes). Kept narrow so the overflow below has something to bite on. */
36+
.calendar-view .calendar-event-attributes {
37+
min-width: 0;
5738
}
5839

5940
.calendar-container .promoted-attribute {
@@ -94,109 +75,80 @@
9475

9576
/* #region Events */
9677

97-
/*
98-
* week, month, year views
99-
*/
100-
101-
.calendar-container a.fc-event {
78+
.calendar-view .calendar-event:hover {
10279
text-decoration: none;
10380
}
10481

105-
.calendar-container a.fc-event.archived {
106-
opacity: .65;
107-
}
108-
109-
.calendar-container a.fc-event.archived::after {
110-
content: "";
111-
position: absolute;
112-
top: 0;
113-
left: 0;
114-
right: 0;
115-
bottom: 0;
116-
z-index: -1;
117-
118-
--c1: transparent;
119-
--c2: var(--callendar-coll-event-archived-sripe-color, #00000013);
120-
121-
background: repeating-linear-gradient(45deg, var(--c1), var(--c1) 8px,
122-
var(--c2) 8px, var(--c2) 16px);
123-
}
124-
125-
.calendar-view a.fc-timegrid-event,
126-
.calendar-view a.fc-daygrid-event,
127-
.calendar-view .fc-daygrid-dot-event .fc-event-title {
128-
font-weight: 500;
129-
}
130-
131-
.calendar-view a.fc-timegrid-event,
132-
.calendar-view a.fc-daygrid-event {
133-
--border-color: transparent;
134-
135-
border: 2px solid;
136-
border-left-width: 4px;
137-
border-color: var(--border-color) var(--border-color) var(--border-color)
138-
var(--fc-event-text-color) !important;
139-
background: var(--fc-event-bg-color) !important;
82+
/*
83+
* Forma draws a chip out of a single accent colour: a solid bar down its leading edge, and a 30%
84+
* tint of that same colour for the body. Trilium's chip is a pair rather than a derivation — a
85+
* black bar over a flat #eaeaea body — so the two are split. The accent is handed to the bar
86+
* through the palette's --fc-forma-event, and the body is stated here, on a class eventClass puts
87+
* on every chip.
88+
*/
89+
.calendar-view .calendar-event {
90+
background: var(--calendar-coll-event-background-color, var(--accented-background-color));
14091

141-
padding-left: 8px;
92+
/* Said outright, and so beyond the reach of the app-wide a:hover, the chip being an anchor. */
93+
color: var(--calendar-coll-event-text-color, var(--main-text-color));
14294
}
14395

144-
.calendar-view .fc-timegrid-event.with-hue,
145-
.calendar-view .fc-daygrid-event.with-hue {
146-
--fc-event-text-color: var(--custom-color);
147-
148-
--fc-event-bg-color: hsl(var(--custom-color-hue),
96+
/*
97+
* A note carrying a colour of its own says both: the bar takes the colour, the body a pale wash of
98+
* the same hue. Said as border-color rather than through --fc-event-color, which FullCalendar
99+
* writes inline on the chip and a stylesheet therefore cannot reach.
100+
*/
101+
.calendar-view .calendar-event.with-hue {
102+
border-color: var(--custom-color);
103+
color: var(--custom-color);
104+
background: hsl(var(--custom-color-hue),
149105
var(--calendar-coll-event-background-saturation, 80%),
150-
var(--calendar-coll-event-background-lightness, 95%)) !important;
151-
}
152-
153-
.calendar-view a.fc-timegrid-event:focus-visible,
154-
.calendar-view a.fc-daygrid-event:focus-visible {
155-
outline: none;
106+
var(--calendar-coll-event-background-lightness, 95%));
156107
}
157108

158-
.calendar-view a.fc-timegrid-event.fc-event-selected,
159-
.calendar-view a.fc-timegrid-event.fc-event:focus,
160-
.calendar-view a.fc-daygrid-event.fc-event-selected,
161-
.calendar-view a.fc-daygrid-event.fc-event:focus {
162-
--border-color: var(--custom-color, var(--input-focus-outline-color));
109+
/* Flat body and all, a chip still has to answer the pointer; Forma's own hover tint is what the
110+
rule above overrides. */
111+
.calendar-view .calendar-event:hover {
112+
filter: var(--calendar-coll-event-hover-filter, none);
163113
}
164114

165-
/* The event the popover stands for, marked the way a focused event is. */
166-
.calendar-view a.fc-timegrid-event.calendar-event-selected,
167-
.calendar-view a.fc-daygrid-event.calendar-event-selected {
168-
--border-color: var(--custom-color, var(--input-focus-outline-color));
115+
/* The shading a dragged range stands in, named by us rather than left to the theme's hashed class
116+
so the ghost can anchor to it; the colour is still Forma's, off the palette. */
117+
.calendar-view .calendar-highlight {
118+
background-color: var(--fc-forma-highlight);
169119
}
170120

171-
.calendar-view a.fc-timegrid-event:hover,
172-
.calendar-view a.fc-daygrid-event:hover {
173-
filter: var(--calendar-coll-event-hover-filter, none);
174-
border-color: var(--fc-event-text-color);
175-
text-decoration: none;
176-
color: currentColor;
177-
opacity: 1;
121+
/* The note's icon, leading its title on the chip (see eventContent). */
122+
.calendar-view .calendar-event-icon {
123+
margin-inline-end: .25em;
178124
}
179125

180-
.calendar-view .fc-daygrid-event-dot {
181-
display: none;
126+
/* An archived note is faded and hatched, the stripes drawn behind its content. */
127+
.calendar-view .calendar-event.archived {
128+
opacity: .65;
182129
}
183130

131+
.calendar-view .calendar-event.archived::after {
132+
content: "";
133+
position: absolute;
134+
top: 0;
135+
inset-inline-start: 0;
136+
inset-inline-end: 0;
137+
bottom: 0;
138+
z-index: -1;
184139

185-
.calendar-view .fc-event-time {
186-
opacity: .75;
187-
}
188-
189-
/*
190-
* List view
191-
*/
140+
--c1: transparent;
141+
--c2: var(--callendar-coll-event-archived-sripe-color, #00000013);
192142

193-
.fc-list-table tr.fc-event.archived {
194-
opacity: .5;
143+
background: repeating-linear-gradient(45deg, var(--c1), var(--c1) 8px,
144+
var(--c2) 8px, var(--c2) 16px);
195145
}
196146

197-
.fc-list-table .fc-list-event-dot {
198-
/* Apply note colors to the list item dots */
199-
--fc-event-border-color: var(--custom-color);
147+
/* The event the popover stands for, marked the way a focused event is. An outline rather than a
148+
border, the chip's only border being the bar down its leading edge. */
149+
.calendar-view .calendar-event-selected {
150+
outline: 2px solid var(--custom-color, var(--input-focus-outline-color));
151+
outline-offset: -2px;
200152
}
201153

202154
/* #endregion */

0 commit comments

Comments
 (0)