@shower/core 3.0.0-2 → 3.3.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +2 -2
- package/dist/shower.js +360 -238
- package/lib/default-options.js +14 -0
- package/lib/modules/a11y.js +38 -0
- package/lib/modules/install.js +28 -0
- package/lib/modules/keys.js +113 -0
- package/lib/modules/location.js +53 -0
- package/lib/modules/mouse.js +41 -0
- package/lib/modules/next.js +54 -0
- package/lib/modules/parse-timing.mjs +28 -0
- package/lib/modules/progress.js +22 -0
- package/lib/modules/timer.js +34 -0
- package/lib/modules/title.js +21 -0
- package/lib/modules/touch.js +34 -0
- package/lib/modules/view.js +54 -0
- package/lib/shower.js +66 -65
- package/lib/slide.js +99 -0
- package/lib/start.js +14 -0
- package/lib/utils.js +22 -0
- package/package.json +30 -30
package/lib/shower.js
CHANGED
|
@@ -1,86 +1,89 @@
|
|
|
1
1
|
import defaultOptions from './default-options';
|
|
2
2
|
import Slide from './slide';
|
|
3
|
-
import {
|
|
4
|
-
import installModules from './modules';
|
|
5
|
-
|
|
6
|
-
const ensureSlideId = (slideElement, index) => {
|
|
7
|
-
if (!slideElement.id) {
|
|
8
|
-
slideElement.id = index + 1;
|
|
9
|
-
}
|
|
10
|
-
};
|
|
3
|
+
import { defineReadOnly, ShowerError } from './utils';
|
|
4
|
+
import installModules from './modules/install';
|
|
11
5
|
|
|
12
6
|
class Shower extends EventTarget {
|
|
7
|
+
/**
|
|
8
|
+
* @param {object=} options
|
|
9
|
+
*/
|
|
13
10
|
constructor(options) {
|
|
14
11
|
super();
|
|
15
12
|
|
|
13
|
+
defineReadOnly(this, {
|
|
14
|
+
options: { ...defaultOptions, ...options },
|
|
15
|
+
});
|
|
16
|
+
|
|
16
17
|
this._mode = 'list';
|
|
17
18
|
this._isStarted = false;
|
|
18
|
-
this.
|
|
19
|
+
this._container = null;
|
|
19
20
|
}
|
|
20
21
|
|
|
21
22
|
/**
|
|
22
|
-
* @param {object} options
|
|
23
|
+
* @param {object=} options
|
|
24
|
+
* @throws {ShowerError}
|
|
23
25
|
*/
|
|
24
26
|
configure(options) {
|
|
27
|
+
if (this._isStarted) {
|
|
28
|
+
throw new ShowerError('Shower should be configured before it is started.');
|
|
29
|
+
}
|
|
30
|
+
|
|
25
31
|
Object.assign(this.options, options);
|
|
26
32
|
}
|
|
27
33
|
|
|
34
|
+
/**
|
|
35
|
+
* @throws {ShowerError}
|
|
36
|
+
* @emits Shower#start
|
|
37
|
+
*/
|
|
28
38
|
start() {
|
|
29
39
|
if (this._isStarted) return;
|
|
30
40
|
|
|
31
41
|
const { containerSelector } = this.options;
|
|
32
|
-
this.
|
|
33
|
-
if (!this.
|
|
34
|
-
throw new
|
|
42
|
+
this._container = document.querySelector(containerSelector);
|
|
43
|
+
if (!this._container) {
|
|
44
|
+
throw new ShowerError(
|
|
45
|
+
`Shower container with selector '${containerSelector}' was not found.`,
|
|
46
|
+
);
|
|
35
47
|
}
|
|
36
48
|
|
|
37
|
-
this._isStarted = true;
|
|
38
49
|
this._initSlides();
|
|
39
|
-
|
|
40
|
-
// maintains invariant: active slide always exists in `full` mode
|
|
41
|
-
this.addEventListener('modechange', () => {
|
|
42
|
-
if (this.isFullMode && !this.activeSlide) {
|
|
43
|
-
this.first();
|
|
44
|
-
}
|
|
45
|
-
});
|
|
46
|
-
|
|
47
50
|
installModules(this);
|
|
51
|
+
|
|
52
|
+
this._isStarted = true;
|
|
48
53
|
this.dispatchEvent(new Event('start'));
|
|
49
54
|
}
|
|
50
55
|
|
|
51
56
|
_initSlides() {
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
].filter(slideElement => !slideElement.hidden);
|
|
55
|
-
|
|
56
|
-
slideElements.forEach(ensureSlideId);
|
|
57
|
-
this.slides = slideElements.map(slideElement => {
|
|
58
|
-
const slide = new Slide(slideElement, this.options);
|
|
59
|
-
|
|
60
|
-
slide.addEventListener('activate', () => {
|
|
61
|
-
this._toggleActiveSlide(slide);
|
|
62
|
-
});
|
|
63
|
-
|
|
64
|
-
slide.element.addEventListener('click', () => {
|
|
65
|
-
if (this.isListMode) {
|
|
66
|
-
slide.activate();
|
|
67
|
-
this.enterFullMode();
|
|
68
|
-
}
|
|
69
|
-
});
|
|
70
|
-
|
|
71
|
-
return slide;
|
|
72
|
-
});
|
|
73
|
-
}
|
|
57
|
+
const visibleSlideSelector = `${this.options.slideSelector}:not([hidden])`;
|
|
58
|
+
const visibleSlideElements = this._container.querySelectorAll(visibleSlideSelector);
|
|
74
59
|
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
if (slide !== target) {
|
|
79
|
-
slide.deactivate();
|
|
60
|
+
this.slides = Array.from(visibleSlideElements, (slideElement, index) => {
|
|
61
|
+
if (!slideElement.id) {
|
|
62
|
+
slideElement.id = index + 1;
|
|
80
63
|
}
|
|
64
|
+
|
|
65
|
+
return new Slide(this, slideElement);
|
|
81
66
|
});
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
_setMode(mode) {
|
|
70
|
+
if (mode === this._mode) return;
|
|
82
71
|
|
|
83
|
-
this.
|
|
72
|
+
this._mode = mode;
|
|
73
|
+
this.dispatchEvent(new Event('modechange'));
|
|
74
|
+
}
|
|
75
|
+
|
|
76
|
+
/**
|
|
77
|
+
* @param {Event} event
|
|
78
|
+
*/
|
|
79
|
+
dispatchEvent(event) {
|
|
80
|
+
if (!this._isStarted) return false;
|
|
81
|
+
|
|
82
|
+
return super.dispatchEvent(event);
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
get container() {
|
|
86
|
+
return this._container;
|
|
84
87
|
}
|
|
85
88
|
|
|
86
89
|
get isFullMode() {
|
|
@@ -92,31 +95,27 @@ class Shower extends EventTarget {
|
|
|
92
95
|
}
|
|
93
96
|
|
|
94
97
|
get activeSlide() {
|
|
95
|
-
return this.slides.find(slide => slide.isActive);
|
|
98
|
+
return this.slides.find((slide) => slide.isActive);
|
|
96
99
|
}
|
|
97
100
|
|
|
98
101
|
get activeSlideIndex() {
|
|
99
|
-
return this.slides.findIndex(slide => slide.isActive);
|
|
102
|
+
return this.slides.findIndex((slide) => slide.isActive);
|
|
100
103
|
}
|
|
101
104
|
|
|
102
105
|
/**
|
|
103
106
|
* Slide fills the maximum area.
|
|
107
|
+
* @emits Shower#modechange
|
|
104
108
|
*/
|
|
105
109
|
enterFullMode() {
|
|
106
|
-
|
|
107
|
-
this._mode = 'full';
|
|
108
|
-
this.dispatchEvent(new Event('modechange'));
|
|
109
|
-
}
|
|
110
|
+
this._setMode('full');
|
|
110
111
|
}
|
|
111
112
|
|
|
112
113
|
/**
|
|
113
114
|
* Shower returns into list mode.
|
|
115
|
+
* @emits Shower#modechange
|
|
114
116
|
*/
|
|
115
117
|
exitFullMode() {
|
|
116
|
-
|
|
117
|
-
this._mode = 'list';
|
|
118
|
-
this.dispatchEvent(new Event('modechange'));
|
|
119
|
-
}
|
|
118
|
+
this._setMode('list');
|
|
120
119
|
}
|
|
121
120
|
|
|
122
121
|
/**
|
|
@@ -132,27 +131,29 @@ class Shower extends EventTarget {
|
|
|
132
131
|
/**
|
|
133
132
|
* @param {number} delta
|
|
134
133
|
*/
|
|
135
|
-
|
|
134
|
+
goBy(delta) {
|
|
136
135
|
this.goTo(this.activeSlideIndex + delta);
|
|
137
136
|
}
|
|
138
137
|
|
|
139
138
|
/**
|
|
140
|
-
* @param {boolean
|
|
139
|
+
* @param {boolean} [isForce=false]
|
|
140
|
+
* @emits Shower#prev
|
|
141
141
|
*/
|
|
142
142
|
prev(isForce) {
|
|
143
143
|
const prev = new Event('prev', { cancelable: !isForce });
|
|
144
144
|
if (this.dispatchEvent(prev)) {
|
|
145
|
-
this.
|
|
145
|
+
this.goBy(-1);
|
|
146
146
|
}
|
|
147
147
|
}
|
|
148
148
|
|
|
149
149
|
/**
|
|
150
|
-
* @param {boolean
|
|
150
|
+
* @param {boolean} [isForce=false]
|
|
151
|
+
* @emits Shower#next
|
|
151
152
|
*/
|
|
152
153
|
next(isForce) {
|
|
153
154
|
const next = new Event('next', { cancelable: !isForce });
|
|
154
155
|
if (this.dispatchEvent(next)) {
|
|
155
|
-
this.
|
|
156
|
+
this.goBy(1);
|
|
156
157
|
}
|
|
157
158
|
}
|
|
158
159
|
|
package/lib/slide.js
ADDED
|
@@ -0,0 +1,99 @@
|
|
|
1
|
+
import { defineReadOnly, ShowerError } from './utils';
|
|
2
|
+
|
|
3
|
+
class Slide extends EventTarget {
|
|
4
|
+
/**
|
|
5
|
+
* @param {Shower} shower
|
|
6
|
+
* @param {HTMLElement} element
|
|
7
|
+
*/
|
|
8
|
+
constructor(shower, element) {
|
|
9
|
+
super();
|
|
10
|
+
|
|
11
|
+
defineReadOnly(this, {
|
|
12
|
+
shower,
|
|
13
|
+
element,
|
|
14
|
+
state: {
|
|
15
|
+
visitCount: 0,
|
|
16
|
+
innerStepCount: 0,
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
this._isActive = false;
|
|
21
|
+
this._options = this.shower.options;
|
|
22
|
+
|
|
23
|
+
this.element.addEventListener('click', (event) => {
|
|
24
|
+
if (event.defaultPrevented) return;
|
|
25
|
+
|
|
26
|
+
this.activate();
|
|
27
|
+
this.shower.enterFullMode();
|
|
28
|
+
});
|
|
29
|
+
}
|
|
30
|
+
|
|
31
|
+
get isActive() {
|
|
32
|
+
return this._isActive;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
get isVisited() {
|
|
36
|
+
return this.state.visitCount > 0;
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
get id() {
|
|
40
|
+
return this.element.id;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
get title() {
|
|
44
|
+
const titleElement = this.element.querySelector(this._options.slideTitleSelector);
|
|
45
|
+
return titleElement ? titleElement.innerText : '';
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Deactivates currently active slide (if any) and activates itself.
|
|
50
|
+
* @emits Slide#deactivate
|
|
51
|
+
* @emits Slide#activate
|
|
52
|
+
* @emits Shower#slidechange
|
|
53
|
+
*/
|
|
54
|
+
activate() {
|
|
55
|
+
if (this._isActive) return;
|
|
56
|
+
|
|
57
|
+
const prev = this.shower.activeSlide;
|
|
58
|
+
if (prev) {
|
|
59
|
+
prev._deactivate();
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
this.state.visitCount++;
|
|
63
|
+
this.element.classList.add(this._options.activeSlideClass);
|
|
64
|
+
|
|
65
|
+
this._isActive = true;
|
|
66
|
+
this.dispatchEvent(new Event('activate'));
|
|
67
|
+
this.shower.dispatchEvent(
|
|
68
|
+
new CustomEvent('slidechange', {
|
|
69
|
+
detail: { prev },
|
|
70
|
+
}),
|
|
71
|
+
);
|
|
72
|
+
}
|
|
73
|
+
|
|
74
|
+
/**
|
|
75
|
+
* @throws {ShowerError}
|
|
76
|
+
* @emits Slide#deactivate
|
|
77
|
+
*/
|
|
78
|
+
deactivate() {
|
|
79
|
+
if (this.shower.isFullMode) {
|
|
80
|
+
throw new ShowerError('In full mode, another slide should be activated instead.');
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
if (this._isActive) {
|
|
84
|
+
this._deactivate();
|
|
85
|
+
}
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
_deactivate() {
|
|
89
|
+
this.element.classList.replace(
|
|
90
|
+
this._options.activeSlideClass,
|
|
91
|
+
this._options.visitedSlideClass,
|
|
92
|
+
);
|
|
93
|
+
|
|
94
|
+
this._isActive = false;
|
|
95
|
+
this.dispatchEvent(new Event('deactivate'));
|
|
96
|
+
}
|
|
97
|
+
}
|
|
98
|
+
|
|
99
|
+
export default Slide;
|
package/lib/start.js
ADDED
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
import { contentLoaded } from './utils';
|
|
2
|
+
import Shower from './shower';
|
|
3
|
+
|
|
4
|
+
const options = document.currentScript.dataset;
|
|
5
|
+
const shower = new Shower(options);
|
|
6
|
+
|
|
7
|
+
Object.defineProperty(window, 'shower', {
|
|
8
|
+
value: shower,
|
|
9
|
+
configurable: true,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
contentLoaded(() => {
|
|
13
|
+
shower.start();
|
|
14
|
+
});
|
package/lib/utils.js
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
export const isInteractiveElement = (element) => element.tabIndex !== -1;
|
|
2
|
+
|
|
3
|
+
export const contentLoaded = (callback) => {
|
|
4
|
+
if (document.currentScript.async) {
|
|
5
|
+
callback();
|
|
6
|
+
} else {
|
|
7
|
+
document.addEventListener('DOMContentLoaded', callback);
|
|
8
|
+
}
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const defineReadOnly = (target, props) => {
|
|
12
|
+
for (const [key, value] of Object.entries(props)) {
|
|
13
|
+
Object.defineProperty(target, key, {
|
|
14
|
+
value,
|
|
15
|
+
writable: false,
|
|
16
|
+
enumerable: true,
|
|
17
|
+
configurable: true,
|
|
18
|
+
});
|
|
19
|
+
}
|
|
20
|
+
};
|
|
21
|
+
|
|
22
|
+
export class ShowerError extends Error {}
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@shower/core",
|
|
3
3
|
"description": "Core for Shower HTML presentation engine",
|
|
4
|
-
"version": "3.
|
|
4
|
+
"version": "3.3.0",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -21,43 +21,43 @@
|
|
|
21
21
|
],
|
|
22
22
|
"main": "lib/shower.js",
|
|
23
23
|
"files": [
|
|
24
|
+
"lib",
|
|
24
25
|
"dist/shower.js"
|
|
25
26
|
],
|
|
26
27
|
"devDependencies": {
|
|
27
|
-
"chai": "^4.
|
|
28
|
-
"
|
|
29
|
-
"
|
|
30
|
-
"eslint": "^
|
|
31
|
-
"eslint-config-
|
|
32
|
-
"eslint-
|
|
33
|
-
"eslint-plugin-
|
|
34
|
-
"
|
|
35
|
-
"
|
|
36
|
-
"
|
|
37
|
-
"
|
|
38
|
-
"
|
|
39
|
-
"
|
|
40
|
-
"
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"rollup-plugin-node-resolve": "^4.2.4",
|
|
45
|
-
"sauce-connect-launcher": "^1.2.6",
|
|
46
|
-
"serve": "^11.0.0",
|
|
47
|
-
"serve-handler": "^6.0.0",
|
|
48
|
-
"yn": "^3.1.0"
|
|
28
|
+
"chai": "^4.3.4",
|
|
29
|
+
"chromedriver": "^83.0.1",
|
|
30
|
+
"eslint": "^7.29.0",
|
|
31
|
+
"eslint-config-airbnb-base": "^14.2.1",
|
|
32
|
+
"eslint-config-prettier": "^6.15.0",
|
|
33
|
+
"eslint-plugin-import": "^2.25.2",
|
|
34
|
+
"eslint-plugin-prettier": "^3.4.1",
|
|
35
|
+
"husky": "^4.3.8",
|
|
36
|
+
"lint-staged": "^10.5.4",
|
|
37
|
+
"mocha": "^7.2.0",
|
|
38
|
+
"nightwatch": "^1.7.6",
|
|
39
|
+
"prettier": "^2.3.1",
|
|
40
|
+
"puppeteer": "^3.3.0",
|
|
41
|
+
"rollup": "^2.58.0",
|
|
42
|
+
"sauce-connect-launcher": "^1.3.2",
|
|
43
|
+
"serve": "^11.3.2",
|
|
44
|
+
"serve-handler": "^6.1.3"
|
|
49
45
|
},
|
|
50
46
|
"scripts": {
|
|
51
47
|
"build": "rollup --config",
|
|
48
|
+
"build:watch": "rollup --config --watch",
|
|
52
49
|
"serve": "serve dist",
|
|
53
|
-
"lint": "eslint '**/*.js'",
|
|
54
|
-
"lint:fix": "eslint '**/*.js' --fix",
|
|
50
|
+
"lint": "eslint '**/*.{js,mjs}'",
|
|
51
|
+
"lint:fix": "eslint '**/*.{js,mjs}' --fix",
|
|
55
52
|
"format": "npm run format:js && npm run format:etc",
|
|
56
|
-
"format:js": "prettier '**/*.js' --write",
|
|
53
|
+
"format:js": "prettier '**/*.{js,mjs}' --write",
|
|
57
54
|
"format:etc": "prettier '**/*.{json,md,yml}' --write",
|
|
58
|
-
"test": "npm run lint && npm run test:unit && npm run test:
|
|
59
|
-
"test:unit": "mocha test/unit --require
|
|
60
|
-
"test:local": "nightwatch --env chrome-local",
|
|
61
|
-
"test:sauce": "nightwatch --env chrome,firefox,safari
|
|
55
|
+
"test": "npm run lint && npm run test:unit && npm run test:local",
|
|
56
|
+
"test:unit": "mocha test/unit --require chai/register-should",
|
|
57
|
+
"test:local": "nightwatch --env chrome-local --timeout 500",
|
|
58
|
+
"test:sauce": "nightwatch --env chrome,firefox,safari"
|
|
59
|
+
},
|
|
60
|
+
"config": {
|
|
61
|
+
"test_port": 8031
|
|
62
62
|
}
|
|
63
63
|
}
|