@fullcalendar/multimonth 7.0.0-beta.0 → 7.0.0-beta.3
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 +89 -85
- package/index.global.js +90 -86
- package/index.global.min.js +2 -2
- package/index.js +91 -87
- package/package.json +3 -3
package/index.cjs
CHANGED
@@ -10,10 +10,12 @@ 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();
|
17
19
|
}
|
18
20
|
render() {
|
19
21
|
const { props, context } = this;
|
@@ -21,27 +23,31 @@ class SingleMonth extends internal_cjs.DateComponent {
|
|
21
23
|
const { options } = context;
|
22
24
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
23
25
|
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
|
26
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
31
27
|
dayTableModel.colCnt);
|
32
|
-
|
33
|
-
|
34
|
-
|
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_cjs.createElement("div", { "data-date": props.isoDateStr, className: internal_cjs.joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid'),
|
35
|
+
// override fc-liquid's basis. fc-grow isn't sufficient because doesn't set min-width:0
|
36
|
+
style: { width: props.width } },
|
37
|
+
preact_cjs.createElement("div", { className: "fc-multimonth-header", style: {
|
38
|
+
marginBottom: isHeaderSticky ? internal_cjs.fracToCssDim(invRowAspectRatio) : undefined,
|
39
|
+
} },
|
35
40
|
preact_cjs.createElement("div", { className: "fc-multimonth-title" }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
36
|
-
preact_cjs.createElement(
|
37
|
-
preact_cjs.createElement("div", { className: 'fc-multimonth-body fc-
|
38
|
-
marginTop: -
|
39
|
-
|
41
|
+
preact_cjs.createElement(internal_cjs$1.DayGridHeaderRow, Object.assign({}, rowConfig, { 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,
|
40
45
|
} },
|
41
|
-
preact_cjs.createElement(internal_cjs$1.DayGridRows
|
42
|
-
, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint,
|
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,
|
43
47
|
// content
|
44
|
-
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection
|
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
|
|
@@ -52,91 +58,89 @@ class MultiMonthView extends internal_cjs.DateComponent {
|
|
52
58
|
this.splitDateProfileByMonth = internal_cjs.memoize(splitDateProfileByMonth);
|
53
59
|
this.buildMonthFormat = internal_cjs.memoize(buildMonthFormat);
|
54
60
|
// ref
|
55
|
-
this.
|
56
|
-
this.innerElRef = preact_cjs.createRef();
|
57
|
-
this.
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
+
this.scrollerRef = preact_cjs.createRef();
|
62
|
+
this.innerElRef = preact_cjs.createRef(); // .fc-multimonth-inner
|
63
|
+
this.scrollDate = null;
|
64
|
+
this.updateScroll = () => {
|
65
|
+
if (this.scrollDate != null &&
|
66
|
+
this.state.innerWidth != null // render completed?
|
67
|
+
) {
|
68
|
+
const scroller = this.scrollerRef.current;
|
61
69
|
const innerEl = this.innerElRef.current;
|
62
|
-
const
|
63
|
-
|
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(internal_cjs.compareNumbers);
|
71
|
-
xPadding = clientWidth - xSpan;
|
72
|
-
}
|
73
|
-
}
|
74
|
-
this.setState({ clientWidth, xGap, xPadding });
|
75
|
-
};
|
76
|
-
this.timeScrollResponder = new internal_cjs.ScrollResponder((_time) => {
|
77
|
-
// HACK to scroll to day
|
78
|
-
if (this.state.clientWidth != null) {
|
79
|
-
const { currentDate } = this.props.dateProfile;
|
80
|
-
const rootEl = this.rootElRef.current;
|
81
|
-
const innerEl = this.innerElRef.current;
|
82
|
-
const monthEl = innerEl.querySelector(`[data-date="${internal_cjs.formatIsoMonthStr(currentDate)}"]`);
|
83
|
-
rootEl.scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
70
|
+
const monthEl = innerEl.querySelector(`[data-date="${internal_cjs.formatIsoMonthStr(this.scrollDate)}"]`);
|
71
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
84
72
|
monthEl.getBoundingClientRect().top -
|
85
73
|
innerEl.getBoundingClientRect().top);
|
86
|
-
|
74
|
+
scroller.scrollTo({ y: scrollTop });
|
87
75
|
}
|
88
|
-
|
89
|
-
|
76
|
+
};
|
77
|
+
this.clearScroll = () => {
|
78
|
+
this.scrollDate = null;
|
79
|
+
};
|
90
80
|
}
|
91
81
|
render() {
|
92
82
|
const { context, props, state } = this;
|
93
83
|
const { options } = context;
|
94
|
-
const
|
95
|
-
? 2
|
96
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.clientWidth - state.xPadding + state.xGap) /
|
97
|
-
(options.multiMonthMinWidth + state.xGap)));
|
98
|
-
const monthWidth = state.clientWidth == null
|
99
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
100
|
-
: Math.floor(// exact values can cause expansion to other rows
|
101
|
-
(state.clientWidth - state.xPadding - (state.xGap * (colCount - 1))) /
|
102
|
-
colCount);
|
84
|
+
const verticalScrolling = !props.forPrint && !internal_cjs.getIsHeightAuto(options);
|
103
85
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
104
86
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
105
|
-
const
|
106
|
-
|
107
|
-
|
87
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
88
|
+
const { innerWidth } = state;
|
89
|
+
let cols;
|
90
|
+
let computedMonthWidth;
|
91
|
+
let cssMonthWidth;
|
92
|
+
let hasLateralSiblings = false;
|
93
|
+
if (innerWidth != null) {
|
94
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
95
|
+
if (props.forPrint) {
|
96
|
+
cols = Math.min(cols, 2);
|
97
|
+
}
|
98
|
+
computedMonthWidth = innerWidth / cols;
|
99
|
+
cssMonthWidth = internal_cjs.fracToCssDim(1 / cols);
|
100
|
+
hasLateralSiblings = cols > 1;
|
101
|
+
}
|
102
|
+
return (preact_cjs.createElement(internal_cjs.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact_cjs.createElement(internal_cjs.ViewContainer, { className: internal_cjs.joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
108
103
|
'fc-multimonth-singlecol' :
|
109
|
-
'fc-multimonth-multicol',
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
'fc-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
return (preact_cjs.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidth })));
|
119
|
-
}))))));
|
104
|
+
'fc-multimonth-multicol',
|
105
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
106
|
+
// https://stackoverflow.com/a/60256345
|
107
|
+
!props.forPrint && 'fc-flex-col'), viewSpec: context.viewSpec },
|
108
|
+
preact_cjs.createElement(internal_cjs.Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
109
|
+
preact_cjs.createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile) => {
|
110
|
+
const monthStr = internal_cjs.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
111
|
+
return (preact_cjs.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
112
|
+
})))))));
|
120
113
|
}
|
114
|
+
// Lifecycle
|
115
|
+
// -----------------------------------------------------------------------------------------------
|
121
116
|
componentDidMount() {
|
122
|
-
|
123
|
-
|
124
|
-
this.
|
125
|
-
|
126
|
-
|
117
|
+
this.resetScroll();
|
118
|
+
this.scrollerRef.current.addScrollEndListener(this.clearScroll);
|
119
|
+
this.disconnectInnerWidth = internal_cjs.watchWidth(this.innerElRef.current, (innerWidth) => {
|
120
|
+
internal_cjs.afterSize(() => {
|
121
|
+
this.setState({ innerWidth });
|
122
|
+
});
|
123
|
+
});
|
127
124
|
}
|
128
125
|
componentDidUpdate(prevProps) {
|
129
|
-
|
130
|
-
|
131
|
-
this.timeScrollResponder.handleScroll(options.scrollTime);
|
126
|
+
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
127
|
+
this.resetScroll();
|
132
128
|
}
|
133
129
|
else {
|
134
|
-
|
130
|
+
// NOT optimal to update so often
|
131
|
+
// TODO: isolate dependencies of scroll coordinate
|
132
|
+
this.updateScroll();
|
135
133
|
}
|
136
134
|
}
|
137
135
|
componentWillUnmount() {
|
138
|
-
this.
|
139
|
-
this.
|
136
|
+
this.scrollerRef.current.removeScrollEndListener(this.clearScroll);
|
137
|
+
this.disconnectInnerWidth();
|
138
|
+
}
|
139
|
+
// Scrolling
|
140
|
+
// -----------------------------------------------------------------------------------------------
|
141
|
+
resetScroll() {
|
142
|
+
this.scrollDate = this.props.dateProfile.currentDate;
|
143
|
+
this.updateScroll();
|
140
144
|
}
|
141
145
|
}
|
142
146
|
// date profile
|
@@ -202,7 +206,7 @@ const OPTION_REFINERS = {
|
|
202
206
|
multiMonthMinWidth: Number,
|
203
207
|
};
|
204
208
|
|
205
|
-
var css_248z = ".fc-
|
209
|
+
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: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}";
|
206
210
|
internal_cjs.injectStyles(css_248z);
|
207
211
|
|
208
212
|
var index = index_cjs.createPlugin({
|
@@ -220,7 +224,7 @@ var index = index_cjs.createPlugin({
|
|
220
224
|
type: 'multiMonth',
|
221
225
|
duration: { years: 1 },
|
222
226
|
fixedWeekCount: true,
|
223
|
-
showNonCurrentDates: false,
|
227
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
224
228
|
},
|
225
229
|
},
|
226
230
|
});
|
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.3
|
3
3
|
Docs & License: https://fullcalendar.io/docs/multimonth-grid
|
4
4
|
(c) 2024 Adam Shaw
|
5
5
|
*/
|
@@ -9,10 +9,12 @@ 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();
|
16
18
|
}
|
17
19
|
render() {
|
18
20
|
const { props, context } = this;
|
@@ -20,27 +22,31 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
20
22
|
const { options } = context;
|
21
23
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
22
24
|
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
|
25
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
30
26
|
dayTableModel.colCnt);
|
31
|
-
|
32
|
-
|
33
|
-
|
27
|
+
const rowConfig = this.buildDateRowConfig(dayTableModel.headerDates, false, // datesRepDistinctDays
|
28
|
+
dateProfile, props.todayRange, dayHeaderFormat, context);
|
29
|
+
const invAspectRatio = 1 / options.aspectRatio;
|
30
|
+
const invRowAspectRatio = invAspectRatio / dayTableModel.rowCnt;
|
31
|
+
const isHeaderSticky = !forPrint;
|
32
|
+
const isAspectRatio = !forPrint || props.hasLateralSiblings;
|
33
|
+
return (preact.createElement("div", { "data-date": props.isoDateStr, className: internal.joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid'),
|
34
|
+
// override fc-liquid's basis. fc-grow isn't sufficient because doesn't set min-width:0
|
35
|
+
style: { width: props.width } },
|
36
|
+
preact.createElement("div", { className: "fc-multimonth-header", style: {
|
37
|
+
marginBottom: isHeaderSticky ? internal.fracToCssDim(invRowAspectRatio) : undefined,
|
38
|
+
} },
|
34
39
|
preact.createElement("div", { className: "fc-multimonth-title" }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
35
|
-
preact.createElement(
|
36
|
-
preact.createElement("div", { className: 'fc-multimonth-body fc-
|
37
|
-
marginTop: -
|
38
|
-
|
40
|
+
preact.createElement(internal$1.DayGridHeaderRow, Object.assign({}, rowConfig, { 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,
|
39
44
|
} },
|
40
|
-
preact.createElement(internal$1.DayGridRows
|
41
|
-
, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint,
|
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,
|
42
46
|
// content
|
43
|
-
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection
|
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
|
|
@@ -51,91 +57,89 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
51
57
|
this.splitDateProfileByMonth = internal.memoize(splitDateProfileByMonth);
|
52
58
|
this.buildMonthFormat = internal.memoize(buildMonthFormat);
|
53
59
|
// ref
|
54
|
-
this.
|
55
|
-
this.innerElRef = preact.createRef();
|
56
|
-
this.
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
+
this.scrollerRef = preact.createRef();
|
61
|
+
this.innerElRef = preact.createRef(); // .fc-multimonth-inner
|
62
|
+
this.scrollDate = null;
|
63
|
+
this.updateScroll = () => {
|
64
|
+
if (this.scrollDate != null &&
|
65
|
+
this.state.innerWidth != null // render completed?
|
66
|
+
) {
|
67
|
+
const scroller = this.scrollerRef.current;
|
60
68
|
const innerEl = this.innerElRef.current;
|
61
|
-
const
|
62
|
-
|
63
|
-
const box0 = children[0].getBoundingClientRect();
|
64
|
-
const box1 = children[1].getBoundingClientRect();
|
65
|
-
let xSpan;
|
66
|
-
[xGap, xSpan] = [
|
67
|
-
Math.abs(box0.left - box1.right),
|
68
|
-
Math.abs(box0.right - box1.left),
|
69
|
-
].sort(internal.compareNumbers);
|
70
|
-
xPadding = clientWidth - xSpan;
|
71
|
-
}
|
72
|
-
}
|
73
|
-
this.setState({ clientWidth, xGap, xPadding });
|
74
|
-
};
|
75
|
-
this.timeScrollResponder = new internal.ScrollResponder((_time) => {
|
76
|
-
// HACK to scroll to day
|
77
|
-
if (this.state.clientWidth != null) {
|
78
|
-
const { currentDate } = this.props.dateProfile;
|
79
|
-
const rootEl = this.rootElRef.current;
|
80
|
-
const innerEl = this.innerElRef.current;
|
81
|
-
const monthEl = innerEl.querySelector(`[data-date="${internal.formatIsoMonthStr(currentDate)}"]`);
|
82
|
-
rootEl.scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
69
|
+
const monthEl = innerEl.querySelector(`[data-date="${internal.formatIsoMonthStr(this.scrollDate)}"]`);
|
70
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
83
71
|
monthEl.getBoundingClientRect().top -
|
84
72
|
innerEl.getBoundingClientRect().top);
|
85
|
-
|
73
|
+
scroller.scrollTo({ y: scrollTop });
|
86
74
|
}
|
87
|
-
|
88
|
-
|
75
|
+
};
|
76
|
+
this.clearScroll = () => {
|
77
|
+
this.scrollDate = null;
|
78
|
+
};
|
89
79
|
}
|
90
80
|
render() {
|
91
81
|
const { context, props, state } = this;
|
92
82
|
const { options } = context;
|
93
|
-
const
|
94
|
-
? 2
|
95
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.clientWidth - state.xPadding + state.xGap) /
|
96
|
-
(options.multiMonthMinWidth + state.xGap)));
|
97
|
-
const monthWidth = state.clientWidth == null
|
98
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
99
|
-
: Math.floor(// exact values can cause expansion to other rows
|
100
|
-
(state.clientWidth - state.xPadding - (state.xGap * (colCount - 1))) /
|
101
|
-
colCount);
|
83
|
+
const verticalScrolling = !props.forPrint && !internal.getIsHeightAuto(options);
|
102
84
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
103
85
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
104
|
-
const
|
105
|
-
|
106
|
-
|
86
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
87
|
+
const { innerWidth } = state;
|
88
|
+
let cols;
|
89
|
+
let computedMonthWidth;
|
90
|
+
let cssMonthWidth;
|
91
|
+
let hasLateralSiblings = false;
|
92
|
+
if (innerWidth != null) {
|
93
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
94
|
+
if (props.forPrint) {
|
95
|
+
cols = Math.min(cols, 2);
|
96
|
+
}
|
97
|
+
computedMonthWidth = innerWidth / cols;
|
98
|
+
cssMonthWidth = internal.fracToCssDim(1 / cols);
|
99
|
+
hasLateralSiblings = cols > 1;
|
100
|
+
}
|
101
|
+
return (preact.createElement(internal.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact.createElement(internal.ViewContainer, { className: internal.joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
107
102
|
'fc-multimonth-singlecol' :
|
108
|
-
'fc-multimonth-multicol',
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
'fc-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidth })));
|
118
|
-
}))))));
|
103
|
+
'fc-multimonth-multicol',
|
104
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
105
|
+
// https://stackoverflow.com/a/60256345
|
106
|
+
!props.forPrint && 'fc-flex-col'), viewSpec: context.viewSpec },
|
107
|
+
preact.createElement(internal.Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
108
|
+
preact.createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile) => {
|
109
|
+
const monthStr = internal.formatIsoMonthStr(monthDateProfile.currentRange.start);
|
110
|
+
return (preact.createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
111
|
+
})))))));
|
119
112
|
}
|
113
|
+
// Lifecycle
|
114
|
+
// -----------------------------------------------------------------------------------------------
|
120
115
|
componentDidMount() {
|
121
|
-
|
122
|
-
|
123
|
-
this.
|
124
|
-
|
125
|
-
|
116
|
+
this.resetScroll();
|
117
|
+
this.scrollerRef.current.addScrollEndListener(this.clearScroll);
|
118
|
+
this.disconnectInnerWidth = internal.watchWidth(this.innerElRef.current, (innerWidth) => {
|
119
|
+
internal.afterSize(() => {
|
120
|
+
this.setState({ innerWidth });
|
121
|
+
});
|
122
|
+
});
|
126
123
|
}
|
127
124
|
componentDidUpdate(prevProps) {
|
128
|
-
|
129
|
-
|
130
|
-
this.timeScrollResponder.handleScroll(options.scrollTime);
|
125
|
+
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
126
|
+
this.resetScroll();
|
131
127
|
}
|
132
128
|
else {
|
133
|
-
|
129
|
+
// NOT optimal to update so often
|
130
|
+
// TODO: isolate dependencies of scroll coordinate
|
131
|
+
this.updateScroll();
|
134
132
|
}
|
135
133
|
}
|
136
134
|
componentWillUnmount() {
|
137
|
-
this.
|
138
|
-
this.
|
135
|
+
this.scrollerRef.current.removeScrollEndListener(this.clearScroll);
|
136
|
+
this.disconnectInnerWidth();
|
137
|
+
}
|
138
|
+
// Scrolling
|
139
|
+
// -----------------------------------------------------------------------------------------------
|
140
|
+
resetScroll() {
|
141
|
+
this.scrollDate = this.props.dateProfile.currentDate;
|
142
|
+
this.updateScroll();
|
139
143
|
}
|
140
144
|
}
|
141
145
|
// date profile
|
@@ -201,7 +205,7 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
201
205
|
multiMonthMinWidth: Number,
|
202
206
|
};
|
203
207
|
|
204
|
-
var css_248z = ".fc-
|
208
|
+
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: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}";
|
205
209
|
internal.injectStyles(css_248z);
|
206
210
|
|
207
211
|
var plugin = core.createPlugin({
|
@@ -219,7 +223,7 @@ FullCalendar.MultiMonth = (function (exports, core, internal$1, internal, preact
|
|
219
223
|
type: 'multiMonth',
|
220
224
|
duration: { years: 1 },
|
221
225
|
fixedWeekCount: true,
|
222
|
-
showNonCurrentDates: false,
|
226
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
223
227
|
},
|
224
228
|
},
|
225
229
|
});
|
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.3
|
3
3
|
Docs & License: https://fullcalendar.io/docs/multimonth-grid
|
4
4
|
(c) 2024 Adam Shaw
|
5
5
|
*/
|
6
|
-
FullCalendar.MultiMonth=function(e,t,n,
|
6
|
+
FullCalendar.MultiMonth=function(e,t,i,n,r){"use strict";class o 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}render(){const{props:e,context:t}=this,{dateProfile:o,forPrint:l}=e,{options:a}=t,s=this.buildDayTableModel(o,t.dateProfileGenerator),c=this.slicer.sliceProps(e,o,a.nextDayThreshold,t,s),m=this.createDayHeaderFormatter(a.dayHeaderFormat,!1,s.colCnt),d=this.buildDateRowConfig(s.headerDates,!1,o,e.todayRange,m,t),h=1/a.aspectRatio,u=h/s.rowCnt,f=!l,g=!l||e.hasLateralSiblings;return r.createElement("div",{"data-date":e.isoDateStr,className:n.joinClassNames("fc-multimonth-month",e.hasLateralSiblings&&"fc-break-inside-avoid"),style:{width:e.width}},r.createElement("div",{className:"fc-multimonth-header",style:{marginBottom:f?n.fracToCssDim(u):void 0}},r.createElement("div",{className:"fc-multimonth-title"},t.dateEnv.format(e.dateProfile.currentRange.start,e.titleFormat)),r.createElement(i.DayGridHeaderRow,Object.assign({},d,{className:"fc-multimonth-header-row"}))),r.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}},r.createElement(i.DayGridRows,{dateProfile:e.dateProfile,todayRange:e.todayRange,cellRows:s.cellRows,className:g?"fc-fill":"",forPrint:l&&!e.hasLateralSiblings,dayMaxEvents:l?void 0:a.dayMaxEvents,dayMaxEventRows:l&&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 l extends n.DateComponent{constructor(){super(...arguments),this.splitDateProfileByMonth=n.memoize(s),this.buildMonthFormat=n.memoize(d),this.scrollerRef=r.createRef(),this.innerElRef=r.createRef(),this.scrollDate=null,this.updateScroll=()=>{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)}"]`),r=Math.ceil(i.getBoundingClientRect().top-t.getBoundingClientRect().top);e.scrollTo({y:r})}},this.clearScroll=()=>{this.scrollDate=null}}render(){const{context:e,props:t,state:i}=this,{options:l}=e,a=!t.forPrint&&!n.getIsHeightAuto(l),s=this.splitDateProfileByMonth(e.dateProfileGenerator,t.dateProfile,e.dateEnv,l.fixedWeekCount,l.showNonCurrentDates),c=this.buildMonthFormat(l.multiMonthTitleFormat,s),{multiMonthMinWidth:m,multiMonthMaxColumns:d}=l,{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),r.createElement(n.NowTimer,{unit:"day"},(i,l)=>r.createElement(n.ViewContainer,{className:n.joinClassNames("fc-multimonth fc-border",1===u?"fc-multimonth-singlecol":"fc-multimonth-multicol",!t.forPrint&&"fc-flex-col"),viewSpec:e.viewSpec},r.createElement(n.Scroller,{vertical:a,className:a?"fc-liquid":"",ref:this.scrollerRef},r.createElement("div",{ref:this.innerElRef,className:"fc-multimonth-inner"},s.map(e=>{const i=n.formatIsoMonthStr(e.currentRange.start);return r.createElement(o,Object.assign({},t,{key:i,todayRange:l,isoDateStr:i,titleFormat:c,dateProfile:e,width:g,visibleWidth:f,hasLateralSiblings:p}))})))))}componentDidMount(){this.resetScroll(),this.scrollerRef.current.addScrollEndListener(this.clearScroll),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.updateScroll()}componentWillUnmount(){this.scrollerRef.current.removeScrollEndListener(this.clearScroll),this.disconnectInnerWidth()}resetScroll(){this.scrollDate=this.props.dateProfile.currentDate,this.updateScroll()}}const a=n.createDuration(1,"month");function s(e,t,r,o,l){const{start:s,end:c}=t.currentRange;let m=s;const d=[];for(;m.valueOf()<c.valueOf();){const s=r.add(m,a),c={start:e.skipHiddenDays(m),end:e.skipHiddenDays(s,-1,!0)};let h=i.buildDayTableRenderRange({currentRange:c,snapToWeek:!0,fixedWeekCount:o,dateEnv:r});h={start:e.skipHiddenDays(h.start),end:e.skipHiddenDays(h.end,-1,!0)};const u=t.activeRange?n.intersectRanges(t.activeRange,l?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: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:l,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,17 @@
|
|
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, joinClassNames, fracToCssDim, formatIsoMonthStr, getIsHeightAuto, NowTimer, ViewContainer, Scroller, 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();
|
13
15
|
}
|
14
16
|
render() {
|
15
17
|
const { props, context } = this;
|
@@ -17,27 +19,31 @@ class SingleMonth extends DateComponent {
|
|
17
19
|
const { options } = context;
|
18
20
|
const dayTableModel = this.buildDayTableModel(dateProfile, context.dateProfileGenerator);
|
19
21
|
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
|
22
|
+
const dayHeaderFormat = this.createDayHeaderFormatter(options.dayHeaderFormat, false, // datesRepDistinctDays
|
27
23
|
dayTableModel.colCnt);
|
28
|
-
|
29
|
-
|
30
|
-
|
24
|
+
const rowConfig = this.buildDateRowConfig(dayTableModel.headerDates, false, // datesRepDistinctDays
|
25
|
+
dateProfile, props.todayRange, dayHeaderFormat, context);
|
26
|
+
const invAspectRatio = 1 / options.aspectRatio;
|
27
|
+
const invRowAspectRatio = invAspectRatio / dayTableModel.rowCnt;
|
28
|
+
const isHeaderSticky = !forPrint;
|
29
|
+
const isAspectRatio = !forPrint || props.hasLateralSiblings;
|
30
|
+
return (createElement("div", { "data-date": props.isoDateStr, className: joinClassNames('fc-multimonth-month', props.hasLateralSiblings && 'fc-break-inside-avoid'),
|
31
|
+
// override fc-liquid's basis. fc-grow isn't sufficient because doesn't set min-width:0
|
32
|
+
style: { width: props.width } },
|
33
|
+
createElement("div", { className: "fc-multimonth-header", style: {
|
34
|
+
marginBottom: isHeaderSticky ? fracToCssDim(invRowAspectRatio) : undefined,
|
35
|
+
} },
|
31
36
|
createElement("div", { className: "fc-multimonth-title" }, context.dateEnv.format(props.dateProfile.currentRange.start, props.titleFormat)),
|
32
|
-
createElement(
|
33
|
-
createElement("div", { className: 'fc-multimonth-body fc-
|
34
|
-
marginTop: -
|
35
|
-
|
37
|
+
createElement(DayGridHeaderRow, Object.assign({}, rowConfig, { 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,
|
36
41
|
} },
|
37
|
-
createElement(DayGridRows
|
38
|
-
, { dateProfile: props.dateProfile, todayRange: props.todayRange, cellRows: dayTableModel.cellRows, forPrint: props.forPrint,
|
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,
|
39
43
|
// content
|
40
|
-
fgEventSegs: slicedProps.fgEventSegs, bgEventSegs: slicedProps.bgEventSegs, businessHourSegs: slicedProps.businessHourSegs, dateSelectionSegs: slicedProps.dateSelectionSegs, eventDrag: slicedProps.eventDrag, eventResize: slicedProps.eventResize, eventSelection: slicedProps.eventSelection
|
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
|
|
@@ -48,91 +54,89 @@ class MultiMonthView extends DateComponent {
|
|
48
54
|
this.splitDateProfileByMonth = memoize(splitDateProfileByMonth);
|
49
55
|
this.buildMonthFormat = memoize(buildMonthFormat);
|
50
56
|
// ref
|
51
|
-
this.
|
52
|
-
this.innerElRef = createRef();
|
53
|
-
this.
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
+
this.scrollerRef = createRef();
|
58
|
+
this.innerElRef = createRef(); // .fc-multimonth-inner
|
59
|
+
this.scrollDate = null;
|
60
|
+
this.updateScroll = () => {
|
61
|
+
if (this.scrollDate != null &&
|
62
|
+
this.state.innerWidth != null // render completed?
|
63
|
+
) {
|
64
|
+
const scroller = this.scrollerRef.current;
|
57
65
|
const innerEl = this.innerElRef.current;
|
58
|
-
const
|
59
|
-
|
60
|
-
const box0 = children[0].getBoundingClientRect();
|
61
|
-
const box1 = children[1].getBoundingClientRect();
|
62
|
-
let xSpan;
|
63
|
-
[xGap, xSpan] = [
|
64
|
-
Math.abs(box0.left - box1.right),
|
65
|
-
Math.abs(box0.right - box1.left),
|
66
|
-
].sort(compareNumbers);
|
67
|
-
xPadding = clientWidth - xSpan;
|
68
|
-
}
|
69
|
-
}
|
70
|
-
this.setState({ clientWidth, xGap, xPadding });
|
71
|
-
};
|
72
|
-
this.timeScrollResponder = new ScrollResponder((_time) => {
|
73
|
-
// HACK to scroll to day
|
74
|
-
if (this.state.clientWidth != null) {
|
75
|
-
const { currentDate } = this.props.dateProfile;
|
76
|
-
const rootEl = this.rootElRef.current;
|
77
|
-
const innerEl = this.innerElRef.current;
|
78
|
-
const monthEl = innerEl.querySelector(`[data-date="${formatIsoMonthStr(currentDate)}"]`);
|
79
|
-
rootEl.scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
66
|
+
const monthEl = innerEl.querySelector(`[data-date="${formatIsoMonthStr(this.scrollDate)}"]`);
|
67
|
+
const scrollTop = Math.ceil(// for fractions, err on the side of scrolling further
|
80
68
|
monthEl.getBoundingClientRect().top -
|
81
69
|
innerEl.getBoundingClientRect().top);
|
82
|
-
|
70
|
+
scroller.scrollTo({ y: scrollTop });
|
83
71
|
}
|
84
|
-
|
85
|
-
|
72
|
+
};
|
73
|
+
this.clearScroll = () => {
|
74
|
+
this.scrollDate = null;
|
75
|
+
};
|
86
76
|
}
|
87
77
|
render() {
|
88
78
|
const { context, props, state } = this;
|
89
79
|
const { options } = context;
|
90
|
-
const
|
91
|
-
? 2
|
92
|
-
: Math.min(options.multiMonthMaxColumns, Math.floor((state.clientWidth - state.xPadding + state.xGap) /
|
93
|
-
(options.multiMonthMinWidth + state.xGap)));
|
94
|
-
const monthWidth = state.clientWidth == null
|
95
|
-
? '34%' // will expand. now small enough to be 1/3. for allowing gap
|
96
|
-
: Math.floor(// exact values can cause expansion to other rows
|
97
|
-
(state.clientWidth - state.xPadding - (state.xGap * (colCount - 1))) /
|
98
|
-
colCount);
|
80
|
+
const verticalScrolling = !props.forPrint && !getIsHeightAuto(options);
|
99
81
|
const monthDateProfiles = this.splitDateProfileByMonth(context.dateProfileGenerator, props.dateProfile, context.dateEnv, options.fixedWeekCount, options.showNonCurrentDates);
|
100
82
|
const monthTitleFormat = this.buildMonthFormat(options.multiMonthTitleFormat, monthDateProfiles);
|
101
|
-
const
|
102
|
-
|
103
|
-
|
83
|
+
const { multiMonthMinWidth, multiMonthMaxColumns } = options;
|
84
|
+
const { innerWidth } = state;
|
85
|
+
let cols;
|
86
|
+
let computedMonthWidth;
|
87
|
+
let cssMonthWidth;
|
88
|
+
let hasLateralSiblings = false;
|
89
|
+
if (innerWidth != null) {
|
90
|
+
cols = Math.max(1, Math.min(multiMonthMaxColumns, Math.floor(innerWidth / multiMonthMinWidth)));
|
91
|
+
if (props.forPrint) {
|
92
|
+
cols = Math.min(cols, 2);
|
93
|
+
}
|
94
|
+
computedMonthWidth = innerWidth / cols;
|
95
|
+
cssMonthWidth = fracToCssDim(1 / cols);
|
96
|
+
hasLateralSiblings = cols > 1;
|
97
|
+
}
|
98
|
+
return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(ViewContainer, { className: joinClassNames('fc-multimonth fc-border', (cols === 1) ?
|
104
99
|
'fc-multimonth-singlecol' :
|
105
|
-
'fc-multimonth-multicol',
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
'fc-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
return (createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: monthWidth })));
|
115
|
-
}))))));
|
100
|
+
'fc-multimonth-multicol',
|
101
|
+
// HACK for Safari. Can't do break-inside:avoid with flexbox items, likely b/c it's not standard:
|
102
|
+
// https://stackoverflow.com/a/60256345
|
103
|
+
!props.forPrint && 'fc-flex-col'), viewSpec: context.viewSpec },
|
104
|
+
createElement(Scroller, { vertical: verticalScrolling, className: verticalScrolling ? 'fc-liquid' : '', ref: this.scrollerRef },
|
105
|
+
createElement("div", { ref: this.innerElRef, className: 'fc-multimonth-inner' }, monthDateProfiles.map((monthDateProfile) => {
|
106
|
+
const monthStr = formatIsoMonthStr(monthDateProfile.currentRange.start);
|
107
|
+
return (createElement(SingleMonth, Object.assign({}, props, { key: monthStr, todayRange: todayRange, isoDateStr: monthStr, titleFormat: monthTitleFormat, dateProfile: monthDateProfile, width: cssMonthWidth, visibleWidth: computedMonthWidth, hasLateralSiblings: hasLateralSiblings })));
|
108
|
+
})))))));
|
116
109
|
}
|
110
|
+
// Lifecycle
|
111
|
+
// -----------------------------------------------------------------------------------------------
|
117
112
|
componentDidMount() {
|
118
|
-
|
119
|
-
|
120
|
-
this.
|
121
|
-
|
122
|
-
|
113
|
+
this.resetScroll();
|
114
|
+
this.scrollerRef.current.addScrollEndListener(this.clearScroll);
|
115
|
+
this.disconnectInnerWidth = watchWidth(this.innerElRef.current, (innerWidth) => {
|
116
|
+
afterSize(() => {
|
117
|
+
this.setState({ innerWidth });
|
118
|
+
});
|
119
|
+
});
|
123
120
|
}
|
124
121
|
componentDidUpdate(prevProps) {
|
125
|
-
|
126
|
-
|
127
|
-
this.timeScrollResponder.handleScroll(options.scrollTime);
|
122
|
+
if (prevProps.dateProfile !== this.props.dateProfile && this.context.options.scrollTimeReset) {
|
123
|
+
this.resetScroll();
|
128
124
|
}
|
129
125
|
else {
|
130
|
-
|
126
|
+
// NOT optimal to update so often
|
127
|
+
// TODO: isolate dependencies of scroll coordinate
|
128
|
+
this.updateScroll();
|
131
129
|
}
|
132
130
|
}
|
133
131
|
componentWillUnmount() {
|
134
|
-
this.
|
135
|
-
this.
|
132
|
+
this.scrollerRef.current.removeScrollEndListener(this.clearScroll);
|
133
|
+
this.disconnectInnerWidth();
|
134
|
+
}
|
135
|
+
// Scrolling
|
136
|
+
// -----------------------------------------------------------------------------------------------
|
137
|
+
resetScroll() {
|
138
|
+
this.scrollDate = this.props.dateProfile.currentDate;
|
139
|
+
this.updateScroll();
|
136
140
|
}
|
137
141
|
}
|
138
142
|
// date profile
|
@@ -198,7 +202,7 @@ const OPTION_REFINERS = {
|
|
198
202
|
multiMonthMinWidth: Number,
|
199
203
|
};
|
200
204
|
|
201
|
-
var css_248z = ".fc-
|
205
|
+
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: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}";
|
202
206
|
injectStyles(css_248z);
|
203
207
|
|
204
208
|
var index = createPlugin({
|
@@ -216,7 +220,7 @@ var index = createPlugin({
|
|
216
220
|
type: 'multiMonth',
|
217
221
|
duration: { years: 1 },
|
218
222
|
fixedWeekCount: true,
|
219
|
-
showNonCurrentDates: false,
|
223
|
+
showNonCurrentDates: false, // TODO: looks bad when single-col layout
|
220
224
|
},
|
221
225
|
},
|
222
226
|
});
|
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.3",
|
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.3"
|
16
16
|
},
|
17
17
|
"peerDependencies": {
|
18
|
-
"@fullcalendar/core": "7.0.0-beta.
|
18
|
+
"@fullcalendar/core": "7.0.0-beta.3"
|
19
19
|
},
|
20
20
|
"type": "module",
|
21
21
|
"bugs": "https://fullcalendar.io/reporting-bugs",
|