@fullcalendar/scrollgrid 7.0.0-beta.0 → 7.0.0-beta.1

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 CHANGED
@@ -13,7 +13,7 @@ var premiumCommonPlugin__default = /*#__PURE__*/_interopDefaultLegacy(premiumCom
13
13
 
14
14
  var index = index_cjs.createPlugin({
15
15
  name: '@fullcalendar/scrollgrid',
16
- premiumReleaseDate: '2024-10-01',
16
+ premiumReleaseDate: '2024-10-09',
17
17
  deps: [premiumCommonPlugin__default["default"]],
18
18
  scrollerSyncerClass: internalCommon.ScrollerSyncer
19
19
  });
package/index.global.js CHANGED
@@ -1,5 +1,5 @@
1
1
  /*!
2
- FullCalendar ScrollGrid Plugin v7.0.0-beta.0
2
+ FullCalendar ScrollGrid Plugin v7.0.0-beta.1
3
3
  Docs & License: https://fullcalendar.io/docs/premium
4
4
  (c) 2024 Adam Shaw
5
5
  */
@@ -10,160 +10,74 @@ FullCalendar.ScrollGrid = (function (exports, core, premiumCommonPlugin, interna
10
10
 
11
11
  var premiumCommonPlugin__default = /*#__PURE__*/_interopDefault(premiumCommonPlugin);
12
12
 
13
- const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
14
13
  /*
15
- ALSO, with the ability to disable touch
14
+ Fires:
15
+ - scrollEnd: (x, y) => void
16
16
  */
17
- class ScrollListener {
18
- constructor(el) {
19
- this.el = el;
20
- this.emitter = new internal$1.Emitter();
21
- this.isScrolling = false;
22
- this.isTouching = false; // user currently has finger down?
23
- this.isRecentlyWheeled = false;
24
- this.isRecentlyScrolled = false;
25
- this.wheelWaiter = new internal$1.DelayedRunner(this._handleWheelWaited.bind(this));
26
- this.scrollWaiter = new internal$1.DelayedRunner(this._handleScrollWaited.bind(this));
27
- // Handlers
28
- // ----------------------------------------------------------------------------------------------
29
- this.handleScroll = () => {
30
- this.startScroll();
31
- this.emitter.trigger('scroll', this.isRecentlyWheeled, this.isTouching);
32
- this.isRecentlyScrolled = true;
33
- this.scrollWaiter.request(500);
34
- };
35
- // will fire *before* the scroll event is fired (might not cause a scroll)
36
- this.handleWheel = () => {
37
- this.isRecentlyWheeled = true;
38
- this.wheelWaiter.request(500);
39
- };
40
- // will fire *before* the scroll event is fired (might not cause a scroll)
41
- this.handleTouchStart = () => {
42
- this.isTouching = true;
43
- };
44
- this.handleTouchEnd = () => {
45
- this.isTouching = false;
46
- // if the user ended their touch, and the scroll area wasn't moving,
47
- // we consider this to be the end of the scroll.
48
- if (!this.isRecentlyScrolled) {
49
- this.endScroll(); // won't fire if already ended
50
- }
51
- };
52
- el.addEventListener('scroll', this.handleScroll);
53
- el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
54
- el.addEventListener('touchend', this.handleTouchEnd);
55
- for (let eventName of WHEEL_EVENT_NAMES) {
56
- el.addEventListener(eventName, this.handleWheel, { passive: true });
57
- }
58
- }
59
- destroy() {
60
- let { el } = this;
61
- el.removeEventListener('scroll', this.handleScroll);
62
- el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
63
- el.removeEventListener('touchend', this.handleTouchEnd);
64
- for (let eventName of WHEEL_EVENT_NAMES) {
65
- el.removeEventListener(eventName, this.handleWheel, { passive: true });
66
- }
67
- }
68
- // Start / Stop
69
- // ----------------------------------------------------------------------------------------------
70
- startScroll() {
71
- if (!this.isScrolling) {
72
- this.isScrolling = true;
73
- this.emitter.trigger('scrollStart', this.isRecentlyWheeled, this.isTouching);
74
- }
75
- }
76
- endScroll() {
77
- if (this.isScrolling) {
78
- this.emitter.trigger('scrollEnd');
79
- this.isScrolling = false;
80
- this.isRecentlyScrolled = true;
81
- this.isRecentlyWheeled = false;
82
- this.scrollWaiter.clear();
83
- this.wheelWaiter.clear();
84
- }
85
- }
86
- _handleScrollWaited() {
87
- this.isRecentlyScrolled = false;
88
- // only end the scroll if not currently touching.
89
- // if touching, the scrolling will end later, on touchend.
90
- if (!this.isTouching) {
91
- this.endScroll(); // won't fire if already ended
92
- }
93
- }
94
- _handleWheelWaited() {
95
- this.isRecentlyWheeled = false;
96
- }
97
- }
98
-
99
17
  class ScrollerSyncer {
100
18
  constructor(isHorizontal = false) {
101
19
  this.isHorizontal = isHorizontal;
102
- this.scrollers = []; // TODO: move away from requiring Scroller
103
- this.scrollListeners = [];
20
+ this.emitter = new internal$1.Emitter();
21
+ this.scrollers = [];
22
+ this.destroyFuncs = [];
104
23
  this.isPaused = false;
105
24
  }
106
- handleChildren(scrollers, isRtl) {
25
+ handleChildren(scrollers) {
107
26
  if (!internal$1.isArraysEqual(this.scrollers, scrollers)) {
108
27
  this.destroy();
109
- this.scrollers = scrollers;
110
- const scrollListeners = [];
111
28
  for (const scroller of scrollers) {
112
29
  if (scroller) { // could be null
113
- scrollListeners.push(this.bindScroller(scroller.el));
30
+ this.destroyFuncs.push(this.bindScroller(scroller));
31
+ this.scrollers.push(scroller);
114
32
  }
115
33
  }
116
- this.scrollListeners = scrollListeners;
117
34
  }
118
- this.isRtl = isRtl;
119
35
  }
120
36
  destroy() {
121
- for (let scrollListener of this.scrollListeners) {
122
- scrollListener.destroy();
37
+ for (let destroyFunc of this.destroyFuncs) {
38
+ destroyFunc();
123
39
  }
40
+ this.destroyFuncs = [];
41
+ this.scrollers = [];
124
42
  }
125
43
  get x() {
126
- const { scrollListeners, masterEl, isRtl } = this;
127
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
128
- return internal$1.getNormalizedScrollX(el, isRtl);
44
+ const { scrollers, masterScroller } = this;
45
+ return (masterScroller || scrollers[0]).x;
129
46
  }
130
47
  get y() {
131
- const { scrollListeners, masterEl } = this;
132
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
133
- return el.scrollTop;
48
+ const { scrollers, masterScroller } = this;
49
+ return (masterScroller || scrollers[0]).y;
134
50
  }
135
- scrollTo({ x, y }) {
51
+ scrollTo(scrollArg) {
136
52
  this.isPaused = true;
137
- const { scrollListeners, isRtl } = this;
138
- if (y != null) {
139
- for (let scrollListener of scrollListeners) {
140
- scrollListener.el.scrollTop = y;
141
- }
142
- }
143
- if (x != null) {
144
- for (let scrollListener of scrollListeners) {
145
- internal$1.setNormalizedScrollX(scrollListener.el, isRtl, x);
146
- }
53
+ const { scrollers } = this;
54
+ for (let scroller of scrollers) {
55
+ scroller.scrollTo(scrollArg);
147
56
  }
148
57
  this.isPaused = false;
149
58
  }
150
- bindScroller(el) {
59
+ addScrollEndListener(handler) {
60
+ this.emitter.on('scrollEnd', handler);
61
+ }
62
+ removeScrollEndListener(handler) {
63
+ this.emitter.off('scrollEnd', handler);
64
+ }
65
+ bindScroller(scroller) {
151
66
  let { isHorizontal } = this;
152
- let scrollListener = new ScrollListener(el);
153
67
  const onScroll = (isWheel, isTouch) => {
154
68
  if (!this.isPaused) {
155
- if (!this.masterEl || (this.masterEl !== el && (isWheel || isTouch))) {
156
- this.assignMaster(el);
69
+ if (!this.masterScroller || (this.masterScroller !== scroller && (isWheel || isTouch))) {
70
+ this.assignMaster(scroller);
157
71
  }
158
- if (this.masterEl === el) { // dealing with current
159
- for (let scrollListener of this.scrollListeners) {
160
- const otherEl = scrollListener.el;
161
- if (otherEl !== el) {
72
+ if (this.masterScroller === scroller) { // dealing with current
73
+ for (let otherScroller of this.scrollers) {
74
+ if (otherScroller !== scroller) {
162
75
  if (isHorizontal) {
163
- otherEl.scrollLeft = el.scrollLeft;
76
+ // TODO: user raw scrollLeft for better performance. No normalization necessary
77
+ otherScroller.scrollTo({ x: scroller.x });
164
78
  }
165
79
  else {
166
- otherEl.scrollTop = el.scrollTop;
80
+ otherScroller.scrollTo({ y: scroller.y });
167
81
  }
168
82
  }
169
83
  }
@@ -171,19 +85,23 @@ FullCalendar.ScrollGrid = (function (exports, core, premiumCommonPlugin, interna
171
85
  }
172
86
  };
173
87
  const onScrollEnd = () => {
174
- if (this.masterEl === el) {
175
- this.masterEl = null;
88
+ if (this.masterScroller === scroller) {
89
+ this.masterScroller = null;
90
+ this.emitter.trigger('scrollEnd', this.x, this.y);
176
91
  }
177
92
  };
178
- scrollListener.emitter.on('scroll', onScroll);
179
- scrollListener.emitter.on('scrollEnd', onScrollEnd);
180
- return scrollListener;
93
+ scroller.listener.emitter.on('scroll', onScroll);
94
+ scroller.listener.emitter.on('scrollEnd', onScrollEnd);
95
+ return () => {
96
+ scroller.listener.emitter.off('scroll', onScroll);
97
+ scroller.listener.emitter.off('scrollEnd', onScrollEnd);
98
+ };
181
99
  }
182
- assignMaster(el) {
183
- this.masterEl = el;
184
- for (let scrollListener of this.scrollListeners) {
185
- if (scrollListener.el !== el) {
186
- scrollListener.endScroll(); // to prevent residual scrolls from reclaiming master
100
+ assignMaster(masterScroller) {
101
+ this.masterScroller = masterScroller;
102
+ for (let scroller of this.scrollers) {
103
+ if (scroller !== masterScroller) {
104
+ scroller.endScroll(); // to prevent residual scrolls from reclaiming master
187
105
  }
188
106
  }
189
107
  }
@@ -191,7 +109,7 @@ FullCalendar.ScrollGrid = (function (exports, core, premiumCommonPlugin, interna
191
109
 
192
110
  var plugin = core.createPlugin({
193
111
  name: '@fullcalendar/scrollgrid',
194
- premiumReleaseDate: '2024-10-01',
112
+ premiumReleaseDate: '2024-10-09',
195
113
  deps: [premiumCommonPlugin__default["default"]],
196
114
  scrollerSyncerClass: ScrollerSyncer
197
115
  });
@@ -1,6 +1,6 @@
1
1
  /*!
2
- FullCalendar ScrollGrid Plugin v7.0.0-beta.0
2
+ FullCalendar ScrollGrid Plugin v7.0.0-beta.1
3
3
  Docs & License: https://fullcalendar.io/docs/premium
4
4
  (c) 2024 Adam Shaw
5
5
  */
6
- FullCalendar.ScrollGrid=function(e,l,s,t){"use strict";function i(e){return e&&e.__esModule?e:{default:e}}var r=i(s);const o="wheel mousewheel DomMouseScroll MozMousePixelScroll".split(" ");class h{constructor(e){this.el=e,this.emitter=new t.Emitter,this.isScrolling=!1,this.isTouching=!1,this.isRecentlyWheeled=!1,this.isRecentlyScrolled=!1,this.wheelWaiter=new t.DelayedRunner(this._handleWheelWaited.bind(this)),this.scrollWaiter=new t.DelayedRunner(this._handleScrollWaited.bind(this)),this.handleScroll=()=>{this.startScroll(),this.emitter.trigger("scroll",this.isRecentlyWheeled,this.isTouching),this.isRecentlyScrolled=!0,this.scrollWaiter.request(500)},this.handleWheel=()=>{this.isRecentlyWheeled=!0,this.wheelWaiter.request(500)},this.handleTouchStart=()=>{this.isTouching=!0},this.handleTouchEnd=()=>{this.isTouching=!1,this.isRecentlyScrolled||this.endScroll()},e.addEventListener("scroll",this.handleScroll),e.addEventListener("touchstart",this.handleTouchStart,{passive:!0}),e.addEventListener("touchend",this.handleTouchEnd);for(let l of o)e.addEventListener(l,this.handleWheel,{passive:!0})}destroy(){let{el:e}=this;e.removeEventListener("scroll",this.handleScroll),e.removeEventListener("touchstart",this.handleTouchStart,{passive:!0}),e.removeEventListener("touchend",this.handleTouchEnd);for(let l of o)e.removeEventListener(l,this.handleWheel,{passive:!0})}startScroll(){this.isScrolling||(this.isScrolling=!0,this.emitter.trigger("scrollStart",this.isRecentlyWheeled,this.isTouching))}endScroll(){this.isScrolling&&(this.emitter.trigger("scrollEnd"),this.isScrolling=!1,this.isRecentlyScrolled=!0,this.isRecentlyWheeled=!1,this.scrollWaiter.clear(),this.wheelWaiter.clear())}_handleScrollWaited(){this.isRecentlyScrolled=!1,this.isTouching||this.endScroll()}_handleWheelWaited(){this.isRecentlyWheeled=!1}}class n{constructor(e=!1){this.isHorizontal=e,this.scrollers=[],this.scrollListeners=[],this.isPaused=!1}handleChildren(e,l){if(!t.isArraysEqual(this.scrollers,e)){this.destroy(),this.scrollers=e;const l=[];for(const s of e)s&&l.push(this.bindScroller(s.el));this.scrollListeners=l}this.isRtl=l}destroy(){for(let e of this.scrollListeners)e.destroy()}get x(){const{scrollListeners:e,masterEl:l,isRtl:s}=this,i=l||(e.length?e[0].el:void 0);return t.getNormalizedScrollX(i,s)}get y(){const{scrollListeners:e,masterEl:l}=this;return(l||(e.length?e[0].el:void 0)).scrollTop}scrollTo({x:e,y:l}){this.isPaused=!0;const{scrollListeners:s,isRtl:i}=this;if(null!=l)for(let e of s)e.el.scrollTop=l;if(null!=e)for(let l of s)t.setNormalizedScrollX(l.el,i,e);this.isPaused=!1}bindScroller(e){let{isHorizontal:l}=this,s=new h(e);return s.emitter.on("scroll",(s,t)=>{if(!this.isPaused&&((!this.masterEl||this.masterEl!==e&&(s||t))&&this.assignMaster(e),this.masterEl===e))for(let s of this.scrollListeners){const t=s.el;t!==e&&(l?t.scrollLeft=e.scrollLeft:t.scrollTop=e.scrollTop)}}),s.emitter.on("scrollEnd",()=>{this.masterEl===e&&(this.masterEl=null)}),s}assignMaster(e){this.masterEl=e;for(let l of this.scrollListeners)l.el!==e&&l.endScroll()}}var c=l.createPlugin({name:"@fullcalendar/scrollgrid",premiumReleaseDate:"2024-10-01",deps:[r.default],scrollerSyncerClass:n}),a={__proto__:null,ScrollerSyncer:n};return l.globalPlugins.push(c),e.Internal=a,e.default=c,Object.defineProperty(e,"__esModule",{value:!0}),e}({},FullCalendar,FullCalendar.PremiumCommon,FullCalendar.Internal);
6
+ FullCalendar.ScrollGrid=function(r,s,l,e){"use strict";function t(r){return r&&r.__esModule?r:{default:r}}var o=t(l);class i{constructor(r=!1){this.isHorizontal=r,this.emitter=new e.Emitter,this.scrollers=[],this.destroyFuncs=[],this.isPaused=!1}handleChildren(r){if(!e.isArraysEqual(this.scrollers,r)){this.destroy();for(const s of r)s&&(this.destroyFuncs.push(this.bindScroller(s)),this.scrollers.push(s))}}destroy(){for(let r of this.destroyFuncs)r();this.destroyFuncs=[],this.scrollers=[]}get x(){const{scrollers:r,masterScroller:s}=this;return(s||r[0]).x}get y(){const{scrollers:r,masterScroller:s}=this;return(s||r[0]).y}scrollTo(r){this.isPaused=!0;const{scrollers:s}=this;for(let l of s)l.scrollTo(r);this.isPaused=!1}addScrollEndListener(r){this.emitter.on("scrollEnd",r)}removeScrollEndListener(r){this.emitter.off("scrollEnd",r)}bindScroller(r){let{isHorizontal:s}=this;const l=(l,e)=>{if(!this.isPaused&&((!this.masterScroller||this.masterScroller!==r&&(l||e))&&this.assignMaster(r),this.masterScroller===r))for(let l of this.scrollers)l!==r&&(s?l.scrollTo({x:r.x}):l.scrollTo({y:r.y}))},e=()=>{this.masterScroller===r&&(this.masterScroller=null,this.emitter.trigger("scrollEnd",this.x,this.y))};return r.listener.emitter.on("scroll",l),r.listener.emitter.on("scrollEnd",e),()=>{r.listener.emitter.off("scroll",l),r.listener.emitter.off("scrollEnd",e)}}assignMaster(r){this.masterScroller=r;for(let s of this.scrollers)s!==r&&s.endScroll()}}var n=s.createPlugin({name:"@fullcalendar/scrollgrid",premiumReleaseDate:"2024-10-09",deps:[o.default],scrollerSyncerClass:i}),c={__proto__:null,ScrollerSyncer:i};return s.globalPlugins.push(n),r.Internal=c,r.default=n,Object.defineProperty(r,"__esModule",{value:!0}),r}({},FullCalendar,FullCalendar.PremiumCommon,FullCalendar.Internal);
package/index.js CHANGED
@@ -5,7 +5,7 @@ import '@fullcalendar/core/internal.js';
5
5
 
6
6
  var index = createPlugin({
7
7
  name: '@fullcalendar/scrollgrid',
8
- premiumReleaseDate: '2024-10-01',
8
+ premiumReleaseDate: '2024-10-09',
9
9
  deps: [premiumCommonPlugin],
10
10
  scrollerSyncerClass: ScrollerSyncer
11
11
  });
package/internal.cjs CHANGED
@@ -4,160 +4,74 @@ Object.defineProperty(exports, '__esModule', { value: true });
4
4
 
5
5
  var internal_cjs = require('@fullcalendar/core/internal.cjs');
6
6
 
7
- const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
8
7
  /*
9
- ALSO, with the ability to disable touch
8
+ Fires:
9
+ - scrollEnd: (x, y) => void
10
10
  */
11
- class ScrollListener {
12
- constructor(el) {
13
- this.el = el;
14
- this.emitter = new internal_cjs.Emitter();
15
- this.isScrolling = false;
16
- this.isTouching = false; // user currently has finger down?
17
- this.isRecentlyWheeled = false;
18
- this.isRecentlyScrolled = false;
19
- this.wheelWaiter = new internal_cjs.DelayedRunner(this._handleWheelWaited.bind(this));
20
- this.scrollWaiter = new internal_cjs.DelayedRunner(this._handleScrollWaited.bind(this));
21
- // Handlers
22
- // ----------------------------------------------------------------------------------------------
23
- this.handleScroll = () => {
24
- this.startScroll();
25
- this.emitter.trigger('scroll', this.isRecentlyWheeled, this.isTouching);
26
- this.isRecentlyScrolled = true;
27
- this.scrollWaiter.request(500);
28
- };
29
- // will fire *before* the scroll event is fired (might not cause a scroll)
30
- this.handleWheel = () => {
31
- this.isRecentlyWheeled = true;
32
- this.wheelWaiter.request(500);
33
- };
34
- // will fire *before* the scroll event is fired (might not cause a scroll)
35
- this.handleTouchStart = () => {
36
- this.isTouching = true;
37
- };
38
- this.handleTouchEnd = () => {
39
- this.isTouching = false;
40
- // if the user ended their touch, and the scroll area wasn't moving,
41
- // we consider this to be the end of the scroll.
42
- if (!this.isRecentlyScrolled) {
43
- this.endScroll(); // won't fire if already ended
44
- }
45
- };
46
- el.addEventListener('scroll', this.handleScroll);
47
- el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
48
- el.addEventListener('touchend', this.handleTouchEnd);
49
- for (let eventName of WHEEL_EVENT_NAMES) {
50
- el.addEventListener(eventName, this.handleWheel, { passive: true });
51
- }
52
- }
53
- destroy() {
54
- let { el } = this;
55
- el.removeEventListener('scroll', this.handleScroll);
56
- el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
57
- el.removeEventListener('touchend', this.handleTouchEnd);
58
- for (let eventName of WHEEL_EVENT_NAMES) {
59
- el.removeEventListener(eventName, this.handleWheel, { passive: true });
60
- }
61
- }
62
- // Start / Stop
63
- // ----------------------------------------------------------------------------------------------
64
- startScroll() {
65
- if (!this.isScrolling) {
66
- this.isScrolling = true;
67
- this.emitter.trigger('scrollStart', this.isRecentlyWheeled, this.isTouching);
68
- }
69
- }
70
- endScroll() {
71
- if (this.isScrolling) {
72
- this.emitter.trigger('scrollEnd');
73
- this.isScrolling = false;
74
- this.isRecentlyScrolled = true;
75
- this.isRecentlyWheeled = false;
76
- this.scrollWaiter.clear();
77
- this.wheelWaiter.clear();
78
- }
79
- }
80
- _handleScrollWaited() {
81
- this.isRecentlyScrolled = false;
82
- // only end the scroll if not currently touching.
83
- // if touching, the scrolling will end later, on touchend.
84
- if (!this.isTouching) {
85
- this.endScroll(); // won't fire if already ended
86
- }
87
- }
88
- _handleWheelWaited() {
89
- this.isRecentlyWheeled = false;
90
- }
91
- }
92
-
93
11
  class ScrollerSyncer {
94
12
  constructor(isHorizontal = false) {
95
13
  this.isHorizontal = isHorizontal;
96
- this.scrollers = []; // TODO: move away from requiring Scroller
97
- this.scrollListeners = [];
14
+ this.emitter = new internal_cjs.Emitter();
15
+ this.scrollers = [];
16
+ this.destroyFuncs = [];
98
17
  this.isPaused = false;
99
18
  }
100
- handleChildren(scrollers, isRtl) {
19
+ handleChildren(scrollers) {
101
20
  if (!internal_cjs.isArraysEqual(this.scrollers, scrollers)) {
102
21
  this.destroy();
103
- this.scrollers = scrollers;
104
- const scrollListeners = [];
105
22
  for (const scroller of scrollers) {
106
23
  if (scroller) { // could be null
107
- scrollListeners.push(this.bindScroller(scroller.el));
24
+ this.destroyFuncs.push(this.bindScroller(scroller));
25
+ this.scrollers.push(scroller);
108
26
  }
109
27
  }
110
- this.scrollListeners = scrollListeners;
111
28
  }
112
- this.isRtl = isRtl;
113
29
  }
114
30
  destroy() {
115
- for (let scrollListener of this.scrollListeners) {
116
- scrollListener.destroy();
31
+ for (let destroyFunc of this.destroyFuncs) {
32
+ destroyFunc();
117
33
  }
34
+ this.destroyFuncs = [];
35
+ this.scrollers = [];
118
36
  }
119
37
  get x() {
120
- const { scrollListeners, masterEl, isRtl } = this;
121
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
122
- return internal_cjs.getNormalizedScrollX(el, isRtl);
38
+ const { scrollers, masterScroller } = this;
39
+ return (masterScroller || scrollers[0]).x;
123
40
  }
124
41
  get y() {
125
- const { scrollListeners, masterEl } = this;
126
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
127
- return el.scrollTop;
42
+ const { scrollers, masterScroller } = this;
43
+ return (masterScroller || scrollers[0]).y;
128
44
  }
129
- scrollTo({ x, y }) {
45
+ scrollTo(scrollArg) {
130
46
  this.isPaused = true;
131
- const { scrollListeners, isRtl } = this;
132
- if (y != null) {
133
- for (let scrollListener of scrollListeners) {
134
- scrollListener.el.scrollTop = y;
135
- }
136
- }
137
- if (x != null) {
138
- for (let scrollListener of scrollListeners) {
139
- internal_cjs.setNormalizedScrollX(scrollListener.el, isRtl, x);
140
- }
47
+ const { scrollers } = this;
48
+ for (let scroller of scrollers) {
49
+ scroller.scrollTo(scrollArg);
141
50
  }
142
51
  this.isPaused = false;
143
52
  }
144
- bindScroller(el) {
53
+ addScrollEndListener(handler) {
54
+ this.emitter.on('scrollEnd', handler);
55
+ }
56
+ removeScrollEndListener(handler) {
57
+ this.emitter.off('scrollEnd', handler);
58
+ }
59
+ bindScroller(scroller) {
145
60
  let { isHorizontal } = this;
146
- let scrollListener = new ScrollListener(el);
147
61
  const onScroll = (isWheel, isTouch) => {
148
62
  if (!this.isPaused) {
149
- if (!this.masterEl || (this.masterEl !== el && (isWheel || isTouch))) {
150
- this.assignMaster(el);
63
+ if (!this.masterScroller || (this.masterScroller !== scroller && (isWheel || isTouch))) {
64
+ this.assignMaster(scroller);
151
65
  }
152
- if (this.masterEl === el) { // dealing with current
153
- for (let scrollListener of this.scrollListeners) {
154
- const otherEl = scrollListener.el;
155
- if (otherEl !== el) {
66
+ if (this.masterScroller === scroller) { // dealing with current
67
+ for (let otherScroller of this.scrollers) {
68
+ if (otherScroller !== scroller) {
156
69
  if (isHorizontal) {
157
- otherEl.scrollLeft = el.scrollLeft;
70
+ // TODO: user raw scrollLeft for better performance. No normalization necessary
71
+ otherScroller.scrollTo({ x: scroller.x });
158
72
  }
159
73
  else {
160
- otherEl.scrollTop = el.scrollTop;
74
+ otherScroller.scrollTo({ y: scroller.y });
161
75
  }
162
76
  }
163
77
  }
@@ -165,19 +79,23 @@ class ScrollerSyncer {
165
79
  }
166
80
  };
167
81
  const onScrollEnd = () => {
168
- if (this.masterEl === el) {
169
- this.masterEl = null;
82
+ if (this.masterScroller === scroller) {
83
+ this.masterScroller = null;
84
+ this.emitter.trigger('scrollEnd', this.x, this.y);
170
85
  }
171
86
  };
172
- scrollListener.emitter.on('scroll', onScroll);
173
- scrollListener.emitter.on('scrollEnd', onScrollEnd);
174
- return scrollListener;
87
+ scroller.listener.emitter.on('scroll', onScroll);
88
+ scroller.listener.emitter.on('scrollEnd', onScrollEnd);
89
+ return () => {
90
+ scroller.listener.emitter.off('scroll', onScroll);
91
+ scroller.listener.emitter.off('scrollEnd', onScrollEnd);
92
+ };
175
93
  }
176
- assignMaster(el) {
177
- this.masterEl = el;
178
- for (let scrollListener of this.scrollListeners) {
179
- if (scrollListener.el !== el) {
180
- scrollListener.endScroll(); // to prevent residual scrolls from reclaiming master
94
+ assignMaster(masterScroller) {
95
+ this.masterScroller = masterScroller;
96
+ for (let scroller of this.scrollers) {
97
+ if (scroller !== masterScroller) {
98
+ scroller.endScroll(); // to prevent residual scrolls from reclaiming master
181
99
  }
182
100
  }
183
101
  }
package/internal.d.ts CHANGED
@@ -1,44 +1,25 @@
1
- import { Emitter, ScrollerSyncerInterface, Scroller } from '@fullcalendar/core/internal';
2
-
3
- declare class ScrollListener {
4
- el: HTMLElement;
5
- emitter: Emitter<any>;
6
- private isScrolling;
7
- private isTouching;
8
- private isRecentlyWheeled;
9
- private isRecentlyScrolled;
10
- private wheelWaiter;
11
- private scrollWaiter;
12
- constructor(el: HTMLElement);
13
- destroy(): void;
14
- private startScroll;
15
- endScroll(): void;
16
- handleScroll: () => void;
17
- _handleScrollWaited(): void;
18
- handleWheel: () => void;
19
- _handleWheelWaited(): void;
20
- handleTouchStart: () => void;
21
- handleTouchEnd: () => void;
22
- }
1
+ import { ScrollerSyncerInterface, Scroller } from '@fullcalendar/core/internal';
23
2
 
24
3
  declare class ScrollerSyncer implements ScrollerSyncerInterface {
25
4
  private isHorizontal;
5
+ private emitter;
26
6
  private scrollers;
27
- private scrollListeners;
28
- private masterEl;
7
+ private destroyFuncs;
8
+ private masterScroller;
29
9
  private isPaused;
30
- private isRtl;
31
10
  constructor(isHorizontal?: boolean);
32
- handleChildren(scrollers: Scroller[], isRtl: boolean): void;
11
+ handleChildren(scrollers: Scroller[]): void;
33
12
  destroy(): void;
34
13
  get x(): number;
35
14
  get y(): number;
36
- scrollTo({ x, y }: {
15
+ scrollTo(scrollArg: {
37
16
  x?: number;
38
17
  y?: number;
39
18
  }): void;
40
- bindScroller(el: HTMLElement): ScrollListener;
41
- assignMaster(el: HTMLElement): void;
19
+ addScrollEndListener(handler: (x: number, y: number) => void): void;
20
+ removeScrollEndListener(handler: (x: number, y: number) => void): void;
21
+ bindScroller(scroller: Scroller): () => void;
22
+ assignMaster(masterScroller: Scroller): void;
42
23
  }
43
24
 
44
25
  export { ScrollerSyncer };
package/internal.js CHANGED
@@ -1,159 +1,73 @@
1
- import { Emitter, DelayedRunner, isArraysEqual, getNormalizedScrollX, setNormalizedScrollX } from '@fullcalendar/core/internal.js';
1
+ import { Emitter, isArraysEqual } from '@fullcalendar/core/internal.js';
2
2
 
3
- const WHEEL_EVENT_NAMES = 'wheel mousewheel DomMouseScroll MozMousePixelScroll'.split(' ');
4
3
  /*
5
- ALSO, with the ability to disable touch
4
+ Fires:
5
+ - scrollEnd: (x, y) => void
6
6
  */
7
- class ScrollListener {
8
- constructor(el) {
9
- this.el = el;
10
- this.emitter = new Emitter();
11
- this.isScrolling = false;
12
- this.isTouching = false; // user currently has finger down?
13
- this.isRecentlyWheeled = false;
14
- this.isRecentlyScrolled = false;
15
- this.wheelWaiter = new DelayedRunner(this._handleWheelWaited.bind(this));
16
- this.scrollWaiter = new DelayedRunner(this._handleScrollWaited.bind(this));
17
- // Handlers
18
- // ----------------------------------------------------------------------------------------------
19
- this.handleScroll = () => {
20
- this.startScroll();
21
- this.emitter.trigger('scroll', this.isRecentlyWheeled, this.isTouching);
22
- this.isRecentlyScrolled = true;
23
- this.scrollWaiter.request(500);
24
- };
25
- // will fire *before* the scroll event is fired (might not cause a scroll)
26
- this.handleWheel = () => {
27
- this.isRecentlyWheeled = true;
28
- this.wheelWaiter.request(500);
29
- };
30
- // will fire *before* the scroll event is fired (might not cause a scroll)
31
- this.handleTouchStart = () => {
32
- this.isTouching = true;
33
- };
34
- this.handleTouchEnd = () => {
35
- this.isTouching = false;
36
- // if the user ended their touch, and the scroll area wasn't moving,
37
- // we consider this to be the end of the scroll.
38
- if (!this.isRecentlyScrolled) {
39
- this.endScroll(); // won't fire if already ended
40
- }
41
- };
42
- el.addEventListener('scroll', this.handleScroll);
43
- el.addEventListener('touchstart', this.handleTouchStart, { passive: true });
44
- el.addEventListener('touchend', this.handleTouchEnd);
45
- for (let eventName of WHEEL_EVENT_NAMES) {
46
- el.addEventListener(eventName, this.handleWheel, { passive: true });
47
- }
48
- }
49
- destroy() {
50
- let { el } = this;
51
- el.removeEventListener('scroll', this.handleScroll);
52
- el.removeEventListener('touchstart', this.handleTouchStart, { passive: true });
53
- el.removeEventListener('touchend', this.handleTouchEnd);
54
- for (let eventName of WHEEL_EVENT_NAMES) {
55
- el.removeEventListener(eventName, this.handleWheel, { passive: true });
56
- }
57
- }
58
- // Start / Stop
59
- // ----------------------------------------------------------------------------------------------
60
- startScroll() {
61
- if (!this.isScrolling) {
62
- this.isScrolling = true;
63
- this.emitter.trigger('scrollStart', this.isRecentlyWheeled, this.isTouching);
64
- }
65
- }
66
- endScroll() {
67
- if (this.isScrolling) {
68
- this.emitter.trigger('scrollEnd');
69
- this.isScrolling = false;
70
- this.isRecentlyScrolled = true;
71
- this.isRecentlyWheeled = false;
72
- this.scrollWaiter.clear();
73
- this.wheelWaiter.clear();
74
- }
75
- }
76
- _handleScrollWaited() {
77
- this.isRecentlyScrolled = false;
78
- // only end the scroll if not currently touching.
79
- // if touching, the scrolling will end later, on touchend.
80
- if (!this.isTouching) {
81
- this.endScroll(); // won't fire if already ended
82
- }
83
- }
84
- _handleWheelWaited() {
85
- this.isRecentlyWheeled = false;
86
- }
87
- }
88
-
89
7
  class ScrollerSyncer {
90
8
  constructor(isHorizontal = false) {
91
9
  this.isHorizontal = isHorizontal;
92
- this.scrollers = []; // TODO: move away from requiring Scroller
93
- this.scrollListeners = [];
10
+ this.emitter = new Emitter();
11
+ this.scrollers = [];
12
+ this.destroyFuncs = [];
94
13
  this.isPaused = false;
95
14
  }
96
- handleChildren(scrollers, isRtl) {
15
+ handleChildren(scrollers) {
97
16
  if (!isArraysEqual(this.scrollers, scrollers)) {
98
17
  this.destroy();
99
- this.scrollers = scrollers;
100
- const scrollListeners = [];
101
18
  for (const scroller of scrollers) {
102
19
  if (scroller) { // could be null
103
- scrollListeners.push(this.bindScroller(scroller.el));
20
+ this.destroyFuncs.push(this.bindScroller(scroller));
21
+ this.scrollers.push(scroller);
104
22
  }
105
23
  }
106
- this.scrollListeners = scrollListeners;
107
24
  }
108
- this.isRtl = isRtl;
109
25
  }
110
26
  destroy() {
111
- for (let scrollListener of this.scrollListeners) {
112
- scrollListener.destroy();
27
+ for (let destroyFunc of this.destroyFuncs) {
28
+ destroyFunc();
113
29
  }
30
+ this.destroyFuncs = [];
31
+ this.scrollers = [];
114
32
  }
115
33
  get x() {
116
- const { scrollListeners, masterEl, isRtl } = this;
117
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
118
- return getNormalizedScrollX(el, isRtl);
34
+ const { scrollers, masterScroller } = this;
35
+ return (masterScroller || scrollers[0]).x;
119
36
  }
120
37
  get y() {
121
- const { scrollListeners, masterEl } = this;
122
- const el = masterEl || (scrollListeners.length ? scrollListeners[0].el : undefined);
123
- return el.scrollTop;
38
+ const { scrollers, masterScroller } = this;
39
+ return (masterScroller || scrollers[0]).y;
124
40
  }
125
- scrollTo({ x, y }) {
41
+ scrollTo(scrollArg) {
126
42
  this.isPaused = true;
127
- const { scrollListeners, isRtl } = this;
128
- if (y != null) {
129
- for (let scrollListener of scrollListeners) {
130
- scrollListener.el.scrollTop = y;
131
- }
132
- }
133
- if (x != null) {
134
- for (let scrollListener of scrollListeners) {
135
- setNormalizedScrollX(scrollListener.el, isRtl, x);
136
- }
43
+ const { scrollers } = this;
44
+ for (let scroller of scrollers) {
45
+ scroller.scrollTo(scrollArg);
137
46
  }
138
47
  this.isPaused = false;
139
48
  }
140
- bindScroller(el) {
49
+ addScrollEndListener(handler) {
50
+ this.emitter.on('scrollEnd', handler);
51
+ }
52
+ removeScrollEndListener(handler) {
53
+ this.emitter.off('scrollEnd', handler);
54
+ }
55
+ bindScroller(scroller) {
141
56
  let { isHorizontal } = this;
142
- let scrollListener = new ScrollListener(el);
143
57
  const onScroll = (isWheel, isTouch) => {
144
58
  if (!this.isPaused) {
145
- if (!this.masterEl || (this.masterEl !== el && (isWheel || isTouch))) {
146
- this.assignMaster(el);
59
+ if (!this.masterScroller || (this.masterScroller !== scroller && (isWheel || isTouch))) {
60
+ this.assignMaster(scroller);
147
61
  }
148
- if (this.masterEl === el) { // dealing with current
149
- for (let scrollListener of this.scrollListeners) {
150
- const otherEl = scrollListener.el;
151
- if (otherEl !== el) {
62
+ if (this.masterScroller === scroller) { // dealing with current
63
+ for (let otherScroller of this.scrollers) {
64
+ if (otherScroller !== scroller) {
152
65
  if (isHorizontal) {
153
- otherEl.scrollLeft = el.scrollLeft;
66
+ // TODO: user raw scrollLeft for better performance. No normalization necessary
67
+ otherScroller.scrollTo({ x: scroller.x });
154
68
  }
155
69
  else {
156
- otherEl.scrollTop = el.scrollTop;
70
+ otherScroller.scrollTo({ y: scroller.y });
157
71
  }
158
72
  }
159
73
  }
@@ -161,19 +75,23 @@ class ScrollerSyncer {
161
75
  }
162
76
  };
163
77
  const onScrollEnd = () => {
164
- if (this.masterEl === el) {
165
- this.masterEl = null;
78
+ if (this.masterScroller === scroller) {
79
+ this.masterScroller = null;
80
+ this.emitter.trigger('scrollEnd', this.x, this.y);
166
81
  }
167
82
  };
168
- scrollListener.emitter.on('scroll', onScroll);
169
- scrollListener.emitter.on('scrollEnd', onScrollEnd);
170
- return scrollListener;
83
+ scroller.listener.emitter.on('scroll', onScroll);
84
+ scroller.listener.emitter.on('scrollEnd', onScrollEnd);
85
+ return () => {
86
+ scroller.listener.emitter.off('scroll', onScroll);
87
+ scroller.listener.emitter.off('scrollEnd', onScrollEnd);
88
+ };
171
89
  }
172
- assignMaster(el) {
173
- this.masterEl = el;
174
- for (let scrollListener of this.scrollListeners) {
175
- if (scrollListener.el !== el) {
176
- scrollListener.endScroll(); // to prevent residual scrolls from reclaiming master
90
+ assignMaster(masterScroller) {
91
+ this.masterScroller = masterScroller;
92
+ for (let scroller of this.scrollers) {
93
+ if (scroller !== masterScroller) {
94
+ scroller.endScroll(); // to prevent residual scrolls from reclaiming master
177
95
  }
178
96
  }
179
97
  }
package/package.json CHANGED
@@ -1,13 +1,13 @@
1
1
  {
2
2
  "name": "@fullcalendar/scrollgrid",
3
- "version": "7.0.0-beta.0",
3
+ "version": "7.0.0-beta.1",
4
4
  "title": "FullCalendar ScrollGrid Plugin",
5
5
  "description": "Tabular data chunked into scrollable panes",
6
6
  "dependencies": {
7
- "@fullcalendar/premium-common": "7.0.0-beta.0"
7
+ "@fullcalendar/premium-common": "7.0.0-beta.1"
8
8
  },
9
9
  "peerDependencies": {
10
- "@fullcalendar/core": "7.0.0-beta.0"
10
+ "@fullcalendar/core": "7.0.0-beta.1"
11
11
  },
12
12
  "type": "module",
13
13
  "keywords": [