UNPKG

@ishitatsuyuki/oruga-next

Version:

UI components for Vue.js and CSS framework agnostic

331 lines (325 loc) 12 kB
'use strict'; var vue = require('vue'); var helpers = require('./helpers.js'); var config = require('./config.js'); var BaseComponentMixin = require('./BaseComponentMixin-a03c02e3.js'); var MatchMediaMixin = require('./MatchMediaMixin-08658b15.js'); /** * A sidebar to use as left/right overlay or static * @displayName Sidebar * @style _sidebar.scss */ var script = vue.defineComponent({ name: 'OSidebar', mixins: [BaseComponentMixin.BaseComponentMixin, MatchMediaMixin.MatchMediaMixin], configField: 'sidebar', emits: ['update:open', 'close'], props: { /** To control the behaviour of the sidebar programmatically, use the v-model:open to make it two-way binding */ open: Boolean, /** * Color of the sidebar, optional * @values primary, info, success, warning, danger, and any other custom color */ variant: [String, Object], /** Show an overlay like modal */ overlay: Boolean, /** * Skeleton position in relation to the window * @values fixed, absolute, static */ position: { type: String, default: () => { return helpers.getValueByPath(config.getOptions(), 'sidebar.position', 'fixed'); }, validator: (value) => { return [ 'fixed', 'absolute', 'static' ].indexOf(value) >= 0; } }, /** Show sidebar in fullheight */ fullheight: Boolean, /** Show sidebar in fullwidth */ fullwidth: Boolean, /** Show the sidebar on right */ right: Boolean, /** * Custom layout on mobile * @values fullwidth, reduced, hidden */ mobile: { type: String, validator: (value) => { return [ '', 'fullwidth', 'reduced', 'hidden' ].indexOf(value) >= 0; } }, /** Show a small sidebar */ reduce: Boolean, /** Expand sidebar on hover when reduced or mobile is reduce */ expandOnHover: Boolean, /** Expand sidebar on hover with fixed position when reduced or mobile is reduce */ expandOnHoverFixed: Boolean, /** * Sidebar cancel options * @values true, false, 'escape', 'outside' */ canCancel: { type: [Array, Boolean], default: () => { return helpers.getValueByPath(config.getOptions(), 'sidebar.canCancel', ['escape', 'outside']); } }, /** * Callback on cancel */ onCancel: { type: Function, default: () => { } }, scroll: { type: String, default: () => { return helpers.getValueByPath(config.getOptions(), 'sidebar.scroll', 'clip'); }, validator: (value) => { return [ 'clip', 'keep' ].indexOf(value) >= 0; } }, rootClass: [String, Function, Array], overlayClass: [String, Function, Array], contentClass: [String, Function, Array], fixedClass: [String, Function, Array], staticClass: [String, Function, Array], absoluteClass: [String, Function, Array], fullheightClass: [String, Function, Array], fullwidthClass: [String, Function, Array], rightClass: [String, Function, Array], reduceClass: [String, Function, Array], expandOnHoverClass: [String, Function, Array], expandOnHoverFixedClass: [String, Function, Array], variantClass: [String, Function, Array], mobileClass: [String, Function, Array], }, data() { return { isOpen: this.open, transitionName: null, animating: true, savedScrollTop: null }; }, computed: { rootClasses() { return [ this.computedClass('rootClass', 'o-side'), { [this.computedClass('mobileClass', 'o-side--mobile')]: this.isMatchMedia }, ]; }, overlayClasses() { return [ this.computedClass('overlayClass', 'o-side__overlay') ]; }, contentClasses() { return [ this.computedClass('contentClass', 'o-side__content'), { [this.computedClass('variantClass', 'o-side__content--', this.variant)]: this.variant }, { [this.computedClass('fixedClass', 'o-side__content--fixed')]: this.isFixed }, { [this.computedClass('staticClass', 'o-side__content--static')]: this.isStatic }, { [this.computedClass('absoluteClass', 'o-side__content--absolute')]: this.isAbsolute }, { [this.computedClass('fullheightClass', 'o-side__content--fullheight')]: this.fullheight }, { [this.computedClass('fullwidthClass', 'o-side__content--fullwidth')]: this.fullwidth || (this.mobile === 'fullwidth' && this.isMatchMedia) }, { [this.computedClass('rightClass', 'o-side__content--right')]: this.right }, { [this.computedClass('reduceClass', 'o-side__content--mini')]: this.reduce || (this.mobile === 'reduced' && this.isMatchMedia) }, { [this.computedClass('expandOnHoverClass', 'o-side__content--mini-expand')]: (this.expandOnHover && this.mobile !== 'fullwidth') }, { [this.computedClass('expandOnHoverFixedClass', 'o-side__content--expand-mini-hover-fixed')]: (this.expandOnHover && this.expandOnHoverFixed && this.mobile !== 'fullwidth') } ]; }, cancelOptions() { return typeof this.canCancel === 'boolean' ? this.canCancel ? helpers.getValueByPath(config.getOptions(), 'sidebar.canCancel', ['escape', 'outside']) : [] : this.canCancel; }, isStatic() { return this.position === 'static'; }, isFixed() { return this.position === 'fixed'; }, isAbsolute() { return this.position === 'absolute'; }, hideOnMobile() { return this.mobile === 'hidden' && this.isMatchMedia; } }, watch: { open: { handler(value) { this.isOpen = value; if (this.overlay) { this.handleScroll(); } const open = this.right ? !value : value; this.transitionName = !open ? 'slide-prev' : 'slide-next'; }, immediate: true } }, methods: { /** * Keypress event that is bound to the document. */ keyPress({ key }) { if (this.isFixed) { if (this.isOpen && (key === 'Escape' || key === 'Esc')) this.cancel('escape'); } }, /** * Close the Sidebar if canCancel and call the onCancel prop (function). */ cancel(method) { if (this.cancelOptions.indexOf(method) < 0) return; if (this.isStatic) return; this.onCancel.apply(null, arguments); this.close(); }, /** * Call the onCancel prop (function) and emit events */ close() { this.isOpen = false; this.$emit('close'); this.$emit('update:open', false); }, /** * Close fixed sidebar if clicked outside. */ clickedOutside(event) { if (!this.isFixed || !this.isOpen || this.animating) { return; } if (!event.composedPath().includes(this.$refs.sidebarContent)) { this.cancel('outside'); } }, /** * Transition before-enter hook */ beforeEnter() { this.animating = true; }, /** * Transition after-leave hook */ afterEnter() { this.animating = false; }, handleScroll() { if (typeof window === 'undefined') return; if (this.scroll === 'clip') { if (this.open) { document.documentElement.classList.add('o-clipped'); } else { document.documentElement.classList.remove('o-clipped'); } return; } this.savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop; if (this.open) { document.body.classList.add('o-noscroll'); } else { document.body.classList.remove('o-noscroll'); } if (this.open) { document.body.style.top = `-${this.savedScrollTop}px`; return; } document.documentElement.scrollTop = this.savedScrollTop; document.body.style.top = null; this.savedScrollTop = null; } }, created() { if (typeof window !== 'undefined') { document.addEventListener('keyup', this.keyPress); document.addEventListener('click', this.clickedOutside); } }, mounted() { if (typeof window !== 'undefined') { if (this.isFixed) { document.body.appendChild(this.$el); } if (this.overlay && this.open) { this.handleScroll(); } } }, beforeUnmount() { if (typeof window !== 'undefined') { document.removeEventListener('keyup', this.keyPress); document.removeEventListener('click', this.clickedOutside); if (this.overlay) { // reset scroll document.documentElement.classList.remove('o-clipped'); const savedScrollTop = !this.savedScrollTop ? document.documentElement.scrollTop : this.savedScrollTop; document.body.classList.remove('o-noscroll'); document.documentElement.scrollTop = savedScrollTop; document.body.style.top = null; } } if (this.isFixed) { helpers.removeElement(this.$el); } } }); function render(_ctx, _cache, $props, $setup, $data, $options) { return vue.withDirectives((vue.openBlock(), vue.createBlock("div", { class: _ctx.rootClasses }, [_ctx.overlay && _ctx.isOpen ? (vue.openBlock(), vue.createBlock("div", { key: 0, class: _ctx.overlayClasses }, null, 2 /* CLASS */ )) : vue.createCommentVNode("v-if", true), vue.createVNode(vue.Transition, { name: _ctx.transitionName, "onBefore-enter": _ctx.beforeEnter, "onAfter-enter": _ctx.afterEnter }, { default: vue.withCtx(() => [vue.withDirectives(vue.createVNode("div", { ref: "sidebarContent", class: _ctx.contentClasses }, [vue.renderSlot(_ctx.$slots, "default")], 2 /* CLASS */ ), [[vue.vShow, _ctx.isOpen]])]), _: 3 }, 8 /* PROPS */ , ["name", "onBefore-enter", "onAfter-enter"])], 2 /* CLASS */ )), [[vue.vShow, !_ctx.hideOnMobile]]); } script.render = render; script.__file = "src/components/sidebar/Sidebar.vue"; exports.script = script;