uikit 3.23.13 → 3.23.14-dev.be820cd99

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.
Files changed (128) hide show
  1. package/.prettierignore +1 -0
  2. package/CHANGELOG.md +36 -0
  3. package/dist/css/uikit-core-rtl.css +301 -111
  4. package/dist/css/uikit-core-rtl.min.css +1 -1
  5. package/dist/css/uikit-core.css +301 -111
  6. package/dist/css/uikit-core.min.css +1 -1
  7. package/dist/css/uikit-rtl.css +305 -111
  8. package/dist/css/uikit-rtl.min.css +1 -1
  9. package/dist/css/uikit.css +305 -111
  10. package/dist/css/uikit.min.css +1 -1
  11. package/dist/js/components/countdown.js +1 -1
  12. package/dist/js/components/countdown.min.js +1 -1
  13. package/dist/js/components/filter.js +49 -23
  14. package/dist/js/components/filter.min.js +1 -1
  15. package/dist/js/components/lightbox-panel.js +19 -14
  16. package/dist/js/components/lightbox-panel.min.js +1 -1
  17. package/dist/js/components/lightbox.js +19 -14
  18. package/dist/js/components/lightbox.min.js +1 -1
  19. package/dist/js/components/notification.js +1 -1
  20. package/dist/js/components/notification.min.js +1 -1
  21. package/dist/js/components/parallax.js +2 -2
  22. package/dist/js/components/parallax.min.js +1 -1
  23. package/dist/js/components/slider-parallax.js +2 -2
  24. package/dist/js/components/slider-parallax.min.js +1 -1
  25. package/dist/js/components/slider.js +8 -3
  26. package/dist/js/components/slider.min.js +1 -1
  27. package/dist/js/components/slideshow-parallax.js +2 -2
  28. package/dist/js/components/slideshow-parallax.min.js +1 -1
  29. package/dist/js/components/slideshow.js +8 -3
  30. package/dist/js/components/slideshow.min.js +1 -1
  31. package/dist/js/components/sortable.js +48 -22
  32. package/dist/js/components/sortable.min.js +1 -1
  33. package/dist/js/components/tooltip.js +3 -3
  34. package/dist/js/components/tooltip.min.js +1 -1
  35. package/dist/js/components/upload.js +2 -2
  36. package/dist/js/components/upload.min.js +1 -1
  37. package/dist/js/uikit-core.js +136 -63
  38. package/dist/js/uikit-core.min.js +1 -1
  39. package/dist/js/uikit-icons.js +1 -1
  40. package/dist/js/uikit-icons.min.js +1 -1
  41. package/dist/js/uikit.js +185 -90
  42. package/dist/js/uikit.min.js +1 -1
  43. package/eslint.config.mjs +1 -1
  44. package/package.json +1 -1
  45. package/src/images/backgrounds/form-checkbox.svg +2 -2
  46. package/src/images/components/navbar-toggle-icon.svg +14 -7
  47. package/src/js/api/options.js +1 -1
  48. package/src/js/components/lightbox-panel.js +6 -4
  49. package/src/js/components/upload.js +1 -3
  50. package/src/js/core/drop.js +3 -8
  51. package/src/js/core/dropnav.js +4 -4
  52. package/src/js/core/img.js +1 -1
  53. package/src/js/core/index.js +1 -0
  54. package/src/js/core/navbar.js +3 -5
  55. package/src/js/core/overflow-fade.js +67 -0
  56. package/src/js/core/scrollspy.js +10 -10
  57. package/src/js/core/sticky.js +18 -16
  58. package/src/js/core/toggle.js +3 -5
  59. package/src/js/core/video.js +36 -10
  60. package/src/js/mixin/internal/animate-fade.js +32 -15
  61. package/src/js/mixin/internal/animate-slide.js +27 -9
  62. package/src/js/mixin/modal.js +3 -4
  63. package/src/js/mixin/slider.js +3 -1
  64. package/src/js/mixin/togglable.js +1 -1
  65. package/src/js/util/animation.js +3 -4
  66. package/src/js/util/await.js +7 -0
  67. package/src/js/util/player.js +1 -3
  68. package/src/js/util/scroll.js +2 -2
  69. package/src/js/util/selector.js +1 -1
  70. package/src/js/util/viewport.js +3 -1
  71. package/src/less/components/base.less +19 -4
  72. package/src/less/components/card.less +40 -1
  73. package/src/less/components/countdown.less +4 -42
  74. package/src/less/components/form.less +87 -67
  75. package/src/less/components/heading.less +0 -84
  76. package/src/less/components/icon.less +44 -0
  77. package/src/less/components/navbar.less +23 -9
  78. package/src/less/components/position.less +1 -1
  79. package/src/less/components/section.less +33 -0
  80. package/src/less/components/subnav.less +1 -1
  81. package/src/less/components/utility.less +45 -0
  82. package/src/less/components/visibility.less +1 -1
  83. package/src/less/components/width.less +0 -5
  84. package/src/less/theme/card.less +13 -0
  85. package/src/less/theme/icon.less +14 -0
  86. package/src/scss/components/base.scss +19 -4
  87. package/src/scss/components/card.scss +35 -1
  88. package/src/scss/components/countdown.scss +2 -40
  89. package/src/scss/components/form.scss +73 -55
  90. package/src/scss/components/heading.scss +0 -83
  91. package/src/scss/components/icon.scss +22 -0
  92. package/src/scss/components/navbar.scss +23 -9
  93. package/src/scss/components/position.scss +1 -1
  94. package/src/scss/components/section.scss +33 -0
  95. package/src/scss/components/subnav.scss +1 -1
  96. package/src/scss/components/utility.scss +45 -0
  97. package/src/scss/components/visibility.scss +1 -1
  98. package/src/scss/components/width.scss +0 -15
  99. package/src/scss/mixins-theme.scss +28 -8
  100. package/src/scss/mixins.scss +25 -8
  101. package/src/scss/variables-theme.scss +27 -30
  102. package/src/scss/variables.scss +25 -30
  103. package/tests/article.html +7 -7
  104. package/tests/base.html +13 -13
  105. package/tests/card.html +9 -1
  106. package/tests/column.html +1 -1
  107. package/tests/countdown.html +595 -8
  108. package/tests/cover.html +7 -13
  109. package/tests/dropbar.html +3 -3
  110. package/tests/dropdown.html +13 -13
  111. package/tests/dropnav.html +9 -9
  112. package/tests/form.html +56 -3
  113. package/tests/icon.html +31 -0
  114. package/tests/index.html +68 -58
  115. package/tests/js/index.js +76 -83
  116. package/tests/lightbox.html +4 -4
  117. package/tests/link.html +71 -8
  118. package/tests/modal.html +1 -1
  119. package/tests/navbar.html +32 -32
  120. package/tests/notification.html +5 -5
  121. package/tests/search.html +3 -3
  122. package/tests/slideshow.html +3 -3
  123. package/tests/sticky-navbar.html +72 -72
  124. package/tests/subnav.html +106 -2
  125. package/tests/tab.html +68 -21
  126. package/tests/table.html +8 -8
  127. package/tests/utility.html +159 -1
  128. package/tests/video.html +137 -10
package/eslint.config.mjs CHANGED
@@ -10,7 +10,7 @@ export default [
10
10
  },
11
11
  {
12
12
  rules: {
13
- 'no-unused-vars': ['error', { caughtErrors: 'none' }],
13
+ 'no-empty': ['error', { allowEmptyCatch: true }],
14
14
  },
15
15
  languageOptions: {
16
16
  ecmaVersion: 'latest',
package/package.json CHANGED
@@ -2,7 +2,7 @@
2
2
  "name": "uikit",
3
3
  "title": "UIkit",
4
4
  "description": "UIkit is a lightweight and modular front-end framework for developing fast and powerful web interfaces.",
5
- "version": "3.23.13",
5
+ "version": "3.23.14-dev.be820cd99",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -1,3 +1,3 @@
1
- <svg width="14" height="11" viewBox="0 0 14 11" xmlns="http://www.w3.org/2000/svg">
2
- <polygon fill="#000" points="12 1 5 7.5 2 5 1 5.5 5 10 13 1.5" />
1
+ <svg width="16" height="16" viewBox="0 0 16 16" xmlns="http://www.w3.org/2000/svg">
2
+ <polygon fill="#000" points="13 3.5 6 10 3 7.5 2 8 6 12.5 14 4 13 3.5" />
3
3
  </svg>
@@ -9,15 +9,22 @@
9
9
  }
10
10
 
11
11
  .uk-navbar-toggle-icon svg > .line-3 { opacity: 0; }
12
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-3 { opacity: 1; }
12
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-3,
13
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-3 { opacity: 1; }
13
14
 
14
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-2 { transform: rotate(45deg); }
15
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-3 { transform: rotate(-45deg); }
15
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-2,
16
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-2 { transform: rotate(45deg); }
17
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-3,
18
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-3 { transform: rotate(-45deg); }
16
19
 
17
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-1,
18
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-4 { opacity: 0; }
19
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-1 { transform: translateY(6px) scaleX(0); }
20
- .uk-navbar-toggle-animate[aria-expanded="true"] svg > .line-4 { transform: translateY(-6px) scaleX(0); }
20
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-1,
21
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-1,
22
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-4,
23
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-4 { opacity: 0; }
24
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-1,
25
+ [aria-expanded="true"] >.uk-navbar-toggle-icon svg > .line-1 { transform: translateY(6px) scaleX(0); }
26
+ [aria-expanded="true"].uk-navbar-toggle-icon svg > .line-4,
27
+ [aria-expanded="true"] > .uk-navbar-toggle-icon svg > .line-4 { transform: translateY(-6px) scaleX(0); }
21
28
 
22
29
  </style>
23
30
  <rect class="line-1" y="3" width="20" height="2" />
@@ -149,7 +149,7 @@ export function parseOptions(options, args = []) {
149
149
  return options;
150
150
  }, {})
151
151
  : {};
152
- } catch (e) {
152
+ } catch {
153
153
  return {};
154
154
  }
155
155
  }
@@ -285,7 +285,7 @@ export default {
285
285
  allowfullscreen: '',
286
286
  style: 'max-width: 100%; box-sizing: border-box;',
287
287
  'uk-responsive': '',
288
- 'uk-video': `${Boolean(this.videoAutoplay)}`,
288
+ 'uk-video': Boolean(this.videoAutoplay),
289
289
  };
290
290
 
291
291
  // Image
@@ -310,8 +310,9 @@ export default {
310
310
  playsinline: '',
311
311
  controls: inline ? null : '',
312
312
  loop: inline ? '' : null,
313
+ muted: inline ? '' : null,
313
314
  poster: this.videoAutoplay ? null : item.poster,
314
- 'uk-video': inline ? 'automute: true' : Boolean(this.videoAutoplay),
315
+ 'uk-video': Boolean(this.videoAutoplay),
315
316
  ...attrs,
316
317
  });
317
318
 
@@ -373,7 +374,7 @@ export default {
373
374
  ...attrs,
374
375
  }),
375
376
  );
376
- } catch (e) {
377
+ } catch {
377
378
  this.setError(item);
378
379
  }
379
380
  }
@@ -460,7 +461,8 @@ function toThumbnavItem(item, videoAutoplay) {
460
461
  src: item.thumb,
461
462
  loop: '',
462
463
  playsinline: '',
463
- 'uk-video': `autoplay: ${Boolean(videoAutoplay)}; automute: true`,
464
+ muted: '',
465
+ 'uk-video': videoAutoplay === 'inline',
464
466
  })
465
467
  : createEl('canvas');
466
468
 
@@ -217,9 +217,7 @@ function send(url, env) {
217
217
  if (prop in xhr) {
218
218
  try {
219
219
  xhr[prop] = env[prop];
220
- } catch (e) {
221
- // noop
222
- }
220
+ } catch {}
223
221
  }
224
222
  }
225
223
 
@@ -28,6 +28,7 @@ import {
28
28
  query,
29
29
  removeClass,
30
30
  } from 'uikit-util';
31
+ import Class from '../mixin/class';
31
32
  import Container from '../mixin/container';
32
33
  import { maybeDefaultPreventClick } from '../mixin/event';
33
34
  import Position, { storeScrollPosition } from '../mixin/position';
@@ -38,7 +39,7 @@ import { preventBackgroundScroll } from '../util/scroll';
38
39
  export let active;
39
40
 
40
41
  export default {
41
- mixins: [Container, Position, Togglable],
42
+ mixins: [Class, Container, Position, Togglable],
42
43
 
43
44
  args: 'pos',
44
45
 
@@ -55,7 +56,6 @@ export default {
55
56
  delayShow: Number,
56
57
  delayHide: Number,
57
58
  autoUpdate: Boolean,
58
- clsDrop: String,
59
59
  animateOut: Boolean,
60
60
  bgScroll: Boolean,
61
61
  closeOnScroll: Boolean,
@@ -74,7 +74,6 @@ export default {
74
74
  delayShow: 0,
75
75
  delayHide: 800,
76
76
  autoUpdate: true,
77
- clsDrop: false,
78
77
  animateOut: false,
79
78
  bgScroll: true,
80
79
  animation: ['uk-animation-fade'],
@@ -107,12 +106,8 @@ export default {
107
106
  this.tracker = new MouseTracker();
108
107
  },
109
108
 
110
- beforeConnect() {
111
- this.clsDrop = this.$props.clsDrop || this.$options.id;
112
- },
113
-
114
109
  connected() {
115
- addClass(this.$el, 'uk-drop', this.clsDrop);
110
+ addClass(this.$el, 'uk-drop');
116
111
 
117
112
  if (this.toggle && !this.targetEl) {
118
113
  this.targetEl = createToggleComponent(this);
@@ -37,7 +37,6 @@ export default {
37
37
 
38
38
  props: {
39
39
  align: String,
40
- clsDrop: String,
41
40
  boundary: Boolean,
42
41
  dropbar: Boolean,
43
42
  dropbarAnchor: Boolean,
@@ -62,6 +61,7 @@ export default {
62
61
  boundary: true,
63
62
  dropbar: false,
64
63
  dropbarAnchor: false,
64
+ flip: true,
65
65
  delayShow: 160,
66
66
  duration: 200,
67
67
  container: false,
@@ -79,7 +79,7 @@ export default {
79
79
  dropbar =
80
80
  this._dropbar || query(dropbar, this.$el) || $(`+ .${this.clsDropbar}`, this.$el);
81
81
 
82
- return dropbar ? dropbar : (this._dropbar = $('<div></div>'));
82
+ return dropbar ? dropbar : (this._dropbar = $('<div>'));
83
83
  },
84
84
 
85
85
  dropContainer(_, $el) {
@@ -404,10 +404,10 @@ export default {
404
404
  this.dropdowns.filter((el) => !this.getDropdown(el)),
405
405
  {
406
406
  ...this.$props,
407
- flip: false,
407
+ flip: this.flip && !this.$props.dropbar,
408
408
  shift: true,
409
409
  pos: `bottom-${this.align}`,
410
- boundary: this.boundary === true ? this.$el : this.boundary,
410
+ boundaryX: this.boundary === true ? this.$el : this.boundary,
411
411
  },
412
412
  );
413
413
  },
@@ -138,7 +138,7 @@ function parseSources(sources) {
138
138
  if (startsWith(sources, '[')) {
139
139
  try {
140
140
  sources = JSON.parse(sources);
141
- } catch (e) {
141
+ } catch {
142
142
  sources = [];
143
143
  }
144
144
  } else {
@@ -18,6 +18,7 @@ export { default as Nav } from './nav';
18
18
  export { default as Navbar } from './navbar';
19
19
  export { default as Offcanvas } from './offcanvas';
20
20
  export { default as OverflowAuto } from './overflow-auto';
21
+ export { default as OverflowFade } from './overflow-fade';
21
22
  export { default as Responsive } from './responsive';
22
23
  export { default as Scroll } from './scroll';
23
24
  export { default as Scrollspy } from './scrollspy';
@@ -1,4 +1,5 @@
1
1
  import { $$, addClass, css, hasClass, offset, removeClass } from 'uikit-util';
2
+ import { awaitTimeout } from '../util/await';
2
3
  import { active } from './drop';
3
4
  import Dropnav from './dropnav';
4
5
 
@@ -12,6 +13,7 @@ export default {
12
13
  },
13
14
 
14
15
  data: {
16
+ flip: false,
15
17
  delayShow: 200,
16
18
  clsDrop: 'uk-navbar-dropdown',
17
19
  selNavItem:
@@ -59,7 +61,7 @@ export default {
59
61
  el: ({ dropContainer }) => dropContainer,
60
62
 
61
63
  async handler() {
62
- await awaitMacroTask();
64
+ await awaitTimeout();
63
65
 
64
66
  if (this._transparent && (!active || !this.dropContainer.contains(active.$el))) {
65
67
  addClass(this.navbarContainer, clsNavbarTransparent);
@@ -92,7 +94,3 @@ export default {
92
94
  },
93
95
  },
94
96
  };
95
-
96
- function awaitMacroTask() {
97
- return new Promise((resolve) => setTimeout(resolve));
98
- }
@@ -0,0 +1,67 @@
1
+ import { children, clamp, css, toggleClass } from 'uikit-util';
2
+ import { mutation, resize } from '../api/observables';
3
+
4
+ export default {
5
+ data: {
6
+ fadeDuration: 0.05,
7
+ },
8
+
9
+ events: {
10
+ name: 'scroll',
11
+
12
+ self: true,
13
+
14
+ passive: true,
15
+
16
+ handler() {
17
+ this.$emit();
18
+ },
19
+ },
20
+
21
+ observe: [
22
+ mutation({
23
+ options: {
24
+ subtree: true,
25
+ childList: true,
26
+ },
27
+ }),
28
+ resize({
29
+ target: ({ $el }) => [$el, ...children($el)],
30
+ }),
31
+ ],
32
+
33
+ update: {
34
+ read() {
35
+ const overflow = [];
36
+ for (const prop of ['Width', 'Height']) {
37
+ overflow.push(this.$el[`scroll${prop}`] - this.$el[`client${prop}`]);
38
+ }
39
+ return { overflow };
40
+ },
41
+
42
+ write({ overflow }) {
43
+ for (let i = 0; i < 2; i++) {
44
+ toggleClass(
45
+ this.$el,
46
+ `${this.$options.id}-${i ? 'vertical' : 'horizontal'}`,
47
+ overflow[i] && !overflow[i - 1],
48
+ );
49
+
50
+ if (!overflow[i - 1]) {
51
+ const dir = i ? 'Top' : 'Left';
52
+ const percent = overflow[i] ? this.$el[`scroll${dir}`] / overflow[i] : 0;
53
+
54
+ const toValue = (value) =>
55
+ overflow[i] ? clamp((this.fadeDuration - value) / this.fadeDuration) : 1;
56
+
57
+ css(this.$el, {
58
+ '--uk-overflow-fade-start-opacity': toValue(percent),
59
+ '--uk-overflow-fade-end-opacity': toValue(1 - percent),
60
+ });
61
+ }
62
+ }
63
+ },
64
+
65
+ events: ['resize'],
66
+ },
67
+ };
@@ -9,6 +9,7 @@ import {
9
9
  trigger,
10
10
  } from 'uikit-util';
11
11
  import { intersection } from '../api/observables';
12
+ import { awaitTimeout } from '../util/await';
12
13
 
13
14
  const clsInView = 'uk-scrollspy-inview';
14
15
 
@@ -52,7 +53,7 @@ export default {
52
53
 
53
54
  disconnected() {
54
55
  for (const [el, state] of this.elementData.entries()) {
55
- removeClass(el, clsInView, state?.cls || '');
56
+ removeClass(el, clsInView, state.cls || '');
56
57
  }
57
58
  delete this.elementData;
58
59
  },
@@ -89,15 +90,14 @@ export default {
89
90
  if (state.show && !state.inview && !state.queued) {
90
91
  state.queued = true;
91
92
 
92
- data.promise = (data.promise || Promise.resolve())
93
- .then(() => new Promise((resolve) => setTimeout(resolve, this.delay)))
94
- .then(() => {
95
- this.toggle(el, true);
96
- setTimeout(() => {
97
- state.queued = false;
98
- this.$emit();
99
- }, 300);
100
- });
93
+ data.promise = (data.promise || Promise.resolve()).then(async () => {
94
+ await awaitTimeout(state.show ? this.delay : 0);
95
+ this.toggle(el, true);
96
+ setTimeout(() => {
97
+ state.queued = false;
98
+ this.$emit();
99
+ }, 300);
100
+ });
101
101
  } else if (!state.show && state.inview && !state.queued && this.repeat) {
102
102
  this.toggle(el, false);
103
103
  }
@@ -30,6 +30,7 @@ import {
30
30
  import { resize, scroll, viewport } from '../api/observables';
31
31
  import Class from '../mixin/class';
32
32
  import Media from '../mixin/media';
33
+ import { awaitFrame, awaitTimeout } from '../util/await';
33
34
 
34
35
  export default {
35
36
  mixins: [Class, Media],
@@ -123,26 +124,26 @@ export default {
123
124
 
124
125
  filter: ({ targetOffset }) => targetOffset !== false,
125
126
 
126
- handler() {
127
+ async handler() {
127
128
  const { scrollingElement } = document;
128
129
 
129
130
  if (!location.hash || scrollingElement.scrollTop === 0) {
130
131
  return;
131
132
  }
132
133
 
133
- setTimeout(() => {
134
- const targetOffset = getOffset($(location.hash));
135
- const elOffset = getOffset(this.$el);
136
-
137
- if (this.isFixed && intersectRect(targetOffset, elOffset)) {
138
- scrollingElement.scrollTop = Math.ceil(
139
- targetOffset.top -
140
- elOffset.height -
141
- toPx(this.targetOffset, 'height', this.placeholder) -
142
- toPx(this.offset, 'height', this.placeholder),
143
- );
144
- }
145
- });
134
+ await awaitTimeout();
135
+
136
+ const targetOffset = getOffset($(location.hash));
137
+ const elOffset = getOffset(this.$el);
138
+
139
+ if (this.isFixed && intersectRect(targetOffset, elOffset)) {
140
+ scrollingElement.scrollTop = Math.ceil(
141
+ targetOffset.top -
142
+ elOffset.height -
143
+ toPx(this.targetOffset, 'height', this.placeholder) -
144
+ toPx(this.offset, 'height', this.placeholder),
145
+ );
146
+ }
146
147
  },
147
148
  },
148
149
  ],
@@ -491,10 +492,11 @@ function reset(el) {
491
492
  }
492
493
 
493
494
  const clsTransitionDisable = 'uk-transition-disable';
494
- function preventTransition(element) {
495
+ async function preventTransition(element) {
495
496
  if (!hasClass(element, clsTransitionDisable)) {
496
497
  addClass(element, clsTransitionDisable);
497
- requestAnimationFrame(() => removeClass(element, clsTransitionDisable));
498
+ await awaitFrame();
499
+ removeClass(element, clsTransitionDisable);
498
500
  }
499
501
  }
500
502
 
@@ -96,9 +96,7 @@ export default {
96
96
  },
97
97
 
98
98
  {
99
- // mouseenter mouseleave are added because of Firefox bug,
100
- // where pointerleave is triggered immediately after pointerenter on scroll
101
- name: `mouseenter mouseleave ${pointerEnter} ${pointerLeave} focus blur`,
99
+ name: `${pointerEnter} ${pointerLeave} focus blur`,
102
100
 
103
101
  filter: ({ mode }) => includes(mode, 'hover'),
104
102
 
@@ -107,14 +105,14 @@ export default {
107
105
  return;
108
106
  }
109
107
 
110
- const show = includes(['mouseenter', pointerEnter, 'focus'], e.type);
108
+ const show = includes([pointerEnter, 'focus'], e.type);
111
109
  const expanded = this.isToggled(this.target);
112
110
 
113
111
  // Skip hide if still hovered or focused
114
112
  if (
115
113
  !show &&
116
114
  (!isBoolean(this._showState) ||
117
- (e.type !== 'blur' && matches(this.$el, ':focus')) ||
115
+ (e.type === pointerLeave && matches(this.$el, ':focus')) ||
118
116
  (e.type === 'blur' && matches(this.$el, ':hover')))
119
117
  ) {
120
118
  // Reset showState if already hidden
@@ -9,6 +9,7 @@ import {
9
9
  play,
10
10
  pointerEnter,
11
11
  pointerLeave,
12
+ query,
12
13
  } from 'uikit-util';
13
14
  import { intersection } from '../api/observables';
14
15
 
@@ -18,31 +19,37 @@ export default {
18
19
  props: {
19
20
  automute: Boolean,
20
21
  autoplay: Boolean,
22
+ restart: Boolean,
23
+ hoverTarget: Boolean,
21
24
  },
22
25
 
23
26
  data: {
24
27
  automute: false,
25
28
  autoplay: true,
29
+ restart: false,
30
+ hoverTarget: false,
26
31
  },
27
32
 
28
33
  beforeConnect() {
29
- if (this.autoplay === 'inview' && !hasAttr(this.$el, 'preload')) {
34
+ const isVideo = isTag(this.$el, 'video');
35
+ if (this.autoplay === 'inview' && isVideo && !hasAttr(this.$el, 'preload')) {
30
36
  this.$el.preload = 'none';
31
37
  }
32
38
 
33
- if (isTag(this.$el, 'iframe') && !hasAttr(this.$el, 'allow')) {
39
+ if (!isVideo && !hasAttr(this.$el, 'allow')) {
34
40
  this.$el.allow = 'autoplay';
35
41
  }
36
42
 
37
43
  if (this.autoplay === 'hover') {
38
- if (isTag(this.$el, 'video')) {
44
+ if (isVideo) {
39
45
  this.$el.tabIndex = 0;
40
46
  } else {
41
47
  this.autoplay = true;
42
48
  }
43
49
  }
44
50
 
45
- if (this.automute) {
51
+ // If the video is added to the DOM through JS, the muted attribute is ignored
52
+ if (this.automute || hasAttr(this.$el, 'muted')) {
46
53
  mute(this.$el);
47
54
  }
48
55
  },
@@ -51,13 +58,15 @@ export default {
51
58
  {
52
59
  name: `${pointerEnter} focusin`,
53
60
 
61
+ el: ({ hoverTarget, $el }) => query(hoverTarget, $el) || $el,
62
+
54
63
  filter: ({ autoplay }) => includes(autoplay, 'hover'),
55
64
 
56
65
  handler(e) {
57
66
  if (!isTouch(e) || !isPlaying(this.$el)) {
58
67
  play(this.$el);
59
68
  } else {
60
- pause(this.$el);
69
+ pauseHover(this.$el, this.restart);
61
70
  }
62
71
  },
63
72
  },
@@ -65,11 +74,13 @@ export default {
65
74
  {
66
75
  name: `${pointerLeave} focusout`,
67
76
 
77
+ el: ({ hoverTarget, $el }) => query(hoverTarget, $el) || $el,
78
+
68
79
  filter: ({ autoplay }) => includes(autoplay, 'hover'),
69
80
 
70
81
  handler(e) {
71
82
  if (!isTouch(e)) {
72
- pause(this.$el);
83
+ pauseHover(this.$el, this.restart);
73
84
  }
74
85
  },
75
86
  },
@@ -77,15 +88,23 @@ export default {
77
88
 
78
89
  observe: [
79
90
  intersection({
80
- filter: ({ autoplay }) => autoplay !== 'hover',
81
- handler([{ isIntersecting }]) {
91
+ filter: ({ $el }) => $el.preload === 'none',
92
+ handler([{ target }]) {
93
+ target.preload = '';
94
+ this.$reset();
95
+ },
96
+ }),
97
+
98
+ intersection({
99
+ filter: ({ $el, autoplay }) => autoplay !== 'hover' && $el.preload !== 'none',
100
+ handler([{ isIntersecting, target }]) {
82
101
  if (!document.fullscreenElement) {
83
102
  if (isIntersecting) {
84
103
  if (this.autoplay) {
85
- play(this.$el);
104
+ play(target);
86
105
  }
87
106
  } else {
88
- pause(this.$el);
107
+ pauseHover(target, this.restart);
89
108
  }
90
109
  }
91
110
  },
@@ -100,3 +119,10 @@ export default {
100
119
  function isPlaying(videoEl) {
101
120
  return !videoEl.paused && !videoEl.ended;
102
121
  }
122
+
123
+ function pauseHover(el, restart) {
124
+ pause(el);
125
+ if (restart && isTag(el, 'video')) {
126
+ el.currentTime = 0;
127
+ }
128
+ }
@@ -4,6 +4,7 @@ import {
4
4
  css,
5
5
  hasClass,
6
6
  height,
7
+ isInView,
7
8
  isVisible,
8
9
  once,
9
10
  removeClass,
@@ -12,6 +13,7 @@ import {
12
13
  Transition,
13
14
  } from 'uikit-util';
14
15
  import { getRows } from '../../core/margin';
16
+ import { awaitTimeout } from '../../util/await';
15
17
 
16
18
  const clsLeave = 'uk-transition-leave';
17
19
  const clsEnter = 'uk-transition-enter';
@@ -28,12 +30,21 @@ export default function fade(action, target, duration, stagger = 0) {
28
30
  addClass(target, clsLeave);
29
31
 
30
32
  await (stagger
31
- ? Promise.all(
32
- getTransitionNodes(target).map(async (child, i) => {
33
- await awaitTimeout(i * stagger);
34
- return Transition.start(child, propsOut, duration / 2, 'ease');
35
- }),
36
- )
33
+ ? getTransitionNodes(target).reduce(async (promise, child, i, array) => {
34
+ await promise;
35
+
36
+ if (!isInView(child) || !isCurrentIndex()) {
37
+ css(child, propsOut);
38
+ return;
39
+ }
40
+
41
+ await awaitTimeout(stagger);
42
+
43
+ const transition = Transition.start(child, propsOut, duration / 2, 'ease');
44
+ if (array.length - 1 === i) {
45
+ await transition;
46
+ }
47
+ }, Promise.resolve())
37
48
  : Transition.start(target, propsOut, duration / 2, 'ease'));
38
49
 
39
50
  removeClass(target, clsLeave);
@@ -64,13 +75,23 @@ export default function fade(action, target, duration, stagger = 0) {
64
75
  const nodes = getTransitionNodes(target);
65
76
  css(children(target), propsOut);
66
77
 
67
- transitions = nodes.map(async (child, i) => {
68
- await awaitTimeout(i * stagger);
69
- await Transition.start(child, propsIn, duration / 2, 'ease');
70
- if (isCurrentIndex()) {
78
+ transitions = nodes.reduce(async (promise, child, i, array) => {
79
+ await promise;
80
+
81
+ if (!isInView(child) || !isCurrentIndex()) {
71
82
  resetProps(child, propsIn);
83
+ return;
72
84
  }
73
- });
85
+
86
+ await awaitTimeout(stagger);
87
+
88
+ const transition = Transition.start(child, propsIn, duration / 2, 'ease').then(
89
+ () => isCurrentIndex() && resetProps(child, propsIn),
90
+ );
91
+ if (array.length - 1 === i) {
92
+ await transition;
93
+ }
94
+ }, Promise.resolve());
74
95
 
75
96
  targetDuration += nodes.length * stagger;
76
97
  }
@@ -118,7 +139,3 @@ function waitTransitionend(target) {
118
139
  function getTransitionNodes(target) {
119
140
  return getRows(children(target)).flat().filter(isVisible);
120
141
  }
121
-
122
- function awaitTimeout(timeout) {
123
- return new Promise((resolve) => setTimeout(resolve, timeout));
124
- }