= {}) {
84 | this._parentEl = parentEl;
85 | this._selector = selector;
86 | this._type = type;
87 | this._classPrefix = classPrefix;
88 | this._bulletCount = bulletCount;
89 | this._renderBullet = renderBullet;
90 | this._renderActiveBullet = renderActiveBullet;
91 | this._renderFraction = renderFraction;
92 | this._fractionCurrentFormat = fractionCurrentFormat;
93 | this._fractionTotalFormat = fractionTotalFormat;
94 | this._scrollOnChange = scrollOnChange;
95 | }
96 |
97 | public init(flicking: Flicking): void {
98 | if (this._flicking) {
99 | this.destroy();
100 | }
101 |
102 | this._flicking = flicking;
103 |
104 | const type = this._type;
105 | const selector = this._selector;
106 | const parentEl = this._parentEl ? this._parentEl : flicking.element;
107 | const wrapper = parentEl.querySelector(selector);
108 |
109 | if (!wrapper) {
110 | throw new Error(`[Flicking-Pagination] Couldn't find element with the given selector: ${selector}`);
111 | }
112 |
113 | this._wrapper = wrapper as HTMLElement;
114 | this._renderer = this._createRenderer(type);
115 |
116 | flicking.on(EVENTS.WILL_CHANGE, this._onIndexChange);
117 | flicking.on(EVENTS.WILL_RESTORE, this._onIndexChange);
118 | flicking.on(EVENTS.PANEL_CHANGE, this.update);
119 |
120 | this.update();
121 | }
122 |
123 | public destroy(): void {
124 | const flicking = this._flicking;
125 |
126 | if (!flicking) {
127 | return;
128 | }
129 |
130 | flicking.off(EVENTS.WILL_CHANGE, this._onIndexChange);
131 | flicking.off(EVENTS.WILL_RESTORE, this._onIndexChange);
132 | flicking.off(EVENTS.PANEL_CHANGE, this.update);
133 |
134 | this._renderer.destroy();
135 | this._removeAllChilds();
136 | this._flicking = null;
137 | }
138 |
139 | public update = (): void => {
140 | this._removeAllChilds();
141 | this._renderer.render();
142 | };
143 |
144 | private _createRenderer(type: PaginationOptions["type"]) {
145 | const options = {
146 | flicking: this._flicking!,
147 | pagination: this,
148 | wrapper: this._wrapper
149 | };
150 |
151 | switch (type) {
152 | case PAGINATION.TYPE.BULLET:
153 | return new BulletRenderer(options);
154 | case PAGINATION.TYPE.FRACTION:
155 | return new FractionRenderer(options);
156 | case PAGINATION.TYPE.SCROLL:
157 | return new ScrollBulletRenderer(options);
158 | default:
159 | throw new Error(`[Flicking-Pagination] type "${type}" is not supported.`);
160 | }
161 | }
162 |
163 | private _onIndexChange = (evt: { index: number }) => {
164 | this._renderer.update(evt.index);
165 | };
166 |
167 | private _removeAllChilds() {
168 | const wrapper = this._wrapper;
169 |
170 | while (wrapper.firstChild) {
171 | wrapper.removeChild(wrapper.firstChild);
172 | }
173 | }
174 | }
175 |
176 | export default Pagination;
177 |
--------------------------------------------------------------------------------
/src/pagination/index.ts:
--------------------------------------------------------------------------------
1 | import Pagination, { PaginationOptions } from "./Pagination";
2 |
3 | export {
4 | Pagination
5 | };
6 |
7 | export type {
8 | PaginationOptions
9 | };
10 |
--------------------------------------------------------------------------------
/src/pagination/renderer/BulletRenderer.ts:
--------------------------------------------------------------------------------
1 | import { PAGINATION } from "../../const";
2 | import { addClass, removeClass } from "../../utils";
3 |
4 | import Renderer from "./Renderer";
5 |
6 | class BulletRenderer extends Renderer {
7 | private _bullets: HTMLElement[] = [];
8 | private _prevIndex: number = -1;
9 |
10 | private get _bulletClass() {
11 | const pagination = this._pagination;
12 | return `${pagination.classPrefix}-${PAGINATION.BULLET_SUFFIX}`;
13 | }
14 |
15 | private get _activeClass() {
16 | const pagination = this._pagination;
17 | return `${pagination.classPrefix}-${PAGINATION.BULLET_ACTIVE_SUFFIX}`;
18 | }
19 |
20 | public destroy() {
21 | this._bullets = [];
22 | this._prevIndex = -1;
23 | }
24 |
25 | public render() {
26 | const flicking = this._flicking;
27 | const pagination = this._pagination;
28 | const wrapper = this._wrapper;
29 | const bulletClass = this._bulletClass;
30 | const activeClass = this._activeClass;
31 | const renderBullet = pagination.renderBullet;
32 | const renderActiveBullet = pagination.renderActiveBullet;
33 | const bulletWrapperClass = `${pagination.classPrefix}-${PAGINATION.BULLET_WRAPPER_SUFFIX}`;
34 | const anchorPoints = flicking.camera.anchorPoints;
35 |
36 | addClass(wrapper, bulletWrapperClass);
37 |
38 | wrapper.innerHTML = anchorPoints
39 | .map((anchorPoint, index) => {
40 | if (renderActiveBullet && anchorPoint.panel.index === flicking.index) {
41 | return renderActiveBullet(bulletClass, index);
42 | } else {
43 | return renderBullet(bulletClass, index);
44 | }
45 | })
46 | .join("\n");
47 |
48 | const bullets = [].slice.call(wrapper.children) as HTMLElement[];
49 |
50 | bullets.forEach((bullet, index) => {
51 | const anchorPoint = anchorPoints[index];
52 |
53 | if (anchorPoint.panel.index === flicking.index) {
54 | addClass(bullet, activeClass);
55 | this._prevIndex = index;
56 | }
57 |
58 | this._addBulletEvents(bullet, index);
59 | });
60 |
61 | this._bullets = bullets;
62 | }
63 |
64 | public update(index: number) {
65 | const flicking = this._flicking;
66 | const pagination = this._pagination;
67 | const wrapper = this._wrapper;
68 | const bullets = this._bullets;
69 | const bulletClass = this._bulletClass;
70 | const activeClass = this._activeClass;
71 | const prevIndex = this._prevIndex;
72 | const anchorPoints = flicking.camera.anchorPoints;
73 | const renderBullet = pagination.renderBullet;
74 | const renderActiveBullet = pagination.renderActiveBullet;
75 |
76 | if (anchorPoints.length <= 0) return;
77 |
78 | const anchorOffset = anchorPoints[0].panel.index;
79 | const activeBulletIndex = index - anchorOffset;
80 |
81 | if (prevIndex === activeBulletIndex) return;
82 |
83 | if (renderActiveBullet) {
84 | const prevBullet = bullets[prevIndex];
85 | if (prevBullet) {
86 | const newBullet = this._createBulletFromString(
87 | renderBullet(bulletClass, prevIndex),
88 | prevIndex
89 | );
90 | prevBullet.parentElement!.replaceChild(newBullet, prevBullet);
91 | bullets[prevIndex] = newBullet;
92 | }
93 |
94 | const activeBullet = bullets[activeBulletIndex];
95 | const newActiveBullet = this._createBulletFromString(
96 | renderActiveBullet(`${bulletClass} ${activeClass}`, activeBulletIndex),
97 | activeBulletIndex
98 | );
99 |
100 | wrapper.replaceChild(newActiveBullet, activeBullet);
101 | bullets[activeBulletIndex] = newActiveBullet;
102 | } else {
103 | const activeBullet = bullets[activeBulletIndex];
104 | const prevBullet = bullets[prevIndex];
105 |
106 | if (prevBullet) {
107 | removeClass(prevBullet, activeClass);
108 | }
109 |
110 | addClass(activeBullet, activeClass);
111 | }
112 |
113 | this._prevIndex = activeBulletIndex;
114 | }
115 | }
116 |
117 | export default BulletRenderer;
118 |
--------------------------------------------------------------------------------
/src/pagination/renderer/FractionRenderer.ts:
--------------------------------------------------------------------------------
1 | import { PAGINATION } from "../../const";
2 | import { addClass } from "../../utils";
3 |
4 | import Renderer from "./Renderer";
5 |
6 | class FractionRenderer extends Renderer {
7 | private _prevIndex: number = -1;
8 | private _prevTotal: number = -1;
9 |
10 | public destroy(): void {
11 | this._prevIndex = -1;
12 | this._prevTotal = -1;
13 | }
14 |
15 | public render() {
16 | const flicking = this._flicking;
17 | const wrapper = this._wrapper;
18 | const pagination = this._pagination;
19 | const fractionWrapperClass = `${pagination.classPrefix}-${PAGINATION.FRACTION_WRAPPER_SUFFIX}`;
20 | const fractionCurrentClass = `${pagination.classPrefix}-${PAGINATION.FRACTION_CURRENT_SUFFIX}`;
21 | const fractionTotalClass = `${pagination.classPrefix}-${PAGINATION.FRACTION_TOTAL_SUFFIX}`;
22 |
23 | addClass(wrapper, fractionWrapperClass);
24 |
25 | wrapper.innerHTML = pagination.renderFraction(fractionCurrentClass, fractionTotalClass);
26 |
27 | this.update(flicking.index);
28 | }
29 |
30 | public update(index: number) {
31 | const flicking = this._flicking;
32 | const wrapper = this._wrapper;
33 | const pagination = this._pagination;
34 |
35 | const anchorPoints = flicking.camera.anchorPoints;
36 | const currentIndex = anchorPoints.length > 0
37 | ? index - anchorPoints[0].panel.index + 1
38 | : 0;
39 | const anchorCount = anchorPoints.length;
40 |
41 | if (currentIndex === this._prevIndex && anchorCount === this._prevTotal) return;
42 |
43 | const fractionCurrentSelector = `.${pagination.classPrefix}-${PAGINATION.FRACTION_CURRENT_SUFFIX}`;
44 | const fractionTotalSelector = `.${pagination.classPrefix}-${PAGINATION.FRACTION_TOTAL_SUFFIX}`;
45 |
46 | const currentWrapper = wrapper.querySelector(fractionCurrentSelector) as HTMLElement;
47 | const totalWrapper = wrapper.querySelector(fractionTotalSelector) as HTMLElement;
48 |
49 | currentWrapper.innerHTML = pagination.fractionCurrentFormat(currentIndex);
50 | totalWrapper.innerHTML = pagination.fractionTotalFormat(anchorCount);
51 |
52 | this._prevIndex = currentIndex;
53 | this._prevTotal = anchorCount;
54 | }
55 | }
56 |
57 | export default FractionRenderer;
58 |
--------------------------------------------------------------------------------
/src/pagination/renderer/Renderer.ts:
--------------------------------------------------------------------------------
1 | import Flicking, { FlickingError } from "@egjs/flicking";
2 |
3 | import { BROWSER } from "../../event";
4 | import Pagination from "../Pagination";
5 |
6 | export interface RendererOptions {
7 | flicking: Flicking;
8 | pagination: Pagination;
9 | wrapper: HTMLElement;
10 | }
11 |
12 | abstract class Renderer {
13 | protected _flicking: Flicking;
14 | protected _pagination: Pagination;
15 | protected _wrapper: HTMLElement;
16 |
17 | public constructor({
18 | flicking,
19 | pagination,
20 | wrapper
21 | }: RendererOptions) {
22 | this._flicking = flicking;
23 | this._pagination = pagination;
24 | this._wrapper = wrapper;
25 | }
26 |
27 | public abstract destroy(): void;
28 | public abstract render(): void;
29 | public abstract update(index: number): void;
30 |
31 | protected _createBulletFromString(html: string, index: number) {
32 | const range = document.createRange();
33 | const frag = range.createContextualFragment(html);
34 | const bullet = frag.firstChild as HTMLElement;
35 |
36 | this._addBulletEvents(bullet, index);
37 |
38 | return bullet;
39 | }
40 |
41 | protected _addBulletEvents(bullet: HTMLElement, index: number) {
42 | const anchorPoints = this._flicking.camera.anchorPoints;
43 | const panelIndex = anchorPoints[index].panel.index;
44 |
45 | bullet.addEventListener(BROWSER.MOUSE_DOWN, e => {
46 | e.stopPropagation();
47 | });
48 |
49 | bullet.addEventListener(BROWSER.TOUCH_START, e => {
50 | e.stopPropagation();
51 | }, { passive: true });
52 |
53 | bullet.addEventListener(BROWSER.CLICK, () => {
54 | this._flicking.moveTo(panelIndex)
55 | .catch(err => {
56 | if (err instanceof FlickingError) return;
57 | throw err;
58 | });
59 | });
60 | }
61 | }
62 |
63 | export default Renderer;
64 |
--------------------------------------------------------------------------------
/src/pagination/renderer/ScrollBulletRenderer.ts:
--------------------------------------------------------------------------------
1 | import { DIRECTION } from "@egjs/flicking";
2 |
3 | import { PAGINATION } from "../../const";
4 | import { addClass, removeClass } from "../../utils";
5 |
6 | import Renderer from "./Renderer";
7 |
8 | class ScrollBulletRenderer extends Renderer {
9 | private _bullets: HTMLElement[] = [];
10 | private _bulletSize: number = 0;
11 | private _previousIndex: number = -1;
12 | private _sliderIndex: number = -1;
13 |
14 | public destroy(): void {
15 | this._bullets = [];
16 | this._bulletSize = 0;
17 | this._previousIndex = -1;
18 | this._sliderIndex = -1;
19 | }
20 |
21 | public render() {
22 | const wrapper = this._wrapper;
23 | const flicking = this._flicking;
24 | const pagination = this._pagination;
25 | const renderBullet = pagination.renderBullet;
26 | const anchorPoints = flicking.camera.anchorPoints;
27 |
28 | const dynamicWrapperClass = `${pagination.classPrefix}-${PAGINATION.SCROLL_WRAPPER_SUFFIX}`;
29 | const bulletClass = `${pagination.classPrefix}-${PAGINATION.BULLET_SUFFIX}`;
30 | const sliderClass = `${pagination.classPrefix}-${PAGINATION.SCROLL_SLIDER_SUFFIX}`;
31 | const uninitClass = `${pagination.classPrefix}-${PAGINATION.SCROLL_UNINIT_SUFFIX}`;
32 |
33 | const sliderEl = document.createElement("div");
34 |
35 | addClass(sliderEl, sliderClass);
36 | addClass(wrapper, uninitClass);
37 | addClass(wrapper, dynamicWrapperClass);
38 | wrapper.appendChild(sliderEl);
39 |
40 | sliderEl.innerHTML = anchorPoints
41 | .map((_, index) => renderBullet(bulletClass, index))
42 | .join("\n");
43 |
44 | const bullets = [].slice.call(sliderEl.children) as HTMLElement[];
45 |
46 | bullets.forEach((bullet, index) => {
47 | this._addBulletEvents(bullet, index);
48 | });
49 |
50 | if (bullets.length <= 0) return;
51 |
52 | const bulletStyle = getComputedStyle(bullets[0]);
53 | const bulletSize = bullets[0].clientWidth + parseFloat(bulletStyle.marginLeft) + parseFloat(bulletStyle.marginRight);
54 |
55 | wrapper.style.width = `${bulletSize * pagination.bulletCount}px`;
56 |
57 | this._bullets = bullets;
58 | this._bulletSize = bulletSize;
59 | this._previousIndex = -1;
60 |
61 | this.update(this._flicking.index);
62 |
63 | window.requestAnimationFrame(() => {
64 | removeClass(wrapper, uninitClass);
65 | });
66 | }
67 |
68 | public update(index: number) {
69 | const pagination = this._pagination;
70 | const flicking = this._flicking;
71 | const bullets = this._bullets;
72 | const prevIndex = this._previousIndex;
73 |
74 | const renderBullet = pagination.renderBullet;
75 | const renderActiveBullet = pagination.renderActiveBullet;
76 |
77 | const anchorPoints = flicking.camera.anchorPoints;
78 | const anchorOffset = anchorPoints[0].panel.index;
79 | const activeIndex = index - anchorOffset;
80 |
81 | if (anchorPoints.length <= 0) return;
82 |
83 | const bulletClass = `${pagination.classPrefix}-${PAGINATION.BULLET_SUFFIX}`;
84 | const bulletActiveClass = `${pagination.classPrefix}-${PAGINATION.BULLET_ACTIVE_SUFFIX}`;
85 | const prevClassPrefix = `${pagination.classPrefix}-${PAGINATION.SCROLL_PREV_SUFFIX}`;
86 | const nextClassPrefix = `${pagination.classPrefix}-${PAGINATION.SCROLL_NEXT_SUFFIX}`;
87 | const bulletPrevClass = (offset: number) => `${prevClassPrefix}${offset > 1 ? offset : ""}`;
88 | const bulletNextClass = (offset: number) => `${nextClassPrefix}${offset > 1 ? offset : ""}`;
89 |
90 | const prevClassRegex = new RegExp(`^${prevClassPrefix}`);
91 | const nextClassRegex = new RegExp(`^${nextClassPrefix}`);
92 |
93 | if (renderActiveBullet) {
94 | const prevBullet = bullets[prevIndex];
95 | if (prevBullet) {
96 | const newBullet = this._createBulletFromString(
97 | renderBullet(bulletClass, prevIndex),
98 | prevIndex
99 | );
100 | prevBullet.parentElement!.replaceChild(newBullet, prevBullet);
101 | bullets[prevIndex] = newBullet;
102 | }
103 |
104 | const activeBullet = bullets[activeIndex];
105 | if (activeBullet) {
106 | const newActiveBullet = this._createBulletFromString(
107 | renderActiveBullet(bulletClass, activeIndex),
108 | activeIndex
109 | );
110 |
111 | activeBullet.parentElement!.replaceChild(newActiveBullet, activeBullet);
112 | bullets[activeIndex] = newActiveBullet;
113 | }
114 | }
115 |
116 | bullets.forEach((bullet, idx) => {
117 | const indexOffset = idx - activeIndex;
118 | const classList = bullet.className.split(" ");
119 |
120 | for (const className of classList) {
121 | if (className === bulletActiveClass || prevClassRegex.test(className) || nextClassRegex.test(className)) {
122 | removeClass(bullet, className);
123 | }
124 | }
125 |
126 | if (indexOffset === 0) {
127 | addClass(bullet, bulletActiveClass);
128 | } else if (indexOffset > 0) {
129 | addClass(bullet, bulletNextClass(Math.abs(indexOffset)));
130 | } else {
131 | addClass(bullet, bulletPrevClass(Math.abs(indexOffset)));
132 | }
133 | });
134 |
135 | pagination.scrollOnChange(activeIndex, {
136 | total: bullets.length,
137 | prevIndex,
138 | sliderIndex: this._sliderIndex,
139 | direction: activeIndex > prevIndex ? DIRECTION.NEXT : DIRECTION.PREV,
140 | bullets: [...bullets],
141 | moveTo: this.moveTo
142 | });
143 |
144 | this._previousIndex = activeIndex;
145 | }
146 |
147 | public moveTo = (index: number): void => {
148 | const pagination = this._pagination;
149 | const sliderEl = this._wrapper.firstElementChild as HTMLElement;
150 | const bulletSize = this._bulletSize;
151 | const wrapperSize = bulletSize * pagination.bulletCount;
152 |
153 | sliderEl.style.transform = `translate(${wrapperSize / 2 - (index + 0.5) * bulletSize}px)`;
154 | this._sliderIndex = index;
155 | };
156 | }
157 |
158 | export default ScrollBulletRenderer;
159 |
--------------------------------------------------------------------------------
/src/type.ts:
--------------------------------------------------------------------------------
1 | interface ScrollContext {
2 | total: number;
3 | prevIndex: number;
4 | sliderIndex: number;
5 | direction: "NEXT" | "PREV";
6 | bullets: HTMLElement[];
7 | moveTo: (index: number) => void;
8 | }
9 |
10 | export default ScrollContext;
11 |
--------------------------------------------------------------------------------
/src/utils.ts:
--------------------------------------------------------------------------------
1 | export const addClass = (el: HTMLElement, className: string) => {
2 | if (!el) return;
3 |
4 | if (el.classList) {
5 | el.classList.add(className);
6 | } else {
7 | const classes = el.className.split(" ");
8 |
9 | if (classes.indexOf(className) < 0) {
10 | el.className = `${el.className} ${className}`;
11 | }
12 | }
13 | };
14 |
15 | export const removeClass = (el: HTMLElement, className: string) => {
16 | if (!el) return;
17 |
18 | if (el.classList) {
19 | el.classList.remove(className);
20 | } else {
21 | const classRegex = new RegExp(`( |^)${className}( |$)`, "g");
22 | el.className.replace(classRegex, " ");
23 | }
24 | };
25 |
26 | export const getElement = (selector: string, parent: HTMLElement, pluginName: string) => {
27 | const el = parent.querySelector(selector);
28 |
29 | if (!el) {
30 | throw new Error(`[Flicking-${pluginName}] Couldn't find element with the given selector: ${selector}`);
31 | }
32 |
33 | return el as HTMLElement;
34 | };
35 |
--------------------------------------------------------------------------------
/test/hammer-simulator.run.js:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unsafe-call */
2 | /* eslint-disable @typescript-eslint/no-unsafe-member-access */
3 |
4 | Simulator.setType("touch");
5 | Simulator.events.touch.fakeSupport();
6 |
--------------------------------------------------------------------------------
/test/manual/arrow.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Arrow
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
Arrow "is-circle"
33 |
34 |
35 |
36 |
37 |
38 |
39 |
40 |
41 |
42 |
43 |
44 |
45 |
46 |
47 |
48 |
49 |
50 |
51 |
52 |
53 |
54 |
Arrow "is-outside"
55 |
56 |
57 |
58 |
59 |
60 |
61 |
62 |
63 |
64 |
65 |
66 |
67 |
68 |
69 |
70 |
71 |
72 |
73 |
74 |
75 |
76 |
77 |
--------------------------------------------------------------------------------
/test/manual/autoplay.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 | Flicking plugins test page
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 | AutoPlay
13 |
14 |
15 |
16 |
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 | freeScroll
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc orci ante, placerat in nunc vel, cursus laoreet turpis. Quisque elementum turpis eget ligula condimentum gravida. Proin vulputate est arcu. Nunc elementum, massa a tempus dignissim, urna elit porttitor odio, in fringilla dolor metus finibus ante. Morbi sagittis finibus posuere. Interdum et malesuada fames ac ante ipsum primis in faucibus. Ut blandit, neque et scelerisque lacinia, libero ipsum dictum massa, eget cursus arcu turpis quis nunc. Proin rhoncus auctor arcu at bibendum. Aenean urna diam, cursus in lacus non, dignissim finibus purus. Nulla semper massa lorem, a elementum eros tempus et. Curabitur quis elit eget lorem bibendum ultrices sodales sit amet risus. Aenean ipsum justo, varius non pharetra eget, fermentum in sapien.
38 |
39 | Fusce cursus finibus tellus, at consectetur ligula maximus eget. Nunc faucibus sollicitudin odio ut placerat. Sed et rhoncus diam. Curabitur gravida iaculis tempus. Pellentesque gravida malesuada nulla, blandit consectetur metus convallis in. Etiam vel sodales ipsum, vitae maximus nunc. Mauris a mattis quam. Vestibulum vitae est mauris. In interdum quam lectus, varius tempus magna ultricies ut. Mauris nec est justo. Etiam neque magna, euismod sed erat vitae, sollicitudin tincidunt nisl.
40 |
41 | Lorem ipsum dolor sit amet, consectetur adipiscing elit. Praesent ac ex orci. Praesent nec porttitor ex. Ut ut leo faucibus, viverra odio in, ultricies nulla. Duis id lectus sapien. Donec eleifend massa nec urna semper, sit amet accumsan elit eleifend. Nullam tempus dui vitae rhoncus semper. Vestibulum ante ipsum primis in faucibus orci luctus et ultrices posuere cubilia curae; Suspendisse vitae erat vitae eros luctus congue vitae at elit. Nam a diam nec turpis consequat sodales. Ut ut semper massa. Aenean a diam eu odio venenatis pellentesque vel non tellus. Nulla augue arcu, placerat et ligula quis, ultrices sodales leo.
42 |
43 | Integer nec libero non justo blandit tincidunt vel id odio. Etiam ornare vel metus vitae ullamcorper. Sed porta lacus eget lectus sodales, non cursus nibh posuere. Suspendisse gravida vestibulum lacus, ac pulvinar purus maximus quis. Aliquam consequat lacinia pellentesque. Curabitur non auctor metus, ut sollicitudin ex. Curabitur elit turpis, porta eget magna in, semper lobortis lacus. Mauris aliquet sapien nec turpis euismod, sit amet consequat purus convallis. Aliquam congue magna vel nulla varius, sit amet condimentum nunc dignissim. Morbi malesuada ante finibus posuere tempor. Proin efficitur quam nec lectus consequat, a euismod ex gravida. Aliquam euismod arcu at nibh venenatis interdum. Donec augue nibh, convallis ac luctus quis, luctus sit amet quam. Phasellus dapibus venenatis dui at consequat. Aenean non venenatis lorem, a imperdiet eros. Ut ac diam id nulla placerat dapibus.
44 |
45 | Ut non hendrerit quam. Ut eu leo at ex ultrices pretium. Sed nec mauris est. Donec vehicula, nunc non fringilla iaculis, augue nisl pulvinar est, efficitur feugiat erat turpis nec tellus. Nullam condimentum dui sed velit dapibus, ut consectetur enim ornare. Nam sagittis nec massa vel faucibus. Donec vestibulum lobortis turpis, non ullamcorper urna sagittis vel. Phasellus porta orci vel arcu congue, ac aliquam ipsum volutpat. Morbi vel nisi ante. Fusce egestas elit sit amet malesuada placerat. Cras euismod, eros tincidunt aliquet posuere, odio nisi lacinia metus, ut consequat tellus enim nec justo. Nunc iaculis luctus dolor maximus semper. Suspendisse ac nulla ut dui feugiat fringilla.
46 |
47 |
48 |
55 |
56 |
57 |
58 |
--------------------------------------------------------------------------------
/test/manual/css/arrow.css:
--------------------------------------------------------------------------------
1 | .overflow-wrapper {
2 | overflow: visible;
3 | position: relative;
4 | }
5 |
--------------------------------------------------------------------------------
/test/manual/css/common.css:
--------------------------------------------------------------------------------
1 | html, body {
2 | width: 100%;
3 | height: 100%;
4 | }
5 | body {
6 | display: flex;
7 | flex-direction: column;
8 | align-items: center;
9 | }
10 | .flick-container {
11 | width: 500px;
12 | height: 130px;
13 | margin: 0px auto;
14 | background: #eee;
15 | background: rgba(55, 55, 55 0.1);
16 | border-radius: 5px;
17 | }
18 | .flicking-camera > * {
19 | width: 200px;
20 | height: 200px;
21 | }
22 | #parallax .panel {
23 | width: 500px;
24 | position: relative;
25 | overflow: hidden;
26 | margin-right: 10px;
27 | }
28 | .autoplay .panel:after {
29 | content: "";
30 | position: relative;
31 | display: block;
32 | border-radius: 5px;
33 | width: 200px;
34 | background: #f55;
35 | height: 120px
36 | }
37 | .autoplay .panel.p1:after {
38 | background: #F47071;
39 | }
40 | .autoplay .panel.p2:after {
41 | background: #F69462;
42 | }
43 | .autoplay .panel.p3:after {
44 | background: #EDE484;
45 | }
46 | .autoplay .panel.p4:after {
47 | background: #90F290;
48 | }
49 | .autoplay .panel.p5:after {
50 | background: #78CAFF;
51 | }
52 | .flick-container .panel img {
53 | user-select: none;
54 | -webkit-user-drag: none;
55 | position: absolute;
56 | top: -100%;
57 | bottom: -100%;
58 | left: 50%;
59 | }
60 |
--------------------------------------------------------------------------------
/test/manual/css/pagination.css:
--------------------------------------------------------------------------------
1 | .pagination-bound-bullets,
2 | .pagination-bound-scroll {
3 | font-size: 0;
4 | }
5 |
6 | .pagination-bound-scroll {
7 | left: 50%;
8 | transform: translate(-50%);
9 | white-space: nowrap;
10 | overflow: hidden;
11 | }
12 |
13 | .pagination-bound-scroll .pagination-bound-slider {
14 | transition: .2s transform;
15 | }
16 |
17 | .pagination-bound-bullet {
18 | display: inline-block;
19 | width: 8px;
20 | height: 8px;
21 | margin: 0 4px;
22 | border-radius: 50%;
23 | background-color: rgb(10 10 10 / 10%);
24 | cursor: pointer;
25 | font-size: 1rem;
26 | }
27 |
28 | .pagination-bound-scroll .pagination-bound-bullet {
29 | vertical-align: middle;
30 | position: relative;
31 | transition: .2s transform;
32 | }
33 |
34 | .pagination-bound-bullet-active {
35 | background-color: #f2a65e;
36 | }
37 |
38 | .pagination-bound-scroll .pagination-bound-bullet {
39 | vertical-align: middle;
40 | position: relative;
41 | transition: .2s transform,.2s left;
42 | transform: scale(1);
43 | }
44 |
--------------------------------------------------------------------------------
/test/manual/css/sync.css:
--------------------------------------------------------------------------------
1 | .flicking-camera .card {
2 | height: 120px;
3 | }
4 |
5 | .slide {
6 | width: 100%;
7 | height: 200px;
8 | }
9 |
10 | .slide img {
11 | width: 100%;
12 | }
13 |
14 | .card img {
15 | width: 100%;
16 | height: 100%;
17 | }
18 |
19 | .flicking-thumbnail-active {
20 | border: 4px solid blue;
21 | }
22 |
23 | .sync-wrapper {
24 | font-size: 50px;
25 | color: white;
26 | position: relative;
27 | display: flex;
28 | justify-content: center;
29 | align-items: center;
30 | }
31 |
32 | .sync-index {
33 | position: absolute;
34 | }
35 |
--------------------------------------------------------------------------------
/test/manual/img/bg01.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naver/egjs-flicking-plugins/87671e2b11a36dfb021bb3af9dc49a40fa390b9e/test/manual/img/bg01.jpg
--------------------------------------------------------------------------------
/test/manual/img/bg02.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naver/egjs-flicking-plugins/87671e2b11a36dfb021bb3af9dc49a40fa390b9e/test/manual/img/bg02.jpg
--------------------------------------------------------------------------------
/test/manual/img/bg03.jpg:
--------------------------------------------------------------------------------
https://raw.githubusercontent.com/naver/egjs-flicking-plugins/87671e2b11a36dfb021bb3af9dc49a40fa390b9e/test/manual/img/bg03.jpg
--------------------------------------------------------------------------------
/test/manual/js/arrow.js:
--------------------------------------------------------------------------------
1 | const flicking = new Flicking("#arrow", { moveType: "freeScroll" });
2 |
3 | flicking.addPlugins(new Flicking.Plugins.Arrow({
4 | moveByViewportSize: true
5 | }));
6 |
7 | const flicking2 = new Flicking("#arrow2", { circular: true });
8 |
9 | flicking2.addPlugins(new Flicking.Plugins.Arrow({
10 | moveCount: 3
11 | }));
12 |
13 | const flicking3 = new Flicking("#arrow3");
14 |
15 | flicking3.addPlugins(new Flicking.Plugins.Arrow({
16 | parentEl: document.querySelector("#arrow3-wrapper")
17 | }));
18 |
--------------------------------------------------------------------------------
/test/manual/js/autoplay.js:
--------------------------------------------------------------------------------
1 | const flicking = new Flicking("#autoplay", {
2 | circular: true
3 | });
4 |
5 | // AutoPlay
6 | const autoplay = new Flicking.Plugins.AutoPlay({
7 | stopOnHover: true
8 | });
9 | flicking.addPlugins(autoplay)
10 |
11 | document.getElementById("play").addEventListener("click", () => {
12 | autoplay.play();
13 | });
14 |
15 | document.getElementById("stop").addEventListener("click", () => {
16 | autoplay.stop();
17 | });
18 |
19 | const flicking2 = new Flicking("#parallax", {
20 | circular: true
21 | });
22 |
23 | flicking2.addPlugins(new Flicking.Plugins.Parallax("img"));
24 |
25 |
26 | const flicking3 = new Flicking("#autoplay2", {
27 | align: "prev",
28 | moveType: "freeScroll",
29 | circular: true,
30 | easing: x => x
31 | // duration: 5000,
32 | });
33 |
34 | // AutoPlay
35 | const autoplay3 = new Flicking.Plugins.AutoPlay({
36 | direction: "prev",
37 | duration: 0,
38 | animationDuration: 1000
39 | });
40 | flicking3.addPlugins(autoplay3);
41 |
--------------------------------------------------------------------------------
/test/manual/js/pagination.js:
--------------------------------------------------------------------------------
1 | const flicking = new Flicking("#pagination", { bound: true });
2 |
3 | flicking.addPlugins(new Flicking.Plugins.Pagination());
4 |
5 | const flicking1_1 = new Flicking("#pagination2", { bound: true });
6 |
7 | flicking1_1.addPlugins(new Flicking.Plugins.Pagination({
8 | renderBullet: (className, index) => `${index + 1}`,
9 | renderActiveBullet: (className) => ``
10 | }));
11 |
12 | const flicking2 = new Flicking("#pagination-number");
13 |
14 | flicking2.addPlugins(new Flicking.Plugins.Pagination({ type: "fraction" }));
15 |
16 | const flicking3 = new Flicking("#pagination-dynamic");
17 |
18 | flicking3.addPlugins(new Flicking.Plugins.Pagination({
19 | type: "scroll",
20 | renderBullet: (className, index) => `${index + 1}`,
21 | renderActiveBullet: (className) => ``
22 | }));
23 |
24 | const flicking4 = new Flicking("#pagination-dynamic2");
25 |
26 | flicking4.addPlugins(new Flicking.Plugins.Pagination({
27 | type: "scroll",
28 | classPrefix: "pagination-bound",
29 | renderBullet: (className) => ``,
30 | scrollOnChange: (index, ctx) => {
31 | if (ctx.sliderIndex < 0) {
32 | const firstIndex = Math.min(Math.max(index, 2), ctx.total - 2);
33 | ctx.moveTo(firstIndex);
34 | applyBulletSize(firstIndex, ctx);
35 | return;
36 | }
37 |
38 | const offset = Math.abs(index - ctx.sliderIndex);
39 | if (offset < 2) return;
40 |
41 | const newIndex = Math.min(Math.max(index - Math.sign(index - ctx.sliderIndex), 2), ctx.total - 3);
42 | ctx.moveTo(newIndex);
43 |
44 | applyBulletSize(newIndex, ctx);
45 | }
46 | }));
47 |
48 | const applyBulletSize = (newIndex, ctx) => {
49 | const bullets = ctx.bullets;
50 | const visibleBullets = bullets.slice(newIndex - 2, newIndex + 3)
51 | .map((bullet, idx) => ({
52 | index: newIndex - 2 + idx,
53 | bullet
54 | }));
55 |
56 | const middle = visibleBullets.splice(1, 3);
57 |
58 | visibleBullets.forEach(({ index, bullet }) => {
59 | if (index === 0 || index === ctx.total - 1) {
60 | bullet.style.transform = "";
61 | } else {
62 | bullet.style.transform = "scale(0.5)";
63 | }
64 | });
65 |
66 | middle.forEach(({ bullet }) => {
67 | bullet.style.transform = "";
68 | });
69 | };
70 |
71 | document.querySelector("#remove").addEventListener("click", () => {
72 | flicking.remove(0, flicking.panelCount);
73 | });
74 |
75 | document.querySelector("#add").addEventListener("click", () => {
76 | flicking.append(``);
77 | });
78 |
--------------------------------------------------------------------------------
/test/manual/js/perspective.js:
--------------------------------------------------------------------------------
1 | const flicking = new Flicking("#perspective");
2 |
3 | // Perspective
4 | const perspective = new Flicking.Plugins.Perspective();
5 | flicking.addPlugins(perspective)
6 |
7 |
--------------------------------------------------------------------------------
/test/manual/js/sync.js:
--------------------------------------------------------------------------------
1 | const flicking0 = new Flicking("#flick0", {
2 | bound: true
3 | });
4 |
5 | const flicking1 = new Flicking("#flick1", {
6 | bound: true
7 | });
8 |
9 | const flicking2 = new Flicking("#flick2", {
10 | bound: true
11 | });
12 |
13 | flicking0.addPlugins(new Flicking.Plugins.Sync({
14 | type: "camera",
15 | synchronizedFlickingOptions: [
16 | {
17 | flicking: flicking0,
18 | isClickable: true
19 | },
20 | {
21 | flicking: flicking1,
22 | isClickable: true
23 | },
24 | {
25 | flicking: flicking2,
26 | isClickable: true
27 | }
28 | ]
29 | }));
30 |
31 | const flicking3 = new Flicking("#flick3");
32 |
33 | const flicking4 = new Flicking("#flick4", {
34 | bound: true
35 | });
36 |
37 | flicking3.addPlugins(new Flicking.Plugins.Sync({
38 | type: "index",
39 | synchronizedFlickingOptions: [
40 | {
41 | flicking: flicking3,
42 | isSlidable: true
43 | },
44 | {
45 | flicking: flicking4,
46 | isClickable: true,
47 | activeClass: "flicking-thumbnail-active"
48 | }
49 | ]
50 | }));
51 |
--------------------------------------------------------------------------------
/test/manual/pagination.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
Pagination
16 |
31 |
32 |
33 | Pagination
34 |
49 | Pagination - Fraction
50 |
60 | Pagination - Scroll
61 |
74 | Pagination - Scroll-Bound
75 |
88 |
89 |
90 |
91 |
92 |
--------------------------------------------------------------------------------
/test/manual/perspective.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
15 |
16 |
Perspective
17 |
18 |
19 |
20 |
21 |
22 |
23 |
24 |
25 |
26 |
27 |
28 |
29 |
30 |
31 |
32 |
33 |
34 |
35 |
--------------------------------------------------------------------------------
/test/manual/sync.html:
--------------------------------------------------------------------------------
1 |
2 |
3 |
4 |
5 |
6 |
7 |
8 |
9 |
10 |
11 |
12 |
13 |
14 |
Sync Between Flickings
15 |
16 |
17 | 🍔 Hamburger
18 | 🍕 Pizza
19 | 🍞 Bread
20 | 🍔 Hamburger
21 | 🍕 Pizza
22 | 🍞 Bread
23 | 🍔 Hamburger
24 | 🍕 Pizza
25 | 🍞 Bread
26 | 🍔 Hamburger
27 | 🍕 Pizza
28 | 🍞 Bread
29 | 🍔 Hamburger
30 | 🍕 Pizza
31 | 🍞 Bread
32 | 🍔 Hamburger
33 | 🍕 Pizza
34 | 🍞 Bread
35 | 🍔 Hamburger
36 | 🍕 Pizza
37 | 🍞 Bread
38 | 🍔 Hamburger
39 | 🍕 Pizza
40 | 🍞 Bread
41 | 🍔 Hamburger
42 | 🍕 Pizza
43 | 🍞 Bread
44 |
45 |
46 |
47 |
48 | 🍎 Apple
49 | 🍉 Watermelon
50 | 🥝 Kiwi
51 | 🍎 Apple
52 | 🍉 Watermelon
53 | 🥝 Kiwi
54 | 🍎 Apple
55 | 🍉 Watermelon
56 | 🥝 Kiwi
57 |
58 |
59 |
60 |
61 | 🥛 Milk
62 | ☕ Coffee
63 | 🍵 Green tea
64 | 🥛 Milk
65 | ☕ Coffee
66 | 🍵 Green tea
67 | 🥛 Milk
68 | ☕ Coffee
69 | 🍵 Green tea
70 | 🥛 Milk
71 | ☕ Coffee
72 | 🍵 Green tea
73 | 🥛 Milk
74 | ☕ Coffee
75 | 🍵 Green tea
76 | 🥛 Milk
77 | ☕ Coffee
78 | 🍵 Green tea
79 |
80 |
81 |
Sync By Index
82 |
83 |
84 |
1
85 |
2
86 |
3
87 |
4
88 |
5
89 |
6
90 |
7
91 |
8
92 |
9
93 |
94 |
95 |
96 |
97 |
1
98 |
2
99 |
3
100 |
4
101 |
5
102 |
6
103 |
7
104 |
8
105 |
9
106 |
107 |
108 |
109 |
110 |
111 |
112 |
--------------------------------------------------------------------------------
/test/setup.js:
--------------------------------------------------------------------------------
1 | window.timer = sinon.useFakeTimers();
2 |
3 | beforeEach(() => {
4 | window.timer.reset();
5 | });
6 |
--------------------------------------------------------------------------------
/test/unit/.eslintrc.js:
--------------------------------------------------------------------------------
1 | module.exports = {
2 | "extends": [
3 | "../../.eslintrc.js"
4 | ],
5 | "parser": "@typescript-eslint/parser",
6 | "parserOptions": {
7 | "project": "tsconfig.eslint.json",
8 | "sourceType": "module"
9 | },
10 | "rules": {
11 | "import/order": "off",
12 | "guard-for-in": "off",
13 | "max-classes-per-file": "off",
14 | "prefer-arrow/prefer-arrow-functions": "off",
15 | "arrow-body-style": "off",
16 | "@typescript-eslint/ban-types": "off",
17 | "@typescript-eslint/no-unused-expressions": "off",
18 | "@typescript-eslint/no-unsafe-assignment": "off",
19 | "@typescript-eslint/ban-ts-comment": "off",
20 | "@typescript-eslint/no-empty-function": "off",
21 | "@typescript-eslint/no-unsafe-member-access": "off",
22 | "@typescript-eslint/no-unsafe-call": "off",
23 | "@typescript-eslint/no-unsafe-return": "off",
24 | "@typescript-eslint/naming-convention": "off",
25 | "@typescript-eslint/no-unused-vars": "off",
26 | "@typescript-eslint/restrict-template-expressions": "off",
27 | "@typescript-eslint/require-await": "off",
28 | "@typescript-eslint/restrict-plus-operands": "off",
29 | "@jsdoc/check-indentation": "off"
30 | }
31 | };
32 |
--------------------------------------------------------------------------------
/test/unit/Arrow.spec.ts:
--------------------------------------------------------------------------------
1 | import Flicking from "@egjs/flicking";
2 | import * as sinon from "sinon";
3 | import Arrow from "../../src/Arrow";
4 |
5 | import { cleanup, createArrowFixture } from "./utils";
6 |
7 | describe("Arrow", () => {
8 | afterEach(() => {
9 | cleanup();
10 | });
11 |
12 | it("should add touch start listener with passive: true", async () => {
13 | // Given
14 | const flicking = new Flicking(createArrowFixture(), { autoInit: false });
15 | const plugin = new Arrow();
16 |
17 | // When
18 | const prevArrow = flicking.element.querySelector(".flicking-arrow-prev");
19 | const nextArrow = flicking.element.querySelector(".flicking-arrow-next");
20 | const prevEventsSpy = sinon.spy(prevArrow, "addEventListener");
21 | const nextEventsSpy = sinon.spy(nextArrow, "addEventListener");
22 |
23 | flicking.addPlugins(plugin);
24 | await flicking.init();
25 |
26 | // Then
27 | expect(prevEventsSpy.calledWith("touchstart")).to.be.true;
28 | expect(nextEventsSpy.calledWith("touchstart")).to.be.true;
29 | expect(prevEventsSpy.args.filter(([type]) => type === "touchstart").every(([type, _, options]) => {
30 | return type === "touchstart" && options && (options as AddEventListenerOptions).passive;
31 | })).to.be.true;
32 | expect(nextEventsSpy.args.filter(([type]) => type === "touchstart").every(([type, _, options]) => {
33 | return type === "touchstart" && options && (options as AddEventListenerOptions).passive;
34 | })).to.be.true;
35 | });
36 | });
37 |
--------------------------------------------------------------------------------
/test/unit/AutoPlay.spec.ts:
--------------------------------------------------------------------------------
1 | /* eslint-disable @typescript-eslint/no-unused-expressions */
2 | import Flicking, { EVENTS } from "@egjs/flicking";
3 | import * as sinon from "sinon";
4 |
5 | import AutoPlay from "../../src/AutoPlay";
6 |
7 | import { createFlickingFixture, sandbox, tick, wait, waitEvent } from "./utils";
8 |
9 | describe("AutoPlay", () => {
10 | it("can receive older API of receiving duration and direction", () => {
11 | // Given & When
12 | const plugin = new AutoPlay({ duration: 500, direction: "PREV" });
13 |
14 | // Then
15 | expect(plugin.duration).to.equal(500);
16 | expect(plugin.direction).to.equal("PREV");
17 | });
18 |
19 | it("should call play after initializing", async () => {
20 | // Given
21 | const plugin = new AutoPlay();
22 | const flicking = new Flicking(createFlickingFixture());
23 | const playSpy = sinon.spy(plugin, "play");
24 |
25 | // When
26 | flicking.addPlugins(plugin);
27 | await waitEvent(flicking, "ready");
28 |
29 | // Then
30 | expect(playSpy.calledOnce).to.be.true;
31 | });
32 |
33 | it("should not call play after initializing if stopOnInit is true", async () => {
34 | // Given
35 | const plugin = new AutoPlay({ stopOnInit: true });
36 | const flicking = new Flicking(createFlickingFixture());
37 | const playSpy = sinon.spy(plugin, "play");
38 |
39 | // When
40 | await waitEvent(flicking, "ready");
41 | flicking.addPlugins(plugin);
42 |
43 | // Then
44 | expect(playSpy.called).to.be.false;
45 | expect(plugin.playing).to.be.false;
46 | });
47 |
48 | it("should call Flicking's move method after duration", async () => {
49 | // Given
50 | const plugin = new AutoPlay({ direction: "NEXT", duration: 500 });
51 | const flicking = new Flicking(createFlickingFixture());
52 | const nextStub = sinon.stub(flicking, "next");
53 |
54 | nextStub.resolves(void 0);
55 |
56 | // When
57 | flicking.addPlugins(plugin);
58 | await waitEvent(flicking, "ready");
59 |
60 | // Then
61 | expect(nextStub.called).to.be.false;
62 | tick(500);
63 | expect(nextStub.calledOnce).to.be.true;
64 | });
65 |
66 | it("should apply animationDuration to animation when moving panel", async () => {
67 | // Given
68 | const plugin = new AutoPlay({ direction: "NEXT", duration: 500, animationDuration: 200 });
69 | const flicking = new Flicking(createFlickingFixture());
70 | const nextSpy = sinon.spy(flicking, "next");
71 |
72 | // When
73 | flicking.addPlugins(plugin);
74 | await waitEvent(flicking, "ready");
75 |
76 | // Then
77 | expect(nextSpy.called).to.be.false;
78 | tick(500);
79 | expect(nextSpy.calledOnce).to.be.true;
80 | expect(nextSpy.firstCall.calledWith(200)).to.be.true;
81 | });
82 |
83 | it("can stop autoplay if stop is called before duration", () => {
84 | // Given
85 | const plugin = new AutoPlay({ direction: "NEXT", duration: 500 });
86 | const flicking = new Flicking(createFlickingFixture());
87 | const nextStub = sinon.stub(flicking, "next");
88 |
89 | nextStub.resolves(void 0);
90 |
91 | // When
92 | flicking.addPlugins(plugin);
93 |
94 | // Then
95 | expect(nextStub.called).to.be.false;
96 | tick(250);
97 | expect(nextStub.called).to.be.false;
98 | plugin.stop();
99 | tick(500);
100 | expect(nextStub.called).to.be.false;
101 | });
102 |
103 | it("should call stop if mouse is entered and stopOnHover is true", async () => {
104 | // Given
105 | const plugin = new AutoPlay({ stopOnHover: true });
106 | const flicking = new Flicking(createFlickingFixture());
107 | const stopSpy = sinon.spy();
108 |
109 | plugin.stop = stopSpy;
110 |
111 | // When
112 | flicking.addPlugins(plugin);
113 | await waitEvent(flicking, "ready");
114 | const wrapper = flicking.element;
115 |
116 | // Then
117 | expect(stopSpy.calledOnce).to.be.true; // removing previous one
118 | wrapper.dispatchEvent(new Event("mouseenter"));
119 | expect(stopSpy.calledTwice).to.be.true;
120 | });
121 |
122 | it("should call next after duration if mouse leaved and stopOnHover is true", async () => {
123 | // Given
124 | const plugin = new AutoPlay({ stopOnHover: true });
125 | const flicking = new Flicking(createFlickingFixture());
126 | const nextStub = sinon.stub(flicking, "next");
127 |
128 | nextStub.resolves(void 0);
129 |
130 | // When
131 | flicking.addPlugins(plugin);
132 | await waitEvent(flicking, "ready");
133 | const wrapper = flicking.element;
134 |
135 | // Then
136 | expect(nextStub.called).to.be.false;
137 | wrapper.dispatchEvent(new Event("mouseleave"));
138 | tick(2000);
139 | expect(nextStub.calledOnce).to.be.true;
140 | tick(2000);
141 | expect(nextStub.calledTwice).to.be.true;
142 | });
143 |
144 | it("should call next after delayAfterHover milliseconds when mouse leaved and stopOnHover is true", async () => {
145 | // Given
146 | const plugin = new AutoPlay({
147 | direction: "NEXT",
148 | duration: 1000,
149 | stopOnHover: true,
150 | delayAfterHover: 500
151 | });
152 | const flicking = new Flicking(createFlickingFixture());
153 | const nextStub = sinon.stub(flicking, "next");
154 |
155 | nextStub.resolves(void 0);
156 |
157 | // When
158 | flicking.addPlugins(plugin);
159 | await waitEvent(flicking, "ready");
160 | const wrapper = flicking.element;
161 |
162 | // Then
163 | expect(nextStub.called).to.be.false;
164 | tick(1200);
165 | expect(nextStub.calledOnce).to.be.true;
166 | wrapper.dispatchEvent(new Event("mouseenter"));
167 | tick(1200);
168 | expect(nextStub.calledOnce).to.be.true;
169 | wrapper.dispatchEvent(new Event("mouseleave"));
170 | tick(700);
171 | expect(nextStub.calledTwice).to.be.true;
172 | });
173 |
174 | it("should detach flicking event handlers when destroyed", () => {
175 | // Given
176 | const plugin = new AutoPlay({ stopOnHover: true });
177 | const flicking = new Flicking(createFlickingFixture());
178 | const playSpy = sinon.spy();
179 | const stopSpy = sinon.spy();
180 |
181 | plugin.play = playSpy;
182 | plugin.stop = stopSpy;
183 |
184 | // When
185 | flicking.addPlugins(plugin);
186 | plugin.destroy();
187 | playSpy.resetHistory();
188 | stopSpy.resetHistory();
189 |
190 | void flicking.next(500);
191 | tick(500);
192 |
193 | // Then
194 | expect(playSpy.called).to.be.false;
195 | expect(stopSpy.called).to.be.false;
196 | });
197 |
198 | it("won't call next if Flicking is already moving", () => {
199 | // Given
200 | const plugin = new AutoPlay({ duration: 500, stopOnHover: true });
201 | const flicking = new Flicking(createFlickingFixture());
202 | const nextStub = sinon.stub(flicking, "next");
203 |
204 | nextStub.resolves(void 0);
205 |
206 | // When
207 | const animatingStub = sinon.stub(flicking, "animating");
208 | animatingStub.get(() => true);
209 |
210 | flicking.addPlugins(plugin);
211 | tick(1000);
212 |
213 | // Then
214 | expect(nextStub.called).to.be.false;
215 | });
216 |
217 | it("should apply the status of autoplay to playing property", async () => {
218 | // Given
219 | const plugin = new AutoPlay({ stopOnHover: true });
220 | const flicking = new Flicking(createFlickingFixture());
221 |
222 | // When
223 | flicking.addPlugins(plugin);
224 | await waitEvent(flicking, "ready");
225 | const wrapper = flicking.element;
226 |
227 | // Then
228 | expect(plugin.playing).to.be.true;
229 | wrapper.dispatchEvent(new Event("mouseenter"));
230 | expect(plugin.playing).to.be.false;
231 | wrapper.dispatchEvent(new Event("mouseleave"));
232 | expect(plugin.playing).to.be.true;
233 | });
234 | (["PREV", "NEXT"] as const).forEach(direction => {
235 | it(`should call resume ${direction} as much as the ratio-fixed duration when stop playing`, async () => {
236 | // Given
237 | const wrapper = sandbox("flick");
238 | const viewportEl = document.createElement("div");
239 | viewportEl.style.width = "199px";
240 | viewportEl.className = "flicking-viewport";
241 | viewportEl.innerHTML = `
242 |
247 | `;
248 | wrapper.appendChild(viewportEl);
249 |
250 | const plugin = new AutoPlay({
251 | stopOnHover: true,
252 | duration: 0,
253 | direction,
254 | animationDuration: 1000
255 | });
256 | const flicking = new Flicking(viewportEl, {
257 | align: "prev",
258 | moveType: "freeScroll",
259 | circular: true,
260 | easing: x => x,
261 | duration: 1000
262 | });
263 | flicking.addPlugins(plugin);
264 | await waitEvent(flicking, "ready");
265 | const flickingWrapper = flicking.element;
266 |
267 | await wait(500);
268 | const target = flickingWrapper.querySelector(".flicking-panel-target")!;
269 |
270 | // When
271 | target.dispatchEvent(new MouseEvent("mousedown", {
272 | buttons: 1,
273 | clientX: 0,
274 | clientY: 0,
275 | bubbles: true,
276 | cancelable: true
277 | }));
278 |
279 | // half (100) 0.5s
280 | const halfIndex = flicking.currentPanel.index;
281 |
282 | await wait(100);
283 | target.dispatchEvent(new MouseEvent("mouseup", {
284 | buttons: 1,
285 | clientX: 0,
286 | clientY: 0,
287 | bubbles: true,
288 | cancelable: true
289 | }));
290 | await wait(600);
291 |
292 | // half (200) 0.5s
293 | const nextIndex = flicking.currentPanel.index;
294 |
295 | // Then
296 | expect(halfIndex).to.be.equals(0);
297 | if (direction === "PREV") {
298 | // 0 => 2 (-1)
299 | expect(nextIndex).to.be.equals(2);
300 | } else {
301 | // 0 => 1
302 | expect(nextIndex).to.be.equals(1);
303 | }
304 | });
305 |
306 | });
307 | });
308 |
--------------------------------------------------------------------------------
/test/unit/Fade.spec.ts:
--------------------------------------------------------------------------------
1 | import Flicking from "@egjs/flicking";
2 |
3 | import Fade from "../../src/Fade";
4 | import { sandbox, cleanup, waitEvent } from "../unit/utils";
5 |
6 | describe("Fade", () => {
7 | let flicking: Flicking;
8 |
9 | beforeEach(() => {
10 | const wrapper = sandbox("flick");
11 | const viewportEl = document.createElement("div");
12 | viewportEl.style.width = "199px";
13 | viewportEl.className = "flicking-viewport";
14 | viewportEl.innerHTML = `
15 |
20 | `;
21 | wrapper.appendChild(viewportEl);
22 | flicking = new Flicking(viewportEl);
23 | });
24 |
25 | afterEach(() => {
26 | cleanup();
27 | flicking.destroy();
28 | });
29 |
30 | it("should set opacity to 1 for current panel", async () => {
31 | // Given & When
32 | flicking.addPlugins(new Fade());
33 | await waitEvent(flicking, "ready");
34 |
35 | // Then
36 | expect(flicking.currentPanel.element.style.opacity).to.equal("1");
37 | });
38 |
39 | it("should apply opacity to child elements if a selector is given", async () => {
40 | // Given & When
41 | flicking.addPlugins(new Fade("p"));
42 | await waitEvent(flicking, "ready");
43 |
44 | // Then
45 | const currentPanelEl = flicking.currentPanel.element;
46 |
47 | expect(currentPanelEl.style.opacity).to.equal("");
48 | expect(currentPanelEl.querySelector("p").style.opacity).to.equal("1");
49 | });
50 |
51 | it("should not set opacity for invisible panels", () => {
52 | // Given & When
53 | flicking.addPlugins(new Fade());
54 |
55 | // Then
56 | expect(flicking.getPanel(1).element.style.opacity).to.equal("");
57 | expect(flicking.getPanel(2).element.style.opacity).to.equal("");
58 | });
59 |
60 | it("should be updated whenever flicking moves", async () => {
61 | // Given
62 | flicking.addPlugins(new Fade());
63 | await waitEvent(flicking, "ready");
64 |
65 | // When
66 | void flicking.moveTo(1, 0);
67 |
68 | // Then
69 | expect(flicking.getPanel(1).element.style.opacity).to.equal("1");
70 | });
71 | });
72 |
--------------------------------------------------------------------------------
/test/unit/Pagination.spec.ts:
--------------------------------------------------------------------------------
1 | import * as sinon from "sinon";
2 | import Pagination from "../../src/pagination/Pagination";
3 | import { cleanup, createFlicking, createPaginationFixture, sandbox } from "./utils";
4 |
5 | describe("Pagination", () => {
6 | afterEach(() => {
7 | cleanup();
8 | });
9 |
10 | const createFixture = () => {
11 | const fixtureWrapper = sandbox("pagination");
12 | const fixture = createPaginationFixture();
13 | fixtureWrapper.appendChild(fixture);
14 |
15 | return fixture;
16 | };
17 |
18 | describe("Options", () => {
19 | describe("renderBullet", () => {
20 | it("should not remove custom classes when the 'scroll' type is used", async () => {
21 | // Given
22 | const flicking = await createFlicking(createFixture());
23 | const pagination = new Pagination({
24 | renderBullet: (className: string) => ``,
25 | type: "scroll"
26 | });
27 |
28 | // When
29 | pagination.init(flicking);
30 |
31 | // Then
32 | const bullets = [].slice.apply(document.querySelectorAll(".flicking-pagination-bullet"));
33 | expect(bullets.every(bullet => bullet.classList.contains("test"))).to.be.true;
34 | });
35 | });
36 |
37 | describe("renderActiveBullet", () => {
38 | it("is false by default", async () => {
39 | // Given
40 | const flicking = await createFlicking(createFixture());
41 | const pagination = new Pagination();
42 |
43 | // When
44 | pagination.init(flicking);
45 |
46 | // Then
47 | expect(pagination.renderActiveBullet).to.be.null;
48 | });
49 |
50 | it("should render active bullet if a render function is given", async () => {
51 | // Given
52 | const flicking = await createFlicking(createFixture());
53 | const pagination = new Pagination({
54 | type: "bullet", // default
55 | renderActiveBullet: (className) => `ACTIVE`
56 | });
57 |
58 | // When
59 | pagination.init(flicking);
60 |
61 | // Then
62 | const activeBullet = document.querySelector(".flicking-pagination-bullet-active");
63 | expect(activeBullet).not.to.be.null;
64 | expect(activeBullet?.innerHTML).to.equal("ACTIVE");
65 | });
66 |
67 | it("should render active bullet if a render function is given & type is scroll", async () => {
68 | // Given
69 | const flicking = await createFlicking(createFixture());
70 | const pagination = new Pagination({
71 | type: "scroll",
72 | renderActiveBullet: (className) => `ACTIVE`
73 | });
74 |
75 | // When
76 | pagination.init(flicking);
77 |
78 | // Then
79 | const activeBullet = document.querySelector(".flicking-pagination-bullet-active");
80 | expect(activeBullet).not.to.be.null;
81 | expect(activeBullet?.innerHTML).to.equal("ACTIVE");
82 | });
83 | });
84 | });
85 |
86 | describe("Index change", () => {
87 | it("should not throw any error while Flicking is replacing all panels", async () => {
88 | // Given
89 | const flicking = await createFlicking(createFixture());
90 | const pagination = new Pagination({
91 | renderBullet: (className: string) => ``,
92 | type: "bullet"
93 | });
94 |
95 | // When
96 | pagination.init(flicking);
97 | flicking.remove(0, flicking.panelCount);
98 | flicking.append(document.createElement("div"));
99 |
100 | // Then
101 | // There should be one active bullet selected
102 | const bullets = [].slice.apply(document.querySelectorAll(".flicking-pagination-bullet-active"));
103 | expect(bullets.length).to.equal(1);
104 | });
105 | });
106 |
107 | describe("Events", () => {
108 | let addEventListener: sinon.SinonStub;
109 |
110 | beforeEach(() => {
111 | addEventListener = sinon.stub(HTMLElement.prototype, "addEventListener");
112 | });
113 |
114 | afterEach(() => {
115 | addEventListener.restore();
116 | });
117 |
118 | it("should add touch start listener with passive: true", async () => {
119 | // Given
120 | const flicking = await createFlicking(createFixture());
121 | const pagination = new Pagination();
122 |
123 | // When
124 | flicking.addPlugins(pagination);
125 | await flicking.init();
126 |
127 | // Then
128 | const touchStartEvents = addEventListener.getCalls()
129 | .filter(call => {
130 | return call.args[0] === "touchstart" && call.thisValue.classList.contains("flicking-pagination-bullet");
131 | });
132 |
133 | expect(touchStartEvents.length).to.be.greaterThan(0);
134 | expect(touchStartEvents.every(call => {
135 | const [type, _, options] = call.args;
136 | return type === "touchstart" && options && (options as AddEventListenerOptions).passive;
137 | })).to.be.true;
138 | });
139 | });
140 | });
141 |
--------------------------------------------------------------------------------
/test/unit/Parallax.spec.ts:
--------------------------------------------------------------------------------
1 | import Flicking from "@egjs/flicking";
2 |
3 | import Parallax from "../../src/Parallax";
4 | import { sandbox, cleanup, waitEvent } from "../unit/utils";
5 |
6 | describe("Parallax", () => {
7 | let flicking: Flicking;
8 |
9 | beforeEach(() => {
10 | const wrapper = sandbox("flick");
11 | const viewportEl = document.createElement("div");
12 | viewportEl.style.width = "199px";
13 | viewportEl.className = "flicking-viewport";
14 | viewportEl.innerHTML = `
15 |
20 | `;
21 | wrapper.appendChild(viewportEl);
22 | flicking = new Flicking(viewportEl);
23 | });
24 |
25 | afterEach(() => {
26 | cleanup();
27 | flicking.destroy();
28 | });
29 |
30 | it("should apply transform for visible panels", () => {
31 | // Given & When
32 | flicking.addPlugins(new Parallax());
33 |
34 | // Then
35 | const visiblePanels = flicking.visiblePanels;
36 |
37 | visiblePanels.forEach(panel => {
38 | expect(panel.element.style.transform).not.to.equal("");
39 | });
40 | });
41 |
42 | it("should apply opacity to child elements if a selector is given", () => {
43 | // Given & When
44 | flicking.addPlugins(new Parallax("p"));
45 |
46 | // Then
47 | const visiblePanels = flicking.visiblePanels;
48 | visiblePanels.forEach(panel => {
49 | expect(panel.element.querySelector("p").style.transform).not.to.equal("");
50 | });
51 | });
52 |
53 | it("should not set opacity for invisible panels", () => {
54 | // Given & When
55 | flicking.addPlugins(new Parallax());
56 |
57 | // Then
58 | expect(flicking.getPanel(1).element.style.transform).to.equal("");
59 | expect(flicking.getPanel(2).element.style.transform).to.equal("");
60 | });
61 |
62 | it("should be updated whenever flicking moves", async () => {
63 | // Given
64 | flicking.addPlugins(new Parallax());
65 | await waitEvent(flicking, "ready");
66 |
67 | // When
68 | void flicking.moveTo(1, 0);
69 |
70 | // Then
71 | expect(flicking.getPanel(1).element.style.transform).not.to.equal("");
72 | });
73 | });
74 |
--------------------------------------------------------------------------------
/test/unit/Perspective.spec.ts:
--------------------------------------------------------------------------------
1 | import Flicking from "@egjs/flicking";
2 |
3 | import Perspective from "../../src/Perspective";
4 | import { sandbox, cleanup, waitEvent } from "../unit/utils";
5 |
6 | describe("Perspective", () => {
7 | let flicking: Flicking;
8 |
9 | beforeEach(() => {
10 | const wrapper = sandbox("flick");
11 | wrapper.style.width = "199px";
12 | wrapper.classList.add("flicking-viewport");
13 | wrapper.innerHTML = `
14 |
19 | `;
20 | flicking = new Flicking(wrapper);
21 | });
22 |
23 | afterEach(() => {
24 | cleanup();
25 | flicking.destroy();
26 | });
27 |
28 | it("should set transform to default for current panel", async () => {
29 | // Given & When
30 | flicking.addPlugins(new Perspective());
31 | await waitEvent(flicking, "ready");
32 |
33 | // Then
34 | expect(flicking.currentPanel.element.style.transform).to.equal("perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)");
35 | });
36 |
37 | it("should set transform to default that except perspective for current panel when allocated other arguments", async () => {
38 | // Given & When
39 | flicking.addPlugins(new Perspective({perspective: 500, rotate: 0.5, scale: 0.5}));
40 | await waitEvent(flicking, "ready");
41 |
42 | // Then
43 | expect(flicking.currentPanel.element.style.transform).to.equal("perspective(500px) rotateX(0deg) rotateY(0deg) scale(1)");
44 | });
45 |
46 | it("should apply transform to child elements if a selector is given", async () => {
47 | // Given & When
48 | flicking.addPlugins(new Perspective({selector: "p"}));
49 | await waitEvent(flicking, "ready");
50 |
51 | // Then
52 | const currentPanelEl = flicking.currentPanel.element;
53 |
54 | expect(currentPanelEl.style.transform).to.equal("");
55 | expect(currentPanelEl.querySelector("p").style.transform).to.equal("perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)");
56 | });
57 |
58 | it("should not set transform for invisible panels", () => {
59 | // Given & When
60 | flicking.addPlugins(new Perspective());
61 |
62 | // Then
63 | expect(flicking.getPanel(1).element.style.transform).to.equal("");
64 | expect(flicking.getPanel(2).element.style.transform).to.equal("");
65 | });
66 |
67 | it("should be updated whenever flicking moves", async () => {
68 | // Given
69 | flicking.addPlugins(new Perspective());
70 | await waitEvent(flicking, "ready");
71 |
72 | // When
73 | void flicking.moveTo(1, 0);
74 |
75 | // Then
76 | expect(flicking.getPanel(1).element.style.transform).to.equal("perspective(1000px) rotateX(0deg) rotateY(0deg) scale(1)");
77 | });
78 | });
79 |
--------------------------------------------------------------------------------
/test/unit/Sync.spec.ts:
--------------------------------------------------------------------------------
1 | import Flicking from "@egjs/flicking";
2 |
3 | import Sync from "../../src/Sync";
4 | import { sandbox, cleanup, waitEvent, tick, simulate } from "../unit/utils";
5 |
6 | describe("Sync", () => {
7 | let flicking0: Flicking;
8 | let flicking1: Flicking;
9 |
10 | beforeEach(() => {
11 | const wrapper0 = sandbox("flick0");
12 | const viewportEl0 = document.createElement("div");
13 | viewportEl0.style.width = "199px";
14 | viewportEl0.className = "flicking-viewport";
15 | viewportEl0.innerHTML = `
16 |
21 | `;
22 | wrapper0.appendChild(viewportEl0);
23 | flicking0 = new Flicking(viewportEl0);
24 |
25 | const wrapper1 = sandbox("flick1");
26 | const viewportEl1 = document.createElement("div");
27 | viewportEl1.style.width = "199px";
28 | viewportEl1.className = "flicking-viewport";
29 | viewportEl1.innerHTML = `
30 |
31 |
32 |
33 |
34 |
35 |
36 |
37 |
38 | `;
39 | wrapper1.appendChild(viewportEl1);
40 | flicking1 = new Flicking(viewportEl1);
41 | });
42 |
43 | afterEach(() => {
44 | cleanup();
45 | flicking0.destroy();
46 | flicking1.destroy();
47 | });
48 |
49 | it("main flicking should move with other flickings", async () => {
50 | // Given
51 | flicking0.addPlugins(new Sync({
52 | type: "camera",
53 | synchronizedFlickingOptions: [
54 | {
55 | flicking: flicking0
56 | },
57 | {
58 | flicking: flicking1
59 | }
60 | ]
61 | }));
62 | await waitEvent(flicking0, "ready");
63 |
64 | // When
65 | void flicking0.control.moveToPosition(500, 0);
66 |
67 | // Then
68 | expect(flicking1.camera.position).equal(1100);
69 | expect(flicking1.camera.position).not.to.equal(100);
70 | });
71 |
72 | it("other flickings should move main flicking", async () => {
73 | // Given
74 | flicking0.addPlugins(new Sync({
75 | type: "camera",
76 | synchronizedFlickingOptions: [
77 | {
78 | flicking: flicking0
79 | },
80 | {
81 | flicking: flicking1
82 | }
83 | ]
84 | }));
85 | await waitEvent(flicking1, "ready");
86 |
87 | // When
88 | void flicking1.control.moveToPosition(1100, 0);
89 |
90 | // Then
91 | expect(flicking0.camera.position).equal(500);
92 | expect(flicking0.camera.position).not.to.equal(100);
93 | });
94 |
95 | it("active panel should have active class", async () => {
96 | // Given
97 | flicking0.addPlugins(new Sync({
98 | type: "index",
99 | synchronizedFlickingOptions: [
100 | {
101 | flicking: flicking0,
102 | isSlidable: true
103 | },
104 | {
105 | flicking: flicking1,
106 | activeClass: "flicking-thumbnail-active"
107 | }
108 | ]
109 | }));
110 | await waitEvent(flicking0, "ready");
111 |
112 | // When
113 | void flicking0.moveTo(1, 0);
114 | tick(1000);
115 |
116 | // Then
117 | flicking1.panels.forEach((panel, index) => {
118 | expect(panel.element.classList.contains("flicking-thumbnail-active")).equal(index === 1);
119 | });
120 | });
121 |
122 | it("slidable flicking should move other flickings", async () => {
123 | // Given
124 | flicking0.addPlugins(new Sync({
125 | type: "index",
126 | synchronizedFlickingOptions: [
127 | {
128 | flicking: flicking0,
129 | isSlidable: true
130 | },
131 | {
132 | flicking: flicking1
133 | }
134 | ]
135 | }));
136 | await waitEvent(flicking0, "ready");
137 |
138 | // When
139 | void flicking0.moveTo(2, 0);
140 | tick(1000);
141 |
142 | // Then
143 | expect(flicking1.index).equal(2);
144 | expect(flicking1.camera.position).equal(flicking1.panels[2].position);
145 | });
146 |
147 | it("clickable flicking should move other flickings", async () => {
148 | // Given
149 | flicking0.addPlugins(new Sync({
150 | type: "index",
151 | synchronizedFlickingOptions: [
152 | {
153 | flicking: flicking0,
154 | isClickable: true
155 | },
156 | {
157 | flicking: flicking1
158 | }
159 | ]
160 | }));
161 | await waitEvent(flicking0, "ready");
162 |
163 | // When
164 | await simulate(flicking0.panels[2].element, { deltaX: 0, deltaY: 0 });
165 | tick(10000);
166 |
167 | // Then
168 | expect(flicking1.index).equal(2);
169 | expect(flicking1.camera.position).equal(flicking1.panels[2].position);
170 | });
171 |
172 | [true, false].forEach((enabled) => {
173 | it(`should check and recover enabled status of flickings (enabled: ${enabled})`, async () => {
174 | // Given
175 | flicking0.addPlugins(new Sync({
176 | type: "camera",
177 | synchronizedFlickingOptions: [
178 | {
179 | flicking: flicking0
180 | },
181 | {
182 | flicking: flicking1
183 | }
184 | ]
185 | }));
186 | await waitEvent(flicking0, "ready");
187 |
188 | if (!enabled) {
189 | flicking1.disableInput();
190 | }
191 |
192 | // When
193 | void flicking0.control.moveToPosition(500, 0);
194 |
195 | // Then
196 | expect(flicking1.camera.position).equal(1100);
197 | expect(flicking1.control.controller.enabled).equal(enabled);
198 | });
199 | })
200 | });
201 |
--------------------------------------------------------------------------------
/test/unit/utils.ts:
--------------------------------------------------------------------------------
1 | import Flicking, { EVENTS } from "@egjs/flicking";
2 |
3 | declare let Simulator: any;
4 |
5 | export const tick = (time: number) => {
6 | (window as any).timer.tick(time);
7 | };
8 |
9 | export const sandbox = (obj: object | string, prop?: object): HTMLElement => {
10 | const tmp = document.createElement("div");
11 | tmp.className = "_tempSandbox_";
12 | if (typeof obj === "string") {
13 | tmp.id = obj;
14 | } else {
15 | tmp.id = "sandbox";
16 | }
17 |
18 | if (typeof obj === "object" || typeof prop === "object") {
19 | const attrs = typeof prop === "object" ? prop : obj;
20 | for (const p in attrs as object) {
21 | if (/class|className/.test(p)) {
22 | tmp.setAttribute(p, attrs[p] + " _tempSandbox_");
23 | } else {
24 | tmp.setAttribute(p, attrs[p]);
25 | }
26 | }
27 | }
28 | return document.body.appendChild(tmp);
29 | };
30 |
31 | export function cleanup() {
32 | const elements: HTMLElement[] = [].slice.call(document.querySelectorAll("._tempSandbox_"));
33 | elements.forEach(v => {
34 | v.parentNode.removeChild(v);
35 | });
36 | }
37 |
38 | export const createFlickingFixture = () => {
39 | const viewport = document.createElement("div");
40 | const camera = document.createElement("div");
41 | const panels = [0, 1, 2].map(() => document.createElement("div"));
42 |
43 | viewport.className = "flicking-viewport";
44 | camera.className = "flicking-camera";
45 |
46 | viewport.appendChild(camera);
47 | panels.forEach(panel => camera.appendChild(panel));
48 |
49 | return viewport;
50 | };
51 |
52 | export const createPaginationFixture = () => {
53 | const defaultFixture = createFlickingFixture();
54 | const paginationWrapper = document.createElement("div");
55 |
56 | paginationWrapper.className = "flicking-pagination";
57 | defaultFixture.appendChild(paginationWrapper);
58 |
59 | return defaultFixture;
60 | };
61 |
62 | export const createArrowFixture = () => {
63 | const defaultFixture = createFlickingFixture();
64 | const prevArrow = document.createElement("span");
65 | const nextArrow = document.createElement("span");
66 |
67 | prevArrow.className = "flicking-arrow-prev";
68 | nextArrow.className = "flicking-arrow-next";
69 |
70 | defaultFixture.appendChild(prevArrow);
71 | defaultFixture.appendChild(nextArrow);
72 |
73 | return defaultFixture;
74 | };
75 |
76 | export const createFlicking = async (el: HTMLElement, option: ConstructorParameters[1] = {}): Promise => {
77 | const flicking = new Flicking(el, option);
78 |
79 | if (!flicking.autoInit) return Promise.resolve(flicking);
80 |
81 | return new Promise(resolve => {
82 | flicking.once(EVENTS.READY, () => resolve(flicking));
83 | });
84 | };
85 |
86 |
87 | export const simulate = (el: HTMLElement, option: Partial<{
88 | pos: [number, number];
89 | deltaX: number;
90 | deltaY: number;
91 | duration: number;
92 | easing: string;
93 | }> = {}, time: number = 10000): Promise => {
94 | const elBbox = el.getBoundingClientRect();
95 |
96 | return new Promise(resolve => {
97 | Simulator.gestures.pan(el, {
98 | pos: [elBbox.left + elBbox.width / 2, elBbox.top + elBbox.height / 2],
99 | deltaX: 0,
100 | deltaY: 0,
101 | duration: 500,
102 | easing: "linear",
103 | ...option
104 | }, resolve);
105 |
106 | tick(time);
107 | });
108 | };
109 |
110 | export const waitEvent = (flicking: Flicking, eventName: typeof EVENTS[keyof typeof EVENTS]) => new Promise(res => {
111 | flicking.once(eventName, res);
112 | });
113 |
114 |
115 | export const wait = (time = 100) => new Promise(resolve => {
116 | setTimeout(resolve, time);
117 | tick(time);
118 | });
119 |
--------------------------------------------------------------------------------
/tsconfig.declaration.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "compilerOptions": {
4 | "removeComments": true,
5 | "declaration": true,
6 | "emitDeclarationOnly": true,
7 | "declarationDir": "declaration",
8 | "strictNullChecks": false,
9 | }
10 | }
11 |
--------------------------------------------------------------------------------
/tsconfig.eslint.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig.json",
3 | "include": [
4 | "./**/*.ts",
5 | "./**/*.js",
6 | "./**/.*.js"
7 | ]
8 | }
9 |
--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------
1 | {
2 | "compilerOptions": {
3 | "outDir": "./outjs/",
4 | "sourceMap": true,
5 | "module": "es2015",
6 | "target": "es5",
7 | "strictNullChecks": true,
8 | "moduleResolution": "node",
9 | "lib": [
10 | "es2015",
11 | "dom"
12 | ],
13 | },
14 | "include": [
15 | "./src/**/*.ts"
16 | ]
17 | }
--------------------------------------------------------------------------------
/tsconfig.test.json:
--------------------------------------------------------------------------------
1 | {
2 | "extends": "./tsconfig",
3 | "compilerOptions": {
4 | "module": "commonjs",
5 | "noImplicitAny": false,
6 | "strictNullChecks": false,
7 | "noUnusedLocals": false,
8 | "experimentalDecorators": true,
9 | "types": [
10 | "karma-chai",
11 | "mocha",
12 | ]
13 | },
14 | "include": [
15 | "./src/**/*.ts",
16 | "./test/**/*.ts"
17 | ],
18 | "exclude": [
19 | "./node_modules/**/*.ts"
20 | ]
21 | }
22 |
--------------------------------------------------------------------------------