UNPKG

quasar

Version:

Build high-performance VueJS user interfaces (SPA, PWA, SSR, Mobile and Desktop) in record time

286 lines (238 loc) 7.36 kB
import { h, ref, reactive, computed, watch, provide, onUnmounted, getCurrentInstance } from 'vue' import { isRuntimeSsrPreHydration } from '../../plugins/platform/Platform.js' import QScrollObserver from '../scroll-observer/QScrollObserver.js' import QResizeObserver from '../resize-observer/QResizeObserver.js' import { createComponent } from '../../utils/private.create/create.js' import { getScrollbarWidth } from '../../utils/scroll/scroll.js' import { hMergeSlot } from '../../utils/private.render/render.js' import { layoutKey } from '../../utils/private.symbols/symbols.js' export default createComponent({ name: 'QLayout', props: { container: Boolean, view: { type: String, default: 'hhh lpr fff', validator: v => /^(h|l)h(h|r) lpr (f|l)f(f|r)$/.test(v.toLowerCase()) }, onScroll: Function, onScrollHeight: Function, onResize: Function }, setup(props, { slots, emit }) { const { proxy: { $q } } = getCurrentInstance() const rootRef = ref(null) // page related const height = ref($q.screen.height) const width = ref(props.container === true ? 0 : $q.screen.width) const scroll = ref({ position: 0, direction: 'down', inflectionPoint: 0 }) // container only prop const containerHeight = ref(0) const scrollbarWidth = ref( isRuntimeSsrPreHydration.value === true ? 0 : getScrollbarWidth() ) const classes = computed( () => 'q-layout q-layout--' + (props.container === true ? 'containerized' : 'standard') ) const style = computed(() => props.container === false ? { minHeight: $q.screen.height + 'px' } : null ) // used by container only const targetStyle = computed(() => scrollbarWidth.value !== 0 ? { [$q.lang.rtl === true ? 'left' : 'right']: `${scrollbarWidth.value}px` } : null ) const targetChildStyle = computed(() => scrollbarWidth.value !== 0 ? { [$q.lang.rtl === true ? 'right' : 'left']: 0, [$q.lang.rtl === true ? 'left' : 'right']: `-${scrollbarWidth.value}px`, width: `calc(100% + ${scrollbarWidth.value}px)` } : null ) function onPageScroll(data) { if (props.container === true || document.qScrollPrevented !== true) { const info = { position: data.position.top, direction: data.direction, directionChanged: data.directionChanged, inflectionPoint: data.inflectionPoint.top, delta: data.delta.top } scroll.value = info if (props.onScroll !== void 0) emit('scroll', info) } } function onPageResize(data) { const { height: newHeight, width: newWidth } = data let resized = false if (height.value !== newHeight) { resized = true height.value = newHeight if (props.onScrollHeight !== void 0) emit('scrollHeight', newHeight) updateScrollbarWidth() } if (width.value !== newWidth) { resized = true width.value = newWidth } if (resized === true && props.onResize !== void 0) { emit('resize', data) } } function onContainerResize({ height: newHeight }) { if (containerHeight.value !== newHeight) { containerHeight.value = newHeight updateScrollbarWidth() } } function updateScrollbarWidth() { if (props.container === true) { const newWidth = height.value > containerHeight.value ? getScrollbarWidth() : 0 if (scrollbarWidth.value !== newWidth) { scrollbarWidth.value = newWidth } } } let animateTimer = null const $layout = { instances: {}, view: computed(() => props.view), isContainer: computed(() => props.container), rootRef, height, containerHeight, scrollbarWidth, totalWidth: computed(() => width.value + scrollbarWidth.value), rows: computed(() => { const rows = props.view.toLowerCase().split(' ') return { top: rows[0].split(''), middle: rows[1].split(''), bottom: rows[2].split('') } }), header: reactive({ size: 0, offset: 0, space: false }), right: reactive({ size: 300, offset: 0, space: false }), footer: reactive({ size: 0, offset: 0, space: false }), left: reactive({ size: 300, offset: 0, space: false }), scroll, animate() { if (animateTimer !== null) { clearTimeout(animateTimer) } else { document.body.classList.add('q-body--layout-animate') } animateTimer = setTimeout(() => { animateTimer = null document.body.classList.remove('q-body--layout-animate') }, 155) }, update(part, prop, val) { $layout[part][prop] = val } } provide(layoutKey, $layout) // prevent scrollbar flicker while resizing window height // if no page scrollbar is already present if (__QUASAR_SSR_SERVER__ !== true && getScrollbarWidth() > 0) { let timer = null const el = document.body function restoreScrollbar() { timer = null el.classList.remove('hide-scrollbar') } function hideScrollbar() { if (timer === null) { // if it has no scrollbar then there's nothing to do if (el.scrollHeight > $q.screen.height) return el.classList.add('hide-scrollbar') } else { clearTimeout(timer) } timer = setTimeout(restoreScrollbar, 300) } function updateScrollEvent(action) { if (timer !== null && action === 'remove') { clearTimeout(timer) restoreScrollbar() } window[`${action}EventListener`]('resize', hideScrollbar) } watch( () => (props.container !== true ? 'add' : 'remove'), updateScrollEvent ) if (props.container !== true) updateScrollEvent('add') onUnmounted(() => { updateScrollEvent('remove') }) } return () => { const content = hMergeSlot(slots.default, [ h(QScrollObserver, { onScroll: onPageScroll }), h(QResizeObserver, { onResize: onPageResize }) ]) const layout = h( 'div', { class: classes.value, style: style.value, ref: props.container === true ? void 0 : rootRef, tabindex: -1 }, content ) if (props.container === true) { return h( 'div', { class: 'q-layout-container overflow-hidden', ref: rootRef }, [ h(QResizeObserver, { onResize: onContainerResize }), h( 'div', { class: 'absolute-full', style: targetStyle.value }, [ h( 'div', { class: 'scroll', style: targetChildStyle.value }, [layout] ) ] ) ] ) } return layout } } })