@fullcalendar/daygrid 6.0.3 → 6.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/internal.d.ts CHANGED
@@ -1,5 +1,5 @@
1
1
  import { Duration, CssDimValue } from '@fullcalendar/core';
2
- import { DateComponent, ViewContext, DateProfile, DayTableModel, EventStore, EventUiHash, DateSpan, EventInteractionState, Seg, Slicer, DateRange, Hit, DayTableCell, EventSegUiInteractionState, Dictionary, ViewProps, ChunkConfigRowContent, ChunkContentCallbackArgs, DateProfileGenerator } from '@fullcalendar/core/internal';
2
+ import { DateComponent, ViewContext, DateProfile, DayTableModel, EventStore, EventUiHash, DateSpan, EventInteractionState, Seg, Slicer, DateRange, DateProfileGenerator, DateEnv, Hit, DayTableCell, EventSegUiInteractionState, Dictionary, ViewProps, ChunkConfigRowContent, ChunkContentCallbackArgs } from '@fullcalendar/core/internal';
3
3
  import { createElement, VNode, RefObject } from '@fullcalendar/core/preact';
4
4
 
5
5
  interface DayTableProps {
@@ -42,13 +42,20 @@ declare class DayTableSlicer extends Slicer<TableSeg, [DayTableModel]> {
42
42
  sliceRange(dateRange: DateRange, dayTableModel: DayTableModel): TableSeg[];
43
43
  }
44
44
 
45
- interface TableProps {
45
+ declare class TableDateProfileGenerator extends DateProfileGenerator {
46
+ buildRenderRange(currentRange: any, currentRangeUnit: any, isRangeAllDay: any): DateRange;
47
+ }
48
+ declare function buildDayTableRenderRange(props: {
49
+ currentRange: DateRange;
50
+ snapToWeek: boolean;
51
+ fixedWeekCount: boolean;
52
+ dateEnv: DateEnv;
53
+ }): DateRange;
54
+
55
+ interface TableRowsProps {
46
56
  dateProfile: DateProfile;
47
57
  cells: DayTableCell[][];
48
58
  renderRowIntro?: () => VNode;
49
- colGroupNode: VNode;
50
- tableMinWidth: CssDimValue;
51
- expandRows: boolean;
52
59
  showWeekNumbers: boolean;
53
60
  clientWidth: number | null;
54
61
  clientHeight: number | null;
@@ -61,11 +68,10 @@ interface TableProps {
61
68
  eventResize: EventSegUiInteractionState | null;
62
69
  dayMaxEvents: boolean | number;
63
70
  dayMaxEventRows: boolean | number;
64
- headerAlignElRef?: RefObject<HTMLElement>;
65
71
  forPrint: boolean;
66
72
  isHitComboAllowed?: (hit0: Hit, hit1: Hit) => boolean;
67
73
  }
68
- declare class Table extends DateComponent<TableProps> {
74
+ declare class TableRows extends DateComponent<TableRowsProps> {
69
75
  private splitBusinessHourSegs;
70
76
  private splitBgEventSegs;
71
77
  private splitFgEventSegs;
@@ -77,13 +83,30 @@ declare class Table extends DateComponent<TableProps> {
77
83
  private rowPositions;
78
84
  private colPositions;
79
85
  render(): createElement.JSX.Element;
80
- handleRootEl: (rootEl: HTMLElement | null) => void;
86
+ componentDidMount(): void;
87
+ componentWillUnmount(): void;
81
88
  prepareHits(): void;
82
89
  queryHit(positionLeft: number, positionTop: number): Hit;
83
90
  private getCellEl;
84
91
  private getCellRange;
85
92
  }
86
93
 
94
+ interface TableProps extends TableRowsProps {
95
+ colGroupNode: VNode;
96
+ tableMinWidth: CssDimValue;
97
+ expandRows: boolean;
98
+ headerAlignElRef?: RefObject<HTMLElement>;
99
+ }
100
+ declare class Table extends DateComponent<TableProps> {
101
+ private elRef;
102
+ private needsScrollReset;
103
+ render(): createElement.JSX.Element;
104
+ componentDidMount(): void;
105
+ componentDidUpdate(prevProps: TableProps): void;
106
+ requestScrollReset(): void;
107
+ flushScrollReset(): void;
108
+ }
109
+
87
110
  declare abstract class TableView<State = Dictionary> extends DateComponent<ViewProps, State> {
88
111
  protected headerElRef: RefObject<HTMLTableCellElement>;
89
112
  renderSimpleLayout(headerRowContent: ChunkConfigRowContent, bodyContent: (contentArg: ChunkContentCallbackArgs) => VNode): createElement.JSX.Element;
@@ -98,4 +121,4 @@ declare class DayTableView extends TableView {
98
121
  }
99
122
  declare function buildDayTableModel(dateProfile: DateProfile, dateProfileGenerator: DateProfileGenerator): DayTableModel;
100
123
 
101
- export { DayTableView as DayGridView, DayTable, DayTableSlicer, Table, TableSeg, TableView, buildDayTableModel };
124
+ export { DayTableView as DayGridView, DayTable, DayTableSlicer, Table, TableDateProfileGenerator, TableRows, TableSeg, TableView, buildDayTableModel, buildDayTableRenderRange };
package/internal.esm.js CHANGED
@@ -1,4 +1,4 @@
1
- import { DateComponent, getStickyHeaderDates, ViewContainer, SimpleScrollGrid, getStickyFooterScrollbar, renderScrollShim, createFormatter, BaseComponent, StandardEvent, buildSegTimeText, EventContainer, getSegAnchorAttrs, memoize, MoreLinkContainer, getSegMeta, getUniqueDomId, setRef, DayCellContainer, WeekNumberContainer, buildNavLinkAttrs, hasCustomDayCellContent, intersectRanges, addDays, SegHierarchy, buildEntryKey, intersectSpans, RefMap, sortEventSegs, isPropsEqual, buildEventRangeKey, BgEvent, renderFill, PositionCache, NowTimer, Slicer, DayHeader, DaySeriesModel, DayTableModel } from '@fullcalendar/core/internal';
1
+ import { DateComponent, getStickyHeaderDates, ViewContainer, SimpleScrollGrid, getStickyFooterScrollbar, renderScrollShim, createFormatter, BaseComponent, StandardEvent, buildSegTimeText, EventContainer, getSegAnchorAttrs, memoize, MoreLinkContainer, getSegMeta, getUniqueDomId, setRef, DayCellContainer, WeekNumberContainer, buildNavLinkAttrs, hasCustomDayCellContent, intersectRanges, addDays, SegHierarchy, buildEntryKey, intersectSpans, RefMap, sortEventSegs, isPropsEqual, buildEventRangeKey, BgEvent, renderFill, PositionCache, NowTimer, formatIsoMonthStr, formatDayString, Slicer, DayHeader, DaySeriesModel, DayTableModel, DateProfileGenerator, addWeeks, diffWeeks } from '@fullcalendar/core/internal';
2
2
  import { createRef, createElement, Fragment } from '@fullcalendar/core/preact';
3
3
 
4
4
  /* An abstract class for the daygrid views, as well as month view. Renders one or more rows of day cells.
@@ -214,14 +214,24 @@ class TableCell extends DateComponent {
214
214
  let { context, props, state, rootElRef } = this;
215
215
  let { options } = context;
216
216
  let { date, dateProfile } = props;
217
+ // TODO: memoize this?
218
+ let isMonthStart = props.showDayNumber &&
219
+ dateProfile.currentRangeUnit !== 'month' && (dateProfile.currentRange.start.valueOf() === date.valueOf() ||
220
+ date.getUTCDate() === 1);
217
221
  return (createElement(DayCellContainer, { elTag: "td", elRef: this.handleRootEl, elClasses: [
218
222
  'fc-daygrid-day',
219
223
  ...(props.extraClassNames || []),
220
- ], elAttrs: Object.assign(Object.assign(Object.assign({}, props.extraDataAttrs), (props.showDayNumber ? { 'aria-labelledby': state.dayNumberId } : {})), { role: 'gridcell' }), defaultGenerator: renderTopInner, date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraRenderProps: props.extraRenderProps }, (InnerContent, renderProps) => (createElement("div", { className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", ref: props.innerElRef },
224
+ ], elAttrs: Object.assign(Object.assign(Object.assign({}, props.extraDataAttrs), (props.showDayNumber ? { 'aria-labelledby': state.dayNumberId } : {})), { role: 'gridcell' }), defaultGenerator: renderTopInner, date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart, extraRenderProps: props.extraRenderProps }, (InnerContent, renderProps) => (createElement("div", { ref: props.innerElRef, className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", style: { minHeight: props.minHeight } },
221
225
  props.showWeekNumber && (createElement(WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: buildNavLinkAttrs(context, date, 'week'), date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT })),
222
- Boolean(!renderProps.isDisabled &&
223
- (props.showDayNumber || hasCustomDayCellContent(options) || props.forceDayTop)) && (createElement("div", { className: "fc-daygrid-day-top" },
224
- createElement(InnerContent, { elTag: "a", elClasses: ['fc-daygrid-day-number'], elAttrs: Object.assign(Object.assign({}, buildNavLinkAttrs(context, date)), { id: state.dayNumberId }) }))),
226
+ !renderProps.isDisabled &&
227
+ (props.showDayNumber || hasCustomDayCellContent(options) || props.forceDayTop) ? (createElement("div", { className: "fc-daygrid-day-top" },
228
+ createElement(InnerContent, { elTag: "a", elClasses: [
229
+ 'fc-daygrid-day-number',
230
+ isMonthStart && 'fc-daygrid-month-start',
231
+ ], elAttrs: Object.assign(Object.assign({}, buildNavLinkAttrs(context, date)), { id: state.dayNumberId }) }))) : props.showDayNumber ? (
232
+ // for creating correct amount of space (see issue #7162)
233
+ createElement("div", { className: "fc-daygrid-day-top", style: { visibility: 'hidden' } },
234
+ createElement("a", { className: "fc-daygrid-day-number" }, "\u00A0"))) : undefined,
225
235
  createElement("div", { className: "fc-daygrid-day-events", ref: props.fgContentElRef },
226
236
  props.fgContent,
227
237
  createElement("div", { className: "fc-daygrid-day-bottom", style: { marginTop: props.moreMarginTop } },
@@ -502,7 +512,7 @@ class TableRow extends DateComponent {
502
512
  createElement(Fragment, null,
503
513
  this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
504
514
  this.renderFillSegs(businessHoursByCol[col], 'non-business'),
505
- this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))) }));
515
+ this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), minHeight: props.cellMinHeight }));
506
516
  })));
507
517
  }
508
518
  componentDidMount() {
@@ -674,7 +684,7 @@ function buildAbsoluteTopHash(colPlacements) {
674
684
  return topsByInstanceId;
675
685
  }
676
686
 
677
- class Table extends DateComponent {
687
+ class TableRows extends DateComponent {
678
688
  constructor() {
679
689
  super(...arguments);
680
690
  this.splitBusinessHourSegs = memoize(splitSegsByRow);
@@ -684,22 +694,9 @@ class Table extends DateComponent {
684
694
  this.splitEventDrag = memoize(splitInteractionByRow);
685
695
  this.splitEventResize = memoize(splitInteractionByRow);
686
696
  this.rowRefs = new RefMap();
687
- this.handleRootEl = (rootEl) => {
688
- this.rootEl = rootEl;
689
- if (rootEl) {
690
- this.context.registerInteractiveComponent(this, {
691
- el: rootEl,
692
- isHitComboAllowed: this.props.isHitComboAllowed,
693
- });
694
- }
695
- else {
696
- this.context.unregisterInteractiveComponent(this);
697
- }
698
- };
699
697
  }
700
698
  render() {
701
- let { props } = this;
702
- let { dateProfile, dayMaxEventRows, dayMaxEvents, expandRows } = props;
699
+ let { props, context } = this;
703
700
  let rowCnt = props.cells.length;
704
701
  let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
705
702
  let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
@@ -707,36 +704,33 @@ class Table extends DateComponent {
707
704
  let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
708
705
  let eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
709
706
  let eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt);
710
- let limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
711
- // if rows can't expand to fill fixed height, can't do balanced-height event limit
712
- // TODO: best place to normalize these options?
713
- if (limitViaBalanced && !expandRows) {
714
- limitViaBalanced = false;
715
- dayMaxEventRows = null;
716
- dayMaxEvents = null;
707
+ // for DayGrid view with many rows, force a min-height on cells so doesn't appear squished
708
+ // choose 7 because a month view will have max 6 rows
709
+ let cellMinHeight = (rowCnt >= 7 && props.clientWidth) ?
710
+ props.clientWidth / context.options.aspectRatio / 6 :
711
+ null;
712
+ return (createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(Fragment, null, props.cells.map((cells, row) => (createElement(TableRow, { ref: this.rowRefs.createRef(row), key: cells.length
713
+ ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
714
+ : row // in case there are no cells (like when resource view is loading)
715
+ , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: props.dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, cellMinHeight: cellMinHeight, forPrint: props.forPrint })))))));
716
+ }
717
+ componentDidMount() {
718
+ // HACK: need a daygrid wrapper parent to do positioning
719
+ // NOTE: a daygrid resource view w/o resources can have zero cells
720
+ const firstCellEl = this.rowRefs.currentMap[0].getCellEls()[0];
721
+ this.rootEl = firstCellEl ? firstCellEl.closest('.fc-daygrid-body') : null;
722
+ if (this.rootEl) {
723
+ this.context.registerInteractiveComponent(this, {
724
+ el: this.rootEl,
725
+ isHitComboAllowed: this.props.isHitComboAllowed,
726
+ });
727
+ }
728
+ }
729
+ componentWillUnmount() {
730
+ if (this.rootEl) {
731
+ this.context.unregisterInteractiveComponent(this);
732
+ this.rootEl = null;
717
733
  }
718
- let classNames = [
719
- 'fc-daygrid-body',
720
- limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
721
- expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
722
- ];
723
- return (createElement("div", { className: classNames.join(' '), ref: this.handleRootEl, style: {
724
- // these props are important to give this wrapper correct dimensions for interactions
725
- // TODO: if we set it here, can we avoid giving to inner tables?
726
- width: props.clientWidth,
727
- minWidth: props.tableMinWidth,
728
- } },
729
- createElement(NowTimer, { unit: "day" }, (nowDate, todayRange) => (createElement(Fragment, null,
730
- createElement("table", { role: "presentation", className: "fc-scrollgrid-sync-table", style: {
731
- width: props.clientWidth,
732
- minWidth: props.tableMinWidth,
733
- height: expandRows ? props.clientHeight : '',
734
- } },
735
- props.colGroupNode,
736
- createElement("tbody", { role: "presentation" }, props.cells.map((cells, row) => (createElement(TableRow, { ref: this.rowRefs.createRef(row), key: cells.length
737
- ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
738
- : row // in case there are no cells (like when resource view is loading)
739
- , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint }))))))))));
740
734
  }
741
735
  // Hit System
742
736
  // ----------------------------------------------------------------------------------------------------
@@ -781,6 +775,87 @@ function isSegAllDay(seg) {
781
775
  return seg.eventRange.def.allDay;
782
776
  }
783
777
 
778
+ class Table extends DateComponent {
779
+ constructor() {
780
+ super(...arguments);
781
+ this.elRef = createRef();
782
+ this.needsScrollReset = false;
783
+ }
784
+ render() {
785
+ let { props } = this;
786
+ let { dayMaxEventRows, dayMaxEvents, expandRows } = props;
787
+ let limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
788
+ // if rows can't expand to fill fixed height, can't do balanced-height event limit
789
+ // TODO: best place to normalize these options?
790
+ if (limitViaBalanced && !expandRows) {
791
+ limitViaBalanced = false;
792
+ dayMaxEventRows = null;
793
+ dayMaxEvents = null;
794
+ }
795
+ let classNames = [
796
+ 'fc-daygrid-body',
797
+ limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
798
+ expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
799
+ ];
800
+ return (createElement("div", { ref: this.elRef, className: classNames.join(' '), style: {
801
+ // these props are important to give this wrapper correct dimensions for interactions
802
+ // TODO: if we set it here, can we avoid giving to inner tables?
803
+ width: props.clientWidth,
804
+ minWidth: props.tableMinWidth,
805
+ } },
806
+ createElement("table", { role: "presentation", className: "fc-scrollgrid-sync-table", style: {
807
+ width: props.clientWidth,
808
+ minWidth: props.tableMinWidth,
809
+ height: expandRows ? props.clientHeight : '',
810
+ } },
811
+ props.colGroupNode,
812
+ createElement("tbody", { role: "presentation" },
813
+ createElement(TableRows, { dateProfile: props.dateProfile, cells: props.cells, renderRowIntro: props.renderRowIntro, showWeekNumbers: props.showWeekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed })))));
814
+ }
815
+ componentDidMount() {
816
+ this.requestScrollReset();
817
+ }
818
+ componentDidUpdate(prevProps) {
819
+ if (prevProps.dateProfile !== this.props.dateProfile) {
820
+ this.requestScrollReset();
821
+ }
822
+ else {
823
+ this.flushScrollReset();
824
+ }
825
+ }
826
+ requestScrollReset() {
827
+ this.needsScrollReset = true;
828
+ this.flushScrollReset();
829
+ }
830
+ flushScrollReset() {
831
+ if (this.needsScrollReset &&
832
+ this.props.clientWidth // sizes computed?
833
+ ) {
834
+ const subjectEl = getScrollSubjectEl(this.elRef.current, this.props.dateProfile);
835
+ if (subjectEl) {
836
+ const originEl = subjectEl.closest('.fc-daygrid-body');
837
+ const scrollEl = originEl.closest('.fc-scroller');
838
+ const scrollTop = subjectEl.getBoundingClientRect().top -
839
+ originEl.getBoundingClientRect().top;
840
+ scrollEl.scrollTop = scrollTop ? (scrollTop + 1) : 0; // overcome border
841
+ }
842
+ this.needsScrollReset = false;
843
+ }
844
+ }
845
+ }
846
+ function getScrollSubjectEl(containerEl, dateProfile) {
847
+ let el;
848
+ if (dateProfile.currentRangeUnit.match(/year|month/)) {
849
+ el = containerEl.querySelector(`[data-date="${formatIsoMonthStr(dateProfile.currentDate)}-01"]`);
850
+ // even if view is month-based, first-of-month might be hidden...
851
+ }
852
+ if (!el) {
853
+ el = containerEl.querySelector(`[data-date="${formatDayString(dateProfile.currentDate)}"]`);
854
+ // could still be hidden if an interior-view hidden day
855
+ }
856
+ return el;
857
+ }
858
+
784
859
  class DayTableSlicer extends Slicer {
785
860
  constructor() {
786
861
  super(...arguments);
@@ -809,6 +884,7 @@ class DayTableView extends TableView {
809
884
  this.buildDayTableModel = memoize(buildDayTableModel);
810
885
  this.headerRef = createRef();
811
886
  this.tableRef = createRef();
887
+ // can't override any lifecycle methods from parent
812
888
  }
813
889
  render() {
814
890
  let { options, dateProfileGenerator } = this.context;
@@ -826,4 +902,42 @@ function buildDayTableModel(dateProfile, dateProfileGenerator) {
826
902
  return new DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
827
903
  }
828
904
 
829
- export { DayTableView as DayGridView, DayTable, DayTableSlicer, Table, TableView, buildDayTableModel };
905
+ class TableDateProfileGenerator extends DateProfileGenerator {
906
+ // Computes the date range that will be rendered
907
+ buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
908
+ let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay);
909
+ let { props } = this;
910
+ return buildDayTableRenderRange({
911
+ currentRange: renderRange,
912
+ snapToWeek: /^(year|month)$/.test(currentRangeUnit),
913
+ fixedWeekCount: props.fixedWeekCount,
914
+ dateEnv: props.dateEnv,
915
+ });
916
+ }
917
+ }
918
+ function buildDayTableRenderRange(props) {
919
+ let { dateEnv, currentRange } = props;
920
+ let { start, end } = currentRange;
921
+ let endOfWeek;
922
+ // year and month views should be aligned with weeks. this is already done for week
923
+ if (props.snapToWeek) {
924
+ start = dateEnv.startOfWeek(start);
925
+ // make end-of-week if not already
926
+ endOfWeek = dateEnv.startOfWeek(end);
927
+ if (endOfWeek.valueOf() !== end.valueOf()) {
928
+ end = addWeeks(endOfWeek, 1);
929
+ }
930
+ }
931
+ // ensure 6 weeks
932
+ if (props.fixedWeekCount) {
933
+ // TODO: instead of these date-math gymnastics (for multimonth view),
934
+ // compute dateprofiles of all months, then use start of first and end of last.
935
+ let lastMonthRenderStart = dateEnv.startOfWeek(dateEnv.startOfMonth(addDays(currentRange.end, -1)));
936
+ let rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
937
+ diffWeeks(lastMonthRenderStart, end));
938
+ end = addWeeks(end, 6 - rowCnt);
939
+ }
940
+ return { start, end };
941
+ }
942
+
943
+ export { DayTableView as DayGridView, DayTable, DayTableSlicer, Table, TableDateProfileGenerator, TableRows, TableView, buildDayTableModel, buildDayTableRenderRange };
package/internal.js CHANGED
@@ -218,14 +218,24 @@ class TableCell extends internal.DateComponent {
218
218
  let { context, props, state, rootElRef } = this;
219
219
  let { options } = context;
220
220
  let { date, dateProfile } = props;
221
+ // TODO: memoize this?
222
+ let isMonthStart = props.showDayNumber &&
223
+ dateProfile.currentRangeUnit !== 'month' && (dateProfile.currentRange.start.valueOf() === date.valueOf() ||
224
+ date.getUTCDate() === 1);
221
225
  return (preact.createElement(internal.DayCellContainer, { elTag: "td", elRef: this.handleRootEl, elClasses: [
222
226
  'fc-daygrid-day',
223
227
  ...(props.extraClassNames || []),
224
- ], elAttrs: Object.assign(Object.assign(Object.assign({}, props.extraDataAttrs), (props.showDayNumber ? { 'aria-labelledby': state.dayNumberId } : {})), { role: 'gridcell' }), defaultGenerator: renderTopInner, date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, extraRenderProps: props.extraRenderProps }, (InnerContent, renderProps) => (preact.createElement("div", { className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", ref: props.innerElRef },
228
+ ], elAttrs: Object.assign(Object.assign(Object.assign({}, props.extraDataAttrs), (props.showDayNumber ? { 'aria-labelledby': state.dayNumberId } : {})), { role: 'gridcell' }), defaultGenerator: renderTopInner, date: date, dateProfile: dateProfile, todayRange: props.todayRange, showDayNumber: props.showDayNumber, isMonthStart: isMonthStart, extraRenderProps: props.extraRenderProps }, (InnerContent, renderProps) => (preact.createElement("div", { ref: props.innerElRef, className: "fc-daygrid-day-frame fc-scrollgrid-sync-inner", style: { minHeight: props.minHeight } },
225
229
  props.showWeekNumber && (preact.createElement(internal.WeekNumberContainer, { elTag: "a", elClasses: ['fc-daygrid-week-number'], elAttrs: internal.buildNavLinkAttrs(context, date, 'week'), date: date, defaultFormat: DEFAULT_WEEK_NUM_FORMAT })),
226
- Boolean(!renderProps.isDisabled &&
227
- (props.showDayNumber || internal.hasCustomDayCellContent(options) || props.forceDayTop)) && (preact.createElement("div", { className: "fc-daygrid-day-top" },
228
- preact.createElement(InnerContent, { elTag: "a", elClasses: ['fc-daygrid-day-number'], elAttrs: Object.assign(Object.assign({}, internal.buildNavLinkAttrs(context, date)), { id: state.dayNumberId }) }))),
230
+ !renderProps.isDisabled &&
231
+ (props.showDayNumber || internal.hasCustomDayCellContent(options) || props.forceDayTop) ? (preact.createElement("div", { className: "fc-daygrid-day-top" },
232
+ preact.createElement(InnerContent, { elTag: "a", elClasses: [
233
+ 'fc-daygrid-day-number',
234
+ isMonthStart && 'fc-daygrid-month-start',
235
+ ], elAttrs: Object.assign(Object.assign({}, internal.buildNavLinkAttrs(context, date)), { id: state.dayNumberId }) }))) : props.showDayNumber ? (
236
+ // for creating correct amount of space (see issue #7162)
237
+ preact.createElement("div", { className: "fc-daygrid-day-top", style: { visibility: 'hidden' } },
238
+ preact.createElement("a", { className: "fc-daygrid-day-number" }, "\u00A0"))) : undefined,
229
239
  preact.createElement("div", { className: "fc-daygrid-day-events", ref: props.fgContentElRef },
230
240
  props.fgContent,
231
241
  preact.createElement("div", { className: "fc-daygrid-day-bottom", style: { marginTop: props.moreMarginTop } },
@@ -506,7 +516,7 @@ class TableRow extends internal.DateComponent {
506
516
  preact.createElement(preact.Fragment, null,
507
517
  this.renderFillSegs(highlightSegsByCol[col], 'highlight'),
508
518
  this.renderFillSegs(businessHoursByCol[col], 'non-business'),
509
- this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))) }));
519
+ this.renderFillSegs(bgEventSegsByCol[col], 'bg-event'))), minHeight: props.cellMinHeight }));
510
520
  })));
511
521
  }
512
522
  componentDidMount() {
@@ -678,7 +688,7 @@ function buildAbsoluteTopHash(colPlacements) {
678
688
  return topsByInstanceId;
679
689
  }
680
690
 
681
- class Table extends internal.DateComponent {
691
+ class TableRows extends internal.DateComponent {
682
692
  constructor() {
683
693
  super(...arguments);
684
694
  this.splitBusinessHourSegs = internal.memoize(splitSegsByRow);
@@ -688,22 +698,9 @@ class Table extends internal.DateComponent {
688
698
  this.splitEventDrag = internal.memoize(splitInteractionByRow);
689
699
  this.splitEventResize = internal.memoize(splitInteractionByRow);
690
700
  this.rowRefs = new internal.RefMap();
691
- this.handleRootEl = (rootEl) => {
692
- this.rootEl = rootEl;
693
- if (rootEl) {
694
- this.context.registerInteractiveComponent(this, {
695
- el: rootEl,
696
- isHitComboAllowed: this.props.isHitComboAllowed,
697
- });
698
- }
699
- else {
700
- this.context.unregisterInteractiveComponent(this);
701
- }
702
- };
703
701
  }
704
702
  render() {
705
- let { props } = this;
706
- let { dateProfile, dayMaxEventRows, dayMaxEvents, expandRows } = props;
703
+ let { props, context } = this;
707
704
  let rowCnt = props.cells.length;
708
705
  let businessHourSegsByRow = this.splitBusinessHourSegs(props.businessHourSegs, rowCnt);
709
706
  let bgEventSegsByRow = this.splitBgEventSegs(props.bgEventSegs, rowCnt);
@@ -711,36 +708,33 @@ class Table extends internal.DateComponent {
711
708
  let dateSelectionSegsByRow = this.splitDateSelectionSegs(props.dateSelectionSegs, rowCnt);
712
709
  let eventDragByRow = this.splitEventDrag(props.eventDrag, rowCnt);
713
710
  let eventResizeByRow = this.splitEventResize(props.eventResize, rowCnt);
714
- let limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
715
- // if rows can't expand to fill fixed height, can't do balanced-height event limit
716
- // TODO: best place to normalize these options?
717
- if (limitViaBalanced && !expandRows) {
718
- limitViaBalanced = false;
719
- dayMaxEventRows = null;
720
- dayMaxEvents = null;
711
+ // for DayGrid view with many rows, force a min-height on cells so doesn't appear squished
712
+ // choose 7 because a month view will have max 6 rows
713
+ let cellMinHeight = (rowCnt >= 7 && props.clientWidth) ?
714
+ props.clientWidth / context.options.aspectRatio / 6 :
715
+ null;
716
+ return (preact.createElement(internal.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact.createElement(preact.Fragment, null, props.cells.map((cells, row) => (preact.createElement(TableRow, { ref: this.rowRefs.createRef(row), key: cells.length
717
+ ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
718
+ : row // in case there are no cells (like when resource view is loading)
719
+ , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: props.dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: props.dayMaxEvents, dayMaxEventRows: props.dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, cellMinHeight: cellMinHeight, forPrint: props.forPrint })))))));
720
+ }
721
+ componentDidMount() {
722
+ // HACK: need a daygrid wrapper parent to do positioning
723
+ // NOTE: a daygrid resource view w/o resources can have zero cells
724
+ const firstCellEl = this.rowRefs.currentMap[0].getCellEls()[0];
725
+ this.rootEl = firstCellEl ? firstCellEl.closest('.fc-daygrid-body') : null;
726
+ if (this.rootEl) {
727
+ this.context.registerInteractiveComponent(this, {
728
+ el: this.rootEl,
729
+ isHitComboAllowed: this.props.isHitComboAllowed,
730
+ });
731
+ }
732
+ }
733
+ componentWillUnmount() {
734
+ if (this.rootEl) {
735
+ this.context.unregisterInteractiveComponent(this);
736
+ this.rootEl = null;
721
737
  }
722
- let classNames = [
723
- 'fc-daygrid-body',
724
- limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
725
- expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
726
- ];
727
- return (preact.createElement("div", { className: classNames.join(' '), ref: this.handleRootEl, style: {
728
- // these props are important to give this wrapper correct dimensions for interactions
729
- // TODO: if we set it here, can we avoid giving to inner tables?
730
- width: props.clientWidth,
731
- minWidth: props.tableMinWidth,
732
- } },
733
- preact.createElement(internal.NowTimer, { unit: "day" }, (nowDate, todayRange) => (preact.createElement(preact.Fragment, null,
734
- preact.createElement("table", { role: "presentation", className: "fc-scrollgrid-sync-table", style: {
735
- width: props.clientWidth,
736
- minWidth: props.tableMinWidth,
737
- height: expandRows ? props.clientHeight : '',
738
- } },
739
- props.colGroupNode,
740
- preact.createElement("tbody", { role: "presentation" }, props.cells.map((cells, row) => (preact.createElement(TableRow, { ref: this.rowRefs.createRef(row), key: cells.length
741
- ? cells[0].date.toISOString() /* best? or put key on cell? or use diff formatter? */
742
- : row // in case there are no cells (like when resource view is loading)
743
- , showDayNumbers: rowCnt > 1, showWeekNumbers: props.showWeekNumbers, todayRange: todayRange, dateProfile: dateProfile, cells: cells, renderIntro: props.renderRowIntro, businessHourSegs: businessHourSegsByRow[row], eventSelection: props.eventSelection, bgEventSegs: bgEventSegsByRow[row].filter(isSegAllDay) /* hack */, fgEventSegs: fgEventSegsByRow[row], dateSelectionSegs: dateSelectionSegsByRow[row], eventDrag: eventDragByRow[row], eventResize: eventResizeByRow[row], dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, clientWidth: props.clientWidth, clientHeight: props.clientHeight, forPrint: props.forPrint }))))))))));
744
738
  }
745
739
  // Hit System
746
740
  // ----------------------------------------------------------------------------------------------------
@@ -785,6 +779,87 @@ function isSegAllDay(seg) {
785
779
  return seg.eventRange.def.allDay;
786
780
  }
787
781
 
782
+ class Table extends internal.DateComponent {
783
+ constructor() {
784
+ super(...arguments);
785
+ this.elRef = preact.createRef();
786
+ this.needsScrollReset = false;
787
+ }
788
+ render() {
789
+ let { props } = this;
790
+ let { dayMaxEventRows, dayMaxEvents, expandRows } = props;
791
+ let limitViaBalanced = dayMaxEvents === true || dayMaxEventRows === true;
792
+ // if rows can't expand to fill fixed height, can't do balanced-height event limit
793
+ // TODO: best place to normalize these options?
794
+ if (limitViaBalanced && !expandRows) {
795
+ limitViaBalanced = false;
796
+ dayMaxEventRows = null;
797
+ dayMaxEvents = null;
798
+ }
799
+ let classNames = [
800
+ 'fc-daygrid-body',
801
+ limitViaBalanced ? 'fc-daygrid-body-balanced' : 'fc-daygrid-body-unbalanced',
802
+ expandRows ? '' : 'fc-daygrid-body-natural', // will height of one row depend on the others?
803
+ ];
804
+ return (preact.createElement("div", { ref: this.elRef, className: classNames.join(' '), style: {
805
+ // these props are important to give this wrapper correct dimensions for interactions
806
+ // TODO: if we set it here, can we avoid giving to inner tables?
807
+ width: props.clientWidth,
808
+ minWidth: props.tableMinWidth,
809
+ } },
810
+ preact.createElement("table", { role: "presentation", className: "fc-scrollgrid-sync-table", style: {
811
+ width: props.clientWidth,
812
+ minWidth: props.tableMinWidth,
813
+ height: expandRows ? props.clientHeight : '',
814
+ } },
815
+ props.colGroupNode,
816
+ preact.createElement("tbody", { role: "presentation" },
817
+ preact.createElement(TableRows, { dateProfile: props.dateProfile, cells: props.cells, renderRowIntro: props.renderRowIntro, showWeekNumbers: props.showWeekNumbers, clientWidth: props.clientWidth, clientHeight: props.clientHeight, businessHourSegs: props.businessHourSegs, bgEventSegs: props.bgEventSegs, fgEventSegs: props.fgEventSegs, dateSelectionSegs: props.dateSelectionSegs, eventSelection: props.eventSelection, eventDrag: props.eventDrag, eventResize: props.eventResize, dayMaxEvents: dayMaxEvents, dayMaxEventRows: dayMaxEventRows, forPrint: props.forPrint, isHitComboAllowed: props.isHitComboAllowed })))));
818
+ }
819
+ componentDidMount() {
820
+ this.requestScrollReset();
821
+ }
822
+ componentDidUpdate(prevProps) {
823
+ if (prevProps.dateProfile !== this.props.dateProfile) {
824
+ this.requestScrollReset();
825
+ }
826
+ else {
827
+ this.flushScrollReset();
828
+ }
829
+ }
830
+ requestScrollReset() {
831
+ this.needsScrollReset = true;
832
+ this.flushScrollReset();
833
+ }
834
+ flushScrollReset() {
835
+ if (this.needsScrollReset &&
836
+ this.props.clientWidth // sizes computed?
837
+ ) {
838
+ const subjectEl = getScrollSubjectEl(this.elRef.current, this.props.dateProfile);
839
+ if (subjectEl) {
840
+ const originEl = subjectEl.closest('.fc-daygrid-body');
841
+ const scrollEl = originEl.closest('.fc-scroller');
842
+ const scrollTop = subjectEl.getBoundingClientRect().top -
843
+ originEl.getBoundingClientRect().top;
844
+ scrollEl.scrollTop = scrollTop ? (scrollTop + 1) : 0; // overcome border
845
+ }
846
+ this.needsScrollReset = false;
847
+ }
848
+ }
849
+ }
850
+ function getScrollSubjectEl(containerEl, dateProfile) {
851
+ let el;
852
+ if (dateProfile.currentRangeUnit.match(/year|month/)) {
853
+ el = containerEl.querySelector(`[data-date="${internal.formatIsoMonthStr(dateProfile.currentDate)}-01"]`);
854
+ // even if view is month-based, first-of-month might be hidden...
855
+ }
856
+ if (!el) {
857
+ el = containerEl.querySelector(`[data-date="${internal.formatDayString(dateProfile.currentDate)}"]`);
858
+ // could still be hidden if an interior-view hidden day
859
+ }
860
+ return el;
861
+ }
862
+
788
863
  class DayTableSlicer extends internal.Slicer {
789
864
  constructor() {
790
865
  super(...arguments);
@@ -813,6 +888,7 @@ class DayTableView extends TableView {
813
888
  this.buildDayTableModel = internal.memoize(buildDayTableModel);
814
889
  this.headerRef = preact.createRef();
815
890
  this.tableRef = preact.createRef();
891
+ // can't override any lifecycle methods from parent
816
892
  }
817
893
  render() {
818
894
  let { options, dateProfileGenerator } = this.context;
@@ -830,9 +906,50 @@ function buildDayTableModel(dateProfile, dateProfileGenerator) {
830
906
  return new internal.DayTableModel(daySeries, /year|month|week/.test(dateProfile.currentRangeUnit));
831
907
  }
832
908
 
909
+ class TableDateProfileGenerator extends internal.DateProfileGenerator {
910
+ // Computes the date range that will be rendered
911
+ buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay) {
912
+ let renderRange = super.buildRenderRange(currentRange, currentRangeUnit, isRangeAllDay);
913
+ let { props } = this;
914
+ return buildDayTableRenderRange({
915
+ currentRange: renderRange,
916
+ snapToWeek: /^(year|month)$/.test(currentRangeUnit),
917
+ fixedWeekCount: props.fixedWeekCount,
918
+ dateEnv: props.dateEnv,
919
+ });
920
+ }
921
+ }
922
+ function buildDayTableRenderRange(props) {
923
+ let { dateEnv, currentRange } = props;
924
+ let { start, end } = currentRange;
925
+ let endOfWeek;
926
+ // year and month views should be aligned with weeks. this is already done for week
927
+ if (props.snapToWeek) {
928
+ start = dateEnv.startOfWeek(start);
929
+ // make end-of-week if not already
930
+ endOfWeek = dateEnv.startOfWeek(end);
931
+ if (endOfWeek.valueOf() !== end.valueOf()) {
932
+ end = internal.addWeeks(endOfWeek, 1);
933
+ }
934
+ }
935
+ // ensure 6 weeks
936
+ if (props.fixedWeekCount) {
937
+ // TODO: instead of these date-math gymnastics (for multimonth view),
938
+ // compute dateprofiles of all months, then use start of first and end of last.
939
+ let lastMonthRenderStart = dateEnv.startOfWeek(dateEnv.startOfMonth(internal.addDays(currentRange.end, -1)));
940
+ let rowCnt = Math.ceil(// could be partial weeks due to hiddenDays
941
+ internal.diffWeeks(lastMonthRenderStart, end));
942
+ end = internal.addWeeks(end, 6 - rowCnt);
943
+ }
944
+ return { start, end };
945
+ }
946
+
833
947
  exports.DayGridView = DayTableView;
834
948
  exports.DayTable = DayTable;
835
949
  exports.DayTableSlicer = DayTableSlicer;
836
950
  exports.Table = Table;
951
+ exports.TableDateProfileGenerator = TableDateProfileGenerator;
952
+ exports.TableRows = TableRows;
837
953
  exports.TableView = TableView;
838
954
  exports.buildDayTableModel = buildDayTableModel;
955
+ exports.buildDayTableRenderRange = buildDayTableRenderRange;