vuikit
Version:
A responsive Vue UI library for web site interfaces based on UIkit
300 lines (292 loc) • 9.11 kB
JavaScript
/**
* Vuikit 0.8.10
* (c) 2018 Miljan Aleksic
* @license MIT
**/
/* Substantial part of the code is adapted from UIkit,
Copyright (c) 2013-2018 YOOtheme GmbH, getuikit.com */
import { $ } from './util/core';
import { css } from './util/style';
import { attr } from './util/attr';
import { warn } from './util/debug';
import { query } from './util/selector';
import { toMedia } from './util/misc';
import { fastdom } from './util/fastdom';
import { Animation } from './util/animation';
import { after, remove } from './util/dom';
import { within, isVisible } from './util/filter';
import { filterOutTextNodes } from './util/vue';
import { offset, height } from './util/dimensions';
import { assign, isNumeric, isString, toFloat, noop } from './util/lang';
import { hasClass, addClass, removeClass, toggleClass, replaceClass } from './util/class';
import MixinEvents from './mixins/events';
import MixinFastdom from './mixins/fastdom';
var ACTIVE = 'active';
var INACTIVE = 'inactive';
var constants = /*#__PURE__*/Object.freeze({
ACTIVE: ACTIVE,
INACTIVE: INACTIVE
});
var sticky = {
name: 'VkSticky',
abstract: true,
mixins: [MixinEvents, MixinFastdom],
props: {
top: {
type: [Number, String],
default: 0
},
bottom: {
type: [Boolean, String],
default: false
},
offset: {
type: Number,
default: 0
},
widthElement: {
default: false
},
animation: {
type: String,
default: ''
},
showOnUp: {
type: Boolean,
default: false
},
media: {
type: [Number, String]
},
selTarget: {
type: String
},
target: {
type: [Number, Boolean],
default: false
}
},
classMapping: {
clsFixed: 'uk-sticky-fixed',
clsBelow: 'uk-sticky-below',
clsActive: 'uk-active',
clsInactive: ''
},
data: function () { return ({
isActive: false
}); },
computed: {
outerHeight: function outerHeight () {
return (this.isActive ? this.$refs.placeholder : this.$el).offsetHeight
},
$selTarget: function $selTarget () {
return this.selTarget
? $(this.selTarget, this.$el)
: this.$el
}
},
fastdom: [
{
write: function write () {
var ref = this.$refs;
var placeholder = ref.placeholder;
var widthElement = ref.widthElement;
var outerHeight = (this.isActive ? placeholder : this.$el).offsetHeight;
css(placeholder, assign(
{height: css(this.$el, 'position') !== 'absolute' ? outerHeight : ''},
css(this.$el, ['marginTop', 'marginBottom', 'marginLeft', 'marginRight'])
));
if (!within(placeholder, document)) {
after(this.$el, placeholder);
attr(placeholder, 'hidden', '');
}
attr(widthElement, 'hidden', null);
this.width = widthElement.offsetWidth;
attr(widthElement, 'hidden', this.isActive ? null : '');
this.topOffset = offset(this.isActive ? placeholder : this.$el).top;
this.bottomOffset = this.topOffset + outerHeight;
var bottom = parseProp('bottom', this);
this.stickAt = Math.max(toFloat(parseProp('top', this)), this.topOffset) - this.offset;
this.stickUntil = bottom && bottom - outerHeight;
this.inactive = this.media && !window.matchMedia(toMedia(this.media)).matches;
if (this.isActive) {
this.update();
}
},
events: ['load', 'resize']
},
{
read: function read (_, ref) {
var scrollY = ref.scrollY; if ( scrollY === void 0 ) scrollY = window.pageYOffset;
this.scroll = scrollY;
return {
scroll: scrollY,
visible: isVisible(this.$el)
}
},
write: function write (ref, ref$1) {
var this$1 = this;
var visible = ref.visible;
var scroll = ref.scroll;
if ( ref$1 === void 0 ) ref$1 = {};
var dir = ref$1.dir;
if (scroll < 0 || !visible || this.disabled || this.showOnUp && !dir) {
return
}
if (this.inactive ||
scroll < this.stickAt ||
this.showOnUp && (scroll <= this.stickAt || dir === 'down' || dir === 'up' && !this.isActive && scroll <= this.bottomOffset)
) {
if (!this.isActive) {
return
}
this.isActive = false;
if (this.animation && scroll > this.topOffset) {
Animation.cancel(this.$el);
Animation.out(this.$el, ("uk-animation-" + (this.animation))).then(function () { return this$1.hide(); }, noop);
} else {
this.hide();
}
} else if (this.isActive) {
this.update();
} else if (this.animation) {
Animation.cancel(this.$el);
this.show();
Animation.in(this.$el, ("uk-animation-" + (this.animation))).catch(noop);
} else {
this.show();
}
},
events: ['scroll']
}
],
methods: {
show: function show () {
this.isActive = true;
this.update();
attr(this.$refs.placeholder, 'hidden', null);
},
hide: function hide () {
var ref = this.$options.classMapping;
var clsFixed = ref.clsFixed;
var clsBelow = ref.clsBelow;
var clsActive = ref.clsActive;
if (!this.isActive || hasClass(this.$selTarget, clsActive)) {
this.$emit(INACTIVE);
}
removeClass(this.$el, clsFixed, clsBelow);
css(this.$el, { position: '', top: '', width: '' });
attr(this.$refs.placeholder, 'hidden', '');
},
update: function update () {
var ref = this.$options.classMapping;
var clsFixed = ref.clsFixed;
var clsBelow = ref.clsBelow;
var clsActive = ref.clsActive;
var active = this.stickAt !== 0 || this.scroll > this.stickAt;
var top = Math.max(0, this.offset);
if (this.stickUntil && this.scroll > this.stickUntil - this.offset) {
top = this.stickUntil - this.scroll;
}
css(this.$el, {
position: 'fixed',
top: (top + "px"),
width: this.width
});
if (hasClass(this.$selTarget, clsActive)) {
if (!active) {
this.$emit(INACTIVE);
}
} else if (active) {
this.$emit(ACTIVE);
}
toggleClass(this.$el, clsBelow, this.scroll > this.bottomOffset);
addClass(this.$el, clsFixed);
}
},
created: function created () {
var this$1 = this;
var ref = this.$options.classMapping;
var clsActive = ref.clsActive;
var clsInactive = ref.clsInactive;
this.$on(ACTIVE, function () { return replaceClass(this$1.$selTarget, clsInactive, clsActive); });
this.$on(INACTIVE, function () { return replaceClass(this$1.$selTarget, clsActive, clsInactive); });
},
mounted: function mounted () {
addClass(this.$el, 'uk-sticky');
this.$refs.placeholder = $('<div class="uk-sticky-placeholder"></div>');
this.$refs.widthElement = (this.widthElement && query(this.widthElement)) || this.$refs.placeholder;
if (!this.isActive) {
this.hide();
}
},
domReady: function domReady () {
var this$1 = this;
if (!(this.target && location.hash && window.pageYOffset > 0)) {
return
}
var target = $(location.hash);
if (target) {
fastdom.read(function () {
var ref = offset(target);
var top = ref.top;
var elTop = offset(this$1.$el).top;
var elHeight = this$1.$el.offsetHeight;
if (elTop + elHeight >= top && elTop <= top + target.offsetHeight) {
window.scrollTo(0, top - elHeight - this$1.target - this$1.offset);
}
});
}
},
beforeDestroy: function beforeDestroy () {
var ref = this.$options.classMapping;
var clsInactive = ref.clsInactive;
if (this.isActive) {
this.isActive = false;
this.hide();
removeClass(this.$selTarget, clsInactive);
}
remove(this.$refs.placeholder);
this.$refs.placeholder = null;
this.$refs.widthElement = null;
},
render: function render (h) {
var children = this.$slots.default;
if (!children) {
return
}
children = filterOutTextNodes(children);
if (!children.length) {
return
}
if (process.env.NODE_ENV !== 'production' && children.length > 1) {
warn('vk-sticky can only be used on a single element', this.$parent);
}
return children[0]
}
}
function parseProp (prop, ref) {
var $props = ref.$props;
var $el = ref.$el;
var propOffset = ref[(prop + "Offset")];
var value = $props[prop];
value = isString(value) && value === ''
? true
: value;
if (!value) {
return
}
if (isNumeric(value)) {
return propOffset + toFloat(value)
} else if (isString(value) && /^-?\d+vh$/.test(value)) {
return height(window) * toFloat(value) / 100
} else {
var el = value === true ? $el.parentNode : query(value, $el);
if (el) {
return offset(el).top + el.offsetHeight
}
}
}
var index = { constants: constants }
export default index;
export { sticky as Sticky };