UNPKG

quasar

Version:

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

228 lines (187 loc) 5.36 kB
import { ref, watch, onMounted, onBeforeUnmount, nextTick, getCurrentInstance } from 'vue' import { clearSelection } from '../../utils/private.selection/selection.js' import { addEvt, cleanEvt, prevent } from '../../utils/event/event.js' import { isKeyCode } from '../../utils/private.keyboard/key-composition.js' export const useAnchorStaticProps = { /* SSR does not know about Element */ target: __QUASAR_SSR_SERVER__ ? { default: true } : { type: [ Boolean, String, Element ], default: true }, noParentEvent: Boolean } export const useAnchorProps = { ...useAnchorStaticProps, contextMenu: Boolean } export default function ({ showing, avoidEmit, // required for QPopupProxy (true) configureAnchorEl // optional }) { const { props, proxy, emit } = getCurrentInstance() const anchorEl = ref(null) let touchTimer = null function canShow (evt) { // abort with no parent configured or on multi-touch return anchorEl.value === null ? false : (evt === void 0 || evt.touches === void 0 || evt.touches.length <= 1) } const anchorEvents = {} if (configureAnchorEl === void 0) { // default configureAnchorEl is designed for // QMenu & QPopupProxy (which is why it's handled here) Object.assign(anchorEvents, { hide (evt) { proxy.hide(evt) }, toggle (evt) { proxy.toggle(evt) evt.qAnchorHandled = true }, toggleKey (evt) { isKeyCode(evt, 13) === true && anchorEvents.toggle(evt) }, contextClick (evt) { proxy.hide(evt) prevent(evt) nextTick(() => { proxy.show(evt) evt.qAnchorHandled = true }) }, prevent, mobileTouch (evt) { anchorEvents.mobileCleanup(evt) if (canShow(evt) !== true) { return } proxy.hide(evt) anchorEl.value.classList.add('non-selectable') const target = evt.target addEvt(anchorEvents, 'anchor', [ [ target, 'touchmove', 'mobileCleanup', 'passive' ], [ target, 'touchend', 'mobileCleanup', 'passive' ], [ target, 'touchcancel', 'mobileCleanup', 'passive' ], [ anchorEl.value, 'contextmenu', 'prevent', 'notPassive' ] ]) touchTimer = setTimeout(() => { touchTimer = null proxy.show(evt) evt.qAnchorHandled = true }, 300) }, mobileCleanup (evt) { anchorEl.value.classList.remove('non-selectable') if (touchTimer !== null) { clearTimeout(touchTimer) touchTimer = null } if (showing.value === true && evt !== void 0) { clearSelection() } } }) configureAnchorEl = function (context = props.contextMenu) { if (props.noParentEvent === true || anchorEl.value === null) return let evts if (context === true) { if (proxy.$q.platform.is.mobile === true) { evts = [ [ anchorEl.value, 'touchstart', 'mobileTouch', 'passive' ] ] } else { evts = [ [ anchorEl.value, 'mousedown', 'hide', 'passive' ], [ anchorEl.value, 'contextmenu', 'contextClick', 'notPassive' ] ] } } else { evts = [ [ anchorEl.value, 'click', 'toggle', 'passive' ], [ anchorEl.value, 'keyup', 'toggleKey', 'passive' ] ] } addEvt(anchorEvents, 'anchor', evts) } } function unconfigureAnchorEl () { cleanEvt(anchorEvents, 'anchor') } function setAnchorEl (el) { anchorEl.value = el while (anchorEl.value.classList.contains('q-anchor--skip')) { anchorEl.value = anchorEl.value.parentNode } configureAnchorEl() } function pickAnchorEl () { if (props.target === false || props.target === '' || proxy.$el.parentNode === null) { anchorEl.value = null } else if (props.target === true) { setAnchorEl(proxy.$el.parentNode) } else { let el = props.target if (typeof props.target === 'string') { try { el = document.querySelector(props.target) } catch (err) { el = void 0 } } if (el !== void 0 && el !== null) { anchorEl.value = el.$el || el configureAnchorEl() } else { anchorEl.value = null console.error(`Anchor: target "${ props.target }" not found`) } } } watch(() => props.contextMenu, val => { if (anchorEl.value !== null) { unconfigureAnchorEl() configureAnchorEl(val) } }) watch(() => props.target, () => { if (anchorEl.value !== null) { unconfigureAnchorEl() } pickAnchorEl() }) watch(() => props.noParentEvent, val => { if (anchorEl.value !== null) { if (val === true) { unconfigureAnchorEl() } else { configureAnchorEl() } } }) onMounted(() => { pickAnchorEl() if (avoidEmit !== true && props.modelValue === true && anchorEl.value === null) { emit('update:modelValue', false) } }) onBeforeUnmount(() => { touchTimer !== null && clearTimeout(touchTimer) unconfigureAnchorEl() }) return { anchorEl, canShow, anchorEvents } }