@fullcalendar/multimonth 7.0.0-beta.1 → 7.0.0-beta.4
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/index.cjs +79 -84
- package/index.global.js +80 -85
- package/index.global.min.js +2 -2
- package/index.js +81 -86
- package/package.json +3 -3
package/index.cjs
CHANGED
@@ -10,10 +10,13 @@ var preact_cjs = require('@fullcalendar/core/preact.cjs');
|
|
10
10
|
class SingleMonth extends internal_cjs.DateComponent {
|
11
11
|
constructor() {
|
12
12
|
super(...arguments);
|
13
|
-
this.slicer = new internal_cjs$1.DayTableSlicer();
|
14
13
|
// memo
|
15
14
|
this.buildDayTableModel = internal_cjs.memoize(internal_cjs$1.buildDayTableModel);
|
16
15
|
this.createDayHeaderFormatter = internal_cjs.memoize(internal_cjs$1.createDayHeaderFormatter);
|
16
|
+
this.buildDateRowConfig = internal_cjs.memoize(internal_cjs$1.buildDateRowConfig);
|
17
|
+
// internal
|
18
|
+
this.slicer = new internal_cjs$1.DayTableSlicer();
|
19
|
+
this.titleId = internal_cjs.getUniqueDomId();
|
17
20
|
}
|
18
21
|
render() {
|
19
22
|
const { props, context } = this;
|
@@ -21,27 +24,30 @@ class SingleMonth extends internal_cjs.DateComponent {
|
|
21
24
|
const { options } = context;
|
22
25
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
23
26
|
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
24
|
-
|
25
|
-
const tableHeight = typeof props.width === 'number'
|
26
|
-
? props.width / options.aspectRatio
|
27
|
-
: null;
|
28
|
-
const rowCnt = dayTableModel.cellRows.length;
|
29
|
-
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
30
|
-
const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, false, // datesRepDistinctDays
|
27
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
31
28
|
dayTableModel.colCnt);
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
29
|
+
const rowConfig = this.buildDateRowConfig(dayTableModel.headerDates, false, // datesRepDistinctDays
|
30
|
+
dateProfile, props.todayRange, dayHeaderFormat, context);
|
31
|
+
const invAspectRatio = 1 / options.aspectRatio;
|
32
|
+
const invRowAspectRatio = invAspectRatio / dayTableModel.rowCnt;
|
33
|
+
const isHeaderSticky = !forPrint;
|
34
|
+
const isAspectRatio = !forPrint || props.hasLateralSiblings;
|
35
|
+
return (preact_cjs.createElement("div", { role: 'listitem', className: 'fc-multimonth-month-outer', style: { width: props.width } },
|
36
|
+
preact_cjs.createElement("div", { role: 'grid', "aria-labelledby": this.titleId, "data-date": props.isoDateStr, className: internal_cjs.joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid') },
|
37
|
+
preact_cjs.createElement("div", { className: "fc-multimonth-header", style: {
|
38
|
+
marginBottom: isHeaderSticky ? internal_cjs.fracToCssDim(invRowAspectRatio) : undefined,
|
39
|
+
} },
|
40
|
+
preact_cjs.createElement("div", { id: this.titleId, className: 'fc-multimonth-title' }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
41
|
+
preact_cjs.createElement(internal_cjs$1.DayGridHeaderRow, Object.assign({}, rowConfig, { role: 'row', className: 'fc-multimonth-header-row' }))),
|
42
|
+
preact_cjs.createElement("div", { className: internal_cjs.joinClassNames('fc-multimonth-body', isAspectRatio && 'fc-rel'), style: {
|
43
|
+
marginTop: isHeaderSticky ? internal_cjs.fracToCssDim(-invRowAspectRatio) : undefined,
|
44
|
+
paddingBottom: isAspectRatio ? internal_cjs.fracToCssDim(invAspectRatio) : undefined,
|
45
|
+
} },
|
46
|
+
preact_cjs.createElement(internal_cjs$1.DayGridRows, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, className: isAspectRatio ? 'fc-fill' : '', forPrint: forPrint && !props.hasLateralSiblings, dayMaxEvents: forPrint ? undefined : options.dayMaxEvents, dayMaxEventRows: (forPrint && props.hasLateralSiblings) ? 1 : options.dayMaxEventRows,
|
47
|
+
// content
|
48
|
+
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
|
49
|
+
// dimensions
|
50
|
+
visibleWidth: props.visibleWidth })))));
|
45
51
|
}
|
46
52
|
}
|
47
53
|
|
@@ -53,82 +59,57 @@ class MultiMonthView extends internal_cjs.DateComponent {
|
|
53
59
|
this.buildMonthFormat = internal_cjs.memoize(buildMonthFormat);
|
54
60
|
// ref
|
55
61
|
this.scrollerRef = preact_cjs.createRef();
|
56
|
-
this.innerElRef = preact_cjs.createRef();
|
57
|
-
// internal
|
62
|
+
this.innerElRef = preact_cjs.createRef(); // .fc-multimonth-inner
|
58
63
|
this.scrollDate = null;
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
let { xGap, xPadding } = this.state;
|
63
|
-
// for first time, assume 2 columns and query gap/padding
|
64
|
-
if (xGap == null) {
|
65
|
-
const innerEl = this.innerElRef.current;
|
66
|
-
const children = innerEl.childNodes;
|
67
|
-
if (children.length > 1) {
|
68
|
-
const box0 = children[0].getBoundingClientRect();
|
69
|
-
const box1 = children[1].getBoundingClientRect();
|
70
|
-
let xSpan;
|
71
|
-
[xGap, xSpan] = [
|
72
|
-
Math.abs(box0.left - box1.right),
|
73
|
-
Math.abs(box0.right - box1.left),
|
74
|
-
].sort(internal_cjs.compareNumbers);
|
75
|
-
xPadding = width - xSpan;
|
76
|
-
}
|
77
|
-
}
|
78
|
-
this.setState({ width, xGap, xPadding });
|
79
|
-
};
|
80
|
-
this.updateScroll = () => {
|
81
|
-
if (this.scrollDate != null && this.state.width != null) {
|
82
|
-
const scroller = this.scrollerRef.current;
|
83
|
-
const innerEl = this.innerElRef.current;
|
84
|
-
const monthEl = innerEl.querySelector(`[data-date="${internal_cjs.formatIsoMonthStr(this.scrollDate)}"]`);
|
85
|
-
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
86
|
-
monthEl.getBoundingClientRect().top -
|
87
|
-
innerEl.getBoundingClientRect().top);
|
88
|
-
scroller.scrollTo({ y: scrollTop });
|
64
|
+
this.handleScrollEnd = ({ isUser }) => {
|
65
|
+
if (isUser) {
|
66
|
+
this.scrollDate = null;
|
89
67
|
}
|
90
68
|
};
|
91
|
-
this.clearScroll = () => {
|
92
|
-
this.scrollDate = null;
|
93
|
-
};
|
94
69
|
}
|
95
70
|
render() {
|
96
71
|
const { context, props, state } = this;
|
97
72
|
const { options } = context;
|
98
73
|
const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
|
99
|
-
const colCount = state.width == null
|
100
|
-
? 2
|
101
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.width - state.xPadding + state.xGap) /
|
102
|
-
(options.multiMonthMinWidth + state.xGap)));
|
103
|
-
const monthWidth = state.width == null
|
104
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
105
|
-
: Math.floor(// exact values can cause expansion to other rows
|
106
|
-
(state.width - state.xPadding - (state.xGap * (colCount - 1))) /
|
107
|
-
colCount);
|
108
74
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
109
75
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
110
|
-
const
|
111
|
-
|
112
|
-
|
76
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
77
|
+
const { innerWidth } = state;
|
78
|
+
let cols;
|
79
|
+
let computedMonthWidth;
|
80
|
+
let cssMonthWidth;
|
81
|
+
let hasLateralSiblings = false;
|
82
|
+
if (innerWidth != null) {
|
83
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
84
|
+
if (props.forPrint) {
|
85
|
+
cols = Math.min(cols, 2);
|
86
|
+
}
|
87
|
+
computedMonthWidth = innerWidth / cols;
|
88
|
+
cssMonthWidth = internal_cjs.fracToCssDim(1 / cols);
|
89
|
+
hasLateralSiblings = cols > 1;
|
90
|
+
}
|
91
|
+
return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact_cjs.createElement(internal_cjs.ViewContainer, { viewSpec: context.viewSpec, className: internal_cjs.joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
113
92
|
'fc-multimonth-singlecol' :
|
114
|
-
'fc-multimonth-multicol',
|
115
|
-
'
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
verticalScrolling ? 'fc-liquid' : '',
|
121
|
-
], ref: this.scrollerRef, widthRef: this.handleWidth },
|
122
|
-
preact_cjs.createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile, i) => {
|
93
|
+
'fc-multimonth-multicol',
|
94
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
95
|
+
// https://stackoverflow.com/a/60256345
|
96
|
+
!props.forPrint && 'fc-flex-col') },
|
97
|
+
preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
98
|
+
preact_cjs.createElement("div", { role: 'list', "aria-labelledby": props.labelId, "aria-label": props.labelStr, className: 'fc-multimonth-inner', ref: this.innerElRef }, monthDateProfiles.map((monthDateProfile) => {
|
123
99
|
const monthStr = internal_cjs.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
124
|
-
return (preact_cjs.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width:
|
100
|
+
return (preact_cjs.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
125
101
|
})))))));
|
126
102
|
}
|
127
103
|
// Lifecycle
|
128
104
|
// -----------------------------------------------------------------------------------------------
|
129
105
|
componentDidMount() {
|
130
106
|
this.resetScroll();
|
131
|
-
this.scrollerRef.current.addScrollEndListener(this.
|
107
|
+
this.scrollerRef.current.addScrollEndListener(this.handleScrollEnd);
|
108
|
+
this.disconnectInnerWidth = internal_cjs.watchWidth(this.innerElRef.current, (innerWidth) => {
|
109
|
+
internal_cjs.afterSize(() => {
|
110
|
+
this.setState({ innerWidth });
|
111
|
+
});
|
112
|
+
});
|
132
113
|
}
|
133
114
|
componentDidUpdate(prevProps) {
|
134
115
|
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
@@ -137,17 +118,31 @@ class MultiMonthView extends internal_cjs.DateComponent {
|
|
137
118
|
else {
|
138
119
|
// NOT optimal to update so often
|
139
120
|
// TODO: isolate dependencies of scroll coordinate
|
140
|
-
this.
|
121
|
+
this.applyScroll();
|
141
122
|
}
|
142
123
|
}
|
143
124
|
componentWillUnmount() {
|
144
|
-
this.scrollerRef.current.removeScrollEndListener(this.
|
125
|
+
this.scrollerRef.current.removeScrollEndListener(this.handleScrollEnd);
|
126
|
+
this.disconnectInnerWidth();
|
145
127
|
}
|
146
128
|
// Scrolling
|
147
129
|
// -----------------------------------------------------------------------------------------------
|
148
130
|
resetScroll() {
|
149
131
|
this.scrollDate = this.props.dateProfile.currentDate;
|
150
|
-
this.
|
132
|
+
this.applyScroll();
|
133
|
+
}
|
134
|
+
applyScroll() {
|
135
|
+
if (this.scrollDate != null &&
|
136
|
+
this.state.innerWidth != null // render completed?
|
137
|
+
) {
|
138
|
+
const scroller = this.scrollerRef.current;
|
139
|
+
const innerEl = this.innerElRef.current;
|
140
|
+
const monthEl = innerEl.querySelector(`[data-date="${internal_cjs.formatIsoMonthStr(this.scrollDate)}"]`);
|
141
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
142
|
+
monthEl.getBoundingClientRect().top -
|
143
|
+
innerEl.getBoundingClientRect().top);
|
144
|
+
scroller.scrollTo({ y: scrollTop });
|
145
|
+
}
|
151
146
|
}
|
152
147
|
}
|
153
148
|
// date profile
|
@@ -213,7 +208,7 @@ const OPTION_REFINERS = {
|
|
213
208
|
multiMonthMinWidth: Number,
|
214
209
|
};
|
215
210
|
|
216
|
-
var css_248z = ".fc-multimonth-inner{display:flex;flex-wrap:wrap}.fc-
|
211
|
+
var css_248z = ".fc-media-screen .fc-multimonth-inner{display:flex;flex-direction:row;flex-wrap:wrap}.fc-media-print.fc-direction-ltr .fc-multimonth-inner>*{float:left}.fc-media-print.fc-direction-rtl .fc-multimonth-inner>*{float:right}.fc-media-print .fc-multimonth-inner:after{clear:both;content:\"\";display:block}.fc-multimonth-multicol .fc-multimonth-month{padding:1.2em}.fc-multimonth-singlecol .fc-multimonth-title{padding:.5em 0}.fc-multimonth-multicol .fc-multimonth-title{padding-bottom:1em}.fc-multimonth-title{font-size:1.2em;font-weight:700;text-align:center}.fc-multimonth-header-row{border-top:1px solid var(--fc-border-color)}.fc-multimonth-body:not(.fc-multimonth-singlecol .fc-multimonth-month-outer:last-child .fc-multimonth-body),.fc-multimonth-header-row{border-bottom:1px solid var(--fc-border-color)}.fc-multimonth-multicol .fc-multimonth-body,.fc-multimonth-multicol .fc-multimonth-header-row{border-left:1px solid var(--fc-border-color);border-right:1px solid var(--fc-border-color);font-size:.9em;line-height:1}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:sticky;top:0;z-index:2}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-body{position:relative;z-index:1}";
|
217
212
|
internal_cjs.injectStyles(css_248z);
|
218
213
|
|
219
214
|
var index = index_cjs.createPlugin({
|
@@ -231,7 +226,7 @@ var index = index_cjs.createPlugin({
|
|
231
226
|
type: 'multiMonth',
|
232
227
|
duration: { years: 1 },
|
233
228
|
fixedWeekCount: true,
|
234
|
-
showNonCurrentDates: false,
|
229
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
235
230
|
},
|
236
231
|
},
|
237
232
|
});
|
package/index.global.js
CHANGED
@@ -1,5 +1,5 @@
|
|
1
1
|
/*!
|
2
|
-
FullCalendar Multi-Month Plugin v7.0.0-beta.
|
2
|
+
FullCalendar Multi-Month Plugin v7.0.0-beta.4
|
3
3
|
Docs & License: https://fullcalendar.io/docs/multimonth-grid
|
4
4
|
(c) 2024 Adam Shaw
|
5
5
|
*/
|
@@ -9,10 +9,13 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
9
9
|
class SingleMonth extends internal.DateComponent {
|
10
10
|
constructor() {
|
11
11
|
super(...arguments);
|
12
|
-
this.slicer = new internal$1.DayTableSlicer();
|
13
12
|
// memo
|
14
13
|
this.buildDayTableModel = internal.memoize(internal$1.buildDayTableModel);
|
15
14
|
this.createDayHeaderFormatter = internal.memoize(internal$1.createDayHeaderFormatter);
|
15
|
+
this.buildDateRowConfig = internal.memoize(internal$1.buildDateRowConfig);
|
16
|
+
// internal
|
17
|
+
this.slicer = new internal$1.DayTableSlicer();
|
18
|
+
this.titleId = internal.getUniqueDomId();
|
16
19
|
}
|
17
20
|
render() {
|
18
21
|
const { props, context } = this;
|
@@ -20,27 +23,30 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
20
23
|
const { options } = context;
|
21
24
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
22
25
|
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
23
|
-
|
24
|
-
const tableHeight = typeof props.width === 'number'
|
25
|
-
? props.width / options.aspectRatio
|
26
|
-
: null;
|
27
|
-
const rowCnt = dayTableModel.cellRows.length;
|
28
|
-
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
29
|
-
const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, false, // datesRepDistinctDays
|
26
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
30
27
|
dayTableModel.colCnt);
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
28
|
+
const rowConfig = this.buildDateRowConfig(dayTableModel.headerDates, false, // datesRepDistinctDays
|
29
|
+
dateProfile, props.todayRange, dayHeaderFormat, context);
|
30
|
+
const invAspectRatio = 1 / options.aspectRatio;
|
31
|
+
const invRowAspectRatio = invAspectRatio / dayTableModel.rowCnt;
|
32
|
+
const isHeaderSticky = !forPrint;
|
33
|
+
const isAspectRatio = !forPrint || props.hasLateralSiblings;
|
34
|
+
return (preact.createElement("div", { role: 'listitem', className: 'fc-multimonth-month-outer', style: { width: props.width } },
|
35
|
+
preact.createElement("div", { role: 'grid', "aria-labelledby": this.titleId, "data-date": props.isoDateStr, className: internal.joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid') },
|
36
|
+
preact.createElement("div", { className: "fc-multimonth-header", style: {
|
37
|
+
marginBottom: isHeaderSticky ? internal.fracToCssDim(invRowAspectRatio) : undefined,
|
38
|
+
} },
|
39
|
+
preact.createElement("div", { id: this.titleId, className: 'fc-multimonth-title' }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
40
|
+
preact.createElement(internal$1.DayGridHeaderRow, Object.assign({}, rowConfig, { role: 'row', className: 'fc-multimonth-header-row' }))),
|
41
|
+
preact.createElement("div", { className: internal.joinClassNames('fc-multimonth-body', isAspectRatio && 'fc-rel'), style: {
|
42
|
+
marginTop: isHeaderSticky ? internal.fracToCssDim(-invRowAspectRatio) : undefined,
|
43
|
+
paddingBottom: isAspectRatio ? internal.fracToCssDim(invAspectRatio) : undefined,
|
44
|
+
} },
|
45
|
+
preact.createElement(internal$1.DayGridRows, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, className: isAspectRatio ? 'fc-fill' : '', forPrint: forPrint && !props.hasLateralSiblings, dayMaxEvents: forPrint ? undefined : options.dayMaxEvents, dayMaxEventRows: (forPrint && props.hasLateralSiblings) ? 1 : options.dayMaxEventRows,
|
46
|
+
// content
|
47
|
+
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
|
48
|
+
// dimensions
|
49
|
+
visibleWidth: props.visibleWidth })))));
|
44
50
|
}
|
45
51
|
}
|
46
52
|
|
@@ -52,82 +58,57 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
52
58
|
this.buildMonthFormat = internal.memoize(buildMonthFormat);
|
53
59
|
// ref
|
54
60
|
this.scrollerRef = preact.createRef();
|
55
|
-
this.innerElRef = preact.createRef();
|
56
|
-
// internal
|
61
|
+
this.innerElRef = preact.createRef(); // .fc-multimonth-inner
|
57
62
|
this.scrollDate = null;
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
let { xGap, xPadding } = this.state;
|
62
|
-
// for first time, assume 2 columns and query gap/padding
|
63
|
-
if (xGap == null) {
|
64
|
-
const innerEl = this.innerElRef.current;
|
65
|
-
const children = innerEl.childNodes;
|
66
|
-
if (children.length > 1) {
|
67
|
-
const box0 = children[0].getBoundingClientRect();
|
68
|
-
const box1 = children[1].getBoundingClientRect();
|
69
|
-
let xSpan;
|
70
|
-
[xGap, xSpan] = [
|
71
|
-
Math.abs(box0.left - box1.right),
|
72
|
-
Math.abs(box0.right - box1.left),
|
73
|
-
].sort(internal.compareNumbers);
|
74
|
-
xPadding = width - xSpan;
|
75
|
-
}
|
76
|
-
}
|
77
|
-
this.setState({ width, xGap, xPadding });
|
78
|
-
};
|
79
|
-
this.updateScroll = () => {
|
80
|
-
if (this.scrollDate != null && this.state.width != null) {
|
81
|
-
const scroller = this.scrollerRef.current;
|
82
|
-
const innerEl = this.innerElRef.current;
|
83
|
-
const monthEl = innerEl.querySelector(`[data-date="${internal.formatIsoMonthStr(this.scrollDate)}"]`);
|
84
|
-
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
85
|
-
monthEl.getBoundingClientRect().top -
|
86
|
-
innerEl.getBoundingClientRect().top);
|
87
|
-
scroller.scrollTo({ y: scrollTop });
|
63
|
+
this.handleScrollEnd = ({ isUser }) => {
|
64
|
+
if (isUser) {
|
65
|
+
this.scrollDate = null;
|
88
66
|
}
|
89
67
|
};
|
90
|
-
this.clearScroll = () => {
|
91
|
-
this.scrollDate = null;
|
92
|
-
};
|
93
68
|
}
|
94
69
|
render() {
|
95
70
|
const { context, props, state } = this;
|
96
71
|
const { options } = context;
|
97
72
|
const verticalScrolling = !props.forPrint && !internal.getIsHeightAuto(options);
|
98
|
-
const colCount = state.width == null
|
99
|
-
? 2
|
100
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.width - state.xPadding + state.xGap) /
|
101
|
-
(options.multiMonthMinWidth + state.xGap)));
|
102
|
-
const monthWidth = state.width == null
|
103
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
104
|
-
: Math.floor(// exact values can cause expansion to other rows
|
105
|
-
(state.width - state.xPadding - (state.xGap * (colCount - 1))) /
|
106
|
-
colCount);
|
107
73
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
108
74
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
109
|
-
const
|
110
|
-
|
111
|
-
|
75
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
76
|
+
const { innerWidth } = state;
|
77
|
+
let cols;
|
78
|
+
let computedMonthWidth;
|
79
|
+
let cssMonthWidth;
|
80
|
+
let hasLateralSiblings = false;
|
81
|
+
if (innerWidth != null) {
|
82
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
83
|
+
if (props.forPrint) {
|
84
|
+
cols = Math.min(cols, 2);
|
85
|
+
}
|
86
|
+
computedMonthWidth = innerWidth / cols;
|
87
|
+
cssMonthWidth = internal.fracToCssDim(1 / cols);
|
88
|
+
hasLateralSiblings = cols > 1;
|
89
|
+
}
|
90
|
+
return (preact.createElement(internal.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact.createElement(internal.ViewContainer, { viewSpec: context.viewSpec, className: internal.joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
112
91
|
'fc-multimonth-singlecol' :
|
113
|
-
'fc-multimonth-multicol',
|
114
|
-
'
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
verticalScrolling ? 'fc-liquid' : '',
|
120
|
-
], ref: this.scrollerRef, widthRef: this.handleWidth },
|
121
|
-
preact.createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile, i) => {
|
92
|
+
'fc-multimonth-multicol',
|
93
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
94
|
+
// https://stackoverflow.com/a/60256345
|
95
|
+
!props.forPrint && 'fc-flex-col') },
|
96
|
+
preact.createElement(internal.Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
97
|
+
preact.createElement("div", { role: 'list', "aria-labelledby": props.labelId, "aria-label": props.labelStr, className: 'fc-multimonth-inner', ref: this.innerElRef }, monthDateProfiles.map((monthDateProfile) => {
|
122
98
|
const monthStr = internal.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
123
|
-
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width:
|
99
|
+
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
124
100
|
})))))));
|
125
101
|
}
|
126
102
|
// Lifecycle
|
127
103
|
// -----------------------------------------------------------------------------------------------
|
128
104
|
componentDidMount() {
|
129
105
|
this.resetScroll();
|
130
|
-
this.scrollerRef.current.addScrollEndListener(this.
|
106
|
+
this.scrollerRef.current.addScrollEndListener(this.handleScrollEnd);
|
107
|
+
this.disconnectInnerWidth = internal.watchWidth(this.innerElRef.current, (innerWidth) => {
|
108
|
+
internal.afterSize(() => {
|
109
|
+
this.setState({ innerWidth });
|
110
|
+
});
|
111
|
+
});
|
131
112
|
}
|
132
113
|
componentDidUpdate(prevProps) {
|
133
114
|
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
@@ -136,17 +117,31 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
136
117
|
else {
|
137
118
|
// NOT optimal to update so often
|
138
119
|
// TODO: isolate dependencies of scroll coordinate
|
139
|
-
this.
|
120
|
+
this.applyScroll();
|
140
121
|
}
|
141
122
|
}
|
142
123
|
componentWillUnmount() {
|
143
|
-
this.scrollerRef.current.removeScrollEndListener(this.
|
124
|
+
this.scrollerRef.current.removeScrollEndListener(this.handleScrollEnd);
|
125
|
+
this.disconnectInnerWidth();
|
144
126
|
}
|
145
127
|
// Scrolling
|
146
128
|
// -----------------------------------------------------------------------------------------------
|
147
129
|
resetScroll() {
|
148
130
|
this.scrollDate = this.props.dateProfile.currentDate;
|
149
|
-
this.
|
131
|
+
this.applyScroll();
|
132
|
+
}
|
133
|
+
applyScroll() {
|
134
|
+
if (this.scrollDate != null &&
|
135
|
+
this.state.innerWidth != null // render completed?
|
136
|
+
) {
|
137
|
+
const scroller = this.scrollerRef.current;
|
138
|
+
const innerEl = this.innerElRef.current;
|
139
|
+
const monthEl = innerEl.querySelector(`[data-date="${internal.formatIsoMonthStr(this.scrollDate)}"]`);
|
140
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
141
|
+
monthEl.getBoundingClientRect().top -
|
142
|
+
innerEl.getBoundingClientRect().top);
|
143
|
+
scroller.scrollTo({ y: scrollTop });
|
144
|
+
}
|
150
145
|
}
|
151
146
|
}
|
152
147
|
// date profile
|
@@ -212,7 +207,7 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
212
207
|
multiMonthMinWidth: Number,
|
213
208
|
};
|
214
209
|
|
215
|
-
var css_248z = ".fc-multimonth-inner{display:flex;flex-wrap:wrap}.fc-
|
210
|
+
var css_248z = ".fc-media-screen .fc-multimonth-inner{display:flex;flex-direction:row;flex-wrap:wrap}.fc-media-print.fc-direction-ltr .fc-multimonth-inner>*{float:left}.fc-media-print.fc-direction-rtl .fc-multimonth-inner>*{float:right}.fc-media-print .fc-multimonth-inner:after{clear:both;content:\"\";display:block}.fc-multimonth-multicol .fc-multimonth-month{padding:1.2em}.fc-multimonth-singlecol .fc-multimonth-title{padding:.5em 0}.fc-multimonth-multicol .fc-multimonth-title{padding-bottom:1em}.fc-multimonth-title{font-size:1.2em;font-weight:700;text-align:center}.fc-multimonth-header-row{border-top:1px solid var(--fc-border-color)}.fc-multimonth-body:not(.fc-multimonth-singlecol .fc-multimonth-month-outer:last-child .fc-multimonth-body),.fc-multimonth-header-row{border-bottom:1px solid var(--fc-border-color)}.fc-multimonth-multicol .fc-multimonth-body,.fc-multimonth-multicol .fc-multimonth-header-row{border-left:1px solid var(--fc-border-color);border-right:1px solid var(--fc-border-color);font-size:.9em;line-height:1}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:sticky;top:0;z-index:2}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-body{position:relative;z-index:1}";
|
216
211
|
internal.injectStyles(css_248z);
|
217
212
|
|
218
213
|
var plugin = core.createPlugin({
|
@@ -230,7 +225,7 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
230
225
|
type: 'multiMonth',
|
231
226
|
duration: { years: 1 },
|
232
227
|
fixedWeekCount: true,
|
233
|
-
showNonCurrentDates: false,
|
228
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
234
229
|
},
|
235
230
|
},
|
236
231
|
});
|
package/index.global.min.js
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
/*!
|
2
|
-
FullCalendar Multi-Month Plugin v7.0.0-beta.
|
2
|
+
FullCalendar Multi-Month Plugin v7.0.0-beta.4
|
3
3
|
Docs & License: https://fullcalendar.io/docs/multimonth-grid
|
4
4
|
(c) 2024 Adam Shaw
|
5
5
|
*/
|
6
|
-
FullCalendar.MultiMonth=function(e,t,
|
6
|
+
FullCalendar.MultiMonth=function(e,t,i,n,l){"use strict";class r extends n.DateComponent{constructor(){super(...arguments),this.buildDayTableModel=n.memoize(i.buildDayTableModel),this.createDayHeaderFormatter=n.memoize(i.createDayHeaderFormatter),this.buildDateRowConfig=n.memoize(i.buildDateRowConfig),this.slicer=new i.DayTableSlicer,this.titleId=n.getUniqueDomId()}render(){const{props:e,context:t}=this,{dateProfile:r,forPrint:o}=e,{options:a}=t,s=this.buildDayTableModel(r,t.dateProfileGenerator),c=this.slicer.sliceProps(e,r,a.nextDayThreshold,t,s),m=this.createDayHeaderFormatter(a.dayHeaderFormat,!1,s.colCnt),d=this.buildDateRowConfig(s.headerDates,!1,r,e.todayRange,m,t),h=1/a.aspectRatio,u=h/s.rowCnt,f=!o,g=!o||e.hasLateralSiblings;return l.createElement("div",{role:"listitem",className:"fc-multimonth-month-outer",style:{width:e.width}},l.createElement("div",{role:"grid","aria-labelledby":this.titleId,"data-date":e.isoDateStr,className:n.joinClassNames("fc-multimonth-month",e.hasLateralSiblings&&"fc-break-inside-avoid")},l.createElement("div",{className:"fc-multimonth-header",style:{marginBottom:f?n.fracToCssDim(u):void 0}},l.createElement("div",{id:this.titleId,className:"fc-multimonth-title"},t.dateEnv.format(e.dateProfile.currentRange.start,e.titleFormat)),l.createElement(i.DayGridHeaderRow,Object.assign({},d,{role:"row",className:"fc-multimonth-header-row"}))),l.createElement("div",{className:n.joinClassNames("fc-multimonth-body",g&&"fc-rel"),style:{marginTop:f?n.fracToCssDim(-u):void 0,paddingBottom:g?n.fracToCssDim(h):void 0}},l.createElement(i.DayGridRows,{dateProfile:e.dateProfile,todayRange:e.todayRange,cellRows:s.cellRows,className:g?"fc-fill":"",forPrint:o&&!e.hasLateralSiblings,dayMaxEvents:o?void 0:a.dayMaxEvents,dayMaxEventRows:o&&e.hasLateralSiblings?1:a.dayMaxEventRows,fgEventSegs:c.fgEventSegs,bgEventSegs:c.bgEventSegs,businessHourSegs:c.businessHourSegs,dateSelectionSegs:c.dateSelectionSegs,eventDrag:c.eventDrag,eventResize:c.eventResize,eventSelection:c.eventSelection,visibleWidth:e.visibleWidth}))))}}class o extends n.DateComponent{constructor(){super(...arguments),this.splitDateProfileByMonth=n.memoize(s),this.buildMonthFormat=n.memoize(d),this.scrollerRef=l.createRef(),this.innerElRef=l.createRef(),this.scrollDate=null,this.handleScrollEnd=({isUser:e})=>{e&&(this.scrollDate=null)}}render(){const{context:e,props:t,state:i}=this,{options:o}=e,a=!t.forPrint&&!n.getIsHeightAuto(o),s=this.splitDateProfileByMonth(e.dateProfileGenerator,t.dateProfile,e.dateEnv,o.fixedWeekCount,o.showNonCurrentDates),c=this.buildMonthFormat(o.multiMonthTitleFormat,s),{multiMonthMinWidth:m,multiMonthMaxColumns:d}=o,{innerWidth:h}=i;let u,f,g,p=!1;return null!=h&&(u=Math.max(1,Math.min(d,Math.floor(h/m))),t.forPrint&&(u=Math.min(u,2)),f=h/u,g=n.fracToCssDim(1/u),p=u>1),l.createElement(n.NowTimer,{unit:"day"},(i,o)=>l.createElement(n.ViewContainer,{viewSpec:e.viewSpec,className:n.joinClassNames("fc-multimonth fc-border",1===u?"fc-multimonth-singlecol":"fc-multimonth-multicol",!t.forPrint&&"fc-flex-col")},l.createElement(n.Scroller,{vertical:a,className:a?"fc-liquid":"",ref:this.scrollerRef},l.createElement("div",{role:"list","aria-labelledby":t.labelId,"aria-label":t.labelStr,className:"fc-multimonth-inner",ref:this.innerElRef},s.map(e=>{const i=n.formatIsoMonthStr(e.currentRange.start);return l.createElement(r,Object.assign({},t,{key:i,todayRange:o,isoDateStr:i,titleFormat:c,dateProfile:e,width:g,visibleWidth:f,hasLateralSiblings:p}))})))))}componentDidMount(){this.resetScroll(),this.scrollerRef.current.addScrollEndListener(this.handleScrollEnd),this.disconnectInnerWidth=n.watchWidth(this.innerElRef.current,e=>{n.afterSize(()=>{this.setState({innerWidth:e})})})}componentDidUpdate(e){e.dateProfile!==this.props.dateProfile&&this.context.options.scrollTimeReset?this.resetScroll():this.applyScroll()}componentWillUnmount(){this.scrollerRef.current.removeScrollEndListener(this.handleScrollEnd),this.disconnectInnerWidth()}resetScroll(){this.scrollDate=this.props.dateProfile.currentDate,this.applyScroll()}applyScroll(){if(null!=this.scrollDate&&null!=this.state.innerWidth){const e=this.scrollerRef.current,t=this.innerElRef.current,i=t.querySelector(`[data-date="${n.formatIsoMonthStr(this.scrollDate)}"]`),l=Math.ceil(i.getBoundingClientRect().top-t.getBoundingClientRect().top);e.scrollTo({y:l})}}}const a=n.createDuration(1,"month");function s(e,t,l,r,o){const{start:s,end:c}=t.currentRange;let m=s;const d=[];for(;m.valueOf()<c.valueOf();){const s=l.add(m,a),c={start:e.skipHiddenDays(m),end:e.skipHiddenDays(s,-1,!0)};let h=i.buildDayTableRenderRange({currentRange:c,snapToWeek:!0,fixedWeekCount:r,dateEnv:l});h={start:e.skipHiddenDays(h.start),end:e.skipHiddenDays(h.end,-1,!0)};const u=t.activeRange?n.intersectRanges(t.activeRange,o?h:c):null;d.push({currentDate:t.currentDate,isValid:t.isValid,validRange:t.validRange,renderRange:h,activeRange:u,currentRange:c,currentRangeUnit:"month",isRangeAllDay:!0,dateIncrement:t.dateIncrement,slotMinTime:t.slotMaxTime,slotMaxTime:t.slotMinTime}),m=s}return d}const c=n.createFormatter({year:"numeric",month:"long"}),m=n.createFormatter({month:"long"});function d(e,t){return e||(t[0].currentRange.start.getUTCFullYear()!==t[t.length-1].currentRange.start.getUTCFullYear()?c:m)}const h={multiMonthTitleFormat:n.createFormatter,multiMonthMaxColumns:Number,multiMonthMinWidth:Number};n.injectStyles('.fc-media-screen .fc-multimonth-inner{display:flex;flex-direction:row;flex-wrap:wrap}.fc-media-print.fc-direction-ltr .fc-multimonth-inner>*{float:left}.fc-media-print.fc-direction-rtl .fc-multimonth-inner>*{float:right}.fc-media-print .fc-multimonth-inner:after{clear:both;content:"";display:block}.fc-multimonth-multicol .fc-multimonth-month{padding:1.2em}.fc-multimonth-singlecol .fc-multimonth-title{padding:.5em 0}.fc-multimonth-multicol .fc-multimonth-title{padding-bottom:1em}.fc-multimonth-title{font-size:1.2em;font-weight:700;text-align:center}.fc-multimonth-header-row{border-top:1px solid var(--fc-border-color)}.fc-multimonth-body:not(.fc-multimonth-singlecol .fc-multimonth-month-outer:last-child .fc-multimonth-body),.fc-multimonth-header-row{border-bottom:1px solid var(--fc-border-color)}.fc-multimonth-multicol .fc-multimonth-body,.fc-multimonth-multicol .fc-multimonth-header-row{border-left:1px solid var(--fc-border-color);border-right:1px solid var(--fc-border-color);font-size:.9em;line-height:1}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:sticky;top:0;z-index:2}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-body{position:relative;z-index:1}');var u=t.createPlugin({name:"@fullcalendar/multimonth",initialView:"multiMonthYear",optionRefiners:h,views:{multiMonth:{component:o,dateProfileGeneratorClass:i.TableDateProfileGenerator,multiMonthMinWidth:350,multiMonthMaxColumns:3},multiMonthYear:{type:"multiMonth",duration:{years:1},fixedWeekCount:!0,showNonCurrentDates:!1}}});return t.globalPlugins.push(u),e.default=u,Object.defineProperty(e,"__esModule",{value:!0}),e}({},FullCalendar,FullCalendar.DayGrid.Internal,FullCalendar.Internal,FullCalendar.Preact);
|
package/index.js
CHANGED
@@ -1,15 +1,18 @@
|
|
1
1
|
import { createPlugin } from '@fullcalendar/core/index.js';
|
2
|
-
import {
|
3
|
-
import { DateComponent, memoize,
|
2
|
+
import { buildDayTableModel, createDayHeaderFormatter, buildDateRowConfig, DayTableSlicer, DayGridHeaderRow, DayGridRows, buildDayTableRenderRange, TableDateProfileGenerator } from '@fullcalendar/daygrid/internal.js';
|
3
|
+
import { DateComponent, memoize, getUniqueDomId, joinClassNames, fracToCssDim, getIsHeightAuto, NowTimer, ViewContainer, Scroller, formatIsoMonthStr, watchWidth, afterSize, createDuration, intersectRanges, createFormatter, injectStyles } from '@fullcalendar/core/internal.js';
|
4
4
|
import { createElement, createRef } from '@fullcalendar/core/preact.js';
|
5
5
|
|
6
6
|
class SingleMonth extends DateComponent {
|
7
7
|
constructor() {
|
8
8
|
super(...arguments);
|
9
|
-
this.slicer = new DayTableSlicer();
|
10
9
|
// memo
|
11
10
|
this.buildDayTableModel = memoize(buildDayTableModel);
|
12
11
|
this.createDayHeaderFormatter = memoize(createDayHeaderFormatter);
|
12
|
+
this.buildDateRowConfig = memoize(buildDateRowConfig);
|
13
|
+
// internal
|
14
|
+
this.slicer = new DayTableSlicer();
|
15
|
+
this.titleId = getUniqueDomId();
|
13
16
|
}
|
14
17
|
render() {
|
15
18
|
const { props, context } = this;
|
@@ -17,27 +20,30 @@ class SingleMonth extends DateComponent {
|
|
17
20
|
const { options } = context;
|
18
21
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
19
22
|
const slicedProps = this.slicer.sliceProps(props, dateProfile, options.nextDayThreshold, context, dayTableModel);
|
20
|
-
|
21
|
-
const tableHeight = typeof props.width === 'number'
|
22
|
-
? props.width / options.aspectRatio
|
23
|
-
: null;
|
24
|
-
const rowCnt = dayTableModel.cellRows.length;
|
25
|
-
const rowHeight = tableHeight != null ? tableHeight / rowCnt : null;
|
26
|
-
const dayHeaderFormat = this.createDayHeaderFormatter(context.options.dayHeaderFormat, false, // datesRepDistinctDays
|
23
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
27
24
|
dayTableModel.colCnt);
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
25
|
+
const rowConfig = this.buildDateRowConfig(dayTableModel.headerDates, false, // datesRepDistinctDays
|
26
|
+
dateProfile, props.todayRange, dayHeaderFormat, context);
|
27
|
+
const invAspectRatio = 1 / options.aspectRatio;
|
28
|
+
const invRowAspectRatio = invAspectRatio / dayTableModel.rowCnt;
|
29
|
+
const isHeaderSticky = !forPrint;
|
30
|
+
const isAspectRatio = !forPrint || props.hasLateralSiblings;
|
31
|
+
return (createElement("div", { role: 'listitem', className: 'fc-multimonth-month-outer', style: { width: props.width } },
|
32
|
+
createElement("div", { role: 'grid', "aria-labelledby": this.titleId, "data-date": props.isoDateStr, className: joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid') },
|
33
|
+
createElement("div", { className: "fc-multimonth-header", style: {
|
34
|
+
marginBottom: isHeaderSticky ? fracToCssDim(invRowAspectRatio) : undefined,
|
35
|
+
} },
|
36
|
+
createElement("div", { id: this.titleId, className: 'fc-multimonth-title' }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
37
|
+
createElement(DayGridHeaderRow, Object.assign({}, rowConfig, { role: 'row', className: 'fc-multimonth-header-row' }))),
|
38
|
+
createElement("div", { className: joinClassNames('fc-multimonth-body', isAspectRatio && 'fc-rel'), style: {
|
39
|
+
marginTop: isHeaderSticky ? fracToCssDim(-invRowAspectRatio) : undefined,
|
40
|
+
paddingBottom: isAspectRatio ? fracToCssDim(invAspectRatio) : undefined,
|
41
|
+
} },
|
42
|
+
createElement(DayGridRows, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, className: isAspectRatio ? 'fc-fill' : '', forPrint: forPrint && !props.hasLateralSiblings, dayMaxEvents: forPrint ? undefined : options.dayMaxEvents, dayMaxEventRows: (forPrint && props.hasLateralSiblings) ? 1 : options.dayMaxEventRows,
|
43
|
+
// content
|
44
|
+
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection,
|
45
|
+
// dimensions
|
46
|
+
visibleWidth: props.visibleWidth })))));
|
41
47
|
}
|
42
48
|
}
|
43
49
|
|
@@ -49,82 +55,57 @@ class MultiMonthView extends DateComponent {
|
|
49
55
|
this.buildMonthFormat = memoize(buildMonthFormat);
|
50
56
|
// ref
|
51
57
|
this.scrollerRef = createRef();
|
52
|
-
this.innerElRef = createRef();
|
53
|
-
// internal
|
58
|
+
this.innerElRef = createRef(); // .fc-multimonth-inner
|
54
59
|
this.scrollDate = null;
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
let { xGap, xPadding } = this.state;
|
59
|
-
// for first time, assume 2 columns and query gap/padding
|
60
|
-
if (xGap == null) {
|
61
|
-
const innerEl = this.innerElRef.current;
|
62
|
-
const children = innerEl.childNodes;
|
63
|
-
if (children.length > 1) {
|
64
|
-
const box0 = children[0].getBoundingClientRect();
|
65
|
-
const box1 = children[1].getBoundingClientRect();
|
66
|
-
let xSpan;
|
67
|
-
[xGap, xSpan] = [
|
68
|
-
Math.abs(box0.left - box1.right),
|
69
|
-
Math.abs(box0.right - box1.left),
|
70
|
-
].sort(compareNumbers);
|
71
|
-
xPadding = width - xSpan;
|
72
|
-
}
|
73
|
-
}
|
74
|
-
this.setState({ width, xGap, xPadding });
|
75
|
-
};
|
76
|
-
this.updateScroll = () => {
|
77
|
-
if (this.scrollDate != null && this.state.width != null) {
|
78
|
-
const scroller = this.scrollerRef.current;
|
79
|
-
const innerEl = this.innerElRef.current;
|
80
|
-
const monthEl = innerEl.querySelector(`[data-date="${formatIsoMonthStr(this.scrollDate)}"]`);
|
81
|
-
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
82
|
-
monthEl.getBoundingClientRect().top -
|
83
|
-
innerEl.getBoundingClientRect().top);
|
84
|
-
scroller.scrollTo({ y: scrollTop });
|
60
|
+
this.handleScrollEnd = ({ isUser }) => {
|
61
|
+
if (isUser) {
|
62
|
+
this.scrollDate = null;
|
85
63
|
}
|
86
64
|
};
|
87
|
-
this.clearScroll = () => {
|
88
|
-
this.scrollDate = null;
|
89
|
-
};
|
90
65
|
}
|
91
66
|
render() {
|
92
67
|
const { context, props, state } = this;
|
93
68
|
const { options } = context;
|
94
69
|
const verticalScrolling = !props.forPrint && !getIsHeightAuto(options);
|
95
|
-
const colCount = state.width == null
|
96
|
-
? 2
|
97
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.width - state.xPadding + state.xGap) /
|
98
|
-
(options.multiMonthMinWidth + state.xGap)));
|
99
|
-
const monthWidth = state.width == null
|
100
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
101
|
-
: Math.floor(// exact values can cause expansion to other rows
|
102
|
-
(state.width - state.xPadding - (state.xGap * (colCount - 1))) /
|
103
|
-
colCount);
|
104
70
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
105
71
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
106
|
-
const
|
107
|
-
|
108
|
-
|
72
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
73
|
+
const { innerWidth } = state;
|
74
|
+
let cols;
|
75
|
+
let computedMonthWidth;
|
76
|
+
let cssMonthWidth;
|
77
|
+
let hasLateralSiblings = false;
|
78
|
+
if (innerWidth != null) {
|
79
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
80
|
+
if (props.forPrint) {
|
81
|
+
cols = Math.min(cols, 2);
|
82
|
+
}
|
83
|
+
computedMonthWidth = innerWidth / cols;
|
84
|
+
cssMonthWidth = fracToCssDim(1 / cols);
|
85
|
+
hasLateralSiblings = cols > 1;
|
86
|
+
}
|
87
|
+
return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(ViewContainer, { viewSpec: context.viewSpec, className: joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
109
88
|
'fc-multimonth-singlecol' :
|
110
|
-
'fc-multimonth-multicol',
|
111
|
-
'
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
verticalScrolling ? 'fc-liquid' : '',
|
117
|
-
], ref: this.scrollerRef, widthRef: this.handleWidth },
|
118
|
-
createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile, i) => {
|
89
|
+
'fc-multimonth-multicol',
|
90
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
91
|
+
// https://stackoverflow.com/a/60256345
|
92
|
+
!props.forPrint && 'fc-flex-col') },
|
93
|
+
createElement(Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
94
|
+
createElement("div", { role: 'list', "aria-labelledby": props.labelId, "aria-label": props.labelStr, className: 'fc-multimonth-inner', ref: this.innerElRef }, monthDateProfiles.map((monthDateProfile) => {
|
119
95
|
const monthStr = formatIsoMonthStr(monthDateProfile.currentRange.start);
|
120
|
-
return (createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width:
|
96
|
+
return (createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
121
97
|
})))))));
|
122
98
|
}
|
123
99
|
// Lifecycle
|
124
100
|
// -----------------------------------------------------------------------------------------------
|
125
101
|
componentDidMount() {
|
126
102
|
this.resetScroll();
|
127
|
-
this.scrollerRef.current.addScrollEndListener(this.
|
103
|
+
this.scrollerRef.current.addScrollEndListener(this.handleScrollEnd);
|
104
|
+
this.disconnectInnerWidth = watchWidth(this.innerElRef.current, (innerWidth) => {
|
105
|
+
afterSize(() => {
|
106
|
+
this.setState({ innerWidth });
|
107
|
+
});
|
108
|
+
});
|
128
109
|
}
|
129
110
|
componentDidUpdate(prevProps) {
|
130
111
|
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
@@ -133,17 +114,31 @@ class MultiMonthView extends DateComponent {
|
|
133
114
|
else {
|
134
115
|
// NOT optimal to update so often
|
135
116
|
// TODO: isolate dependencies of scroll coordinate
|
136
|
-
this.
|
117
|
+
this.applyScroll();
|
137
118
|
}
|
138
119
|
}
|
139
120
|
componentWillUnmount() {
|
140
|
-
this.scrollerRef.current.removeScrollEndListener(this.
|
121
|
+
this.scrollerRef.current.removeScrollEndListener(this.handleScrollEnd);
|
122
|
+
this.disconnectInnerWidth();
|
141
123
|
}
|
142
124
|
// Scrolling
|
143
125
|
// -----------------------------------------------------------------------------------------------
|
144
126
|
resetScroll() {
|
145
127
|
this.scrollDate = this.props.dateProfile.currentDate;
|
146
|
-
this.
|
128
|
+
this.applyScroll();
|
129
|
+
}
|
130
|
+
applyScroll() {
|
131
|
+
if (this.scrollDate != null &&
|
132
|
+
this.state.innerWidth != null // render completed?
|
133
|
+
) {
|
134
|
+
const scroller = this.scrollerRef.current;
|
135
|
+
const innerEl = this.innerElRef.current;
|
136
|
+
const monthEl = innerEl.querySelector(`[data-date="${formatIsoMonthStr(this.scrollDate)}"]`);
|
137
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
138
|
+
monthEl.getBoundingClientRect().top -
|
139
|
+
innerEl.getBoundingClientRect().top);
|
140
|
+
scroller.scrollTo({ y: scrollTop });
|
141
|
+
}
|
147
142
|
}
|
148
143
|
}
|
149
144
|
// date profile
|
@@ -209,7 +204,7 @@ const OPTION_REFINERS = {
|
|
209
204
|
multiMonthMinWidth: Number,
|
210
205
|
};
|
211
206
|
|
212
|
-
var css_248z = ".fc-multimonth-inner{display:flex;flex-wrap:wrap}.fc-
|
207
|
+
var css_248z = ".fc-media-screen .fc-multimonth-inner{display:flex;flex-direction:row;flex-wrap:wrap}.fc-media-print.fc-direction-ltr .fc-multimonth-inner>*{float:left}.fc-media-print.fc-direction-rtl .fc-multimonth-inner>*{float:right}.fc-media-print .fc-multimonth-inner:after{clear:both;content:\"\";display:block}.fc-multimonth-multicol .fc-multimonth-month{padding:1.2em}.fc-multimonth-singlecol .fc-multimonth-title{padding:.5em 0}.fc-multimonth-multicol .fc-multimonth-title{padding-bottom:1em}.fc-multimonth-title{font-size:1.2em;font-weight:700;text-align:center}.fc-multimonth-header-row{border-top:1px solid var(--fc-border-color)}.fc-multimonth-body:not(.fc-multimonth-singlecol .fc-multimonth-month-outer:last-child .fc-multimonth-body),.fc-multimonth-header-row{border-bottom:1px solid var(--fc-border-color)}.fc-multimonth-multicol .fc-multimonth-body,.fc-multimonth-multicol .fc-multimonth-header-row{border-left:1px solid var(--fc-border-color);border-right:1px solid var(--fc-border-color);font-size:.9em;line-height:1}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-header{background:var(--fc-page-bg-color);position:sticky;top:0;z-index:2}.fc-media-screen .fc-multimonth-singlecol .fc-multimonth-body{position:relative;z-index:1}";
|
213
208
|
injectStyles(css_248z);
|
214
209
|
|
215
210
|
var index = createPlugin({
|
@@ -227,7 +222,7 @@ var index = createPlugin({
|
|
227
222
|
type: 'multiMonth',
|
228
223
|
duration: { years: 1 },
|
229
224
|
fixedWeekCount: true,
|
230
|
-
showNonCurrentDates: false,
|
225
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
231
226
|
},
|
232
227
|
},
|
233
228
|
});
|
package/package.json
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
{
|
2
2
|
"name": "@fullcalendar/multimonth",
|
3
|
-
"version": "7.0.0-beta.
|
3
|
+
"version": "7.0.0-beta.4",
|
4
4
|
"title": "FullCalendar Multi-Month Plugin",
|
5
5
|
"description": "Display a sequence or grid of multiple months",
|
6
6
|
"keywords": [
|
@@ -12,10 +12,10 @@
|
|
12
12
|
],
|
13
13
|
"homepage": "https://fullcalendar.io/docs/multimonth-grid",
|
14
14
|
"dependencies": {
|
15
|
-
"@fullcalendar/daygrid": "7.0.0-beta.
|
15
|
+
"@fullcalendar/daygrid": "7.0.0-beta.4"
|
16
16
|
},
|
17
17
|
"peerDependencies": {
|
18
|
-
"@fullcalendar/core": "7.0.0-beta.
|
18
|
+
"@fullcalendar/core": "7.0.0-beta.4"
|
19
19
|
},
|
20
20
|
"type": "module",
|
21
21
|
"bugs": "https://fullcalendar.io/reporting-bugs",
|