uikit 3.14.3 → 3.14.4-dev.10a07fe5a

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 (86) hide show
  1. package/CHANGELOG.md +22 -0
  2. package/dist/css/uikit-core-rtl.css +84 -29
  3. package/dist/css/uikit-core-rtl.min.css +1 -1
  4. package/dist/css/uikit-core.css +84 -29
  5. package/dist/css/uikit-core.min.css +1 -1
  6. package/dist/css/uikit-rtl.css +90 -44
  7. package/dist/css/uikit-rtl.min.css +1 -1
  8. package/dist/css/uikit.css +90 -44
  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 +1 -1
  13. package/dist/js/components/filter.min.js +1 -1
  14. package/dist/js/components/lightbox-panel.js +189 -26
  15. package/dist/js/components/lightbox-panel.min.js +1 -1
  16. package/dist/js/components/lightbox.js +189 -26
  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 +1 -1
  21. package/dist/js/components/parallax.min.js +1 -1
  22. package/dist/js/components/slider-parallax.js +1 -1
  23. package/dist/js/components/slider-parallax.min.js +1 -1
  24. package/dist/js/components/slider.js +1 -1
  25. package/dist/js/components/slider.min.js +1 -1
  26. package/dist/js/components/slideshow-parallax.js +1 -1
  27. package/dist/js/components/slideshow-parallax.min.js +1 -1
  28. package/dist/js/components/slideshow.js +1 -1
  29. package/dist/js/components/slideshow.min.js +1 -1
  30. package/dist/js/components/sortable.js +1 -1
  31. package/dist/js/components/sortable.min.js +1 -1
  32. package/dist/js/components/tooltip.js +135 -34
  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 +801 -617
  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 +801 -617
  41. package/dist/js/uikit.min.js +1 -1
  42. package/package.json +1 -1
  43. package/src/js/core/accordion.js +3 -3
  44. package/src/js/core/alert.js +1 -1
  45. package/src/js/core/drop.js +47 -20
  46. package/src/js/core/height-viewport.js +14 -9
  47. package/src/js/core/navbar.js +11 -9
  48. package/src/js/core/offcanvas.js +1 -47
  49. package/src/js/core/switcher.js +1 -1
  50. package/src/js/mixin/modal.js +90 -4
  51. package/src/js/mixin/position.js +44 -13
  52. package/src/js/mixin/togglable.js +105 -24
  53. package/src/js/util/animation.js +1 -0
  54. package/src/js/util/viewport.js +21 -10
  55. package/src/less/components/drop.less +19 -5
  56. package/src/less/components/dropdown.less +21 -5
  57. package/src/less/components/margin.less +13 -14
  58. package/src/less/components/modal.less +19 -4
  59. package/src/less/components/nav.less +1 -1
  60. package/src/less/components/navbar.less +56 -25
  61. package/src/less/components/offcanvas.less +21 -21
  62. package/src/less/components/position.less +1 -1
  63. package/src/less/components/utility.less +0 -1
  64. package/src/less/theme/dropdown.less +11 -0
  65. package/src/less/theme/navbar.less +12 -12
  66. package/src/scss/components/drop.scss +19 -5
  67. package/src/scss/components/dropdown.scss +21 -5
  68. package/src/scss/components/margin.scss +13 -14
  69. package/src/scss/components/modal.scss +19 -4
  70. package/src/scss/components/nav.scss +1 -1
  71. package/src/scss/components/navbar.scss +45 -14
  72. package/src/scss/components/offcanvas.scss +21 -21
  73. package/src/scss/components/position.scss +1 -1
  74. package/src/scss/components/utility.scss +0 -1
  75. package/src/scss/mixins-theme.scss +8 -12
  76. package/src/scss/mixins.scss +2 -0
  77. package/src/scss/theme/dropdown.scss +8 -0
  78. package/src/scss/theme/navbar.scss +9 -0
  79. package/src/scss/variables-theme.scss +27 -11
  80. package/src/scss/variables.scss +25 -11
  81. package/tests/drop.html +151 -2
  82. package/tests/dropdown.html +228 -13
  83. package/tests/height-viewport.html +62 -0
  84. package/tests/navbar.html +325 -18
  85. package/tests/offcanvas.html +8 -8
  86. package/tests/sticky-navbar.html +132 -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.14.3",
5
+ "version": "3.14.4-dev.10a07fe5a",
6
6
  "main": "dist/js/uikit.js",
7
7
  "style": "dist/css/uikit.css",
8
8
  "sideEffects": [
@@ -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 },
@@ -8,7 +8,6 @@ import {
8
8
  apply,
9
9
  attr,
10
10
  css,
11
- getCssVar,
12
11
  hasClass,
13
12
  includes,
14
13
  isTouch,
@@ -16,6 +15,7 @@ import {
16
15
  MouseTracker,
17
16
  observeResize,
18
17
  offset,
18
+ offsetViewport,
19
19
  on,
20
20
  once,
21
21
  parent,
@@ -28,9 +28,9 @@ import {
28
28
  removeClass,
29
29
  scrollParents,
30
30
  toggleClass,
31
- toPx,
32
31
  within,
33
32
  } from 'uikit-util';
33
+ import { preventBackgroundScroll, preventOverscroll } from '../mixin/modal';
34
34
 
35
35
  export let active;
36
36
 
@@ -48,6 +48,8 @@ export default {
48
48
  delayHide: Number,
49
49
  display: String,
50
50
  clsDrop: String,
51
+ animateOut: Boolean,
52
+ bgScroll: Boolean,
51
53
  },
52
54
 
53
55
  data: {
@@ -62,6 +64,8 @@ export default {
62
64
  animation: ['uk-animation-fade'],
63
65
  cls: 'uk-open',
64
66
  container: false,
67
+ animateOut: false,
68
+ bgScroll: true,
65
69
  },
66
70
 
67
71
  created() {
@@ -249,7 +253,11 @@ export default {
249
253
  }
250
254
  }),
251
255
 
252
- ...(this.display === 'static'
256
+ ...(this.bgScroll
257
+ ? []
258
+ : [preventOverscroll(this.$el), preventBackgroundScroll()]),
259
+
260
+ ...(this.display === 'static' && this.align !== 'stretch'
253
261
  ? []
254
262
  : (() => {
255
263
  const handler = () => this.$emit();
@@ -310,7 +318,7 @@ export default {
310
318
  methods: {
311
319
  show(target = this.target, delay = true) {
312
320
  if (this.isToggled() && target && this.target && target !== this.target) {
313
- this.hide(false);
321
+ this.hide(false, false);
314
322
  }
315
323
 
316
324
  this.target = target;
@@ -330,7 +338,7 @@ export default {
330
338
  let prev;
331
339
  while (active && prev !== active && !within(this.$el, active.$el)) {
332
340
  prev = active;
333
- active.hide(false);
341
+ active.hide(false, false);
334
342
  }
335
343
  }
336
344
 
@@ -344,8 +352,8 @@ export default {
344
352
  );
345
353
  },
346
354
 
347
- hide(delay = true) {
348
- const hide = () => this.toggleElement(this.$el, false, false);
355
+ hide(delay = true, animate = true) {
356
+ const hide = () => this.toggleElement(this.$el, false, this.animateOut && animate);
349
357
 
350
358
  this.clearTimers();
351
359
 
@@ -377,40 +385,59 @@ export default {
377
385
  position() {
378
386
  removeClass(this.$el, `${this.clsDrop}-stack`);
379
387
  toggleClass(this.$el, `${this.clsDrop}-boundary`, this.boundaryAlign);
388
+ toggleClass(this.$el, `${this.clsDrop}-stretch`, this.align === 'stretch');
380
389
 
381
390
  const boundary = query(this.boundary, this.$el);
382
- const scrollParentOffset = offset(
383
- scrollParents(boundary && this.boundaryAlign ? boundary : this.$el)[0]
391
+ const target = boundary && this.boundaryAlign ? boundary : this.target;
392
+ const [scrollParent] = scrollParents(
393
+ boundary && this.boundaryAlign ? boundary : this.$el
384
394
  );
395
+ const scrollParentOffset = offset(scrollParent);
385
396
  const boundaryOffset = boundary ? offset(boundary) : scrollParentOffset;
397
+ const viewportOffset = this.getViewportOffset(this.$el);
386
398
 
387
399
  css(this.$el, 'maxWidth', '');
388
- const maxWidth =
389
- scrollParentOffset.width -
390
- 2 * toPx(getCssVar('position-viewport-offset', this.$el));
400
+ const maxWidth = scrollParentOffset.width - 2 * viewportOffset;
391
401
 
392
- if (this.pos[1] === 'justify') {
402
+ if (this.align === 'justify') {
393
403
  const prop = this.axis === 'y' ? 'width' : 'height';
394
404
  css(
395
405
  this.$el,
396
406
  prop,
397
407
  Math.min(
398
408
  (boundary ? boundaryOffset : offset(this.target))[prop],
399
- scrollParentOffset[prop] -
400
- 2 * toPx(getCssVar('position-viewport-offset', this.$el))
409
+ scrollParentOffset[prop] - 2 * viewportOffset
401
410
  )
402
411
  );
412
+ } else if (this.align === 'stretch') {
413
+ this.flip = this.axis === 'y' ? 'x' : 'y';
414
+ this.display = 'static';
415
+
416
+ const viewport = offsetViewport(scrollParent);
417
+ const targetDim = offset(target);
418
+ const elOffset = Math.abs(this.getPositionOffset(this.$el)) + viewportOffset;
419
+
420
+ css(this.$el, {
421
+ width:
422
+ this.axis === 'y'
423
+ ? viewport.width
424
+ : (this.dir === 'left'
425
+ ? targetDim.left - viewport.left
426
+ : viewport.right - targetDim.right) - elOffset,
427
+ height:
428
+ this.axis === 'x'
429
+ ? viewport.height
430
+ : (this.dir === 'top'
431
+ ? targetDim.top - viewport.top
432
+ : viewport.bottom - targetDim.bottom) - elOffset,
433
+ });
403
434
  } else if (this.$el.offsetWidth > maxWidth) {
404
435
  addClass(this.$el, `${this.clsDrop}-stack`);
405
436
  }
406
437
 
407
438
  css(this.$el, 'maxWidth', maxWidth);
408
439
 
409
- this.positionAt(
410
- this.$el,
411
- boundary && this.boundaryAlign ? boundary : this.target,
412
- boundary
413
- );
440
+ this.positionAt(this.$el, target, boundary);
414
441
  },
415
442
  },
416
443
  };
@@ -4,12 +4,13 @@ import {
4
4
  css,
5
5
  dimensions,
6
6
  endsWith,
7
- height,
8
7
  isNumeric,
9
8
  isString,
10
9
  isVisible,
11
- offset,
10
+ offsetPosition,
11
+ offsetViewport,
12
12
  query,
13
+ scrollParents,
13
14
  toFloat,
14
15
  } from 'uikit-util';
15
16
 
@@ -32,7 +33,7 @@ export default {
32
33
 
33
34
  resizeTargets() {
34
35
  // check for offsetTop change
35
- return [this.$el, document.documentElement];
36
+ return [this.$el, ...scrollParents(this.$el, /auto|scroll/)];
36
37
  },
37
38
 
38
39
  update: {
@@ -44,21 +45,25 @@ export default {
44
45
  let minHeight = '';
45
46
  const box = boxModelAdjust(this.$el, 'height', 'content-box');
46
47
 
48
+ const [scrollElement] = scrollParents(this.$el, /auto|scroll/);
49
+ const { height: viewportHeight } = offsetViewport(scrollElement);
50
+
47
51
  if (this.expand) {
48
52
  minHeight = Math.max(
49
- height(window) -
50
- (dimensions(document.documentElement).height -
51
- dimensions(this.$el).height) -
53
+ viewportHeight -
54
+ (dimensions(scrollElement).height - dimensions(this.$el).height) -
52
55
  box,
53
56
  0
54
57
  );
55
58
  } else {
56
59
  // on mobile devices (iOS and Android) window.innerHeight !== 100vh
57
- minHeight = 'calc(100vh';
60
+ minHeight = `calc(${
61
+ document.scrollingElement === scrollElement ? '100vh' : `${viewportHeight}px`
62
+ }`;
58
63
 
59
64
  if (this.offsetTop) {
60
- const { top } = offset(this.$el);
61
- 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` : '';
62
67
  }
63
68
 
64
69
  if (this.offsetBottom === true) {
@@ -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,8 +309,8 @@ export default {
309
309
  return this.dropbar;
310
310
  },
311
311
 
312
- handler(_, { $el }) {
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
 
@@ -360,8 +360,8 @@ 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
 
@@ -387,7 +387,7 @@ export default {
387
387
 
388
388
  el = oldHeight < newHeight && el;
389
389
 
390
- 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)`);
391
391
 
392
392
  height(dropbar, oldHeight);
393
393
 
@@ -396,12 +396,14 @@ export default {
396
396
  Transition.start(dropbar, { height: newHeight }, this.duration),
397
397
  Transition.start(
398
398
  el,
399
- { clip: `rect(0,${el.offsetWidth}px,${newHeight}px,0)` },
399
+ {
400
+ clipPath: `polygon(0 0,100% 0,100% ${newHeight}px,0 ${newHeight}px)`,
401
+ },
400
402
  this.duration
401
403
  ),
402
404
  ])
403
405
  .catch(noop)
404
- .then(() => css(el, { clip: '' }));
406
+ .then(() => css(el, { clipPath: '' }));
405
407
  },
406
408
 
407
409
  getDropdown(el) {
@@ -93,22 +93,6 @@ export default {
93
93
  },
94
94
  },
95
95
 
96
- {
97
- name: 'touchstart',
98
-
99
- passive: true,
100
-
101
- el() {
102
- return this.panel;
103
- },
104
-
105
- handler({ targetTouches }) {
106
- if (targetTouches.length === 1) {
107
- this.clientY = targetTouches[0].clientY;
108
- }
109
- },
110
- },
111
-
112
96
  {
113
97
  name: 'touchmove',
114
98
 
@@ -124,33 +108,6 @@ export default {
124
108
  },
125
109
  },
126
110
 
127
- {
128
- name: 'touchmove',
129
-
130
- passive: false,
131
-
132
- el() {
133
- return this.panel;
134
- },
135
-
136
- handler(e) {
137
- if (e.targetTouches.length !== 1) {
138
- return;
139
- }
140
-
141
- const clientY = e.targetTouches[0].clientY - this.clientY;
142
- const { scrollTop, scrollHeight, clientHeight } = this.panel;
143
-
144
- if (
145
- clientHeight >= scrollHeight ||
146
- (scrollTop === 0 && clientY > 0) ||
147
- (scrollHeight - scrollTop <= clientHeight && clientY < 0)
148
- ) {
149
- e.cancelable && e.preventDefault();
150
- }
151
- },
152
- },
153
-
154
111
  {
155
112
  name: 'show',
156
113
 
@@ -162,7 +119,6 @@ export default {
162
119
  addClass(parent(this.panel), this.clsMode);
163
120
  }
164
121
 
165
- css(document.documentElement, 'overflowY', this.overlay ? 'hidden' : '');
166
122
  addClass(document.body, this.clsContainer, this.clsFlip);
167
123
  css(document.body, 'touch-action', 'pan-y pinch-zoom');
168
124
  css(this.$el, 'display', 'block');
@@ -170,7 +126,7 @@ export default {
170
126
  addClass(
171
127
  this.panel,
172
128
  this.clsSidebarAnimation,
173
- this.mode !== 'reveal' ? this.clsMode : ''
129
+ this.mode === 'reveal' ? '' : this.clsMode
174
130
  );
175
131
 
176
132
  height(document.body); // force reflow
@@ -207,8 +163,6 @@ export default {
207
163
  removeClass(this.$el, this.clsOverlay);
208
164
  css(this.$el, 'display', '');
209
165
  removeClass(document.body, this.clsContainer, this.clsFlip);
210
-
211
- css(document.documentElement, 'overflowY', '');
212
166
  },
213
167
  },
214
168
 
@@ -48,7 +48,7 @@ export default {
48
48
 
49
49
  watch(connects) {
50
50
  if (this.swiping) {
51
- css(connects, 'touch-action', 'pan-y pinch-zoom');
51
+ css(connects, 'touchAction', 'pan-y pinch-zoom');
52
52
  }
53
53
 
54
54
  const index = this.index();
@@ -2,6 +2,7 @@ import {
2
2
  $,
3
3
  addClass,
4
4
  append,
5
+ apply,
5
6
  attr,
6
7
  css,
7
8
  endsWith,
@@ -15,6 +16,7 @@ import {
15
16
  pointerDown,
16
17
  pointerUp,
17
18
  removeClass,
19
+ scrollParents,
18
20
  toFloat,
19
21
  width,
20
22
  within,
@@ -121,17 +123,26 @@ export default {
121
123
  self: true,
122
124
 
123
125
  handler() {
124
- const docEl = document.documentElement;
126
+ once(
127
+ this.$el,
128
+ 'hide',
129
+ on(document, 'focusin', (e) => {
130
+ if (!within(e.target, this.panel)) {
131
+ this.$el.focus();
132
+ }
133
+ })
134
+ );
125
135
 
126
- if (width(window) > docEl.clientWidth && this.overlay) {
127
- css(document.body, 'overflowY', 'scroll');
136
+ if (this.overlay) {
137
+ once(this.$el, 'hide', preventOverscroll(this.$el));
138
+ once(this.$el, 'hide', preventBackgroundScroll());
128
139
  }
129
140
 
130
141
  if (this.stack) {
131
142
  css(this.$el, 'zIndex', toFloat(css(this.$el, 'zIndex')) + active.length);
132
143
  }
133
144
 
134
- addClass(docEl, this.clsPage);
145
+ addClass(document.documentElement, this.clsPage);
135
146
 
136
147
  if (this.bgClose) {
137
148
  once(
@@ -273,3 +284,78 @@ function animate({ transitionElement, _toggle }) {
273
284
  function toMs(time) {
274
285
  return time ? (endsWith(time, 'ms') ? toFloat(time) : toFloat(time) * 1000) : 0;
275
286
  }
287
+
288
+ export function preventOverscroll(el) {
289
+ if (CSS.supports('overscroll-behavior', 'contain')) {
290
+ const elements = filterChildren(el, (child) => /auto|scroll/.test(css(child, 'overflow')));
291
+ css(elements, 'overscrollBehavior', 'contain');
292
+ return () => css(elements, 'overscrollBehavior', '');
293
+ }
294
+
295
+ let startClientY;
296
+
297
+ const events = [
298
+ on(
299
+ el,
300
+ 'touchstart',
301
+ ({ targetTouches }) => {
302
+ if (targetTouches.length === 1) {
303
+ startClientY = targetTouches[0].clientY;
304
+ }
305
+ },
306
+ { passive: true }
307
+ ),
308
+
309
+ on(
310
+ el,
311
+ 'touchmove',
312
+ (e) => {
313
+ if (e.targetTouches.length !== 1) {
314
+ return;
315
+ }
316
+
317
+ let [scrollParent] = scrollParents(e.target, /auto|scroll/);
318
+ if (!within(scrollParent, el)) {
319
+ scrollParent = el;
320
+ }
321
+
322
+ const clientY = e.targetTouches[0].clientY - startClientY;
323
+ const { scrollTop, scrollHeight, clientHeight } = scrollParent;
324
+
325
+ if (
326
+ clientHeight >= scrollHeight ||
327
+ (scrollTop === 0 && clientY > 0) ||
328
+ (scrollHeight - scrollTop <= clientHeight && clientY < 0)
329
+ ) {
330
+ e.cancelable && e.preventDefault();
331
+ }
332
+ },
333
+ { passive: false }
334
+ ),
335
+ ];
336
+
337
+ return () => events.forEach((fn) => fn());
338
+ }
339
+
340
+ export function preventBackgroundScroll() {
341
+ const { body, documentElement } = document;
342
+ css(body, {
343
+ overflowY: width(window) > documentElement.clientWidth ? 'scroll' : '',
344
+ touchAction: 'none',
345
+ });
346
+ css(documentElement, 'overflowY', 'hidden');
347
+ return () => {
348
+ css(documentElement, 'overflowY', '');
349
+ css(body, { overflowY: '', touchAction: '' });
350
+ };
351
+ }
352
+
353
+ function filterChildren(el, fn) {
354
+ const children = [];
355
+ apply(el, (node) => {
356
+ if (fn(node)) {
357
+ children.push(node);
358
+ }
359
+ });
360
+ return children;
361
+ }
@@ -1,4 +1,13 @@
1
- import { flipPosition, getCssVar, includes, isRtl, positionAt, toPx } from 'uikit-util';
1
+ import {
2
+ css,
3
+ dimensions,
4
+ flipPosition,
5
+ getCssVar,
6
+ includes,
7
+ isRtl,
8
+ positionAt,
9
+ toPx,
10
+ } from 'uikit-util';
2
11
 
3
12
  export default {
4
13
  props: {
@@ -15,23 +24,17 @@ export default {
15
24
 
16
25
  connected() {
17
26
  this.pos = this.$props.pos.split('-').concat('center').slice(0, 2);
18
- this.axis = includes(['top', 'bottom'], this.pos[0]) ? 'y' : 'x';
27
+ [this.dir, this.align] = this.pos;
28
+ this.axis = includes(['top', 'bottom'], this.dir) ? 'y' : 'x';
19
29
  },
20
30
 
21
31
  methods: {
22
32
  positionAt(element, target, boundary) {
23
- const [dir, align] = this.pos;
24
-
25
- let offset = toPx(
26
- this.offset === false ? getCssVar('position-offset', element) : this.offset,
27
- this.axis === 'x' ? 'width' : 'height',
28
- element
29
- );
30
- offset = [includes(['left', 'top'], dir) ? -offset : +offset, 0];
33
+ let offset = [this.getPositionOffset(element), this.getShiftOffset(element)];
31
34
 
32
35
  const attach = {
33
- element: [flipPosition(dir), align],
34
- target: [dir, align],
36
+ element: [flipPosition(this.dir), this.align],
37
+ target: [this.dir, this.align],
35
38
  };
36
39
 
37
40
  if (this.axis === 'y') {
@@ -41,13 +44,41 @@ export default {
41
44
  offset = offset.reverse();
42
45
  }
43
46
 
47
+ // Ensure none positioned element does not generate scrollbars
48
+ const elDim = dimensions(element);
49
+ css(element, { top: -elDim.height, left: -elDim.width });
50
+
44
51
  positionAt(element, target, {
45
52
  attach,
46
53
  offset,
47
54
  boundary,
48
55
  flip: this.flip,
49
- viewportOffset: toPx(getCssVar('position-viewport-offset', element)),
56
+ viewportOffset: this.getViewportOffset(element),
50
57
  });
51
58
  },
59
+
60
+ getPositionOffset(element) {
61
+ return (
62
+ toPx(
63
+ this.offset === false ? getCssVar('position-offset', element) : this.offset,
64
+ this.axis === 'x' ? 'width' : 'height',
65
+ element
66
+ ) * (includes(['left', 'top'], this.dir) ? -1 : 1)
67
+ );
68
+ },
69
+
70
+ getShiftOffset(element) {
71
+ return includes(['center', 'justify', 'stretch'], this.align)
72
+ ? 0
73
+ : toPx(
74
+ getCssVar('position-shift-offset', element),
75
+ this.axis === 'y' ? 'width' : 'height',
76
+ element
77
+ ) * (includes(['left', 'top'], this.align) ? 1 : -1);
78
+ },
79
+
80
+ getViewportOffset(element) {
81
+ return toPx(getCssVar('position-viewport-offset', element));
82
+ },
52
83
  },
53
84
  };