@shower/core 3.2.0 → 3.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,12 +1,14 @@
1
1
  export default {
2
- containerSelector: '.shower',
3
- progressSelector: '.progress',
4
- stepSelector: '.next',
5
- fullModeClass: 'full',
6
- listModeClass: 'list',
2
+ containerSelector: '.shower',
3
+ progressSelector: '.progress',
4
+ stepSelector: '.next',
5
+ fullModeClass: 'full',
6
+ listModeClass: 'list',
7
+ mouseHiddenClass: 'pointless',
8
+ mouseInactivityTimeout: 5000,
7
9
 
8
- slideSelector: '.slide',
9
- slideTitleSelector: 'h2',
10
- activeSlideClass: 'active',
11
- visitedSlideClass: 'visited',
10
+ slideSelector: '.slide',
11
+ slideTitleSelector: 'h2',
12
+ activeSlideClass: 'active',
13
+ visitedSlideClass: 'visited',
12
14
  };
@@ -1,14 +1,14 @@
1
- import { contentLoaded } from './utils';
2
- import Shower from './shower';
1
+ import { contentLoaded } from './utils.js';
2
+ import Shower from './shower.js';
3
3
 
4
4
  const options = document.currentScript.dataset;
5
5
  const shower = new Shower(options);
6
6
 
7
7
  Object.defineProperty(window, 'shower', {
8
- value: shower,
9
- configurable: true,
8
+ value: shower,
9
+ configurable: true,
10
10
  });
11
11
 
12
12
  contentLoaded(() => {
13
- shower.start();
13
+ shower.start();
14
14
  });
@@ -1,38 +1,40 @@
1
1
  const createLiveRegion = () => {
2
- const liveRegion = document.createElement('section');
3
- liveRegion.className = 'region';
4
- liveRegion.setAttribute('role', 'region');
5
- liveRegion.setAttribute('aria-live', 'assertive');
6
- liveRegion.setAttribute('aria-relevant', 'all');
7
- liveRegion.setAttribute('aria-label', 'Slide Content: Auto-updating');
8
- return liveRegion;
2
+ const liveRegion = document.createElement('section');
3
+ liveRegion.className = 'region';
4
+ liveRegion.setAttribute('role', 'region');
5
+ liveRegion.setAttribute('aria-live', 'assertive');
6
+ liveRegion.setAttribute('aria-relevant', 'all');
7
+ liveRegion.setAttribute('aria-label', 'Slide Content: Auto-updating');
8
+ return liveRegion;
9
9
  };
10
10
 
11
11
  export default (shower) => {
12
- const { container } = shower;
13
- const liveRegion = createLiveRegion();
14
- container.appendChild(liveRegion);
12
+ const { container } = shower;
13
+ const liveRegion = createLiveRegion();
14
+ container.appendChild(liveRegion);
15
15
 
16
- const updateDocumentRole = () => {
17
- if (shower.isFullMode) {
18
- container.setAttribute('role', 'application');
19
- } else {
20
- container.removeAttribute('role');
21
- }
22
- };
16
+ const updateDocumentRole = () => {
17
+ if (shower.isFullMode) {
18
+ container.setAttribute('role', 'application');
19
+ } else {
20
+ container.removeAttribute('role');
21
+ }
22
+ };
23
23
 
24
- const updateLiveRegion = () => {
25
- const slide = shower.activeSlide;
26
- if (slide) {
27
- liveRegion.innerHTML = slide.element.innerHTML;
28
- }
29
- };
24
+ const updateLiveRegion = () => {
25
+ const slide = shower.activeSlide;
26
+ if (slide) {
27
+ const clone = slide.element.cloneNode(true);
28
+ clone.querySelectorAll('style').forEach((style) => style.remove());
29
+ liveRegion.innerHTML = clone.innerHTML;
30
+ }
31
+ };
30
32
 
31
- shower.addEventListener('start', () => {
32
- updateDocumentRole();
33
- updateLiveRegion();
34
- });
33
+ shower.addEventListener('start', () => {
34
+ updateDocumentRole();
35
+ updateLiveRegion();
36
+ });
35
37
 
36
- shower.addEventListener('modechange', updateDocumentRole);
37
- shower.addEventListener('slidechange', updateLiveRegion);
38
+ shower.addEventListener('modechange', updateDocumentRole);
39
+ shower.addEventListener('slidechange', updateLiveRegion);
38
40
  };
@@ -1,26 +1,28 @@
1
- import a11y from './a11y';
2
- import keys from './keys';
3
- import location from './location';
4
- import next from './next';
5
- import progress from './progress';
6
- import timer from './timer';
7
- import title from './title';
8
- import view from './view';
9
- import touch from './touch';
1
+ import a11y from './a11y.js';
2
+ import keys from './keys.js';
3
+ import location from './location.js';
4
+ import next from './next.js';
5
+ import progress from './progress.js';
6
+ import timer from './timer.js';
7
+ import title from './title.js';
8
+ import view from './view.js';
9
+ import touch from './touch.js';
10
+ import mouse from './mouse.js';
10
11
 
11
12
  export default (shower) => {
12
- a11y(shower);
13
- progress(shower);
14
- keys(shower);
15
- next(shower);
16
- timer(shower); // should come after `keys` and `next`
17
- title(shower);
18
- location(shower); // should come after `title`
19
- view(shower);
20
- touch(shower);
13
+ a11y(shower);
14
+ progress(shower);
15
+ keys(shower);
16
+ next(shower);
17
+ timer(shower); // should come after `keys` and `next`
18
+ title(shower);
19
+ location(shower); // should come after `title`
20
+ view(shower);
21
+ touch(shower);
22
+ mouse(shower);
21
23
 
22
- // maintains invariant: active slide always exists in `full` mode
23
- if (shower.isFullMode && !shower.activeSlide) {
24
- shower.first();
25
- }
24
+ // maintains invariant: active slide always exists in `full` mode
25
+ if (shower.isFullMode && !shower.activeSlide) {
26
+ shower.first();
27
+ }
26
28
  };
@@ -1,113 +1,113 @@
1
- import { isInteractiveElement } from '../utils';
1
+ import { isInteractiveElement } from '../utils.js';
2
2
 
3
3
  export default (shower) => {
4
- const doSlideActions = (event) => {
5
- const isShowerAction = !(event.ctrlKey || event.altKey || event.metaKey);
4
+ const doSlideActions = (event) => {
5
+ const isShowerAction = !(event.ctrlKey || event.altKey || event.metaKey);
6
6
 
7
- switch (event.key.toUpperCase()) {
8
- case 'ENTER':
9
- if (event.metaKey && shower.isListMode) {
10
- if (event.shiftKey) {
11
- event.preventDefault();
12
- shower.first();
13
- }
7
+ switch (event.key.toUpperCase()) {
8
+ case 'ENTER':
9
+ if (event.metaKey && shower.isListMode) {
10
+ if (event.shiftKey) {
11
+ event.preventDefault();
12
+ shower.first();
13
+ }
14
14
 
15
- break;
16
- }
15
+ break;
16
+ }
17
17
 
18
- event.preventDefault();
19
- if (event.shiftKey) {
20
- shower.prev();
21
- } else {
22
- shower.next();
23
- }
24
- break;
18
+ event.preventDefault();
19
+ if (event.shiftKey) {
20
+ shower.prev();
21
+ } else {
22
+ shower.next();
23
+ }
24
+ break;
25
25
 
26
- case 'BACKSPACE':
27
- case 'PAGEUP':
28
- case 'ARROWUP':
29
- case 'ARROWLEFT':
30
- case 'H':
31
- case 'K':
32
- case 'P':
33
- if (isShowerAction) {
34
- event.preventDefault();
35
- shower.prev(event.shiftKey);
36
- }
37
- break;
26
+ case 'BACKSPACE':
27
+ case 'PAGEUP':
28
+ case 'ARROWUP':
29
+ case 'ARROWLEFT':
30
+ case 'H':
31
+ case 'K':
32
+ case 'P':
33
+ if (isShowerAction) {
34
+ event.preventDefault();
35
+ shower.prev(event.shiftKey);
36
+ }
37
+ break;
38
38
 
39
- case 'PAGEDOWN':
40
- case 'ARROWDOWN':
41
- case 'ARROWRIGHT':
42
- case 'L':
43
- case 'J':
44
- case 'N':
45
- if (isShowerAction) {
46
- event.preventDefault();
47
- shower.next(event.shiftKey);
48
- }
49
- break;
39
+ case 'PAGEDOWN':
40
+ case 'ARROWDOWN':
41
+ case 'ARROWRIGHT':
42
+ case 'L':
43
+ case 'J':
44
+ case 'N':
45
+ if (isShowerAction) {
46
+ event.preventDefault();
47
+ shower.next(event.shiftKey);
48
+ }
49
+ break;
50
50
 
51
- case ' ':
52
- if (isShowerAction && shower.isFullMode) {
53
- event.preventDefault();
54
- if (event.shiftKey) {
55
- shower.prev();
56
- } else {
57
- shower.next();
58
- }
59
- }
60
- break;
51
+ case ' ':
52
+ if (isShowerAction && shower.isFullMode) {
53
+ event.preventDefault();
54
+ if (event.shiftKey) {
55
+ shower.prev();
56
+ } else {
57
+ shower.next();
58
+ }
59
+ }
60
+ break;
61
61
 
62
- case 'HOME':
63
- event.preventDefault();
64
- shower.first();
65
- break;
62
+ case 'HOME':
63
+ event.preventDefault();
64
+ shower.first();
65
+ break;
66
66
 
67
- case 'END':
68
- event.preventDefault();
69
- shower.last();
70
- break;
71
- }
72
- };
67
+ case 'END':
68
+ event.preventDefault();
69
+ shower.last();
70
+ break;
71
+ }
72
+ };
73
73
 
74
- const doModeActions = (event) => {
75
- switch (event.key.toUpperCase()) {
76
- case 'ESCAPE':
77
- if (shower.isFullMode) {
78
- event.preventDefault();
79
- shower.exitFullMode();
80
- }
81
- break;
74
+ const doModeActions = (event) => {
75
+ switch (event.key.toUpperCase()) {
76
+ case 'ESCAPE':
77
+ if (shower.isFullMode) {
78
+ event.preventDefault();
79
+ shower.exitFullMode();
80
+ }
81
+ break;
82
82
 
83
- case 'ENTER':
84
- if (event.metaKey && shower.isListMode) {
85
- event.preventDefault();
86
- shower.enterFullMode();
87
- }
88
- break;
83
+ case 'ENTER':
84
+ if (event.metaKey && shower.isListMode) {
85
+ event.preventDefault();
86
+ shower.enterFullMode();
87
+ }
88
+ break;
89
89
 
90
- case 'P':
91
- if (event.metaKey && event.altKey && shower.isListMode) {
92
- event.preventDefault();
93
- shower.enterFullMode();
94
- }
95
- break;
90
+ case 'P':
91
+ if (event.metaKey && event.altKey && shower.isListMode) {
92
+ event.preventDefault();
93
+ shower.enterFullMode();
94
+ }
95
+ break;
96
96
 
97
- case 'F5':
98
- if (event.shiftKey && shower.isListMode) {
99
- event.preventDefault();
100
- shower.enterFullMode();
101
- }
102
- break;
103
- }
104
- };
97
+ case 'F5':
98
+ if (event.shiftKey && shower.isListMode) {
99
+ event.preventDefault();
100
+ shower.enterFullMode();
101
+ }
102
+ break;
103
+ }
104
+ };
105
105
 
106
- shower.container.addEventListener('keydown', (event) => {
107
- if (event.defaultPrevented) return;
108
- if (isInteractiveElement(event.target)) return;
106
+ shower.container.addEventListener('keydown', (event) => {
107
+ if (event.defaultPrevented) return;
108
+ if (isInteractiveElement(event.target)) return;
109
109
 
110
- doSlideActions(event);
111
- doModeActions(event);
112
- });
110
+ doSlideActions(event);
111
+ doModeActions(event);
112
+ });
113
113
  };
@@ -1,53 +1,53 @@
1
1
  export default (shower) => {
2
- const composeURL = () => {
3
- const search = shower.isFullMode ? '?full' : '';
4
- const slide = shower.activeSlide;
5
- const hash = slide ? `#${slide.id}` : '';
6
-
7
- return location.pathname + search + hash; // path is required to clear search params
8
- };
9
-
10
- const applyURLMode = () => {
11
- const isFull = new URLSearchParams(location.search).has('full');
12
- if (isFull) {
13
- shower.enterFullMode();
14
- } else {
15
- shower.exitFullMode();
16
- }
17
- };
18
-
19
- const applyURLSlide = () => {
20
- const id = location.hash.slice(1);
21
- if (!id) return;
22
-
23
- const target = shower.slides.find((slide) => slide.id === id);
24
- if (target) {
25
- target.activate();
26
- } else if (!shower.activeSlide) {
27
- shower.first(); // invalid hash
28
- }
29
- };
30
-
31
- const applyURL = () => {
32
- applyURLMode();
33
- applyURLSlide();
34
- };
35
-
36
- applyURL();
37
- window.addEventListener('popstate', applyURL);
38
-
39
- shower.addEventListener('start', () => {
40
- history.replaceState(null, document.title, composeURL());
41
- });
42
-
43
- shower.addEventListener('modechange', () => {
44
- history.replaceState(null, document.title, composeURL());
45
- });
46
-
47
- shower.addEventListener('slidechange', () => {
48
- const url = composeURL();
49
- if (!location.href.endsWith(url)) {
50
- history.pushState(null, document.title, url);
51
- }
52
- });
2
+ const composeURL = () => {
3
+ const search = shower.isFullMode ? '?full' : '';
4
+ const slide = shower.activeSlide;
5
+ const hash = slide ? `#${slide.id}` : '';
6
+
7
+ return location.pathname + search + hash; // path is required to clear search params
8
+ };
9
+
10
+ const applyURLMode = () => {
11
+ const isFull = new URLSearchParams(location.search).has('full');
12
+ if (isFull) {
13
+ shower.enterFullMode();
14
+ } else {
15
+ shower.exitFullMode();
16
+ }
17
+ };
18
+
19
+ const applyURLSlide = () => {
20
+ const id = location.hash.slice(1);
21
+ if (!id) return;
22
+
23
+ const target = shower.slides.find((slide) => slide.id === id);
24
+ if (target) {
25
+ target.activate();
26
+ } else if (!shower.activeSlide) {
27
+ shower.first(); // invalid hash
28
+ }
29
+ };
30
+
31
+ const applyURL = () => {
32
+ applyURLMode();
33
+ applyURLSlide();
34
+ };
35
+
36
+ applyURL();
37
+ window.addEventListener('popstate', applyURL);
38
+
39
+ shower.addEventListener('start', () => {
40
+ history.replaceState(null, document.title, composeURL());
41
+ });
42
+
43
+ shower.addEventListener('modechange', () => {
44
+ history.replaceState(null, document.title, composeURL());
45
+ });
46
+
47
+ shower.addEventListener('slidechange', () => {
48
+ const url = composeURL();
49
+ if (!location.href.endsWith(url)) {
50
+ history.pushState(null, document.title, url);
51
+ }
52
+ });
53
53
  };
@@ -0,0 +1,41 @@
1
+ export default (shower) => {
2
+ const { mouseHiddenClass, mouseInactivityTimeout } = shower.options;
3
+
4
+ let hideMouseTimeoutId = null;
5
+
6
+ const cleanUp = () => {
7
+ shower.container.classList.remove(mouseHiddenClass);
8
+ clearTimeout(hideMouseTimeoutId);
9
+ hideMouseTimeoutId = null;
10
+ };
11
+
12
+ const hideMouseIfInactive = () => {
13
+ if (hideMouseTimeoutId !== null) {
14
+ cleanUp();
15
+ }
16
+
17
+ hideMouseTimeoutId = setTimeout(() => {
18
+ shower.container.classList.add(mouseHiddenClass);
19
+ }, mouseInactivityTimeout);
20
+ };
21
+
22
+ const initHideMouseIfInactiveModule = () => {
23
+ shower.container.addEventListener('mousemove', hideMouseIfInactive);
24
+ };
25
+
26
+ const destroyHideMouseIfInactiveModule = () => {
27
+ shower.container.removeEventListener('mousemove', hideMouseIfInactive);
28
+ cleanUp();
29
+ };
30
+
31
+ const handleModeChange = () => {
32
+ if (shower.isFullMode) {
33
+ initHideMouseIfInactiveModule();
34
+ } else {
35
+ destroyHideMouseIfInactiveModule();
36
+ }
37
+ };
38
+
39
+ shower.addEventListener('start', handleModeChange);
40
+ shower.addEventListener('modechange', handleModeChange);
41
+ };
@@ -1,54 +1,54 @@
1
1
  export default (shower) => {
2
- const { stepSelector, activeSlideClass, visitedSlideClass } = shower.options;
2
+ const { stepSelector, activeSlideClass, visitedSlideClass } = shower.options;
3
3
 
4
- let innerSteps;
5
- let activeIndex;
4
+ let innerSteps;
5
+ let activeIndex;
6
6
 
7
- const isActive = (step) => step.classList.contains(activeSlideClass);
8
- const isVisited = (step) => step.classList.contains(visitedSlideClass);
7
+ const isActive = (step) => step.classList.contains(activeSlideClass);
8
+ const isVisited = (step) => step.classList.contains(visitedSlideClass);
9
9
 
10
- const setInnerStepsState = () => {
11
- if (shower.isListMode) return;
10
+ const setInnerStepsState = () => {
11
+ if (shower.isListMode) return;
12
12
 
13
- const slide = shower.activeSlide;
13
+ const slide = shower.activeSlide;
14
14
 
15
- innerSteps = [...slide.element.querySelectorAll(stepSelector)];
16
- activeIndex =
17
- innerSteps.length && innerSteps.every(isVisited)
18
- ? innerSteps.length
19
- : innerSteps.filter(isActive).length - 1;
15
+ innerSteps = [...slide.element.querySelectorAll(stepSelector)];
16
+ activeIndex =
17
+ innerSteps.length && innerSteps.every(isVisited)
18
+ ? innerSteps.length
19
+ : innerSteps.filter(isActive).length - 1;
20
20
 
21
- slide.state.innerStepCount = innerSteps.length;
22
- };
21
+ slide.state.innerStepCount = innerSteps.length;
22
+ };
23
23
 
24
- shower.addEventListener('start', setInnerStepsState);
25
- shower.addEventListener('modechange', setInnerStepsState);
26
- shower.addEventListener('slidechange', setInnerStepsState);
24
+ shower.addEventListener('start', setInnerStepsState);
25
+ shower.addEventListener('modechange', setInnerStepsState);
26
+ shower.addEventListener('slidechange', setInnerStepsState);
27
27
 
28
- shower.addEventListener('next', (event) => {
29
- if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
28
+ shower.addEventListener('next', (event) => {
29
+ if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
30
30
 
31
- activeIndex++;
32
- innerSteps.forEach((step, index) => {
33
- step.classList.toggle(visitedSlideClass, index < activeIndex);
34
- step.classList.toggle(activeSlideClass, index === activeIndex);
35
- });
31
+ activeIndex++;
32
+ innerSteps.forEach((step, index) => {
33
+ step.classList.toggle(visitedSlideClass, index < activeIndex);
34
+ step.classList.toggle(activeSlideClass, index === activeIndex);
35
+ });
36
36
 
37
- if (activeIndex < innerSteps.length) {
38
- event.preventDefault();
39
- }
40
- });
37
+ if (activeIndex < innerSteps.length) {
38
+ event.preventDefault();
39
+ }
40
+ });
41
41
 
42
- shower.addEventListener('prev', (event) => {
43
- if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
44
- if (activeIndex === -1 || activeIndex === innerSteps.length) return;
42
+ shower.addEventListener('prev', (event) => {
43
+ if (shower.isListMode || event.defaultPrevented || !event.cancelable) return;
44
+ if (activeIndex === -1 || activeIndex === innerSteps.length) return;
45
45
 
46
- activeIndex--;
47
- innerSteps.forEach((step, index) => {
48
- step.classList.toggle(visitedSlideClass, index < activeIndex + 1);
49
- step.classList.toggle(activeSlideClass, index === activeIndex);
50
- });
46
+ activeIndex--;
47
+ innerSteps.forEach((step, index) => {
48
+ step.classList.toggle(visitedSlideClass, index < activeIndex + 1);
49
+ step.classList.toggle(activeSlideClass, index === activeIndex);
50
+ });
51
51
 
52
- event.preventDefault();
53
- });
52
+ event.preventDefault();
53
+ });
54
54
  };
@@ -0,0 +1,28 @@
1
+ const units = ['s', 'm', 'h'];
2
+ const hasUnits = (timing) => {
3
+ return units.some((unit) => timing.includes(unit));
4
+ };
5
+
6
+ const parseUnits = (timing) => {
7
+ return units.map((unit) => timing.match(`(\\S+)${unit}`)).map((match) => match && match[1]);
8
+ };
9
+
10
+ const parseColons = (timing) => {
11
+ return `::${timing}`.split(':').reverse();
12
+ };
13
+
14
+ const SEC_IN_MIN = 60;
15
+ const SEC_IN_HOUR = SEC_IN_MIN * 60;
16
+
17
+ export default (timing) => {
18
+ if (!timing) return 0;
19
+
20
+ const parsed = hasUnits(timing) ? parseUnits(timing) : parseColons(timing);
21
+
22
+ let [sec, min, hour] = parsed.map(Number);
23
+
24
+ sec += min * SEC_IN_MIN;
25
+ sec += hour * SEC_IN_HOUR;
26
+
27
+ return Math.max(sec * 1000, 0);
28
+ };