@egjs/flicking 4.12.0-beta.8 → 4.12.0-beta.9

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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@egjs/flicking",
3
- "version": "4.12.0-beta.8",
3
+ "version": "4.12.0-beta.9",
4
4
  "description": "Everyday 30 million people experience. It's reliable, flexible and extendable carousel.",
5
5
  "main": "dist/flicking.cjs.js",
6
6
  "module": "dist/flicking.esm.js",
@@ -2,9 +2,16 @@
2
2
  position: relative
3
3
  overflow: hidden
4
4
  &.vertical
5
+ display: -webkit-inline-box
6
+ display: -ms-inline-flexbox
5
7
  display: inline-flex
6
8
  >.flicking-camera
9
+ display: -webkit-inline-box
10
+ display: -ms-inline-flexbox
7
11
  display: inline-flex
12
+ -webkit-box-orient: vertical
13
+ -webkit-box-direction: normal
14
+ -ms-flex-direction: column
8
15
  flex-direction: column
9
16
  &.flicking-hidden
10
17
  >.flicking-camera
@@ -14,10 +21,16 @@
14
21
  .flicking-camera
15
22
  width: 100%
16
23
  height: 100%
24
+ display: -webkit-box
25
+ display: -ms-flexbox
17
26
  display: flex
18
27
  position: relative
28
+ -webkit-box-orient: horizontal
29
+ -webkit-box-direction: normal
30
+ -ms-flex-direction: row
19
31
  flex-direction: row
20
32
  z-index: 1
21
33
  will-change: transform
22
34
  >*
35
+ -ms-flex-negative: 0
23
36
  flex-shrink: 0
@@ -2,15 +2,43 @@
2
2
  * Copyright (c) 2015 NAVER Corp.
3
3
  * egjs projects are licensed under the MIT license
4
4
  */
5
-
6
- import Flicking, { FlickingOptions } from "./Flicking";
7
- import { ChangedEvent, MoveEndEvent, MoveEvent } from "./type/event";
5
+ import { ComponentEvent } from "@egjs/component";
6
+ import { EventKey } from "@egjs/component/declaration/types";
7
+
8
+ import Flicking, { FlickingEvents, FlickingOptions } from "./Flicking";
9
+ import {
10
+ ChangedEvent,
11
+ HoldEndEvent,
12
+ HoldStartEvent,
13
+ MoveEndEvent,
14
+ MoveEvent,
15
+ MoveStartEvent,
16
+ WillChangeEvent
17
+ } from "./type/event";
8
18
  import { LiteralUnion, ValueOf } from "./type/internal";
9
19
  import { CLASS, EVENTS, MOVE_DIRECTION } from "./const/external";
10
20
  import { getDataAttributes, includes, toArray } from "./utils";
11
21
 
12
- export interface CrossFlickingEvents {
13
- // FlickingEvent 들을 확장하자...
22
+ export const SIDE_EVENTS = {
23
+ HOLD_START: "sideHoldStart",
24
+ HOLD_END: "sideHoldEnd",
25
+ MOVE_START: "sideMoveStart",
26
+ MOVE: "sideMove",
27
+ MOVE_END: "sideMoveEnd",
28
+ WILL_CHANGE: "sideWillChange",
29
+ CHANGED: "sideChanged"
30
+ } as const;
31
+
32
+ export type CrossFlickingEvent<T> = { mainIndex: number } & T;
33
+
34
+ export interface CrossFlickingEvents extends FlickingEvents {
35
+ [SIDE_EVENTS.HOLD_START]: CrossFlickingEvent<HoldStartEvent>;
36
+ [SIDE_EVENTS.HOLD_END]: CrossFlickingEvent<HoldEndEvent>;
37
+ [SIDE_EVENTS.MOVE_START]: CrossFlickingEvent<MoveStartEvent>;
38
+ [SIDE_EVENTS.MOVE]: CrossFlickingEvent<MoveEvent>;
39
+ [SIDE_EVENTS.MOVE_END]: CrossFlickingEvent<MoveEndEvent>;
40
+ [SIDE_EVENTS.WILL_CHANGE]: CrossFlickingEvent<WillChangeEvent>;
41
+ [SIDE_EVENTS.CHANGED]: CrossFlickingEvent<ChangedEvent>;
14
42
  }
15
43
 
16
44
  export interface CrossFlickingOptions extends FlickingOptions {
@@ -64,38 +92,40 @@ export class CrossFlicking extends Flicking {
64
92
  public set sideOptions(val: CrossFlickingOptions["sideOptions"]) {
65
93
  this._sideOptions = val;
66
94
  }
95
+
67
96
  public set preserveIndex(val: CrossFlickingOptions["preserveIndex"]) {
68
97
  this._preserveIndex = val;
69
98
  }
99
+
70
100
  public set disableSlideOnHold(val: CrossFlickingOptions["disableSlideOnHold"]) {
71
101
  this._disableSlideOnHold = val;
72
102
  }
103
+
73
104
  public set disableIndexSync(val: CrossFlickingOptions["disableIndexSync"]) {
74
105
  this._disableIndexSync = val;
75
106
  }
76
107
 
77
108
  public constructor(
78
109
  root: HTMLElement | string,
79
- options: Partial<CrossFlickingOptions> = {
80
- sideOptions: {},
81
- preserveIndex: false,
82
- disableSlideOnHold: false,
83
- disableIndexSync: false,
84
- }
110
+ options: Partial<CrossFlickingOptions>
85
111
  ) {
86
112
  super(root, options);
113
+ const {
114
+ sideOptions = {},
115
+ preserveIndex = true,
116
+ disableSlideOnHold = true,
117
+ disableIndexSync = false
118
+ } = options;
87
119
 
88
120
  // Internal states
89
121
  this._moveDirection = null;
90
122
  this._nextIndex = 0;
91
123
 
92
124
  // Bind options
93
- this._sideOptions = options.sideOptions;
94
- this._preserveIndex = options.preserveIndex;
95
- this._disableSlideOnHold = options.disableSlideOnHold;
96
- this._disableIndexSync = options.disableIndexSync;
97
-
98
- // Create core components
125
+ this._sideOptions = sideOptions;
126
+ this._preserveIndex = preserveIndex;
127
+ this._disableSlideOnHold = disableSlideOnHold;
128
+ this._disableIndexSync = disableIndexSync;
99
129
  }
100
130
 
101
131
  public init(): Promise<void> {
@@ -107,7 +137,9 @@ export class CrossFlicking extends Flicking {
107
137
  }
108
138
 
109
139
  public destroy(): void {
110
- // TODO 모든 child flicking destroy
140
+ this._sideFlicking.forEach((flicking) => {
141
+ flicking.destroy();
142
+ });
111
143
  super.destroy();
112
144
  }
113
145
 
@@ -116,92 +148,49 @@ export class CrossFlicking extends Flicking {
116
148
  this.on(EVENTS.MOVE, this._onHorizontalMove);
117
149
  this.on(EVENTS.MOVE_END, this._onHorizontalMoveEnd);
118
150
 
119
- this._sideFlicking.forEach((flicking) => {
151
+ this._sideFlicking.forEach((flicking, mainIndex) => {
120
152
  flicking.on(EVENTS.HOLD_START, this._onSideHoldStart);
121
153
  flicking.on(EVENTS.MOVE, this._onSideMove);
122
154
  flicking.on(EVENTS.MOVE_END, this._onSideMoveEnd);
123
155
  flicking.on(EVENTS.CHANGED, this._onSideChanged);
124
- });
125
- }
126
-
127
- private _getGroupFromAttribute(panels: HTMLElement[]): Record<string, HTMLElement[]> {
128
- const groupKeys: string[] = [];
129
- const groupPanels: Record<string, HTMLElement[]> = {};
130
156
 
131
- panels.forEach((panel) => {
132
- const groupKey = getDataAttributes(panel, "data-cross-").groupkey;
133
- if (groupKey && !includes(groupKeys, groupKey)) {
134
- groupKeys.push(groupKey);
135
- groupPanels[groupKey] = [panel];
136
- } else if (groupKey) {
137
- groupPanels[groupKey].push(panel);
138
- }
157
+ Object.keys(SIDE_EVENTS).forEach((name: EventKey<FlickingEvents>) => {
158
+ flicking.on(EVENTS[name], (event) => {
159
+ this.trigger(
160
+ new ComponentEvent(SIDE_EVENTS[name], {
161
+ mainIndex,
162
+ ...event
163
+ })
164
+ );
165
+ });
166
+ });
139
167
  });
140
-
141
- return groupPanels;
142
168
  }
143
169
 
144
170
  private _createSideState(): SideState[] {
145
171
  const viewportEl = this.element;
146
172
  const cameraEl = this.camera.element;
147
173
  const panels = toArray(cameraEl.children) as HTMLElement[];
148
- let sideState: SideState[] = [];
149
- let sidePanels: string = "";
150
-
151
- // check data attribute exists
152
- const sideCamera = document.createElement("div");
153
- sideCamera.classList.add(CLASS.CAMERA);
154
-
155
174
  const isCrossStructure = getDataAttributes(
156
175
  viewportEl,
157
176
  "data-cross-"
158
177
  ).structure;
178
+ let sideState: SideState[] = [];
159
179
 
160
180
  if (!isCrossStructure) {
161
- viewportEl.setAttribute("data-cross-structure", "true");
162
-
163
181
  const groupPanels = this._getGroupFromAttribute(panels);
164
182
  const groupKeys = Object.keys(groupPanels);
165
183
 
166
184
  if (groupKeys.length) {
167
185
  sideState = this._getSideStateFromGroup(groupPanels);
168
186
  this.remove(0, this.panelCount - groupKeys.length);
169
- sideState.forEach((state, i) => {
170
- const panel = this.camera.children[i];
171
- sidePanels += state.element.innerHTML;
172
- Array.from(panel.attributes).forEach(attribute => panel.removeAttribute(attribute.name));
173
- });
174
187
  } else {
175
- sideState = panels.reduce(
176
- (state: SideState[], panel: HTMLElement, i: number) => {
177
- const start = state.length ? +state[state.length - 1].end + 1 : 0;
178
- sidePanels += panel.innerHTML;
179
- return [
180
- ...state,
181
- {
182
- key: i.toString(),
183
- start,
184
- end: start + panel.children.length - 1,
185
- element: panel,
186
- },
187
- ];
188
- },
189
- []
190
- );
188
+ sideState = this._getSideStateFromPanels(panels);
191
189
  }
192
190
 
193
- sideCamera.innerHTML = sidePanels;
194
- sideState.forEach((_, i) => {
195
- const panel = this.camera.children[i];
196
- [CLASS.VIEWPORT, CLASS.VERTICAL].forEach((className) => {
197
- if (!panel.classList.contains(className)) {
198
- panel.classList.add(className);
199
- }
200
- });
201
- panel.innerHTML = sideCamera.outerHTML;
202
- });
191
+ this._createCrossStructure(sideState);
203
192
  } else {
204
- sideState = this._getSideStateFromPanels(panels);
193
+ sideState = this._getSideStateFromCrossStructure(panels);
205
194
  }
206
195
 
207
196
  void this.resize();
@@ -209,7 +198,56 @@ export class CrossFlicking extends Flicking {
209
198
  return sideState;
210
199
  }
211
200
 
212
- private _getSideStateFromGroup(groupPanels: Record<string, HTMLElement[]>): SideState[] {
201
+ private _createCrossStructure(sideState: SideState[]) {
202
+ const sideCamera = document.createElement("div");
203
+ let sidePanels: string = "";
204
+
205
+ sideCamera.classList.add(CLASS.CAMERA);
206
+ sideState.forEach((state, i) => {
207
+ const panel = this.camera.children[i];
208
+ sidePanels += state.element.innerHTML;
209
+ Array.from(panel.attributes).forEach((attribute) =>
210
+ panel.removeAttribute(attribute.name)
211
+ );
212
+ });
213
+
214
+ sideCamera.innerHTML = sidePanels;
215
+
216
+ sideState.forEach((_, i) => {
217
+ const panel = this.camera.children[i];
218
+ [CLASS.VIEWPORT, CLASS.VERTICAL].forEach((className) => {
219
+ if (!panel.classList.contains(className)) {
220
+ panel.classList.add(className);
221
+ }
222
+ });
223
+ panel.innerHTML = sideCamera.outerHTML;
224
+ });
225
+
226
+ this.element.setAttribute("data-cross-structure", "true");
227
+ }
228
+
229
+ private _getGroupFromAttribute(
230
+ panels: HTMLElement[]
231
+ ): Record<string, HTMLElement[]> {
232
+ const groupKeys: string[] = [];
233
+ const groupPanels: Record<string, HTMLElement[]> = {};
234
+
235
+ panels.forEach((panel) => {
236
+ const groupKey = getDataAttributes(panel, "data-cross-").groupkey;
237
+ if (groupKey && !includes(groupKeys, groupKey)) {
238
+ groupKeys.push(groupKey);
239
+ groupPanels[groupKey] = [panel];
240
+ } else if (groupKey) {
241
+ groupPanels[groupKey].push(panel);
242
+ }
243
+ });
244
+
245
+ return groupPanels;
246
+ }
247
+
248
+ private _getSideStateFromGroup(
249
+ groupPanels: Record<string, HTMLElement[]>
250
+ ): SideState[] {
213
251
  return Object.keys(groupPanels).reduce(
214
252
  (state: SideState[], key: string) => {
215
253
  const start = state.length ? +state[state.length - 1].end + 1 : 0;
@@ -235,6 +273,24 @@ export class CrossFlicking extends Flicking {
235
273
  }
236
274
 
237
275
  private _getSideStateFromPanels(panels: HTMLElement[]): SideState[] {
276
+ return panels.reduce(
277
+ (state: SideState[], panel: HTMLElement, i: number) => {
278
+ const start = state.length ? +state[state.length - 1].end + 1 : 0;
279
+ return [
280
+ ...state,
281
+ {
282
+ key: i.toString(),
283
+ start,
284
+ end: start + panel.children.length - 1,
285
+ element: panel
286
+ }
287
+ ];
288
+ },
289
+ []
290
+ );
291
+ }
292
+
293
+ private _getSideStateFromCrossStructure(panels: HTMLElement[]): SideState[] {
238
294
  const groupPanels = this._getGroupFromAttribute(panels);
239
295
  return this._getSideStateFromGroup(groupPanels);
240
296
  }
@@ -245,13 +301,16 @@ export class CrossFlicking extends Flicking {
245
301
  ...this.sideOptions,
246
302
  horizontal: false,
247
303
  panelsPerView: 1,
248
- defaultIndex: state.start,
249
- moveType: "strict"
304
+ defaultIndex: state.start
250
305
  });
251
306
  });
252
307
  }
253
308
 
254
- private _syncToCategory(index: number, outerIndex: number): void {
309
+ private _syncToCategory = (index: number, outerIndex: number): void => {
310
+ if (this._disableIndexSync) {
311
+ return;
312
+ }
313
+
255
314
  this.stopAnimation();
256
315
  this._sideFlicking.forEach((child, i) => {
257
316
  const { start, end } = this._sideState[i];
@@ -262,18 +321,61 @@ export class CrossFlicking extends Flicking {
262
321
  void this.moveTo(i, 0);
263
322
  }
264
323
  });
265
- }
324
+ };
325
+
326
+ private _setDraggable = (
327
+ direction: ValueOf<typeof MOVE_DIRECTION>,
328
+ draggable: boolean
329
+ ): void => {
330
+ if (!this._disableSlideOnHold) {
331
+ return;
332
+ }
333
+
334
+ const threshold = draggable
335
+ ? this.dragThreshold && this.dragThreshold >= 10
336
+ ? this.dragThreshold
337
+ : 10
338
+ : Infinity;
339
+
340
+ if ((direction === MOVE_DIRECTION.HORIZONTAL) === this.horizontal) {
341
+ this.dragThreshold = threshold;
342
+ } else if ((direction === MOVE_DIRECTION.VERTICAL) === this.horizontal) {
343
+ this._sideFlicking.forEach((child) => {
344
+ child.dragThreshold = threshold;
345
+ });
346
+ }
347
+ };
348
+
349
+ private _setPreviousSideIndex = () => {
350
+ this._sideFlicking.forEach((child, i) => {
351
+ const { start, end } = this._sideState[i];
352
+
353
+ if (this._preserveIndex) {
354
+ if (this._nextIndex !== i) {
355
+ if (child.index < start) {
356
+ child.stopAnimation();
357
+ void child.moveTo(start, 0);
358
+ } else if (child.index > end) {
359
+ child.stopAnimation();
360
+ void child.moveTo(end, 0);
361
+ }
362
+ }
363
+ } else {
364
+ if (this._nextIndex !== i) {
365
+ void child.moveTo(start, 0);
366
+ }
367
+ }
368
+ });
369
+ };
266
370
 
267
371
  private _onHorizontalHoldStart = (): void => {
268
- this.dragThreshold = 10;
372
+ this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
269
373
  this._moveDirection = null;
270
374
  };
271
375
 
272
376
  private _onHorizontalMove = (e: MoveEvent): void => {
273
377
  if (e.isTrusted && !this._moveDirection) {
274
- this._sideFlicking.forEach((child) => {
275
- child.dragThreshold = Infinity;
276
- });
378
+ this._setDraggable(MOVE_DIRECTION.VERTICAL, false);
277
379
  this._moveDirection = MOVE_DIRECTION.HORIZONTAL;
278
380
  }
279
381
  };
@@ -281,11 +383,6 @@ export class CrossFlicking extends Flicking {
281
383
  private _onHorizontalMoveEnd = (e: MoveEndEvent): void => {
282
384
  const visiblePanels = this.visiblePanels;
283
385
 
284
- this._sideFlicking.forEach((child) => {
285
- child.dragThreshold = 10;
286
- });
287
- this._moveDirection = null;
288
-
289
386
  if (visiblePanels.length > 1) {
290
387
  this._nextIndex =
291
388
  e.direction === "NEXT"
@@ -295,22 +392,11 @@ export class CrossFlicking extends Flicking {
295
392
  this._nextIndex = visiblePanels[0].index;
296
393
  }
297
394
 
298
- // _syncToCategory에서 완전히 가로 이동이 이루어지기 전에 세로 방향 index가 변하는 경우가 있어 requestAnimationFrame 처리
299
- requestAnimationFrame(() => {
300
- this._sideFlicking.forEach((child, i) => {
301
- if (this._nextIndex !== i) {
302
- const { start, end } = this._sideState[i];
395
+ this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
396
+ this._moveDirection = null;
303
397
 
304
- if (child.index < start) {
305
- child.stopAnimation();
306
- void child.moveTo(start, 0);
307
- } else if (child.index > end) {
308
- child.stopAnimation();
309
- void child.moveTo(end, 0);
310
- }
311
- }
312
- });
313
- });
398
+ // _syncToCategory에서 완전히 가로 이동이 이루어지기 전에 세로 방향 index 변하는 경우가 있어 requestAnimationFrame 처리
399
+ requestAnimationFrame(() => this._setPreviousSideIndex());
314
400
 
315
401
  if (e.isTrusted) {
316
402
  this._syncToCategory(
@@ -321,27 +407,25 @@ export class CrossFlicking extends Flicking {
321
407
  };
322
408
 
323
409
  private _onSideHoldStart = (): void => {
324
- this._sideFlicking.forEach((child) => {
325
- child.dragThreshold = 10;
326
- });
410
+ this._setDraggable(MOVE_DIRECTION.VERTICAL, true);
327
411
  this._moveDirection = null;
328
412
  };
329
413
 
330
414
  private _onSideMove = (e: MoveEvent): void => {
331
415
  if (e.isTrusted && !this._moveDirection) {
332
- this.dragThreshold = Infinity;
416
+ this._setDraggable(MOVE_DIRECTION.HORIZONTAL, false);
333
417
  this._moveDirection = MOVE_DIRECTION.VERTICAL;
334
418
  }
335
419
  };
336
420
 
337
421
  private _onSideMoveEnd = (): void => {
338
- this.dragThreshold = 10;
422
+ this._setDraggable(MOVE_DIRECTION.HORIZONTAL, true);
339
423
  this._moveDirection = null;
340
424
  };
341
425
 
342
426
  private _onSideChanged = (e: ChangedEvent): void => {
343
- // this.visiblePanels.length 2보다 크다면 가로 방향 Flicking이 조작 중이라는 것을 의미합니다.
344
- // 경우 가로 방향 Flicking의 이동이 완전히 끝난 _onHorizontalMoveEnd 에서 syncToCategory할 것이므로 여기서는 하지 않습니다.
427
+ // If this.visiblePanels.length >= 2, it means that horizontal flicking is being dragged.
428
+ // In this case, syncToCategory in _onHorizontalMoveEnd will fire after moving finishes, so we don't fire it here.
345
429
  if (
346
430
  this.visiblePanels.length < 2 &&
347
431
  this._sideFlicking[this.index] === e.currentTarget
@@ -32,6 +32,7 @@ class Camera {
32
32
  private _mode: CameraMode;
33
33
  private _el: HTMLElement;
34
34
  private _transform: string;
35
+ private _lookedOffset = 0;
35
36
  private _position: number;
36
37
  private _alignPos: number;
37
38
  private _offset: number;
@@ -292,6 +293,8 @@ class Camera {
292
293
  * @return {this}
293
294
  */
294
295
  public lookAt(pos: number): void {
296
+ const prevOffset = this._offset;
297
+ const isChangedOffset = this._lookedOffset !== prevOffset;
295
298
  const flicking = getFlickingAttached(this._flicking);
296
299
  const prevPos = this._position;
297
300
 
@@ -304,7 +307,12 @@ class Camera {
304
307
  if (toggled) {
305
308
  void flicking.renderer.render().then(() => {
306
309
  this.updateOffset();
310
+ this._lookedOffset = this._offset;
307
311
  });
312
+ } else if (isChangedOffset) {
313
+ // sync offset for renderOnlyVisible on resize
314
+ this.updateOffset();
315
+ this._lookedOffset = this._offset;
308
316
  } else {
309
317
  this.applyTransform();
310
318
  }
@@ -502,8 +510,8 @@ class Camera {
502
510
  }
503
511
 
504
512
  /**
505
- * Update Viewport's height to active panel's height
506
- * @ko 현재 선택된 패널의 높이와 동일하도록 뷰포트의 높이를 업데이트합니다
513
+ * Update Viewport's height to visible panel's max height
514
+ * @ko 현재 활성화된 패널과 보이는 패널의 최대 높이와 동일하도록 뷰포트의 높이를 업데이트합니다
507
515
  * @throws {FlickingError}
508
516
  * {@link ERROR_CODE NOT_ATTACHED_TO_FLICKING} When {@link Camera#init init} is not called before
509
517
  * <ko>{@link ERROR_CODE NOT_ATTACHED_TO_FLICKING} {@link Camera#init init}이 이전에 호출되지 않은 경우</ko>
@@ -513,11 +521,22 @@ class Camera {
513
521
  public updateAdaptiveHeight() {
514
522
  const flicking = getFlickingAttached(this._flicking);
515
523
  const activePanel = flicking.control.activePanel;
524
+ const visiblePanels = flicking.visiblePanels;
525
+
526
+ const selectedPanels = [...visiblePanels];
527
+
528
+ if (activePanel) {
529
+ selectedPanels.push(activePanel);
530
+ }
531
+
532
+ if (!flicking.horizontal || !flicking.adaptive || !selectedPanels.length) return;
533
+
534
+
535
+ const maxHeight = Math.max(...selectedPanels.map(panel => panel.height));
516
536
 
517
- if (!flicking.horizontal || !flicking.adaptive || !activePanel) return;
518
537
 
519
538
  flicking.viewport.setSize({
520
- height: activePanel.height
539
+ height: maxHeight
521
540
  });
522
541
  }
523
542
 
@@ -599,6 +618,7 @@ class Camera {
599
618
 
600
619
  private _resetInternalValues() {
601
620
  this._position = 0;
621
+ this._lookedOffset = 0;
602
622
  this._alignPos = 0;
603
623
  this._offset = 0;
604
624
  this._circularOffset = 0;