uikit 3.25.29-dev.3bc1304 → 3.25.29-dev.a0b5ca8

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 (58) hide show
  1. package/CHANGELOG.md +20 -0
  2. package/dist/css/uikit-core-rtl.css +1 -1
  3. package/dist/css/uikit-core-rtl.min.css +1 -1
  4. package/dist/css/uikit-core.css +1 -1
  5. package/dist/css/uikit-core.min.css +1 -1
  6. package/dist/css/uikit-rtl.css +1 -1
  7. package/dist/css/uikit-rtl.min.css +1 -1
  8. package/dist/css/uikit.css +1 -1
  9. package/dist/css/uikit.min.css +1 -1
  10. package/dist/js/components/countdown.js +1 -1
  11. package/dist/js/components/countdown.min.js +1 -1
  12. package/dist/js/components/filter.js +2 -2
  13. package/dist/js/components/filter.min.js +1 -1
  14. package/dist/js/components/lightbox-panel.js +16 -12
  15. package/dist/js/components/lightbox-panel.min.js +1 -1
  16. package/dist/js/components/lightbox.js +16 -12
  17. package/dist/js/components/lightbox.min.js +1 -1
  18. package/dist/js/components/notification.js +1 -1
  19. package/dist/js/components/notification.min.js +1 -1
  20. package/dist/js/components/parallax.js +11 -15
  21. package/dist/js/components/parallax.min.js +1 -1
  22. package/dist/js/components/slider-parallax.js +11 -15
  23. package/dist/js/components/slider-parallax.min.js +1 -1
  24. package/dist/js/components/slider.js +11 -15
  25. package/dist/js/components/slider.min.js +1 -1
  26. package/dist/js/components/slideshow-parallax.js +11 -15
  27. package/dist/js/components/slideshow-parallax.min.js +1 -1
  28. package/dist/js/components/slideshow.js +11 -15
  29. package/dist/js/components/slideshow.min.js +1 -1
  30. package/dist/js/components/sortable.js +2 -2
  31. package/dist/js/components/sortable.min.js +1 -1
  32. package/dist/js/components/tooltip.js +1 -1
  33. package/dist/js/components/tooltip.min.js +1 -1
  34. package/dist/js/components/upload.js +1 -1
  35. package/dist/js/components/upload.min.js +1 -1
  36. package/dist/js/uikit-core.js +63 -52
  37. package/dist/js/uikit-core.min.js +1 -1
  38. package/dist/js/uikit-icons.js +1 -1
  39. package/dist/js/uikit-icons.min.js +1 -1
  40. package/dist/js/uikit.js +66 -55
  41. package/dist/js/uikit.min.js +1 -1
  42. package/package.json +1 -1
  43. package/src/js/components/lightbox-panel.js +3 -3
  44. package/src/js/core/dropnav.js +1 -1
  45. package/src/js/core/grid.js +1 -1
  46. package/src/js/core/icon.js +3 -1
  47. package/src/js/core/img.js +3 -3
  48. package/src/js/core/inverse.js +4 -2
  49. package/src/js/core/margin.js +1 -1
  50. package/src/js/core/modal.js +1 -1
  51. package/src/js/core/overflow-fade.js +6 -2
  52. package/src/js/core/svg.js +5 -1
  53. package/src/js/core/switcher.js +1 -0
  54. package/src/js/util/filter.js +6 -1
  55. package/src/js/util/lang.js +8 -33
  56. package/src/js/util/scroll.js +14 -9
  57. package/src/js/util/svg.js +7 -6
  58. package/tests/overflow-fade.test.mjs +69 -0
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.25.29-dev.3bc1304",
5
+ "version": "3.25.29-dev.a0b5ca8",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -344,7 +344,7 @@ export default {
344
344
  try {
345
345
  const { height, width } = await (
346
346
  await fetch(
347
- `https://vimeo.com/api/oembed.json?maxwidth=1920&url=${encodeURI(
347
+ `https://vimeo.com/api/oembed.json?maxwidth=1920&url=${encodeURIComponent(
348
348
  src,
349
349
  )}`,
350
350
  { credentials: 'omit' },
@@ -463,9 +463,9 @@ function toThumbnavItem(item, videoAutoplay) {
463
463
  }
464
464
 
465
465
  function isImage(src) {
466
- return src?.match(/\.(avif|jpe?g|jfif|a?png|gif|svg|webp)($|\?)/i);
466
+ return src?.match(/\.(avif|jpe?g|jfif|a?png|gif|svg|webp)($|[?#])/i);
467
467
  }
468
468
 
469
469
  function isVideo(src) {
470
- return src?.match(/\.(mp4|webm|ogv)($|\?)/i);
470
+ return src?.match(/\.(mp4|webm|ogv)($|[?#])/i);
471
471
  }
@@ -210,7 +210,7 @@ export default {
210
210
  elements,
211
211
  findIndex(elements, (el) => matches(el, ':focus')),
212
212
  )
213
- ].focus();
213
+ ]?.focus();
214
214
  return;
215
215
  }
216
216
 
@@ -132,7 +132,7 @@ export default {
132
132
  },
133
133
 
134
134
  write({ columns, scrolled, scrollColumns, translates }) {
135
- if (!scrolled && !translates) {
135
+ if (scrolled === false && !translates) {
136
136
  return;
137
137
  }
138
138
 
@@ -251,7 +251,9 @@ function install(UIkit) {
251
251
  if (UIkit._initialized) {
252
252
  apply(document.body, (el) =>
253
253
  each(UIkit.getComponents(el), (cmp) => {
254
- cmp.$options.isIcon && cmp.icon in added && cmp.$reset();
254
+ cmp.$options.isIcon &&
255
+ (aliases[cmp.icon] || cmp.icon) in added &&
256
+ cmp.$reset();
255
257
  }),
256
258
  );
257
259
  }
@@ -7,7 +7,6 @@ import {
7
7
  data,
8
8
  escape,
9
9
  fragment,
10
- includes,
11
10
  isArray,
12
11
  isEmpty,
13
12
  isTag,
@@ -88,8 +87,9 @@ function setSrcAttrs(el, src) {
88
87
  const elements = isTag(parentNode, 'picture') ? children(parentNode) : [el];
89
88
  elements.forEach((el) => setSourceProps(el, el));
90
89
  } else if (src) {
91
- if (!includes(el.style.backgroundImage, src)) {
92
- css(el, 'backgroundImage', `url(${escape(src)})`);
90
+ const previousBackground = el.style.backgroundImage;
91
+ css(el, 'backgroundImage', `url(${escape(src)})`);
92
+ if (el.style.backgroundImage !== previousBackground) {
93
93
  trigger(el, createEvent('load', false));
94
94
  }
95
95
  }
@@ -64,6 +64,7 @@ export default {
64
64
  [...toNodes(target), document.documentElement],
65
65
  handler,
66
66
  );
67
+ const transitionObserver = observeResize([], handler);
67
68
  const listener = [
68
69
  on(document, 'scroll itemshown itemhidden', handler, {
69
70
  passive: true,
@@ -71,11 +72,11 @@ export default {
71
72
  }),
72
73
  on(document, 'show hide transitionstart', (e) => {
73
74
  handler();
74
- return observer.observe(e.target);
75
+ return transitionObserver.observe(e.target);
75
76
  }),
76
77
  on(document, 'shown hidden transitionend transitioncancel', (e) => {
77
78
  handler();
78
- return observer.unobserve(e.target);
79
+ return transitionObserver.unobserve(e.target);
79
80
  }),
80
81
  ];
81
82
 
@@ -84,6 +85,7 @@ export default {
84
85
  unobserve: observer.unobserve.bind(observer),
85
86
  disconnect() {
86
87
  observer.disconnect();
88
+ transitionObserver.disconnect();
87
89
  listener.forEach((off) => off());
88
90
  },
89
91
  };
@@ -85,7 +85,7 @@ export function getRows(elements) {
85
85
  const offsetCurrent = getOffset(current[0], withOffset);
86
86
 
87
87
  if (offset.top >= offsetCurrent.bottom - 1 && offset.top !== offsetCurrent.top) {
88
- sorted.push([el]);
88
+ sorted.splice(i + 1, 0, [el]);
89
89
  break;
90
90
  }
91
91
 
@@ -164,7 +164,7 @@ function install({ modal }) {
164
164
 
165
165
  return assign(
166
166
  new Promise((resolve) => {
167
- const off = on(dialog.$el, 'hide', () => resolve(hideFn()));
167
+ const off = on(dialog.$el, 'hide', () => resolve(hideFn()), { self: true });
168
168
 
169
169
  on(dialog.$el, 'submit', 'form', (e) => {
170
170
  e.preventDefault();
@@ -5,8 +5,10 @@ import {
5
5
  getEventPos,
6
6
  hasClass,
7
7
  isInput,
8
+ isRtl,
8
9
  isTouch,
9
10
  on,
11
+ once,
10
12
  pointerCancel,
11
13
  pointerDown,
12
14
  pointerMove,
@@ -73,7 +75,9 @@ export default {
73
75
 
74
76
  if (!overflow[i - 1]) {
75
77
  const dir = i ? 'Top' : 'Left';
76
- const percent = overflow[i] ? this.$el[`scroll${dir}`] / overflow[i] : 0;
78
+ // The horizontal mask runs left to right, even in RTL.
79
+ const position = this.$el[`scroll${dir}`] + (!i && isRtl ? overflow[i] : 0);
80
+ const percent = overflow[i] ? position / overflow[i] : 0;
77
81
 
78
82
  const toValue = (value) =>
79
83
  overflow[i] ? clamp((this.fadeDuration - value) / this.fadeDuration) : 1;
@@ -110,7 +114,7 @@ function handleMouseDrag(e) {
110
114
  let started;
111
115
 
112
116
  const off = on(document, pointerMove, move(e), pointerOptions);
113
- on(document, [pointerUp, pointerCancel], end, { capture: true, once: true });
117
+ once(document, [pointerUp, pointerCancel], end, true);
114
118
 
115
119
  function move(e) {
116
120
  let origin = getEventPos(e);
@@ -51,7 +51,11 @@ export default {
51
51
  methods: {
52
52
  async getSvg() {
53
53
  if (isTag(this.$el, 'img') && !this.$el.complete && this.$el.loading === 'lazy') {
54
- await new Promise((resolve) => once(this.$el, 'load', resolve));
54
+ await new Promise((resolve, reject) =>
55
+ once(this.$el, 'load error', (event) =>
56
+ event.type === 'load' ? resolve() : reject(event),
57
+ ),
58
+ );
55
59
  }
56
60
 
57
61
  return parseSVG(await loadSVG(this.src), this.icon) || Promise.reject('SVG not found.');
@@ -108,6 +108,7 @@ export default {
108
108
 
109
109
  handler(e) {
110
110
  if (
111
+ !e.defaultPrevented &&
111
112
  !matches(e.current, selDisabled) &&
112
113
  (e.type === 'click' || e.keyCode === keyMap.SPACE)
113
114
  ) {
@@ -83,7 +83,12 @@ export function isSameSiteAnchor(el) {
83
83
  export function getTargetedElement(el) {
84
84
  if (isSameSiteAnchor(el)) {
85
85
  const { hash, ownerDocument } = toNode(el);
86
- const id = decodeURIComponent(hash).slice(1);
86
+ let id = hash.slice(1);
87
+ try {
88
+ id = decodeURIComponent(id);
89
+ } catch {
90
+ // Fall back to the literal fragment for malformed encoding.
91
+ }
87
92
  return id
88
93
  ? ownerDocument.getElementById(id) || ownerDocument.getElementsByName(id)[0]
89
94
  : ownerDocument.documentElement;
@@ -211,44 +211,19 @@ export function pointInRect(point, rect) {
211
211
  );
212
212
  }
213
213
 
214
- function ratio(dimensions, prop, value) {
215
- const aProp = prop === 'width' ? 'height' : 'width';
214
+ function cover(dimensions, maxDimensions) {
215
+ const scale = Math.max(
216
+ dimensions.width ? maxDimensions.width / dimensions.width : 0,
217
+ dimensions.height ? maxDimensions.height / dimensions.height : 0,
218
+ );
216
219
 
217
220
  return {
218
- [aProp]: dimensions[prop]
219
- ? Math.round((value * dimensions[aProp]) / dimensions[prop])
220
- : dimensions[aProp],
221
- [prop]: value,
221
+ width: Math.round(dimensions.width * scale),
222
+ height: Math.round(dimensions.height * scale),
222
223
  };
223
224
  }
224
225
 
225
- function contain(dimensions, maxDimensions) {
226
- dimensions = { ...dimensions };
227
-
228
- for (const prop in dimensions) {
229
- dimensions =
230
- dimensions[prop] > maxDimensions[prop]
231
- ? ratio(dimensions, prop, maxDimensions[prop])
232
- : dimensions;
233
- }
234
-
235
- return dimensions;
236
- }
237
-
238
- function cover(dimensions, maxDimensions) {
239
- dimensions = contain(dimensions, maxDimensions);
240
-
241
- for (const prop in dimensions) {
242
- dimensions =
243
- dimensions[prop] < maxDimensions[prop]
244
- ? ratio(dimensions, prop, maxDimensions[prop])
245
- : dimensions;
246
- }
247
-
248
- return dimensions;
249
- }
250
-
251
- export const Dimensions = { ratio, contain, cover };
226
+ export const Dimensions = { cover };
252
227
 
253
228
  export function getIndex(i, elements, current = 0, finite = false) {
254
229
  elements = toNodes(elements);
@@ -1,6 +1,6 @@
1
1
  import { css, getEventPos, matches, on, once, resetProps, scrollParents, width } from 'uikit-util';
2
2
 
3
- let prevented;
3
+ let scrollLocks = 0;
4
4
  export function preventBackgroundScroll(el) {
5
5
  // 'overscroll-behavior: contain' only works consistently if el overflows (Safari)
6
6
  const off = on(
@@ -44,21 +44,26 @@ export function preventBackgroundScroll(el) {
44
44
  { passive: true },
45
45
  );
46
46
 
47
- if (prevented) {
48
- return off;
49
- }
50
- prevented = true;
51
-
52
47
  const { scrollingElement } = document;
53
48
  const props = {
54
49
  overflowY: CSS.supports('overflow', 'clip') ? 'clip' : 'hidden',
55
50
  touchAction: 'none',
56
51
  scrollbarGutter: width(window) - scrollingElement.clientWidth ? 'stable' : '',
57
52
  };
58
- css(scrollingElement, props);
53
+
54
+ if (!scrollLocks++) {
55
+ css(scrollingElement, props);
56
+ }
57
+
58
+ let released = false;
59
59
  return () => {
60
- prevented = false;
60
+ if (released) {
61
+ return;
62
+ }
63
+ released = true;
61
64
  off();
62
- resetProps(scrollingElement, props);
65
+ if (!--scrollLocks) {
66
+ resetProps(scrollingElement, props);
67
+ }
63
68
  };
64
69
  }
@@ -1,10 +1,11 @@
1
1
  import { $$ } from './dom';
2
- import { isVisible } from './filter.js';
3
2
 
4
3
  export function getMaxPathLength(el) {
5
- return isVisible(el)
6
- ? Math.ceil(
7
- Math.max(0, ...$$('[stroke]', el).map((stroke) => stroke.getTotalLength?.() || 0)),
8
- )
9
- : 0;
4
+ try {
5
+ return Math.ceil(
6
+ Math.max(0, ...$$('[stroke]', el).map((stroke) => stroke.getTotalLength?.() || 0)),
7
+ );
8
+ } catch {
9
+ return 0;
10
+ }
10
11
  }
@@ -0,0 +1,69 @@
1
+ import assert from 'node:assert/strict';
2
+ import { readFile } from 'node:fs/promises';
3
+ import test from 'node:test';
4
+ import vm from 'node:vm';
5
+
6
+ const source = await readFile(new URL('../src/js/core/overflow-fade.js', import.meta.url), 'utf8');
7
+ function loadComponent(direction) {
8
+ return vm.runInNewContext(
9
+ source.replace(/^import[\s\S]*?from '[^']+';\s*/gm, '').replace('export default', 'component ='),
10
+ {
11
+ clamp: (value) => Math.min(1, Math.max(0, value)),
12
+ isRtl: direction === 'rtl',
13
+ css: (el, value) => (typeof value === 'string' ? el[value] : Object.assign(el.style, value)),
14
+ mutation: () => ({}),
15
+ resize: () => ({}),
16
+ toggleClass: () => {},
17
+ pointerDown: 'pointerdown',
18
+ },
19
+ );
20
+ }
21
+
22
+ function getOpacities(direction, scrollLeft, scrollTop = 0, horizontal = 200, vertical = 0) {
23
+ const component = loadComponent(direction);
24
+ const context = {
25
+ $el: {
26
+ direction,
27
+ scrollLeft,
28
+ scrollTop,
29
+ scrollWidth: 100 + horizontal,
30
+ clientWidth: 100,
31
+ scrollHeight: 100 + vertical,
32
+ clientHeight: 100,
33
+ style: {},
34
+ },
35
+ $options: { id: 'uk-overflow-fade' },
36
+ ...component.data,
37
+ };
38
+ component.update.write.call(context, component.update.read.call(context));
39
+ return [
40
+ context.$el.style['--uk-overflow-fade-start-opacity'],
41
+ context.$el.style['--uk-overflow-fade-end-opacity'],
42
+ ];
43
+ }
44
+
45
+ test('RTL horizontal fades follow physical left and right edges', () => {
46
+ assert.deepEqual(getOpacities('rtl', 0), [0, 1]);
47
+ assert.deepEqual(getOpacities('rtl', -100), [0, 0]);
48
+ assert.deepEqual(getOpacities('rtl', -200), [1, 0]);
49
+ });
50
+
51
+ test('LTR horizontal fades are unchanged', () => {
52
+ assert.deepEqual(getOpacities('ltr', 0), [1, 0]);
53
+ assert.deepEqual(getOpacities('ltr', 100), [0, 0]);
54
+ assert.deepEqual(getOpacities('ltr', 200), [0, 1]);
55
+ });
56
+
57
+ test('vertical fades are independent of direction', () => {
58
+ for (const direction of ['ltr', 'rtl']) {
59
+ assert.deepEqual(getOpacities(direction, 0, 0, 0, 200), [1, 0]);
60
+ assert.deepEqual(getOpacities(direction, 0, 100, 0, 200), [0, 0]);
61
+ assert.deepEqual(getOpacities(direction, 0, 200, 0, 200), [0, 1]);
62
+ }
63
+ });
64
+
65
+ test('elements without overflow remain opaque', () => {
66
+ for (const direction of ['ltr', 'rtl']) {
67
+ assert.deepEqual(getOpacities(direction, 0, 0, 0), [1, 1]);
68
+ }
69
+ });