uikit 3.14.2-dev.e270e98f7 → 3.14.3-dev.5325d42a0

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 (96) hide show
  1. package/CHANGELOG.md +30 -0
  2. package/build/util.js +8 -2
  3. package/dist/css/uikit-core-rtl.css +93 -21
  4. package/dist/css/uikit-core-rtl.min.css +1 -1
  5. package/dist/css/uikit-core.css +93 -21
  6. package/dist/css/uikit-core.min.css +1 -1
  7. package/dist/css/uikit-rtl.css +99 -33
  8. package/dist/css/uikit-rtl.min.css +1 -1
  9. package/dist/css/uikit.css +99 -33
  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 +1 -1
  14. package/dist/js/components/filter.min.js +1 -1
  15. package/dist/js/components/lightbox-panel.js +106 -17
  16. package/dist/js/components/lightbox-panel.min.js +1 -1
  17. package/dist/js/components/lightbox.js +106 -17
  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 +71 -36
  22. package/dist/js/components/parallax.min.js +1 -1
  23. package/dist/js/components/slider-parallax.js +55 -35
  24. package/dist/js/components/slider-parallax.min.js +1 -1
  25. package/dist/js/components/slider.js +1 -1
  26. package/dist/js/components/slider.min.js +1 -1
  27. package/dist/js/components/slideshow-parallax.js +55 -35
  28. package/dist/js/components/slideshow-parallax.min.js +1 -1
  29. package/dist/js/components/slideshow.js +1 -1
  30. package/dist/js/components/slideshow.min.js +1 -1
  31. package/dist/js/components/sortable.js +1 -1
  32. package/dist/js/components/sortable.min.js +1 -1
  33. package/dist/js/components/tooltip.js +142 -36
  34. package/dist/js/components/tooltip.min.js +1 -1
  35. package/dist/js/components/upload.js +1 -1
  36. package/dist/js/components/upload.min.js +1 -1
  37. package/dist/js/uikit-core.js +276 -129
  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 +346 -164
  42. package/dist/js/uikit.min.js +1 -1
  43. package/package.json +11 -11
  44. package/src/js/components/parallax.js +16 -1
  45. package/src/js/core/accordion.js +3 -3
  46. package/src/js/core/alert.js +1 -1
  47. package/src/js/core/drop.js +47 -18
  48. package/src/js/core/height-viewport.js +15 -11
  49. package/src/js/core/navbar.js +19 -18
  50. package/src/js/core/scrollspy.js +4 -0
  51. package/src/js/core/toggle.js +9 -8
  52. package/src/js/mixin/parallax.js +54 -34
  53. package/src/js/mixin/position.js +36 -20
  54. package/src/js/mixin/togglable.js +110 -19
  55. package/src/js/util/animation.js +1 -0
  56. package/src/js/util/dom.js +4 -3
  57. package/src/js/util/position.js +24 -22
  58. package/src/js/util/viewport.js +7 -8
  59. package/src/less/components/drop.less +19 -4
  60. package/src/less/components/dropdown.less +21 -4
  61. package/src/less/components/margin.less +13 -14
  62. package/src/less/components/modal.less +19 -4
  63. package/src/less/components/nav.less +1 -1
  64. package/src/less/components/navbar.less +60 -19
  65. package/src/less/components/offcanvas.less +21 -21
  66. package/src/less/components/position.less +1 -1
  67. package/src/less/components/sticky.less +7 -0
  68. package/src/less/components/tooltip.less +1 -0
  69. package/src/less/components/utility.less +1 -2
  70. package/src/less/theme/dropdown.less +11 -0
  71. package/src/less/theme/navbar.less +10 -10
  72. package/src/scss/components/drop.scss +19 -4
  73. package/src/scss/components/dropdown.scss +21 -4
  74. package/src/scss/components/margin.scss +13 -14
  75. package/src/scss/components/modal.scss +19 -4
  76. package/src/scss/components/nav.scss +1 -1
  77. package/src/scss/components/navbar.scss +49 -8
  78. package/src/scss/components/offcanvas.scss +21 -21
  79. package/src/scss/components/position.scss +1 -1
  80. package/src/scss/components/sticky.scss +7 -0
  81. package/src/scss/components/tooltip.scss +1 -0
  82. package/src/scss/components/utility.scss +1 -2
  83. package/src/scss/mixins-theme.scss +8 -10
  84. package/src/scss/mixins.scss +2 -0
  85. package/src/scss/theme/dropdown.scss +8 -0
  86. package/src/scss/theme/navbar.scss +7 -0
  87. package/src/scss/variables-theme.scss +26 -11
  88. package/src/scss/variables.scss +24 -11
  89. package/tests/drop.html +165 -4
  90. package/tests/dropdown.html +234 -13
  91. package/tests/height-viewport.html +62 -0
  92. package/tests/navbar.html +333 -64
  93. package/tests/offcanvas.html +8 -8
  94. package/tests/sticky-navbar.html +132 -0
  95. package/tests/sticky-parallax.html +2 -1
  96. package/tests/sticky.html +5 -4
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.14.2-dev.e270e98f7",
5
+ "version": "3.14.3-dev.5325d42a0",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -34,32 +34,32 @@
34
34
  },
35
35
  "homepage": "https://getuikit.com",
36
36
  "devDependencies": {
37
- "@babel/core": "^7.17.8",
38
- "@babel/preset-env": "^7.16.4",
37
+ "@babel/core": "^7.17.10",
38
+ "@babel/preset-env": "^7.17.10",
39
39
  "@rollup/plugin-alias": "^3.1.9",
40
40
  "@rollup/plugin-babel": "^5.3.1",
41
41
  "@rollup/plugin-replace": "^4.0.0",
42
- "archiver": "^5.3.0",
42
+ "archiver": "^5.3.1",
43
43
  "camelcase": "^6.3.0",
44
44
  "clean-css": "^5.3.0",
45
45
  "dateformat": "^5.0.3",
46
- "esbuild": "^0.14.32",
47
- "eslint": "^8.12.0",
46
+ "esbuild": "^0.14.38",
47
+ "eslint": "^8.15.0",
48
48
  "eslint-config-prettier": "^8.5.0",
49
- "fs-extra": "^10.0.1",
50
- "glob": "^7.2.0",
51
- "inquirer": "^8.2.2",
49
+ "fs-extra": "^10.1.0",
50
+ "glob": "^8.0.1",
51
+ "inquirer": "^8.2.4",
52
52
  "less": "^4.1.2",
53
53
  "minimist": "^1.2.6",
54
54
  "number-precision": "^1.5.2",
55
55
  "p-limit": "^4.0.0",
56
56
  "prettier": "^2.6.2",
57
- "rollup": "^2.70.1",
57
+ "rollup": "^2.72.1",
58
58
  "rollup-plugin-esbuild": "^4.9.1",
59
59
  "rollup-plugin-html": "^0.2.1",
60
60
  "rollup-plugin-modify": "^3.0.0",
61
61
  "rtlcss": "^3.5.0",
62
- "semver": "^7.3.2",
62
+ "semver": "^7.3.7",
63
63
  "svgo": "^2.8.0",
64
64
  "watch-run": "^1.2.5"
65
65
  }
@@ -73,8 +73,23 @@ export default {
73
73
  },
74
74
  };
75
75
 
76
+ /*
77
+ * Inspired by https://gist.github.com/gre/1650294?permalink_comment_id=3477425#gistcomment-3477425
78
+ *
79
+ * linear: 0
80
+ * easeInSine: 0.5
81
+ * easeOutSine: -0.5
82
+ * easeInQuad: 1
83
+ * easeOutQuad: -1
84
+ * easeInCubic: 2
85
+ * easeOutCubic: -2
86
+ * easeInQuart: 3
87
+ * easeOutQuart: -3
88
+ * easeInQuint: 4
89
+ * easeOutQuint: -4
90
+ */
76
91
  function ease(percent, easing) {
77
- return easing >= 0 ? Math.pow(percent, easing + 1) : 1 - Math.pow(1 - percent, -easing + 1);
92
+ return easing >= 0 ? Math.pow(percent, easing + 1) : 1 - Math.pow(1 - percent, 1 - easing);
78
93
  }
79
94
 
80
95
  // SVG elements do not inherit from HTMLElement
@@ -1,6 +1,6 @@
1
1
  import Class from '../mixin/class';
2
2
  import Lazyload from '../mixin/lazyload';
3
- import { default as Togglable, toggleHeight } from '../mixin/togglable';
3
+ import { slide, default as Togglable } from '../mixin/togglable';
4
4
  import {
5
5
  $,
6
6
  $$,
@@ -34,7 +34,7 @@ export default {
34
34
  data: {
35
35
  targets: '> *',
36
36
  active: false,
37
- animation: [true],
37
+ animation: ['slide'],
38
38
  collapsible: true,
39
39
  multiple: false,
40
40
  clsOpen: 'uk-open',
@@ -144,7 +144,7 @@ export default {
144
144
  }
145
145
 
146
146
  hide(content, false);
147
- await toggleHeight(this)(el._wrapper, show);
147
+ await slide(this)(el._wrapper, show);
148
148
  hide(content, !show);
149
149
 
150
150
  delete el._wrapper;
@@ -11,7 +11,7 @@ export default {
11
11
  },
12
12
 
13
13
  data: {
14
- animation: [true],
14
+ animation: ['slide'],
15
15
  selClose: '.uk-alert-close',
16
16
  duration: 150,
17
17
  hideProps: { opacity: 0, ...Togglable.data.hideProps },
@@ -47,6 +47,7 @@ export default {
47
47
  delayHide: Number,
48
48
  display: String,
49
49
  clsDrop: String,
50
+ animateOut: Boolean,
50
51
  },
51
52
 
52
53
  data: {
@@ -61,6 +62,7 @@ export default {
61
62
  animation: ['uk-animation-fade'],
62
63
  cls: 'uk-open',
63
64
  container: false,
65
+ animateOut: false,
64
66
  },
65
67
 
66
68
  created() {
@@ -248,7 +250,7 @@ export default {
248
250
  }
249
251
  }),
250
252
 
251
- ...(this.display === 'static'
253
+ ...(this.display === 'static' && this.align !== 'stretch'
252
254
  ? []
253
255
  : (() => {
254
256
  const handler = () => this.$emit();
@@ -309,7 +311,7 @@ export default {
309
311
  methods: {
310
312
  show(target = this.target, delay = true) {
311
313
  if (this.isToggled() && target && this.target && target !== this.target) {
312
- this.hide(false);
314
+ this.hide(false, false);
313
315
  }
314
316
 
315
317
  this.target = target;
@@ -329,7 +331,7 @@ export default {
329
331
  let prev;
330
332
  while (active && prev !== active && !within(this.$el, active.$el)) {
331
333
  prev = active;
332
- active.hide(false);
334
+ active.hide(false, false);
333
335
  }
334
336
  }
335
337
 
@@ -343,8 +345,8 @@ export default {
343
345
  );
344
346
  },
345
347
 
346
- hide(delay = true) {
347
- const hide = () => this.toggleElement(this.$el, false, false);
348
+ hide(delay = true, animate = true) {
349
+ const hide = () => this.toggleElement(this.$el, false, this.animateOut && animate);
348
350
 
349
351
  this.clearTimers();
350
352
 
@@ -376,32 +378,59 @@ export default {
376
378
  position() {
377
379
  removeClass(this.$el, `${this.clsDrop}-stack`);
378
380
  toggleClass(this.$el, `${this.clsDrop}-boundary`, this.boundaryAlign);
381
+ toggleClass(this.$el, `${this.clsDrop}-stretch`, this.align === 'stretch');
379
382
 
380
383
  const boundary = query(this.boundary, this.$el);
381
- const [scrollParent] = scrollParents(this.$el);
382
- const scrollParentOffset = offsetViewport(scrollParent);
384
+ const target = boundary && this.boundaryAlign ? boundary : this.target;
385
+ const [scrollParent] = scrollParents(
386
+ boundary && this.boundaryAlign ? boundary : this.$el
387
+ );
388
+ const scrollParentOffset = offset(scrollParent);
383
389
  const boundaryOffset = boundary ? offset(boundary) : scrollParentOffset;
390
+ const viewportOffset = this.getViewportOffset(this.$el);
384
391
 
385
392
  css(this.$el, 'maxWidth', '');
386
- const maxWidth =
387
- scrollParentOffset.width - (this.boundaryAlign ? 0 : 2 * this.viewportPadding);
393
+ const maxWidth = scrollParentOffset.width - 2 * viewportOffset;
388
394
 
389
- if (this.pos[1] === 'justify') {
395
+ if (this.align === 'justify') {
390
396
  const prop = this.axis === 'y' ? 'width' : 'height';
391
- const targetOffset = offset(this.target);
392
- const alignTo = this.boundaryAlign ? boundaryOffset : targetOffset;
393
- css(this.$el, prop, alignTo[prop]);
397
+ css(
398
+ this.$el,
399
+ prop,
400
+ Math.min(
401
+ (boundary ? boundaryOffset : offset(this.target))[prop],
402
+ scrollParentOffset[prop] - 2 * viewportOffset
403
+ )
404
+ );
405
+ } else if (this.align === 'stretch') {
406
+ this.flip = this.axis === 'y' ? 'x' : 'y';
407
+ this.display = 'static';
408
+
409
+ const viewport = offsetViewport(scrollParent);
410
+ const targetDim = offset(target);
411
+ const elOffset = Math.abs(this.getPositionOffset(this.$el)) + viewportOffset;
412
+
413
+ css(this.$el, {
414
+ width:
415
+ this.axis === 'y'
416
+ ? viewport.width
417
+ : (this.dir === 'left'
418
+ ? targetDim.left - viewport.left
419
+ : viewport.right - targetDim.right) - elOffset,
420
+ height:
421
+ this.axis === 'x'
422
+ ? viewport.height
423
+ : (this.dir === 'top'
424
+ ? targetDim.top - viewport.top
425
+ : viewport.bottom - targetDim.bottom) - elOffset,
426
+ });
394
427
  } else if (this.$el.offsetWidth > maxWidth) {
395
428
  addClass(this.$el, `${this.clsDrop}-stack`);
396
429
  }
397
430
 
398
431
  css(this.$el, 'maxWidth', maxWidth);
399
432
 
400
- this.positionAt(
401
- this.$el,
402
- boundary && this.boundaryAlign ? boundary : this.target,
403
- boundary
404
- );
433
+ this.positionAt(this.$el, target, boundary);
405
434
  },
406
435
  },
407
436
  };
@@ -1,21 +1,21 @@
1
- import Class from '../mixin/class';
2
1
  import Resize from '../mixin/resize';
3
2
  import {
4
3
  boxModelAdjust,
5
4
  css,
6
5
  dimensions,
7
6
  endsWith,
8
- height,
9
7
  isNumeric,
10
8
  isString,
11
9
  isVisible,
12
- offset,
10
+ offsetPosition,
11
+ offsetViewport,
13
12
  query,
13
+ scrollParents,
14
14
  toFloat,
15
15
  } from 'uikit-util';
16
16
 
17
17
  export default {
18
- mixins: [Class, Resize],
18
+ mixins: [Resize],
19
19
 
20
20
  props: {
21
21
  expand: Boolean,
@@ -33,7 +33,7 @@ export default {
33
33
 
34
34
  resizeTargets() {
35
35
  // check for offsetTop change
36
- return [this.$el, document.documentElement];
36
+ return [this.$el, ...scrollParents(this.$el, /auto|scroll/)];
37
37
  },
38
38
 
39
39
  update: {
@@ -45,21 +45,25 @@ export default {
45
45
  let minHeight = '';
46
46
  const box = boxModelAdjust(this.$el, 'height', 'content-box');
47
47
 
48
+ const [scrollElement] = scrollParents(this.$el, /auto|scroll/);
49
+ const { height: viewportHeight } = offsetViewport(scrollElement);
50
+
48
51
  if (this.expand) {
49
52
  minHeight = Math.max(
50
- height(window) -
51
- (dimensions(document.documentElement).height -
52
- dimensions(this.$el).height) -
53
+ viewportHeight -
54
+ (dimensions(scrollElement).height - dimensions(this.$el).height) -
53
55
  box,
54
56
  0
55
57
  );
56
58
  } else {
57
59
  // on mobile devices (iOS and Android) window.innerHeight !== 100vh
58
- minHeight = 'calc(100vh';
60
+ minHeight = `calc(${
61
+ document.scrollingElement === scrollElement ? '100vh' : `${viewportHeight}px`
62
+ }`;
59
63
 
60
64
  if (this.offsetTop) {
61
- const { top } = offset(this.$el);
62
- minHeight += top > 0 && top < height(window) / 2 ? ` - ${top}px` : '';
65
+ const top = offsetPosition(this.$el)[0] - offsetPosition(scrollElement)[0];
66
+ minHeight += top > 0 && top < viewportHeight / 2 ? ` - ${top}px` : '';
63
67
  }
64
68
 
65
69
  if (this.offsetBottom === true) {
@@ -13,9 +13,9 @@ import {
13
13
  height,
14
14
  includes,
15
15
  isRtl,
16
- isVisible,
17
16
  matches,
18
17
  noop,
18
+ observeResize,
19
19
  offset,
20
20
  once,
21
21
  parent,
@@ -285,8 +285,8 @@ export default {
285
285
  return this.dropbar;
286
286
  },
287
287
 
288
- handler(_, { $el }) {
289
- if (!hasClass($el, this.clsDrop)) {
288
+ handler(_, { $el, align }) {
289
+ if (!hasClass($el, this.clsDrop) || align === 'stretch') {
290
290
  return;
291
291
  }
292
292
 
@@ -309,19 +309,19 @@ export default {
309
309
  return this.dropbar;
310
310
  },
311
311
 
312
- handler(_, { $el, pos: [dir] = [] }) {
313
- if (!hasClass($el, this.clsDrop)) {
312
+ handler(_, { $el, align }) {
313
+ if (!hasClass($el, this.clsDrop) || align === 'stretch') {
314
314
  return;
315
315
  }
316
316
 
317
- if (dir === 'bottom') {
317
+ this._observer = observeResize($el, () =>
318
318
  this.transitionTo(
319
319
  offset($el).bottom -
320
320
  offset(this.dropbar).top +
321
321
  toFloat(css($el, 'marginBottom')),
322
322
  $el
323
- );
324
- }
323
+ )
324
+ );
325
325
  },
326
326
  },
327
327
 
@@ -360,11 +360,13 @@ export default {
360
360
  return this.dropbar;
361
361
  },
362
362
 
363
- handler(_, { $el }) {
364
- if (!hasClass($el, this.clsDrop)) {
363
+ handler(_, { $el, align }) {
364
+ if (!hasClass($el, this.clsDrop) || align === 'stretch') {
365
365
  return;
366
366
  }
367
367
 
368
+ this._observer.disconnect();
369
+
368
370
  const active = this.getActive();
369
371
 
370
372
  if (!active || active?.$el === $el) {
@@ -381,28 +383,27 @@ export default {
381
383
 
382
384
  transitionTo(newHeight, el) {
383
385
  const { dropbar } = this;
384
- const oldHeight = isVisible(dropbar) ? height(dropbar) : 0;
386
+ const oldHeight = height(dropbar);
385
387
 
386
388
  el = oldHeight < newHeight && el;
387
389
 
388
- css(el, 'clip', `rect(0,${el.offsetWidth}px,${oldHeight}px,0)`);
390
+ css(el, 'clipPath', `polygon(0 0,100% 0,100% ${oldHeight}px,0 ${oldHeight}px)`);
389
391
 
390
392
  height(dropbar, oldHeight);
391
393
 
392
394
  Transition.cancel([el, dropbar]);
393
- return Promise.all([
395
+ Promise.all([
394
396
  Transition.start(dropbar, { height: newHeight }, this.duration),
395
397
  Transition.start(
396
398
  el,
397
- { clip: `rect(0,${el.offsetWidth}px,${newHeight}px,0)` },
399
+ {
400
+ clipPath: `polygon(0 0,100% 0,100% ${newHeight}px,0 ${newHeight}px)`,
401
+ },
398
402
  this.duration
399
403
  ),
400
404
  ])
401
405
  .catch(noop)
402
- .then(() => {
403
- css(el, { clip: '' });
404
- this.$update(dropbar);
405
- });
406
+ .then(() => css(el, { clipPath: '' }));
406
407
  },
407
408
 
408
409
  getDropdown(el) {
@@ -128,6 +128,10 @@ export default {
128
128
  toggle(el, inview) {
129
129
  const state = this._data.elements.get(el);
130
130
 
131
+ if (!state) {
132
+ return;
133
+ }
134
+
131
135
  state.off?.();
132
136
 
133
137
  css(el, 'visibility', !inview && this.hidden ? 'hidden' : '');
@@ -77,6 +77,8 @@ export default {
77
77
  },
78
78
 
79
79
  handler(e) {
80
+ this._preventClick = null;
81
+
80
82
  if (!isTouch(e) || this._showState) {
81
83
  return;
82
84
  }
@@ -156,9 +158,14 @@ export default {
156
158
  {
157
159
  name: 'click',
158
160
 
161
+ filter() {
162
+ return ['click', 'hover'].some((mode) => includes(this.mode, mode));
163
+ },
164
+
159
165
  handler(e) {
160
166
  let link;
161
167
  if (
168
+ this._preventClick ||
162
169
  closest(e.target, 'a[href="#"], a[href=""]') ||
163
170
  ((link = closest(e.target, 'a[href]')) &&
164
171
  (attr(this.$el, 'aria-expanded') !== 'true' ||
@@ -167,15 +174,9 @@ export default {
167
174
  e.preventDefault();
168
175
  }
169
176
 
170
- if (this._preventClick) {
171
- return (this._preventClick = null);
172
- }
173
-
174
- if (!includes(this.mode, 'click')) {
175
- return;
177
+ if (!this._preventClick && includes(this.mode, 'click')) {
178
+ this.toggle();
176
179
  }
177
-
178
- this.toggle();
179
180
  },
180
181
  },
181
182
 
@@ -47,12 +47,17 @@ export default {
47
47
 
48
48
  computed: {
49
49
  props(properties, $el) {
50
- return keys(props).reduce((result, prop) => {
51
- if (!isUndefined(properties[prop])) {
52
- result[prop] = props[prop](prop, $el, properties[prop].slice());
50
+ const stops = {};
51
+ for (const prop in properties) {
52
+ if (prop in props && !isUndefined(properties[prop])) {
53
+ stops[prop] = properties[prop].slice();
53
54
  }
54
- return result;
55
- }, {});
55
+ }
56
+ const result = {};
57
+ for (const prop in stops) {
58
+ result[prop] = props[prop](prop, $el, stops[prop], stops);
59
+ }
60
+ return result;
56
61
  },
57
62
  },
58
63
 
@@ -181,58 +186,66 @@ function strokeFn(prop, el, stops) {
181
186
  };
182
187
  }
183
188
 
184
- function backgroundFn(prop, el, stops) {
189
+ function backgroundFn(prop, el, stops, props) {
185
190
  if (stops.length === 1) {
186
191
  stops.unshift(0);
187
192
  }
188
193
 
189
- prop = prop.substr(-1);
190
- const attr = prop === 'y' ? 'height' : 'width';
191
- stops = parseStops(stops, (stop) => toPx(stop, attr, el));
194
+ const attr = prop === 'bgy' ? 'height' : 'width';
195
+ props[prop] = parseStops(stops, (stop) => toPx(stop, attr, el));
196
+
197
+ const bgProps = ['bgx', 'bgy'].filter((prop) => prop in props);
198
+ if (bgProps.length === 2 && prop === 'bgx') {
199
+ return noop;
200
+ }
201
+
202
+ if (getCssValue(el, 'backgroundSize', '') === 'cover') {
203
+ return backgroundCoverFn(prop, el, stops, props);
204
+ }
192
205
 
193
- const bgPos = getCssValue(el, `background-position-${prop}`, '');
206
+ const positions = {};
207
+ for (const prop of bgProps) {
208
+ positions[prop] = getBackgroundPos(el, prop);
209
+ }
194
210
 
195
- return getCssValue(el, 'backgroundSize', '') === 'cover'
196
- ? backgroundCoverFn(prop, el, stops, bgPos, attr)
197
- : setBackgroundPosFn(prop, stops, bgPos);
211
+ return setBackgroundPosFn(bgProps, props, positions);
198
212
  }
199
213
 
200
- function backgroundCoverFn(prop, el, stops, bgPos, attr) {
214
+ function backgroundCoverFn(prop, el, stops, props) {
201
215
  const dimImage = getBackgroundImageDimensions(el);
202
216
 
203
217
  if (!dimImage.width) {
204
218
  return noop;
205
219
  }
206
220
 
207
- const values = stops.map(([value]) => value);
208
- const min = Math.min(...values);
209
- const max = Math.max(...values);
210
- const down = values.indexOf(min) < values.indexOf(max);
211
-
212
- const diff = max - min;
213
- let pos = (down ? -diff : 0) - (down ? min : max);
214
-
215
221
  const dimEl = {
216
222
  width: el.offsetWidth,
217
223
  height: el.offsetHeight,
218
224
  };
219
225
 
220
- const baseDim = Dimensions.cover(dimImage, dimEl);
221
- const span = baseDim[attr] - dimEl[attr];
226
+ const bgProps = ['bgx', 'bgy'].filter((prop) => prop in props);
222
227
 
223
- if (span < diff) {
224
- dimEl[attr] = baseDim[attr] + diff - span;
225
- } else if (span > diff) {
226
- const posPercentage = dimEl[attr] / toPx(bgPos, attr, el, true);
228
+ const positions = {};
229
+ for (const prop of bgProps) {
230
+ const values = props[prop].map(([value]) => value);
231
+ const min = Math.min(...values);
232
+ const max = Math.max(...values);
233
+ const down = values.indexOf(min) < values.indexOf(max);
234
+ const diff = max - min;
227
235
 
228
- if (posPercentage) {
229
- pos -= (span - diff) / posPercentage;
230
- }
236
+ positions[prop] = `${(down ? -diff : 0) - (down ? min : max)}px`;
237
+ dimEl[prop === 'bgy' ? 'height' : 'width'] += diff;
231
238
  }
232
239
 
233
240
  const dim = Dimensions.cover(dimImage, dimEl);
234
241
 
235
- const fn = setBackgroundPosFn(prop, stops, `${pos}px`);
242
+ for (const prop of bgProps) {
243
+ const attr = prop === 'bgy' ? 'height' : 'width';
244
+ const overflow = dim[attr] - dimEl[attr];
245
+ positions[prop] = `max(${getBackgroundPos(el, prop)},-${overflow}px) + ${positions[prop]}`;
246
+ }
247
+
248
+ const fn = setBackgroundPosFn(bgProps, positions, props);
236
249
  return (css, percent) => {
237
250
  fn(css, percent);
238
251
  css.backgroundSize = `${dim.width}px ${dim.height}px`;
@@ -240,9 +253,16 @@ function backgroundCoverFn(prop, el, stops, bgPos, attr) {
240
253
  };
241
254
  }
242
255
 
243
- function setBackgroundPosFn(prop, stops, pos) {
256
+ function getBackgroundPos(el, prop) {
257
+ return getCssValue(el, `background-position-${prop.substr(-1)}`, '');
258
+ }
259
+
260
+ function setBackgroundPosFn(bgProps, positions, props) {
244
261
  return function (css, percent) {
245
- css[`background-position-${prop}`] = `calc(${pos} + ${getValue(stops, percent)}px)`;
262
+ for (const prop of bgProps) {
263
+ const value = getValue(props[prop], percent);
264
+ css[`background-position-${prop.substr(-1)}`] = `calc(${positions[prop]} + ${value}px)`;
265
+ }
246
266
  };
247
267
  }
248
268