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