UNPKG

vuikit

Version:

A responsive Vue UI library for web site interfaces based on UIkit

300 lines (292 loc) 9.11 kB
/** * 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 };