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
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.16.27",
5
+ "version": "3.17.1-dev.7c0766a6f",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -25,13 +25,13 @@ function init(App) {
25
25
  }
26
26
 
27
27
  new MutationObserver((records) => records.forEach(applyChildListMutation)).observe(document, {
28
- childList: true,
29
28
  subtree: true,
29
+ childList: true,
30
30
  });
31
31
 
32
32
  new MutationObserver((records) => records.forEach(applyAttributeMutation)).observe(document, {
33
- attributes: true,
34
33
  subtree: true,
34
+ attributes: true,
35
35
  });
36
36
 
37
37
  App._initialized = true;
@@ -80,7 +80,7 @@ function registerComputedObserver() {
80
80
  }
81
81
  });
82
82
  observer.observe(document, {
83
- childList: true,
84
83
  subtree: true,
84
+ childList: true,
85
85
  });
86
86
  }
@@ -59,10 +59,7 @@ export function viewport(options) {
59
59
  export function scroll(options) {
60
60
  return observe(
61
61
  (target, handler) => ({
62
- disconnect: on(toScrollTargets(target), 'scroll', handler, {
63
- passive: true,
64
- capture: true,
65
- }),
62
+ disconnect: on(toScrollTargets(target), 'scroll', handler, { passive: true }),
66
63
  }),
67
64
  options,
68
65
  'scroll',
@@ -27,7 +27,7 @@ export default {
27
27
  message: '',
28
28
  status: '',
29
29
  timeout: 5000,
30
- group: null,
30
+ group: '',
31
31
  pos: 'top-center',
32
32
  clsContainer: 'uk-notification',
33
33
  clsClose: 'uk-notification-close',
@@ -1,10 +1,12 @@
1
- import { boxModelAdjust, css } from 'uikit-util';
1
+ import { boxModelAdjust, css, inBrowser } from 'uikit-util';
2
2
  import Class from '../mixin/class';
3
3
  import SliderReactive from '../mixin/slider-reactive';
4
4
  import Slideshow from '../mixin/slideshow';
5
5
  import SliderPreload from './internal/slider-preload';
6
6
  import Animations from './internal/slideshow-animations';
7
7
 
8
+ const supportsAspectRatio = inBrowser && CSS.supports('aspect-ratio', '1/1');
9
+
8
10
  export default {
9
11
  mixins: [Class, Slideshow, SliderReactive, SliderPreload],
10
12
 
@@ -24,9 +26,24 @@ export default {
24
26
  Animations,
25
27
  },
26
28
 
29
+ watch: {
30
+ list(list) {
31
+ if (list && supportsAspectRatio) {
32
+ css(list, {
33
+ aspectRatio: this.ratio.replace(':', '/'),
34
+ minHeight: this.minHeight || '',
35
+ maxHeight: this.maxHeight || '',
36
+ minWidth: '100%',
37
+ maxWidth: '100%',
38
+ });
39
+ }
40
+ },
41
+ },
42
+
27
43
  update: {
44
+ // deprecated: Remove with iOS 17
28
45
  read() {
29
- if (!this.list) {
46
+ if (!this.list || supportsAspectRatio) {
30
47
  return false;
31
48
  }
32
49
 
@@ -1,7 +1,17 @@
1
- import { css, Dimensions, parent } from 'uikit-util';
1
+ import { attr, css, Dimensions, isTag, parent } from 'uikit-util';
2
2
  import { resize } from '../api/observables';
3
3
  import Video from './video';
4
4
 
5
+ const coverProps = {
6
+ top: 0, // resets the css from [uk-cover]
7
+ left: 0, // resets the css from [uk-cover]
8
+ width: '100%',
9
+ height: '100%',
10
+ transform: 'none',
11
+ objectFit: 'cover',
12
+ objectPosition: 'center',
13
+ };
14
+
5
15
  export default {
6
16
  mixins: [Video],
7
17
 
@@ -22,10 +32,29 @@ export default {
22
32
 
23
33
  observe: resize({
24
34
  target: ({ $el }) => [getPositionedParent($el) || parent($el)],
35
+ filter: ({ _useObjectFit }) => !_useObjectFit,
25
36
  }),
26
37
 
38
+ connected() {
39
+ this._useObjectFit = isTag(this.$el, 'img', 'video');
40
+
41
+ if (this._useObjectFit) {
42
+ css(this.$el, coverProps);
43
+ }
44
+ },
45
+
46
+ disconnected() {
47
+ if (this._useObjectFit) {
48
+ attr(this.$el, 'style', '');
49
+ }
50
+ },
51
+
27
52
  update: {
28
53
  read() {
54
+ if (this._useObjectFit) {
55
+ return;
56
+ }
57
+
29
58
  const { ratio, cover } = Dimensions;
30
59
  const { $el, width, height } = this;
31
60
 
@@ -58,6 +58,7 @@ export default {
58
58
  clsDrop: String,
59
59
  animateOut: Boolean,
60
60
  bgScroll: Boolean,
61
+ closeOnScroll: Boolean,
61
62
  },
62
63
 
63
64
  data: {
@@ -79,6 +80,7 @@ export default {
79
80
  animation: ['uk-animation-fade'],
80
81
  cls: 'uk-open',
81
82
  container: false,
83
+ closeOnScroll: false,
82
84
  },
83
85
 
84
86
  computed: {
@@ -247,14 +249,10 @@ export default {
247
249
  self: true,
248
250
 
249
251
  handler(e, toggled) {
250
- attr(this.targetEl, 'aria-expanded', toggled ? true : null);
251
-
252
- if (!toggled) {
253
- return;
252
+ if (toggled) {
253
+ this.clearTimers();
254
+ this.position();
254
255
  }
255
-
256
- this.clearTimers();
257
- this.position();
258
256
  },
259
257
  },
260
258
 
@@ -267,12 +265,14 @@ export default {
267
265
  active = this;
268
266
 
269
267
  this.tracker.init();
268
+ attr(this.targetEl, 'aria-expanded', true);
270
269
 
271
270
  const handlers = [
272
271
  listenForResize(this),
273
272
  listenForEscClose(this),
274
273
  listenForBackgroundClose(this),
275
274
  this.autoUpdate && listenForScroll(this),
275
+ this.closeOnScroll && listenForScrollClose(this),
276
276
  !this.bgScroll && preventBackgroundScroll(this.$el),
277
277
  ];
278
278
 
@@ -306,6 +306,7 @@ export default {
306
306
 
307
307
  active = this.isActive() ? null : active;
308
308
  this.tracker.cancel();
309
+ attr(this.targetEl, 'aria-expanded', null);
309
310
  },
310
311
  },
311
312
  ],
@@ -360,6 +361,7 @@ export default {
360
361
 
361
362
  this.clearTimers();
362
363
 
364
+ this.isDelayedHide = delay;
363
365
  this.isDelaying = getPositionedElements(this.$el).some((el) =>
364
366
  this.tracker.movesTo(el),
365
367
  );
@@ -483,8 +485,8 @@ function listenForResize(drop) {
483
485
  return () => off.map((observer) => observer.disconnect());
484
486
  }
485
487
 
486
- function listenForScroll(drop) {
487
- return on([document, ...overflowParents(drop.$el)], 'scroll', () => drop.$emit(), {
488
+ function listenForScroll(drop, fn = () => drop.$emit()) {
489
+ return on([document, ...overflowParents(drop.$el)], 'scroll', fn, {
488
490
  passive: true,
489
491
  });
490
492
  }
@@ -497,6 +499,10 @@ function listenForEscClose(drop) {
497
499
  });
498
500
  }
499
501
 
502
+ function listenForScrollClose(drop) {
503
+ return listenForScroll(drop, () => drop.hide(false));
504
+ }
505
+
500
506
  function listenForBackgroundClose(drop) {
501
507
  return on(document, pointerDown, ({ target }) => {
502
508
  if (!within(target, drop.$el)) {
@@ -47,6 +47,7 @@ export default {
47
47
  targetY: Boolean,
48
48
  animation: Boolean,
49
49
  animateOut: Boolean,
50
+ closeOnScroll: Boolean,
50
51
  },
51
52
 
52
53
  data: {
@@ -77,6 +78,10 @@ export default {
77
78
  return dropbar ? dropbar : (this._dropbar = $('<div></div>'));
78
79
  },
79
80
 
81
+ dropbarOffset() {
82
+ return 0;
83
+ },
84
+
80
85
  dropContainer(_, $el) {
81
86
  return this.container || $el;
82
87
  },
@@ -291,9 +296,16 @@ export default {
291
296
  const minTop = Math.min(...targetOffsets.map(({ top }) => top));
292
297
  const maxBottom = Math.max(...targetOffsets.map(({ bottom }) => bottom));
293
298
  const dropbarOffset = offset(this.dropbar);
294
- css(this.dropbar, 'top', this.dropbar.offsetTop - (dropbarOffset.top - minTop));
299
+ css(
300
+ this.dropbar,
301
+ 'top',
302
+ this.dropbar.offsetTop - (dropbarOffset.top - minTop) - this.dropbarOffset,
303
+ );
295
304
  this.transitionTo(
296
- maxBottom - minTop + toFloat(css(target, 'marginBottom')),
305
+ maxBottom -
306
+ minTop +
307
+ toFloat(css(target, 'marginBottom')) +
308
+ this.dropbarOffset,
297
309
  target,
298
310
  );
299
311
  };
@@ -319,6 +331,8 @@ export default {
319
331
  if (
320
332
  matches(this.dropbar, ':hover') &&
321
333
  active.$el === e.target &&
334
+ includes(active.mode, 'hover') &&
335
+ active.isDelayedHide &&
322
336
  !this.items.some((el) => active.targetEl !== el && matches(el, ':focus'))
323
337
  ) {
324
338
  e.preventDefault();
@@ -1,13 +1,13 @@
1
1
  import {
2
2
  addClass,
3
- children,
4
3
  css,
5
4
  hasClass,
5
+ isRtl,
6
6
  scrolledOver,
7
- sortBy,
8
7
  sumBy,
9
8
  toFloat,
10
9
  toggleClass,
10
+ toPx,
11
11
  } from 'uikit-util';
12
12
  import { scroll } from '../api/observables';
13
13
  import Class from '../mixin/class';
@@ -22,7 +22,10 @@ export default {
22
22
 
23
23
  props: {
24
24
  masonry: Boolean,
25
- parallax: Number,
25
+ parallax: String,
26
+ parallaxStart: String,
27
+ parallaxEnd: String,
28
+ parallaxJustify: Boolean,
26
29
  },
27
30
 
28
31
  data: {
@@ -30,18 +33,21 @@ export default {
30
33
  clsStack: 'uk-grid-stack',
31
34
  masonry: false,
32
35
  parallax: 0,
36
+ parallaxStart: 0,
37
+ parallaxEnd: 0,
38
+ parallaxJustify: false,
33
39
  },
34
40
 
35
41
  connected() {
36
- this.masonry && addClass(this.$el, 'uk-flex-top uk-flex-wrap-top');
42
+ this.masonry && addClass(this.$el, 'uk-flex-top', 'uk-flex-wrap-top');
37
43
  },
38
44
 
39
- observe: scroll({ filter: ({ parallax }) => parallax }),
45
+ observe: scroll({ filter: ({ parallax, parallaxJustify }) => parallax || parallaxJustify }),
40
46
 
41
47
  update: [
42
48
  {
43
- write({ columns }) {
44
- toggleClass(this.$el, this.clsStack, columns.length < 2);
49
+ write({ rows }) {
50
+ toggleClass(this.$el, this.clsStack, !rows[0][1]);
45
51
  },
46
52
 
47
53
  events: ['resize'],
@@ -49,43 +55,62 @@ export default {
49
55
 
50
56
  {
51
57
  read(data) {
52
- let { columns, rows } = data;
58
+ const { rows } = data;
59
+ let { masonry, parallax, parallaxJustify, margin } = this;
60
+
61
+ parallax = Math.max(0, toPx(parallax));
53
62
 
54
63
  // Filter component makes elements positioned absolute
55
64
  if (
56
- !columns.length ||
57
- (!this.masonry && !this.parallax) ||
58
- positionedAbsolute(this.$el)
65
+ !(masonry || parallax || parallaxJustify) ||
66
+ positionedAbsolute(rows) ||
67
+ rows[0].some((el, i) =>
68
+ rows.some((row) => row[i] && row[i].offsetWidth !== el.offsetWidth),
69
+ )
59
70
  ) {
60
- data.translates = false;
61
- return false;
71
+ return (data.translates = data.scrollColumns = false);
62
72
  }
63
73
 
64
- let translates = false;
65
-
66
- const nodes = children(this.$el);
67
- const columnHeights = columns.map((column) => sumBy(column, 'offsetHeight'));
68
- const margin = getMarginTop(nodes, this.margin) * (rows.length - 1);
69
- const elHeight = Math.max(...columnHeights) + margin;
74
+ let gutter = getGutter(rows, margin);
70
75
 
71
- if (this.masonry) {
72
- columns = columns.map((column) => sortBy(column, 'offsetTop'));
73
- translates = getTranslates(rows, columns);
76
+ let columns;
77
+ let translates;
78
+ if (masonry) {
79
+ [columns, translates] = applyMasonry(rows, gutter, masonry === 'next');
80
+ } else {
81
+ columns = transpose(rows);
74
82
  }
75
83
 
76
- let padding = Math.abs(this.parallax);
77
- if (padding) {
78
- padding = columnHeights.reduce(
79
- (newPadding, hgt, i) =>
80
- Math.max(
81
- newPadding,
82
- hgt + margin + (i % 2 ? padding : padding / 8) - elHeight,
83
- ),
84
- 0,
84
+ const columnHeights = columns.map(
85
+ (column) => sumBy(column, 'offsetHeight') + gutter * (column.length - 1),
86
+ );
87
+ const height = Math.max(0, ...columnHeights);
88
+
89
+ let scrollColumns;
90
+ let parallaxStart;
91
+ let parallaxEnd;
92
+ if (parallax || parallaxJustify) {
93
+ scrollColumns = columnHeights.map((hgt, i) =>
94
+ parallaxJustify ? height - hgt + parallax : parallax / (i % 2 || 8),
85
95
  );
96
+ if (!parallaxJustify) {
97
+ parallax = Math.max(
98
+ ...columnHeights.map((hgt, i) => hgt + scrollColumns[i] - height),
99
+ );
100
+ }
101
+ parallaxStart = toPx(this.parallaxStart, 'height', this.$el, true);
102
+ parallaxEnd = toPx(this.parallaxEnd, 'height', this.$el, true);
86
103
  }
87
104
 
88
- return { padding, columns, translates, height: translates ? elHeight : '' };
105
+ return {
106
+ columns,
107
+ translates,
108
+ scrollColumns,
109
+ parallaxStart,
110
+ parallaxEnd,
111
+ padding: parallax,
112
+ height: translates ? height : '',
113
+ };
89
114
  },
90
115
 
91
116
  write({ height, padding }) {
@@ -97,36 +122,33 @@ export default {
97
122
  },
98
123
 
99
124
  {
100
- read() {
101
- if (this.parallax && positionedAbsolute(this.$el)) {
125
+ read({ rows, scrollColumns, parallaxStart, parallaxEnd }) {
126
+ if (scrollColumns && positionedAbsolute(rows)) {
102
127
  return false;
103
128
  }
104
129
 
105
130
  return {
106
- scrolled: this.parallax
107
- ? scrolledOver(this.$el) * Math.abs(this.parallax)
131
+ scrolled: scrollColumns
132
+ ? scrolledOver(this.$el, parallaxStart, parallaxEnd)
108
133
  : false,
109
134
  };
110
135
  },
111
136
 
112
- write({ columns, scrolled, translates }) {
113
- if (scrolled === false && !translates) {
137
+ write({ columns, scrolled, scrollColumns, translates }) {
138
+ if (!scrolled && !translates) {
114
139
  return;
115
140
  }
116
141
 
117
142
  columns.forEach((column, i) =>
118
- column.forEach((el, j) =>
119
- css(
120
- el,
121
- 'transform',
122
- !scrolled && !translates
123
- ? ''
124
- : `translateY(${
125
- (translates && -translates[i][j]) +
126
- (scrolled ? (i % 2 ? scrolled : scrolled / 8) : 0)
127
- }px)`,
128
- ),
129
- ),
143
+ column.forEach((el, j) => {
144
+ let [x, y] = (translates && translates[i][j]) || [0, 0];
145
+
146
+ if (scrolled) {
147
+ y += scrolled * scrollColumns[i];
148
+ }
149
+
150
+ css(el, 'transform', `translate(${x}px, ${y}px)`);
151
+ }),
130
152
  );
131
153
  },
132
154
 
@@ -135,24 +157,57 @@ export default {
135
157
  ],
136
158
  };
137
159
 
138
- function positionedAbsolute(el) {
139
- return children(el).some((el) => css(el, 'position') === 'absolute');
160
+ function positionedAbsolute(rows) {
161
+ return rows.flat().some((el) => css(el, 'position') === 'absolute');
140
162
  }
141
163
 
142
- function getTranslates(rows, columns) {
143
- const rowHeights = rows.map((row) => Math.max(...row.map((el) => el.offsetHeight)));
164
+ function applyMasonry(rows, gutter, next) {
165
+ const columns = [];
166
+ const translates = [];
167
+ const columnHeights = Array(rows[0].length).fill(0);
168
+ let rowHeights = 0;
169
+ for (let row of rows) {
170
+ if (isRtl) {
171
+ row = row.reverse();
172
+ }
173
+
174
+ let height = 0;
175
+ for (const j in row) {
176
+ const { offsetWidth, offsetHeight } = row[j];
177
+ const index = next ? j : columnHeights.indexOf(Math.min(...columnHeights));
178
+ push(columns, index, row[j]);
179
+ push(translates, index, [
180
+ (index - j) * offsetWidth * (isRtl ? -1 : 1),
181
+ columnHeights[index] - rowHeights,
182
+ ]);
183
+ columnHeights[index] += offsetHeight + gutter;
184
+ height = Math.max(height, offsetHeight);
185
+ }
186
+
187
+ rowHeights += height + gutter;
188
+ }
189
+
190
+ return [columns, translates];
191
+ }
144
192
 
145
- return columns.map((elements) => {
146
- let prev = 0;
147
- return elements.map(
148
- (element, row) =>
149
- (prev += row ? rowHeights[row - 1] - elements[row - 1].offsetHeight : 0),
150
- );
151
- });
193
+ function getGutter(rows, cls) {
194
+ const node = rows.flat().find((el) => hasClass(el, cls));
195
+ return toFloat(node ? css(node, 'marginTop') : css(rows[0][0], 'paddingLeft'));
152
196
  }
153
197
 
154
- function getMarginTop(nodes, cls) {
155
- const [node] = nodes.filter((el) => hasClass(el, cls));
198
+ function transpose(rows) {
199
+ const columns = [];
200
+ for (const row of rows) {
201
+ for (const i in row) {
202
+ push(columns, i, row[i]);
203
+ }
204
+ }
205
+ return columns;
206
+ }
156
207
 
157
- return toFloat(node ? css(node, 'marginTop') : css(nodes[0], 'paddingLeft'));
208
+ function push(array, index, value) {
209
+ if (!array[index]) {
210
+ array[index] = [];
211
+ }
212
+ array[index].push(value);
158
213
  }
@@ -62,7 +62,10 @@ export default {
62
62
  } else {
63
63
  if (this.offsetTop) {
64
64
  if (isScrollingElement) {
65
- const top = offsetPosition(this.$el)[0] - offsetPosition(scrollElement)[0];
65
+ const offsetTopEl =
66
+ this.offsetTop === true ? this.$el : query(this.offsetTop, this.$el);
67
+ const top =
68
+ offsetPosition(offsetTopEl)[0] - offsetPosition(scrollElement)[0];
66
69
  minHeight += top > 0 && top < viewportHeight / 2 ? ` - ${top}px` : '';
67
70
  } else {
68
71
  minHeight += ` - ${css(scrollElement, 'paddingTop')}`;
@@ -1,4 +1,4 @@
1
- import { children, isRtl, isVisible, offsetPosition, toggleClass } from 'uikit-util';
1
+ import { children, isRtl, isVisible, offsetPosition, toArray, toggleClass } from 'uikit-util';
2
2
  import { mutation, resize } from '../api/observables';
3
3
 
4
4
  export default {
@@ -27,19 +27,16 @@ export default {
27
27
 
28
28
  update: {
29
29
  read() {
30
- const rows = getRows(this.$el.children);
31
-
32
30
  return {
33
- rows,
34
- columns: getColumns(rows),
31
+ rows: getRows(toArray(this.$el.children)),
35
32
  };
36
33
  },
37
34
 
38
- write({ columns, rows }) {
35
+ write({ rows }) {
39
36
  for (const row of rows) {
40
- for (const column of row) {
41
- toggleClass(column, this.margin, rows[0] !== row);
42
- toggleClass(column, this.firstColumn, columns[0].includes(column));
37
+ for (const el of row) {
38
+ toggleClass(el, this.margin, rows[0] !== row);
39
+ toggleClass(el, this.firstColumn, row[isRtl ? row.length - 1 : 0] === el);
43
40
  }
44
41
  }
45
42
  },
@@ -48,32 +45,18 @@ export default {
48
45
  },
49
46
  };
50
47
 
51
- export function getRows(items) {
52
- return sortBy(items, 'top', 'bottom');
53
- }
54
-
55
- function getColumns(rows) {
56
- const columns = [];
57
-
58
- for (const row of rows) {
59
- const sorted = sortBy(row, 'left', 'right');
60
- for (let j = 0; j < sorted.length; j++) {
61
- columns[j] = columns[j] ? columns[j].concat(sorted[j]) : sorted[j];
62
- }
63
- }
64
-
65
- return isRtl ? columns.reverse() : columns;
66
- }
67
-
68
- function sortBy(items, startProp, endProp) {
48
+ export function getRows(elements) {
69
49
  const sorted = [[]];
50
+ const withOffset = elements.some(
51
+ (el, i) => i && elements[i - 1].offsetParent !== el.offsetParent,
52
+ );
70
53
 
71
- for (const el of items) {
54
+ for (const el of elements) {
72
55
  if (!isVisible(el)) {
73
56
  continue;
74
57
  }
75
58
 
76
- let dim = getOffset(el);
59
+ const offset = getOffset(el, withOffset);
77
60
 
78
61
  for (let i = sorted.length - 1; i >= 0; i--) {
79
62
  const current = sorted[i];
@@ -83,21 +66,22 @@ function sortBy(items, startProp, endProp) {
83
66
  break;
84
67
  }
85
68
 
86
- let startDim;
87
- if (current[0].offsetParent === el.offsetParent) {
88
- startDim = getOffset(current[0]);
89
- } else {
90
- dim = getOffset(el, true);
91
- startDim = getOffset(current[0], true);
92
- }
69
+ const offsetCurrent = getOffset(current[0], withOffset);
93
70
 
94
- if (dim[startProp] >= startDim[endProp] - 1 && dim[startProp] !== startDim[startProp]) {
71
+ if (offset.top >= offsetCurrent.bottom - 1 && offset.top !== offsetCurrent.top) {
95
72
  sorted.push([el]);
96
73
  break;
97
74
  }
98
75
 
99
- if (dim[endProp] - 1 > startDim[startProp] || dim[startProp] === startDim[startProp]) {
100
- current.push(el);
76
+ if (offset.bottom - 1 > offsetCurrent.top || offset.top === offsetCurrent.top) {
77
+ let j = current.length - 1;
78
+ for (; j >= 0; j--) {
79
+ const offsetCurrent = getOffset(current[j], withOffset);
80
+ if (offset.left >= offsetCurrent.left) {
81
+ break;
82
+ }
83
+ }
84
+ current.splice(j + 1, 0, el);
101
85
  break;
102
86
  }
103
87