@egjs/flicking 4.5.0 → 4.6.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.
@@ -0,0 +1,133 @@
1
+ /*
2
+ * Copyright (c) 2015 NAVER Corp.
3
+ * egjs projects are licensed under the MIT license
4
+ */
5
+ import Component from "@egjs/component";
6
+
7
+ import { isString } from "../utils";
8
+
9
+ export interface ResizeWatherOptions {
10
+ resizeDebounce?: number;
11
+ maxResizeDebounce?: number;
12
+ useResizeObserver?: boolean;
13
+ useWindowResize?: boolean;
14
+ watchDirection?: "width" | "height" | "box" | false;
15
+ rectBox?: "border-box" | "content-box";
16
+ }
17
+
18
+ class ResizeWatcher {
19
+ private _container: HTMLElement;
20
+ private _rect = { width: 0, height: 0 };
21
+ private _resizeTimer = 0;
22
+ private _maxResizeDebounceTimer = 0;
23
+ private _emitter: Component<{ resize: void }>;
24
+ private _observer: ResizeObserver | null;
25
+ private _options!: Required<ResizeWatherOptions>;
26
+
27
+ public constructor(container: HTMLElement | string, options: ResizeWatherOptions = {}) {
28
+ this._options = {
29
+ resizeDebounce: 100,
30
+ maxResizeDebounce: 0,
31
+ useResizeObserver: false,
32
+ useWindowResize: true,
33
+ watchDirection: false,
34
+ rectBox: "content-box",
35
+ ...options
36
+ };
37
+
38
+ this._container = isString(container) ? document.querySelector<HTMLElement>(container)! : container;
39
+ this._init();
40
+ }
41
+
42
+ public getRect() {
43
+ return this._rect;
44
+ }
45
+
46
+ public setRect(rect: { width: number; height: number }) {
47
+ this._rect = { ...rect };
48
+ }
49
+
50
+ public resize() {
51
+ const container = this._container;
52
+
53
+ this.setRect(this._options.rectBox === "border-box" ? {
54
+ width: container.offsetWidth,
55
+ height: container.offsetHeight
56
+ } : {
57
+ width: container.clientWidth,
58
+ height: container.clientHeight
59
+ });
60
+ }
61
+
62
+ public listen(callback: () => void) {
63
+ this._emitter.on("resize", callback);
64
+ return this;
65
+ }
66
+
67
+ public destroy() {
68
+ this._observer?.disconnect();
69
+ if (this._options.useWindowResize) {
70
+ window.removeEventListener("reisze", this._onResize);
71
+ }
72
+ }
73
+
74
+ private _init() {
75
+ const container = this._container;
76
+ const options = this._options;
77
+
78
+ this._emitter = new Component();
79
+ if (options.useResizeObserver && !!window.ResizeObserver) {
80
+ this._observer = new window.ResizeObserver(this._scheduleResize);
81
+ this._observer.observe(container, {
82
+ box: options.rectBox
83
+ });
84
+ }
85
+ if (options.useWindowResize) {
86
+ window.addEventListener("resize", this._scheduleResize);
87
+ }
88
+ this.resize();
89
+ }
90
+
91
+ private _onResize = () => {
92
+ clearTimeout(this._resizeTimer);
93
+ clearTimeout(this._maxResizeDebounceTimer);
94
+
95
+ this._maxResizeDebounceTimer = 0;
96
+ this._resizeTimer = 0;
97
+
98
+ const watchDirection = this._options.watchDirection;
99
+ const prevRect = this._rect;
100
+ this.resize();
101
+ const rect = this._rect;
102
+ const isWatchWidth = watchDirection === "box" || watchDirection === "width";
103
+ const isWatchHeight = watchDirection === "box" || watchDirection === "height";
104
+ const isResize = !watchDirection
105
+ || (isWatchWidth && prevRect.width !== rect.width)
106
+ || (isWatchHeight && prevRect.height !== rect.height);
107
+
108
+ if (isResize) {
109
+ this._emitter.trigger("resize");
110
+ }
111
+ };
112
+
113
+ private _scheduleResize = () => {
114
+ const {
115
+ resizeDebounce,
116
+ maxResizeDebounce
117
+ } = this._options;
118
+
119
+
120
+ if (!this._maxResizeDebounceTimer && maxResizeDebounce >= resizeDebounce) {
121
+ this._maxResizeDebounceTimer = window.setTimeout(this._onResize, maxResizeDebounce);
122
+ }
123
+
124
+ if (this._resizeTimer) {
125
+ clearTimeout(this._resizeTimer);
126
+ this._resizeTimer = 0;
127
+ }
128
+
129
+ this._resizeTimer = window.setTimeout(this._onResize, resizeDebounce);
130
+ };
131
+ }
132
+
133
+ export default ResizeWatcher;
@@ -27,6 +27,7 @@ abstract class Renderer {
27
27
  // Internal States
28
28
  protected _flicking: Flicking | null;
29
29
  protected _panels: Panel[];
30
+ protected _rendering: boolean;
30
31
 
31
32
  // Options
32
33
  protected _align: NonNullable<RendererOptions["align"]>;
@@ -41,6 +42,14 @@ abstract class Renderer {
41
42
  * @see Panel
42
43
  */
43
44
  public get panels() { return this._panels; }
45
+ /**
46
+ * A boolean value indicating whether rendering is in progress
47
+ * @ko 현재 렌더링이 시작되어 끝나기 전까지의 상태인지의 여부
48
+ * @type {boolean}
49
+ * @readonly
50
+ * @internal
51
+ */
52
+ public get rendering() { return this._rendering; }
44
53
  /**
45
54
  * Count of panels
46
55
  * @ko 전체 패널의 개수
@@ -80,6 +89,7 @@ abstract class Renderer {
80
89
  }: RendererOptions) {
81
90
  this._flicking = null;
82
91
  this._panels = [];
92
+ this._rendering = false;
83
93
 
84
94
  // Bind options
85
95
  this._align = align;
@@ -182,10 +192,28 @@ abstract class Renderer {
182
192
  elements: any[];
183
193
  hasDOMInElements: boolean;
184
194
  }>): Panel[] {
195
+ const allPanelsInserted = this.batchInsertDefer(...items);
196
+
197
+ if (allPanelsInserted.length <= 0) return [];
198
+
199
+ this.updateAfterPanelChange(allPanelsInserted, []);
200
+
201
+ return allPanelsInserted;
202
+ }
203
+
204
+ /**
205
+ * Defers update
206
+ * camera position & others will be updated after calling updateAfterPanelChange
207
+ * @internal
208
+ */
209
+ public batchInsertDefer(...items: Array<{
210
+ index: number;
211
+ elements: any[];
212
+ hasDOMInElements: boolean;
213
+ }>) {
185
214
  const panels = this._panels;
186
215
  const flicking = getFlickingAttached(this._flicking);
187
216
 
188
- const { control } = flicking;
189
217
  const prevFirstPanel = panels[0];
190
218
  const align = parsePanelAlign(this._align);
191
219
 
@@ -219,30 +247,6 @@ abstract class Renderer {
219
247
  return [...addedPanels, ...panelsInserted];
220
248
  }, []);
221
249
 
222
- if (allPanelsInserted.length <= 0) return [];
223
-
224
- // Update camera & control
225
- this._updateCameraAndControl();
226
-
227
- void this.render();
228
-
229
- // Move to the first panel added if no panels existed
230
- // FIXME: fix for animating case
231
- if (allPanelsInserted.length > 0 && !control.animating) {
232
- void control.moveToPanel(control.activePanel || allPanelsInserted[0], {
233
- duration: 0
234
- }).catch(() => void 0);
235
- }
236
-
237
- flicking.camera.updateOffset();
238
-
239
- flicking.trigger(new ComponentEvent(EVENTS.PANEL_CHANGE, {
240
- added: allPanelsInserted,
241
- removed: []
242
- }));
243
-
244
- this.checkPanelContentsReady(allPanelsInserted);
245
-
246
250
  return allPanelsInserted;
247
251
  }
248
252
 
@@ -257,13 +261,35 @@ abstract class Renderer {
257
261
  * @param {boolean} [items.hasDOMInElements=1] Whether it contains actual DOM elements. If set to true, renderer will remove them from the camera element<ko>내부에 실제 DOM 엘리먼트들을 포함하고 있는지 여부. true로 설정할 경우, 렌더러는 해당 엘리먼트들을 카메라 엘리먼트 내부에서 제거합니다</ko>
258
262
  * @return An array of removed panels<ko>제거된 패널들의 배열</ko>
259
263
  */
260
- public batchRemove(...items: Array<{ index: number; deleteCount: number; hasDOMInElements: boolean }>): Panel[] {
264
+ public batchRemove(...items: Array<{
265
+ index: number;
266
+ deleteCount: number;
267
+ hasDOMInElements: boolean;
268
+ }>): Panel[] {
269
+ const allPanelsRemoved = this.batchRemoveDefer(...items);
270
+
271
+ if (allPanelsRemoved.length <= 0) return [];
272
+
273
+ this.updateAfterPanelChange([], allPanelsRemoved);
274
+
275
+ return allPanelsRemoved;
276
+ }
277
+
278
+ /**
279
+ * Defers update
280
+ * camera position & others will be updated after calling updateAfterPanelChange
281
+ * @internal
282
+ */
283
+ public batchRemoveDefer(...items: Array<{
284
+ index: number;
285
+ deleteCount: number;
286
+ hasDOMInElements: boolean;
287
+ }>) {
261
288
  const panels = this._panels;
262
289
  const flicking = getFlickingAttached(this._flicking);
263
290
 
264
- const { camera, control } = flicking;
291
+ const { control } = flicking;
265
292
  const activePanel = control.activePanel;
266
- const activeIndex = control.activeIndex;
267
293
 
268
294
  const allPanelsRemoved = items.reduce((removed, item) => {
269
295
  const { index, deleteCount } = item;
@@ -294,35 +320,56 @@ abstract class Renderer {
294
320
  return [...removed, ...panelsRemoved];
295
321
  }, []);
296
322
 
323
+ return allPanelsRemoved;
324
+ }
325
+
326
+ /**
327
+ * @internal
328
+ */
329
+ public updateAfterPanelChange(panelsAdded: Panel[], panelsRemoved: Panel[]) {
330
+ const flicking = getFlickingAttached(this._flicking);
331
+ const { camera, control } = flicking;
332
+ const panels = this._panels;
333
+ const activePanel = control.activePanel;
334
+
297
335
  // Update camera & control
298
336
  this._updateCameraAndControl();
299
337
 
300
338
  void this.render();
301
339
 
302
- // FIXME: fix for animating case
303
- if (allPanelsRemoved.length > 0 && !control.animating) {
304
- const targetPanel = includes(allPanelsRemoved, activePanel)
305
- ? (panels[activeIndex] || panels[panels.length - 1])
306
- : activePanel;
340
+ if (!activePanel || activePanel.removed) {
341
+ if (panels.length <= 0) {
342
+ // All panels removed
343
+ camera.lookAt(0);
344
+ } else {
345
+ let targetIndex = activePanel?.index ?? 0;
346
+ if (targetIndex > panels.length - 1) {
347
+ targetIndex = panels.length - 1;
348
+ }
307
349
 
308
- if (targetPanel) {
309
- void control.moveToPanel(targetPanel, {
350
+ void control.moveToPanel(panels[targetIndex], {
310
351
  duration: 0
311
352
  }).catch(() => void 0);
312
- } else {
313
- // All panels removed
314
- camera.lookAt(0);
315
353
  }
354
+ } else {
355
+ void control.moveToPanel(control.activePanel!, {
356
+ duration: 0
357
+ }).catch(() => void 0);
316
358
  }
317
359
 
318
360
  flicking.camera.updateOffset();
319
361
 
320
- flicking.trigger(new ComponentEvent(EVENTS.PANEL_CHANGE, {
321
- added: [],
322
- removed: allPanelsRemoved
323
- }));
362
+ if (panelsAdded.length > 0 || panelsRemoved.length > 0) {
363
+ flicking.trigger(new ComponentEvent(EVENTS.PANEL_CHANGE, {
364
+ added: panelsAdded,
365
+ removed: panelsRemoved
366
+ }));
324
367
 
325
- return allPanelsRemoved;
368
+ this.checkPanelContentsReady([
369
+ ...panelsAdded,
370
+ ...panelsRemoved
371
+ ]);
372
+ }
326
373
  }
327
374
 
328
375
  /**
@@ -367,6 +414,7 @@ abstract class Renderer {
367
414
  if (!flicking.initialized) return;
368
415
 
369
416
  camera.updateRange();
417
+ camera.updateOffset();
370
418
  camera.updateAnchors();
371
419
 
372
420
  if (control.animating) {
@@ -402,6 +450,7 @@ abstract class Renderer {
402
450
  const { camera, control } = flicking;
403
451
 
404
452
  camera.updateRange();
453
+ camera.updateOffset();
405
454
  camera.updateAnchors();
406
455
  camera.resetNeedPanelHistory();
407
456
  control.updateInput();
@@ -1,38 +0,0 @@
1
- .flicking-viewport {
2
- position: relative;
3
- overflow: hidden;
4
- }
5
- .flicking-viewport:not(.vertical) {
6
- width: 100%;
7
- height: 100%;
8
- }
9
- .flicking-camera {
10
- width: 100%;
11
- height: 100%;
12
- position: relative;
13
- z-index: 1;
14
- white-space: nowrap;
15
- will-change: transform;
16
- }
17
-
18
- .flicking-camera>* {
19
- display: inline-block;
20
- white-space: normal;
21
- vertical-align: top;
22
- }
23
- .flicking-viewport.vertical,
24
- .flicking-viewport.vertical > .flicking-camera {
25
- display: inline-block;
26
- }
27
- .flicking-viewport.vertical.middle > .flicking-camera > * {
28
- vertical-align: middle;
29
- }
30
- .flicking-viewport.vertical.bottom > .flicking-camera > * {
31
- vertical-align: bottom;
32
- }
33
- .flicking-viewport.vertical > .flicking-camera > * {
34
- display: block;
35
- }
36
- .flicking-viewport.flicking-hidden > .flicking-camera > * {
37
- visibility: hidden;
38
- }
package/css/flicking.css DELETED
@@ -1,28 +0,0 @@
1
- .flicking-viewport {
2
- position: relative;
3
- overflow: hidden;
4
- }
5
-
6
- .flicking-camera {
7
- width: 100%;
8
- height: 100%;
9
- display: flex;
10
- position: relative;
11
- flex-direction: row;
12
- z-index: 1;
13
- }
14
- .flicking-camera>* {
15
- flex-shrink: 0;
16
- }
17
-
18
- .flicking-viewport.vertical {
19
- display: inline-flex;
20
- }
21
- .flicking-viewport.vertical>.flicking-camera {
22
- display: inline-flex;
23
- flex-direction: column;
24
- }
25
-
26
- .flicking-viewport.flicking-hidden .flicking-camera > * {
27
- visibility: hidden;
28
- }
@@ -1 +0,0 @@
1
- {"version":3,"sources":["<input css 1>"],"names":[],"mappings":"AAAA,mBAAmB,iBAAiB,CAAC,eAAe,CAAC,kCAAkC,UAAU,CAAC,WAAW,CAAC,iBAAiB,UAAU,CAAC,WAAW,CAAC,iBAAiB,CAAC,SAAS,CAAC,kBAAkB,CAAC,qBAAqB,CAAC,mBAAmB,oBAAoB,CAAC,kBAAkB,CAAC,kBAAkB,CAAC,yEAAyE,oBAAoB,CAAC,sDAAsD,qBAAqB,CAAC,sDAAsD,qBAAqB,CAAC,+CAA+C,aAAa,CAAC,sDAAsD,iBAAiB","file":"flicking-inline.css","sourcesContent":[".flicking-viewport{position:relative;overflow:hidden}.flicking-viewport:not(.vertical){width:100%;height:100%}.flicking-camera{width:100%;height:100%;position:relative;z-index:1;white-space:nowrap;will-change:transform}.flicking-camera>*{display:inline-block;white-space:normal;vertical-align:top}.flicking-viewport.vertical,.flicking-viewport.vertical>.flicking-camera{display:inline-block}.flicking-viewport.vertical.middle>.flicking-camera>*{vertical-align:middle}.flicking-viewport.vertical.bottom>.flicking-camera>*{vertical-align:bottom}.flicking-viewport.vertical>.flicking-camera>*{display:block}.flicking-viewport.flicking-hidden>.flicking-camera>*{visibility:hidden}"]}
@@ -1 +0,0 @@
1
- {"version":3,"sources":["<input css 2>"],"names":[],"mappings":"AAAA,mBAAmB,iBAAiB,CAAC,eAAe,CAAC,iBAAiB,UAAU,CAAC,WAAW,CAAC,mBAAmB,CAAC,mBAAmB,CAAC,YAAY,CAAC,iBAAiB,CAAC,6BAA6B,CAAC,4BAA4B,CAAC,sBAAsB,CAAC,kBAAkB,CAAC,SAAS,CAAC,mBAAmB,mBAAmB,CAAC,aAAa,CAAC,4BAA4B,0BAA0B,CAAC,0BAA0B,CAAC,mBAAmB,CAAC,6CAA6C,0BAA0B,CAAC,0BAA0B,CAAC,mBAAmB,CAAC,2BAA2B,CAAC,4BAA4B,CAAC,yBAAyB,CAAC,qBAAqB,CAAC,sDAAsD,iBAAiB","file":"flicking.css","sourcesContent":[".flicking-viewport{position:relative;overflow:hidden}.flicking-camera{width:100%;height:100%;display:-webkit-box;display:-ms-flexbox;display:flex;position:relative;-webkit-box-orient:horizontal;-webkit-box-direction:normal;-ms-flex-direction:row;flex-direction:row;z-index:1}.flicking-camera>*{-ms-flex-negative:0;flex-shrink:0}.flicking-viewport.vertical{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex}.flicking-viewport.vertical>.flicking-camera{display:-webkit-inline-box;display:-ms-inline-flexbox;display:inline-flex;-webkit-box-orient:vertical;-webkit-box-direction:normal;-ms-flex-direction:column;flex-direction:column}.flicking-viewport.flicking-hidden .flicking-camera>*{visibility:hidden}"]}