uikit 3.16.27 → 3.17.1-dev.7c0766a6f

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 (119) hide show
  1. package/CHANGELOG.md +25 -0
  2. package/build/less.js +5 -4
  3. package/build/util.js +7 -9
  4. package/dist/css/uikit-core-rtl.css +53 -18
  5. package/dist/css/uikit-core-rtl.min.css +1 -1
  6. package/dist/css/uikit-core.css +53 -18
  7. package/dist/css/uikit-core.min.css +1 -1
  8. package/dist/css/uikit-rtl.css +56 -17
  9. package/dist/css/uikit-rtl.min.css +1 -1
  10. package/dist/css/uikit.css +56 -17
  11. package/dist/css/uikit.min.css +1 -1
  12. package/dist/js/components/countdown.js +1 -1
  13. package/dist/js/components/countdown.min.js +1 -1
  14. package/dist/js/components/filter.js +24 -43
  15. package/dist/js/components/filter.min.js +1 -1
  16. package/dist/js/components/lightbox-panel.js +2 -3
  17. package/dist/js/components/lightbox-panel.min.js +1 -1
  18. package/dist/js/components/lightbox.js +2 -3
  19. package/dist/js/components/lightbox.min.js +1 -1
  20. package/dist/js/components/notification.js +2 -2
  21. package/dist/js/components/notification.min.js +1 -1
  22. package/dist/js/components/parallax.js +7 -17
  23. package/dist/js/components/parallax.min.js +1 -1
  24. package/dist/js/components/slider-parallax.js +6 -13
  25. package/dist/js/components/slider-parallax.min.js +1 -1
  26. package/dist/js/components/slider.js +2 -2
  27. package/dist/js/components/slider.min.js +1 -1
  28. package/dist/js/components/slideshow-parallax.js +6 -13
  29. package/dist/js/components/slideshow-parallax.min.js +1 -1
  30. package/dist/js/components/slideshow.js +19 -4
  31. package/dist/js/components/slideshow.min.js +1 -1
  32. package/dist/js/components/sortable.js +24 -43
  33. package/dist/js/components/sortable.min.js +1 -1
  34. package/dist/js/components/tooltip.js +1 -2
  35. package/dist/js/components/tooltip.min.js +1 -1
  36. package/dist/js/components/upload.js +1 -1
  37. package/dist/js/components/upload.min.js +1 -1
  38. package/dist/js/uikit-core.js +455 -185
  39. package/dist/js/uikit-core.min.js +1 -1
  40. package/dist/js/uikit-icons.js +1 -1
  41. package/dist/js/uikit-icons.min.js +1 -1
  42. package/dist/js/uikit.js +483 -240
  43. package/dist/js/uikit.min.js +1 -9502
  44. package/package.json +1 -1
  45. package/src/js/api/boot.js +2 -2
  46. package/src/js/api/computed.js +1 -1
  47. package/src/js/api/observables.js +1 -4
  48. package/src/js/components/notification.js +1 -1
  49. package/src/js/components/slideshow.js +19 -2
  50. package/src/js/core/cover.js +30 -1
  51. package/src/js/core/drop.js +15 -9
  52. package/src/js/core/dropnav.js +16 -2
  53. package/src/js/core/grid.js +119 -64
  54. package/src/js/core/height-viewport.js +4 -1
  55. package/src/js/core/margin.js +23 -39
  56. package/src/js/core/navbar.js +219 -1
  57. package/src/js/core/scrollspy-nav.js +13 -4
  58. package/src/js/core/scrollspy.js +1 -1
  59. package/src/js/core/sticky.js +10 -11
  60. package/src/js/mixin/internal/animate-fade.js +1 -11
  61. package/src/js/mixin/internal/slideshow-animations.js +1 -1
  62. package/src/js/mixin/slider-reactive.js +1 -1
  63. package/src/js/mixin/togglable.js +0 -2
  64. package/src/js/util/animation.js +19 -13
  65. package/src/js/util/class.js +30 -26
  66. package/src/js/util/dom.js +3 -8
  67. package/src/js/util/filter.js +2 -6
  68. package/src/js/util/viewport.js +26 -4
  69. package/src/less/components/base.less +6 -3
  70. package/src/less/components/form.less +3 -0
  71. package/src/less/components/heading.less +18 -0
  72. package/src/less/components/navbar.less +6 -12
  73. package/src/less/components/pagination.less +13 -6
  74. package/src/less/components/position.less +1 -0
  75. package/src/less/components/section.less +6 -0
  76. package/src/less/theme/heading.less +4 -0
  77. package/src/less/theme/navbar.less +5 -0
  78. package/src/scss/components/base.scss +4 -2
  79. package/src/scss/components/form.scss +3 -0
  80. package/src/scss/components/heading.scss +13 -0
  81. package/src/scss/components/navbar.scss +5 -12
  82. package/src/scss/components/pagination.scss +13 -6
  83. package/src/scss/components/position.scss +1 -0
  84. package/src/scss/components/section.scss +4 -0
  85. package/src/scss/mixins-theme.scss +13 -1
  86. package/src/scss/mixins.scss +8 -1
  87. package/src/scss/theme/heading.scss +4 -0
  88. package/src/scss/variables-theme.scss +7 -0
  89. package/src/scss/variables.scss +7 -0
  90. package/tests/cover.html +2 -2
  91. package/tests/drop.html +13 -7
  92. package/tests/dropnav.html +22 -10
  93. package/tests/filter.html +5 -5
  94. package/tests/form.html +1 -1
  95. package/tests/grid-masonry.html +49 -73
  96. package/tests/grid-parallax.html +82 -40
  97. package/tests/grid.html +21 -3
  98. package/tests/heading.html +33 -4
  99. package/tests/height-viewport.html +3 -3
  100. package/tests/height.html +2 -2
  101. package/tests/icon.html +1 -1
  102. package/tests/image.html +6 -6
  103. package/tests/leader.html +3 -3
  104. package/tests/nav.html +1 -1
  105. package/tests/navbar.html +28 -10
  106. package/tests/notification.html +1 -1
  107. package/tests/offcanvas.html +2 -2
  108. package/tests/parallax.html +1 -1
  109. package/tests/scrollspy.html +3 -3
  110. package/tests/sortable.html +3 -3
  111. package/tests/sticky-navbar.html +233 -14
  112. package/tests/sticky.html +8 -8
  113. package/tests/svg.html +1 -1
  114. package/tests/tab.html +1 -1
  115. package/tests/toggle.html +2 -2
  116. package/tests/tooltip.html +2 -2
  117. package/tests/upload.html +16 -16
  118. package/tests/utility.html +2 -2
  119. package/tests/video.html +3 -3
@@ -1,13 +1,44 @@
1
- import { $$, css, hasClass } from 'uikit-util';
1
+ import {
2
+ $$,
3
+ addClass,
4
+ closest,
5
+ css,
6
+ hasClass,
7
+ observeResize,
8
+ offset,
9
+ on,
10
+ parent,
11
+ pointInRect,
12
+ removeClass,
13
+ replaceClass,
14
+ scrollParent,
15
+ } from 'uikit-util';
16
+ import { intersection, mutation } from '../api/observables';
2
17
  import Dropnav from './dropnav';
3
18
 
4
19
  export default {
5
20
  extends: Dropnav,
6
21
 
22
+ props: {
23
+ dropbarTransparentMode: Boolean,
24
+ },
25
+
7
26
  data: {
8
27
  clsDrop: 'uk-navbar-dropdown',
9
28
  selNavItem:
10
29
  '.uk-navbar-nav > li > a,a.uk-navbar-item,button.uk-navbar-item,.uk-navbar-item a,.uk-navbar-item button,.uk-navbar-toggle', // Simplify with :where() selector once browser target is Safari 14+
30
+ selTransparentTarget: '[class*="uk-section"]',
31
+ dropbarTransparentMode: false,
32
+ },
33
+
34
+ computed: {
35
+ dropbarOffset() {
36
+ return this.dropbarTransparentMode === 'behind' ? this.$el.offsetHeight : 0;
37
+ },
38
+
39
+ navbarContainer() {
40
+ return closest(this.$el, '.uk-navbar-container');
41
+ },
11
42
  },
12
43
 
13
44
  watch: {
@@ -30,4 +61,191 @@ export default {
30
61
  }
31
62
  },
32
63
  },
64
+
65
+ disconnect() {
66
+ this._colorListener?.();
67
+ },
68
+
69
+ observe: [
70
+ mutation({
71
+ target: ({ navbarContainer }) => navbarContainer,
72
+ handler() {
73
+ this.registerColorListener();
74
+ },
75
+ options: { attributes: true, attributeFilter: ['class'], attributeOldValue: true },
76
+ }),
77
+ intersection({
78
+ handler(records) {
79
+ this._isIntersecting = records[0].isIntersecting;
80
+ this.registerColorListener();
81
+ },
82
+ args: { intersecting: false },
83
+ }),
84
+ ],
85
+
86
+ events: [
87
+ {
88
+ name: 'show',
89
+
90
+ el() {
91
+ return this.dropContainer;
92
+ },
93
+
94
+ handler({ target }) {
95
+ const transparentMode = this.getTransparentMode(target);
96
+
97
+ if (!transparentMode || this._mode) {
98
+ return;
99
+ }
100
+
101
+ const storePrevColor = () =>
102
+ (this._mode = removeClasses(this.navbarContainer, 'uk-light', 'uk-dark'));
103
+
104
+ if (transparentMode === 'behind') {
105
+ const mode = getDropbarBehindColor(this.$el);
106
+ if (mode) {
107
+ storePrevColor();
108
+ addClass(this.navbarContainer, `uk-${mode}`);
109
+ }
110
+ }
111
+
112
+ if (transparentMode === 'remove') {
113
+ storePrevColor();
114
+ removeClass(this.navbarContainer, 'uk-navbar-transparent');
115
+ }
116
+ },
117
+ },
118
+ {
119
+ name: 'hide',
120
+
121
+ el() {
122
+ return this.dropContainer;
123
+ },
124
+
125
+ async handler({ target }) {
126
+ const transparentMode = this.getTransparentMode(target);
127
+
128
+ if (!transparentMode || !this._mode) {
129
+ return;
130
+ }
131
+
132
+ await awaitMacroTask();
133
+
134
+ if (this.getActive()) {
135
+ return;
136
+ }
137
+
138
+ if (transparentMode === 'behind') {
139
+ const mode = getDropbarBehindColor(this.$el);
140
+ if (mode) {
141
+ removeClass(this.navbarContainer, `uk-${mode}`);
142
+ }
143
+ }
144
+
145
+ addClass(this.navbarContainer, this._mode);
146
+
147
+ if (transparentMode === 'remove') {
148
+ addClass(this.navbarContainer, 'uk-navbar-transparent');
149
+ }
150
+
151
+ this._mode = null;
152
+ },
153
+ },
154
+ ],
155
+
156
+ methods: {
157
+ getTransparentMode(el) {
158
+ if (!this.navbarContainer) {
159
+ return;
160
+ }
161
+
162
+ if (this.dropbar && this.isDropbarDrop(el)) {
163
+ return this.dropbarTransparentMode;
164
+ }
165
+
166
+ const drop = this.getDropdown(el);
167
+
168
+ if (!drop || !hasClass(el, 'uk-dropbar')) {
169
+ return;
170
+ }
171
+
172
+ return drop.inset ? 'behind' : 'remove';
173
+ },
174
+
175
+ registerColorListener() {
176
+ const active =
177
+ this._isIntersecting &&
178
+ hasClass(this.navbarContainer, 'uk-navbar-transparent') &&
179
+ !isWithinMixBlendMode(this.navbarContainer) &&
180
+ !$$('.uk-drop', this.dropContainer)
181
+ .map(this.getDropdown)
182
+ .some(
183
+ (drop) =>
184
+ drop.isToggled() &&
185
+ (drop.inset || this.getTransparentMode(drop.$el) === 'behind'),
186
+ );
187
+
188
+ if (this._colorListener) {
189
+ if (!active) {
190
+ this._colorListener();
191
+ this._colorListener = null;
192
+ }
193
+ return;
194
+ }
195
+
196
+ if (!active) {
197
+ return;
198
+ }
199
+
200
+ this._colorListener = listenForPositionChange(this.navbarContainer, () => {
201
+ const { left, top, height } = offset(this.navbarContainer);
202
+ const startPoint = { x: left, y: Math.max(0, top) + height / 2 };
203
+ const target = $$(this.selTransparentTarget).find((target) =>
204
+ pointInRect(startPoint, offset(target)),
205
+ );
206
+ const color = css(target, '--uk-navbar-color');
207
+ if (color) {
208
+ replaceClass(this.navbarContainer, 'uk-light,uk-dark', `uk-${color}`);
209
+ }
210
+ });
211
+ },
212
+ },
33
213
  };
214
+
215
+ function removeClasses(el, ...classes) {
216
+ for (const cls of classes) {
217
+ if (hasClass(el, cls)) {
218
+ removeClass(el, cls);
219
+ return cls;
220
+ }
221
+ }
222
+ }
223
+
224
+ async function awaitMacroTask() {
225
+ return new Promise((resolve) => setTimeout(resolve));
226
+ }
227
+
228
+ function getDropbarBehindColor(el) {
229
+ return css(el, '--uk-navbar-dropbar-behind-color');
230
+ }
231
+
232
+ function listenForPositionChange(el, handler) {
233
+ const parent = scrollParent(el, true);
234
+ const scrollEl = parent === document.documentElement ? document : parent;
235
+
236
+ const off = on(scrollEl, 'scroll', handler, { passive: true });
237
+ const observer = observeResize([el, parent], handler);
238
+
239
+ return () => {
240
+ off();
241
+ observer.disconnect();
242
+ };
243
+ }
244
+
245
+ function isWithinMixBlendMode(el) {
246
+ do {
247
+ if (css(el, 'mixBlendMode') !== 'normal') {
248
+ return true;
249
+ }
250
+ } while ((el = parent(el)));
251
+ }
@@ -1,6 +1,7 @@
1
1
  import {
2
2
  $$,
3
3
  closest,
4
+ css,
4
5
  getTargetedElement,
5
6
  hasClass,
6
7
  isSameSiteAnchor,
@@ -16,7 +17,7 @@ import { intersection, scroll } from '../api/observables';
16
17
  export default {
17
18
  props: {
18
19
  cls: String,
19
- closest: String,
20
+ closest: Boolean,
20
21
  scroll: Boolean,
21
22
  overflow: Boolean,
22
23
  offset: Number,
@@ -36,14 +37,14 @@ export default {
36
37
  },
37
38
 
38
39
  elements({ closest: selector }) {
39
- return closest(this.links, selector || '*');
40
+ return this.links.map((el) => closest(el, selector || '*'));
40
41
  },
41
42
  },
42
43
 
43
44
  watch: {
44
45
  links(links) {
45
46
  if (this.scroll) {
46
- this.$create('scroll', links, { offset: this.offset || 0 });
47
+ this.$create('scroll', links, { offset: this.offset });
47
48
  }
48
49
  },
49
50
  },
@@ -71,7 +72,9 @@ export default {
71
72
  active = length - 1;
72
73
  } else {
73
74
  for (let i = 0; i < targets.length; i++) {
74
- if (offset(targets[i]).top - viewport.top - this.offset > 0) {
75
+ const fixedEl = findFixedElement(targets[i]);
76
+ const offsetBy = this.offset + (fixedEl ? offset(fixedEl).height : 0);
77
+ if (offset(targets[i]).top - viewport.top - offsetBy > 0) {
75
78
  break;
76
79
  }
77
80
  active = +i;
@@ -102,3 +105,9 @@ export default {
102
105
  },
103
106
  ],
104
107
  };
108
+
109
+ function findFixedElement(target) {
110
+ return target.ownerDocument
111
+ .elementsFromPoint(1, 1)
112
+ .find((el) => ['fixed', 'sticky'].includes(css(el, 'position')) && !el.contains(target));
113
+ }
@@ -80,7 +80,7 @@ export default {
80
80
 
81
81
  this.$emit();
82
82
  },
83
- options: (instance) => ({ rootMargin: instance.margin }),
83
+ options: ({ margin }) => ({ rootMargin: margin }),
84
84
  args: { intersecting: false },
85
85
  }),
86
86
 
@@ -8,6 +8,7 @@ import {
8
8
  css,
9
9
  height as getHeight,
10
10
  offset as getOffset,
11
+ hasClass,
11
12
  intersectRect,
12
13
  isNumeric,
13
14
  isString,
@@ -139,8 +140,6 @@ export default {
139
140
  {
140
141
  name: 'transitionstart',
141
142
 
142
- capture: true,
143
-
144
143
  handler() {
145
144
  this.transitionInProgress = once(
146
145
  this.$el,
@@ -177,7 +176,10 @@ export default {
177
176
 
178
177
  const viewport = toPx('100vh', 'height');
179
178
  const dynamicViewport = getHeight(window);
180
- const maxScrollHeight = document.scrollingElement.scrollHeight - viewport;
179
+ const maxScrollHeight = Math.max(
180
+ 0,
181
+ document.scrollingElement.scrollHeight - viewport,
182
+ );
181
183
 
182
184
  let position = this.position;
183
185
  if (this.overflowFlip && height > viewport) {
@@ -345,7 +347,9 @@ export default {
345
347
  }
346
348
 
347
349
  if (this.animation && scroll > topOffset) {
348
- Animation.cancel(this.$el);
350
+ if (hasClass(this.$el, 'uk-animation-leave')) {
351
+ return;
352
+ }
349
353
  Animation.out(this.$el, this.animation).then(() => this.hide(), noop);
350
354
  } else {
351
355
  this.hide();
@@ -353,7 +357,6 @@ export default {
353
357
  } else if (this.isFixed) {
354
358
  this.update();
355
359
  } else if (this.animation && scroll > topOffset) {
356
- Animation.cancel(this.$el);
357
360
  this.show();
358
361
  Animation.in(this.$el, this.animation).catch(noop);
359
362
  } else {
@@ -412,18 +415,14 @@ export default {
412
415
  let position = 'fixed';
413
416
 
414
417
  if (scroll > end) {
415
- offset += end - offsetParentTop;
418
+ offset += end - offsetParentTop + overflowScroll - overflow;
416
419
  position = 'absolute';
417
420
  }
418
421
 
419
422
  css(this.$el, { position, width, marginTop: 0 }, 'important');
420
423
  }
421
424
 
422
- if (overflow) {
423
- offset -= overflowScroll;
424
- }
425
-
426
- css(this.$el, 'top', offset);
425
+ css(this.$el, 'top', offset - overflowScroll);
427
426
 
428
427
  this.setActive(active);
429
428
  toggleClass(
@@ -7,7 +7,6 @@ import {
7
7
  isInView,
8
8
  once,
9
9
  removeClass,
10
- sortBy,
11
10
  toNumber,
12
11
  Transition,
13
12
  } from 'uikit-util';
@@ -118,16 +117,7 @@ function waitTransitionend(target) {
118
117
  }
119
118
 
120
119
  function getTransitionNodes(target) {
121
- return getRows(children(target)).reduce(
122
- (nodes, row) =>
123
- nodes.concat(
124
- sortBy(
125
- row.filter((el) => isInView(el)),
126
- 'offsetLeft',
127
- ),
128
- ),
129
- [],
130
- );
120
+ return getRows(children(target)).flat().filter(isInView);
131
121
  }
132
122
 
133
123
  function awaitFrame() {
@@ -20,7 +20,7 @@ export default {
20
20
  };
21
21
 
22
22
  export function translated(el) {
23
- return Math.abs(css(el, 'transform').split(',')[4] / el.offsetWidth) || 0;
23
+ return Math.abs(css(el, 'transform').split(',')[4] / el.offsetWidth);
24
24
  }
25
25
 
26
26
  export function translate(value = 0, unit = '%') {
@@ -5,7 +5,7 @@ export default {
5
5
  return;
6
6
  }
7
7
 
8
- const index = this.getValidIndex(this.index);
8
+ const index = this.getValidIndex();
9
9
 
10
10
  if (!~this.prevIndex || this.index !== index) {
11
11
  this.show(index);
@@ -247,8 +247,6 @@ export async function toggleTransition(
247
247
  }
248
248
 
249
249
  function toggleAnimation(el, show, cmp) {
250
- Animation.cancel(el);
251
-
252
250
  const { animation, duration, _toggle } = cmp;
253
251
 
254
252
  if (show) {
@@ -4,6 +4,10 @@ import { once, trigger } from './event';
4
4
  import { startsWith, toNodes } from './lang';
5
5
  import { css, propName } from './style';
6
6
 
7
+ const clsTransition = 'uk-transition';
8
+ const clsTransitionEnd = 'transitionend';
9
+ const clsTransitionCanceled = 'transitioncanceled';
10
+
7
11
  function transition(element, props, duration = 400, timing = 'linear') {
8
12
  duration = Math.round(duration);
9
13
  return Promise.all(
@@ -17,25 +21,25 @@ function transition(element, props, duration = 400, timing = 'linear') {
17
21
  }
18
22
  }
19
23
 
20
- const timer = setTimeout(() => trigger(element, 'transitionend'), duration);
24
+ const timer = setTimeout(() => trigger(element, clsTransitionEnd), duration);
21
25
 
22
26
  once(
23
27
  element,
24
- 'transitionend transitioncanceled',
28
+ [clsTransitionEnd, clsTransitionCanceled],
25
29
  ({ type }) => {
26
30
  clearTimeout(timer);
27
- removeClass(element, 'uk-transition');
31
+ removeClass(element, clsTransition);
28
32
  css(element, {
29
33
  transitionProperty: '',
30
34
  transitionDuration: '',
31
35
  transitionTimingFunction: '',
32
36
  });
33
- type === 'transitioncanceled' ? reject() : resolve(element);
37
+ type === clsTransitionCanceled ? reject() : resolve(element);
34
38
  },
35
39
  { self: true },
36
40
  );
37
41
 
38
- addClass(element, 'uk-transition');
42
+ addClass(element, clsTransition);
39
43
  css(element, {
40
44
  transitionProperty: Object.keys(props).map(propName).join(','),
41
45
  transitionDuration: `${duration}ms`,
@@ -51,37 +55,39 @@ export const Transition = {
51
55
  start: transition,
52
56
 
53
57
  async stop(element) {
54
- trigger(element, 'transitionend');
58
+ trigger(element, clsTransitionEnd);
55
59
  await Promise.resolve();
56
60
  },
57
61
 
58
62
  async cancel(element) {
59
- trigger(element, 'transitioncanceled');
63
+ trigger(element, clsTransitionCanceled);
60
64
  await Promise.resolve();
61
65
  },
62
66
 
63
67
  inProgress(element) {
64
- return hasClass(element, 'uk-transition');
68
+ return hasClass(element, clsTransition);
65
69
  },
66
70
  };
67
71
 
68
72
  const animationPrefix = 'uk-animation-';
73
+ const clsAnimationEnd = 'animationend';
74
+ const clsAnimationCanceled = 'animationcanceled';
69
75
 
70
76
  function animate(element, animation, duration = 200, origin, out) {
71
77
  return Promise.all(
72
78
  toNodes(element).map(
73
79
  (element) =>
74
80
  new Promise((resolve, reject) => {
75
- trigger(element, 'animationcanceled');
76
- const timer = setTimeout(() => trigger(element, 'animationend'), duration);
81
+ trigger(element, clsAnimationCanceled);
82
+ const timer = setTimeout(() => trigger(element, clsAnimationEnd), duration);
77
83
 
78
84
  once(
79
85
  element,
80
- 'animationend animationcanceled',
86
+ [clsAnimationEnd, clsAnimationCanceled],
81
87
  ({ type }) => {
82
88
  clearTimeout(timer);
83
89
 
84
- type === 'animationcanceled' ? reject() : resolve(element);
90
+ type === clsAnimationCanceled ? reject() : resolve(element);
85
91
 
86
92
  css(element, 'animationDuration', '');
87
93
  removeClasses(element, `${animationPrefix}\\S*`);
@@ -115,6 +121,6 @@ export const Animation = {
115
121
  },
116
122
 
117
123
  cancel(element) {
118
- trigger(element, 'animationcanceled');
124
+ trigger(element, clsAnimationCanceled);
119
125
  },
120
126
  };
@@ -1,32 +1,44 @@
1
- import { attr } from './attr';
2
- import { isUndefined, toNodes } from './lang';
1
+ import { includes, isArray, isUndefined, toArray, toNodes } from './lang';
3
2
 
4
- export function addClass(element, ...args) {
5
- apply(element, args, 'add');
3
+ export function addClass(element, ...classes) {
4
+ for (const node of toNodes(element)) {
5
+ const add = toClasses(classes).filter((cls) => !hasClass(node, cls));
6
+ if (add.length) {
7
+ node.classList.add(...add);
8
+ }
9
+ }
6
10
  }
7
11
 
8
- export function removeClass(element, ...args) {
9
- apply(element, args, 'remove');
12
+ export function removeClass(element, ...classes) {
13
+ for (const node of toNodes(element)) {
14
+ const remove = toClasses(classes).filter((cls) => hasClass(node, cls));
15
+ if (remove.length) {
16
+ node.classList.remove(...remove);
17
+ }
18
+ }
10
19
  }
11
20
 
12
- export function removeClasses(element, cls) {
13
- attr(element, 'class', (value) =>
14
- (value || '').replace(new RegExp(`\\b${cls}\\b\\s?`, 'g'), ''),
15
- );
21
+ export function removeClasses(element, clsRegex) {
22
+ clsRegex = new RegExp(clsRegex);
23
+ for (const node of toNodes(element)) {
24
+ node.classList.remove(...toArray(node.classList).filter((cls) => cls.match(clsRegex)));
25
+ }
16
26
  }
17
27
 
18
- export function replaceClass(element, ...args) {
19
- args[0] && removeClass(element, args[0]);
20
- args[1] && addClass(element, args[1]);
28
+ export function replaceClass(element, oldClass, newClass) {
29
+ newClass = toClasses(newClass);
30
+ oldClass = toClasses(oldClass).filter((cls) => !includes(newClass, cls));
31
+ removeClass(element, oldClass);
32
+ addClass(element, newClass);
21
33
  }
22
34
 
23
35
  export function hasClass(element, cls) {
24
- [cls] = getClasses(cls);
25
- return !!cls && toNodes(element).some((node) => node.classList.contains(cls));
36
+ [cls] = toClasses(cls);
37
+ return toNodes(element).some((node) => node.classList.contains(cls));
26
38
  }
27
39
 
28
40
  export function toggleClass(element, cls, force) {
29
- const classes = getClasses(cls);
41
+ const classes = toClasses(cls);
30
42
 
31
43
  if (!isUndefined(force)) {
32
44
  force = !!force;
@@ -39,14 +51,6 @@ export function toggleClass(element, cls, force) {
39
51
  }
40
52
  }
41
53
 
42
- function apply(element, args, fn) {
43
- args = args.reduce((args, arg) => args.concat(getClasses(arg)), []);
44
-
45
- for (const node of toNodes(element)) {
46
- node.classList[fn](...args);
47
- }
48
- }
49
-
50
- function getClasses(str) {
51
- return String(str).split(/[ ,]/).filter(Boolean);
54
+ function toClasses(str) {
55
+ return isArray(str) ? str.map(toClasses).flat() : String(str).split(/[ ,]/).filter(Boolean);
52
56
  }
@@ -72,7 +72,6 @@ export function unwrap(element) {
72
72
  .forEach((parent) => parent.replaceWith(...parent.childNodes));
73
73
  }
74
74
 
75
- const fragmentRe = /^\s*<(\w+|!)[^>]*>/;
76
75
  const singleTagRe = /^<(\w+)\s*\/?>(?:<\/\1>)?$/;
77
76
 
78
77
  export function fragment(html) {
@@ -81,14 +80,10 @@ export function fragment(html) {
81
80
  return document.createElement(matches[1]);
82
81
  }
83
82
 
84
- const container = document.createElement('div');
85
- if (fragmentRe.test(html)) {
86
- container.insertAdjacentHTML('beforeend', html.trim());
87
- } else {
88
- container.textContent = html;
89
- }
83
+ const container = document.createElement('template');
84
+ container.innerHTML = html.trim();
90
85
 
91
- return unwrapSingle(container.childNodes);
86
+ return unwrapSingle(container.content.childNodes);
92
87
  }
93
88
 
94
89
  function unwrapSingle(nodes) {
@@ -1,4 +1,4 @@
1
- import { isElement, isString, startsWith, toArray, toNode, toNodes } from './lang';
1
+ import { isString, startsWith, toArray, toNode, toNodes } from './lang';
2
2
 
3
3
  const voidElements = {
4
4
  area: true,
@@ -50,11 +50,7 @@ export function matches(element, selector) {
50
50
  }
51
51
 
52
52
  export function closest(element, selector) {
53
- return isElement(element)
54
- ? element.closest(startsWith(selector, '>') ? selector.slice(1) : selector)
55
- : toNodes(element)
56
- .map((element) => closest(element, selector))
57
- .filter(Boolean);
53
+ return toNode(element)?.closest(startsWith(selector, '>') ? selector.slice(1) : selector);
58
54
  }
59
55
 
60
56
  export function within(element, selector) {