@fullcalendar/multimonth 6.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/LICENSE.md +22 -0
- package/README.md +32 -0
- package/index.d.ts +20 -0
- package/index.esm.js +238 -0
- package/index.global.js +249 -0
- package/index.global.min.js +6 -0
- package/index.js +242 -0
- package/package.json +53 -0
package/LICENSE.md
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2021 Adam Shaw
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
|
6
|
+
a copy of this software and associated documentation files (the
|
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
|
11
|
+
the following conditions:
|
|
12
|
+
|
|
13
|
+
The above copyright notice and this permission notice shall be
|
|
14
|
+
included in all copies or substantial portions of the Software.
|
|
15
|
+
|
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
|
|
2
|
+
# FullCalendar Multi-Month Plugin
|
|
3
|
+
|
|
4
|
+
Display a sequence or grid of multiple months
|
|
5
|
+
|
|
6
|
+
## Installation
|
|
7
|
+
|
|
8
|
+
Install the necessary packages:
|
|
9
|
+
|
|
10
|
+
```sh
|
|
11
|
+
npm install @fullcalendar/core @fullcalendar/multimonth
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
## Usage
|
|
15
|
+
|
|
16
|
+
Instantiate a Calendar with the necessary plugin:
|
|
17
|
+
|
|
18
|
+
```js
|
|
19
|
+
import { Calendar } from '@fullcalendar/core'
|
|
20
|
+
import multiMonthPlugin from '@fullcalendar/multimonth'
|
|
21
|
+
|
|
22
|
+
const calendarEl = document.getElementById('calendar')
|
|
23
|
+
const calendar = new Calendar(calendarEl, {
|
|
24
|
+
plugins: [multiMonthPlugin],
|
|
25
|
+
initialView: 'multiMonthYear',
|
|
26
|
+
events: [
|
|
27
|
+
{ title: 'Meeting', start: new Date() }
|
|
28
|
+
]
|
|
29
|
+
})
|
|
30
|
+
|
|
31
|
+
calendar.render()
|
|
32
|
+
```
|
package/index.d.ts
ADDED
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { PluginDef } from '@fullcalendar/core';
|
|
2
|
+
import { createFormatter } from '@fullcalendar/core/internal';
|
|
3
|
+
|
|
4
|
+
declare const OPTION_REFINERS: {
|
|
5
|
+
multiMonthTitleFormat: typeof createFormatter;
|
|
6
|
+
multiMonthMaxColumns: NumberConstructor;
|
|
7
|
+
multiMonthMinWidth: NumberConstructor;
|
|
8
|
+
};
|
|
9
|
+
|
|
10
|
+
type ExtraOptionRefiners = typeof OPTION_REFINERS;
|
|
11
|
+
declare module '@fullcalendar/core/internal' {
|
|
12
|
+
interface BaseOptionRefiners extends ExtraOptionRefiners {
|
|
13
|
+
}
|
|
14
|
+
}
|
|
15
|
+
//# sourceMappingURL=ambient.d.ts.map
|
|
16
|
+
|
|
17
|
+
declare const _default: PluginDef;
|
|
18
|
+
//# sourceMappingURL=index.d.ts.map
|
|
19
|
+
|
|
20
|
+
export { _default as default };
|
package/index.esm.js
ADDED
|
@@ -0,0 +1,238 @@
|
|
|
1
|
+
import { createPlugin } from '@fullcalendar/core';
|
|
2
|
+
import { buildDayTableModel, DayTableSlicer, TableRows, buildDayTableRenderRange, TableDateProfileGenerator } from '@fullcalendar/daygrid/internal';
|
|
3
|
+
import { DateComponent, memoize, getUniqueDomId, DayHeader, ViewContainer, formatIsoMonthStr, isPropsEqual, createDuration, intersectRanges, createFormatter, injectStyles } from '@fullcalendar/core/internal';
|
|
4
|
+
import { createElement, createRef } from '@fullcalendar/core/preact';
|
|
5
|
+
|
|
6
|
+
class SingleMonth extends DateComponent {
|
|
7
|
+
constructor() {
|
|
8
|
+
super(...arguments);
|
|
9
|
+
this.buildDayTableModel = memoize(buildDayTableModel);
|
|
10
|
+
this.slicer = new DayTableSlicer();
|
|
11
|
+
this.state = {
|
|
12
|
+
labelId: getUniqueDomId(),
|
|
13
|
+
};
|
|
14
|
+
}
|
|
15
|
+
render() {
|
|
16
|
+
const { props, state, context } = this;
|
|
17
|
+
const { dateProfile, forPrint } = props;
|
|
18
|
+
const { options } = context;
|
|
19
|
+
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
|
20
|
+
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
|
21
|
+
// ensure single-month has aspect ratio
|
|
22
|
+
const tableHeight = props.tableWidth != null ? props.tableWidth / options.aspectRatio : null;
|
|
23
|
+
const rowCnt = dayTableModel.cells.length;
|
|
24
|
+
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
|
25
|
+
return (createElement("div", { ref: props.elRef, "data-date": props.isoDateStr, className: "fc-multimonth-month", style: { width: props.width }, role: "grid", "aria-labelledby": state.labelId },
|
|
26
|
+
createElement("div", { className: "fc-multimonth-header", style: { marginBottom: rowHeight }, role: "presentation" },
|
|
27
|
+
createElement("div", { className: "fc-multimonth-title", id: state.labelId }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
|
28
|
+
createElement("table", { className: [
|
|
29
|
+
'fc-multimonth-header-table',
|
|
30
|
+
context.theme.getClass('table'),
|
|
31
|
+
].join(' '), role: "presentation" },
|
|
32
|
+
createElement("thead", { role: "rowgroup" },
|
|
33
|
+
createElement(DayHeader, { dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: false })))),
|
|
34
|
+
createElement("div", { className: [
|
|
35
|
+
'fc-multimonth-daygrid',
|
|
36
|
+
'fc-daygrid',
|
|
37
|
+
'fc-daygrid-body',
|
|
38
|
+
!forPrint && 'fc-daygrid-body-balanced',
|
|
39
|
+
forPrint && 'fc-daygrid-body-unbalanced',
|
|
40
|
+
forPrint && 'fc-daygrid-body-natural',
|
|
41
|
+
].join(' '), style: { marginTop: -rowHeight } },
|
|
42
|
+
createElement("table", { className: [
|
|
43
|
+
'fc-multimonth-daygrid-table',
|
|
44
|
+
context.theme.getClass('table'),
|
|
45
|
+
].join(' '), style: { height: forPrint ? '' : tableHeight }, role: "presentation" },
|
|
46
|
+
createElement("tbody", { role: "rowgroup" },
|
|
47
|
+
createElement(TableRows, Object.assign({}, slicedProps, { dateProfile: dateProfile, cells: dayTableModel.cells, eventSelection: props.eventSelection, dayMaxEvents: !forPrint, dayMaxEventRows: !forPrint, showWeekNumbers: options.weekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: forPrint })))))));
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
class MultiMonthView extends DateComponent {
|
|
52
|
+
constructor() {
|
|
53
|
+
super(...arguments);
|
|
54
|
+
this.splitDateProfileByMonth = memoize(splitDateProfileByMonth);
|
|
55
|
+
this.buildMonthFormat = memoize(buildMonthFormat);
|
|
56
|
+
this.scrollElRef = createRef();
|
|
57
|
+
this.firstMonthElRef = createRef();
|
|
58
|
+
this.needsScrollReset = false;
|
|
59
|
+
this.handleSizing = (isForced) => {
|
|
60
|
+
if (isForced) {
|
|
61
|
+
this.updateSize();
|
|
62
|
+
}
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
render() {
|
|
66
|
+
const { context, props, state } = this;
|
|
67
|
+
const { options } = context;
|
|
68
|
+
const { clientWidth, clientHeight } = state;
|
|
69
|
+
const monthHPadding = state.monthHPadding || 0;
|
|
70
|
+
const colCount = Math.min(clientWidth != null ?
|
|
71
|
+
Math.floor(clientWidth / (options.multiMonthMinWidth + monthHPadding)) :
|
|
72
|
+
1, options.multiMonthMaxColumns) || 1;
|
|
73
|
+
const monthWidthPct = (100 / colCount) + '%';
|
|
74
|
+
const monthTableWidth = clientWidth == null ? null :
|
|
75
|
+
(clientWidth / colCount) - monthHPadding;
|
|
76
|
+
const isLegitSingleCol = clientWidth != null && colCount === 1;
|
|
77
|
+
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, isLegitSingleCol ? false : options.fixedWeekCount, options.showNonCurrentDates);
|
|
78
|
+
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
|
79
|
+
const rootClassNames = [
|
|
80
|
+
'fc-multimonth',
|
|
81
|
+
isLegitSingleCol ?
|
|
82
|
+
'fc-multimonth-singlecol' :
|
|
83
|
+
'fc-multimonth-multicol',
|
|
84
|
+
(monthTableWidth != null && monthTableWidth < 400) ?
|
|
85
|
+
'fc-multimonth-compact' :
|
|
86
|
+
'',
|
|
87
|
+
];
|
|
88
|
+
return (createElement(ViewContainer, { elRef: this.scrollElRef, elClasses: rootClassNames, viewSpec: context.viewSpec }, monthDateProfiles.map((monthDateProfile, i) => {
|
|
89
|
+
const monthStr = formatIsoMonthStr(monthDateProfile.currentRange.start);
|
|
90
|
+
return (createElement(SingleMonth, Object.assign({}, props, { key: monthStr, isoDateStr: monthStr, elRef: i === 0 ? this.firstMonthElRef : undefined, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidthPct, tableWidth: monthTableWidth, clientWidth: clientWidth, clientHeight: clientHeight })));
|
|
91
|
+
})));
|
|
92
|
+
}
|
|
93
|
+
componentDidMount() {
|
|
94
|
+
this.updateSize();
|
|
95
|
+
this.context.addResizeHandler(this.handleSizing);
|
|
96
|
+
this.requestScrollReset();
|
|
97
|
+
}
|
|
98
|
+
componentDidUpdate(prevProps) {
|
|
99
|
+
if (!isPropsEqual(prevProps, this.props)) { // an external change?
|
|
100
|
+
this.handleSizing(false);
|
|
101
|
+
}
|
|
102
|
+
if (prevProps.dateProfile !== this.props.dateProfile) {
|
|
103
|
+
this.requestScrollReset();
|
|
104
|
+
}
|
|
105
|
+
else {
|
|
106
|
+
this.flushScrollReset();
|
|
107
|
+
}
|
|
108
|
+
}
|
|
109
|
+
componentWillUnmount() {
|
|
110
|
+
this.context.removeResizeHandler(this.handleSizing);
|
|
111
|
+
}
|
|
112
|
+
updateSize() {
|
|
113
|
+
const scrollEl = this.scrollElRef.current;
|
|
114
|
+
const firstMonthEl = this.firstMonthElRef.current;
|
|
115
|
+
if (scrollEl) {
|
|
116
|
+
this.setState({
|
|
117
|
+
clientWidth: scrollEl.clientWidth,
|
|
118
|
+
clientHeight: scrollEl.clientHeight,
|
|
119
|
+
});
|
|
120
|
+
}
|
|
121
|
+
if (firstMonthEl && scrollEl) {
|
|
122
|
+
if (this.state.monthHPadding == null) { // always remember initial non-zero value
|
|
123
|
+
this.setState({
|
|
124
|
+
monthHPadding: scrollEl.clientWidth - // go within padding
|
|
125
|
+
firstMonthEl.firstChild.offsetWidth,
|
|
126
|
+
});
|
|
127
|
+
}
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
requestScrollReset() {
|
|
131
|
+
this.needsScrollReset = true;
|
|
132
|
+
this.flushScrollReset();
|
|
133
|
+
}
|
|
134
|
+
flushScrollReset() {
|
|
135
|
+
if (this.needsScrollReset &&
|
|
136
|
+
this.state.monthHPadding != null // indicates sizing already happened
|
|
137
|
+
) {
|
|
138
|
+
const { currentDate } = this.props.dateProfile;
|
|
139
|
+
const scrollEl = this.scrollElRef.current;
|
|
140
|
+
const monthEl = scrollEl.querySelector(`[data-date="${formatIsoMonthStr(currentDate)}"]`);
|
|
141
|
+
scrollEl.scrollTop = monthEl.getBoundingClientRect().top -
|
|
142
|
+
this.firstMonthElRef.current.getBoundingClientRect().top;
|
|
143
|
+
this.needsScrollReset = false;
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
// workaround for when queued setState render (w/ clientWidth) gets cancelled because
|
|
147
|
+
// subsequent update and shouldComponentUpdate says not to render :(
|
|
148
|
+
shouldComponentUpdate() {
|
|
149
|
+
return true;
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
// date profile
|
|
153
|
+
// -------------------------------------------------------------------------------------------------
|
|
154
|
+
const oneMonthDuration = createDuration(1, 'month');
|
|
155
|
+
function splitDateProfileByMonth(dateProfileGenerator, dateProfile, dateEnv, fixedWeekCount, showNonCurrentDates) {
|
|
156
|
+
const { start, end } = dateProfile.currentRange;
|
|
157
|
+
let monthStart = start;
|
|
158
|
+
const monthDateProfiles = [];
|
|
159
|
+
while (monthStart.valueOf() < end.valueOf()) {
|
|
160
|
+
const monthEnd = dateEnv.add(monthStart, oneMonthDuration);
|
|
161
|
+
const currentRange = {
|
|
162
|
+
// yuck
|
|
163
|
+
start: dateProfileGenerator.skipHiddenDays(monthStart),
|
|
164
|
+
end: dateProfileGenerator.skipHiddenDays(monthEnd, -1, true),
|
|
165
|
+
};
|
|
166
|
+
let renderRange = buildDayTableRenderRange({
|
|
167
|
+
currentRange,
|
|
168
|
+
snapToWeek: true,
|
|
169
|
+
fixedWeekCount,
|
|
170
|
+
dateEnv,
|
|
171
|
+
});
|
|
172
|
+
renderRange = {
|
|
173
|
+
// yuck
|
|
174
|
+
start: dateProfileGenerator.skipHiddenDays(renderRange.start),
|
|
175
|
+
end: dateProfileGenerator.skipHiddenDays(renderRange.end, -1, true),
|
|
176
|
+
};
|
|
177
|
+
const activeRange = dateProfile.activeRange ?
|
|
178
|
+
intersectRanges(dateProfile.activeRange, showNonCurrentDates ? renderRange : currentRange) :
|
|
179
|
+
null;
|
|
180
|
+
monthDateProfiles.push({
|
|
181
|
+
currentDate: dateProfile.currentDate,
|
|
182
|
+
isValid: dateProfile.isValid,
|
|
183
|
+
validRange: dateProfile.validRange,
|
|
184
|
+
renderRange,
|
|
185
|
+
activeRange,
|
|
186
|
+
currentRange,
|
|
187
|
+
currentRangeUnit: 'month',
|
|
188
|
+
isRangeAllDay: true,
|
|
189
|
+
dateIncrement: dateProfile.dateIncrement,
|
|
190
|
+
slotMinTime: dateProfile.slotMaxTime,
|
|
191
|
+
slotMaxTime: dateProfile.slotMinTime,
|
|
192
|
+
});
|
|
193
|
+
monthStart = monthEnd;
|
|
194
|
+
}
|
|
195
|
+
return monthDateProfiles;
|
|
196
|
+
}
|
|
197
|
+
// date formatting
|
|
198
|
+
// -------------------------------------------------------------------------------------------------
|
|
199
|
+
const YEAR_MONTH_FORMATTER = createFormatter({ year: 'numeric', month: 'long' });
|
|
200
|
+
const YEAR_FORMATTER = createFormatter({ month: 'long' });
|
|
201
|
+
function buildMonthFormat(formatOverride, monthDateProfiles) {
|
|
202
|
+
return formatOverride ||
|
|
203
|
+
((monthDateProfiles[0].currentRange.start.getUTCFullYear() !==
|
|
204
|
+
monthDateProfiles[monthDateProfiles.length - 1].currentRange.start.getUTCFullYear())
|
|
205
|
+
? YEAR_MONTH_FORMATTER
|
|
206
|
+
: YEAR_FORMATTER);
|
|
207
|
+
}
|
|
208
|
+
|
|
209
|
+
const OPTION_REFINERS = {
|
|
210
|
+
multiMonthTitleFormat: createFormatter,
|
|
211
|
+
multiMonthMaxColumns: Number,
|
|
212
|
+
multiMonthMinWidth: Number,
|
|
213
|
+
};
|
|
214
|
+
|
|
215
|
+
var css_248z = ".fc .fc-multimonth{border:1px solid var(--fc-border-color);display:flex;flex-wrap:wrap;overflow-x:hidden;overflow-y:auto}.fc .fc-multimonth-title{font-size:1.2em;font-weight:700;padding:1em 0;text-align:center}.fc .fc-multimonth-daygrid{background:var(--fc-page-bg-color)}.fc .fc-multimonth-daygrid-table,.fc .fc-multimonth-header-table{table-layout:fixed;width:100%}.fc .fc-multimonth-daygrid-table{border-top-style:hidden!important}.fc .fc-multimonth-singlecol .fc-multimonth{position:relative}.fc .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:relative;top:0;z-index:2}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid{position:relative;z-index:1}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid-table,.fc .fc-multimonth-singlecol .fc-multimonth-header-table{border-left-style:hidden;border-right-style:hidden}.fc .fc-multimonth-singlecol .fc-multimonth-month:last-child .fc-multimonth-daygrid-table{border-bottom-style:hidden}.fc .fc-multimonth-multicol{line-height:1}.fc .fc-multimonth-multicol .fc-multimonth-month{padding:0 1.2em 1.2em}.fc .fc-multimonth-multicol .fc-daygrid-more-link{border:1px solid var(--fc-event-border-color);display:block;float:none;padding:1px}.fc .fc-multimonth-compact{line-height:1}.fc .fc-multimonth-compact .fc-multimonth-daygrid-table,.fc .fc-multimonth-compact .fc-multimonth-header-table{font-size:.9em}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{position:sticky}.fc-media-print .fc-multimonth{overflow:visible}";
|
|
216
|
+
injectStyles(css_248z);
|
|
217
|
+
|
|
218
|
+
var index = createPlugin({
|
|
219
|
+
name: '@fullcalendar/multimonth',
|
|
220
|
+
initialView: 'multiMonthYear',
|
|
221
|
+
optionRefiners: OPTION_REFINERS,
|
|
222
|
+
views: {
|
|
223
|
+
multiMonth: {
|
|
224
|
+
component: MultiMonthView,
|
|
225
|
+
dateProfileGeneratorClass: TableDateProfileGenerator,
|
|
226
|
+
multiMonthMinWidth: 350,
|
|
227
|
+
multiMonthMaxColumns: 3,
|
|
228
|
+
},
|
|
229
|
+
multiMonthYear: {
|
|
230
|
+
type: 'multiMonth',
|
|
231
|
+
duration: { years: 1 },
|
|
232
|
+
fixedWeekCount: true,
|
|
233
|
+
showNonCurrentDates: false,
|
|
234
|
+
},
|
|
235
|
+
},
|
|
236
|
+
});
|
|
237
|
+
|
|
238
|
+
export { index as default };
|
package/index.global.js
ADDED
|
@@ -0,0 +1,249 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
FullCalendar Multi-Month Plugin v6.1.0
|
|
3
|
+
Docs & License: https://fullcalendar.io/docs/multimonth-view
|
|
4
|
+
(c) 2022 Adam Shaw
|
|
5
|
+
*/
|
|
6
|
+
FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact) {
|
|
7
|
+
'use strict';
|
|
8
|
+
|
|
9
|
+
class SingleMonth extends internal.DateComponent {
|
|
10
|
+
constructor() {
|
|
11
|
+
super(...arguments);
|
|
12
|
+
this.buildDayTableModel = internal.memoize(internal$1.buildDayTableModel);
|
|
13
|
+
this.slicer = new internal$1.DayTableSlicer();
|
|
14
|
+
this.state = {
|
|
15
|
+
labelId: internal.getUniqueDomId(),
|
|
16
|
+
};
|
|
17
|
+
}
|
|
18
|
+
render() {
|
|
19
|
+
const { props, state, context } = this;
|
|
20
|
+
const { dateProfile, forPrint } = props;
|
|
21
|
+
const { options } = context;
|
|
22
|
+
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
|
23
|
+
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
|
24
|
+
// ensure single-month has aspect ratio
|
|
25
|
+
const tableHeight = props.tableWidth != null ? props.tableWidth / options.aspectRatio : null;
|
|
26
|
+
const rowCnt = dayTableModel.cells.length;
|
|
27
|
+
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
|
28
|
+
return (preact.createElement("div", { ref: props.elRef, "data-date": props.isoDateStr, className: "fc-multimonth-month", style: { width: props.width }, role: "grid", "aria-labelledby": state.labelId },
|
|
29
|
+
preact.createElement("div", { className: "fc-multimonth-header", style: { marginBottom: rowHeight }, role: "presentation" },
|
|
30
|
+
preact.createElement("div", { className: "fc-multimonth-title", id: state.labelId }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
|
31
|
+
preact.createElement("table", { className: [
|
|
32
|
+
'fc-multimonth-header-table',
|
|
33
|
+
context.theme.getClass('table'),
|
|
34
|
+
].join(' '), role: "presentation" },
|
|
35
|
+
preact.createElement("thead", { role: "rowgroup" },
|
|
36
|
+
preact.createElement(internal.DayHeader, { dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: false })))),
|
|
37
|
+
preact.createElement("div", { className: [
|
|
38
|
+
'fc-multimonth-daygrid',
|
|
39
|
+
'fc-daygrid',
|
|
40
|
+
'fc-daygrid-body',
|
|
41
|
+
!forPrint && 'fc-daygrid-body-balanced',
|
|
42
|
+
forPrint && 'fc-daygrid-body-unbalanced',
|
|
43
|
+
forPrint && 'fc-daygrid-body-natural',
|
|
44
|
+
].join(' '), style: { marginTop: -rowHeight } },
|
|
45
|
+
preact.createElement("table", { className: [
|
|
46
|
+
'fc-multimonth-daygrid-table',
|
|
47
|
+
context.theme.getClass('table'),
|
|
48
|
+
].join(' '), style: { height: forPrint ? '' : tableHeight }, role: "presentation" },
|
|
49
|
+
preact.createElement("tbody", { role: "rowgroup" },
|
|
50
|
+
preact.createElement(internal$1.TableRows, Object.assign({}, slicedProps, { dateProfile: dateProfile, cells: dayTableModel.cells, eventSelection: props.eventSelection, dayMaxEvents: !forPrint, dayMaxEventRows: !forPrint, showWeekNumbers: options.weekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: forPrint })))))));
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
class MultiMonthView extends internal.DateComponent {
|
|
55
|
+
constructor() {
|
|
56
|
+
super(...arguments);
|
|
57
|
+
this.splitDateProfileByMonth = internal.memoize(splitDateProfileByMonth);
|
|
58
|
+
this.buildMonthFormat = internal.memoize(buildMonthFormat);
|
|
59
|
+
this.scrollElRef = preact.createRef();
|
|
60
|
+
this.firstMonthElRef = preact.createRef();
|
|
61
|
+
this.needsScrollReset = false;
|
|
62
|
+
this.handleSizing = (isForced) => {
|
|
63
|
+
if (isForced) {
|
|
64
|
+
this.updateSize();
|
|
65
|
+
}
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
render() {
|
|
69
|
+
const { context, props, state } = this;
|
|
70
|
+
const { options } = context;
|
|
71
|
+
const { clientWidth, clientHeight } = state;
|
|
72
|
+
const monthHPadding = state.monthHPadding || 0;
|
|
73
|
+
const colCount = Math.min(clientWidth != null ?
|
|
74
|
+
Math.floor(clientWidth / (options.multiMonthMinWidth + monthHPadding)) :
|
|
75
|
+
1, options.multiMonthMaxColumns) || 1;
|
|
76
|
+
const monthWidthPct = (100 / colCount) + '%';
|
|
77
|
+
const monthTableWidth = clientWidth == null ? null :
|
|
78
|
+
(clientWidth / colCount) - monthHPadding;
|
|
79
|
+
const isLegitSingleCol = clientWidth != null && colCount === 1;
|
|
80
|
+
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, isLegitSingleCol ? false : options.fixedWeekCount, options.showNonCurrentDates);
|
|
81
|
+
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
|
82
|
+
const rootClassNames = [
|
|
83
|
+
'fc-multimonth',
|
|
84
|
+
isLegitSingleCol ?
|
|
85
|
+
'fc-multimonth-singlecol' :
|
|
86
|
+
'fc-multimonth-multicol',
|
|
87
|
+
(monthTableWidth != null && monthTableWidth < 400) ?
|
|
88
|
+
'fc-multimonth-compact' :
|
|
89
|
+
'',
|
|
90
|
+
];
|
|
91
|
+
return (preact.createElement(internal.ViewContainer, { elRef: this.scrollElRef, elClasses: rootClassNames, viewSpec: context.viewSpec }, monthDateProfiles.map((monthDateProfile, i) => {
|
|
92
|
+
const monthStr = internal.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
|
93
|
+
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, isoDateStr: monthStr, elRef: i === 0 ? this.firstMonthElRef : undefined, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidthPct, tableWidth: monthTableWidth, clientWidth: clientWidth, clientHeight: clientHeight })));
|
|
94
|
+
})));
|
|
95
|
+
}
|
|
96
|
+
componentDidMount() {
|
|
97
|
+
this.updateSize();
|
|
98
|
+
this.context.addResizeHandler(this.handleSizing);
|
|
99
|
+
this.requestScrollReset();
|
|
100
|
+
}
|
|
101
|
+
componentDidUpdate(prevProps) {
|
|
102
|
+
if (!internal.isPropsEqual(prevProps, this.props)) { // an external change?
|
|
103
|
+
this.handleSizing(false);
|
|
104
|
+
}
|
|
105
|
+
if (prevProps.dateProfile !== this.props.dateProfile) {
|
|
106
|
+
this.requestScrollReset();
|
|
107
|
+
}
|
|
108
|
+
else {
|
|
109
|
+
this.flushScrollReset();
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
componentWillUnmount() {
|
|
113
|
+
this.context.removeResizeHandler(this.handleSizing);
|
|
114
|
+
}
|
|
115
|
+
updateSize() {
|
|
116
|
+
const scrollEl = this.scrollElRef.current;
|
|
117
|
+
const firstMonthEl = this.firstMonthElRef.current;
|
|
118
|
+
if (scrollEl) {
|
|
119
|
+
this.setState({
|
|
120
|
+
clientWidth: scrollEl.clientWidth,
|
|
121
|
+
clientHeight: scrollEl.clientHeight,
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
if (firstMonthEl && scrollEl) {
|
|
125
|
+
if (this.state.monthHPadding == null) { // always remember initial non-zero value
|
|
126
|
+
this.setState({
|
|
127
|
+
monthHPadding: scrollEl.clientWidth - // go within padding
|
|
128
|
+
firstMonthEl.firstChild.offsetWidth,
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
requestScrollReset() {
|
|
134
|
+
this.needsScrollReset = true;
|
|
135
|
+
this.flushScrollReset();
|
|
136
|
+
}
|
|
137
|
+
flushScrollReset() {
|
|
138
|
+
if (this.needsScrollReset &&
|
|
139
|
+
this.state.monthHPadding != null // indicates sizing already happened
|
|
140
|
+
) {
|
|
141
|
+
const { currentDate } = this.props.dateProfile;
|
|
142
|
+
const scrollEl = this.scrollElRef.current;
|
|
143
|
+
const monthEl = scrollEl.querySelector(`[data-date="${internal.formatIsoMonthStr(currentDate)}"]`);
|
|
144
|
+
scrollEl.scrollTop = monthEl.getBoundingClientRect().top -
|
|
145
|
+
this.firstMonthElRef.current.getBoundingClientRect().top;
|
|
146
|
+
this.needsScrollReset = false;
|
|
147
|
+
}
|
|
148
|
+
}
|
|
149
|
+
// workaround for when queued setState render (w/ clientWidth) gets cancelled because
|
|
150
|
+
// subsequent update and shouldComponentUpdate says not to render :(
|
|
151
|
+
shouldComponentUpdate() {
|
|
152
|
+
return true;
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
// date profile
|
|
156
|
+
// -------------------------------------------------------------------------------------------------
|
|
157
|
+
const oneMonthDuration = internal.createDuration(1, 'month');
|
|
158
|
+
function splitDateProfileByMonth(dateProfileGenerator, dateProfile, dateEnv, fixedWeekCount, showNonCurrentDates) {
|
|
159
|
+
const { start, end } = dateProfile.currentRange;
|
|
160
|
+
let monthStart = start;
|
|
161
|
+
const monthDateProfiles = [];
|
|
162
|
+
while (monthStart.valueOf() < end.valueOf()) {
|
|
163
|
+
const monthEnd = dateEnv.add(monthStart, oneMonthDuration);
|
|
164
|
+
const currentRange = {
|
|
165
|
+
// yuck
|
|
166
|
+
start: dateProfileGenerator.skipHiddenDays(monthStart),
|
|
167
|
+
end: dateProfileGenerator.skipHiddenDays(monthEnd, -1, true),
|
|
168
|
+
};
|
|
169
|
+
let renderRange = internal$1.buildDayTableRenderRange({
|
|
170
|
+
currentRange,
|
|
171
|
+
snapToWeek: true,
|
|
172
|
+
fixedWeekCount,
|
|
173
|
+
dateEnv,
|
|
174
|
+
});
|
|
175
|
+
renderRange = {
|
|
176
|
+
// yuck
|
|
177
|
+
start: dateProfileGenerator.skipHiddenDays(renderRange.start),
|
|
178
|
+
end: dateProfileGenerator.skipHiddenDays(renderRange.end, -1, true),
|
|
179
|
+
};
|
|
180
|
+
const activeRange = dateProfile.activeRange ?
|
|
181
|
+
internal.intersectRanges(dateProfile.activeRange, showNonCurrentDates ? renderRange : currentRange) :
|
|
182
|
+
null;
|
|
183
|
+
monthDateProfiles.push({
|
|
184
|
+
currentDate: dateProfile.currentDate,
|
|
185
|
+
isValid: dateProfile.isValid,
|
|
186
|
+
validRange: dateProfile.validRange,
|
|
187
|
+
renderRange,
|
|
188
|
+
activeRange,
|
|
189
|
+
currentRange,
|
|
190
|
+
currentRangeUnit: 'month',
|
|
191
|
+
isRangeAllDay: true,
|
|
192
|
+
dateIncrement: dateProfile.dateIncrement,
|
|
193
|
+
slotMinTime: dateProfile.slotMaxTime,
|
|
194
|
+
slotMaxTime: dateProfile.slotMinTime,
|
|
195
|
+
});
|
|
196
|
+
monthStart = monthEnd;
|
|
197
|
+
}
|
|
198
|
+
return monthDateProfiles;
|
|
199
|
+
}
|
|
200
|
+
// date formatting
|
|
201
|
+
// -------------------------------------------------------------------------------------------------
|
|
202
|
+
const YEAR_MONTH_FORMATTER = internal.createFormatter({ year: 'numeric', month: 'long' });
|
|
203
|
+
const YEAR_FORMATTER = internal.createFormatter({ month: 'long' });
|
|
204
|
+
function buildMonthFormat(formatOverride, monthDateProfiles) {
|
|
205
|
+
return formatOverride ||
|
|
206
|
+
((monthDateProfiles[0].currentRange.start.getUTCFullYear() !==
|
|
207
|
+
monthDateProfiles[monthDateProfiles.length - 1].currentRange.start.getUTCFullYear())
|
|
208
|
+
? YEAR_MONTH_FORMATTER
|
|
209
|
+
: YEAR_FORMATTER);
|
|
210
|
+
}
|
|
211
|
+
|
|
212
|
+
const OPTION_REFINERS = {
|
|
213
|
+
multiMonthTitleFormat: internal.createFormatter,
|
|
214
|
+
multiMonthMaxColumns: Number,
|
|
215
|
+
multiMonthMinWidth: Number,
|
|
216
|
+
};
|
|
217
|
+
|
|
218
|
+
var css_248z = ".fc .fc-multimonth{border:1px solid var(--fc-border-color);display:flex;flex-wrap:wrap;overflow-x:hidden;overflow-y:auto}.fc .fc-multimonth-title{font-size:1.2em;font-weight:700;padding:1em 0;text-align:center}.fc .fc-multimonth-daygrid{background:var(--fc-page-bg-color)}.fc .fc-multimonth-daygrid-table,.fc .fc-multimonth-header-table{table-layout:fixed;width:100%}.fc .fc-multimonth-daygrid-table{border-top-style:hidden!important}.fc .fc-multimonth-singlecol .fc-multimonth{position:relative}.fc .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:relative;top:0;z-index:2}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid{position:relative;z-index:1}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid-table,.fc .fc-multimonth-singlecol .fc-multimonth-header-table{border-left-style:hidden;border-right-style:hidden}.fc .fc-multimonth-singlecol .fc-multimonth-month:last-child .fc-multimonth-daygrid-table{border-bottom-style:hidden}.fc .fc-multimonth-multicol{line-height:1}.fc .fc-multimonth-multicol .fc-multimonth-month{padding:0 1.2em 1.2em}.fc .fc-multimonth-multicol .fc-daygrid-more-link{border:1px solid var(--fc-event-border-color);display:block;float:none;padding:1px}.fc .fc-multimonth-compact{line-height:1}.fc .fc-multimonth-compact .fc-multimonth-daygrid-table,.fc .fc-multimonth-compact .fc-multimonth-header-table{font-size:.9em}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{position:sticky}.fc-media-print .fc-multimonth{overflow:visible}";
|
|
219
|
+
internal.injectStyles(css_248z);
|
|
220
|
+
|
|
221
|
+
var plugin = core.createPlugin({
|
|
222
|
+
name: '@fullcalendar/multimonth',
|
|
223
|
+
initialView: 'multiMonthYear',
|
|
224
|
+
optionRefiners: OPTION_REFINERS,
|
|
225
|
+
views: {
|
|
226
|
+
multiMonth: {
|
|
227
|
+
component: MultiMonthView,
|
|
228
|
+
dateProfileGeneratorClass: internal$1.TableDateProfileGenerator,
|
|
229
|
+
multiMonthMinWidth: 350,
|
|
230
|
+
multiMonthMaxColumns: 3,
|
|
231
|
+
},
|
|
232
|
+
multiMonthYear: {
|
|
233
|
+
type: 'multiMonth',
|
|
234
|
+
duration: { years: 1 },
|
|
235
|
+
fixedWeekCount: true,
|
|
236
|
+
showNonCurrentDates: false,
|
|
237
|
+
},
|
|
238
|
+
},
|
|
239
|
+
});
|
|
240
|
+
|
|
241
|
+
core.globalPlugins.push(plugin);
|
|
242
|
+
|
|
243
|
+
exports["default"] = plugin;
|
|
244
|
+
|
|
245
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
246
|
+
|
|
247
|
+
return exports;
|
|
248
|
+
|
|
249
|
+
})({}, FullCalendar, FullCalendar.DayGrid.Internal, FullCalendar.Internal, FullCalendar.Preact);
|
|
@@ -0,0 +1,6 @@
|
|
|
1
|
+
/*!
|
|
2
|
+
FullCalendar Multi-Month Plugin v6.1.0
|
|
3
|
+
Docs & License: https://fullcalendar.io/docs/multimonth-view
|
|
4
|
+
(c) 2022 Adam Shaw
|
|
5
|
+
*/
|
|
6
|
+
FullCalendar.MultiMonth=function(t,e,l,i,n){"use strict";class o extends i.DateComponent{constructor(){super(...arguments),this.buildDayTableModel=i.memoize(l.buildDayTableModel),this.slicer=new l.DayTableSlicer,this.state={labelId:i.getUniqueDomId()}}render(){const{props:t,state:e,context:o}=this,{dateProfile:a,forPrint:r}=t,{options:s}=o,c=this.buildDayTableModel(a,o.dateProfileGenerator),d=this.slicer.sliceProps(t,a,s.nextDayThreshold,o,c),m=null!=t.tableWidth?t.tableWidth/s.aspectRatio:null,h=c.cells.length,u=null!=m?m/h:null;return n.createElement("div",{ref:t.elRef,"data-date":t.isoDateStr,className:"fc-multimonth-month",style:{width:t.width},role:"grid","aria-labelledby":e.labelId},n.createElement("div",{className:"fc-multimonth-header",style:{marginBottom:u},role:"presentation"},n.createElement("div",{className:"fc-multimonth-title",id:e.labelId},o.dateEnv.format(t.dateProfile.currentRange.start,t.titleFormat)),n.createElement("table",{className:["fc-multimonth-header-table",o.theme.getClass("table")].join(" "),role:"presentation"},n.createElement("thead",{role:"rowgroup"},n.createElement(i.DayHeader,{dateProfile:t.dateProfile,dates:c.headerDates,datesRepDistinctDays:!1})))),n.createElement("div",{className:["fc-multimonth-daygrid","fc-daygrid","fc-daygrid-body",!r&&"fc-daygrid-body-balanced",r&&"fc-daygrid-body-unbalanced",r&&"fc-daygrid-body-natural"].join(" "),style:{marginTop:-u}},n.createElement("table",{className:["fc-multimonth-daygrid-table",o.theme.getClass("table")].join(" "),style:{height:r?"":m},role:"presentation"},n.createElement("tbody",{role:"rowgroup"},n.createElement(l.TableRows,Object.assign({},d,{dateProfile:a,cells:c.cells,eventSelection:t.eventSelection,dayMaxEvents:!r,dayMaxEventRows:!r,showWeekNumbers:s.weekNumbers,clientWidth:t.clientWidth,clientHeight:t.clientHeight,forPrint:r}))))))}}class a extends i.DateComponent{constructor(){super(...arguments),this.splitDateProfileByMonth=i.memoize(s),this.buildMonthFormat=i.memoize(m),this.scrollElRef=n.createRef(),this.firstMonthElRef=n.createRef(),this.needsScrollReset=!1,this.handleSizing=t=>{t&&this.updateSize()}}render(){const{context:t,props:e,state:l}=this,{options:a}=t,{clientWidth:r,clientHeight:s}=l,c=l.monthHPadding||0,d=Math.min(null!=r?Math.floor(r/(a.multiMonthMinWidth+c)):1,a.multiMonthMaxColumns)||1,m=100/d+"%",h=null==r?null:r/d-c,u=null!=r&&1===d,f=this.splitDateProfileByMonth(t.dateProfileGenerator,e.dateProfile,t.dateEnv,!u&&a.fixedWeekCount,a.showNonCurrentDates),g=this.buildMonthFormat(a.multiMonthTitleFormat,f),p=["fc-multimonth",u?"fc-multimonth-singlecol":"fc-multimonth-multicol",null!=h&&h<400?"fc-multimonth-compact":""];return n.createElement(i.ViewContainer,{elRef:this.scrollElRef,elClasses:p,viewSpec:t.viewSpec},f.map((t,l)=>{const a=i.formatIsoMonthStr(t.currentRange.start);return n.createElement(o,Object.assign({},e,{key:a,isoDateStr:a,elRef:0===l?this.firstMonthElRef:void 0,titleFormat:g,dateProfile:t,width:m,tableWidth:h,clientWidth:r,clientHeight:s}))}))}componentDidMount(){this.updateSize(),this.context.addResizeHandler(this.handleSizing),this.requestScrollReset()}componentDidUpdate(t){i.isPropsEqual(t,this.props)||this.handleSizing(!1),t.dateProfile!==this.props.dateProfile?this.requestScrollReset():this.flushScrollReset()}componentWillUnmount(){this.context.removeResizeHandler(this.handleSizing)}updateSize(){const t=this.scrollElRef.current,e=this.firstMonthElRef.current;t&&this.setState({clientWidth:t.clientWidth,clientHeight:t.clientHeight}),e&&t&&null==this.state.monthHPadding&&this.setState({monthHPadding:t.clientWidth-e.firstChild.offsetWidth})}requestScrollReset(){this.needsScrollReset=!0,this.flushScrollReset()}flushScrollReset(){if(this.needsScrollReset&&null!=this.state.monthHPadding){const{currentDate:t}=this.props.dateProfile,e=this.scrollElRef.current,l=e.querySelector(`[data-date="${i.formatIsoMonthStr(t)}"]`);e.scrollTop=l.getBoundingClientRect().top-this.firstMonthElRef.current.getBoundingClientRect().top,this.needsScrollReset=!1}}shouldComponentUpdate(){return!0}}const r=i.createDuration(1,"month");function s(t,e,n,o,a){const{start:s,end:c}=e.currentRange;let d=s;const m=[];for(;d.valueOf()<c.valueOf();){const s=n.add(d,r),c={start:t.skipHiddenDays(d),end:t.skipHiddenDays(s,-1,!0)};let h=l.buildDayTableRenderRange({currentRange:c,snapToWeek:!0,fixedWeekCount:o,dateEnv:n});h={start:t.skipHiddenDays(h.start),end:t.skipHiddenDays(h.end,-1,!0)};const u=e.activeRange?i.intersectRanges(e.activeRange,a?h:c):null;m.push({currentDate:e.currentDate,isValid:e.isValid,validRange:e.validRange,renderRange:h,activeRange:u,currentRange:c,currentRangeUnit:"month",isRangeAllDay:!0,dateIncrement:e.dateIncrement,slotMinTime:e.slotMaxTime,slotMaxTime:e.slotMinTime}),d=s}return m}const c=i.createFormatter({year:"numeric",month:"long"}),d=i.createFormatter({month:"long"});function m(t,e){return t||(e[0].currentRange.start.getUTCFullYear()!==e[e.length-1].currentRange.start.getUTCFullYear()?c:d)}const h={multiMonthTitleFormat:i.createFormatter,multiMonthMaxColumns:Number,multiMonthMinWidth:Number};i.injectStyles(".fc .fc-multimonth{border:1px solid var(--fc-border-color);display:flex;flex-wrap:wrap;overflow-x:hidden;overflow-y:auto}.fc .fc-multimonth-title{font-size:1.2em;font-weight:700;padding:1em 0;text-align:center}.fc .fc-multimonth-daygrid{background:var(--fc-page-bg-color)}.fc .fc-multimonth-daygrid-table,.fc .fc-multimonth-header-table{table-layout:fixed;width:100%}.fc .fc-multimonth-daygrid-table{border-top-style:hidden!important}.fc .fc-multimonth-singlecol .fc-multimonth{position:relative}.fc .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:relative;top:0;z-index:2}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid{position:relative;z-index:1}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid-table,.fc .fc-multimonth-singlecol .fc-multimonth-header-table{border-left-style:hidden;border-right-style:hidden}.fc .fc-multimonth-singlecol .fc-multimonth-month:last-child .fc-multimonth-daygrid-table{border-bottom-style:hidden}.fc .fc-multimonth-multicol{line-height:1}.fc .fc-multimonth-multicol .fc-multimonth-month{padding:0 1.2em 1.2em}.fc .fc-multimonth-multicol .fc-daygrid-more-link{border:1px solid var(--fc-event-border-color);display:block;float:none;padding:1px}.fc .fc-multimonth-compact{line-height:1}.fc .fc-multimonth-compact .fc-multimonth-daygrid-table,.fc .fc-multimonth-compact .fc-multimonth-header-table{font-size:.9em}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{position:sticky}.fc-media-print .fc-multimonth{overflow:visible}");var u=e.createPlugin({name:"@fullcalendar/multimonth",initialView:"multiMonthYear",optionRefiners:h,views:{multiMonth:{component:a,dateProfileGeneratorClass:l.TableDateProfileGenerator,multiMonthMinWidth:350,multiMonthMaxColumns:3},multiMonthYear:{type:"multiMonth",duration:{years:1},fixedWeekCount:!0,showNonCurrentDates:!1}}});return e.globalPlugins.push(u),t.default=u,Object.defineProperty(t,"__esModule",{value:!0}),t}({},FullCalendar,FullCalendar.DayGrid.Internal,FullCalendar.Internal,FullCalendar.Preact);
|
package/index.js
ADDED
|
@@ -0,0 +1,242 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
Object.defineProperty(exports, '__esModule', { value: true });
|
|
4
|
+
|
|
5
|
+
var core = require('@fullcalendar/core');
|
|
6
|
+
var internal$1 = require('@fullcalendar/daygrid/internal');
|
|
7
|
+
var internal = require('@fullcalendar/core/internal');
|
|
8
|
+
var preact = require('@fullcalendar/core/preact');
|
|
9
|
+
|
|
10
|
+
class SingleMonth extends internal.DateComponent {
|
|
11
|
+
constructor() {
|
|
12
|
+
super(...arguments);
|
|
13
|
+
this.buildDayTableModel = internal.memoize(internal$1.buildDayTableModel);
|
|
14
|
+
this.slicer = new internal$1.DayTableSlicer();
|
|
15
|
+
this.state = {
|
|
16
|
+
labelId: internal.getUniqueDomId(),
|
|
17
|
+
};
|
|
18
|
+
}
|
|
19
|
+
render() {
|
|
20
|
+
const { props, state, context } = this;
|
|
21
|
+
const { dateProfile, forPrint } = props;
|
|
22
|
+
const { options } = context;
|
|
23
|
+
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
|
24
|
+
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
|
25
|
+
// ensure single-month has aspect ratio
|
|
26
|
+
const tableHeight = props.tableWidth != null ? props.tableWidth / options.aspectRatio : null;
|
|
27
|
+
const rowCnt = dayTableModel.cells.length;
|
|
28
|
+
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
|
29
|
+
return (preact.createElement("div", { ref: props.elRef, "data-date": props.isoDateStr, className: "fc-multimonth-month", style: { width: props.width }, role: "grid", "aria-labelledby": state.labelId },
|
|
30
|
+
preact.createElement("div", { className: "fc-multimonth-header", style: { marginBottom: rowHeight }, role: "presentation" },
|
|
31
|
+
preact.createElement("div", { className: "fc-multimonth-title", id: state.labelId }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
|
32
|
+
preact.createElement("table", { className: [
|
|
33
|
+
'fc-multimonth-header-table',
|
|
34
|
+
context.theme.getClass('table'),
|
|
35
|
+
].join(' '), role: "presentation" },
|
|
36
|
+
preact.createElement("thead", { role: "rowgroup" },
|
|
37
|
+
preact.createElement(internal.DayHeader, { dateProfile: props.dateProfile, dates: dayTableModel.headerDates, datesRepDistinctDays: false })))),
|
|
38
|
+
preact.createElement("div", { className: [
|
|
39
|
+
'fc-multimonth-daygrid',
|
|
40
|
+
'fc-daygrid',
|
|
41
|
+
'fc-daygrid-body',
|
|
42
|
+
!forPrint && 'fc-daygrid-body-balanced',
|
|
43
|
+
forPrint && 'fc-daygrid-body-unbalanced',
|
|
44
|
+
forPrint && 'fc-daygrid-body-natural',
|
|
45
|
+
].join(' '), style: { marginTop: -rowHeight } },
|
|
46
|
+
preact.createElement("table", { className: [
|
|
47
|
+
'fc-multimonth-daygrid-table',
|
|
48
|
+
context.theme.getClass('table'),
|
|
49
|
+
].join(' '), style: { height: forPrint ? '' : tableHeight }, role: "presentation" },
|
|
50
|
+
preact.createElement("tbody", { role: "rowgroup" },
|
|
51
|
+
preact.createElement(internal$1.TableRows, Object.assign({}, slicedProps, { dateProfile: dateProfile, cells: dayTableModel.cells, eventSelection: props.eventSelection, dayMaxEvents: !forPrint, dayMaxEventRows: !forPrint, showWeekNumbers: options.weekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: forPrint })))))));
|
|
52
|
+
}
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
class MultiMonthView extends internal.DateComponent {
|
|
56
|
+
constructor() {
|
|
57
|
+
super(...arguments);
|
|
58
|
+
this.splitDateProfileByMonth = internal.memoize(splitDateProfileByMonth);
|
|
59
|
+
this.buildMonthFormat = internal.memoize(buildMonthFormat);
|
|
60
|
+
this.scrollElRef = preact.createRef();
|
|
61
|
+
this.firstMonthElRef = preact.createRef();
|
|
62
|
+
this.needsScrollReset = false;
|
|
63
|
+
this.handleSizing = (isForced) => {
|
|
64
|
+
if (isForced) {
|
|
65
|
+
this.updateSize();
|
|
66
|
+
}
|
|
67
|
+
};
|
|
68
|
+
}
|
|
69
|
+
render() {
|
|
70
|
+
const { context, props, state } = this;
|
|
71
|
+
const { options } = context;
|
|
72
|
+
const { clientWidth, clientHeight } = state;
|
|
73
|
+
const monthHPadding = state.monthHPadding || 0;
|
|
74
|
+
const colCount = Math.min(clientWidth != null ?
|
|
75
|
+
Math.floor(clientWidth / (options.multiMonthMinWidth + monthHPadding)) :
|
|
76
|
+
1, options.multiMonthMaxColumns) || 1;
|
|
77
|
+
const monthWidthPct = (100 / colCount) + '%';
|
|
78
|
+
const monthTableWidth = clientWidth == null ? null :
|
|
79
|
+
(clientWidth / colCount) - monthHPadding;
|
|
80
|
+
const isLegitSingleCol = clientWidth != null && colCount === 1;
|
|
81
|
+
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, isLegitSingleCol ? false : options.fixedWeekCount, options.showNonCurrentDates);
|
|
82
|
+
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
|
83
|
+
const rootClassNames = [
|
|
84
|
+
'fc-multimonth',
|
|
85
|
+
isLegitSingleCol ?
|
|
86
|
+
'fc-multimonth-singlecol' :
|
|
87
|
+
'fc-multimonth-multicol',
|
|
88
|
+
(monthTableWidth != null && monthTableWidth < 400) ?
|
|
89
|
+
'fc-multimonth-compact' :
|
|
90
|
+
'',
|
|
91
|
+
];
|
|
92
|
+
return (preact.createElement(internal.ViewContainer, { elRef: this.scrollElRef, elClasses: rootClassNames, viewSpec: context.viewSpec }, monthDateProfiles.map((monthDateProfile, i) => {
|
|
93
|
+
const monthStr = internal.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
|
94
|
+
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, isoDateStr: monthStr, elRef: i === 0 ? this.firstMonthElRef : undefined, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidthPct, tableWidth: monthTableWidth, clientWidth: clientWidth, clientHeight: clientHeight })));
|
|
95
|
+
})));
|
|
96
|
+
}
|
|
97
|
+
componentDidMount() {
|
|
98
|
+
this.updateSize();
|
|
99
|
+
this.context.addResizeHandler(this.handleSizing);
|
|
100
|
+
this.requestScrollReset();
|
|
101
|
+
}
|
|
102
|
+
componentDidUpdate(prevProps) {
|
|
103
|
+
if (!internal.isPropsEqual(prevProps, this.props)) { // an external change?
|
|
104
|
+
this.handleSizing(false);
|
|
105
|
+
}
|
|
106
|
+
if (prevProps.dateProfile !== this.props.dateProfile) {
|
|
107
|
+
this.requestScrollReset();
|
|
108
|
+
}
|
|
109
|
+
else {
|
|
110
|
+
this.flushScrollReset();
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
componentWillUnmount() {
|
|
114
|
+
this.context.removeResizeHandler(this.handleSizing);
|
|
115
|
+
}
|
|
116
|
+
updateSize() {
|
|
117
|
+
const scrollEl = this.scrollElRef.current;
|
|
118
|
+
const firstMonthEl = this.firstMonthElRef.current;
|
|
119
|
+
if (scrollEl) {
|
|
120
|
+
this.setState({
|
|
121
|
+
clientWidth: scrollEl.clientWidth,
|
|
122
|
+
clientHeight: scrollEl.clientHeight,
|
|
123
|
+
});
|
|
124
|
+
}
|
|
125
|
+
if (firstMonthEl && scrollEl) {
|
|
126
|
+
if (this.state.monthHPadding == null) { // always remember initial non-zero value
|
|
127
|
+
this.setState({
|
|
128
|
+
monthHPadding: scrollEl.clientWidth - // go within padding
|
|
129
|
+
firstMonthEl.firstChild.offsetWidth,
|
|
130
|
+
});
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
requestScrollReset() {
|
|
135
|
+
this.needsScrollReset = true;
|
|
136
|
+
this.flushScrollReset();
|
|
137
|
+
}
|
|
138
|
+
flushScrollReset() {
|
|
139
|
+
if (this.needsScrollReset &&
|
|
140
|
+
this.state.monthHPadding != null // indicates sizing already happened
|
|
141
|
+
) {
|
|
142
|
+
const { currentDate } = this.props.dateProfile;
|
|
143
|
+
const scrollEl = this.scrollElRef.current;
|
|
144
|
+
const monthEl = scrollEl.querySelector(`[data-date="${internal.formatIsoMonthStr(currentDate)}"]`);
|
|
145
|
+
scrollEl.scrollTop = monthEl.getBoundingClientRect().top -
|
|
146
|
+
this.firstMonthElRef.current.getBoundingClientRect().top;
|
|
147
|
+
this.needsScrollReset = false;
|
|
148
|
+
}
|
|
149
|
+
}
|
|
150
|
+
// workaround for when queued setState render (w/ clientWidth) gets cancelled because
|
|
151
|
+
// subsequent update and shouldComponentUpdate says not to render :(
|
|
152
|
+
shouldComponentUpdate() {
|
|
153
|
+
return true;
|
|
154
|
+
}
|
|
155
|
+
}
|
|
156
|
+
// date profile
|
|
157
|
+
// -------------------------------------------------------------------------------------------------
|
|
158
|
+
const oneMonthDuration = internal.createDuration(1, 'month');
|
|
159
|
+
function splitDateProfileByMonth(dateProfileGenerator, dateProfile, dateEnv, fixedWeekCount, showNonCurrentDates) {
|
|
160
|
+
const { start, end } = dateProfile.currentRange;
|
|
161
|
+
let monthStart = start;
|
|
162
|
+
const monthDateProfiles = [];
|
|
163
|
+
while (monthStart.valueOf() < end.valueOf()) {
|
|
164
|
+
const monthEnd = dateEnv.add(monthStart, oneMonthDuration);
|
|
165
|
+
const currentRange = {
|
|
166
|
+
// yuck
|
|
167
|
+
start: dateProfileGenerator.skipHiddenDays(monthStart),
|
|
168
|
+
end: dateProfileGenerator.skipHiddenDays(monthEnd, -1, true),
|
|
169
|
+
};
|
|
170
|
+
let renderRange = internal$1.buildDayTableRenderRange({
|
|
171
|
+
currentRange,
|
|
172
|
+
snapToWeek: true,
|
|
173
|
+
fixedWeekCount,
|
|
174
|
+
dateEnv,
|
|
175
|
+
});
|
|
176
|
+
renderRange = {
|
|
177
|
+
// yuck
|
|
178
|
+
start: dateProfileGenerator.skipHiddenDays(renderRange.start),
|
|
179
|
+
end: dateProfileGenerator.skipHiddenDays(renderRange.end, -1, true),
|
|
180
|
+
};
|
|
181
|
+
const activeRange = dateProfile.activeRange ?
|
|
182
|
+
internal.intersectRanges(dateProfile.activeRange, showNonCurrentDates ? renderRange : currentRange) :
|
|
183
|
+
null;
|
|
184
|
+
monthDateProfiles.push({
|
|
185
|
+
currentDate: dateProfile.currentDate,
|
|
186
|
+
isValid: dateProfile.isValid,
|
|
187
|
+
validRange: dateProfile.validRange,
|
|
188
|
+
renderRange,
|
|
189
|
+
activeRange,
|
|
190
|
+
currentRange,
|
|
191
|
+
currentRangeUnit: 'month',
|
|
192
|
+
isRangeAllDay: true,
|
|
193
|
+
dateIncrement: dateProfile.dateIncrement,
|
|
194
|
+
slotMinTime: dateProfile.slotMaxTime,
|
|
195
|
+
slotMaxTime: dateProfile.slotMinTime,
|
|
196
|
+
});
|
|
197
|
+
monthStart = monthEnd;
|
|
198
|
+
}
|
|
199
|
+
return monthDateProfiles;
|
|
200
|
+
}
|
|
201
|
+
// date formatting
|
|
202
|
+
// -------------------------------------------------------------------------------------------------
|
|
203
|
+
const YEAR_MONTH_FORMATTER = internal.createFormatter({ year: 'numeric', month: 'long' });
|
|
204
|
+
const YEAR_FORMATTER = internal.createFormatter({ month: 'long' });
|
|
205
|
+
function buildMonthFormat(formatOverride, monthDateProfiles) {
|
|
206
|
+
return formatOverride ||
|
|
207
|
+
((monthDateProfiles[0].currentRange.start.getUTCFullYear() !==
|
|
208
|
+
monthDateProfiles[monthDateProfiles.length - 1].currentRange.start.getUTCFullYear())
|
|
209
|
+
? YEAR_MONTH_FORMATTER
|
|
210
|
+
: YEAR_FORMATTER);
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
const OPTION_REFINERS = {
|
|
214
|
+
multiMonthTitleFormat: internal.createFormatter,
|
|
215
|
+
multiMonthMaxColumns: Number,
|
|
216
|
+
multiMonthMinWidth: Number,
|
|
217
|
+
};
|
|
218
|
+
|
|
219
|
+
var css_248z = ".fc .fc-multimonth{border:1px solid var(--fc-border-color);display:flex;flex-wrap:wrap;overflow-x:hidden;overflow-y:auto}.fc .fc-multimonth-title{font-size:1.2em;font-weight:700;padding:1em 0;text-align:center}.fc .fc-multimonth-daygrid{background:var(--fc-page-bg-color)}.fc .fc-multimonth-daygrid-table,.fc .fc-multimonth-header-table{table-layout:fixed;width:100%}.fc .fc-multimonth-daygrid-table{border-top-style:hidden!important}.fc .fc-multimonth-singlecol .fc-multimonth{position:relative}.fc .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:relative;top:0;z-index:2}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid{position:relative;z-index:1}.fc .fc-multimonth-singlecol .fc-multimonth-daygrid-table,.fc .fc-multimonth-singlecol .fc-multimonth-header-table{border-left-style:hidden;border-right-style:hidden}.fc .fc-multimonth-singlecol .fc-multimonth-month:last-child .fc-multimonth-daygrid-table{border-bottom-style:hidden}.fc .fc-multimonth-multicol{line-height:1}.fc .fc-multimonth-multicol .fc-multimonth-month{padding:0 1.2em 1.2em}.fc .fc-multimonth-multicol .fc-daygrid-more-link{border:1px solid var(--fc-event-border-color);display:block;float:none;padding:1px}.fc .fc-multimonth-compact{line-height:1}.fc .fc-multimonth-compact .fc-multimonth-daygrid-table,.fc .fc-multimonth-compact .fc-multimonth-header-table{font-size:.9em}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{position:sticky}.fc-media-print .fc-multimonth{overflow:visible}";
|
|
220
|
+
internal.injectStyles(css_248z);
|
|
221
|
+
|
|
222
|
+
var index = core.createPlugin({
|
|
223
|
+
name: '@fullcalendar/multimonth',
|
|
224
|
+
initialView: 'multiMonthYear',
|
|
225
|
+
optionRefiners: OPTION_REFINERS,
|
|
226
|
+
views: {
|
|
227
|
+
multiMonth: {
|
|
228
|
+
component: MultiMonthView,
|
|
229
|
+
dateProfileGeneratorClass: internal$1.TableDateProfileGenerator,
|
|
230
|
+
multiMonthMinWidth: 350,
|
|
231
|
+
multiMonthMaxColumns: 3,
|
|
232
|
+
},
|
|
233
|
+
multiMonthYear: {
|
|
234
|
+
type: 'multiMonth',
|
|
235
|
+
duration: { years: 1 },
|
|
236
|
+
fixedWeekCount: true,
|
|
237
|
+
showNonCurrentDates: false,
|
|
238
|
+
},
|
|
239
|
+
},
|
|
240
|
+
});
|
|
241
|
+
|
|
242
|
+
exports["default"] = index;
|
package/package.json
ADDED
|
@@ -0,0 +1,53 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@fullcalendar/multimonth",
|
|
3
|
+
"version": "6.1.0",
|
|
4
|
+
"title": "FullCalendar Multi-Month Plugin",
|
|
5
|
+
"description": "Display a sequence or grid of multiple months",
|
|
6
|
+
"keywords": [
|
|
7
|
+
"calendar",
|
|
8
|
+
"event",
|
|
9
|
+
"full-sized",
|
|
10
|
+
"fullcalendar",
|
|
11
|
+
"month"
|
|
12
|
+
],
|
|
13
|
+
"homepage": "https://fullcalendar.io/docs/multimonth-view",
|
|
14
|
+
"dependencies": {
|
|
15
|
+
"@fullcalendar/daygrid": "~6.1.0"
|
|
16
|
+
},
|
|
17
|
+
"peerDependencies": {
|
|
18
|
+
"@fullcalendar/core": "~6.1.0"
|
|
19
|
+
},
|
|
20
|
+
"type": "module",
|
|
21
|
+
"bugs": "https://fullcalendar.io/reporting-bugs",
|
|
22
|
+
"repository": {
|
|
23
|
+
"type": "git",
|
|
24
|
+
"url": "https://github.com/fullcalendar/fullcalendar.git",
|
|
25
|
+
"directory": "packages/multimonth"
|
|
26
|
+
},
|
|
27
|
+
"license": "MIT",
|
|
28
|
+
"author": {
|
|
29
|
+
"name": "Adam Shaw",
|
|
30
|
+
"email": "arshaw@arshaw.com",
|
|
31
|
+
"url": "http://arshaw.com/"
|
|
32
|
+
},
|
|
33
|
+
"copyright": "2022 Adam Shaw",
|
|
34
|
+
"types": "./index.d.ts",
|
|
35
|
+
"main": "./index.js",
|
|
36
|
+
"module": "./index.esm.js",
|
|
37
|
+
"unpkg": "./index.global.min.js",
|
|
38
|
+
"jsdelivr": "./index.global.min.js",
|
|
39
|
+
"exports": {
|
|
40
|
+
"./package.json": "./package.json",
|
|
41
|
+
".": {
|
|
42
|
+
"types": "./index.d.ts",
|
|
43
|
+
"require": "./index.js",
|
|
44
|
+
"import": "./index.esm.js"
|
|
45
|
+
},
|
|
46
|
+
"./internal": {
|
|
47
|
+
"types": "./internal.d.ts",
|
|
48
|
+
"require": "./internal.js",
|
|
49
|
+
"import": "./internal.esm.js"
|
|
50
|
+
}
|
|
51
|
+
},
|
|
52
|
+
"sideEffects": false
|
|
53
|
+
}
|