/*! * Quasar Framework v2.12.2 * (c) 2015-present Razvan Stoenescu * Released under the MIT License. */ (function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory(require('vue')) : typeof define === 'function' && define.amd ? define(['vue'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.Quasar = factory(global.Vue)); })(this, (function (vue) { 'use strict'; function injectProp (target, propName, get, set) { Object.defineProperty(target, propName, { get, set, enumerable: true }); return target } function injectMultipleProps (target, props) { for (const key in props) { injectProp(target, key, props[ key ]); } return target } /* eslint-disable no-useless-escape */ /** * __ QUASAR_SSR __ -> runs on SSR on client or server * __ QUASAR_SSR_SERVER __ -> runs on SSR on server * __ QUASAR_SSR_CLIENT __ -> runs on SSR on client * __ QUASAR_SSR_PWA __ -> built with SSR+PWA; may run on SSR on client or on PWA client * (needs runtime detection) */ const isRuntimeSsrPreHydration = vue.ref( false ); let iosCorrection; function getMatch (userAgent, platformMatch) { const match = /(edg|edge|edga|edgios)\/([\w.]+)/.exec(userAgent) || /(opr)[\/]([\w.]+)/.exec(userAgent) || /(vivaldi)[\/]([\w.]+)/.exec(userAgent) || /(chrome|crios)[\/]([\w.]+)/.exec(userAgent) || /(version)(applewebkit)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent) || /(webkit)[\/]([\w.]+).*(version)[\/]([\w.]+).*(safari)[\/]([\w.]+)/.exec(userAgent) || /(firefox|fxios)[\/]([\w.]+)/.exec(userAgent) || /(webkit)[\/]([\w.]+)/.exec(userAgent) || /(opera)(?:.*version|)[\/]([\w.]+)/.exec(userAgent) || []; return { browser: match[ 5 ] || match[ 3 ] || match[ 1 ] || '', version: match[ 2 ] || match[ 4 ] || '0', versionNumber: match[ 4 ] || match[ 2 ] || '0', platform: platformMatch[ 0 ] || '' } } function getPlatformMatch (userAgent) { return /(ipad)/.exec(userAgent) || /(ipod)/.exec(userAgent) || /(windows phone)/.exec(userAgent) || /(iphone)/.exec(userAgent) || /(kindle)/.exec(userAgent) || /(silk)/.exec(userAgent) || /(android)/.exec(userAgent) || /(win)/.exec(userAgent) || /(mac)/.exec(userAgent) || /(linux)/.exec(userAgent) || /(cros)/.exec(userAgent) // TODO: Remove BlackBerry detection. BlackBerry OS, BlackBerry 10, and BlackBerry PlayBook OS // is officially dead as of January 4, 2022 (https://www.blackberry.com/us/en/support/devices/end-of-life) || /(playbook)/.exec(userAgent) || /(bb)/.exec(userAgent) || /(blackberry)/.exec(userAgent) || [] } const hasTouch = 'ontouchstart' in window || window.navigator.maxTouchPoints > 0; function applyIosCorrection (is) { iosCorrection = { is: { ...is } }; delete is.mac; delete is.desktop; const platform = Math.min(window.innerHeight, window.innerWidth) > 414 ? 'ipad' : 'iphone'; Object.assign(is, { mobile: true, ios: true, platform, [ platform ]: true }); } function getPlatform (UA) { const userAgent = UA.toLowerCase(), platformMatch = getPlatformMatch(userAgent), matched = getMatch(userAgent, platformMatch), browser = {}; if (matched.browser) { browser[ matched.browser ] = true; browser.version = matched.version; browser.versionNumber = parseInt(matched.versionNumber, 10); } if (matched.platform) { browser[ matched.platform ] = true; } const knownMobiles = browser.android || browser.ios || browser.bb || browser.blackberry || browser.ipad || browser.iphone || browser.ipod || browser.kindle || browser.playbook || browser.silk || browser[ 'windows phone' ]; // These are all considered mobile platforms, meaning they run a mobile browser if (knownMobiles === true || userAgent.indexOf('mobile') > -1) { browser.mobile = true; if (browser.edga || browser.edgios) { browser.edge = true; matched.browser = 'edge'; } else if (browser.crios) { browser.chrome = true; matched.browser = 'chrome'; } else if (browser.fxios) { browser.firefox = true; matched.browser = 'firefox'; } } // If it's not mobile we should consider it's desktop platform, meaning it runs a desktop browser // It's a workaround for anonymized user agents // (browser.cros || browser.mac || browser.linux || browser.win) else { browser.desktop = true; } // Set iOS if on iPod, iPad or iPhone if (browser.ipod || browser.ipad || browser.iphone) { browser.ios = true; } if (browser[ 'windows phone' ]) { browser.winphone = true; delete browser[ 'windows phone' ]; } // TODO: The assumption about WebKit based browsers below is not completely accurate. // Google released Blink(a fork of WebKit) engine on April 3, 2013, which is really different than WebKit today. // Today, one might want to check for WebKit to deal with its bugs, which is used on all browsers on iOS, and Safari browser on all platforms. // Chrome, Opera 15+, Vivaldi and Safari are webkit based browsers if ( browser.chrome || browser.opr || browser.safari || browser.vivaldi // we expect unknown, non iOS mobile browsers to be webkit based || ( browser.mobile === true && browser.ios !== true && knownMobiles !== true ) ) { browser.webkit = true; } // TODO: (Qv3) rename the terms 'edge' to 'edge legacy'(or remove it) then 'edge chromium' to 'edge' to match with the known up-to-date terms // Microsoft Edge is the new Chromium-based browser. Microsoft Edge Legacy is the old EdgeHTML-based browser (EOL: March 9, 2021). if (browser.edg) { matched.browser = 'edgechromium'; browser.edgeChromium = true; } // Blackberry browsers are marked as Safari on BlackBerry if ((browser.safari && browser.blackberry) || browser.bb) { matched.browser = 'blackberry'; browser.blackberry = true; } // Playbook browsers are marked as Safari on Playbook if (browser.safari && browser.playbook) { matched.browser = 'playbook'; browser.playbook = true; } // Opera 15+ are identified as opr if (browser.opr) { matched.browser = 'opera'; browser.opera = true; } // Stock Android browsers are marked as Safari on Android. if (browser.safari && browser.android) { matched.browser = 'android'; browser.android = true; } // Kindle browsers are marked as Safari on Kindle if (browser.safari && browser.kindle) { matched.browser = 'kindle'; browser.kindle = true; } // Kindle Silk browsers are marked as Safari on Kindle if (browser.safari && browser.silk) { matched.browser = 'silk'; browser.silk = true; } if (browser.vivaldi) { matched.browser = 'vivaldi'; browser.vivaldi = true; } // Assign the name and platform variable browser.name = matched.browser; browser.platform = matched.platform; { if (userAgent.indexOf('electron') > -1) { browser.electron = true; } else if (document.location.href.indexOf('-extension://') > -1) { browser.bex = true; } else { if (window.Capacitor !== void 0) { browser.capacitor = true; browser.nativeMobile = true; browser.nativeMobileWrapper = 'capacitor'; } else if (window._cordovaNative !== void 0 || window.cordova !== void 0) { browser.cordova = true; browser.nativeMobile = true; browser.nativeMobileWrapper = 'cordova'; } if ( hasTouch === true && browser.mac === true && ( (browser.desktop === true && browser.safari === true) || ( browser.nativeMobile === true && browser.android !== true && browser.ios !== true && browser.ipad !== true ) ) ) { /* * Correction needed for iOS since the default * setting on iPad is to request desktop view; if we have * touch support and the user agent says it's a * desktop, we infer that it's an iPhone/iPad with desktop view * so we must fix the false positives */ applyIosCorrection(browser); } } } return browser } const userAgent = navigator.userAgent || navigator.vendor || window.opera; const ssrClient = { has: { touch: false, webStorage: false }, within: { iframe: false } }; // We export "client" for hydration error-free parts, // like touch directives who do not (and must NOT) wait // for the client takeover; // Do NOT import this directly in your app, unless you really know // what you are doing. const client = { userAgent, is: getPlatform(userAgent), has: { touch: hasTouch }, within: { iframe: window.self !== window.top } }; const Platform = { install (opts) { const { $q } = opts; if (isRuntimeSsrPreHydration.value === true) { // takeover should increase accuracy for // the rest of the props; we also avoid // hydration errors opts.onSSRHydrated.push(() => { Object.assign($q.platform, client); isRuntimeSsrPreHydration.value = false; iosCorrection = void 0; }); // we need to make platform reactive // for the takeover phase $q.platform = vue.reactive(this); } else { $q.platform = this; } } }; { // do not access window.localStorage without // devland actually using it as this will get // reported under "Cookies" in Google Chrome let hasWebStorage; injectProp(client.has, 'webStorage', () => { if (hasWebStorage !== void 0) { return hasWebStorage } try { if (window.localStorage) { hasWebStorage = true; return true } } catch (e) {} hasWebStorage = false; return false }); client.is.ios === true && window.navigator.vendor.toLowerCase().indexOf('apple') === -1; if (isRuntimeSsrPreHydration.value === true) { // must match with server-side before // client taking over in order to prevent // hydration errors Object.assign(Platform, client, iosCorrection, ssrClient); } else { Object.assign(Platform, client); } } var defineReactivePlugin = (state, plugin) => { const reactiveState = vue.reactive(state); for (const name in state) { injectProp( plugin, name, () => reactiveState[ name ], val => { reactiveState[ name ] = val; } ); } return plugin }; const listenOpts = { hasPassive: false, passiveCapture: true, notPassiveCapture: true }; try { const opts = Object.defineProperty({}, 'passive', { get () { Object.assign(listenOpts, { hasPassive: true, passive: { passive: true }, notPassive: { passive: false }, passiveCapture: { passive: true, capture: true }, notPassiveCapture: { passive: false, capture: true } }); } }); window.addEventListener('qtest', null, opts); window.removeEventListener('qtest', null, opts); } catch (e) {} function noop () {} function leftClick (e) { return e.button === 0 } function middleClick (e) { return e.button === 1 } function rightClick (e) { return e.button === 2 } function position (e) { if (e.touches && e.touches[ 0 ]) { e = e.touches[ 0 ]; } else if (e.changedTouches && e.changedTouches[ 0 ]) { e = e.changedTouches[ 0 ]; } else if (e.targetTouches && e.targetTouches[ 0 ]) { e = e.targetTouches[ 0 ]; } return { top: e.clientY, left: e.clientX } } function getEventPath (e) { if (e.path) { return e.path } if (e.composedPath) { return e.composedPath() } const path = []; let el = e.target; while (el) { path.push(el); if (el.tagName === 'HTML') { path.push(document); path.push(window); return path } el = el.parentElement; } } // Reasonable defaults const LINE_HEIGHT = 40, PAGE_HEIGHT = 800; function getMouseWheelDistance (e) { let x = e.deltaX, y = e.deltaY; if ((x || y) && e.deltaMode) { const multiplier = e.deltaMode === 1 ? LINE_HEIGHT : PAGE_HEIGHT; x *= multiplier; y *= multiplier; } if (e.shiftKey && !x) { [ y, x ] = [ x, y ]; } return { x, y } } function stop (e) { e.stopPropagation(); } function prevent (e) { e.cancelable !== false && e.preventDefault(); } function stopAndPrevent (e) { e.cancelable !== false && e.preventDefault(); e.stopPropagation(); } function preventDraggable (el, status) { if (el === void 0 || (status === true && el.__dragPrevented === true)) { return } const fn = status === true ? el => { el.__dragPrevented = true; el.addEventListener('dragstart', prevent, listenOpts.notPassiveCapture); } : el => { delete el.__dragPrevented; el.removeEventListener('dragstart', prevent, listenOpts.notPassiveCapture); }; el.querySelectorAll('a, img').forEach(fn); } function addEvt (ctx, targetName, events) { const name = `__q_${ targetName }_evt`; ctx[ name ] = ctx[ name ] !== void 0 ? ctx[ name ].concat(events) : events; events.forEach(evt => { evt[ 0 ].addEventListener(evt[ 1 ], ctx[ evt[ 2 ] ], listenOpts[ evt[ 3 ] ]); }); } function cleanEvt (ctx, targetName) { const name = `__q_${ targetName }_evt`; if (ctx[ name ] !== void 0) { ctx[ name ].forEach(evt => { evt[ 0 ].removeEventListener(evt[ 1 ], ctx[ evt[ 2 ] ], listenOpts[ evt[ 3 ] ]); }); ctx[ name ] = void 0; } } /* * also update /types/utils/event.d.ts */ var event = { listenOpts, leftClick, middleClick, rightClick, position, getEventPath, getMouseWheelDistance, stop, prevent, stopAndPrevent, preventDraggable }; function debounce (fn, wait = 250, immediate) { let timer = null; function debounced (/* ...args */) { const args = arguments; const later = () => { timer = null; if (immediate !== true) { fn.apply(this, args); } }; if (timer !== null) { clearTimeout(timer); } else if (immediate === true) { fn.apply(this, args); } timer = setTimeout(later, wait); } debounced.cancel = () => { timer !== null && clearTimeout(timer); }; return debounced } const SIZE_LIST = [ 'sm', 'md', 'lg', 'xl' ]; const { passive: passive$4 } = listenOpts; var Screen = defineReactivePlugin({ width: 0, height: 0, name: 'xs', sizes: { sm: 600, md: 1024, lg: 1440, xl: 1920 }, lt: { sm: true, md: true, lg: true, xl: true }, gt: { xs: false, sm: false, md: false, lg: false }, xs: true, sm: false, md: false, lg: false, xl: false }, { setSizes: noop, setDebounce: noop, install ({ $q, onSSRHydrated }) { $q.screen = this; if (this.__installed === true) { if ($q.config.screen !== void 0) { if ($q.config.screen.bodyClasses === false) { document.body.classList.remove(`screen--${ this.name }`); } else { this.__update(true); } } return } const { visualViewport } = window; const target = visualViewport || window; const scrollingElement = document.scrollingElement || document.documentElement; const getSize = visualViewport === void 0 || client.is.mobile === true ? () => [ Math.max(window.innerWidth, scrollingElement.clientWidth), Math.max(window.innerHeight, scrollingElement.clientHeight) ] : () => [ visualViewport.width * visualViewport.scale + window.innerWidth - scrollingElement.clientWidth, visualViewport.height * visualViewport.scale + window.innerHeight - scrollingElement.clientHeight ]; const classes = $q.config.screen !== void 0 && $q.config.screen.bodyClasses === true; this.__update = force => { const [ w, h ] = getSize(); if (h !== this.height) { this.height = h; } if (w !== this.width) { this.width = w; } else if (force !== true) { return } let s = this.sizes; this.gt.xs = w >= s.sm; this.gt.sm = w >= s.md; this.gt.md = w >= s.lg; this.gt.lg = w >= s.xl; this.lt.sm = w < s.sm; this.lt.md = w < s.md; this.lt.lg = w < s.lg; this.lt.xl = w < s.xl; this.xs = this.lt.sm; this.sm = this.gt.xs === true && this.lt.md === true; this.md = this.gt.sm === true && this.lt.lg === true; this.lg = this.gt.md === true && this.lt.xl === true; this.xl = this.gt.lg; s = (this.xs === true && 'xs') || (this.sm === true && 'sm') || (this.md === true && 'md') || (this.lg === true && 'lg') || 'xl'; if (s !== this.name) { if (classes === true) { document.body.classList.remove(`screen--${ this.name }`); document.body.classList.add(`screen--${ s }`); } this.name = s; } }; let updateEvt, updateSizes = {}, updateDebounce = 16; this.setSizes = sizes => { SIZE_LIST.forEach(name => { if (sizes[ name ] !== void 0) { updateSizes[ name ] = sizes[ name ]; } }); }; this.setDebounce = deb => { updateDebounce = deb; }; const start = () => { const style = getComputedStyle(document.body); // if css props available if (style.getPropertyValue('--q-size-sm')) { SIZE_LIST.forEach(name => { this.sizes[ name ] = parseInt(style.getPropertyValue(`--q-size-${ name }`), 10); }); } this.setSizes = sizes => { SIZE_LIST.forEach(name => { if (sizes[ name ]) { this.sizes[ name ] = sizes[ name ]; } }); this.__update(true); }; this.setDebounce = delay => { updateEvt !== void 0 && target.removeEventListener('resize', updateEvt, passive$4); updateEvt = delay > 0 ? debounce(this.__update, delay) : this.__update; target.addEventListener('resize', updateEvt, passive$4); }; this.setDebounce(updateDebounce); if (Object.keys(updateSizes).length !== 0) { this.setSizes(updateSizes); updateSizes = void 0; // free up memory } else { this.__update(); } // due to optimizations, this would be left out otherwise classes === true && this.name === 'xs' && document.body.classList.add('screen--xs'); }; if (isRuntimeSsrPreHydration.value === true) { onSSRHydrated.push(start); } else { start(); } } }); const Plugin$9 = defineReactivePlugin({ isActive: false, mode: false }, { __media: void 0, set (val) { Plugin$9.mode = val; if (val === 'auto') { if (Plugin$9.__media === void 0) { Plugin$9.__media = window.matchMedia('(prefers-color-scheme: dark)'); Plugin$9.__updateMedia = () => { Plugin$9.set('auto'); }; Plugin$9.__media.addListener(Plugin$9.__updateMedia); } val = Plugin$9.__media.matches; } else if (Plugin$9.__media !== void 0) { Plugin$9.__media.removeListener(Plugin$9.__updateMedia); Plugin$9.__media = void 0; } Plugin$9.isActive = val === true; document.body.classList.remove(`body--${ val === true ? 'light' : 'dark' }`); document.body.classList.add(`body--${ val === true ? 'dark' : 'light' }`); }, toggle () { { Plugin$9.set(Plugin$9.isActive === false); } }, install ({ $q, onSSRHydrated, ssrContext }) { const { dark } = $q.config; $q.dark = this; if (this.__installed === true && dark === void 0) { return } this.isActive = dark === true; const initialVal = dark !== void 0 ? dark : false; if (isRuntimeSsrPreHydration.value === true) { const ssrSet = val => { this.__fromSSR = val; }; const originalSet = this.set; this.set = ssrSet; ssrSet(initialVal); onSSRHydrated.push(() => { this.set = originalSet; this.set(this.__fromSSR); }); } else { this.set(initialVal); } } }); const getTrue = () => true; function filterInvalidPath (path) { return typeof path === 'string' && path !== '' && path !== '/' && path !== '#/' } function normalizeExitPath (path) { path.startsWith('#') === true && (path = path.substring(1)); path.startsWith('/') === false && (path = '/' + path); path.endsWith('/') === true && (path = path.substring(0, path.length - 1)); return '#' + path } function getShouldExitFn (cfg) { if (cfg.backButtonExit === false) { return () => false } if (cfg.backButtonExit === '*') { return getTrue } // Add default root path const exitPaths = [ '#/' ]; // Add custom exit paths Array.isArray(cfg.backButtonExit) === true && exitPaths.push( ...cfg.backButtonExit.filter(filterInvalidPath).map(normalizeExitPath) ); return () => exitPaths.includes(window.location.hash) } var History = { __history: [], add: noop, remove: noop, install ({ $q }) { if (this.__installed === true) { return } const { cordova, capacitor } = client.is; if (cordova !== true && capacitor !== true) { return } const qConf = $q.config[ cordova === true ? 'cordova' : 'capacitor' ]; if (qConf !== void 0 && qConf.backButton === false) { return } // if the '@capacitor/app' plugin is not installed // then we got nothing to do if ( // if we're on Capacitor mode capacitor === true // and it's also not in Capacitor's main instance && (window.Capacitor === void 0 || window.Capacitor.Plugins.App === void 0) ) { return } this.add = entry => { if (entry.condition === void 0) { entry.condition = getTrue; } this.__history.push(entry); }; this.remove = entry => { const index = this.__history.indexOf(entry); if (index >= 0) { this.__history.splice(index, 1); } }; const shouldExit = getShouldExitFn( Object.assign( { backButtonExit: true }, qConf ) ); const backHandler = () => { if (this.__history.length) { const entry = this.__history[ this.__history.length - 1 ]; if (entry.condition() === true) { this.__history.pop(); entry.handler(); } } else if (shouldExit() === true) { navigator.app.exitApp(); } else { window.history.back(); } }; if (cordova === true) { document.addEventListener('deviceready', () => { document.addEventListener('backbutton', backHandler, false); }); } else { window.Capacitor.Plugins.App.addListener('backButton', backHandler); } } }; var defaultLang = { isoName: 'en-US', nativeName: 'English (US)', label: { clear: 'Clear', ok: 'OK', cancel: 'Cancel', close: 'Close', set: 'Set', select: 'Select', reset: 'Reset', remove: 'Remove', update: 'Update', create: 'Create', search: 'Search', filter: 'Filter', refresh: 'Refresh', expand: label => (label ? `Expand "${ label }"` : 'Expand'), collapse: label => (label ? `Collapse "${ label }"` : 'Collapse') }, date: { days: 'Sunday_Monday_Tuesday_Wednesday_Thursday_Friday_Saturday'.split('_'), daysShort: 'Sun_Mon_Tue_Wed_Thu_Fri_Sat'.split('_'), months: 'January_February_March_April_May_June_July_August_September_October_November_December'.split('_'), monthsShort: 'Jan_Feb_Mar_Apr_May_Jun_Jul_Aug_Sep_Oct_Nov_Dec'.split('_'), firstDayOfWeek: 0, // 0-6, 0 - Sunday, 1 Monday, ... format24h: false, pluralDay: 'days' }, table: { noData: 'No data available', noResults: 'No matching records found', loading: 'Loading...', selectedRecords: rows => ( rows === 1 ? '1 record selected.' : (rows === 0 ? 'No' : rows) + ' records selected.' ), recordsPerPage: 'Records per page:', allRows: 'All', pagination: (start, end, total) => start + '-' + end + ' of ' + total, columns: 'Columns' }, editor: { url: 'URL', bold: 'Bold', italic: 'Italic', strikethrough: 'Strikethrough', underline: 'Underline', unorderedList: 'Unordered List', orderedList: 'Ordered List', subscript: 'Subscript', superscript: 'Superscript', hyperlink: 'Hyperlink', toggleFullscreen: 'Toggle Fullscreen', quote: 'Quote', left: 'Left align', center: 'Center align', right: 'Right align', justify: 'Justify align', print: 'Print', outdent: 'Decrease indentation', indent: 'Increase indentation', removeFormat: 'Remove formatting', formatting: 'Formatting', fontSize: 'Font Size', align: 'Align', hr: 'Insert Horizontal Rule', undo: 'Undo', redo: 'Redo', heading1: 'Heading 1', heading2: 'Heading 2', heading3: 'Heading 3', heading4: 'Heading 4', heading5: 'Heading 5', heading6: 'Heading 6', paragraph: 'Paragraph', code: 'Code', size1: 'Very small', size2: 'A bit small', size3: 'Normal', size4: 'Medium-large', size5: 'Big', size6: 'Very big', size7: 'Maximum', defaultFont: 'Default Font', viewSource: 'View Source' }, tree: { noNodes: 'No nodes available', noResults: 'No matching nodes found' } }; function getLocale () { const val = Array.isArray(navigator.languages) === true && navigator.languages.length !== 0 ? navigator.languages[ 0 ] : navigator.language; if (typeof val === 'string') { return val.split(/[-_]/).map((v, i) => ( i === 0 ? v.toLowerCase() : ( i > 1 || v.length < 4 ? v.toUpperCase() : (v[ 0 ].toUpperCase() + v.slice(1).toLowerCase()) ) )).join('-') } } const Plugin$8 = defineReactivePlugin({ __langPack: {} }, { getLocale, set (langObject = defaultLang, ssrContext) { const lang = { ...langObject, rtl: langObject.rtl === true, getLocale }; { lang.set = Plugin$8.set; if (Plugin$8.__langConfig === void 0 || Plugin$8.__langConfig.noHtmlAttrs !== true) { const el = document.documentElement; el.setAttribute('dir', lang.rtl === true ? 'rtl' : 'ltr'); el.setAttribute('lang', lang.isoName); } Object.assign(Plugin$8.__langPack, lang); Plugin$8.props = lang; Plugin$8.isoName = lang.isoName; Plugin$8.nativeName = lang.nativeName; } }, install ({ $q, lang, ssrContext }) { { $q.lang = Plugin$8.__langPack; Plugin$8.__langConfig = $q.config.lang; if (this.__installed === true) { lang !== void 0 && this.set(lang); } else { this.set(lang || defaultLang); } } } }); function setCssVar (propName, value, element = document.body) { if (typeof propName !== 'string') { throw new TypeError('Expected a string as propName') } if (typeof value !== 'string') { throw new TypeError('Expected a string as value') } if (!(element instanceof Element)) { throw new TypeError('Expected a DOM element') } element.style.setProperty(`--q-${ propName }`, value); } let lastKeyCompositionStatus = false; function onKeyDownComposition (evt) { lastKeyCompositionStatus = evt.isComposing === true; } function shouldIgnoreKey (evt) { return lastKeyCompositionStatus === true || evt !== Object(evt) || evt.isComposing === true || evt.qKeyEvent === true } function isKeyCode (evt, keyCodes) { return shouldIgnoreKey(evt) === true ? false : [].concat(keyCodes).includes(evt.keyCode) } function getMobilePlatform (is) { if (is.ios === true) return 'ios' if (is.android === true) return 'android' } function getBodyClasses ({ is, has, within }, cfg) { const cls = [ is.desktop === true ? 'desktop' : 'mobile', `${ has.touch === false ? 'no-' : '' }touch` ]; if (is.mobile === true) { const mobile = getMobilePlatform(is); mobile !== void 0 && cls.push('platform-' + mobile); } if (is.nativeMobile === true) { const type = is.nativeMobileWrapper; cls.push(type); cls.push('native-mobile'); if ( is.ios === true && (cfg[ type ] === void 0 || cfg[ type ].iosStatusBarPadding !== false) ) { cls.push('q-ios-padding'); } } else if (is.electron === true) { cls.push('electron'); } else if (is.bex === true) { cls.push('bex'); } within.iframe === true && cls.push('within-iframe'); return cls } function applyClientSsrCorrections () { const { is } = client; const classes = document.body.className; const classList = new Set(classes.replace(/ {2}/g, ' ').split(' ')); if (iosCorrection !== void 0) { classList.delete('desktop'); classList.add('platform-ios'); classList.add('mobile'); } // else: is it SSG? else if (is.nativeMobile !== true && is.electron !== true && is.bex !== true) { if (is.desktop === true) { classList.delete('mobile'); classList.delete('platform-ios'); classList.delete('platform-android'); classList.add('desktop'); } else if (is.mobile === true) { classList.delete('desktop'); classList.add('mobile'); const mobile = getMobilePlatform(is); if (mobile !== void 0) { classList.add(`platform-${ mobile }`); classList.delete(`platform-${ mobile === 'ios' ? 'android' : 'ios' }`); } else { classList.delete('platform-ios'); classList.delete('platform-android'); } } } if (client.has.touch === true) { classList.delete('no-touch'); classList.add('touch'); } if (client.within.iframe === true) { classList.add('within-iframe'); } const newCls = Array.from(classList).join(' '); if (classes !== newCls) { document.body.className = newCls; } } function setColors (brand) { for (const color in brand) { setCssVar(color, brand[ color ]); } } var Body = { install (opts) { if (this.__installed === true) { return } if (isRuntimeSsrPreHydration.value === true) { applyClientSsrCorrections(); } else { const { $q } = opts; $q.config.brand !== void 0 && setColors($q.config.brand); const cls = getBodyClasses(client, $q.config); document.body.classList.add.apply(document.body.classList, cls); } if (client.is.ios === true) { // needed for iOS button active state document.body.addEventListener('touchstart', noop); } window.addEventListener('keydown', onKeyDownComposition, true); } }; var materialIcons = { name: 'material-icons', type: { positive: 'check_circle', negative: 'warning', info: 'info', warning: 'priority_high' }, arrow: { up: 'arrow_upward', right: 'arrow_forward', down: 'arrow_downward', left: 'arrow_back', dropdown: 'arrow_drop_down' }, chevron: { left: 'chevron_left', right: 'chevron_right' }, colorPicker: { spectrum: 'gradient', tune: 'tune', palette: 'style' }, pullToRefresh: { icon: 'refresh' }, carousel: { left: 'chevron_left', right: 'chevron_right', up: 'keyboard_arrow_up', down: 'keyboard_arrow_down', navigationIcon: 'lens' }, chip: { remove: 'cancel', selected: 'check' }, datetime: { arrowLeft: 'chevron_left', arrowRight: 'chevron_right', now: 'access_time', today: 'today' }, editor: { bold: 'format_bold', italic: 'format_italic', strikethrough: 'strikethrough_s', underline: 'format_underlined', unorderedList: 'format_list_bulleted', orderedList: 'format_list_numbered', subscript: 'vertical_align_bottom', superscript: 'vertical_align_top', hyperlink: 'link', toggleFullscreen: 'fullscreen', quote: 'format_quote', left: 'format_align_left', center: 'format_align_center', right: 'format_align_right', justify: 'format_align_justify', print: 'print', outdent: 'format_indent_decrease', indent: 'format_indent_increase', removeFormat: 'format_clear', formatting: 'text_format', fontSize: 'format_size', align: 'format_align_left', hr: 'remove', undo: 'undo', redo: 'redo', heading: 'format_size', code: 'code', size: 'format_size', font: 'font_download', viewSource: 'code' }, expansionItem: { icon: 'keyboard_arrow_down', denseIcon: 'arrow_drop_down' }, fab: { icon: 'add', activeIcon: 'close' }, field: { clear: 'cancel', error: 'error' }, pagination: { first: 'first_page', prev: 'keyboard_arrow_left', next: 'keyboard_arrow_right', last: 'last_page' }, rating: { icon: 'grade' }, stepper: { done: 'check', active: 'edit', error: 'warning' }, tabs: { left: 'chevron_left', right: 'chevron_right', up: 'keyboard_arrow_up', down: 'keyboard_arrow_down' }, table: { arrowUp: 'arrow_upward', warning: 'warning', firstPage: 'first_page', prevPage: 'chevron_left', nextPage: 'chevron_right', lastPage: 'last_page' }, tree: { icon: 'play_arrow' }, uploader: { done: 'done', clear: 'clear', add: 'add_box', upload: 'cloud_upload', removeQueue: 'clear_all', removeUploaded: 'done_all' } }; const Plugin$7 = defineReactivePlugin({ iconMapFn: null, __icons: {} }, { set (setObject, ssrContext) { const def = { ...setObject, rtl: setObject.rtl === true }; { def.set = Plugin$7.set; Object.assign(Plugin$7.__icons, def); } }, install ({ $q, iconSet, ssrContext }) { { if ($q.config.iconMapFn !== void 0) { this.iconMapFn = $q.config.iconMapFn; } $q.iconSet = this.__icons; injectProp($q, 'iconMapFn', () => this.iconMapFn, val => { this.iconMapFn = val; }); if (this.__installed === true) { iconSet !== void 0 && this.set(iconSet); } else { this.set(iconSet || materialIcons); } } } }); const quasarKey = '_q_'; const timelineKey = '_q_t_'; const stepperKey = '_q_s_'; const layoutKey = '_q_l_'; const pageContainerKey = '_q_pc_'; const fabKey = '_q_f_'; const formKey = '_q_fo_'; const tabsKey = '_q_tabs_'; const uploaderKey = '_q_u_'; const emptyRenderFn = () => {}; const globalConfig = {}; let globalConfigIsFrozen = false; function freezeGlobalConfig () { globalConfigIsFrozen = true; } function isDeepEqual (a, b) { if (a === b) { return true } if (a !== null && b !== null && typeof a === 'object' && typeof b === 'object') { if (a.constructor !== b.constructor) { return false } let length, i; if (a.constructor === Array) { length = a.length; if (length !== b.length) { return false } for (i = length; i-- !== 0;) { if (isDeepEqual(a[ i ], b[ i ]) !== true) { return false } } return true } if (a.constructor === Map) { if (a.size !== b.size) { return false } let iter = a.entries(); i = iter.next(); while (i.done !== true) { if (b.has(i.value[ 0 ]) !== true) { return false } i = iter.next(); } iter = a.entries(); i = iter.next(); while (i.done !== true) { if (isDeepEqual(i.value[ 1 ], b.get(i.value[ 0 ])) !== true) { return false } i = iter.next(); } return true } if (a.constructor === Set) { if (a.size !== b.size) { return false } const iter = a.entries(); i = iter.next(); while (i.done !== true) { if (b.has(i.value[ 0 ]) !== true) { return false } i = iter.next(); } return true } if (a.buffer != null && a.buffer.constructor === ArrayBuffer) { length = a.length; if (length !== b.length) { return false } for (i = length; i-- !== 0;) { if (a[ i ] !== b[ i ]) { return false } } return true } if (a.constructor === RegExp) { return a.source === b.source && a.flags === b.flags } if (a.valueOf !== Object.prototype.valueOf) { return a.valueOf() === b.valueOf() } if (a.toString !== Object.prototype.toString) { return a.toString() === b.toString() } const keys = Object.keys(a).filter(key => a[ key ] !== void 0); length = keys.length; if (length !== Object.keys(b).filter(key => b[ key ] !== void 0).length) { return false } for (i = length; i-- !== 0;) { const key = keys[ i ]; if (isDeepEqual(a[ key ], b[ key ]) !== true) { return false } } return true } // true if both NaN, false otherwise return a !== a && b !== b // eslint-disable-line no-self-compare } // not perfect, but what we ARE interested is for Arrays not to slip in // as spread operator will mess things up in various areas function isObject (v) { return v !== null && typeof v === 'object' && Array.isArray(v) !== true } function isDate (v) { return Object.prototype.toString.call(v) === '[object Date]' } function isRegexp (v) { return Object.prototype.toString.call(v) === '[object RegExp]' } function isNumber (v) { return typeof v === 'number' && isFinite(v) } var is = { deepEqual: isDeepEqual, object: isObject, date: isDate, regexp: isRegexp, number: isNumber }; const autoInstalledPlugins = [ Platform, Body, Plugin$9, Screen, History, Plugin$8, Plugin$7 ]; function createChildApp (appCfg, parentApp) { const app = vue.createApp(appCfg); app.config.globalProperties = parentApp.config.globalProperties; const { reload, ...appContext } = parentApp._context; Object.assign(app._context, appContext); return app } function installPlugins (pluginOpts, pluginList) { pluginList.forEach(Plugin => { Plugin.install(pluginOpts); Plugin.__installed = true; }); } function prepareApp (app, uiOpts, pluginOpts) { app.config.globalProperties.$q = pluginOpts.$q; app.provide(quasarKey, pluginOpts.$q); installPlugins(pluginOpts, autoInstalledPlugins); uiOpts.components !== void 0 && Object.values(uiOpts.components).forEach(c => { if (isObject(c) === true && c.name !== void 0) { app.component(c.name, c); } }); uiOpts.directives !== void 0 && Object.values(uiOpts.directives).forEach(d => { if (isObject(d) === true && d.name !== void 0) { app.directive(d.name, d); } }); uiOpts.plugins !== void 0 && installPlugins( pluginOpts, Object.values(uiOpts.plugins).filter( p => typeof p.install === 'function' && autoInstalledPlugins.includes(p) === false ) ); if (isRuntimeSsrPreHydration.value === true) { pluginOpts.$q.onSSRHydrated = () => { pluginOpts.onSSRHydrated.forEach(fn => { fn(); }); pluginOpts.$q.onSSRHydrated = () => {}; }; } } var installQuasar = function (parentApp, opts = {}) { const $q = { version: '2.12.2' }; if (globalConfigIsFrozen === false) { if (opts.config !== void 0) { Object.assign(globalConfig, opts.config); } $q.config = { ...globalConfig }; freezeGlobalConfig(); } else { $q.config = opts.config || {}; } prepareApp(parentApp, opts, { parentApp, $q, lang: opts.lang, iconSet: opts.iconSet, onSSRHydrated: [] }); }; const createComponent = raw => vue.markRaw(vue.defineComponent(raw)); const createDirective = raw => vue.markRaw(raw); const units = [ 'B', 'KB', 'MB', 'GB', 'TB', 'PB' ]; function humanStorageSize (bytes) { let u = 0; while (parseInt(bytes, 10) >= 1024 && u < units.length - 1) { bytes /= 1024; ++u; } return `${ bytes.toFixed(1) }${ units[ u ] }` } function capitalize (str) { return str.charAt(0).toUpperCase() + str.slice(1) } function between (v, min, max) { return max <= min ? min : Math.min(max, Math.max(min, v)) } function normalizeToInterval (v, min, max) { if (max <= min) { return min } const size = (max - min + 1); let index = min + (v - min) % size; if (index < min) { index = size + index; } return index === 0 ? 0 : index // fix for (-a % a) => -0 } function pad (v, length = 2, char = '0') { if (v === void 0 || v === null) { return v } const val = '' + v; return val.length >= length ? val : new Array(length - val.length + 1).join(char) + val } var format = { humanStorageSize, capitalize, between, normalizeToInterval, pad }; const xhr = XMLHttpRequest, open = xhr.prototype.open, positionValues = [ 'top', 'right', 'bottom', 'left' ]; let stack = []; let highjackCount = 0; function translate ({ p, pos, active, horiz, reverse, dir }) { let x = 1, y = 1; if (horiz === true) { if (reverse === true) { x = -1; } if (pos === 'bottom') { y = -1; } return { transform: `translate3d(${ x * (p - 100) }%,${ active ? 0 : y * -200 }%,0)` } } if (reverse === true) { y = -1; } if (pos === 'right') { x = -1; } return { transform: `translate3d(${ active ? 0 : dir * x * -200 }%,${ y * (p - 100) }%,0)` } } function inc (p, amount) { if (typeof amount !== 'number') { if (p < 25) { amount = Math.random() * 3 + 3; } else if (p < 65) { amount = Math.random() * 3; } else if (p < 85) { amount = Math.random() * 2; } else if (p < 99) { amount = 0.6; } else { amount = 0; } } return between(p + amount, 0, 100) } function highjackAjax (stackEntry) { highjackCount++; stack.push(stackEntry); if (highjackCount > 1) { return } xhr.prototype.open = function (_, url) { const stopStack = []; const loadStart = () => { stack.forEach(entry => { if ( entry.hijackFilter.value === null || (entry.hijackFilter.value(url) === true) ) { entry.start(); stopStack.push(entry.stop); } }); }; const loadEnd = () => { stopStack.forEach(stop => { stop(); }); }; this.addEventListener('loadstart', loadStart, { once: true }); this.addEventListener('loadend', loadEnd, { once: true }); open.apply(this, arguments); }; } function restoreAjax (start) { stack = stack.filter(entry => entry.start !== start); highjackCount = Math.max(0, highjackCount - 1); if (highjackCount === 0) { xhr.prototype.open = open; } } var QAjaxBar = createComponent({ name: 'QAjaxBar', props: { position: { type: String, default: 'top', validator: val => positionValues.includes(val) }, size: { type: String, default: '2px' }, color: String, skipHijack: Boolean, reverse: Boolean, hijackFilter: Function }, emits: [ 'start', 'stop' ], setup (props, { emit }) { const { proxy } = vue.getCurrentInstance(); const progress = vue.ref(0); const onScreen = vue.ref(false); const animate = vue.ref(true); let sessions = 0, timer = null, speed; const classes = vue.computed(() => `q-loading-bar q-loading-bar--${ props.position }` + (props.color !== void 0 ? ` bg-${ props.color }` : '') + (animate.value === true ? '' : ' no-transition') ); const horizontal = vue.computed(() => props.position === 'top' || props.position === 'bottom'); const sizeProp = vue.computed(() => (horizontal.value === true ? 'height' : 'width')); const style = vue.computed(() => { const active = onScreen.value; const obj = translate({ p: progress.value, pos: props.position, active, horiz: horizontal.value, reverse: proxy.$q.lang.rtl === true && [ 'top', 'bottom' ].includes(props.position) ? props.reverse === false : props.reverse, dir: proxy.$q.lang.rtl === true ? -1 : 1 }); obj[ sizeProp.value ] = props.size; obj.opacity = active ? 1 : 0; return obj }); const attributes = vue.computed(() => ( onScreen.value === true ? { role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': progress.value } : { 'aria-hidden': 'true' } )); function start (newSpeed = 300) { const oldSpeed = speed; speed = Math.max(0, newSpeed) || 0; sessions++; if (sessions > 1) { if (oldSpeed === 0 && newSpeed > 0) { planNextStep(); } else if (timer !== null && oldSpeed > 0 && newSpeed <= 0) { clearTimeout(timer); timer = null; } return sessions } timer !== null && clearTimeout(timer); emit('start'); progress.value = 0; timer = setTimeout(() => { timer = null; animate.value = true; newSpeed > 0 && planNextStep(); }, onScreen.value === true ? 500 : 1); if (onScreen.value !== true) { onScreen.value = true; animate.value = false; } return sessions } function increment (amount) { if (sessions > 0) { progress.value = inc(progress.value, amount); } return sessions } function stop () { sessions = Math.max(0, sessions - 1); if (sessions > 0) { return sessions } if (timer !== null) { clearTimeout(timer); timer = null; } emit('stop'); const end = () => { animate.value = true; progress.value = 100; timer = setTimeout(() => { timer = null; onScreen.value = false; }, 1000); }; if (progress.value === 0) { timer = setTimeout(end, 1); } else { end(); } return sessions } function planNextStep () { if (progress.value < 100) { timer = setTimeout(() => { timer = null; increment(); planNextStep(); }, speed); } } let hijacked; vue.onMounted(() => { if (props.skipHijack !== true) { hijacked = true; highjackAjax({ start, stop, hijackFilter: vue.computed(() => props.hijackFilter || null) }); } }); vue.onBeforeUnmount(() => { timer !== null && clearTimeout(timer); hijacked === true && restoreAjax(start); }); // expose public methods Object.assign(proxy, { start, stop, increment }); return () => vue.h('div', { class: classes.value, style: style.value, ...attributes.value }) } }); const useSizeDefaults = { xs: 18, sm: 24, md: 32, lg: 38, xl: 46 }; const useSizeProps = { size: String }; function useSize (props, sizes = useSizeDefaults) { // return sizeStyle return vue.computed(() => ( props.size !== void 0 ? { fontSize: props.size in sizes ? `${ sizes[ props.size ] }px` : props.size } : null )) } function hSlot (slot, otherwise) { return slot !== void 0 ? slot() || otherwise : otherwise } function hUniqueSlot (slot, otherwise) { if (slot !== void 0) { const vnode = slot(); if (vnode !== void 0 && vnode !== null) { return vnode.slice() } } return otherwise } /** * Source definitely exists, * so it's merged with the possible slot */ function hMergeSlot (slot, source) { return slot !== void 0 ? source.concat(slot()) : source } /** * Merge with possible slot, * even if source might not exist */ function hMergeSlotSafely (slot, source) { if (slot === void 0) { return source } return source !== void 0 ? source.concat(slot()) : slot() } /* * (String) key - unique vnode key * (Boolean) condition - should change ONLY when adding/removing directive */ function hDir ( tag, data, children, key, condition, getDirsFn ) { data.key = key + condition; const vnode = vue.h(tag, data, children); return condition === true ? vue.withDirectives(vnode, getDirsFn()) : vnode } const defaultViewBox = '0 0 24 24'; const sameFn = i => i; const ionFn = i => `ionicons ${ i }`; const libMap = { 'mdi-': i => `mdi ${ i }`, 'icon-': sameFn, // fontawesome equiv 'bt-': i => `bt ${ i }`, 'eva-': i => `eva ${ i }`, 'ion-md': ionFn, 'ion-ios': ionFn, 'ion-logo': ionFn, 'iconfont ': sameFn, 'ti-': i => `themify-icon ${ i }`, 'bi-': i => `bootstrap-icons ${ i }` }; const matMap = { o_: '-outlined', r_: '-round', s_: '-sharp' }; const symMap = { sym_o_: '-outlined', sym_r_: '-rounded', sym_s_: '-sharp' }; const libRE = new RegExp('^(' + Object.keys(libMap).join('|') + ')'); const matRE = new RegExp('^(' + Object.keys(matMap).join('|') + ')'); const symRE = new RegExp('^(' + Object.keys(symMap).join('|') + ')'); const mRE = /^[Mm]\s?[-+]?\.?\d/; const imgRE = /^img:/; const svgUseRE = /^svguse:/; const ionRE = /^ion-/; const faRE = /^(fa-(sharp|solid|regular|light|brands|duotone|thin)|[lf]a[srlbdk]?) /; var QIcon = createComponent({ name: 'QIcon', props: { ...useSizeProps, tag: { type: String, default: 'i' }, name: String, color: String, left: Boolean, right: Boolean }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const sizeStyle = useSize(props); const classes = vue.computed(() => 'q-icon' + (props.left === true ? ' on-left' : '') // TODO Qv3: drop this + (props.right === true ? ' on-right' : '') + (props.color !== void 0 ? ` text-${ props.color }` : '') ); const type = vue.computed(() => { let cls; let icon = props.name; if (icon === 'none' || !icon) { return { none: true } } if ($q.iconMapFn !== null) { const res = $q.iconMapFn(icon); if (res !== void 0) { if (res.icon !== void 0) { icon = res.icon; if (icon === 'none' || !icon) { return { none: true } } } else { return { cls: res.cls, content: res.content !== void 0 ? res.content : ' ' } } } } if (mRE.test(icon) === true) { const [ def, viewBox = defaultViewBox ] = icon.split('|'); return { svg: true, viewBox, nodes: def.split('&&').map(path => { const [ d, style, transform ] = path.split('@@'); return vue.h('path', { style, d, transform }) }) } } if (imgRE.test(icon) === true) { return { img: true, src: icon.substring(4) } } if (svgUseRE.test(icon) === true) { const [ def, viewBox = defaultViewBox ] = icon.split('|'); return { svguse: true, src: def.substring(7), viewBox } } let content = ' '; const matches = icon.match(libRE); if (matches !== null) { cls = libMap[ matches[ 1 ] ](icon); } else if (faRE.test(icon) === true) { cls = icon; } else if (ionRE.test(icon) === true) { cls = `ionicons ion-${ $q.platform.is.ios === true ? 'ios' : 'md' }${ icon.substring(3) }`; } else if (symRE.test(icon) === true) { // "notranslate" class is for Google Translate // to avoid tampering with Material Symbols ligature font // // Caution: To be able to add suffix to the class name, // keep the 'material-symbols' at the end of the string. cls = 'notranslate material-symbols'; const matches = icon.match(symRE); if (matches !== null) { icon = icon.substring(6); cls += symMap[ matches[ 1 ] ]; } content = icon; } else { // "notranslate" class is for Google Translate // to avoid tampering with Material Icons ligature font // // Caution: To be able to add suffix to the class name, // keep the 'material-icons' at the end of the string. cls = 'notranslate material-icons'; const matches = icon.match(matRE); if (matches !== null) { icon = icon.substring(2); cls += matMap[ matches[ 1 ] ]; } content = icon; } return { cls, content } }); return () => { const data = { class: classes.value, style: sizeStyle.value, 'aria-hidden': 'true', role: 'presentation' }; if (type.value.none === true) { return vue.h(props.tag, data, hSlot(slots.default)) } if (type.value.img === true) { return vue.h('span', data, hMergeSlot(slots.default, [ vue.h('img', { src: type.value.src }) ])) } if (type.value.svg === true) { return vue.h('span', data, hMergeSlot(slots.default, [ vue.h('svg', { viewBox: type.value.viewBox || '0 0 24 24' }, type.value.nodes) ])) } if (type.value.svguse === true) { return vue.h('span', data, hMergeSlot(slots.default, [ vue.h('svg', { viewBox: type.value.viewBox }, [ vue.h('use', { 'xlink:href': type.value.src }) ]) ])) } if (type.value.cls !== void 0) { data.class += ' ' + type.value.cls; } return vue.h(props.tag, data, hMergeSlot(slots.default, [ type.value.content ])) } } }); var QAvatar = createComponent({ name: 'QAvatar', props: { ...useSizeProps, fontSize: String, color: String, textColor: String, icon: String, square: Boolean, rounded: Boolean }, setup (props, { slots }) { const sizeStyle = useSize(props); const classes = vue.computed(() => 'q-avatar' + (props.color ? ` bg-${ props.color }` : '') + (props.textColor ? ` text-${ props.textColor } q-chip--colored` : '') + ( props.square === true ? ' q-avatar--square' : (props.rounded === true ? ' rounded-borders' : '') ) ); const contentStyle = vue.computed(() => ( props.fontSize ? { fontSize: props.fontSize } : null )); return () => { const icon = props.icon !== void 0 ? [ vue.h(QIcon, { name: props.icon }) ] : void 0; return vue.h('div', { class: classes.value, style: sizeStyle.value }, [ vue.h('div', { class: 'q-avatar__content row flex-center overflow-hidden', style: contentStyle.value }, hMergeSlotSafely(slots.default, icon)) ]) } } }); const alignValues$3 = [ 'top', 'middle', 'bottom' ]; var QBadge = createComponent({ name: 'QBadge', props: { color: String, textColor: String, floating: Boolean, transparent: Boolean, multiLine: Boolean, outline: Boolean, rounded: Boolean, label: [ Number, String ], align: { type: String, validator: v => alignValues$3.includes(v) } }, setup (props, { slots }) { const style = vue.computed(() => { return props.align !== void 0 ? { verticalAlign: props.align } : null }); const classes = vue.computed(() => { const text = props.outline === true ? props.color || props.textColor : props.textColor; return 'q-badge flex inline items-center no-wrap' + ` q-badge--${ props.multiLine === true ? 'multi' : 'single' }-line` + (props.outline === true ? ' q-badge--outline' : (props.color !== void 0 ? ` bg-${ props.color }` : '') ) + (text !== void 0 ? ` text-${ text }` : '') + (props.floating === true ? ' q-badge--floating' : '') + (props.rounded === true ? ' q-badge--rounded' : '') + (props.transparent === true ? ' q-badge--transparent' : '') }); return () => vue.h('div', { class: classes.value, style: style.value, role: 'status', 'aria-label': props.label }, hMergeSlot(slots.default, props.label !== void 0 ? [ props.label ] : [])) } }); const useDarkProps = { dark: { type: Boolean, default: null } }; function useDark (props, $q) { // return isDark return vue.computed(() => ( props.dark === null ? $q.dark.isActive : props.dark )) } var QBanner = createComponent({ name: 'QBanner', props: { ...useDarkProps, inlineActions: Boolean, dense: Boolean, rounded: Boolean }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const classes = vue.computed(() => 'q-banner row items-center' + (props.dense === true ? ' q-banner--dense' : '') + (isDark.value === true ? ' q-banner--dark q-dark' : '') + (props.rounded === true ? ' rounded-borders' : '') ); const actionClass = vue.computed(() => 'q-banner__actions row items-center justify-end' + ` col-${ props.inlineActions === true ? 'auto' : 'all' }` ); return () => { const child = [ vue.h('div', { class: 'q-banner__avatar col-auto row items-center self-start' }, hSlot(slots.avatar)), vue.h('div', { class: 'q-banner__content col text-body2' }, hSlot(slots.default)) ]; const actions = hSlot(slots.action); actions !== void 0 && child.push( vue.h('div', { class: actionClass.value }, actions) ); return vue.h('div', { class: classes.value + (props.inlineActions === false && actions !== void 0 ? ' q-banner--top-padding' : ''), role: 'alert' }, child) } } }); var QBar = createComponent({ name: 'QBar', props: { ...useDarkProps, dense: Boolean }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const classes = vue.computed(() => 'q-bar row no-wrap items-center' + ` q-bar--${ props.dense === true ? 'dense' : 'standard' } ` + ` q-bar--${ isDark.value === true ? 'dark' : 'light' }` ); return () => vue.h('div', { class: classes.value, role: 'toolbar' }, hSlot(slots.default)) } }); const alignMap = { left: 'start', center: 'center', right: 'end', between: 'between', around: 'around', evenly: 'evenly', stretch: 'stretch' }; const alignValues$2 = Object.keys(alignMap); const useAlignProps = { align: { type: String, validator: v => alignValues$2.includes(v) } }; function useAlign (props) { // return alignClass return vue.computed(() => { const align = props.align === void 0 ? props.vertical === true ? 'stretch' : 'left' : props.align; return `${ props.vertical === true ? 'items' : 'justify' }-${ alignMap[ align ] }` }) } // copied to docs too function getParentProxy (proxy) { if (Object(proxy.$parent) === proxy.$parent) { return proxy.$parent } let { parent } = proxy.$; while (Object(parent) === parent) { if (Object(parent.proxy) === parent.proxy) { return parent.proxy } parent = parent.parent; } } function fillNormalizedVNodes (children, vnode) { if (typeof vnode.type === 'symbol') { if (Array.isArray(vnode.children) === true) { vnode.children.forEach(child => { fillNormalizedVNodes(children, child); }); } } else { children.add(vnode); } } // vnodes from rendered in advanced slots function getNormalizedVNodes (vnodes) { const children = new Set(); vnodes.forEach(vnode => { fillNormalizedVNodes(children, vnode); }); return Array.from(children) } function vmHasRouter (vm) { return vm.appContext.config.globalProperties.$router !== void 0 } function vmIsDestroyed (vm) { return vm.isUnmounted === true || vm.isDeactivated === true } const disabledValues = [ '', true ]; var QBreadcrumbs = createComponent({ name: 'QBreadcrumbs', props: { ...useAlignProps, separator: { type: String, default: '/' }, separatorColor: String, activeColor: { type: String, default: 'primary' }, gutter: { type: String, validator: v => [ 'none', 'xs', 'sm', 'md', 'lg', 'xl' ].includes(v), default: 'sm' } }, setup (props, { slots }) { const alignClass = useAlign(props); const classes = vue.computed(() => `flex items-center ${ alignClass.value }${ props.gutter === 'none' ? '' : ` q-gutter-${ props.gutter }` }` ); const sepClass = vue.computed(() => (props.separatorColor ? ` text-${ props.separatorColor }` : '')); const activeClass = vue.computed(() => ` text-${ props.activeColor }`); return () => { const vnodes = getNormalizedVNodes( hSlot(slots.default) ); if (vnodes.length === 0) { return } let els = 1; const child = [], len = vnodes.filter(c => c.type !== void 0 && c.type.name === 'QBreadcrumbsEl').length, separator = slots.separator !== void 0 ? slots.separator : () => props.separator; vnodes.forEach(comp => { if (comp.type !== void 0 && comp.type.name === 'QBreadcrumbsEl') { const middle = els < len; const disabled = comp.props !== null && disabledValues.includes(comp.props.disable); const cls = (middle === true ? '' : ' q-breadcrumbs--last') + (disabled !== true && middle === true ? activeClass.value : ''); els++; child.push( vue.h('div', { class: `flex items-center${ cls }` }, [ comp ]) ); if (middle === true) { child.push( vue.h('div', { class: 'q-breadcrumbs__separator' + sepClass.value }, separator()) ); } } else { child.push(comp); } }); return vue.h('div', { class: 'q-breadcrumbs' }, [ vue.h('div', { class: classes.value }, child) ]) } } }); /* * Inspired by RouterLink from Vue Router * --> API should match! */ // Get the original path value of a record by following its aliasOf function getOriginalPath (record) { return record ? ( record.aliasOf ? record.aliasOf.path : record.path ) : '' } function isSameRouteRecord (a, b) { // since the original record has an undefined value for aliasOf // but all aliases point to the original record, this will always compare // the original record return (a.aliasOf || a) === (b.aliasOf || b) } function includesParams (outer, inner) { for (const key in inner) { const innerValue = inner[ key ], outerValue = outer[ key ]; if (typeof innerValue === 'string') { if (innerValue !== outerValue) { return false } } else if ( Array.isArray(outerValue) === false || outerValue.length !== innerValue.length || innerValue.some((value, i) => value !== outerValue[ i ]) ) { return false } } return true } function isEquivalentArray (a, b) { return Array.isArray(b) === true ? a.length === b.length && a.every((value, i) => value === b[ i ]) : a.length === 1 && a[ 0 ] === b } function isSameRouteLocationParamsValue (a, b) { return Array.isArray(a) === true ? isEquivalentArray(a, b) : ( Array.isArray(b) === true ? isEquivalentArray(b, a) : a === b ) } function isSameRouteLocationParams (a, b) { if (Object.keys(a).length !== Object.keys(b).length) { return false } for (const key in a) { if (isSameRouteLocationParamsValue(a[ key ], b[ key ]) === false) { return false } } return true } const useRouterLinkProps = { // router-link to: [ String, Object ], replace: Boolean, exact: Boolean, activeClass: { type: String, default: 'q-router-link--active' }, exactActiveClass: { type: String, default: 'q-router-link--exact-active' }, // regular link href: String, target: String, // state disable: Boolean }; // external props: type, tag function useRouterLink ({ fallbackTag, useDisableForRouterLinkProps = true } = {}) { const vm = vue.getCurrentInstance(); const { props, proxy, emit } = vm; const hasRouter = vmHasRouter(vm); const hasHrefLink = vue.computed(() => props.disable !== true && props.href !== void 0); // for perf reasons, we use minimum amount of runtime work const hasRouterLinkProps = useDisableForRouterLinkProps === true ? vue.computed(() => hasRouter === true && props.disable !== true && hasHrefLink.value !== true && props.to !== void 0 && props.to !== null && props.to !== '' ) : vue.computed(() => hasRouter === true && hasHrefLink.value !== true && props.to !== void 0 && props.to !== null && props.to !== '' ); const resolvedLink = vue.computed(() => ( hasRouterLinkProps.value === true ? getLink(props.to) : null )); const hasRouterLink = vue.computed(() => resolvedLink.value !== null); const hasLink = vue.computed(() => hasHrefLink.value === true || hasRouterLink.value === true); const linkTag = vue.computed(() => ( props.type === 'a' || hasLink.value === true ? 'a' : (props.tag || fallbackTag || 'div') )); const linkAttrs = vue.computed(() => ( hasHrefLink.value === true ? { href: props.href, target: props.target } : ( hasRouterLink.value === true ? { href: resolvedLink.value.href, target: props.target } : {} ) )); const linkActiveIndex = vue.computed(() => { if (hasRouterLink.value === false) { return -1 } const { matched } = resolvedLink.value, { length } = matched, routeMatched = matched[ length - 1 ]; if (routeMatched === void 0) { return -1 } const currentMatched = proxy.$route.matched; if (currentMatched.length === 0) { return -1 } const index = currentMatched.findIndex( isSameRouteRecord.bind(null, routeMatched) ); if (index > -1) { return index } // possible parent record const parentRecordPath = getOriginalPath(matched[ length - 2 ]); return ( // we are dealing with nested routes length > 1 // if the parent and matched route have the same path, this link is // referring to the empty child. Or we currently are on a different // child of the same parent && getOriginalPath(routeMatched) === parentRecordPath // avoid comparing the child with its parent && currentMatched[ currentMatched.length - 1 ].path !== parentRecordPath ? currentMatched.findIndex( isSameRouteRecord.bind(null, matched[ length - 2 ]) ) : index ) }); const linkIsActive = vue.computed(() => hasRouterLink.value === true && linkActiveIndex.value !== -1 && includesParams(proxy.$route.params, resolvedLink.value.params) ); const linkIsExactActive = vue.computed(() => linkIsActive.value === true && linkActiveIndex.value === proxy.$route.matched.length - 1 && isSameRouteLocationParams(proxy.$route.params, resolvedLink.value.params) ); const linkClass = vue.computed(() => ( hasRouterLink.value === true ? ( linkIsExactActive.value === true ? ` ${ props.exactActiveClass } ${ props.activeClass }` : ( props.exact === true ? '' : (linkIsActive.value === true ? ` ${ props.activeClass }` : '') ) ) : '' )); function getLink (to) { try { return proxy.$router.resolve(to) } catch (_) {} return null } /** * @returns Promise */ function navigateToRouterLink ( e, { returnRouterError, to = props.to, replace = props.replace } = {} ) { if (props.disable === true) { // ensure native navigation is prevented in all cases, // like when useDisableForRouterLinkProps === false (QRouteTab) e.preventDefault(); return Promise.resolve(false) } if ( // don't redirect with control keys; // should match RouterLink from Vue Router e.metaKey || e.altKey || e.ctrlKey || e.shiftKey // don't redirect on right click || (e.button !== void 0 && e.button !== 0) // don't redirect if it should open in a new window || props.target === '_blank' ) { return Promise.resolve(false) } // hinder the native navigation e.preventDefault(); // then() can also return a "soft" router error (Vue Router behavior) const promise = proxy.$router[ replace === true ? 'replace' : 'push' ](to); return returnRouterError === true ? promise // else catching hard errors and also "soft" ones - then(err => ...) : promise.then(() => {}).catch(() => {}) } // warning! ensure that the component using it has 'click' included in its 'emits' definition prop function navigateOnClick (e) { if (hasRouterLink.value === true) { const go = opts => navigateToRouterLink(e, opts); emit('click', e, go); e.defaultPrevented !== true && go(); } else { emit('click', e); } } return { hasRouterLink, hasHrefLink, hasLink, linkTag, resolvedLink, linkIsActive, linkIsExactActive, linkClass, linkAttrs, getLink, navigateToRouterLink, navigateOnClick } } var QBreadcrumbsEl = createComponent({ name: 'QBreadcrumbsEl', props: { ...useRouterLinkProps, label: String, icon: String, tag: { type: String, default: 'span' } }, emits: [ 'click' ], setup (props, { slots }) { const { linkTag, linkAttrs, linkClass, navigateOnClick } = useRouterLink(); const data = vue.computed(() => { return { class: 'q-breadcrumbs__el q-link ' + 'flex inline items-center relative-position ' + (props.disable !== true ? 'q-link--focusable' + linkClass.value : 'q-breadcrumbs__el--disable'), ...linkAttrs.value, onClick: navigateOnClick } }); const iconClass = vue.computed(() => 'q-breadcrumbs__el-icon' + (props.label !== void 0 ? ' q-breadcrumbs__el-icon--with-label' : '') ); return () => { const child = []; props.icon !== void 0 && child.push( vue.h(QIcon, { class: iconClass.value, name: props.icon }) ); props.label !== void 0 && child.push(props.label); return vue.h( linkTag.value, { ...data.value }, hMergeSlot(slots.default, child) ) } } }); const useSpinnerProps = { size: { type: [ Number, String ], default: '1em' }, color: String }; function useSpinner (props) { return { cSize: vue.computed(() => ( props.size in useSizeDefaults ? `${ useSizeDefaults[ props.size ] }px` : props.size )), classes: vue.computed(() => 'q-spinner' + (props.color ? ` text-${ props.color }` : '') ) } } var QSpinner = createComponent({ name: 'QSpinner', props: { ...useSpinnerProps, thickness: { type: Number, default: 5 } }, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value + ' q-spinner-mat', width: cSize.value, height: cSize.value, viewBox: '25 25 50 50' }, [ vue.h('circle', { class: 'path', cx: '50', cy: '50', r: '20', fill: 'none', stroke: 'currentColor', 'stroke-width': props.thickness, 'stroke-miterlimit': '10' }) ]) } }); function offset (el) { if (el === window) { return { top: 0, left: 0 } } const { top, left } = el.getBoundingClientRect(); return { top, left } } function style (el, property) { return window.getComputedStyle(el).getPropertyValue(property) } function height (el) { return el === window ? window.innerHeight : el.getBoundingClientRect().height } function width$1 (el) { return el === window ? window.innerWidth : el.getBoundingClientRect().width } function css (element, css) { const style = element.style; for (const prop in css) { style[ prop ] = css[ prop ]; } } function cssBatch (elements, style) { elements.forEach(el => css(el, style)); } function ready (fn) { if (typeof fn !== 'function') { return } if (document.readyState !== 'loading') { return fn() } document.addEventListener('DOMContentLoaded', fn, false); } // internal function getElement$1 (el) { if (el === void 0 || el === null) { return void 0 } if (typeof el === 'string') { try { return document.querySelector(el) || void 0 } catch (err) { return void 0 } } const target = vue.unref(el); if (target) { return target.$el || target } } // internal function childHasFocus (el, focusedEl) { if (el === void 0 || el === null || el.contains(focusedEl) === true) { return true } for (let next = el.nextElementSibling; next !== null; next = next.nextElementSibling) { if (next.contains(focusedEl)) { return true } } return false } var dom = { offset, style, height, width: width$1, css, cssBatch, ready }; function throttle (fn, limit = 250) { let wait = false, result; return function (/* ...args */) { if (wait === false) { wait = true; setTimeout(() => { wait = false; }, limit); result = fn.apply(this, arguments); } return result } } function showRipple (evt, el, ctx, forceCenter) { ctx.modifiers.stop === true && stop(evt); const color = ctx.modifiers.color; let center = ctx.modifiers.center; center = center === true || forceCenter === true; const node = document.createElement('span'), innerNode = document.createElement('span'), pos = position(evt), { left, top, width, height } = el.getBoundingClientRect(), diameter = Math.sqrt(width * width + height * height), radius = diameter / 2, centerX = `${ (width - diameter) / 2 }px`, x = center ? centerX : `${ pos.left - left - radius }px`, centerY = `${ (height - diameter) / 2 }px`, y = center ? centerY : `${ pos.top - top - radius }px`; innerNode.className = 'q-ripple__inner'; css(innerNode, { height: `${ diameter }px`, width: `${ diameter }px`, transform: `translate3d(${ x },${ y },0) scale3d(.2,.2,1)`, opacity: 0 }); node.className = `q-ripple${ color ? ' text-' + color : '' }`; node.setAttribute('dir', 'ltr'); node.appendChild(innerNode); el.appendChild(node); const abort = () => { node.remove(); clearTimeout(timer); }; ctx.abort.push(abort); let timer = setTimeout(() => { innerNode.classList.add('q-ripple__inner--enter'); innerNode.style.transform = `translate3d(${ centerX },${ centerY },0) scale3d(1,1,1)`; innerNode.style.opacity = 0.2; timer = setTimeout(() => { innerNode.classList.remove('q-ripple__inner--enter'); innerNode.classList.add('q-ripple__inner--leave'); innerNode.style.opacity = 0; timer = setTimeout(() => { node.remove(); ctx.abort.splice(ctx.abort.indexOf(abort), 1); }, 275); }, 250); }, 50); } function updateModifiers$1 (ctx, { modifiers, value, arg }) { const cfg = Object.assign({}, ctx.cfg.ripple, modifiers, value); ctx.modifiers = { early: cfg.early === true, stop: cfg.stop === true, center: cfg.center === true, color: cfg.color || arg, keyCodes: [].concat(cfg.keyCodes || 13) }; } var Ripple = createDirective({ name: 'ripple', beforeMount (el, binding) { const cfg = binding.instance.$.appContext.config.globalProperties.$q.config || {}; if (cfg.ripple === false) { return } const ctx = { cfg, enabled: binding.value !== false, modifiers: {}, abort: [], start (evt) { if ( ctx.enabled === true && evt.qSkipRipple !== true && evt.type === (ctx.modifiers.early === true ? 'pointerdown' : 'click') ) { showRipple(evt, el, ctx, evt.qKeyEvent === true); } }, keystart: throttle(evt => { if ( ctx.enabled === true && evt.qSkipRipple !== true && isKeyCode(evt, ctx.modifiers.keyCodes) === true && evt.type === `key${ ctx.modifiers.early === true ? 'down' : 'up' }` ) { showRipple(evt, el, ctx, true); } }, 300) }; updateModifiers$1(ctx, binding); el.__qripple = ctx; addEvt(ctx, 'main', [ [ el, 'pointerdown', 'start', 'passive' ], [ el, 'click', 'start', 'passive' ], [ el, 'keydown', 'keystart', 'passive' ], [ el, 'keyup', 'keystart', 'passive' ] ]); }, updated (el, binding) { if (binding.oldValue !== binding.value) { const ctx = el.__qripple; if (ctx !== void 0) { ctx.enabled = binding.value !== false; if (ctx.enabled === true && Object(binding.value) === binding.value) { updateModifiers$1(ctx, binding); } } } }, beforeUnmount (el) { const ctx = el.__qripple; if (ctx !== void 0) { ctx.abort.forEach(fn => { fn(); }); cleanEvt(ctx, 'main'); delete el._qripple; } } } ); const btnPadding = { none: 0, xs: 4, sm: 8, md: 16, lg: 24, xl: 32 }; const defaultSizes$2 = { xs: 8, sm: 10, md: 14, lg: 20, xl: 24 }; const formTypes = [ 'button', 'submit', 'reset' ]; const mediaTypeRE = /[^\s]\/[^\s]/; const btnDesignOptions = [ 'flat', 'outline', 'push', 'unelevated' ]; const getBtnDesign = (props, defaultValue) => { if (props.flat === true) return 'flat' if (props.outline === true) return 'outline' if (props.push === true) return 'push' if (props.unelevated === true) return 'unelevated' return defaultValue }; const getBtnDesignAttr = props => { const design = getBtnDesign(props); return design !== void 0 ? { [ design ]: true } : {} }; const useBtnProps = { ...useSizeProps, ...useRouterLinkProps, type: { type: String, default: 'button' }, label: [ Number, String ], icon: String, iconRight: String, ...btnDesignOptions.reduce( (acc, val) => (acc[ val ] = Boolean) && acc, {} ), square: Boolean, round: Boolean, rounded: Boolean, glossy: Boolean, size: String, fab: Boolean, fabMini: Boolean, padding: String, color: String, textColor: String, noCaps: Boolean, noWrap: Boolean, dense: Boolean, tabindex: [ Number, String ], ripple: { type: [ Boolean, Object ], default: true }, align: { ...useAlignProps.align, default: 'center' }, stack: Boolean, stretch: Boolean, loading: { type: Boolean, default: null }, disable: Boolean }; function useBtn (props) { const sizeStyle = useSize(props, defaultSizes$2); const alignClass = useAlign(props); const { hasRouterLink, hasLink, linkTag, linkAttrs, navigateOnClick } = useRouterLink({ fallbackTag: 'button' }); const style = vue.computed(() => { const obj = props.fab === false && props.fabMini === false ? sizeStyle.value : {}; return props.padding !== void 0 ? Object.assign({}, obj, { padding: props.padding .split(/\s+/) .map(v => (v in btnPadding ? btnPadding[ v ] + 'px' : v)) .join(' '), minWidth: '0', minHeight: '0' }) : obj }); const isRounded = vue.computed(() => props.rounded === true || props.fab === true || props.fabMini === true ); const isActionable = vue.computed(() => props.disable !== true && props.loading !== true ); const tabIndex = vue.computed(() => ( isActionable.value === true ? props.tabindex || 0 : -1 )); const design = vue.computed(() => getBtnDesign(props, 'standard')); const attributes = vue.computed(() => { const acc = { tabindex: tabIndex.value }; if (hasLink.value === true) { Object.assign(acc, linkAttrs.value); } else if (formTypes.includes(props.type) === true) { acc.type = props.type; } if (linkTag.value === 'a') { if (props.disable === true) { acc[ 'aria-disabled' ] = 'true'; } else if (acc.href === void 0) { acc.role = 'button'; } if (hasRouterLink.value !== true && mediaTypeRE.test(props.type) === true) { acc.type = props.type; } } else if (props.disable === true) { acc.disabled = ''; acc[ 'aria-disabled' ] = 'true'; } if (props.loading === true && props.percentage !== void 0) { Object.assign(acc, { role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 100, 'aria-valuenow': props.percentage }); } return acc }); const classes = vue.computed(() => { let colors; if (props.color !== void 0) { if (props.flat === true || props.outline === true) { colors = `text-${ props.textColor || props.color }`; } else { colors = `bg-${ props.color } text-${ props.textColor || 'white' }`; } } else if (props.textColor) { colors = `text-${ props.textColor }`; } const shape = props.round === true ? 'round' : `rectangle${ isRounded.value === true ? ' q-btn--rounded' : (props.square === true ? ' q-btn--square' : '') }`; return `q-btn--${ design.value } q-btn--${ shape }` + (colors !== void 0 ? ' ' + colors : '') + (isActionable.value === true ? ' q-btn--actionable q-focusable q-hoverable' : (props.disable === true ? ' disabled' : '')) + (props.fab === true ? ' q-btn--fab' : (props.fabMini === true ? ' q-btn--fab-mini' : '')) + (props.noCaps === true ? ' q-btn--no-uppercase' : '') + (props.dense === true ? ' q-btn--dense' : '') + (props.stretch === true ? ' no-border-radius self-stretch' : '') + (props.glossy === true ? ' glossy' : '') + (props.square ? ' q-btn--square' : '') }); const innerClasses = vue.computed(() => alignClass.value + (props.stack === true ? ' column' : ' row') + (props.noWrap === true ? ' no-wrap text-no-wrap' : '') + (props.loading === true ? ' q-btn__content--hidden' : '') ); return { classes, style, innerClasses, attributes, hasLink, linkTag, navigateOnClick, isActionable } } const { passiveCapture } = listenOpts; let touchTarget = null, keyboardTarget = null, mouseTarget = null; var QBtn = createComponent({ name: 'QBtn', props: { ...useBtnProps, percentage: Number, darkPercentage: Boolean, onTouchstart: [ Function, Array ] }, emits: [ 'click', 'keydown', 'mousedown', 'keyup' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { classes, style, innerClasses, attributes, hasLink, linkTag, navigateOnClick, isActionable } = useBtn(props); const rootRef = vue.ref(null); const blurTargetRef = vue.ref(null); let localTouchTargetEl = null, avoidMouseRipple, mouseTimer = null; const hasLabel = vue.computed(() => props.label !== void 0 && props.label !== null && props.label !== '' ); const ripple = vue.computed(() => ( props.disable === true || props.ripple === false ? false : { keyCodes: hasLink.value === true ? [ 13, 32 ] : [ 13 ], ...(props.ripple === true ? {} : props.ripple) } )); const rippleProps = vue.computed(() => ({ center: props.round })); const percentageStyle = vue.computed(() => { const val = Math.max(0, Math.min(100, props.percentage)); return val > 0 ? { transition: 'transform 0.6s', transform: `translateX(${ val - 100 }%)` } : {} }); const onEvents = vue.computed(() => { if (props.loading === true) { return { onMousedown: onLoadingEvt, onTouchstart: onLoadingEvt, onClick: onLoadingEvt, onKeydown: onLoadingEvt, onKeyup: onLoadingEvt } } if (isActionable.value === true) { const acc = { onClick, onKeydown, onMousedown }; if (proxy.$q.platform.has.touch === true) { const suffix = props.onTouchstart !== void 0 ? '' : 'Passive'; acc[ `onTouchstart${ suffix }` ] = onTouchstart; } return acc } return { // needed; especially for disabled tags onClick: stopAndPrevent } }); const nodeProps = vue.computed(() => ({ ref: rootRef, class: 'q-btn q-btn-item non-selectable no-outline ' + classes.value, style: style.value, ...attributes.value, ...onEvents.value })); function onClick (e) { // is it already destroyed? if (rootRef.value === null) { return } if (e !== void 0) { if (e.defaultPrevented === true) { return } const el = document.activeElement; // focus button if it came from ENTER on form // prevent the new submit (already done) if ( props.type === 'submit' && el !== document.body && rootRef.value.contains(el) === false // required for iOS and desktop Safari && el.contains(rootRef.value) === false ) { rootRef.value.focus(); const onClickCleanup = () => { document.removeEventListener('keydown', stopAndPrevent, true); document.removeEventListener('keyup', onClickCleanup, passiveCapture); rootRef.value !== null && rootRef.value.removeEventListener('blur', onClickCleanup, passiveCapture); }; document.addEventListener('keydown', stopAndPrevent, true); document.addEventListener('keyup', onClickCleanup, passiveCapture); rootRef.value.addEventListener('blur', onClickCleanup, passiveCapture); } } navigateOnClick(e); } function onKeydown (e) { // is it already destroyed? if (rootRef.value === null) { return } emit('keydown', e); if (isKeyCode(e, [ 13, 32 ]) === true && keyboardTarget !== rootRef.value) { keyboardTarget !== null && cleanup(); if (e.defaultPrevented !== true) { // focus external button if the focus helper was focused before rootRef.value.focus(); keyboardTarget = rootRef.value; rootRef.value.classList.add('q-btn--active'); document.addEventListener('keyup', onPressEnd, true); rootRef.value.addEventListener('blur', onPressEnd, passiveCapture); } stopAndPrevent(e); } } function onTouchstart (e) { // is it already destroyed? if (rootRef.value === null) { return } emit('touchstart', e); if (e.defaultPrevented === true) { return } if (touchTarget !== rootRef.value) { touchTarget !== null && cleanup(); touchTarget = rootRef.value; localTouchTargetEl = e.target; localTouchTargetEl.addEventListener('touchcancel', onPressEnd, passiveCapture); localTouchTargetEl.addEventListener('touchend', onPressEnd, passiveCapture); } // avoid duplicated mousedown event // triggering another early ripple avoidMouseRipple = true; mouseTimer !== null && clearTimeout(mouseTimer); mouseTimer = setTimeout(() => { mouseTimer = null; avoidMouseRipple = false; }, 200); } function onMousedown (e) { // is it already destroyed? if (rootRef.value === null) { return } e.qSkipRipple = avoidMouseRipple === true; emit('mousedown', e); if (e.defaultPrevented !== true && mouseTarget !== rootRef.value) { mouseTarget !== null && cleanup(); mouseTarget = rootRef.value; rootRef.value.classList.add('q-btn--active'); document.addEventListener('mouseup', onPressEnd, passiveCapture); } } function onPressEnd (e) { // is it already destroyed? if (rootRef.value === null) { return } // needed for IE (because it emits blur when focusing button from focus helper) if (e !== void 0 && e.type === 'blur' && document.activeElement === rootRef.value) { return } if (e !== void 0 && e.type === 'keyup') { if (keyboardTarget === rootRef.value && isKeyCode(e, [ 13, 32 ]) === true) { // for click trigger const evt = new MouseEvent('click', e); evt.qKeyEvent = true; e.defaultPrevented === true && prevent(evt); e.cancelBubble === true && stop(evt); rootRef.value.dispatchEvent(evt); stopAndPrevent(e); // for ripple e.qKeyEvent = true; } emit('keyup', e); } cleanup(); } function cleanup (destroying) { const blurTarget = blurTargetRef.value; if ( destroying !== true && (touchTarget === rootRef.value || mouseTarget === rootRef.value) && blurTarget !== null && blurTarget !== document.activeElement ) { blurTarget.setAttribute('tabindex', -1); blurTarget.focus(); } if (touchTarget === rootRef.value) { if (localTouchTargetEl !== null) { localTouchTargetEl.removeEventListener('touchcancel', onPressEnd, passiveCapture); localTouchTargetEl.removeEventListener('touchend', onPressEnd, passiveCapture); } touchTarget = localTouchTargetEl = null; } if (mouseTarget === rootRef.value) { document.removeEventListener('mouseup', onPressEnd, passiveCapture); mouseTarget = null; } if (keyboardTarget === rootRef.value) { document.removeEventListener('keyup', onPressEnd, true); rootRef.value !== null && rootRef.value.removeEventListener('blur', onPressEnd, passiveCapture); keyboardTarget = null; } rootRef.value !== null && rootRef.value.classList.remove('q-btn--active'); } function onLoadingEvt (evt) { stopAndPrevent(evt); evt.qSkipRipple = true; } vue.onBeforeUnmount(() => { cleanup(true); }); // expose public methods Object.assign(proxy, { click: onClick }); return () => { let inner = []; props.icon !== void 0 && inner.push( vue.h(QIcon, { name: props.icon, left: props.stack === false && hasLabel.value === true, role: 'img', 'aria-hidden': 'true' }) ); hasLabel.value === true && inner.push( vue.h('span', { class: 'block' }, [ props.label ]) ); inner = hMergeSlot(slots.default, inner); if (props.iconRight !== void 0 && props.round === false) { inner.push( vue.h(QIcon, { name: props.iconRight, right: props.stack === false && hasLabel.value === true, role: 'img', 'aria-hidden': 'true' }) ); } const child = [ vue.h('span', { class: 'q-focus-helper', ref: blurTargetRef }) ]; if (props.loading === true && props.percentage !== void 0) { child.push( vue.h('span', { class: 'q-btn__progress absolute-full overflow-hidden' + (props.darkPercentage === true ? ' q-btn__progress--dark' : '') }, [ vue.h('span', { class: 'q-btn__progress-indicator fit block', style: percentageStyle.value }) ]) ); } child.push( vue.h('span', { class: 'q-btn__content text-center col items-center q-anchor--skip ' + innerClasses.value }, inner) ); props.loading !== null && child.push( vue.h(vue.Transition, { name: 'q-transition--fade' }, () => ( props.loading === true ? [ vue.h('span', { key: 'loading', class: 'absolute-full flex flex-center' }, slots.loading !== void 0 ? slots.loading() : [ vue.h(QSpinner) ]) ] : null )) ); return vue.withDirectives( vue.h( linkTag.value, nodeProps.value, child ), [ [ Ripple, ripple.value, void 0, rippleProps.value ] ] ) } } }); var QBtnGroup = createComponent({ name: 'QBtnGroup', props: { unelevated: Boolean, outline: Boolean, flat: Boolean, rounded: Boolean, square: Boolean, push: Boolean, stretch: Boolean, glossy: Boolean, spread: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => { const cls = [ 'unelevated', 'outline', 'flat', 'rounded', 'square', 'push', 'stretch', 'glossy' ] .filter(t => props[ t ] === true) .map(t => `q-btn-group--${ t }`).join(' '); return `q-btn-group row no-wrap${ cls.length !== 0 ? ' ' + cls : '' }` + (props.spread === true ? ' q-btn-group--spread' : ' inline') }); return () => vue.h('div', { class: classes.value }, hSlot(slots.default)) } }); function clearSelection () { if (window.getSelection !== void 0) { const selection = window.getSelection(); if (selection.empty !== void 0) { selection.empty(); } else if (selection.removeAllRanges !== void 0) { selection.removeAllRanges(); Platform.is.mobile !== true && selection.addRange(document.createRange()); } } else if (document.selection !== void 0) { document.selection.empty(); } } const useAnchorProps = { target: { default: true }, noParentEvent: Boolean, contextMenu: Boolean }; function useAnchor ({ showing, avoidEmit, // required for QPopupProxy (true) configureAnchorEl // optional }) { const { props, proxy, emit } = vue.getCurrentInstance(); const anchorEl = vue.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); vue.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`); } } } vue.watch(() => props.contextMenu, val => { if (anchorEl.value !== null) { unconfigureAnchorEl(); configureAnchorEl(val); } }); vue.watch(() => props.target, () => { if (anchorEl.value !== null) { unconfigureAnchorEl(); } pickAnchorEl(); }); vue.watch(() => props.noParentEvent, val => { if (anchorEl.value !== null) { if (val === true) { unconfigureAnchorEl(); } else { configureAnchorEl(); } } }); vue.onMounted(() => { pickAnchorEl(); if (avoidEmit !== true && props.modelValue === true && anchorEl.value === null) { emit('update:modelValue', false); } }); vue.onBeforeUnmount(() => { touchTimer !== null && clearTimeout(touchTimer); unconfigureAnchorEl(); }); return { anchorEl, canShow, anchorEvents } } function useScrollTarget ( props, configureScrollTarget ) { const localScrollTarget = vue.ref(null); let scrollFn; function changeScrollEvent (scrollTarget, fn) { const fnProp = `${ fn !== void 0 ? 'add' : 'remove' }EventListener`; const fnHandler = fn !== void 0 ? fn : scrollFn; if (scrollTarget !== window) { scrollTarget[ fnProp ]('scroll', fnHandler, listenOpts.passive); } window[ fnProp ]('scroll', fnHandler, listenOpts.passive); scrollFn = fn; } function unconfigureScrollTarget () { if (localScrollTarget.value !== null) { changeScrollEvent(localScrollTarget.value); localScrollTarget.value = null; } } const noParentEventWatcher = vue.watch(() => props.noParentEvent, () => { if (localScrollTarget.value !== null) { unconfigureScrollTarget(); configureScrollTarget(); } }); vue.onBeforeUnmount(noParentEventWatcher); return { localScrollTarget, unconfigureScrollTarget, changeScrollEvent } } const useModelToggleProps = { modelValue: { type: Boolean, default: null }, 'onUpdate:modelValue': [ Function, Array ] }; const useModelToggleEmits = [ 'beforeShow', 'show', 'beforeHide', 'hide' ]; // handleShow/handleHide -> removeTick(), self (& emit show) function useModelToggle ({ showing, canShow, // optional hideOnRouteChange, // optional handleShow, // optional handleHide, // optional processOnMount // optional }) { const vm = vue.getCurrentInstance(); const { props, emit, proxy } = vm; let payload; function toggle (evt) { if (showing.value === true) { hide(evt); } else { show(evt); } } function show (evt) { if ( props.disable === true || (evt !== void 0 && evt.qAnchorHandled === true) || (canShow !== void 0 && canShow(evt) !== true) ) { return } const listener = props[ 'onUpdate:modelValue' ] !== void 0; if (listener === true && false !== true) { emit('update:modelValue', true); payload = evt; vue.nextTick(() => { if (payload === evt) { payload = void 0; } }); } if (props.modelValue === null || listener === false || false) { processShow(evt); } } function processShow (evt) { if (showing.value === true) { return } showing.value = true; emit('beforeShow', evt); if (handleShow !== void 0) { handleShow(evt); } else { emit('show', evt); } } function hide (evt) { if (props.disable === true) { return } const listener = props[ 'onUpdate:modelValue' ] !== void 0; if (listener === true && false !== true) { emit('update:modelValue', false); payload = evt; vue.nextTick(() => { if (payload === evt) { payload = void 0; } }); } if (props.modelValue === null || listener === false || false) { processHide(evt); } } function processHide (evt) { if (showing.value === false) { return } showing.value = false; emit('beforeHide', evt); if (handleHide !== void 0) { handleHide(evt); } else { emit('hide', evt); } } function processModelChange (val) { if (props.disable === true && val === true) { if (props[ 'onUpdate:modelValue' ] !== void 0) { emit('update:modelValue', false); } } else if ((val === true) !== showing.value) { const fn = val === true ? processShow : processHide; fn(payload); } } vue.watch(() => props.modelValue, processModelChange); if (hideOnRouteChange !== void 0 && vmHasRouter(vm) === true) { vue.watch(() => proxy.$route.fullPath, () => { if (hideOnRouteChange.value === true && showing.value === true) { hide(); } }); } processOnMount === true && vue.onMounted(() => { processModelChange(props.modelValue); }); // expose public methods const publicMethods = { show, hide, toggle }; Object.assign(proxy, publicMethods); return publicMethods } let queue = []; let waitFlags = []; function clearFlag (flag) { waitFlags = waitFlags.filter(entry => entry !== flag); } function addFocusWaitFlag (flag) { clearFlag(flag); waitFlags.push(flag); } function removeFocusWaitFlag (flag) { clearFlag(flag); if (waitFlags.length === 0 && queue.length !== 0) { // only call last focus handler (can't focus multiple things at once) queue[ queue.length - 1 ](); queue = []; } } function addFocusFn (fn) { if (waitFlags.length === 0) { fn(); } else { queue.push(fn); } } function removeFocusFn (fn) { queue = queue.filter(entry => entry !== fn); } const nodesList = []; const portalTypeList = []; let portalIndex = 1; let target = document.body; function createGlobalNode (id, portalType) { const el = document.createElement('div'); el.id = portalType !== void 0 ? `q-portal--${ portalType }--${ portalIndex++ }` : id; if (globalConfig.globalNodes !== void 0) { const cls = globalConfig.globalNodes.class; if (cls !== void 0) { el.className = cls; } } target.appendChild(el); nodesList.push(el); portalTypeList.push(portalType); return el } function removeGlobalNode (el) { const nodeIndex = nodesList.indexOf(el); nodesList.splice(nodeIndex, 1); portalTypeList.splice(nodeIndex, 1); el.remove(); } function changeGlobalNodesTarget (newTarget) { if (newTarget === target) { return } target = newTarget; if ( target === document.body // or we have less than 2 dialogs: || portalTypeList.reduce((acc, type) => (type === 'dialog' ? acc + 1 : acc), 0) < 2 ) { nodesList.forEach(node => { if (node.contains(target) === false) { target.appendChild(node); } }); return } const lastDialogIndex = portalTypeList.lastIndexOf('dialog'); for (let i = 0; i < nodesList.length; i++) { const el = nodesList[ i ]; if ( (i === lastDialogIndex || portalTypeList[ i ] !== 'dialog') && el.contains(target) === false ) { target.appendChild(el); } } } const portalProxyList = []; function getPortalProxy (el) { return portalProxyList.find(proxy => proxy.contentEl !== null && proxy.contentEl.contains(el) ) } function closePortalMenus (proxy, evt) { do { if (proxy.$options.name === 'QMenu') { proxy.hide(evt); // is this a point of separation? if (proxy.$props.separateClosePopup === true) { return getParentProxy(proxy) } } else if (proxy.__qPortal === true) { // treat it as point of separation if parent is QPopupProxy // (so mobile matches desktop behavior) // and hide it too const parent = getParentProxy(proxy); if (parent !== void 0 && parent.$options.name === 'QPopupProxy') { proxy.hide(evt); return parent } else { return proxy } } proxy = getParentProxy(proxy); } while (proxy !== void 0 && proxy !== null) } function closePortals (proxy, evt, depth) { while (depth !== 0 && proxy !== void 0 && proxy !== null) { if (proxy.__qPortal === true) { depth--; if (proxy.$options.name === 'QMenu') { proxy = closePortalMenus(proxy, evt); continue } proxy.hide(evt); } proxy = getParentProxy(proxy); } } function isOnGlobalDialog (vm) { vm = vm.parent; while (vm !== void 0 && vm !== null) { if (vm.type.name === 'QGlobalDialog') { return true } if (vm.type.name === 'QDialog' || vm.type.name === 'QMenu') { return false } vm = vm.parent; } return false } // Warning! // You MUST specify "inheritAttrs: false" in your component function usePortal (vm, innerRef, renderPortalContent, type) { // showing, including while in show/hide transition const portalIsActive = vue.ref(false); // showing & not in any show/hide transition const portalIsAccessible = vue.ref(false); let portalEl = null; const focusObj = {}; const onGlobalDialog = type === 'dialog' && isOnGlobalDialog(vm); function showPortal (isReady) { if (isReady === true) { removeFocusWaitFlag(focusObj); portalIsAccessible.value = true; return } portalIsAccessible.value = false; if (portalIsActive.value === false) { if (onGlobalDialog === false && portalEl === null) { portalEl = createGlobalNode(false, type); } portalIsActive.value = true; // register portal portalProxyList.push(vm.proxy); addFocusWaitFlag(focusObj); } } function hidePortal (isReady) { portalIsAccessible.value = false; if (isReady !== true) { return } removeFocusWaitFlag(focusObj); portalIsActive.value = false; // unregister portal const index = portalProxyList.indexOf(vm.proxy); if (index !== -1) { portalProxyList.splice(index, 1); } if (portalEl !== null) { removeGlobalNode(portalEl); portalEl = null; } } vue.onUnmounted(() => { hidePortal(true); }); // needed for portal vm detection vm.proxy.__qPortal = true; // public way of accessing the rendered content injectProp(vm.proxy, 'contentEl', () => innerRef.value); return { showPortal, hidePortal, portalIsActive, portalIsAccessible, renderPortal: () => ( onGlobalDialog === true ? renderPortalContent() : ( portalIsActive.value === true ? [ vue.h(vue.Teleport, { to: portalEl }, renderPortalContent()) ] : void 0 ) ) } } const useTransitionProps = { transitionShow: { type: String, default: 'fade' }, transitionHide: { type: String, default: 'fade' }, transitionDuration: { type: [ String, Number ], default: 300 } }; function useTransition (props, defaultShowFn = () => {}, defaultHideFn = () => {}) { return { transitionProps: vue.computed(() => { const show = `q-transition--${ props.transitionShow || defaultShowFn() }`; const hide = `q-transition--${ props.transitionHide || defaultHideFn() }`; return { appear: true, enterFromClass: `${ show }-enter-from`, enterActiveClass: `${ show }-enter-active`, enterToClass: `${ show }-enter-to`, leaveFromClass: `${ hide }-leave-from`, leaveActiveClass: `${ hide }-leave-active`, leaveToClass: `${ hide }-leave-to` } }), transitionStyle: vue.computed(() => `--q-transition-duration: ${ props.transitionDuration }ms`) } } /* * Usage: * registerTick(fn) * removeTick() */ function useTick () { let tickFn; const vm = vue.getCurrentInstance(); function removeTick () { tickFn = void 0; } vue.onDeactivated(removeTick); vue.onBeforeUnmount(removeTick); return { removeTick, registerTick (fn) { tickFn = fn; vue.nextTick(() => { if (tickFn === fn) { // we also check if VM is destroyed, since if it // got to trigger one nextTick() we cannot stop it vmIsDestroyed(vm) === false && tickFn(); tickFn = void 0; } }); } } } /* * Usage: * registerTimeout(fn[, delay]) * removeTimeout() */ function useTimeout () { let timer = null; const vm = vue.getCurrentInstance(); function removeTimeout () { if (timer !== null) { clearTimeout(timer); timer = null; } } vue.onDeactivated(removeTimeout); vue.onBeforeUnmount(removeTimeout); return { removeTimeout, registerTimeout (fn, delay) { removeTimeout(); if (vmIsDestroyed(vm) === false) { timer = setTimeout(fn, delay); } } } } const scrollTargets = [ null, document, document.body, document.scrollingElement, document.documentElement ]; function getScrollTarget (el, targetEl) { let target = getElement$1(targetEl); if (target === void 0) { if (el === void 0 || el === null) { return window } target = el.closest('.scroll,.scroll-y,.overflow-auto'); } return scrollTargets.includes(target) ? window : target } function getScrollHeight (el) { return (el === window ? document.body : el).scrollHeight } function getScrollWidth (el) { return (el === window ? document.body : el).scrollWidth } function getVerticalScrollPosition (scrollTarget) { return scrollTarget === window ? window.pageYOffset || window.scrollY || document.body.scrollTop || 0 : scrollTarget.scrollTop } function getHorizontalScrollPosition (scrollTarget) { return scrollTarget === window ? window.pageXOffset || window.scrollX || document.body.scrollLeft || 0 : scrollTarget.scrollLeft } function animVerticalScrollTo (el, to, duration = 0 /* , prevTime */) { const prevTime = arguments[ 3 ] === void 0 ? performance.now() : arguments[ 3 ]; const pos = getVerticalScrollPosition(el); if (duration <= 0) { if (pos !== to) { setScroll$1(el, to); } return } requestAnimationFrame(nowTime => { const frameTime = nowTime - prevTime; const newPos = pos + (to - pos) / Math.max(frameTime, duration) * frameTime; setScroll$1(el, newPos); if (newPos !== to) { animVerticalScrollTo(el, to, duration - frameTime, nowTime); } }); } function animHorizontalScrollTo (el, to, duration = 0 /* , prevTime */) { const prevTime = arguments[ 3 ] === void 0 ? performance.now() : arguments[ 3 ]; const pos = getHorizontalScrollPosition(el); if (duration <= 0) { if (pos !== to) { setHorizontalScroll(el, to); } return } requestAnimationFrame(nowTime => { const frameTime = nowTime - prevTime; const newPos = pos + (to - pos) / Math.max(frameTime, duration) * frameTime; setHorizontalScroll(el, newPos); if (newPos !== to) { animHorizontalScrollTo(el, to, duration - frameTime, nowTime); } }); } function setScroll$1 (scrollTarget, offset) { if (scrollTarget === window) { window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, offset); return } scrollTarget.scrollTop = offset; } function setHorizontalScroll (scrollTarget, offset) { if (scrollTarget === window) { window.scrollTo(offset, window.pageYOffset || window.scrollY || document.body.scrollTop || 0); return } scrollTarget.scrollLeft = offset; } function setVerticalScrollPosition (scrollTarget, offset, duration) { if (duration) { animVerticalScrollTo(scrollTarget, offset, duration); return } setScroll$1(scrollTarget, offset); } function setHorizontalScrollPosition (scrollTarget, offset, duration) { if (duration) { animHorizontalScrollTo(scrollTarget, offset, duration); return } setHorizontalScroll(scrollTarget, offset); } let size; function getScrollbarWidth () { if (size !== undefined) { return size } const inner = document.createElement('p'), outer = document.createElement('div'); css(inner, { width: '100%', height: '200px' }); css(outer, { position: 'absolute', top: '0px', left: '0px', visibility: 'hidden', width: '200px', height: '150px', overflow: 'hidden' }); outer.appendChild(inner); document.body.appendChild(outer); const w1 = inner.offsetWidth; outer.style.overflow = 'scroll'; let w2 = inner.offsetWidth; if (w1 === w2) { w2 = outer.clientWidth; } outer.remove(); size = w1 - w2; return size } function hasScrollbar (el, onY = true) { if (!el || el.nodeType !== Node.ELEMENT_NODE) { return false } return onY ? ( el.scrollHeight > el.clientHeight && ( el.classList.contains('scroll') || el.classList.contains('overflow-auto') || [ 'auto', 'scroll' ].includes(window.getComputedStyle(el)[ 'overflow-y' ]) ) ) : ( el.scrollWidth > el.clientWidth && ( el.classList.contains('scroll') || el.classList.contains('overflow-auto') || [ 'auto', 'scroll' ].includes(window.getComputedStyle(el)[ 'overflow-x' ]) ) ) } var scroll = { getScrollTarget, getScrollHeight, getScrollWidth, getVerticalScrollPosition, getHorizontalScrollPosition, animVerticalScrollTo, animHorizontalScrollTo, setVerticalScrollPosition, setHorizontalScrollPosition, getScrollbarWidth, hasScrollbar }; const handlers$1 = []; let escDown; function onKeydown (evt) { escDown = evt.keyCode === 27; } function onBlur () { if (escDown === true) { escDown = false; } } function onKeyup (evt) { if (escDown === true) { escDown = false; if (isKeyCode(evt, 27) === true) { handlers$1[ handlers$1.length - 1 ](evt); } } } function update$4 (action) { window[ action ]('keydown', onKeydown); window[ action ]('blur', onBlur); window[ action ]('keyup', onKeyup); escDown = false; } function addEscapeKey (fn) { if (client.is.desktop === true) { handlers$1.push(fn); if (handlers$1.length === 1) { update$4('addEventListener'); } } } function removeEscapeKey (fn) { const index = handlers$1.indexOf(fn); if (index > -1) { handlers$1.splice(index, 1); if (handlers$1.length === 0) { update$4('removeEventListener'); } } } const handlers = []; function trigger$1 (e) { handlers[ handlers.length - 1 ](e); } function addFocusout (fn) { if (client.is.desktop === true) { handlers.push(fn); if (handlers.length === 1) { document.body.addEventListener('focusin', trigger$1); } } } function removeFocusout (fn) { const index = handlers.indexOf(fn); if (index > -1) { handlers.splice(index, 1); if (handlers.length === 0) { document.body.removeEventListener('focusin', trigger$1); } } } const { notPassiveCapture } = listenOpts, registeredList = []; function globalHandler (evt) { const target = evt.target; if ( target === void 0 || target.nodeType === 8 || target.classList.contains('no-pointer-events') === true ) { return } // check last portal vm if it's // a QDialog and not in seamless mode let portalIndex = portalProxyList.length - 1; while (portalIndex >= 0) { const proxy = portalProxyList[ portalIndex ].$; // skip QTooltip portals if (proxy.type.name === 'QTooltip') { portalIndex--; continue } if (proxy.type.name !== 'QDialog') { break } if (proxy.props.seamless !== true) { return } portalIndex--; } for (let i = registeredList.length - 1; i >= 0; i--) { const state = registeredList[ i ]; if ( ( state.anchorEl.value === null || state.anchorEl.value.contains(target) === false ) && ( target === document.body || ( state.innerRef.value !== null && state.innerRef.value.contains(target) === false ) ) ) { // mark the event as being processed by clickOutside // used to prevent refocus after menu close evt.qClickOutside = true; state.onClickOutside(evt); } else { return } } } function addClickOutside (clickOutsideProps) { registeredList.push(clickOutsideProps); if (registeredList.length === 1) { document.addEventListener('mousedown', globalHandler, notPassiveCapture); document.addEventListener('touchstart', globalHandler, notPassiveCapture); } } function removeClickOutside (clickOutsideProps) { const index = registeredList.findIndex(h => h === clickOutsideProps); if (index > -1) { registeredList.splice(index, 1); if (registeredList.length === 0) { document.removeEventListener('mousedown', globalHandler, notPassiveCapture); document.removeEventListener('touchstart', globalHandler, notPassiveCapture); } } } let vpLeft, vpTop; function validatePosition (pos) { const parts = pos.split(' '); if (parts.length !== 2) { return false } if ([ 'top', 'center', 'bottom' ].includes(parts[ 0 ]) !== true) { console.error('Anchor/Self position must start with one of top/center/bottom'); return false } if ([ 'left', 'middle', 'right', 'start', 'end' ].includes(parts[ 1 ]) !== true) { console.error('Anchor/Self position must end with one of left/middle/right/start/end'); return false } return true } function validateOffset (val) { if (!val) { return true } if (val.length !== 2) { return false } if (typeof val[ 0 ] !== 'number' || typeof val[ 1 ] !== 'number') { return false } return true } const horizontalPos = { 'start#ltr': 'left', 'start#rtl': 'right', 'end#ltr': 'right', 'end#rtl': 'left' } ;[ 'left', 'middle', 'right' ].forEach(pos => { horizontalPos[ `${ pos }#ltr` ] = pos; horizontalPos[ `${ pos }#rtl` ] = pos; }); function parsePosition (pos, rtl) { const parts = pos.split(' '); return { vertical: parts[ 0 ], horizontal: horizontalPos[ `${ parts[ 1 ] }#${ rtl === true ? 'rtl' : 'ltr' }` ] } } function getAnchorProps (el, offset) { let { top, left, right, bottom, width, height } = el.getBoundingClientRect(); if (offset !== void 0) { top -= offset[ 1 ]; left -= offset[ 0 ]; bottom += offset[ 1 ]; right += offset[ 0 ]; width += offset[ 0 ]; height += offset[ 1 ]; } return { top, bottom, height, left, right, width, middle: left + (right - left) / 2, center: top + (bottom - top) / 2 } } function getAbsoluteAnchorProps (el, absoluteOffset, offset) { let { top, left } = el.getBoundingClientRect(); top += absoluteOffset.top; left += absoluteOffset.left; if (offset !== void 0) { top += offset[ 1 ]; left += offset[ 0 ]; } return { top, bottom: top + 1, height: 1, left, right: left + 1, width: 1, middle: left, center: top } } function getTargetProps (width, height) { return { top: 0, center: height / 2, bottom: height, left: 0, middle: width / 2, right: width } } function getTopLeftProps (anchorProps, targetProps, anchorOrigin, selfOrigin) { return { top: anchorProps[ anchorOrigin.vertical ] - targetProps[ selfOrigin.vertical ], left: anchorProps[ anchorOrigin.horizontal ] - targetProps[ selfOrigin.horizontal ] } } function setPosition (cfg, retryNumber = 0) { if ( cfg.targetEl === null || cfg.anchorEl === null || retryNumber > 5 // we should try only a few times ) { return } // some browsers report zero height or width because // we are trying too early to get these dimensions if (cfg.targetEl.offsetHeight === 0 || cfg.targetEl.offsetWidth === 0) { setTimeout(() => { setPosition(cfg, retryNumber + 1); }, 10); return } const { targetEl, offset, anchorEl, anchorOrigin, selfOrigin, absoluteOffset, fit, cover, maxHeight, maxWidth } = cfg; if (client.is.ios === true && window.visualViewport !== void 0) { // uses the q-position-engine CSS class const el = document.body.style; const { offsetLeft: left, offsetTop: top } = window.visualViewport; if (left !== vpLeft) { el.setProperty('--q-pe-left', left + 'px'); vpLeft = left; } if (top !== vpTop) { el.setProperty('--q-pe-top', top + 'px'); vpTop = top; } } // scroll position might change // if max-height/-width changes, so we // need to restore it after we calculate // the new positioning const { scrollLeft, scrollTop } = targetEl; const anchorProps = absoluteOffset === void 0 ? getAnchorProps(anchorEl, cover === true ? [ 0, 0 ] : offset) : getAbsoluteAnchorProps(anchorEl, absoluteOffset, offset); // we "reset" the critical CSS properties // so we can take an accurate measurement Object.assign(targetEl.style, { top: 0, left: 0, minWidth: null, minHeight: null, maxWidth: maxWidth || '100vw', maxHeight: maxHeight || '100vh', visibility: 'visible' }); const { offsetWidth: origElWidth, offsetHeight: origElHeight } = targetEl; const { elWidth, elHeight } = fit === true || cover === true ? { elWidth: Math.max(anchorProps.width, origElWidth), elHeight: cover === true ? Math.max(anchorProps.height, origElHeight) : origElHeight } : { elWidth: origElWidth, elHeight: origElHeight }; let elStyle = { maxWidth, maxHeight }; if (fit === true || cover === true) { elStyle.minWidth = anchorProps.width + 'px'; if (cover === true) { elStyle.minHeight = anchorProps.height + 'px'; } } Object.assign(targetEl.style, elStyle); const targetProps = getTargetProps(elWidth, elHeight); let props = getTopLeftProps(anchorProps, targetProps, anchorOrigin, selfOrigin); if (absoluteOffset === void 0 || offset === void 0) { applyBoundaries(props, anchorProps, targetProps, anchorOrigin, selfOrigin); } else { // we have touch position or context menu with offset const { top, left } = props; // cache initial values // apply initial boundaries applyBoundaries(props, anchorProps, targetProps, anchorOrigin, selfOrigin); let hasChanged = false; // did it flip vertically? if (props.top !== top) { hasChanged = true; const offsetY = 2 * offset[ 1 ]; anchorProps.center = anchorProps.top -= offsetY; anchorProps.bottom -= offsetY + 2; } // did it flip horizontally? if (props.left !== left) { hasChanged = true; const offsetX = 2 * offset[ 0 ]; anchorProps.middle = anchorProps.left -= offsetX; anchorProps.right -= offsetX + 2; } if (hasChanged === true) { // re-calculate props with the new anchor props = getTopLeftProps(anchorProps, targetProps, anchorOrigin, selfOrigin); // and re-apply boundaries applyBoundaries(props, anchorProps, targetProps, anchorOrigin, selfOrigin); } } elStyle = { top: props.top + 'px', left: props.left + 'px' }; if (props.maxHeight !== void 0) { elStyle.maxHeight = props.maxHeight + 'px'; if (anchorProps.height > props.maxHeight) { elStyle.minHeight = elStyle.maxHeight; } } if (props.maxWidth !== void 0) { elStyle.maxWidth = props.maxWidth + 'px'; if (anchorProps.width > props.maxWidth) { elStyle.minWidth = elStyle.maxWidth; } } Object.assign(targetEl.style, elStyle); // restore scroll position if (targetEl.scrollTop !== scrollTop) { targetEl.scrollTop = scrollTop; } if (targetEl.scrollLeft !== scrollLeft) { targetEl.scrollLeft = scrollLeft; } } function applyBoundaries (props, anchorProps, targetProps, anchorOrigin, selfOrigin) { const currentHeight = targetProps.bottom, currentWidth = targetProps.right, margin = getScrollbarWidth(), innerHeight = window.innerHeight - margin, innerWidth = document.body.clientWidth; if (props.top < 0 || props.top + currentHeight > innerHeight) { if (selfOrigin.vertical === 'center') { props.top = anchorProps[ anchorOrigin.vertical ] > innerHeight / 2 ? Math.max(0, innerHeight - currentHeight) : 0; props.maxHeight = Math.min(currentHeight, innerHeight); } else if (anchorProps[ anchorOrigin.vertical ] > innerHeight / 2) { const anchorY = Math.min( innerHeight, anchorOrigin.vertical === 'center' ? anchorProps.center : (anchorOrigin.vertical === selfOrigin.vertical ? anchorProps.bottom : anchorProps.top) ); props.maxHeight = Math.min(currentHeight, anchorY); props.top = Math.max(0, anchorY - currentHeight); } else { props.top = Math.max(0, anchorOrigin.vertical === 'center' ? anchorProps.center : (anchorOrigin.vertical === selfOrigin.vertical ? anchorProps.top : anchorProps.bottom) ); props.maxHeight = Math.min(currentHeight, innerHeight - props.top); } } if (props.left < 0 || props.left + currentWidth > innerWidth) { props.maxWidth = Math.min(currentWidth, innerWidth); if (selfOrigin.horizontal === 'middle') { props.left = anchorProps[ anchorOrigin.horizontal ] > innerWidth / 2 ? Math.max(0, innerWidth - currentWidth) : 0; } else if (anchorProps[ anchorOrigin.horizontal ] > innerWidth / 2) { const anchorX = Math.min( innerWidth, anchorOrigin.horizontal === 'middle' ? anchorProps.middle : (anchorOrigin.horizontal === selfOrigin.horizontal ? anchorProps.right : anchorProps.left) ); props.maxWidth = Math.min(currentWidth, anchorX); props.left = Math.max(0, anchorX - props.maxWidth); } else { props.left = Math.max(0, anchorOrigin.horizontal === 'middle' ? anchorProps.middle : (anchorOrigin.horizontal === selfOrigin.horizontal ? anchorProps.left : anchorProps.right) ); props.maxWidth = Math.min(currentWidth, innerWidth - props.left); } } } var QMenu = createComponent({ name: 'QMenu', inheritAttrs: false, props: { ...useAnchorProps, ...useModelToggleProps, ...useDarkProps, ...useTransitionProps, persistent: Boolean, autoClose: Boolean, separateClosePopup: Boolean, noRouteDismiss: Boolean, noRefocus: Boolean, noFocus: Boolean, fit: Boolean, cover: Boolean, square: Boolean, anchor: { type: String, validator: validatePosition }, self: { type: String, validator: validatePosition }, offset: { type: Array, validator: validateOffset }, scrollTarget: { default: void 0 }, touchPosition: Boolean, maxHeight: { type: String, default: null }, maxWidth: { type: String, default: null } }, emits: [ ...useModelToggleEmits, 'click', 'escapeKey' ], setup (props, { slots, emit, attrs }) { let refocusTarget = null, absoluteOffset, unwatchPosition, avoidAutoClose; const vm = vue.getCurrentInstance(); const { proxy } = vm; const { $q } = proxy; const innerRef = vue.ref(null); const showing = vue.ref(false); const hideOnRouteChange = vue.computed(() => props.persistent !== true && props.noRouteDismiss !== true ); const isDark = useDark(props, $q); const { registerTick, removeTick } = useTick(); const { registerTimeout } = useTimeout(); const { transitionProps, transitionStyle } = useTransition(props); const { localScrollTarget, changeScrollEvent, unconfigureScrollTarget } = useScrollTarget(props, configureScrollTarget); const { anchorEl, canShow } = useAnchor({ showing }); const { hide } = useModelToggle({ showing, canShow, handleShow, handleHide, hideOnRouteChange, processOnMount: true }); const { showPortal, hidePortal, renderPortal } = usePortal(vm, innerRef, renderPortalContent, 'menu'); const clickOutsideProps = { anchorEl, innerRef, onClickOutside (e) { if (props.persistent !== true && showing.value === true) { hide(e); if ( // always prevent touch event e.type === 'touchstart' // prevent click if it's on a dialog backdrop || e.target.classList.contains('q-dialog__backdrop') ) { stopAndPrevent(e); } return true } } }; const anchorOrigin = vue.computed(() => parsePosition( props.anchor || ( props.cover === true ? 'center middle' : 'bottom start' ), $q.lang.rtl ) ); const selfOrigin = vue.computed(() => ( props.cover === true ? anchorOrigin.value : parsePosition(props.self || 'top start', $q.lang.rtl) )); const menuClass = vue.computed(() => (props.square === true ? ' q-menu--square' : '') + (isDark.value === true ? ' q-menu--dark q-dark' : '') ); const onEvents = vue.computed(() => ( props.autoClose === true ? { onClick: onAutoClose } : {} )); const handlesFocus = vue.computed(() => showing.value === true && props.persistent !== true ); vue.watch(handlesFocus, val => { if (val === true) { addEscapeKey(onEscapeKey); addClickOutside(clickOutsideProps); } else { removeEscapeKey(onEscapeKey); removeClickOutside(clickOutsideProps); } }); function focus () { addFocusFn(() => { let node = innerRef.value; if (node && node.contains(document.activeElement) !== true) { node = node.querySelector('[autofocus][tabindex], [data-autofocus][tabindex]') || node.querySelector('[autofocus] [tabindex], [data-autofocus] [tabindex]') || node.querySelector('[autofocus], [data-autofocus]') || node; node.focus({ preventScroll: true }); } }); } function handleShow (evt) { refocusTarget = props.noRefocus === false ? document.activeElement : null; addFocusout(onFocusout); showPortal(); configureScrollTarget(); absoluteOffset = void 0; if (evt !== void 0 && (props.touchPosition || props.contextMenu)) { const pos = position(evt); if (pos.left !== void 0) { const { top, left } = anchorEl.value.getBoundingClientRect(); absoluteOffset = { left: pos.left - left, top: pos.top - top }; } } if (unwatchPosition === void 0) { unwatchPosition = vue.watch( () => $q.screen.width + '|' + $q.screen.height + '|' + props.self + '|' + props.anchor + '|' + $q.lang.rtl, updatePosition ); } if (props.noFocus !== true) { document.activeElement.blur(); } // should removeTick() if this gets removed registerTick(() => { updatePosition(); props.noFocus !== true && focus(); }); // should removeTimeout() if this gets removed registerTimeout(() => { // required in order to avoid the "double-tap needed" issue if ($q.platform.is.ios === true) { // if auto-close, then this click should // not close the menu avoidAutoClose = props.autoClose; innerRef.value.click(); } updatePosition(); showPortal(true); // done showing portal emit('show', evt); }, props.transitionDuration); } function handleHide (evt) { removeTick(); hidePortal(); anchorCleanup(true); if ( refocusTarget !== null && ( // menu was hidden from code or ESC plugin evt === void 0 // menu was not closed from a mouse or touch clickOutside || evt.qClickOutside !== true ) ) { ((evt && evt.type.indexOf('key') === 0 ? refocusTarget.closest('[tabindex]:not([tabindex^="-"])') : void 0 ) || refocusTarget).focus(); refocusTarget = null; } // should removeTimeout() if this gets removed registerTimeout(() => { hidePortal(true); // done hiding, now destroy emit('hide', evt); }, props.transitionDuration); } function anchorCleanup (hiding) { absoluteOffset = void 0; if (unwatchPosition !== void 0) { unwatchPosition(); unwatchPosition = void 0; } if (hiding === true || showing.value === true) { removeFocusout(onFocusout); unconfigureScrollTarget(); removeClickOutside(clickOutsideProps); removeEscapeKey(onEscapeKey); } if (hiding !== true) { refocusTarget = null; } } function configureScrollTarget () { if (anchorEl.value !== null || props.scrollTarget !== void 0) { localScrollTarget.value = getScrollTarget(anchorEl.value, props.scrollTarget); changeScrollEvent(localScrollTarget.value, updatePosition); } } function onAutoClose (e) { // if auto-close, then the ios double-tap fix which // issues a click should not close the menu if (avoidAutoClose !== true) { closePortalMenus(proxy, e); emit('click', e); } else { avoidAutoClose = false; } } function onFocusout (evt) { // the focus is not in a vue child component if ( handlesFocus.value === true && props.noFocus !== true && childHasFocus(innerRef.value, evt.target) !== true ) { focus(); } } function onEscapeKey (evt) { emit('escapeKey'); hide(evt); } function updatePosition () { setPosition({ targetEl: innerRef.value, offset: props.offset, anchorEl: anchorEl.value, anchorOrigin: anchorOrigin.value, selfOrigin: selfOrigin.value, absoluteOffset, fit: props.fit, cover: props.cover, maxHeight: props.maxHeight, maxWidth: props.maxWidth }); } function renderPortalContent () { return vue.h( vue.Transition, transitionProps.value, () => ( showing.value === true ? vue.h('div', { role: 'menu', ...attrs, ref: innerRef, tabindex: -1, class: [ 'q-menu q-position-engine scroll' + menuClass.value, attrs.class ], style: [ attrs.style, transitionStyle.value ], ...onEvents.value }, hSlot(slots.default)) : null ) ) } vue.onBeforeUnmount(anchorCleanup); // expose public methods Object.assign(proxy, { focus, updatePosition }); return renderPortal } }); /** * Based on the work of https://github.com/jchook/uuid-random */ let buf, bufIdx = 0; const hexBytes = new Array(256); // Pre-calculate toString(16) for speed for (let i = 0; i < 256; i++) { hexBytes[ i ] = (i + 0x100).toString(16).substring(1); } // Use best available PRNG const randomBytes = (() => { // Node & Browser support const lib = typeof crypto !== 'undefined' ? crypto : ( typeof window !== 'undefined' ? window.crypto || window.msCrypto : void 0 ); if (lib !== void 0) { if (lib.randomBytes !== void 0) { return lib.randomBytes } if (lib.getRandomValues !== void 0) { return n => { const bytes = new Uint8Array(n); lib.getRandomValues(bytes); return bytes } } } return n => { const r = []; for (let i = n; i > 0; i--) { r.push(Math.floor(Math.random() * 256)); } return r } })(); // Buffer random numbers for speed // Reduce memory usage by decreasing this number (min 16) // or improve speed by increasing this number (try 16384) const BUFFER_SIZE = 4096; function uid$3 () { // Buffer some random bytes for speed if (buf === void 0 || (bufIdx + 16 > BUFFER_SIZE)) { bufIdx = 0; buf = randomBytes(BUFFER_SIZE); } const b = Array.prototype.slice.call(buf, bufIdx, (bufIdx += 16)); b[ 6 ] = (b[ 6 ] & 0x0f) | 0x40; b[ 8 ] = (b[ 8 ] & 0x3f) | 0x80; return hexBytes[ b[ 0 ] ] + hexBytes[ b[ 1 ] ] + hexBytes[ b[ 2 ] ] + hexBytes[ b[ 3 ] ] + '-' + hexBytes[ b[ 4 ] ] + hexBytes[ b[ 5 ] ] + '-' + hexBytes[ b[ 6 ] ] + hexBytes[ b[ 7 ] ] + '-' + hexBytes[ b[ 8 ] ] + hexBytes[ b[ 9 ] ] + '-' + hexBytes[ b[ 10 ] ] + hexBytes[ b[ 11 ] ] + hexBytes[ b[ 12 ] ] + hexBytes[ b[ 13 ] ] + hexBytes[ b[ 14 ] ] + hexBytes[ b[ 15 ] ] } const btnPropsList = Object.keys(useBtnProps); const passBtnProps = props => btnPropsList.reduce( (acc, key) => { const val = props[ key ]; if (val !== void 0) { acc[ key ] = val; } return acc }, {} ); var QBtnDropdown = createComponent({ name: 'QBtnDropdown', props: { ...useBtnProps, ...useTransitionProps, modelValue: Boolean, split: Boolean, dropdownIcon: String, contentClass: [ Array, String, Object ], contentStyle: [ Array, String, Object ], cover: Boolean, persistent: Boolean, noRouteDismiss: Boolean, autoClose: Boolean, menuAnchor: { type: String, default: 'bottom end' }, menuSelf: { type: String, default: 'top end' }, menuOffset: Array, disableMainBtn: Boolean, disableDropdown: Boolean, noIconAnimation: Boolean, toggleAriaLabel: String }, emits: [ 'update:modelValue', 'click', 'beforeShow', 'show', 'beforeHide', 'hide' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const showing = vue.ref(props.modelValue); const menuRef = vue.ref(null); const targetUid = uid$3(); const ariaAttrs = vue.computed(() => { const acc = { 'aria-expanded': showing.value === true ? 'true' : 'false', 'aria-haspopup': 'true', 'aria-controls': targetUid, 'aria-label': props.toggleAriaLabel || proxy.$q.lang.label[ showing.value === true ? 'collapse' : 'expand' ](props.label) }; if ( props.disable === true || ( (props.split === false && props.disableMainBtn === true) || props.disableDropdown === true ) ) { acc[ 'aria-disabled' ] = 'true'; } return acc }); const iconClass = vue.computed(() => 'q-btn-dropdown__arrow' + (showing.value === true && props.noIconAnimation === false ? ' rotate-180' : '') + (props.split === false ? ' q-btn-dropdown__arrow-container' : '') ); const btnDesignAttr = vue.computed(() => getBtnDesignAttr(props)); const btnProps = vue.computed(() => passBtnProps(props)); vue.watch(() => props.modelValue, val => { menuRef.value !== null && menuRef.value[ val ? 'show' : 'hide' ](); }); vue.watch(() => props.split, hide); function onBeforeShow (e) { showing.value = true; emit('beforeShow', e); } function onShow (e) { emit('show', e); emit('update:modelValue', true); } function onBeforeHide (e) { showing.value = false; emit('beforeHide', e); } function onHide (e) { emit('hide', e); emit('update:modelValue', false); } function onClick (e) { emit('click', e); } function onClickHide (e) { stop(e); hide(); emit('click', e); } function toggle (evt) { menuRef.value !== null && menuRef.value.toggle(evt); } function show (evt) { menuRef.value !== null && menuRef.value.show(evt); } function hide (evt) { menuRef.value !== null && menuRef.value.hide(evt); } // expose public methods Object.assign(proxy, { show, hide, toggle }); vue.onMounted(() => { props.modelValue === true && show(); }); return () => { const Arrow = [ vue.h(QIcon, { class: iconClass.value, name: props.dropdownIcon || proxy.$q.iconSet.arrow.dropdown }) ]; props.disableDropdown !== true && Arrow.push( vue.h(QMenu, { ref: menuRef, id: targetUid, class: props.contentClass, style: props.contentStyle, cover: props.cover, fit: true, persistent: props.persistent, noRouteDismiss: props.noRouteDismiss, autoClose: props.autoClose, anchor: props.menuAnchor, self: props.menuSelf, offset: props.menuOffset, separateClosePopup: true, transitionShow: props.transitionShow, transitionHide: props.transitionHide, transitionDuration: props.transitionDuration, onBeforeShow, onShow, onBeforeHide, onHide }, slots.default) ); if (props.split === false) { return vue.h(QBtn, { class: 'q-btn-dropdown q-btn-dropdown--simple', ...btnProps.value, ...ariaAttrs.value, disable: props.disable === true || props.disableMainBtn === true, noWrap: true, round: false, onClick }, { default: () => hSlot(slots.label, []).concat(Arrow), loading: slots.loading }) } return vue.h(QBtnGroup, { class: 'q-btn-dropdown q-btn-dropdown--split no-wrap q-btn-item', rounded: props.rounded, square: props.square, ...btnDesignAttr.value, glossy: props.glossy, stretch: props.stretch }, () => [ vue.h(QBtn, { class: 'q-btn-dropdown--current', ...btnProps.value, disable: props.disable === true || props.disableMainBtn === true, noWrap: true, round: false, onClick: onClickHide }, { default: slots.label, loading: slots.loading }), vue.h(QBtn, { class: 'q-btn-dropdown__arrow-container q-anchor--skip', ...ariaAttrs.value, ...btnDesignAttr.value, disable: props.disable === true || props.disableDropdown === true, rounded: props.rounded, color: props.color, textColor: props.textColor, dense: props.dense, size: props.size, padding: props.padding, ripple: props.ripple }, () => Arrow) ]) } } }); const useFormProps = { name: String }; function useFormAttrs (props) { return vue.computed(() => ({ type: 'hidden', name: props.name, value: props.modelValue })) } function useFormInject (formAttrs = {}) { return (child, action, className) => { child[ action ]( vue.h('input', { class: 'hidden' + (className || ''), ...formAttrs.value }) ); } } function useFormInputNameAttr (props) { return vue.computed(() => props.name || props.for) } var QBtnToggle = createComponent({ name: 'QBtnToggle', props: { ...useFormProps, modelValue: { required: true }, options: { type: Array, required: true, validator: v => v.every( opt => ('label' in opt || 'icon' in opt || 'slot' in opt) && 'value' in opt ) }, // To avoid seeing the active raise shadow through // the transparent button, give it a color (even white) color: String, textColor: String, toggleColor: { type: String, default: 'primary' }, toggleTextColor: String, outline: Boolean, flat: Boolean, unelevated: Boolean, rounded: Boolean, push: Boolean, glossy: Boolean, size: String, padding: String, noCaps: Boolean, noWrap: Boolean, dense: Boolean, readonly: Boolean, disable: Boolean, stack: Boolean, stretch: Boolean, spread: Boolean, clearable: Boolean, ripple: { type: [ Boolean, Object ], default: true } }, emits: [ 'update:modelValue', 'clear', 'click' ], setup (props, { slots, emit }) { const hasActiveValue = vue.computed(() => props.options.find(opt => opt.value === props.modelValue) !== void 0 ); const formAttrs = vue.computed(() => ({ type: 'hidden', name: props.name, value: props.modelValue })); const injectFormInput = useFormInject(formAttrs); const btnDesignAttr = vue.computed(() => getBtnDesignAttr(props)); const btnOptionDesign = vue.computed(() => ({ rounded: props.rounded, dense: props.dense, ...btnDesignAttr.value })); const btnOptions = vue.computed(() => props.options.map((item, i) => { const { attrs, value, slot, ...opt } = item; return { slot, props: { key: i, 'aria-pressed': value === props.modelValue ? 'true' : 'false', ...attrs, ...opt, ...btnOptionDesign.value, disable: props.disable === true || opt.disable === true, // Options that come from the button specific options first, then from general props color: value === props.modelValue ? mergeOpt(opt, 'toggleColor') : mergeOpt(opt, 'color'), textColor: value === props.modelValue ? mergeOpt(opt, 'toggleTextColor') : mergeOpt(opt, 'textColor'), noCaps: mergeOpt(opt, 'noCaps') === true, noWrap: mergeOpt(opt, 'noWrap') === true, size: mergeOpt(opt, 'size'), padding: mergeOpt(opt, 'padding'), ripple: mergeOpt(opt, 'ripple'), stack: mergeOpt(opt, 'stack') === true, stretch: mergeOpt(opt, 'stretch') === true, onClick (e) { set(value, item, e); } } } })); function set (value, opt, e) { if (props.readonly !== true) { if (props.modelValue === value) { if (props.clearable === true) { emit('update:modelValue', null, null); emit('clear'); } } else { emit('update:modelValue', value, opt); } emit('click', e); } } function mergeOpt (opt, key) { return opt[ key ] === void 0 ? props[ key ] : opt[ key ] } function getContent () { const child = btnOptions.value.map(opt => { return vue.h(QBtn, opt.props, opt.slot !== void 0 ? slots[ opt.slot ] : void 0) }); if (props.name !== void 0 && props.disable !== true && hasActiveValue.value === true) { injectFormInput(child, 'push'); } return hMergeSlot(slots.default, child) } return () => vue.h(QBtnGroup, { class: 'q-btn-toggle', ...btnDesignAttr.value, rounded: props.rounded, stretch: props.stretch, glossy: props.glossy, spread: props.spread }, getContent) } }); var QCard = createComponent({ name: 'QCard', props: { ...useDarkProps, tag: { type: String, default: 'div' }, square: Boolean, flat: Boolean, bordered: Boolean }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const classes = vue.computed(() => 'q-card' + (isDark.value === true ? ' q-card--dark q-dark' : '') + (props.bordered === true ? ' q-card--bordered' : '') + (props.square === true ? ' q-card--square no-border-radius' : '') + (props.flat === true ? ' q-card--flat no-shadow' : '') ); return () => vue.h(props.tag, { class: classes.value }, hSlot(slots.default)) } }); var QCardSection = createComponent({ name: 'QCardSection', props: { tag: { type: String, default: 'div' }, horizontal: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => 'q-card__section' + ` q-card__section--${ props.horizontal === true ? 'horiz row no-wrap' : 'vert' }` ); return () => vue.h(props.tag, { class: classes.value }, hSlot(slots.default)) } }); var QCardActions = createComponent({ name: 'QCardActions', props: { ...useAlignProps, vertical: Boolean }, setup (props, { slots }) { const alignClass = useAlign(props); const classes = vue.computed(() => `q-card__actions ${ alignClass.value }` + ` q-card__actions--${ props.vertical === true ? 'vert column' : 'horiz row' }` ); return () => vue.h('div', { class: classes.value }, hSlot(slots.default)) } }); const modifiersAll = { left: true, right: true, up: true, down: true, horizontal: true, vertical: true }; const directionList = Object.keys(modifiersAll); modifiersAll.all = true; function getModifierDirections (mod) { const dir = {}; for (const direction of directionList) { if (mod[ direction ] === true) { dir[ direction ] = true; } } if (Object.keys(dir).length === 0) { return modifiersAll } if (dir.horizontal === true) { dir.left = dir.right = true; } else if (dir.left === true && dir.right === true) { dir.horizontal = true; } if (dir.vertical === true) { dir.up = dir.down = true; } else if (dir.up === true && dir.down === true) { dir.vertical = true; } if (dir.horizontal === true && dir.vertical === true) { dir.all = true; } return dir } // This is especially important (not the main reason, but important) // for TouchSwipe directive running on Firefox // because text selection on such elements cannot be determined // without additional work (on top of getSelection() API) // https://bugzilla.mozilla.org/show_bug.cgi?id=85686 const avoidNodeNamesList = [ 'INPUT', 'TEXTAREA' ]; function shouldStart (evt, ctx) { return ctx.event === void 0 && evt.target !== void 0 && evt.target.draggable !== true && typeof ctx.handler === 'function' && avoidNodeNamesList.includes(evt.target.nodeName.toUpperCase()) === false && (evt.qClonedBy === void 0 || evt.qClonedBy.indexOf(ctx.uid) === -1) } function parseArg (arg) { // delta (min velocity -- dist / time) // mobile min distance on first move // desktop min distance until deciding if it's a swipe or not const data = [ 0.06, 6, 50 ]; if (typeof arg === 'string' && arg.length) { arg.split(':').forEach((val, index) => { const v = parseFloat(val); v && (data[ index ] = v); }); } return data } var TouchSwipe = createDirective({ name: 'touch-swipe', beforeMount (el, { value, arg, modifiers }) { // early return, we don't need to do anything if (modifiers.mouse !== true && client.has.touch !== true) { return } const mouseCapture = modifiers.mouseCapture === true ? 'Capture' : ''; const ctx = { handler: value, sensitivity: parseArg(arg), direction: getModifierDirections(modifiers), noop, mouseStart (evt) { if (shouldStart(evt, ctx) && leftClick(evt)) { addEvt(ctx, 'temp', [ [ document, 'mousemove', 'move', `notPassive${ mouseCapture }` ], [ document, 'mouseup', 'end', 'notPassiveCapture' ] ]); ctx.start(evt, true); } }, touchStart (evt) { if (shouldStart(evt, ctx)) { const target = evt.target; addEvt(ctx, 'temp', [ [ target, 'touchmove', 'move', 'notPassiveCapture' ], [ target, 'touchcancel', 'end', 'notPassiveCapture' ], [ target, 'touchend', 'end', 'notPassiveCapture' ] ]); ctx.start(evt); } }, start (evt, mouseEvent) { client.is.firefox === true && preventDraggable(el, true); const pos = position(evt); ctx.event = { x: pos.left, y: pos.top, time: Date.now(), mouse: mouseEvent === true, dir: false }; }, move (evt) { if (ctx.event === void 0) { return } if (ctx.event.dir !== false) { stopAndPrevent(evt); return } const time = Date.now() - ctx.event.time; if (time === 0) { return } const pos = position(evt), distX = pos.left - ctx.event.x, absX = Math.abs(distX), distY = pos.top - ctx.event.y, absY = Math.abs(distY); if (ctx.event.mouse !== true) { if (absX < ctx.sensitivity[ 1 ] && absY < ctx.sensitivity[ 1 ]) { ctx.end(evt); return } } // is user trying to select text? // if so, then something should be reported here // (previous selection, if any, was discarded when swipe started) else if (window.getSelection().toString() !== '') { ctx.end(evt); return } else if (absX < ctx.sensitivity[ 2 ] && absY < ctx.sensitivity[ 2 ]) { return } const velX = absX / time, velY = absY / time; if ( ctx.direction.vertical === true && absX < absY && absX < 100 && velY > ctx.sensitivity[ 0 ] ) { ctx.event.dir = distY < 0 ? 'up' : 'down'; } if ( ctx.direction.horizontal === true && absX > absY && absY < 100 && velX > ctx.sensitivity[ 0 ] ) { ctx.event.dir = distX < 0 ? 'left' : 'right'; } if ( ctx.direction.up === true && absX < absY && distY < 0 && absX < 100 && velY > ctx.sensitivity[ 0 ] ) { ctx.event.dir = 'up'; } if ( ctx.direction.down === true && absX < absY && distY > 0 && absX < 100 && velY > ctx.sensitivity[ 0 ] ) { ctx.event.dir = 'down'; } if ( ctx.direction.left === true && absX > absY && distX < 0 && absY < 100 && velX > ctx.sensitivity[ 0 ] ) { ctx.event.dir = 'left'; } if ( ctx.direction.right === true && absX > absY && distX > 0 && absY < 100 && velX > ctx.sensitivity[ 0 ] ) { ctx.event.dir = 'right'; } if (ctx.event.dir !== false) { stopAndPrevent(evt); if (ctx.event.mouse === true) { document.body.classList.add('no-pointer-events--children'); document.body.classList.add('non-selectable'); clearSelection(); ctx.styleCleanup = withDelay => { ctx.styleCleanup = void 0; document.body.classList.remove('non-selectable'); const remove = () => { document.body.classList.remove('no-pointer-events--children'); }; if (withDelay === true) { setTimeout(remove, 50); } else { remove(); } }; } ctx.handler({ evt, touch: ctx.event.mouse !== true, mouse: ctx.event.mouse, direction: ctx.event.dir, duration: time, distance: { x: absX, y: absY } }); } else { ctx.end(evt); } }, end (evt) { if (ctx.event === void 0) { return } cleanEvt(ctx, 'temp'); client.is.firefox === true && preventDraggable(el, false); ctx.styleCleanup !== void 0 && ctx.styleCleanup(true); evt !== void 0 && ctx.event.dir !== false && stopAndPrevent(evt); ctx.event = void 0; } }; el.__qtouchswipe = ctx; if (modifiers.mouse === true) { // account for UMD too where modifiers will be lowercased to work const capture = modifiers.mouseCapture === true || modifiers.mousecapture === true ? 'Capture' : ''; addEvt(ctx, 'main', [ [ el, 'mousedown', 'mouseStart', `passive${ capture }` ] ]); } client.has.touch === true && addEvt(ctx, 'main', [ [ el, 'touchstart', 'touchStart', `passive${ modifiers.capture === true ? 'Capture' : '' }` ], [ el, 'touchmove', 'noop', 'notPassiveCapture' ] // cannot be passive (ex: iOS scroll) ]); }, updated (el, bindings) { const ctx = el.__qtouchswipe; if (ctx !== void 0) { if (bindings.oldValue !== bindings.value) { typeof bindings.value !== 'function' && ctx.end(); ctx.handler = bindings.value; } ctx.direction = getModifierDirections(bindings.modifiers); } }, beforeUnmount (el) { const ctx = el.__qtouchswipe; if (ctx !== void 0) { cleanEvt(ctx, 'main'); cleanEvt(ctx, 'temp'); client.is.firefox === true && preventDraggable(el, false); ctx.styleCleanup !== void 0 && ctx.styleCleanup(); delete el.__qtouchswipe; } } } ); function useCache () { const cache = new Map(); return { getCache: function (key, obj) { return cache[ key ] === void 0 ? (cache[ key ] = obj) : cache[ key ] }, getCacheWithFn: function (key, fn) { return cache[ key ] === void 0 ? (cache[ key ] = fn()) : cache[ key ] } } } const usePanelChildProps = { name: { required: true }, disable: Boolean }; const PanelWrapper$1 = { setup (_, { slots }) { return () => vue.h('div', { class: 'q-panel scroll', role: 'tabpanel' }, hSlot(slots.default)) } }; const usePanelProps = { modelValue: { required: true }, animated: Boolean, infinite: Boolean, swipeable: Boolean, vertical: Boolean, transitionPrev: String, transitionNext: String, transitionDuration: { type: [ String, Number ], default: 300 }, keepAlive: Boolean, keepAliveInclude: [ String, Array, RegExp ], keepAliveExclude: [ String, Array, RegExp ], keepAliveMax: Number }; const usePanelEmits = [ 'update:modelValue', 'beforeTransition', 'transition' ]; function usePanel () { const { props, emit, proxy } = vue.getCurrentInstance(); const { getCacheWithFn } = useCache(); let panels, forcedPanelTransition; const panelIndex = vue.ref(null); const panelTransition = vue.ref(null); function onSwipe (evt) { const dir = props.vertical === true ? 'up' : 'left'; goToPanelByOffset((proxy.$q.lang.rtl === true ? -1 : 1) * (evt.direction === dir ? 1 : -1)); } const panelDirectives = vue.computed(() => { // if props.swipeable return [ [ TouchSwipe, onSwipe, void 0, { horizontal: props.vertical !== true, vertical: props.vertical, mouse: true } ] ] }); const transitionPrev = vue.computed(() => props.transitionPrev || `slide-${ props.vertical === true ? 'down' : 'right' }` ); const transitionNext = vue.computed(() => props.transitionNext || `slide-${ props.vertical === true ? 'up' : 'left' }` ); const transitionStyle = vue.computed( () => `--q-transition-duration: ${ props.transitionDuration }ms` ); const contentKey = vue.computed(() => ( typeof props.modelValue === 'string' || typeof props.modelValue === 'number' ? props.modelValue : String(props.modelValue) )); const keepAliveProps = vue.computed(() => ({ include: props.keepAliveInclude, exclude: props.keepAliveExclude, max: props.keepAliveMax })); const needsUniqueKeepAliveWrapper = vue.computed(() => props.keepAliveInclude !== void 0 || props.keepAliveExclude !== void 0 ); vue.watch(() => props.modelValue, (newVal, oldVal) => { const index = isValidPanelName(newVal) === true ? getPanelIndex(newVal) : -1; if (forcedPanelTransition !== true) { updatePanelTransition( index === -1 ? 0 : (index < getPanelIndex(oldVal) ? -1 : 1) ); } if (panelIndex.value !== index) { panelIndex.value = index; emit('beforeTransition', newVal, oldVal); vue.nextTick(() => { emit('transition', newVal, oldVal); }); } }); function nextPanel () { goToPanelByOffset(1); } function previousPanel () { goToPanelByOffset(-1); } function goToPanel (name) { emit('update:modelValue', name); } function isValidPanelName (name) { return name !== void 0 && name !== null && name !== '' } function getPanelIndex (name) { return panels.findIndex(panel => { return panel.props.name === name && panel.props.disable !== '' && panel.props.disable !== true }) } function getEnabledPanels () { return panels.filter(panel => { return panel.props.disable !== '' && panel.props.disable !== true }) } function updatePanelTransition (direction) { const val = direction !== 0 && props.animated === true && panelIndex.value !== -1 ? 'q-transition--' + (direction === -1 ? transitionPrev.value : transitionNext.value) : null; if (panelTransition.value !== val) { panelTransition.value = val; } } function goToPanelByOffset (direction, startIndex = panelIndex.value) { let index = startIndex + direction; while (index > -1 && index < panels.length) { const opt = panels[ index ]; if ( opt !== void 0 && opt.props.disable !== '' && opt.props.disable !== true ) { updatePanelTransition(direction); forcedPanelTransition = true; emit('update:modelValue', opt.props.name); setTimeout(() => { forcedPanelTransition = false; }); return } index += direction; } if (props.infinite === true && panels.length !== 0 && startIndex !== -1 && startIndex !== panels.length) { goToPanelByOffset(direction, direction === -1 ? panels.length : -1); } } function updatePanelIndex () { const index = getPanelIndex(props.modelValue); if (panelIndex.value !== index) { panelIndex.value = index; } return true } function getPanelContentChild () { const panel = isValidPanelName(props.modelValue) === true && updatePanelIndex() && panels[ panelIndex.value ]; return props.keepAlive === true ? [ vue.h(vue.KeepAlive, keepAliveProps.value, [ vue.h( needsUniqueKeepAliveWrapper.value === true ? getCacheWithFn(contentKey.value, () => ({ ...PanelWrapper$1, name: contentKey.value })) : PanelWrapper$1, { key: contentKey.value, style: transitionStyle.value }, () => panel ) ]) ] : [ vue.h('div', { class: 'q-panel scroll', style: transitionStyle.value, key: contentKey.value, role: 'tabpanel' }, [ panel ]) ] } function getPanelContent () { if (panels.length === 0) { return } return props.animated === true ? [ vue.h(vue.Transition, { name: panelTransition.value }, getPanelContentChild) ] : getPanelContentChild() } function updatePanelsList (slots) { panels = getNormalizedVNodes( hSlot(slots.default, []) ).filter( panel => panel.props !== null && panel.props.slot === void 0 && isValidPanelName(panel.props.name) === true ); return panels.length } function getPanels () { return panels } // expose public methods Object.assign(proxy, { next: nextPanel, previous: previousPanel, goTo: goToPanel }); return { panelIndex, panelDirectives, updatePanelsList, updatePanelIndex, getPanelContent, getEnabledPanels, getPanels, isValidPanelName, keepAliveProps, needsUniqueKeepAliveWrapper, goToPanelByOffset, goToPanel, nextPanel, previousPanel } } let counter = 0; const useFullscreenProps = { fullscreen: Boolean, noRouteFullscreenExit: Boolean }; const useFullscreenEmits = [ 'update:fullscreen', 'fullscreen' ]; function useFullscreen () { const vm = vue.getCurrentInstance(); const { props, emit, proxy } = vm; let historyEntry, fullscreenFillerNode, container; const inFullscreen = vue.ref(false); vmHasRouter(vm) === true && vue.watch(() => proxy.$route.fullPath, () => { props.noRouteFullscreenExit !== true && exitFullscreen(); }); vue.watch(() => props.fullscreen, v => { if (inFullscreen.value !== v) { toggleFullscreen(); } }); vue.watch(inFullscreen, v => { emit('update:fullscreen', v); emit('fullscreen', v); }); function toggleFullscreen () { if (inFullscreen.value === true) { exitFullscreen(); } else { setFullscreen(); } } function setFullscreen () { if (inFullscreen.value === true) { return } inFullscreen.value = true; container = proxy.$el.parentNode; container.replaceChild(fullscreenFillerNode, proxy.$el); document.body.appendChild(proxy.$el); counter++; if (counter === 1) { document.body.classList.add('q-body--fullscreen-mixin'); } historyEntry = { handler: exitFullscreen }; History.add(historyEntry); } function exitFullscreen () { if (inFullscreen.value !== true) { return } if (historyEntry !== void 0) { History.remove(historyEntry); historyEntry = void 0; } container.replaceChild(proxy.$el, fullscreenFillerNode); inFullscreen.value = false; counter = Math.max(0, counter - 1); if (counter === 0) { document.body.classList.remove('q-body--fullscreen-mixin'); if (proxy.$el.scrollIntoView !== void 0) { setTimeout(() => { proxy.$el.scrollIntoView(); }); } } } vue.onBeforeMount(() => { fullscreenFillerNode = document.createElement('span'); }); vue.onMounted(() => { props.fullscreen === true && setFullscreen(); }); vue.onBeforeUnmount(exitFullscreen); // expose public methods Object.assign(proxy, { toggleFullscreen, setFullscreen, exitFullscreen }); return { inFullscreen, toggleFullscreen } } const navigationPositionOptions = [ 'top', 'right', 'bottom', 'left' ]; const controlTypeOptions = [ 'regular', 'flat', 'outline', 'push', 'unelevated' ]; var QCarousel = createComponent({ name: 'QCarousel', props: { ...useDarkProps, ...usePanelProps, ...useFullscreenProps, transitionPrev: { // usePanelParentProps override type: String, default: 'fade' }, transitionNext: { // usePanelParentProps override type: String, default: 'fade' }, height: String, padding: Boolean, controlColor: String, controlTextColor: String, controlType: { type: String, validator: v => controlTypeOptions.includes(v), default: 'flat' }, autoplay: [ Number, Boolean ], arrows: Boolean, prevIcon: String, nextIcon: String, navigation: Boolean, navigationPosition: { type: String, validator: v => navigationPositionOptions.includes(v) }, navigationIcon: String, navigationActiveIcon: String, thumbnails: Boolean }, emits: [ ...useFullscreenEmits, ...usePanelEmits ], setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); let timer = null, panelsLen; const { updatePanelsList, getPanelContent, panelDirectives, goToPanel, previousPanel, nextPanel, getEnabledPanels, panelIndex } = usePanel(); const { inFullscreen } = useFullscreen(); const style = vue.computed(() => ( inFullscreen.value !== true && props.height !== void 0 ? { height: props.height } : {} )); const direction = vue.computed(() => (props.vertical === true ? 'vertical' : 'horizontal')); const classes = vue.computed(() => `q-carousel q-panel-parent q-carousel--with${ props.padding === true ? '' : 'out' }-padding` + (inFullscreen.value === true ? ' fullscreen' : '') + (isDark.value === true ? ' q-carousel--dark q-dark' : '') + (props.arrows === true ? ` q-carousel--arrows-${ direction.value }` : '') + (props.navigation === true ? ` q-carousel--navigation-${ navigationPosition.value }` : '') ); const arrowIcons = vue.computed(() => { const ico = [ props.prevIcon || $q.iconSet.carousel[ props.vertical === true ? 'up' : 'left' ], props.nextIcon || $q.iconSet.carousel[ props.vertical === true ? 'down' : 'right' ] ]; return props.vertical === false && $q.lang.rtl === true ? ico.reverse() : ico }); const navIcon = vue.computed(() => props.navigationIcon || $q.iconSet.carousel.navigationIcon); const navActiveIcon = vue.computed(() => props.navigationActiveIcon || navIcon.value); const navigationPosition = vue.computed(() => props.navigationPosition || (props.vertical === true ? 'right' : 'bottom') ); const controlProps = vue.computed(() => ({ color: props.controlColor, textColor: props.controlTextColor, round: true, [ props.controlType ]: true, dense: true })); vue.watch(() => props.modelValue, () => { if (props.autoplay) { startTimer(); } }); vue.watch(() => props.autoplay, val => { if (val) { startTimer(); } else if (timer !== null) { clearTimeout(timer); timer = null; } }); function startTimer () { const duration = isNumber(props.autoplay) === true ? Math.abs(props.autoplay) : 5000; timer !== null && clearTimeout(timer); timer = setTimeout(() => { timer = null; if (duration >= 0) { nextPanel(); } else { previousPanel(); } }, duration); } vue.onMounted(() => { props.autoplay && startTimer(); }); vue.onBeforeUnmount(() => { timer !== null && clearTimeout(timer); }); function getNavigationContainer (type, mapping) { return vue.h('div', { class: 'q-carousel__control q-carousel__navigation no-wrap absolute flex' + ` q-carousel__navigation--${ type } q-carousel__navigation--${ navigationPosition.value }` + (props.controlColor !== void 0 ? ` text-${ props.controlColor }` : '') }, [ vue.h('div', { class: 'q-carousel__navigation-inner flex flex-center no-wrap' }, getEnabledPanels().map(mapping)) ]) } function getContent () { const node = []; if (props.navigation === true) { const fn = slots[ 'navigation-icon' ] !== void 0 ? slots[ 'navigation-icon' ] : opts => vue.h(QBtn, { key: 'nav' + opts.name, class: `q-carousel__navigation-icon q-carousel__navigation-icon--${ opts.active === true ? '' : 'in' }active`, ...opts.btnProps, onClick: opts.onClick }); const maxIndex = panelsLen - 1; node.push( getNavigationContainer('buttons', (panel, index) => { const name = panel.props.name; const active = panelIndex.value === index; return fn({ index, maxIndex, name, active, btnProps: { icon: active === true ? navActiveIcon.value : navIcon.value, size: 'sm', ...controlProps.value }, onClick: () => { goToPanel(name); } }) }) ); } else if (props.thumbnails === true) { const color = props.controlColor !== void 0 ? ` text-${ props.controlColor }` : ''; node.push(getNavigationContainer('thumbnails', panel => { const slide = panel.props; return vue.h('img', { key: 'tmb#' + slide.name, class: `q-carousel__thumbnail q-carousel__thumbnail--${ slide.name === props.modelValue ? '' : 'in' }active` + color, src: slide.imgSrc || slide[ 'img-src' ], onClick: () => { goToPanel(slide.name); } }) })); } if (props.arrows === true && panelIndex.value >= 0) { if (props.infinite === true || panelIndex.value > 0) { node.push( vue.h('div', { key: 'prev', class: `q-carousel__control q-carousel__arrow q-carousel__prev-arrow q-carousel__prev-arrow--${ direction.value } absolute flex flex-center` }, [ vue.h(QBtn, { icon: arrowIcons.value[ 0 ], ...controlProps.value, onClick: previousPanel }) ]) ); } if (props.infinite === true || panelIndex.value < panelsLen - 1) { node.push( vue.h('div', { key: 'next', class: 'q-carousel__control q-carousel__arrow q-carousel__next-arrow' + ` q-carousel__next-arrow--${ direction.value } absolute flex flex-center` }, [ vue.h(QBtn, { icon: arrowIcons.value[ 1 ], ...controlProps.value, onClick: nextPanel }) ]) ); } } return hMergeSlot(slots.control, node) } return () => { panelsLen = updatePanelsList(slots); return vue.h('div', { class: classes.value, style: style.value }, [ hDir( 'div', { class: 'q-carousel__slides-container' }, getPanelContent(), 'sl-cont', props.swipeable, () => panelDirectives.value ) ].concat(getContent())) } } }); var QCarouselSlide = createComponent({ name: 'QCarouselSlide', props: { ...usePanelChildProps, imgSrc: String }, setup (props, { slots }) { const style = vue.computed(() => ( props.imgSrc ? { backgroundImage: `url("${ props.imgSrc }")` } : {} )); return () => vue.h('div', { class: 'q-carousel__slide', style: style.value }, hSlot(slots.default)) } }); var QCarouselControl = createComponent({ name: 'QCarouselControl', props: { position: { type: String, default: 'bottom-right', validator: v => [ 'top-right', 'top-left', 'bottom-right', 'bottom-left', 'top', 'right', 'bottom', 'left' ].includes(v) }, offset: { type: Array, default: () => [ 18, 18 ], validator: v => v.length === 2 } }, setup (props, { slots }) { const classes = vue.computed(() => `q-carousel__control absolute absolute-${ props.position }`); const style = vue.computed(() => ({ margin: `${ props.offset[ 1 ] }px ${ props.offset[ 0 ] }px` })); return () => vue.h('div', { class: classes.value, style: style.value }, hSlot(slots.default)) } }); var QChatMessage = createComponent({ name: 'QChatMessage', props: { sent: Boolean, label: String, bgColor: String, textColor: String, name: String, avatar: String, text: Array, stamp: String, size: String, labelHtml: Boolean, nameHtml: Boolean, textHtml: Boolean, stampHtml: Boolean }, setup (props, { slots }) { const op = vue.computed(() => (props.sent === true ? 'sent' : 'received')); const textClass = vue.computed(() => `q-message-text-content q-message-text-content--${ op.value }` + (props.textColor !== void 0 ? ` text-${ props.textColor }` : '') ); const messageClass = vue.computed(() => `q-message-text q-message-text--${ op.value }` + (props.bgColor !== void 0 ? ` text-${ props.bgColor }` : '') ); const containerClass = vue.computed(() => 'q-message-container row items-end no-wrap' + (props.sent === true ? ' reverse' : '') ); const sizeClass = vue.computed(() => (props.size !== void 0 ? `col-${ props.size }` : '')); const domProps = vue.computed(() => ({ msg: props.textHtml === true ? 'innerHTML' : 'textContent', stamp: props.stampHtml === true ? 'innerHTML' : 'textContent', name: props.nameHtml === true ? 'innerHTML' : 'textContent', label: props.labelHtml === true ? 'innerHTML' : 'textContent' })); function wrapStamp (node) { if (slots.stamp !== void 0) { return [ node, vue.h('div', { class: 'q-message-stamp' }, slots.stamp()) ] } if (props.stamp) { return [ node, vue.h('div', { class: 'q-message-stamp', [ domProps.value.stamp ]: props.stamp }) ] } return [ node ] } function getText (contentList, withSlots) { const content = withSlots === true ? (contentList.length > 1 ? text => text : text => vue.h('div', [ text ])) : text => vue.h('div', { [ domProps.value.msg ]: text }); return contentList.map((msg, index) => vue.h('div', { key: index, class: messageClass.value }, [ vue.h('div', { class: textClass.value }, wrapStamp(content(msg))) ])) } return () => { const container = []; if (slots.avatar !== void 0) { container.push(slots.avatar()); } else if (props.avatar !== void 0) { container.push( vue.h('img', { class: `q-message-avatar q-message-avatar--${ op.value }`, src: props.avatar, 'aria-hidden': 'true' }) ); } const msg = []; if (slots.name !== void 0) { msg.push( vue.h('div', { class: `q-message-name q-message-name--${ op.value }` }, slots.name()) ); } else if (props.name !== void 0) { msg.push( vue.h('div', { class: `q-message-name q-message-name--${ op.value }`, [ domProps.value.name ]: props.name }) ); } if (slots.default !== void 0) { msg.push( getText( getNormalizedVNodes(slots.default()), true ) ); } else if (props.text !== void 0) { msg.push(getText(props.text)); } container.push( vue.h('div', { class: sizeClass.value }, msg) ); const child = []; if (slots.label !== void 0) { child.push( vue.h('div', { class: 'q-message-label' }, slots.label()) ); } else if (props.label !== void 0) { child.push( vue.h('div', { class: 'q-message-label', [ domProps.value.label ]: props.label }) ); } child.push( vue.h('div', { class: containerClass.value }, container) ); return vue.h('div', { class: `q-message q-message-${ op.value }` }, child) } } }); function useRefocusTarget (props, rootRef) { const refocusRef = vue.ref(null); const refocusTargetEl = vue.computed(() => { if (props.disable === true) { return null } return vue.h('span', { ref: refocusRef, class: 'no-outline', tabindex: -1 }) }); function refocusTarget (e) { const root = rootRef.value; if (e !== void 0 && e.type.indexOf('key') === 0) { if ( root !== null && document.activeElement !== root && root.contains(document.activeElement) === true ) { root.focus(); } } else if ( refocusRef.value !== null && (e === void 0 || (root !== null && root.contains(e.target) === true)) ) { refocusRef.value.focus(); } } return { refocusTargetEl, refocusTarget } } var optionSizes = { xs: 30, sm: 35, md: 40, lg: 50, xl: 60 }; const useCheckboxProps = { ...useDarkProps, ...useSizeProps, ...useFormProps, modelValue: { required: true, default: null }, val: {}, trueValue: { default: true }, falseValue: { default: false }, indeterminateValue: { default: null }, checkedIcon: String, uncheckedIcon: String, indeterminateIcon: String, toggleOrder: { type: String, validator: v => v === 'tf' || v === 'ft' }, toggleIndeterminate: Boolean, label: String, leftLabel: Boolean, color: String, keepColor: Boolean, dense: Boolean, disable: Boolean, tabindex: [ String, Number ] }; const useCheckboxEmits = [ 'update:modelValue' ]; function useCheckbox (type, getInner) { const { props, slots, emit, proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const rootRef = vue.ref(null); const { refocusTargetEl, refocusTarget } = useRefocusTarget(props, rootRef); const sizeStyle = useSize(props, optionSizes); const modelIsArray = vue.computed(() => props.val !== void 0 && Array.isArray(props.modelValue) ); const index = vue.computed(() => { const val = vue.toRaw(props.val); return modelIsArray.value === true ? props.modelValue.findIndex(opt => vue.toRaw(opt) === val) : -1 }); const isTrue = vue.computed(() => ( modelIsArray.value === true ? index.value > -1 : vue.toRaw(props.modelValue) === vue.toRaw(props.trueValue) )); const isFalse = vue.computed(() => ( modelIsArray.value === true ? index.value === -1 : vue.toRaw(props.modelValue) === vue.toRaw(props.falseValue) )); const isIndeterminate = vue.computed(() => isTrue.value === false && isFalse.value === false ); const tabindex = vue.computed(() => ( props.disable === true ? -1 : props.tabindex || 0 )); const classes = vue.computed(() => `q-${ type } cursor-pointer no-outline row inline no-wrap items-center` + (props.disable === true ? ' disabled' : '') + (isDark.value === true ? ` q-${ type }--dark` : '') + (props.dense === true ? ` q-${ type }--dense` : '') + (props.leftLabel === true ? ' reverse' : '') ); const innerClass = vue.computed(() => { const state = isTrue.value === true ? 'truthy' : (isFalse.value === true ? 'falsy' : 'indet'); const color = props.color !== void 0 && ( props.keepColor === true || (type === 'toggle' ? isTrue.value === true : isFalse.value !== true) ) ? ` text-${ props.color }` : ''; return `q-${ type }__inner relative-position non-selectable q-${ type }__inner--${ state }${ color }` }); const formAttrs = vue.computed(() => { const prop = { type: 'checkbox' }; props.name !== void 0 && Object.assign(prop, { // see https://vuejs.org/guide/extras/render-function.html#creating-vnodes (.prop) '.checked': isTrue.value, '^checked': isTrue.value === true ? 'checked' : void 0, name: props.name, value: modelIsArray.value === true ? props.val : props.trueValue }); return prop }); const injectFormInput = useFormInject(formAttrs); const attributes = vue.computed(() => { const attrs = { tabindex: tabindex.value, role: type === 'toggle' ? 'switch' : 'checkbox', 'aria-label': props.label, 'aria-checked': isIndeterminate.value === true ? 'mixed' : (isTrue.value === true ? 'true' : 'false') }; if (props.disable === true) { attrs[ 'aria-disabled' ] = 'true'; } return attrs }); function onClick (e) { if (e !== void 0) { stopAndPrevent(e); refocusTarget(e); } if (props.disable !== true) { emit('update:modelValue', getNextValue(), e); } } function getNextValue () { if (modelIsArray.value === true) { if (isTrue.value === true) { const val = props.modelValue.slice(); val.splice(index.value, 1); return val } return props.modelValue.concat([ props.val ]) } if (isTrue.value === true) { if (props.toggleOrder !== 'ft' || props.toggleIndeterminate === false) { return props.falseValue } } else if (isFalse.value === true) { if (props.toggleOrder === 'ft' || props.toggleIndeterminate === false) { return props.trueValue } } else { return props.toggleOrder !== 'ft' ? props.trueValue : props.falseValue } return props.indeterminateValue } function onKeydown (e) { if (e.keyCode === 13 || e.keyCode === 32) { stopAndPrevent(e); } } function onKeyup (e) { if (e.keyCode === 13 || e.keyCode === 32) { onClick(e); } } const getInnerContent = getInner(isTrue, isIndeterminate); // expose public methods Object.assign(proxy, { toggle: onClick }); return () => { const inner = getInnerContent(); props.disable !== true && injectFormInput( inner, 'unshift', ` q-${ type }__native absolute q-ma-none q-pa-none` ); const child = [ vue.h('div', { class: innerClass.value, style: sizeStyle.value, 'aria-hidden': 'true' }, inner) ]; if (refocusTargetEl.value !== null) { child.push(refocusTargetEl.value); } const label = props.label !== void 0 ? hMergeSlot(slots.default, [ props.label ]) : hSlot(slots.default); label !== void 0 && child.push( vue.h('div', { class: `q-${ type }__label q-anchor--skip` }, label) ); return vue.h('div', { ref: rootRef, class: classes.value, ...attributes.value, onClick, onKeydown, onKeyup }, child) } } const bgNode = vue.h('div', { key: 'svg', class: 'q-checkbox__bg absolute' }, [ vue.h('svg', { class: 'q-checkbox__svg fit absolute-full', viewBox: '0 0 24 24' }, [ vue.h('path', { class: 'q-checkbox__truthy', fill: 'none', d: 'M1.73,12.91 8.1,19.28 22.79,4.59' }), vue.h('path', { class: 'q-checkbox__indet', d: 'M4,14H20V10H4' }) ]) ]); var QCheckbox = createComponent({ name: 'QCheckbox', props: useCheckboxProps, emits: useCheckboxEmits, setup (props) { function getInner (isTrue, isIndeterminate) { const icon = vue.computed(() => (isTrue.value === true ? props.checkedIcon : (isIndeterminate.value === true ? props.indeterminateIcon : props.uncheckedIcon ) ) || null ); return () => ( icon.value !== null ? [ vue.h('div', { key: 'icon', class: 'q-checkbox__icon-container absolute-full flex flex-center no-wrap' }, [ vue.h(QIcon, { class: 'q-checkbox__icon', name: icon.value }) ]) ] : [ bgNode ] ) } return useCheckbox('checkbox', getInner) } }); const defaultSizes$1 = { xs: 8, sm: 10, md: 14, lg: 20, xl: 24 }; var QChip = createComponent({ name: 'QChip', props: { ...useDarkProps, ...useSizeProps, dense: Boolean, icon: String, iconRight: String, iconRemove: String, iconSelected: String, label: [ String, Number ], color: String, textColor: String, modelValue: { type: Boolean, default: true }, selected: { type: Boolean, default: null }, square: Boolean, outline: Boolean, clickable: Boolean, removable: Boolean, removeAriaLabel: String, tabindex: [ String, Number ], disable: Boolean, ripple: { type: [ Boolean, Object ], default: true } }, emits: [ 'update:modelValue', 'update:selected', 'remove', 'click' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const sizeStyle = useSize(props, defaultSizes$1); const hasLeftIcon = vue.computed(() => props.selected === true || props.icon !== void 0); const leftIcon = vue.computed(() => ( props.selected === true ? props.iconSelected || $q.iconSet.chip.selected : props.icon )); const removeIcon = vue.computed(() => props.iconRemove || $q.iconSet.chip.remove); const isClickable = vue.computed(() => props.disable === false && (props.clickable === true || props.selected !== null) ); const classes = vue.computed(() => { const text = props.outline === true ? props.color || props.textColor : props.textColor; return 'q-chip row inline no-wrap items-center' + (props.outline === false && props.color !== void 0 ? ` bg-${ props.color }` : '') + (text ? ` text-${ text } q-chip--colored` : '') + (props.disable === true ? ' disabled' : '') + (props.dense === true ? ' q-chip--dense' : '') + (props.outline === true ? ' q-chip--outline' : '') + (props.selected === true ? ' q-chip--selected' : '') + (isClickable.value === true ? ' q-chip--clickable cursor-pointer non-selectable q-hoverable' : '') + (props.square === true ? ' q-chip--square' : '') + (isDark.value === true ? ' q-chip--dark q-dark' : '') }); const attributes = vue.computed(() => { const chip = props.disable === true ? { tabindex: -1, 'aria-disabled': 'true' } : { tabindex: props.tabindex || 0 }; const remove = { ...chip, role: 'button', 'aria-hidden': 'false', 'aria-label': props.removeAriaLabel || $q.lang.label.remove }; return { chip, remove } }); function onKeyup (e) { e.keyCode === 13 /* ENTER */ && onClick(e); } function onClick (e) { if (!props.disable) { emit('update:selected', !props.selected); emit('click', e); } } function onRemove (e) { if (e.keyCode === void 0 || e.keyCode === 13) { stopAndPrevent(e); if (props.disable === false) { emit('update:modelValue', false); emit('remove'); } } } function getContent () { const child = []; isClickable.value === true && child.push( vue.h('div', { class: 'q-focus-helper' }) ); hasLeftIcon.value === true && child.push( vue.h(QIcon, { class: 'q-chip__icon q-chip__icon--left', name: leftIcon.value }) ); const label = props.label !== void 0 ? [ vue.h('div', { class: 'ellipsis' }, [ props.label ]) ] : void 0; child.push( vue.h('div', { class: 'q-chip__content col row no-wrap items-center q-anchor--skip' }, hMergeSlotSafely(slots.default, label)) ); props.iconRight && child.push( vue.h(QIcon, { class: 'q-chip__icon q-chip__icon--right', name: props.iconRight }) ); props.removable === true && child.push( vue.h(QIcon, { class: 'q-chip__icon q-chip__icon--remove cursor-pointer', name: removeIcon.value, ...attributes.value.remove, onClick: onRemove, onKeyup: onRemove }) ); return child } return () => { if (props.modelValue === false) { return } const data = { class: classes.value, style: sizeStyle.value }; isClickable.value === true && Object.assign( data, attributes.value.chip, { onClick, onKeyup } ); return hDir( 'div', data, getContent(), 'ripple', props.ripple !== false && props.disable !== true, () => [ [ Ripple, props.ripple ] ] ) } } }); // also used by QKnob const useCircularCommonProps = { ...useSizeProps, min: { type: Number, default: 0 }, max: { type: Number, default: 100 }, color: String, centerColor: String, trackColor: String, fontSize: String, rounded: Boolean, // ratio thickness: { type: Number, default: 0.2, validator: v => v >= 0 && v <= 1 }, angle: { type: Number, default: 0 }, showValue: Boolean, reverse: Boolean, instantFeedback: Boolean }; const radius = 50, diameter = 2 * radius, circumference = diameter * Math.PI, strokeDashArray = Math.round(circumference * 1000) / 1000; var QCircularProgress = createComponent({ name: 'QCircularProgress', props: { ...useCircularCommonProps, value: { type: Number, default: 0 }, animationSpeed: { type: [ String, Number ], default: 600 }, indeterminate: Boolean }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const sizeStyle = useSize(props); const svgStyle = vue.computed(() => { const angle = ($q.lang.rtl === true ? -1 : 1) * props.angle; return { transform: props.reverse !== ($q.lang.rtl === true) ? `scale3d(-1, 1, 1) rotate3d(0, 0, 1, ${ -90 - angle }deg)` : `rotate3d(0, 0, 1, ${ angle - 90 }deg)` } }); const circleStyle = vue.computed(() => ( props.instantFeedback !== true && props.indeterminate !== true ? { transition: `stroke-dashoffset ${ props.animationSpeed }ms ease 0s, stroke ${ props.animationSpeed }ms ease` } : '' )); const viewBox = vue.computed(() => diameter / (1 - props.thickness / 2)); const viewBoxAttr = vue.computed(() => `${ viewBox.value / 2 } ${ viewBox.value / 2 } ${ viewBox.value } ${ viewBox.value }` ); const normalized = vue.computed(() => between(props.value, props.min, props.max)); const strokeDashOffset = vue.computed(() => circumference * ( 1 - (normalized.value - props.min) / (props.max - props.min) )); const strokeWidth = vue.computed(() => props.thickness / 2 * viewBox.value); function getCircle ({ thickness, offset, color, cls, rounded }) { return vue.h('circle', { class: 'q-circular-progress__' + cls + (color !== void 0 ? ` text-${ color }` : ''), style: circleStyle.value, fill: 'transparent', stroke: 'currentColor', 'stroke-width': thickness, 'stroke-dasharray': strokeDashArray, 'stroke-dashoffset': offset, 'stroke-linecap': rounded, cx: viewBox.value, cy: viewBox.value, r: radius }) } return () => { const svgChild = []; props.centerColor !== void 0 && props.centerColor !== 'transparent' && svgChild.push( vue.h('circle', { class: `q-circular-progress__center text-${ props.centerColor }`, fill: 'currentColor', r: radius - strokeWidth.value / 2, cx: viewBox.value, cy: viewBox.value }) ); props.trackColor !== void 0 && props.trackColor !== 'transparent' && svgChild.push( getCircle({ cls: 'track', thickness: strokeWidth.value, offset: 0, color: props.trackColor }) ); svgChild.push( getCircle({ cls: 'circle', thickness: strokeWidth.value, offset: strokeDashOffset.value, color: props.color, rounded: props.rounded === true ? 'round' : void 0 }) ); const child = [ vue.h('svg', { class: 'q-circular-progress__svg', style: svgStyle.value, viewBox: viewBoxAttr.value, 'aria-hidden': 'true' }, svgChild) ]; props.showValue === true && child.push( vue.h('div', { class: 'q-circular-progress__text absolute-full row flex-center content-center', style: { fontSize: props.fontSize } }, slots.default !== void 0 ? slots.default() : [ vue.h('div', normalized.value) ]) ); return vue.h('div', { class: `q-circular-progress q-circular-progress--${ props.indeterminate === true ? 'in' : '' }determinate`, style: sizeStyle.value, role: 'progressbar', 'aria-valuemin': props.min, 'aria-valuemax': props.max, 'aria-valuenow': props.indeterminate === true ? void 0 : normalized.value }, hMergeSlotSafely(slots.internal, child)) // "internal" is used by QKnob } } }); function getChanges (evt, ctx, isFinal) { const pos = position(evt); let dir, distX = pos.left - ctx.event.x, distY = pos.top - ctx.event.y, absX = Math.abs(distX), absY = Math.abs(distY); const direction = ctx.direction; if (direction.horizontal === true && direction.vertical !== true) { dir = distX < 0 ? 'left' : 'right'; } else if (direction.horizontal !== true && direction.vertical === true) { dir = distY < 0 ? 'up' : 'down'; } else if (direction.up === true && distY < 0) { dir = 'up'; if (absX > absY) { if (direction.left === true && distX < 0) { dir = 'left'; } else if (direction.right === true && distX > 0) { dir = 'right'; } } } else if (direction.down === true && distY > 0) { dir = 'down'; if (absX > absY) { if (direction.left === true && distX < 0) { dir = 'left'; } else if (direction.right === true && distX > 0) { dir = 'right'; } } } else if (direction.left === true && distX < 0) { dir = 'left'; if (absX < absY) { if (direction.up === true && distY < 0) { dir = 'up'; } else if (direction.down === true && distY > 0) { dir = 'down'; } } } else if (direction.right === true && distX > 0) { dir = 'right'; if (absX < absY) { if (direction.up === true && distY < 0) { dir = 'up'; } else if (direction.down === true && distY > 0) { dir = 'down'; } } } let synthetic = false; if (dir === void 0 && isFinal === false) { if (ctx.event.isFirst === true || ctx.event.lastDir === void 0) { return {} } dir = ctx.event.lastDir; synthetic = true; if (dir === 'left' || dir === 'right') { pos.left -= distX; absX = 0; distX = 0; } else { pos.top -= distY; absY = 0; distY = 0; } } return { synthetic, payload: { evt, touch: ctx.event.mouse !== true, mouse: ctx.event.mouse === true, position: pos, direction: dir, isFirst: ctx.event.isFirst, isFinal: isFinal === true, duration: Date.now() - ctx.event.time, distance: { x: absX, y: absY }, offset: { x: distX, y: distY }, delta: { x: pos.left - ctx.event.lastX, y: pos.top - ctx.event.lastY } } } } let uid$2 = 0; var TouchPan = createDirective({ name: 'touch-pan', beforeMount (el, { value, modifiers }) { // early return, we don't need to do anything if (modifiers.mouse !== true && client.has.touch !== true) { return } function handleEvent (evt, mouseEvent) { if (modifiers.mouse === true && mouseEvent === true) { stopAndPrevent(evt); } else { modifiers.stop === true && stop(evt); modifiers.prevent === true && prevent(evt); } } const ctx = { uid: 'qvtp_' + (uid$2++), handler: value, modifiers, direction: getModifierDirections(modifiers), noop, mouseStart (evt) { if (shouldStart(evt, ctx) && leftClick(evt)) { addEvt(ctx, 'temp', [ [ document, 'mousemove', 'move', 'notPassiveCapture' ], [ document, 'mouseup', 'end', 'passiveCapture' ] ]); ctx.start(evt, true); } }, touchStart (evt) { if (shouldStart(evt, ctx)) { const target = evt.target; addEvt(ctx, 'temp', [ [ target, 'touchmove', 'move', 'notPassiveCapture' ], [ target, 'touchcancel', 'end', 'passiveCapture' ], [ target, 'touchend', 'end', 'passiveCapture' ] ]); ctx.start(evt); } }, start (evt, mouseEvent) { client.is.firefox === true && preventDraggable(el, true); ctx.lastEvt = evt; /* * Stop propagation so possible upper v-touch-pan don't catch this as well; * If we're not the target (based on modifiers), we'll re-emit the event later */ if (mouseEvent === true || modifiers.stop === true) { /* * are we directly switching to detected state? * clone event only otherwise */ if ( ctx.direction.all !== true // account for UMD too where modifiers will be lowercased to work && (mouseEvent !== true || (ctx.modifiers.mouseAllDir !== true && ctx.modifiers.mousealldir !== true)) ) { const clone = evt.type.indexOf('mouse') > -1 ? new MouseEvent(evt.type, evt) : new TouchEvent(evt.type, evt); evt.defaultPrevented === true && prevent(clone); evt.cancelBubble === true && stop(clone); Object.assign(clone, { qKeyEvent: evt.qKeyEvent, qClickOutside: evt.qClickOutside, qAnchorHandled: evt.qAnchorHandled, qClonedBy: evt.qClonedBy === void 0 ? [ ctx.uid ] : evt.qClonedBy.concat(ctx.uid) }); ctx.initialEvent = { target: evt.target, event: clone }; } stop(evt); } const { left, top } = position(evt); ctx.event = { x: left, y: top, time: Date.now(), mouse: mouseEvent === true, detected: false, isFirst: true, isFinal: false, lastX: left, lastY: top }; }, move (evt) { if (ctx.event === void 0) { return } const pos = position(evt), distX = pos.left - ctx.event.x, distY = pos.top - ctx.event.y; // prevent buggy browser behavior (like Blink-based engine ones on Windows) // where the mousemove event occurs even if there's no movement after mousedown // https://bugs.chromium.org/p/chromium/issues/detail?id=161464 // https://bugs.chromium.org/p/chromium/issues/detail?id=721341 // https://github.com/quasarframework/quasar/issues/10721 if (distX === 0 && distY === 0) { return } ctx.lastEvt = evt; const isMouseEvt = ctx.event.mouse === true; const start = () => { handleEvent(evt, isMouseEvt); let cursor; if (modifiers.preserveCursor !== true && modifiers.preservecursor !== true) { cursor = document.documentElement.style.cursor || ''; document.documentElement.style.cursor = 'grabbing'; } isMouseEvt === true && document.body.classList.add('no-pointer-events--children'); document.body.classList.add('non-selectable'); clearSelection(); ctx.styleCleanup = withDelayedFn => { ctx.styleCleanup = void 0; if (cursor !== void 0) { document.documentElement.style.cursor = cursor; } document.body.classList.remove('non-selectable'); if (isMouseEvt === true) { const remove = () => { document.body.classList.remove('no-pointer-events--children'); }; if (withDelayedFn !== void 0) { setTimeout(() => { remove(); withDelayedFn(); }, 50); } else { remove(); } } else if (withDelayedFn !== void 0) { withDelayedFn(); } }; }; if (ctx.event.detected === true) { ctx.event.isFirst !== true && handleEvent(evt, ctx.event.mouse); const { payload, synthetic } = getChanges(evt, ctx, false); if (payload !== void 0) { if (ctx.handler(payload) === false) { ctx.end(evt); } else { if (ctx.styleCleanup === void 0 && ctx.event.isFirst === true) { start(); } ctx.event.lastX = payload.position.left; ctx.event.lastY = payload.position.top; ctx.event.lastDir = synthetic === true ? void 0 : payload.direction; ctx.event.isFirst = false; } } return } if ( ctx.direction.all === true // account for UMD too where modifiers will be lowercased to work || (isMouseEvt === true && (ctx.modifiers.mouseAllDir === true || ctx.modifiers.mousealldir === true)) ) { start(); ctx.event.detected = true; ctx.move(evt); return } const absX = Math.abs(distX), absY = Math.abs(distY); if (absX !== absY) { if ( (ctx.direction.horizontal === true && absX > absY) || (ctx.direction.vertical === true && absX < absY) || (ctx.direction.up === true && absX < absY && distY < 0) || (ctx.direction.down === true && absX < absY && distY > 0) || (ctx.direction.left === true && absX > absY && distX < 0) || (ctx.direction.right === true && absX > absY && distX > 0) ) { ctx.event.detected = true; ctx.move(evt); } else { ctx.end(evt, true); } } }, end (evt, abort) { if (ctx.event === void 0) { return } cleanEvt(ctx, 'temp'); client.is.firefox === true && preventDraggable(el, false); if (abort === true) { ctx.styleCleanup !== void 0 && ctx.styleCleanup(); if (ctx.event.detected !== true && ctx.initialEvent !== void 0) { ctx.initialEvent.target.dispatchEvent(ctx.initialEvent.event); } } else if (ctx.event.detected === true) { ctx.event.isFirst === true && ctx.handler(getChanges(evt === void 0 ? ctx.lastEvt : evt, ctx).payload); const { payload } = getChanges(evt === void 0 ? ctx.lastEvt : evt, ctx, true); const fn = () => { ctx.handler(payload); }; if (ctx.styleCleanup !== void 0) { ctx.styleCleanup(fn); } else { fn(); } } ctx.event = void 0; ctx.initialEvent = void 0; ctx.lastEvt = void 0; } }; el.__qtouchpan = ctx; if (modifiers.mouse === true) { // account for UMD too where modifiers will be lowercased to work const capture = modifiers.mouseCapture === true || modifiers.mousecapture === true ? 'Capture' : ''; addEvt(ctx, 'main', [ [ el, 'mousedown', 'mouseStart', `passive${ capture }` ] ]); } client.has.touch === true && addEvt(ctx, 'main', [ [ el, 'touchstart', 'touchStart', `passive${ modifiers.capture === true ? 'Capture' : '' }` ], [ el, 'touchmove', 'noop', 'notPassiveCapture' ] // cannot be passive (ex: iOS scroll) ]); }, updated (el, bindings) { const ctx = el.__qtouchpan; if (ctx !== void 0) { if (bindings.oldValue !== bindings.value) { typeof value !== 'function' && ctx.end(); ctx.handler = bindings.value; } ctx.direction = getModifierDirections(bindings.modifiers); } }, beforeUnmount (el) { const ctx = el.__qtouchpan; if (ctx !== void 0) { // emit the end event when the directive is destroyed while active // this is only needed in TouchPan because the rest of the touch directives do not emit an end event // the condition is also checked in the start of function but we avoid the call ctx.event !== void 0 && ctx.end(); cleanEvt(ctx, 'main'); cleanEvt(ctx, 'temp'); client.is.firefox === true && preventDraggable(el, false); ctx.styleCleanup !== void 0 && ctx.styleCleanup(); delete el.__qtouchpan; } } } ); const markerPrefixClass = 'q-slider__marker-labels'; const defaultMarkerConvertFn = v => ({ value: v }); const defaultMarkerLabelRenderFn = ({ marker }) => vue.h('div', { key: marker.value, style: marker.style, class: marker.classes }, marker.label); // PGDOWN, LEFT, DOWN, PGUP, RIGHT, UP const keyCodes$2 = [ 34, 37, 40, 33, 39, 38 ]; const useSliderProps = { ...useDarkProps, ...useFormProps, min: { type: Number, default: 0 }, max: { type: Number, default: 100 }, innerMin: Number, innerMax: Number, step: { type: Number, default: 1, validator: v => v >= 0 }, snap: Boolean, vertical: Boolean, reverse: Boolean, hideSelection: Boolean, color: String, markerLabelsClass: String, label: Boolean, labelColor: String, labelTextColor: String, labelAlways: Boolean, switchLabelSide: Boolean, markers: [ Boolean, Number ], markerLabels: [ Boolean, Array, Object, Function ], switchMarkerLabelsSide: Boolean, trackImg: String, trackColor: String, innerTrackImg: String, innerTrackColor: String, selectionColor: String, selectionImg: String, thumbSize: { type: String, default: '20px' }, trackSize: { type: String, default: '4px' }, disable: Boolean, readonly: Boolean, dense: Boolean, tabindex: [ String, Number ], thumbColor: String, thumbPath: { type: String, default: 'M 4, 10 a 6,6 0 1,0 12,0 a 6,6 0 1,0 -12,0' } }; const useSliderEmits = [ 'pan', 'update:modelValue', 'change' ]; function useSlider ({ updateValue, updatePosition, getDragging, formAttrs }) { const { props, emit, slots, proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const injectFormInput = useFormInject(formAttrs); const active = vue.ref(false); const preventFocus = vue.ref(false); const focus = vue.ref(false); const dragging = vue.ref(false); const axis = vue.computed(() => (props.vertical === true ? '--v' : '--h')); const labelSide = vue.computed(() => '-' + (props.switchLabelSide === true ? 'switched' : 'standard')); const isReversed = vue.computed(() => ( props.vertical === true ? props.reverse === true : props.reverse !== ($q.lang.rtl === true) )); const innerMin = vue.computed(() => ( isNaN(props.innerMin) === true || props.innerMin < props.min ? props.min : props.innerMin )); const innerMax = vue.computed(() => ( isNaN(props.innerMax) === true || props.innerMax > props.max ? props.max : props.innerMax )); const editable = vue.computed(() => ( props.disable !== true && props.readonly !== true && innerMin.value < innerMax.value )); const decimals = vue.computed(() => (String(props.step).trim().split('.')[ 1 ] || '').length); const step = vue.computed(() => (props.step === 0 ? 1 : props.step)); const tabindex = vue.computed(() => (editable.value === true ? props.tabindex || 0 : -1)); const trackLen = vue.computed(() => props.max - props.min); const innerBarLen = vue.computed(() => innerMax.value - innerMin.value); const innerMinRatio = vue.computed(() => convertModelToRatio(innerMin.value)); const innerMaxRatio = vue.computed(() => convertModelToRatio(innerMax.value)); const positionProp = vue.computed(() => ( props.vertical === true ? (isReversed.value === true ? 'bottom' : 'top') : (isReversed.value === true ? 'right' : 'left') )); const sizeProp = vue.computed(() => (props.vertical === true ? 'height' : 'width')); const thicknessProp = vue.computed(() => (props.vertical === true ? 'width' : 'height')); const orientation = vue.computed(() => (props.vertical === true ? 'vertical' : 'horizontal')); const attributes = vue.computed(() => { const acc = { role: 'slider', 'aria-valuemin': innerMin.value, 'aria-valuemax': innerMax.value, 'aria-orientation': orientation.value, 'data-step': props.step }; if (props.disable === true) { acc[ 'aria-disabled' ] = 'true'; } else if (props.readonly === true) { acc[ 'aria-readonly' ] = 'true'; } return acc }); const classes = vue.computed(() => `q-slider q-slider${ axis.value } q-slider--${ active.value === true ? '' : 'in' }active inline no-wrap ` + (props.vertical === true ? 'row' : 'column') + (props.disable === true ? ' disabled' : ' q-slider--enabled' + (editable.value === true ? ' q-slider--editable' : '')) + (focus.value === 'both' ? ' q-slider--focus' : '') + (props.label || props.labelAlways === true ? ' q-slider--label' : '') + (props.labelAlways === true ? ' q-slider--label-always' : '') + (isDark.value === true ? ' q-slider--dark' : '') + (props.dense === true ? ' q-slider--dense q-slider--dense' + axis.value : '') ); function getPositionClass (name) { const cls = 'q-slider__' + name; return `${ cls } ${ cls }${ axis.value } ${ cls }${ axis.value }${ labelSide.value }` } function getAxisClass (name) { const cls = 'q-slider__' + name; return `${ cls } ${ cls }${ axis.value }` } const selectionBarClass = vue.computed(() => { const color = props.selectionColor || props.color; return 'q-slider__selection absolute' + (color !== void 0 ? ` text-${ color }` : '') }); const markerClass = vue.computed(() => getAxisClass('markers') + ' absolute overflow-hidden'); const trackContainerClass = vue.computed(() => getAxisClass('track-container')); const pinClass = vue.computed(() => getPositionClass('pin')); const labelClass = vue.computed(() => getPositionClass('label')); const textContainerClass = vue.computed(() => getPositionClass('text-container')); const markerLabelsContainerClass = vue.computed(() => getPositionClass('marker-labels-container') + (props.markerLabelsClass !== void 0 ? ` ${ props.markerLabelsClass }` : '') ); const trackClass = vue.computed(() => 'q-slider__track relative-position no-outline' + (props.trackColor !== void 0 ? ` bg-${ props.trackColor }` : '') ); const trackStyle = vue.computed(() => { const acc = { [ thicknessProp.value ]: props.trackSize }; if (props.trackImg !== void 0) { acc.backgroundImage = `url(${ props.trackImg }) !important`; } return acc }); const innerBarClass = vue.computed(() => 'q-slider__inner absolute' + (props.innerTrackColor !== void 0 ? ` bg-${ props.innerTrackColor }` : '') ); const innerBarStyle = vue.computed(() => { const acc = { [ positionProp.value ]: `${ 100 * innerMinRatio.value }%`, [ sizeProp.value ]: `${ 100 * (innerMaxRatio.value - innerMinRatio.value) }%` }; if (props.innerTrackImg !== void 0) { acc.backgroundImage = `url(${ props.innerTrackImg }) !important`; } return acc }); function convertRatioToModel (ratio) { const { min, max, step } = props; let model = min + ratio * (max - min); if (step > 0) { const modulo = (model - min) % step; model += (Math.abs(modulo) >= step / 2 ? (modulo < 0 ? -1 : 1) * step : 0) - modulo; } if (decimals.value > 0) { model = parseFloat(model.toFixed(decimals.value)); } return between(model, innerMin.value, innerMax.value) } function convertModelToRatio (model) { return trackLen.value === 0 ? 0 : (model - props.min) / trackLen.value } function getDraggingRatio (evt, dragging) { const pos = position(evt), val = props.vertical === true ? between((pos.top - dragging.top) / dragging.height, 0, 1) : between((pos.left - dragging.left) / dragging.width, 0, 1); return between( isReversed.value === true ? 1.0 - val : val, innerMinRatio.value, innerMaxRatio.value ) } const markerStep = vue.computed(() => ( isNumber(props.markers) === true ? props.markers : step.value) ); const markerTicks = vue.computed(() => { const acc = []; const step = markerStep.value; const max = props.max; let value = props.min; do { acc.push(value); value += step; } while (value < max) acc.push(max); return acc }); const markerLabelClass = vue.computed(() => { const prefix = ` ${ markerPrefixClass }${ axis.value }-`; return markerPrefixClass + `${ prefix }${ props.switchMarkerLabelsSide === true ? 'switched' : 'standard' }` + `${ prefix }${ isReversed.value === true ? 'rtl' : 'ltr' }` }); const markerLabelsList = vue.computed(() => { if (props.markerLabels === false) { return null } return getMarkerList(props.markerLabels).map((entry, index) => ({ index, value: entry.value, label: entry.label || entry.value, classes: markerLabelClass.value + (entry.classes !== void 0 ? ' ' + entry.classes : ''), style: { ...getMarkerLabelStyle(entry.value), ...(entry.style || {}) } })) }); const markerScope = vue.computed(() => ({ markerList: markerLabelsList.value, markerMap: markerLabelsMap.value, classes: markerLabelClass.value, // TODO ts definition getStyle: getMarkerLabelStyle })); const markerStyle = vue.computed(() => { if (innerBarLen.value !== 0) { const size = 100 * markerStep.value / innerBarLen.value; return { ...innerBarStyle.value, backgroundSize: props.vertical === true ? `2px ${ size }%` : `${ size }% 2px` } } return null }); function getMarkerList (def) { if (def === false) { return null } if (def === true) { return markerTicks.value.map(defaultMarkerConvertFn) } if (typeof def === 'function') { return markerTicks.value.map(value => { const item = def(value); return isObject(item) === true ? { ...item, value } : { value, label: item } }) } const filterFn = ({ value }) => value >= props.min && value <= props.max; if (Array.isArray(def) === true) { return def .map(item => (isObject(item) === true ? item : { value: item })) .filter(filterFn) } return Object.keys(def).map(key => { const item = def[ key ]; const value = Number(key); return isObject(item) === true ? { ...item, value } : { value, label: item } }).filter(filterFn) } function getMarkerLabelStyle (val) { return { [ positionProp.value ]: `${ 100 * (val - props.min) / trackLen.value }%` } } const markerLabelsMap = vue.computed(() => { if (props.markerLabels === false) { return null } const acc = {}; markerLabelsList.value.forEach(entry => { acc[ entry.value ] = entry; }); return acc }); function getMarkerLabelsContent () { if (slots[ 'marker-label-group' ] !== void 0) { return slots[ 'marker-label-group' ](markerScope.value) } const fn = slots[ 'marker-label' ] || defaultMarkerLabelRenderFn; return markerLabelsList.value.map(marker => fn({ marker, ...markerScope.value })) } const panDirective = vue.computed(() => { // if editable.value === true return [ [ TouchPan, onPan, void 0, { [ orientation.value ]: true, prevent: true, stop: true, mouse: true, mouseAllDir: true } ] ] }); function onPan (event) { if (event.isFinal === true) { if (dragging.value !== void 0) { updatePosition(event.evt); // only if touch, because we also have mousedown/up: event.touch === true && updateValue(true); dragging.value = void 0; emit('pan', 'end'); } active.value = false; focus.value = false; } else if (event.isFirst === true) { dragging.value = getDragging(event.evt); updatePosition(event.evt); updateValue(); active.value = true; emit('pan', 'start'); } else { updatePosition(event.evt); updateValue(); } } function onBlur () { focus.value = false; } function onActivate (evt) { updatePosition(evt, getDragging(evt)); updateValue(); preventFocus.value = true; active.value = true; document.addEventListener('mouseup', onDeactivate, true); } function onDeactivate () { preventFocus.value = false; active.value = false; updateValue(true); onBlur(); document.removeEventListener('mouseup', onDeactivate, true); } function onMobileClick (evt) { updatePosition(evt, getDragging(evt)); updateValue(true); } function onKeyup (evt) { if (keyCodes$2.includes(evt.keyCode)) { updateValue(true); } } function getTextContainerStyle (ratio) { if (props.vertical === true) { return null } const p = $q.lang.rtl !== props.reverse ? 1 - ratio : ratio; return { transform: `translateX(calc(${ 2 * p - 1 } * ${ props.thumbSize } / 2 + ${ 50 - 100 * p }%))` } } function getThumbRenderFn (thumb) { const focusClass = vue.computed(() => ( preventFocus.value === false && (focus.value === thumb.focusValue || focus.value === 'both') ? ' q-slider--focus' : '' )); const classes = vue.computed(() => `q-slider__thumb q-slider__thumb${ axis.value } q-slider__thumb${ axis.value }-${ isReversed.value === true ? 'rtl' : 'ltr' } absolute non-selectable` + focusClass.value + (thumb.thumbColor.value !== void 0 ? ` text-${ thumb.thumbColor.value }` : '') ); const style = vue.computed(() => ({ width: props.thumbSize, height: props.thumbSize, [ positionProp.value ]: `${ 100 * thumb.ratio.value }%`, zIndex: focus.value === thumb.focusValue ? 2 : void 0 })); const pinColor = vue.computed(() => ( thumb.labelColor.value !== void 0 ? ` text-${ thumb.labelColor.value }` : '' )); const textContainerStyle = vue.computed(() => getTextContainerStyle(thumb.ratio.value)); const textClass = vue.computed(() => ( 'q-slider__text' + (thumb.labelTextColor.value !== void 0 ? ` text-${ thumb.labelTextColor.value }` : '') )); return () => { const thumbContent = [ vue.h('svg', { class: 'q-slider__thumb-shape absolute-full', viewBox: '0 0 20 20', 'aria-hidden': 'true' }, [ vue.h('path', { d: props.thumbPath }) ]), vue.h('div', { class: 'q-slider__focus-ring fit' }) ]; if (props.label === true || props.labelAlways === true) { thumbContent.push( vue.h('div', { class: pinClass.value + ' absolute fit no-pointer-events' + pinColor.value }, [ vue.h('div', { class: labelClass.value, style: { minWidth: props.thumbSize } }, [ vue.h('div', { class: textContainerClass.value, style: textContainerStyle.value }, [ vue.h('span', { class: textClass.value }, thumb.label.value) ]) ]) ]) ); if (props.name !== void 0 && props.disable !== true) { injectFormInput(thumbContent, 'push'); } } return vue.h('div', { class: classes.value, style: style.value, ...thumb.getNodeData() }, thumbContent) } } function getContent (selectionBarStyle, trackContainerTabindex, trackContainerEvents, injectThumb) { const trackContent = []; props.innerTrackColor !== 'transparent' && trackContent.push( vue.h('div', { key: 'inner', class: innerBarClass.value, style: innerBarStyle.value }) ); props.selectionColor !== 'transparent' && trackContent.push( vue.h('div', { key: 'selection', class: selectionBarClass.value, style: selectionBarStyle.value }) ); props.markers !== false && trackContent.push( vue.h('div', { key: 'marker', class: markerClass.value, style: markerStyle.value }) ); injectThumb(trackContent); const content = [ hDir( 'div', { key: 'trackC', class: trackContainerClass.value, tabindex: trackContainerTabindex.value, ...trackContainerEvents.value }, [ vue.h('div', { class: trackClass.value, style: trackStyle.value }, trackContent) ], 'slide', editable.value, () => panDirective.value ) ]; if (props.markerLabels !== false) { const action = props.switchMarkerLabelsSide === true ? 'unshift' : 'push'; content[ action ]( vue.h('div', { key: 'markerL', class: markerLabelsContainerClass.value }, getMarkerLabelsContent()) ); } return content } vue.onBeforeUnmount(() => { document.removeEventListener('mouseup', onDeactivate, true); }); return { state: { active, focus, preventFocus, dragging, editable, classes, tabindex, attributes, step, decimals, trackLen, innerMin, innerMinRatio, innerMax, innerMaxRatio, positionProp, sizeProp, isReversed }, methods: { onActivate, onMobileClick, onBlur, onKeyup, getContent, getThumbRenderFn, convertRatioToModel, convertModelToRatio, getDraggingRatio } } } const getNodeData = () => ({}); var QSlider = createComponent({ name: 'QSlider', props: { ...useSliderProps, modelValue: { required: true, default: null, validator: v => typeof v === 'number' || v === null }, labelValue: [ String, Number ] }, emits: useSliderEmits, setup (props, { emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const { state, methods } = useSlider({ updateValue, updatePosition, getDragging, formAttrs: useFormAttrs(props) }); const rootRef = vue.ref(null); const curRatio = vue.ref(0); const model = vue.ref(0); function normalizeModel () { model.value = props.modelValue === null ? state.innerMin.value : between(props.modelValue, state.innerMin.value, state.innerMax.value); } vue.watch( () => `${ props.modelValue }|${ state.innerMin.value }|${ state.innerMax.value }`, normalizeModel ); normalizeModel(); const modelRatio = vue.computed(() => methods.convertModelToRatio(model.value)); const ratio = vue.computed(() => (state.active.value === true ? curRatio.value : modelRatio.value)); const selectionBarStyle = vue.computed(() => { const acc = { [ state.positionProp.value ]: `${ 100 * state.innerMinRatio.value }%`, [ state.sizeProp.value ]: `${ 100 * (ratio.value - state.innerMinRatio.value) }%` }; if (props.selectionImg !== void 0) { acc.backgroundImage = `url(${ props.selectionImg }) !important`; } return acc }); const getThumb = methods.getThumbRenderFn({ focusValue: true, getNodeData, ratio, label: vue.computed(() => ( props.labelValue !== void 0 ? props.labelValue : model.value )), thumbColor: vue.computed(() => props.thumbColor || props.color), labelColor: vue.computed(() => props.labelColor), labelTextColor: vue.computed(() => props.labelTextColor) }); const trackContainerEvents = vue.computed(() => { if (state.editable.value !== true) { return {} } return $q.platform.is.mobile === true ? { onClick: methods.onMobileClick } : { onMousedown: methods.onActivate, onFocus, onBlur: methods.onBlur, onKeydown, onKeyup: methods.onKeyup } }); function updateValue (change) { if (model.value !== props.modelValue) { emit('update:modelValue', model.value); } change === true && emit('change', model.value); } function getDragging () { return rootRef.value.getBoundingClientRect() } function updatePosition (event, dragging = state.dragging.value) { const ratio = methods.getDraggingRatio(event, dragging); model.value = methods.convertRatioToModel(ratio); curRatio.value = props.snap !== true || props.step === 0 ? ratio : methods.convertModelToRatio(model.value); } function onFocus () { state.focus.value = true; } function onKeydown (evt) { if (!keyCodes$2.includes(evt.keyCode)) { return } stopAndPrevent(evt); const stepVal = ([ 34, 33 ].includes(evt.keyCode) ? 10 : 1) * state.step.value, offset = ( ([ 34, 37, 40 ].includes(evt.keyCode) ? -1 : 1) * (state.isReversed.value === true ? -1 : 1) * (props.vertical === true ? -1 : 1) * stepVal ); model.value = between( parseFloat((model.value + offset).toFixed(state.decimals.value)), state.innerMin.value, state.innerMax.value ); updateValue(); } return () => { const content = methods.getContent( selectionBarStyle, state.tabindex, trackContainerEvents, node => { node.push(getThumb()); } ); return vue.h('div', { ref: rootRef, class: state.classes.value + (props.modelValue === null ? ' q-slider--no-value' : ''), ...state.attributes.value, 'aria-valuenow': props.modelValue }, content) } } }); function useCanRender () { const canRender = vue.ref(!isRuntimeSsrPreHydration.value); if (canRender.value === false) { vue.onMounted(() => { canRender.value = true; }); } return canRender } const hasObserver = typeof ResizeObserver !== 'undefined'; const resizeProps = hasObserver === true ? {} : { style: 'display:block;position:absolute;top:0;left:0;right:0;bottom:0;height:100%;width:100%;overflow:hidden;pointer-events:none;z-index:-1;', url: 'about:blank' }; var QResizeObserver = createComponent({ name: 'QResizeObserver', props: { debounce: { type: [ String, Number ], default: 100 } }, emits: [ 'resize' ], setup (props, { emit }) { let timer = null, targetEl, size = { width: -1, height: -1 }; function trigger (immediately) { if (immediately === true || props.debounce === 0 || props.debounce === '0') { emitEvent(); } else if (timer === null) { timer = setTimeout(emitEvent, props.debounce); } } function emitEvent () { if (timer !== null) { clearTimeout(timer); timer = null; } if (targetEl) { const { offsetWidth: width, offsetHeight: height } = targetEl; if (width !== size.width || height !== size.height) { size = { width, height }; emit('resize', size); } } } const { proxy } = vue.getCurrentInstance(); if (hasObserver === true) { let observer; // initialize as soon as possible const init = stop => { targetEl = proxy.$el.parentNode; if (targetEl) { observer = new ResizeObserver(trigger); observer.observe(targetEl); emitEvent(); } else if (stop !== true) { vue.nextTick(() => { init(true); }); } }; vue.onMounted(() => { init(); }); vue.onBeforeUnmount(() => { timer !== null && clearTimeout(timer); if (observer !== void 0) { if (observer.disconnect !== void 0) { observer.disconnect(); } else if (targetEl) { // FF for Android observer.unobserve(targetEl); } } }); return noop } else { // no observer, so fallback to old iframe method const canRender = useCanRender(); let curDocView; function cleanup () { if (timer !== null) { clearTimeout(timer); timer = null; } if (curDocView !== void 0) { // iOS is fuzzy, need to check it first if (curDocView.removeEventListener !== void 0) { curDocView.removeEventListener('resize', trigger, listenOpts.passive); } curDocView = void 0; } } function onObjLoad () { cleanup(); if (targetEl && targetEl.contentDocument) { curDocView = targetEl.contentDocument.defaultView; curDocView.addEventListener('resize', trigger, listenOpts.passive); emitEvent(); } } vue.onMounted(() => { vue.nextTick(() => { targetEl = proxy.$el; targetEl && onObjLoad(); }); }); vue.onBeforeUnmount(cleanup); // expose public method proxy.trigger = trigger; return () => { if (canRender.value === true) { return vue.h('object', { style: resizeProps.style, tabindex: -1, // fix for Firefox type: 'text/html', data: resizeProps.url, 'aria-hidden': 'true', onLoad: onObjLoad }) } } } } }); let rtlHasScrollBug = false; // mobile Chrome takes the crown for this { const scroller = document.createElement('div'); scroller.setAttribute('dir', 'rtl'); Object.assign(scroller.style, { width: '1px', height: '1px', overflow: 'auto' }); const spacer = document.createElement('div'); Object.assign(spacer.style, { width: '1000px', height: '1px' }); document.body.appendChild(scroller); scroller.appendChild(spacer); scroller.scrollLeft = -1000; rtlHasScrollBug = scroller.scrollLeft >= 0; scroller.remove(); } function getIndicatorClass (color, top, vertical) { const pos = vertical === true ? [ 'left', 'right' ] : [ 'top', 'bottom' ]; return `absolute-${ top === true ? pos[ 0 ] : pos[ 1 ] }${ color ? ` text-${ color }` : '' }` } const alignValues$1 = [ 'left', 'center', 'right', 'justify' ]; var QTabs = createComponent({ name: 'QTabs', props: { modelValue: [ Number, String ], align: { type: String, default: 'center', validator: v => alignValues$1.includes(v) }, breakpoint: { type: [ String, Number ], default: 600 }, vertical: Boolean, shrink: Boolean, stretch: Boolean, activeClass: String, activeColor: String, activeBgColor: String, indicatorColor: String, leftIcon: String, rightIcon: String, outsideArrows: Boolean, mobileArrows: Boolean, switchIndicator: Boolean, narrowIndicator: Boolean, inlineLabel: Boolean, noCaps: Boolean, dense: Boolean, contentClass: String, 'onUpdate:modelValue': [ Function, Array ] }, setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const { registerTick: registerScrollTick } = useTick(); const { registerTick: registerUpdateArrowsTick } = useTick(); const { registerTick: registerAnimateTick } = useTick(); const { registerTimeout: registerFocusTimeout, removeTimeout: removeFocusTimeout } = useTimeout(); const { registerTimeout: registerScrollToTabTimeout, removeTimeout: removeScrollToTabTimeout } = useTimeout(); const rootRef = vue.ref(null); const contentRef = vue.ref(null); const currentModel = vue.ref(props.modelValue); const scrollable = vue.ref(false); const leftArrow = vue.ref(true); const rightArrow = vue.ref(false); const justify = vue.ref(false); const tabDataList = []; const tabDataListLen = vue.ref(0); const hasFocus = vue.ref(false); let animateTimer = null, scrollTimer = null, unwatchRoute; const tabProps = vue.computed(() => ({ activeClass: props.activeClass, activeColor: props.activeColor, activeBgColor: props.activeBgColor, indicatorClass: getIndicatorClass( props.indicatorColor, props.switchIndicator, props.vertical ), narrowIndicator: props.narrowIndicator, inlineLabel: props.inlineLabel, noCaps: props.noCaps })); const hasActiveTab = vue.computed(() => { const len = tabDataListLen.value; const val = currentModel.value; for (let i = 0; i < len; i++) { if (tabDataList[ i ].name.value === val) { return true } } return false }); const alignClass = vue.computed(() => { const align = scrollable.value === true ? 'left' : (justify.value === true ? 'justify' : props.align); return `q-tabs__content--align-${ align }` }); const classes = vue.computed(() => 'q-tabs row no-wrap items-center' + ` q-tabs--${ scrollable.value === true ? '' : 'not-' }scrollable` + ` q-tabs--${ props.vertical === true ? 'vertical' : 'horizontal' }` + ` q-tabs__arrows--${ props.outsideArrows === true ? 'outside' : 'inside' }` + ` q-tabs--mobile-with${ props.mobileArrows === true ? '' : 'out' }-arrows` + (props.dense === true ? ' q-tabs--dense' : '') + (props.shrink === true ? ' col-shrink' : '') + (props.stretch === true ? ' self-stretch' : '') ); const innerClass = vue.computed(() => 'q-tabs__content scroll--mobile row no-wrap items-center self-stretch hide-scrollbar relative-position ' + alignClass.value + (props.contentClass !== void 0 ? ` ${ props.contentClass }` : '') ); const domProps = vue.computed(() => ( props.vertical === true ? { container: 'height', content: 'offsetHeight', scroll: 'scrollHeight' } : { container: 'width', content: 'offsetWidth', scroll: 'scrollWidth' } )); const isRTL = vue.computed(() => props.vertical !== true && $q.lang.rtl === true); const rtlPosCorrection = vue.computed(() => rtlHasScrollBug === false && isRTL.value === true); vue.watch(isRTL, updateArrows); vue.watch(() => props.modelValue, name => { updateModel({ name, setCurrent: true, skipEmit: true }); }); vue.watch(() => props.outsideArrows, recalculateScroll); function updateModel ({ name, setCurrent, skipEmit }) { if (currentModel.value !== name) { if (skipEmit !== true && props[ 'onUpdate:modelValue' ] !== void 0) { emit('update:modelValue', name); } if ( setCurrent === true || props[ 'onUpdate:modelValue' ] === void 0 ) { animate(currentModel.value, name); currentModel.value = name; } } } function recalculateScroll () { registerScrollTick(() => { updateContainer({ width: rootRef.value.offsetWidth, height: rootRef.value.offsetHeight }); }); } function updateContainer (domSize) { // it can be called faster than component being initialized // so we need to protect against that case // (one example of such case is the docs release notes page) if (domProps.value === void 0 || contentRef.value === null) { return } const size = domSize[ domProps.value.container ], scrollSize = Math.min( contentRef.value[ domProps.value.scroll ], Array.prototype.reduce.call( contentRef.value.children, (acc, el) => acc + (el[ domProps.value.content ] || 0), 0 ) ), scroll = size > 0 && scrollSize > size; // when there is no tab, in Chrome, size === 0 and scrollSize === 1 scrollable.value = scroll; // Arrows need to be updated even if the scroll status was already true scroll === true && registerUpdateArrowsTick(updateArrows); justify.value = size < parseInt(props.breakpoint, 10); } function animate (oldName, newName) { const oldTab = oldName !== void 0 && oldName !== null && oldName !== '' ? tabDataList.find(tab => tab.name.value === oldName) : null, newTab = newName !== void 0 && newName !== null && newName !== '' ? tabDataList.find(tab => tab.name.value === newName) : null; if (oldTab && newTab) { const oldEl = oldTab.tabIndicatorRef.value, newEl = newTab.tabIndicatorRef.value; if (animateTimer !== null) { clearTimeout(animateTimer); animateTimer = null; } oldEl.style.transition = 'none'; oldEl.style.transform = 'none'; newEl.style.transition = 'none'; newEl.style.transform = 'none'; const oldPos = oldEl.getBoundingClientRect(), newPos = newEl.getBoundingClientRect(); newEl.style.transform = props.vertical === true ? `translate3d(0,${ oldPos.top - newPos.top }px,0) scale3d(1,${ newPos.height ? oldPos.height / newPos.height : 1 },1)` : `translate3d(${ oldPos.left - newPos.left }px,0,0) scale3d(${ newPos.width ? oldPos.width / newPos.width : 1 },1,1)`; // allow scope updates to kick in (QRouteTab needs more time) registerAnimateTick(() => { animateTimer = setTimeout(() => { animateTimer = null; newEl.style.transition = 'transform .25s cubic-bezier(.4, 0, .2, 1)'; newEl.style.transform = 'none'; }, 70); }); } if (newTab && scrollable.value === true) { scrollToTabEl(newTab.rootRef.value); } } function scrollToTabEl (el) { const { left, width, top, height } = contentRef.value.getBoundingClientRect(), newPos = el.getBoundingClientRect(); let offset = props.vertical === true ? newPos.top - top : newPos.left - left; if (offset < 0) { contentRef.value[ props.vertical === true ? 'scrollTop' : 'scrollLeft' ] += Math.floor(offset); updateArrows(); return } offset += props.vertical === true ? newPos.height - height : newPos.width - width; if (offset > 0) { contentRef.value[ props.vertical === true ? 'scrollTop' : 'scrollLeft' ] += Math.ceil(offset); updateArrows(); } } function updateArrows () { const content = contentRef.value; if (content === null) { return } const rect = content.getBoundingClientRect(), pos = props.vertical === true ? content.scrollTop : Math.abs(content.scrollLeft); if (isRTL.value === true) { leftArrow.value = Math.ceil(pos + rect.width) < content.scrollWidth - 1; rightArrow.value = pos > 0; } else { leftArrow.value = pos > 0; rightArrow.value = props.vertical === true ? Math.ceil(pos + rect.height) < content.scrollHeight : Math.ceil(pos + rect.width) < content.scrollWidth; } } function animScrollTo (value) { scrollTimer !== null && clearInterval(scrollTimer); scrollTimer = setInterval(() => { if (scrollTowards(value) === true) { stopAnimScroll(); } }, 5); } function scrollToStart () { animScrollTo(rtlPosCorrection.value === true ? Number.MAX_SAFE_INTEGER : 0); } function scrollToEnd () { animScrollTo(rtlPosCorrection.value === true ? 0 : Number.MAX_SAFE_INTEGER); } function stopAnimScroll () { if (scrollTimer !== null) { clearInterval(scrollTimer); scrollTimer = null; } } function onKbdNavigate (keyCode, fromEl) { const tabs = Array.prototype.filter.call( contentRef.value.children, el => el === fromEl || (el.matches && el.matches('.q-tab.q-focusable') === true) ); const len = tabs.length; if (len === 0) { return } if (keyCode === 36) { // Home scrollToTabEl(tabs[ 0 ]); tabs[ 0 ].focus(); return true } if (keyCode === 35) { // End scrollToTabEl(tabs[ len - 1 ]); tabs[ len - 1 ].focus(); return true } const dirPrev = keyCode === (props.vertical === true ? 38 /* ArrowUp */ : 37 /* ArrowLeft */); const dirNext = keyCode === (props.vertical === true ? 40 /* ArrowDown */ : 39 /* ArrowRight */); const dir = dirPrev === true ? -1 : (dirNext === true ? 1 : void 0); if (dir !== void 0) { const rtlDir = isRTL.value === true ? -1 : 1; const index = tabs.indexOf(fromEl) + dir * rtlDir; if (index >= 0 && index < len) { scrollToTabEl(tabs[ index ]); tabs[ index ].focus({ preventScroll: true }); } return true } } // let's speed up execution of time-sensitive scrollTowards() // with a computed variable by directly applying the minimal // number of instructions on get/set functions const posFn = vue.computed(() => ( rtlPosCorrection.value === true ? { get: content => Math.abs(content.scrollLeft), set: (content, pos) => { content.scrollLeft = -pos; } } : ( props.vertical === true ? { get: content => content.scrollTop, set: (content, pos) => { content.scrollTop = pos; } } : { get: content => content.scrollLeft, set: (content, pos) => { content.scrollLeft = pos; } } ) )); function scrollTowards (value) { const content = contentRef.value, { get, set } = posFn.value; let done = false, pos = get(content); const direction = value < pos ? -1 : 1; pos += direction * 5; if (pos < 0) { done = true; pos = 0; } else if ( (direction === -1 && pos <= value) || (direction === 1 && pos >= value) ) { done = true; pos = value; } set(content, pos); updateArrows(); return done } function hasQueryIncluded (targetQuery, matchingQuery) { for (const key in targetQuery) { if (targetQuery[ key ] !== matchingQuery[ key ]) { return false } } return true } // do not use directly; use verifyRouteModel() instead function updateActiveRoute () { let name = null, bestScore = { matchedLen: 0, queryDiff: 9999, hrefLen: 0 }; const list = tabDataList.filter(tab => tab.routeData !== void 0 && tab.routeData.hasRouterLink.value === true); const { hash: currentHash, query: currentQuery } = proxy.$route; const currentQueryLen = Object.keys(currentQuery).length; // Vue Router does not keep account of hash & query when matching // so we're doing this as well for (const tab of list) { const exact = tab.routeData.exact.value === true; if (tab.routeData[ exact === true ? 'linkIsExactActive' : 'linkIsActive' ].value !== true) { // it cannot match anything as it's not active nor exact-active continue } const { hash, query, matched, href } = tab.routeData.resolvedLink.value; const queryLen = Object.keys(query).length; if (exact === true) { if (hash !== currentHash) { // it's set to exact but it doesn't matches the hash continue } if ( queryLen !== currentQueryLen || hasQueryIncluded(currentQuery, query) === false ) { // it's set to exact but it doesn't matches the query continue } // yey, we found the perfect match (route + hash + query) name = tab.name.value; break } if (hash !== '' && hash !== currentHash) { // it has hash and it doesn't matches continue } if ( queryLen !== 0 && hasQueryIncluded(query, currentQuery) === false ) { // it has query and it doesn't includes the current one continue } const newScore = { matchedLen: matched.length, queryDiff: currentQueryLen - queryLen, hrefLen: href.length - hash.length }; if (newScore.matchedLen > bestScore.matchedLen) { // it matches more routes so it's more specific so we set it as current champion name = tab.name.value; bestScore = newScore; continue } else if (newScore.matchedLen !== bestScore.matchedLen) { // it matches less routes than the current champion so we discard it continue } if (newScore.queryDiff < bestScore.queryDiff) { // query is closer to the current one so we set it as current champion name = tab.name.value; bestScore = newScore; } else if (newScore.queryDiff !== bestScore.queryDiff) { // it matches less routes than the current champion so we discard it continue } if (newScore.hrefLen > bestScore.hrefLen) { // href is lengthier so it's more specific so we set it as current champion name = tab.name.value; bestScore = newScore; } } if ( name === null && tabDataList.some(tab => tab.routeData === void 0 && tab.name.value === currentModel.value) === true ) { // we shouldn't interfere if non-route tab is active return } updateModel({ name, setCurrent: true }); } function onFocusin (e) { removeFocusTimeout(); if ( hasFocus.value !== true && rootRef.value !== null && e.target && typeof e.target.closest === 'function' ) { const tab = e.target.closest('.q-tab'); // if the target is contained by a QTab/QRouteTab // (it might be other elements focused, like additional QBtn) if (tab && rootRef.value.contains(tab) === true) { hasFocus.value = true; scrollable.value === true && scrollToTabEl(tab); } } } function onFocusout () { registerFocusTimeout(() => { hasFocus.value = false; }, 30); } function verifyRouteModel () { if ($tabs.avoidRouteWatcher === false) { registerScrollToTabTimeout(updateActiveRoute); } else { removeScrollToTabTimeout(); } } function watchRoute () { if (unwatchRoute === void 0) { const unwatch = vue.watch(() => proxy.$route.fullPath, verifyRouteModel); unwatchRoute = () => { unwatch(); unwatchRoute = void 0; }; } } function registerTab (tabData) { tabDataList.push(tabData); tabDataListLen.value++; recalculateScroll(); // if it's a QTab or we don't have Vue Router if (tabData.routeData === void 0 || proxy.$route === void 0) { // we should position to the currently active tab (if any) registerScrollToTabTimeout(() => { if (scrollable.value === true) { const value = currentModel.value; const newTab = value !== void 0 && value !== null && value !== '' ? tabDataList.find(tab => tab.name.value === value) : null; newTab && scrollToTabEl(newTab.rootRef.value); } }); } // else if it's a QRouteTab with a valid link else { // start watching route watchRoute(); if (tabData.routeData.hasRouterLink.value === true) { verifyRouteModel(); } } } function unregisterTab (tabData) { tabDataList.splice(tabDataList.indexOf(tabData), 1); tabDataListLen.value--; recalculateScroll(); if (unwatchRoute !== void 0 && tabData.routeData !== void 0) { // unwatch route if we don't have any QRouteTabs left if (tabDataList.every(tab => tab.routeData === void 0) === true) { unwatchRoute(); } // then update model verifyRouteModel(); } } const $tabs = { currentModel, tabProps, hasFocus, hasActiveTab, registerTab, unregisterTab, verifyRouteModel, updateModel, onKbdNavigate, avoidRouteWatcher: false // false | string (uid) }; vue.provide(tabsKey, $tabs); function cleanup () { animateTimer !== null && clearTimeout(animateTimer); stopAnimScroll(); unwatchRoute !== void 0 && unwatchRoute(); } let hadRouteWatcher; vue.onBeforeUnmount(cleanup); vue.onDeactivated(() => { hadRouteWatcher = unwatchRoute !== void 0; cleanup(); }); vue.onActivated(() => { hadRouteWatcher === true && watchRoute(); recalculateScroll(); }); return () => { return vue.h('div', { ref: rootRef, class: classes.value, role: 'tablist', onFocusin, onFocusout }, [ vue.h(QResizeObserver, { onResize: updateContainer }), vue.h('div', { ref: contentRef, class: innerClass.value, onScroll: updateArrows }, hSlot(slots.default)), vue.h(QIcon, { class: 'q-tabs__arrow q-tabs__arrow--left absolute q-tab__icon' + (leftArrow.value === true ? '' : ' q-tabs__arrow--faded'), name: props.leftIcon || $q.iconSet.tabs[ props.vertical === true ? 'up' : 'left' ], onMousedownPassive: scrollToStart, onTouchstartPassive: scrollToStart, onMouseupPassive: stopAnimScroll, onMouseleavePassive: stopAnimScroll, onTouchendPassive: stopAnimScroll }), vue.h(QIcon, { class: 'q-tabs__arrow q-tabs__arrow--right absolute q-tab__icon' + (rightArrow.value === true ? '' : ' q-tabs__arrow--faded'), name: props.rightIcon || $q.iconSet.tabs[ props.vertical === true ? 'down' : 'right' ], onMousedownPassive: scrollToEnd, onTouchstartPassive: scrollToEnd, onMouseupPassive: stopAnimScroll, onMouseleavePassive: stopAnimScroll, onTouchendPassive: stopAnimScroll }) ]) } } }); let id$1 = 0; const useTabEmits = [ 'click', 'keydown' ]; const useTabProps = { icon: String, label: [ Number, String ], alert: [ Boolean, String ], alertIcon: String, name: { type: [ Number, String ], default: () => `t_${ id$1++ }` }, noCaps: Boolean, tabindex: [ String, Number ], disable: Boolean, contentClass: String, ripple: { type: [ Boolean, Object ], default: true } }; function useTab (props, slots, emit, routeData) { const $tabs = vue.inject(tabsKey, emptyRenderFn); if ($tabs === emptyRenderFn) { console.error('QTab/QRouteTab component needs to be child of QTabs'); return emptyRenderFn } const { proxy } = vue.getCurrentInstance(); const blurTargetRef = vue.ref(null); const rootRef = vue.ref(null); const tabIndicatorRef = vue.ref(null); const ripple = vue.computed(() => ( props.disable === true || props.ripple === false ? false : Object.assign( { keyCodes: [ 13, 32 ], early: true }, props.ripple === true ? {} : props.ripple ) )); const isActive = vue.computed(() => $tabs.currentModel.value === props.name); const classes = vue.computed(() => 'q-tab relative-position self-stretch flex flex-center text-center' + ( isActive.value === true ? ( ' q-tab--active' + ($tabs.tabProps.value.activeClass ? ' ' + $tabs.tabProps.value.activeClass : '') + ($tabs.tabProps.value.activeColor ? ` text-${ $tabs.tabProps.value.activeColor }` : '') + ($tabs.tabProps.value.activeBgColor ? ` bg-${ $tabs.tabProps.value.activeBgColor }` : '') ) : ' q-tab--inactive' ) + (props.icon && props.label && $tabs.tabProps.value.inlineLabel === false ? ' q-tab--full' : '') + (props.noCaps === true || $tabs.tabProps.value.noCaps === true ? ' q-tab--no-caps' : '') + (props.disable === true ? ' disabled' : ' q-focusable q-hoverable cursor-pointer') + (routeData !== void 0 ? routeData.linkClass.value : '') ); const innerClass = vue.computed(() => 'q-tab__content self-stretch flex-center relative-position q-anchor--skip non-selectable ' + ($tabs.tabProps.value.inlineLabel === true ? 'row no-wrap q-tab__content--inline' : 'column') + (props.contentClass !== void 0 ? ` ${ props.contentClass }` : '') ); const tabIndex = vue.computed(() => ( ( props.disable === true || $tabs.hasFocus.value === true || (isActive.value === false && $tabs.hasActiveTab.value === true) ) ? -1 : props.tabindex || 0 )); function onClick (e, keyboard) { if (keyboard !== true && blurTargetRef.value !== null) { blurTargetRef.value.focus(); } if (props.disable === true) { // we should hinder native navigation though if (routeData !== void 0 && routeData.hasRouterLink.value === true) { stopAndPrevent(e); } return } // do we have a QTab? if (routeData === void 0) { $tabs.updateModel({ name: props.name }); emit('click', e); return } if (routeData.hasRouterLink.value === true) { const go = (opts = {}) => { // if requiring to go to another route, then we // let the QTabs route watcher do its job, // otherwise directly select this let hardError; const reqId = opts.to === void 0 || isDeepEqual(opts.to, props.to) === true ? ($tabs.avoidRouteWatcher = uid$3()) : null; return routeData.navigateToRouterLink(e, { ...opts, returnRouterError: true }) .catch(err => { hardError = err; }) .then(softError => { if (reqId === $tabs.avoidRouteWatcher) { $tabs.avoidRouteWatcher = false; // if we don't have any hard errors or any soft errors, except for // when navigating to the same route (on all other soft errors, // like when navigation was aborted in a nav guard, we don't activate this tab) if ( hardError === void 0 && ( softError === void 0 || softError.message.startsWith('Avoided redundant navigation') === true ) ) { $tabs.updateModel({ name: props.name }); } } if (opts.returnRouterError === true) { return hardError !== void 0 ? Promise.reject(hardError) : softError } }) }; emit('click', e, go); e.defaultPrevented !== true && go(); return } emit('click', e); } function onKeydown (e) { if (isKeyCode(e, [ 13, 32 ])) { onClick(e, true); } else if ( shouldIgnoreKey(e) !== true && e.keyCode >= 35 && e.keyCode <= 40 && e.altKey !== true && e.metaKey !== true ) { $tabs.onKbdNavigate(e.keyCode, proxy.$el) === true && stopAndPrevent(e); } emit('keydown', e); } function getContent () { const narrow = $tabs.tabProps.value.narrowIndicator, content = [], indicator = vue.h('div', { ref: tabIndicatorRef, class: [ 'q-tab__indicator', $tabs.tabProps.value.indicatorClass ] }); props.icon !== void 0 && content.push( vue.h(QIcon, { class: 'q-tab__icon', name: props.icon }) ); props.label !== void 0 && content.push( vue.h('div', { class: 'q-tab__label' }, props.label) ); props.alert !== false && content.push( props.alertIcon !== void 0 ? vue.h(QIcon, { class: 'q-tab__alert-icon', color: props.alert !== true ? props.alert : void 0, name: props.alertIcon }) : vue.h('div', { class: 'q-tab__alert' + (props.alert !== true ? ` text-${ props.alert }` : '') }) ); narrow === true && content.push(indicator); const node = [ vue.h('div', { class: 'q-focus-helper', tabindex: -1, ref: blurTargetRef }), vue.h('div', { class: innerClass.value }, hMergeSlot(slots.default, content)) ]; narrow === false && node.push(indicator); return node } const tabData = { name: vue.computed(() => props.name), rootRef, tabIndicatorRef, routeData }; vue.onBeforeUnmount(() => { $tabs.unregisterTab(tabData); }); vue.onMounted(() => { $tabs.registerTab(tabData); }); function renderTab (tag, customData) { const data = { ref: rootRef, class: classes.value, tabindex: tabIndex.value, role: 'tab', 'aria-selected': isActive.value === true ? 'true' : 'false', 'aria-disabled': props.disable === true ? 'true' : void 0, onClick, onKeydown, ...customData }; return vue.withDirectives( vue.h(tag, data, getContent()), [ [ Ripple, ripple.value ] ] ) } return { renderTab, $tabs } } var QTab = createComponent({ name: 'QTab', props: useTabProps, emits: useTabEmits, setup (props, { slots, emit }) { const { renderTab } = useTab(props, slots, emit); return () => renderTab('div') } }); var QTabPanels = createComponent({ name: 'QTabPanels', props: { ...usePanelProps, ...useDarkProps }, emits: usePanelEmits, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const { updatePanelsList, getPanelContent, panelDirectives } = usePanel(); const classes = vue.computed(() => 'q-tab-panels q-panel-parent' + (isDark.value === true ? ' q-tab-panels--dark q-dark' : '') ); return () => { updatePanelsList(slots); return hDir( 'div', { class: classes.value }, getPanelContent(), 'pan', props.swipeable, () => panelDirectives.value ) } } }); var QTabPanel = createComponent({ name: 'QTabPanel', props: usePanelChildProps, setup (_, { slots }) { return () => vue.h('div', { class: 'q-tab-panel', role: 'tabpanel' }, hSlot(slots.default)) } }); // file referenced from docs const hex = /^#[0-9a-fA-F]{3}([0-9a-fA-F]{3})?$/, hexa = /^#[0-9a-fA-F]{4}([0-9a-fA-F]{4})?$/, hexOrHexa = /^#([0-9a-fA-F]{3}|[0-9a-fA-F]{4}|[0-9a-fA-F]{6}|[0-9a-fA-F]{8})$/, rgb = /^rgb\(((0|[1-9][\d]?|1[\d]{0,2}|2[\d]?|2[0-4][\d]|25[0-5]),){2}(0|[1-9][\d]?|1[\d]{0,2}|2[\d]?|2[0-4][\d]|25[0-5])\)$/, rgba = /^rgba\(((0|[1-9][\d]?|1[\d]{0,2}|2[\d]?|2[0-4][\d]|25[0-5]),){2}(0|[1-9][\d]?|1[\d]{0,2}|2[\d]?|2[0-4][\d]|25[0-5]),(0|0\.[0-9]+[1-9]|0\.[1-9]+|1)\)$/; // Keep in sync with ui/types/api/validation.d.ts const testPattern = { date: v => /^-?[\d]+\/[0-1]\d\/[0-3]\d$/.test(v), time: v => /^([0-1]?\d|2[0-3]):[0-5]\d$/.test(v), fulltime: v => /^([0-1]?\d|2[0-3]):[0-5]\d:[0-5]\d$/.test(v), timeOrFulltime: v => /^([0-1]?\d|2[0-3]):[0-5]\d(:[0-5]\d)?$/.test(v), // -- RFC 5322 -- // -- Added in v2.6.6 -- // This is a basic helper validation. // For something more complex (like RFC 822) you should write and use your own rule. // We won't be accepting PRs to enhance the one below because of the reason above. // eslint-disable-next-line email: v => /^(([^<>()\[\]\\.,;:\s@"]+(\.[^<>()\[\]\\.,;:\s@"]+)*)|(".+"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}])|(([a-zA-Z\-0-9]+\.)+[a-zA-Z]{2,}))$/.test(v), hexColor: v => hex.test(v), hexaColor: v => hexa.test(v), hexOrHexaColor: v => hexOrHexa.test(v), rgbColor: v => rgb.test(v), rgbaColor: v => rgba.test(v), rgbOrRgbaColor: v => rgb.test(v) || rgba.test(v), hexOrRgbColor: v => hex.test(v) || rgb.test(v), hexaOrRgbaColor: v => hexa.test(v) || rgba.test(v), anyColor: v => hexOrHexa.test(v) || rgb.test(v) || rgba.test(v) }; var patterns = { testPattern }; const reRGBA = /^rgb(a)?\((\d{1,3}),(\d{1,3}),(\d{1,3}),?([01]?\.?\d*?)?\)$/; function rgbToHex ({ r, g, b, a }) { const alpha = a !== void 0; r = Math.round(r); g = Math.round(g); b = Math.round(b); if ( r > 255 || g > 255 || b > 255 || (alpha && a > 100) ) { throw new TypeError('Expected 3 numbers below 256 (and optionally one below 100)') } a = alpha ? (Math.round(255 * a / 100) | 1 << 8).toString(16).slice(1) : ''; return '#' + ((b | g << 8 | r << 16) | 1 << 24).toString(16).slice(1) + a } function rgbToString ({ r, g, b, a }) { return `rgb${ a !== void 0 ? 'a' : '' }(${ r },${ g },${ b }${ a !== void 0 ? ',' + (a / 100) : '' })` } function hexToRgb (hex) { if (typeof hex !== 'string') { throw new TypeError('Expected a string') } hex = hex.replace(/^#/, ''); if (hex.length === 3) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ]; } else if (hex.length === 4) { hex = hex[ 0 ] + hex[ 0 ] + hex[ 1 ] + hex[ 1 ] + hex[ 2 ] + hex[ 2 ] + hex[ 3 ] + hex[ 3 ]; } const num = parseInt(hex, 16); return hex.length > 6 ? { r: num >> 24 & 255, g: num >> 16 & 255, b: num >> 8 & 255, a: Math.round((num & 255) / 2.55) } : { r: num >> 16, g: num >> 8 & 255, b: num & 255 } } function hsvToRgb ({ h, s, v, a }) { let r, g, b; s = s / 100; v = v / 100; h = h / 360; const i = Math.floor(h * 6), f = h * 6 - i, p = v * (1 - s), q = v * (1 - f * s), t = v * (1 - (1 - f) * s); switch (i % 6) { case 0: r = v; g = t; b = p; break case 1: r = q; g = v; b = p; break case 2: r = p; g = v; b = t; break case 3: r = p; g = q; b = v; break case 4: r = t; g = p; b = v; break case 5: r = v; g = p; b = q; break } return { r: Math.round(r * 255), g: Math.round(g * 255), b: Math.round(b * 255), a } } function rgbToHsv ({ r, g, b, a }) { const max = Math.max(r, g, b), min = Math.min(r, g, b), d = max - min, s = (max === 0 ? 0 : d / max), v = max / 255; let h; switch (max) { case min: h = 0; break case r: h = (g - b) + d * (g < b ? 6 : 0); h /= 6 * d; break case g: h = (b - r) + d * 2; h /= 6 * d; break case b: h = (r - g) + d * 4; h /= 6 * d; break } return { h: Math.round(h * 360), s: Math.round(s * 100), v: Math.round(v * 100), a } } function textToRgb (str) { if (typeof str !== 'string') { throw new TypeError('Expected a string') } const color = str.replace(/ /g, ''); const m = reRGBA.exec(color); if (m === null) { return hexToRgb(color) } const rgb = { r: Math.min(255, parseInt(m[ 2 ], 10)), g: Math.min(255, parseInt(m[ 3 ], 10)), b: Math.min(255, parseInt(m[ 4 ], 10)) }; if (m[ 1 ]) { const alpha = parseFloat(m[ 5 ]); rgb.a = Math.min(1, isNaN(alpha) === true ? 1 : alpha) * 100; } return rgb } /* works as darken if percent < 0 */ function lighten (color, percent) { if (typeof color !== 'string') { throw new TypeError('Expected a string as color') } if (typeof percent !== 'number') { throw new TypeError('Expected a numeric percent') } const rgb = textToRgb(color), t = percent < 0 ? 0 : 255, p = Math.abs(percent) / 100, R = rgb.r, G = rgb.g, B = rgb.b; return '#' + ( 0x1000000 + (Math.round((t - R) * p) + R) * 0x10000 + (Math.round((t - G) * p) + G) * 0x100 + (Math.round((t - B) * p) + B) ).toString(16).slice(1) } function luminosity (color) { if (typeof color !== 'string' && (!color || color.r === void 0)) { throw new TypeError('Expected a string or a {r, g, b} object as color') } const rgb = typeof color === 'string' ? textToRgb(color) : color, r = rgb.r / 255, g = rgb.g / 255, b = rgb.b / 255, R = r <= 0.03928 ? r / 12.92 : Math.pow((r + 0.055) / 1.055, 2.4), G = g <= 0.03928 ? g / 12.92 : Math.pow((g + 0.055) / 1.055, 2.4), B = b <= 0.03928 ? b / 12.92 : Math.pow((b + 0.055) / 1.055, 2.4); return 0.2126 * R + 0.7152 * G + 0.0722 * B } function brightness (color) { if (typeof color !== 'string' && (!color || color.r === void 0)) { throw new TypeError('Expected a string or a {r, g, b} object as color') } const rgb = typeof color === 'string' ? textToRgb(color) : color; return (rgb.r * 299 + rgb.g * 587 + rgb.b * 114) / 1000 } function blend (fgColor, bgColor) { if (typeof fgColor !== 'string' && (!fgColor || fgColor.r === void 0)) { throw new TypeError('Expected a string or a {r, g, b[, a]} object as fgColor') } if (typeof bgColor !== 'string' && (!bgColor || bgColor.r === void 0)) { throw new TypeError('Expected a string or a {r, g, b[, a]} object as bgColor') } const rgb1 = typeof fgColor === 'string' ? textToRgb(fgColor) : fgColor, r1 = rgb1.r / 255, g1 = rgb1.g / 255, b1 = rgb1.b / 255, a1 = rgb1.a !== void 0 ? rgb1.a / 100 : 1, rgb2 = typeof bgColor === 'string' ? textToRgb(bgColor) : bgColor, r2 = rgb2.r / 255, g2 = rgb2.g / 255, b2 = rgb2.b / 255, a2 = rgb2.a !== void 0 ? rgb2.a / 100 : 1, a = a1 + a2 * (1 - a1), r = Math.round(((r1 * a1 + r2 * a2 * (1 - a1)) / a) * 255), g = Math.round(((g1 * a1 + g2 * a2 * (1 - a1)) / a) * 255), b = Math.round(((b1 * a1 + b2 * a2 * (1 - a1)) / a) * 255); const ret = { r, g, b, a: Math.round(a * 100) }; return typeof fgColor === 'string' ? rgbToHex(ret) : ret } function changeAlpha (color, offset) { if (typeof color !== 'string') { throw new TypeError('Expected a string as color') } if (offset === void 0 || offset < -1 || offset > 1) { throw new TypeError('Expected offset to be between -1 and 1') } const { r, g, b, a } = textToRgb(color); const alpha = a !== void 0 ? a / 100 : 0; return rgbToHex({ r, g, b, a: Math.round(Math.min(1, Math.max(0, alpha + offset)) * 100) }) } function getPaletteColor (colorName) { if (typeof colorName !== 'string') { throw new TypeError('Expected a string as color') } const el = document.createElement('div'); el.className = `text-${ colorName } invisible fixed no-pointer-events`; document.body.appendChild(el); const result = getComputedStyle(el).getPropertyValue('color'); el.remove(); return rgbToHex(textToRgb(result)) } var colors = { rgbToHex, hexToRgb, hsvToRgb, rgbToHsv, textToRgb, lighten, luminosity, brightness, blend, changeAlpha, getPaletteColor }; const palette = [ 'rgb(255,204,204)', 'rgb(255,230,204)', 'rgb(255,255,204)', 'rgb(204,255,204)', 'rgb(204,255,230)', 'rgb(204,255,255)', 'rgb(204,230,255)', 'rgb(204,204,255)', 'rgb(230,204,255)', 'rgb(255,204,255)', 'rgb(255,153,153)', 'rgb(255,204,153)', 'rgb(255,255,153)', 'rgb(153,255,153)', 'rgb(153,255,204)', 'rgb(153,255,255)', 'rgb(153,204,255)', 'rgb(153,153,255)', 'rgb(204,153,255)', 'rgb(255,153,255)', 'rgb(255,102,102)', 'rgb(255,179,102)', 'rgb(255,255,102)', 'rgb(102,255,102)', 'rgb(102,255,179)', 'rgb(102,255,255)', 'rgb(102,179,255)', 'rgb(102,102,255)', 'rgb(179,102,255)', 'rgb(255,102,255)', 'rgb(255,51,51)', 'rgb(255,153,51)', 'rgb(255,255,51)', 'rgb(51,255,51)', 'rgb(51,255,153)', 'rgb(51,255,255)', 'rgb(51,153,255)', 'rgb(51,51,255)', 'rgb(153,51,255)', 'rgb(255,51,255)', 'rgb(255,0,0)', 'rgb(255,128,0)', 'rgb(255,255,0)', 'rgb(0,255,0)', 'rgb(0,255,128)', 'rgb(0,255,255)', 'rgb(0,128,255)', 'rgb(0,0,255)', 'rgb(128,0,255)', 'rgb(255,0,255)', 'rgb(245,0,0)', 'rgb(245,123,0)', 'rgb(245,245,0)', 'rgb(0,245,0)', 'rgb(0,245,123)', 'rgb(0,245,245)', 'rgb(0,123,245)', 'rgb(0,0,245)', 'rgb(123,0,245)', 'rgb(245,0,245)', 'rgb(214,0,0)', 'rgb(214,108,0)', 'rgb(214,214,0)', 'rgb(0,214,0)', 'rgb(0,214,108)', 'rgb(0,214,214)', 'rgb(0,108,214)', 'rgb(0,0,214)', 'rgb(108,0,214)', 'rgb(214,0,214)', 'rgb(163,0,0)', 'rgb(163,82,0)', 'rgb(163,163,0)', 'rgb(0,163,0)', 'rgb(0,163,82)', 'rgb(0,163,163)', 'rgb(0,82,163)', 'rgb(0,0,163)', 'rgb(82,0,163)', 'rgb(163,0,163)', 'rgb(92,0,0)', 'rgb(92,46,0)', 'rgb(92,92,0)', 'rgb(0,92,0)', 'rgb(0,92,46)', 'rgb(0,92,92)', 'rgb(0,46,92)', 'rgb(0,0,92)', 'rgb(46,0,92)', 'rgb(92,0,92)', 'rgb(255,255,255)', 'rgb(205,205,205)', 'rgb(178,178,178)', 'rgb(153,153,153)', 'rgb(127,127,127)', 'rgb(102,102,102)', 'rgb(76,76,76)', 'rgb(51,51,51)', 'rgb(25,25,25)', 'rgb(0,0,0)' ]; const thumbPath = 'M5 5 h10 v10 h-10 v-10 z'; const alphaTrackImg = 'data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAgAAAAICAYAAADED76LAAAAH0lEQVQoU2NkYGAwZkAFZ5G5jPRRgOYEVDeB3EBjBQBOZwTVugIGyAAAAABJRU5ErkJggg=='; var QColor = createComponent({ name: 'QColor', props: { ...useDarkProps, ...useFormProps, modelValue: String, defaultValue: String, defaultView: { type: String, default: 'spectrum', validator: v => [ 'spectrum', 'tune', 'palette' ].includes(v) }, formatModel: { type: String, default: 'auto', validator: v => [ 'auto', 'hex', 'rgb', 'hexa', 'rgba' ].includes(v) }, palette: Array, noHeader: Boolean, noHeaderTabs: Boolean, noFooter: Boolean, square: Boolean, flat: Boolean, bordered: Boolean, disable: Boolean, readonly: Boolean }, emits: [ 'update:modelValue', 'change' ], setup (props, { emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const { getCache } = useCache(); const spectrumRef = vue.ref(null); const errorIconRef = vue.ref(null); const forceHex = vue.computed(() => ( props.formatModel === 'auto' ? null : props.formatModel.indexOf('hex') > -1 )); const forceAlpha = vue.computed(() => ( props.formatModel === 'auto' ? null : props.formatModel.indexOf('a') > -1 )); const topView = vue.ref( props.formatModel === 'auto' ? ( (props.modelValue === void 0 || props.modelValue === null || props.modelValue === '' || props.modelValue.startsWith('#')) ? 'hex' : 'rgb' ) : (props.formatModel.startsWith('hex') ? 'hex' : 'rgb') ); const view = vue.ref(props.defaultView); const model = vue.ref(parseModel(props.modelValue || props.defaultValue)); const editable = vue.computed(() => props.disable !== true && props.readonly !== true); const isHex = vue.computed(() => props.modelValue === void 0 || props.modelValue === null || props.modelValue === '' || props.modelValue.startsWith('#') ); const isOutputHex = vue.computed(() => ( forceHex.value !== null ? forceHex.value : isHex.value )); const formAttrs = vue.computed(() => ({ type: 'hidden', name: props.name, value: model.value[ isOutputHex.value === true ? 'hex' : 'rgb' ] })); const injectFormInput = useFormInject(formAttrs); const hasAlpha = vue.computed(() => ( forceAlpha.value !== null ? forceAlpha.value : model.value.a !== void 0 )); const currentBgColor = vue.computed(() => ({ backgroundColor: model.value.rgb || '#000' })); const headerClass = vue.computed(() => { const light = model.value.a !== void 0 && model.value.a < 65 ? true : luminosity(model.value) > 0.4; return 'q-color-picker__header-content' + ` q-color-picker__header-content--${ light ? 'light' : 'dark' }` }); const spectrumStyle = vue.computed(() => ({ background: `hsl(${ model.value.h },100%,50%)` })); const spectrumPointerStyle = vue.computed(() => ({ top: `${ 100 - model.value.v }%`, [ $q.lang.rtl === true ? 'right' : 'left' ]: `${ model.value.s }%` })); const computedPalette = vue.computed(() => ( props.palette !== void 0 && props.palette.length !== 0 ? props.palette : palette )); const classes = vue.computed(() => 'q-color-picker' + (props.bordered === true ? ' q-color-picker--bordered' : '') + (props.square === true ? ' q-color-picker--square no-border-radius' : '') + (props.flat === true ? ' q-color-picker--flat no-shadow' : '') + (props.disable === true ? ' disabled' : '') + (isDark.value === true ? ' q-color-picker--dark q-dark' : '') ); const attributes = vue.computed(() => { if (props.disable === true) { return { 'aria-disabled': 'true' } } if (props.readonly === true) { return { 'aria-readonly': 'true' } } return {} }); const spectrumDirective = vue.computed(() => { // if editable.value === true return [ [ TouchPan, onSpectrumPan, void 0, { prevent: true, stop: true, mouse: true } ] ] }); vue.watch(() => props.modelValue, v => { const localModel = parseModel(v || props.defaultValue); if (localModel.hex !== model.value.hex) { model.value = localModel; } }); vue.watch(() => props.defaultValue, v => { if (!props.modelValue && v) { const localModel = parseModel(v); if (localModel.hex !== model.value.hex) { model.value = localModel; } } }); function updateModel (rgb, change) { // update internally model.value.hex = rgbToHex(rgb); model.value.rgb = rgbToString(rgb); model.value.r = rgb.r; model.value.g = rgb.g; model.value.b = rgb.b; model.value.a = rgb.a; const value = model.value[ isOutputHex.value === true ? 'hex' : 'rgb' ]; // emit new value emit('update:modelValue', value); change === true && emit('change', value); } function parseModel (v) { const alpha = forceAlpha.value !== void 0 ? forceAlpha.value : ( props.formatModel === 'auto' ? null : props.formatModel.indexOf('a') > -1 ); if (typeof v !== 'string' || v.length === 0 || testPattern.anyColor(v.replace(/ /g, '')) !== true) { return { h: 0, s: 0, v: 0, r: 0, g: 0, b: 0, a: alpha === true ? 100 : void 0, hex: void 0, rgb: void 0 } } const model = textToRgb(v); if (alpha === true && model.a === void 0) { model.a = 100; } model.hex = rgbToHex(model); model.rgb = rgbToString(model); return Object.assign(model, rgbToHsv(model)) } function changeSpectrum (left, top, change) { const panel = spectrumRef.value; if (panel === null) { return } const width = panel.clientWidth, height = panel.clientHeight, rect = panel.getBoundingClientRect(); let x = Math.min(width, Math.max(0, left - rect.left)); if ($q.lang.rtl === true) { x = width - x; } const y = Math.min(height, Math.max(0, top - rect.top)), s = Math.round(100 * x / width), v = Math.round(100 * Math.max(0, Math.min(1, -(y / height) + 1))), rgb = hsvToRgb({ h: model.value.h, s, v, a: hasAlpha.value === true ? model.value.a : void 0 }); model.value.s = s; model.value.v = v; updateModel(rgb, change); } function onHueChange (val, change) { const h = Math.round(val); const rgb = hsvToRgb({ h, s: model.value.s, v: model.value.v, a: hasAlpha.value === true ? model.value.a : void 0 }); model.value.h = h; updateModel(rgb, change); } function onNumericChange (value, formatModel, max, evt, change) { evt !== void 0 && stop(evt); if (!/^[0-9]+$/.test(value)) { change === true && proxy.$forceUpdate(); return } const val = Math.floor(Number(value)); if (val < 0 || val > max) { change === true && proxy.$forceUpdate(); return } const rgb = { r: formatModel === 'r' ? val : model.value.r, g: formatModel === 'g' ? val : model.value.g, b: formatModel === 'b' ? val : model.value.b, a: hasAlpha.value === true ? (formatModel === 'a' ? val : model.value.a) : void 0 }; if (formatModel !== 'a') { const hsv = rgbToHsv(rgb); model.value.h = hsv.h; model.value.s = hsv.s; model.value.v = hsv.v; } updateModel(rgb, change); if (evt !== void 0 && change !== true && evt.target.selectionEnd !== void 0) { const index = evt.target.selectionEnd; vue.nextTick(() => { evt.target.setSelectionRange(index, index); }); } } function onEditorChange (evt, change) { let rgb; const inp = evt.target.value; stop(evt); if (topView.value === 'hex') { if ( inp.length !== (hasAlpha.value === true ? 9 : 7) || !/^#[0-9A-Fa-f]+$/.test(inp) ) { return true } rgb = hexToRgb(inp); } else { let model; if (!inp.endsWith(')')) { return true } else if (hasAlpha.value !== true && inp.startsWith('rgb(')) { model = inp.substring(4, inp.length - 1).split(',').map(n => parseInt(n, 10)); if ( model.length !== 3 || !/^rgb\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3}\)$/.test(inp) ) { return true } } else if (hasAlpha.value === true && inp.startsWith('rgba(')) { model = inp.substring(5, inp.length - 1).split(','); if ( model.length !== 4 || !/^rgba\([0-9]{1,3},[0-9]{1,3},[0-9]{1,3},(0|0\.[0-9]+[1-9]|0\.[1-9]+|1)\)$/.test(inp) ) { return true } for (let i = 0; i < 3; i++) { const v = parseInt(model[ i ], 10); if (v < 0 || v > 255) { return true } model[ i ] = v; } const v = parseFloat(model[ 3 ]); if (v < 0 || v > 1) { return true } model[ 3 ] = v; } else { return true } if ( model[ 0 ] < 0 || model[ 0 ] > 255 || model[ 1 ] < 0 || model[ 1 ] > 255 || model[ 2 ] < 0 || model[ 2 ] > 255 || (hasAlpha.value === true && (model[ 3 ] < 0 || model[ 3 ] > 1)) ) { return true } rgb = { r: model[ 0 ], g: model[ 1 ], b: model[ 2 ], a: hasAlpha.value === true ? model[ 3 ] * 100 : void 0 }; } const hsv = rgbToHsv(rgb); model.value.h = hsv.h; model.value.s = hsv.s; model.value.v = hsv.v; updateModel(rgb, change); if (change !== true) { const index = evt.target.selectionEnd; vue.nextTick(() => { evt.target.setSelectionRange(index, index); }); } } function onPalettePick (color) { const def = parseModel(color); const rgb = { r: def.r, g: def.g, b: def.b, a: def.a }; if (rgb.a === void 0) { rgb.a = model.value.a; } model.value.h = def.h; model.value.s = def.s; model.value.v = def.v; updateModel(rgb, true); } function onSpectrumPan (evt) { if (evt.isFinal) { changeSpectrum( evt.position.left, evt.position.top, true ); } else { onSpectrumChange(evt); } } const onSpectrumChange = throttle( evt => { changeSpectrum(evt.position.left, evt.position.top); }, 20 ); function onSpectrumClick (evt) { changeSpectrum( evt.pageX - window.pageXOffset, evt.pageY - window.pageYOffset, true ); } function onActivate (evt) { changeSpectrum( evt.pageX - window.pageXOffset, evt.pageY - window.pageYOffset ); } function updateErrorIcon (val) { // we MUST avoid vue triggering a render, // so manually changing this if (errorIconRef.value !== null) { errorIconRef.value.$el.style.opacity = val ? 1 : 0; } } function getHeader () { const child = []; props.noHeaderTabs !== true && child.push( vue.h(QTabs, { class: 'q-color-picker__header-tabs', modelValue: topView.value, dense: true, align: 'justify', ...getCache('topVTab', { 'onUpdate:modelValue': val => { topView.value = val; } }) }, () => [ vue.h(QTab, { label: 'HEX' + (hasAlpha.value === true ? 'A' : ''), name: 'hex', ripple: false }), vue.h(QTab, { label: 'RGB' + (hasAlpha.value === true ? 'A' : ''), name: 'rgb', ripple: false }) ]) ); child.push( vue.h('div', { class: 'q-color-picker__header-banner row flex-center no-wrap' }, [ vue.h('input', { class: 'fit', value: model.value[ topView.value ], ...(editable.value !== true ? { readonly: true } : {} ), ...getCache('topIn', { onInput: evt => { updateErrorIcon(onEditorChange(evt) === true); }, onChange: stop, onBlur: evt => { onEditorChange(evt, true) === true && proxy.$forceUpdate(); updateErrorIcon(false); } }) }), vue.h(QIcon, { ref: errorIconRef, class: 'q-color-picker__error-icon absolute no-pointer-events', name: $q.iconSet.type.negative }) ]) ); return vue.h('div', { class: 'q-color-picker__header relative-position overflow-hidden' }, [ vue.h('div', { class: 'q-color-picker__header-bg absolute-full' }), vue.h('div', { class: headerClass.value, style: currentBgColor.value }, child) ]) } function getContent () { return vue.h(QTabPanels, { modelValue: view.value, animated: true }, () => [ vue.h(QTabPanel, { class: 'q-color-picker__spectrum-tab overflow-hidden', name: 'spectrum' }, getSpectrumTab), vue.h(QTabPanel, { class: 'q-pa-md q-color-picker__tune-tab', name: 'tune' }, getTuneTab), vue.h(QTabPanel, { class: 'q-color-picker__palette-tab', name: 'palette' }, getPaletteTab) ]) } function getFooter () { return vue.h('div', { class: 'q-color-picker__footer relative-position overflow-hidden' }, [ vue.h(QTabs, { class: 'absolute-full', modelValue: view.value, dense: true, align: 'justify', ...getCache('ftIn', { 'onUpdate:modelValue': val => { view.value = val; } }) }, () => [ vue.h(QTab, { icon: $q.iconSet.colorPicker.spectrum, name: 'spectrum', ripple: false }), vue.h(QTab, { icon: $q.iconSet.colorPicker.tune, name: 'tune', ripple: false }), vue.h(QTab, { icon: $q.iconSet.colorPicker.palette, name: 'palette', ripple: false }) ]) ]) } function getSpectrumTab () { const data = { ref: spectrumRef, class: 'q-color-picker__spectrum non-selectable relative-position cursor-pointer' + (editable.value !== true ? ' readonly' : ''), style: spectrumStyle.value, ...(editable.value === true ? { onClick: onSpectrumClick, onMousedown: onActivate } : {} ) }; const child = [ vue.h('div', { style: { paddingBottom: '100%' } }), vue.h('div', { class: 'q-color-picker__spectrum-white absolute-full' }), vue.h('div', { class: 'q-color-picker__spectrum-black absolute-full' }), vue.h('div', { class: 'absolute', style: spectrumPointerStyle.value }, [ model.value.hex !== void 0 ? vue.h('div', { class: 'q-color-picker__spectrum-circle' }) : null ]) ]; const sliders = [ vue.h(QSlider, { class: 'q-color-picker__hue non-selectable', modelValue: model.value.h, min: 0, max: 360, trackSize: '8px', innerTrackColor: 'transparent', selectionColor: 'transparent', readonly: editable.value !== true, thumbPath, 'onUpdate:modelValue': onHueChange, ...getCache('lazyhue', { onChange: val => onHueChange(val, true) }) }) ]; hasAlpha.value === true && sliders.push( vue.h(QSlider, { class: 'q-color-picker__alpha non-selectable', modelValue: model.value.a, min: 0, max: 100, trackSize: '8px', trackColor: 'white', innerTrackColor: 'transparent', selectionColor: 'transparent', trackImg: alphaTrackImg, readonly: editable.value !== true, hideSelection: true, thumbPath, ...getCache('alphaSlide', { 'onUpdate:modelValue': value => onNumericChange(value, 'a', 100), onChange: value => onNumericChange(value, 'a', 100, void 0, true) }) }) ); return [ hDir('div', data, child, 'spec', editable.value, () => spectrumDirective.value), vue.h('div', { class: 'q-color-picker__sliders' }, sliders) ] } function getTuneTab () { return [ vue.h('div', { class: 'row items-center no-wrap' }, [ vue.h('div', 'R'), vue.h(QSlider, { modelValue: model.value.r, min: 0, max: 255, color: 'red', dark: isDark.value, readonly: editable.value !== true, ...getCache('rSlide', { 'onUpdate:modelValue': value => onNumericChange(value, 'r', 255), onChange: value => onNumericChange(value, 'r', 255, void 0, true) }) }), vue.h('input', { value: model.value.r, maxlength: 3, readonly: editable.value !== true, onChange: stop, ...getCache('rIn', { onInput: evt => onNumericChange(evt.target.value, 'r', 255, evt), onBlur: evt => onNumericChange(evt.target.value, 'r', 255, evt, true) }) }) ]), vue.h('div', { class: 'row items-center no-wrap' }, [ vue.h('div', 'G'), vue.h(QSlider, { modelValue: model.value.g, min: 0, max: 255, color: 'green', dark: isDark.value, readonly: editable.value !== true, ...getCache('gSlide', { 'onUpdate:modelValue': value => onNumericChange(value, 'g', 255), onChange: value => onNumericChange(value, 'g', 255, void 0, true) }) }), vue.h('input', { value: model.value.g, maxlength: 3, readonly: editable.value !== true, onChange: stop, ...getCache('gIn', { onInput: evt => onNumericChange(evt.target.value, 'g', 255, evt), onBlur: evt => onNumericChange(evt.target.value, 'g', 255, evt, true) }) }) ]), vue.h('div', { class: 'row items-center no-wrap' }, [ vue.h('div', 'B'), vue.h(QSlider, { modelValue: model.value.b, min: 0, max: 255, color: 'blue', readonly: editable.value !== true, dark: isDark.value, ...getCache('bSlide', { 'onUpdate:modelValue': value => onNumericChange(value, 'b', 255), onChange: value => onNumericChange(value, 'b', 255, void 0, true) }) }), vue.h('input', { value: model.value.b, maxlength: 3, readonly: editable.value !== true, onChange: stop, ...getCache('bIn', { onInput: evt => onNumericChange(evt.target.value, 'b', 255, evt), onBlur: evt => onNumericChange(evt.target.value, 'b', 255, evt, true) }) }) ]), hasAlpha.value === true ? vue.h('div', { class: 'row items-center no-wrap' }, [ vue.h('div', 'A'), vue.h(QSlider, { modelValue: model.value.a, color: 'grey', readonly: editable.value !== true, dark: isDark.value, ...getCache('aSlide', { 'onUpdate:modelValue': value => onNumericChange(value, 'a', 100), onChange: value => onNumericChange(value, 'a', 100, void 0, true) }) }), vue.h('input', { value: model.value.a, maxlength: 3, readonly: editable.value !== true, onChange: stop, ...getCache('aIn', { onInput: evt => onNumericChange(evt.target.value, 'a', 100, evt), onBlur: evt => onNumericChange(evt.target.value, 'a', 100, evt, true) }) }) ]) : null ] } function getPaletteTab () { const fn = color => vue.h('div', { class: 'q-color-picker__cube col-auto', style: { backgroundColor: color }, ...( editable.value === true ? getCache('palette#' + color, { onClick: () => { onPalettePick(color); } }) : {} ) }); return [ vue.h('div', { class: 'row items-center q-color-picker__palette-rows' + (editable.value === true ? ' q-color-picker__palette-rows--editable' : '') }, computedPalette.value.map(fn)) ] } return () => { const child = [ getContent() ]; if (props.name !== void 0 && props.disable !== true) { injectFormInput(child, 'push'); } props.noHeader !== true && child.unshift( getHeader() ); props.noFooter !== true && child.push( getFooter() ); return vue.h('div', { class: classes.value, ...attributes.value }, child) } } }); // taken from https://github.com/jalaali/jalaali-js /* Jalaali years starting the 33-year rule. */ const breaks = [ -61, 9, 38, 199, 426, 686, 756, 818, 1111, 1181, 1210, 1635, 2060, 2097, 2192, 2262, 2324, 2394, 2456, 3178 ]; /* Converts a Gregorian date to Jalaali. */ function toJalaali (gy, gm, gd) { if (Object.prototype.toString.call(gy) === '[object Date]') { gd = gy.getDate(); gm = gy.getMonth() + 1; gy = gy.getFullYear(); } return d2j(g2d(gy, gm, gd)) } /* Converts a Jalaali date to Gregorian. */ function toGregorian (jy, jm, jd) { return d2g(j2d(jy, jm, jd)) } /* Is this a leap year or not? */ function isLeapJalaaliYear (jy) { return jalCalLeap(jy) === 0 } /* Number of days in a given month in a Jalaali year. */ function jalaaliMonthLength (jy, jm) { if (jm <= 6) return 31 if (jm <= 11) return 30 if (isLeapJalaaliYear(jy)) return 30 return 29 } /* This function determines if the Jalaali (Persian) year is leap (366-day long) or is the common year (365 days) @param jy Jalaali calendar year (-61 to 3177) @returns number of years since the last leap year (0 to 4) */ function jalCalLeap (jy) { const bl = breaks.length; let jp = breaks[ 0 ], jm, jump, leap, n, i; if (jy < jp || jy >= breaks[ bl - 1 ]) { throw new Error('Invalid Jalaali year ' + jy) } for (i = 1; i < bl; i += 1) { jm = breaks[ i ]; jump = jm - jp; if (jy < jm) { break } jp = jm; } n = jy - jp; if (jump - n < 6) { n = n - jump + div(jump + 4, 33) * 33; } leap = mod(mod(n + 1, 33) - 1, 4); if (leap === -1) { leap = 4; } return leap } /* This function determines if the Jalaali (Persian) year is leap (366-day long) or is the common year (365 days), and finds the day in March (Gregorian calendar) of the first day of the Jalaali year (jy). @param jy Jalaali calendar year (-61 to 3177) @param withoutLeap when don't need leap (true or false) default is false @return leap: number of years since the last leap year (0 to 4) gy: Gregorian year of the beginning of Jalaali year march: the March day of Farvardin the 1st (1st day of jy) @see: http://www.astro.uni.torun.pl/~kb/Papers/EMP/PersianC-EMP.htm @see: http://www.fourmilab.ch/documents/calendar/ */ function jalCal (jy, withoutLeap) { const bl = breaks.length, gy = jy + 621; let leapJ = -14, jp = breaks[ 0 ], jm, jump, leap, n, i; if (jy < jp || jy >= breaks[ bl - 1 ]) { throw new Error('Invalid Jalaali year ' + jy) } // Find the limiting years for the Jalaali year jy. for (i = 1; i < bl; i += 1) { jm = breaks[ i ]; jump = jm - jp; if (jy < jm) { break } leapJ = leapJ + div(jump, 33) * 8 + div(mod(jump, 33), 4); jp = jm; } n = jy - jp; // Find the number of leap years from AD 621 to the beginning // of the current Jalaali year in the Persian calendar. leapJ = leapJ + div(n, 33) * 8 + div(mod(n, 33) + 3, 4); if (mod(jump, 33) === 4 && jump - n === 4) { leapJ += 1; } // And the same in the Gregorian calendar (until the year gy). const leapG = div(gy, 4) - div((div(gy, 100) + 1) * 3, 4) - 150; // Determine the Gregorian date of Farvardin the 1st. const march = 20 + leapJ - leapG; // Find how many years have passed since the last leap year. if (!withoutLeap) { if (jump - n < 6) { n = n - jump + div(jump + 4, 33) * 33; } leap = mod(mod(n + 1, 33) - 1, 4); if (leap === -1) { leap = 4; } } return { leap, gy, march } } /* Converts a date of the Jalaali calendar to the Julian Day number. @param jy Jalaali year (1 to 3100) @param jm Jalaali month (1 to 12) @param jd Jalaali day (1 to 29/31) @return Julian Day number */ function j2d (jy, jm, jd) { const r = jalCal(jy, true); return g2d(r.gy, 3, r.march) + (jm - 1) * 31 - div(jm, 7) * (jm - 7) + jd - 1 } /* Converts the Julian Day number to a date in the Jalaali calendar. @param jdn Julian Day number @return jy: Jalaali year (1 to 3100) jm: Jalaali month (1 to 12) jd: Jalaali day (1 to 29/31) */ function d2j (jdn) { const gy = d2g(jdn).gy; // Calculate Gregorian year (gy). let jy = gy - 621, jd, jm, k; const r = jalCal(jy, false), jdn1f = g2d(gy, 3, r.march); // Find number of days that passed since 1 Farvardin. k = jdn - jdn1f; if (k >= 0) { if (k <= 185) { // The first 6 months. jm = 1 + div(k, 31); jd = mod(k, 31) + 1; return { jy, jm, jd } } else { // The remaining months. k -= 186; } } else { // Previous Jalaali year. jy -= 1; k += 179; if (r.leap === 1) { k += 1; } } jm = 7 + div(k, 30); jd = mod(k, 30) + 1; return { jy, jm, jd } } /* Calculates the Julian Day number from Gregorian or Julian calendar dates. This integer number corresponds to the noon of the date (i.e. 12 hours of Universal Time). The procedure was tested to be good since 1 March, -100100 (of both calendars) up to a few million years into the future. @param gy Calendar year (years BC numbered 0, -1, -2, ...) @param gm Calendar month (1 to 12) @param gd Calendar day of the month (1 to 28/29/30/31) @return Julian Day number */ function g2d (gy, gm, gd) { let d = div((gy + div(gm - 8, 6) + 100100) * 1461, 4) + div(153 * mod(gm + 9, 12) + 2, 5) + gd - 34840408; d = d - div(div(gy + 100100 + div(gm - 8, 6), 100) * 3, 4) + 752; return d } /* Calculates Gregorian and Julian calendar dates from the Julian Day number (jdn) for the period since jdn=-34839655 (i.e. the year -100100 of both calendars) to some millions years ahead of the present. @param jdn Julian Day number @return gy: Calendar year (years BC numbered 0, -1, -2, ...) gm: Calendar month (1 to 12) gd: Calendar day of the month M (1 to 28/29/30/31) */ function d2g (jdn) { let j = 4 * jdn + 139361631; j = j + div(div(4 * jdn + 183187720, 146097) * 3, 4) * 4 - 3908; const i = div(mod(j, 1461), 4) * 5 + 308, gd = div(mod(i, 153), 5) + 1, gm = mod(div(i, 153), 12) + 1, gy = div(j, 1461) - 100100 + div(8 - gm, 6); return { gy, gm, gd } } /* Utility helper functions. */ function div (a, b) { return ~~(a / b) } function mod (a, b) { return a - ~~(a / b) * b } const calendars = [ 'gregorian', 'persian' ]; const useDatetimeProps = { modelValue: { required: true }, mask: { type: String }, locale: Object, calendar: { type: String, validator: v => calendars.includes(v), default: 'gregorian' }, landscape: Boolean, color: String, textColor: String, square: Boolean, flat: Boolean, bordered: Boolean, readonly: Boolean, disable: Boolean }; const useDatetimeEmits = [ 'update:modelValue' ]; function getDayHash (date) { return date.year + '/' + pad(date.month) + '/' + pad(date.day) } function useDatetime (props, $q) { const editable = vue.computed(() => { return props.disable !== true && props.readonly !== true }); const tabindex = vue.computed(() => { return editable.value === true ? 0 : -1 }); const headerClass = vue.computed(() => { const cls = []; props.color !== void 0 && cls.push(`bg-${ props.color }`); props.textColor !== void 0 && cls.push(`text-${ props.textColor }`); return cls.join(' ') }); function getLocale () { return props.locale !== void 0 ? { ...$q.lang.date, ...props.locale } : $q.lang.date } function getCurrentDate (dateOnly) { const d = new Date(); const timeFill = dateOnly === true ? null : 0; if (props.calendar === 'persian') { const jDate = toJalaali(d); return { year: jDate.jy, month: jDate.jm, day: jDate.jd } } return { year: d.getFullYear(), month: d.getMonth() + 1, day: d.getDate(), hour: timeFill, minute: timeFill, second: timeFill, millisecond: timeFill } } return { editable, tabindex, headerClass, getLocale, getCurrentDate } } /* eslint no-fallthrough: 0 */ const MILLISECONDS_IN_DAY = 86400000, MILLISECONDS_IN_HOUR = 3600000, MILLISECONDS_IN_MINUTE = 60000, defaultMask = 'YYYY-MM-DDTHH:mm:ss.SSSZ', token = /\[((?:[^\]\\]|\\]|\\)*)\]|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]/g, reverseToken = /(\[[^\]]*\])|d{1,4}|M{1,4}|m{1,2}|w{1,2}|Qo|Do|D{1,4}|YY(?:YY)?|H{1,2}|h{1,2}|s{1,2}|S{1,3}|Z{1,2}|a{1,2}|[AQExX]|([.*+:?^,\s${}()|\\]+)/g, regexStore = {}; function getRegexData (mask, dateLocale) { const days = '(' + dateLocale.days.join('|') + ')', key = mask + days; if (regexStore[ key ] !== void 0) { return regexStore[ key ] } const daysShort = '(' + dateLocale.daysShort.join('|') + ')', months = '(' + dateLocale.months.join('|') + ')', monthsShort = '(' + dateLocale.monthsShort.join('|') + ')'; const map = {}; let index = 0; const regexText = mask.replace(reverseToken, match => { index++; switch (match) { case 'YY': map.YY = index; return '(-?\\d{1,2})' case 'YYYY': map.YYYY = index; return '(-?\\d{1,4})' case 'M': map.M = index; return '(\\d{1,2})' case 'MM': map.M = index; // bumping to M return '(\\d{2})' case 'MMM': map.MMM = index; return monthsShort case 'MMMM': map.MMMM = index; return months case 'D': map.D = index; return '(\\d{1,2})' case 'Do': map.D = index++; // bumping to D return '(\\d{1,2}(st|nd|rd|th))' case 'DD': map.D = index; // bumping to D return '(\\d{2})' case 'H': map.H = index; return '(\\d{1,2})' case 'HH': map.H = index; // bumping to H return '(\\d{2})' case 'h': map.h = index; return '(\\d{1,2})' case 'hh': map.h = index; // bumping to h return '(\\d{2})' case 'm': map.m = index; return '(\\d{1,2})' case 'mm': map.m = index; // bumping to m return '(\\d{2})' case 's': map.s = index; return '(\\d{1,2})' case 'ss': map.s = index; // bumping to s return '(\\d{2})' case 'S': map.S = index; return '(\\d{1})' case 'SS': map.S = index; // bump to S return '(\\d{2})' case 'SSS': map.S = index; // bump to S return '(\\d{3})' case 'A': map.A = index; return '(AM|PM)' case 'a': map.a = index; return '(am|pm)' case 'aa': map.aa = index; return '(a\\.m\\.|p\\.m\\.)' case 'ddd': return daysShort case 'dddd': return days case 'Q': case 'd': case 'E': return '(\\d{1})' case 'Qo': return '(1st|2nd|3rd|4th)' case 'DDD': case 'DDDD': return '(\\d{1,3})' case 'w': return '(\\d{1,2})' case 'ww': return '(\\d{2})' case 'Z': // to split: (?:(Z)()()|([+-])?(\\d{2}):?(\\d{2})) map.Z = index; return '(Z|[+-]\\d{2}:\\d{2})' case 'ZZ': map.ZZ = index; return '(Z|[+-]\\d{2}\\d{2})' case 'X': map.X = index; return '(-?\\d+)' case 'x': map.x = index; return '(-?\\d{4,})' default: index--; if (match[ 0 ] === '[') { match = match.substring(1, match.length - 1); } return match.replace(/[.*+?^${}()|[\]\\]/g, '\\$&') } }); const res = { map, regex: new RegExp('^' + regexText) }; regexStore[ key ] = res; return res } function getDateLocale (paramDateLocale, langProps) { return paramDateLocale !== void 0 ? paramDateLocale : ( langProps !== void 0 ? langProps.date : defaultLang.date ) } function formatTimezone (offset, delimeter = '') { const sign = offset > 0 ? '-' : '+', absOffset = Math.abs(offset), hours = Math.floor(absOffset / 60), minutes = absOffset % 60; return sign + pad(hours) + delimeter + pad(minutes) } function applyYearMonthDayChange (date, mod, sign) { let year = date.getFullYear(), month = date.getMonth(); const day = date.getDate(); if (mod.year !== void 0) { year += sign * mod.year; delete mod.year; } if (mod.month !== void 0) { month += sign * mod.month; delete mod.month; } date.setDate(1); date.setMonth(2); date.setFullYear(year); date.setMonth(month); date.setDate(Math.min(day, daysInMonth(date))); if (mod.date !== void 0) { date.setDate(date.getDate() + sign * mod.date); delete mod.date; } return date } function applyYearMonthDay (date, mod, middle) { const year = mod.year !== void 0 ? mod.year : date[ `get${ middle }FullYear` ](), month = mod.month !== void 0 ? mod.month - 1 : date[ `get${ middle }Month` ](), maxDay = (new Date(year, month + 1, 0)).getDate(), day = Math.min(maxDay, mod.date !== void 0 ? mod.date : date[ `get${ middle }Date` ]()); date[ `set${ middle }Date` ](1); date[ `set${ middle }Month` ](2); date[ `set${ middle }FullYear` ](year); date[ `set${ middle }Month` ](month); date[ `set${ middle }Date` ](day); delete mod.year; delete mod.month; delete mod.date; return date } function getChange (date, rawMod, sign) { const mod = normalizeMod(rawMod), d = new Date(date), t = mod.year !== void 0 || mod.month !== void 0 || mod.date !== void 0 ? applyYearMonthDayChange(d, mod, sign) // removes year/month/day : d; for (const key in mod) { const op = capitalize(key); t[ `set${ op }` ](t[ `get${ op }` ]() + sign * mod[ key ]); } return t } function normalizeMod (mod) { const acc = { ...mod }; if (mod.years !== void 0) { acc.year = mod.years; delete acc.years; } if (mod.months !== void 0) { acc.month = mod.months; delete acc.months; } if (mod.days !== void 0) { acc.date = mod.days; delete acc.days; } if (mod.day !== void 0) { acc.date = mod.day; delete acc.day; } if (mod.hour !== void 0) { acc.hours = mod.hour; delete acc.hour; } if (mod.minute !== void 0) { acc.minutes = mod.minute; delete acc.minute; } if (mod.second !== void 0) { acc.seconds = mod.second; delete acc.second; } if (mod.millisecond !== void 0) { acc.milliseconds = mod.millisecond; delete acc.millisecond; } return acc } function adjustDate (date, rawMod, utc) { const mod = normalizeMod(rawMod), middle = utc === true ? 'UTC' : '', d = new Date(date), t = mod.year !== void 0 || mod.month !== void 0 || mod.date !== void 0 ? applyYearMonthDay(d, mod, middle) // removes year/month/day : d; for (const key in mod) { const op = key.charAt(0).toUpperCase() + key.slice(1); t[ `set${ middle }${ op }` ](mod[ key ]); } return t } function extractDate (str, mask, dateLocale) { const d = __splitDate(str, mask, dateLocale); const date = new Date( d.year, d.month === null ? null : d.month - 1, d.day === null ? 1 : d.day, d.hour, d.minute, d.second, d.millisecond ); const tzOffset = date.getTimezoneOffset(); return d.timezoneOffset === null || d.timezoneOffset === tzOffset ? date : getChange(date, { minutes: d.timezoneOffset - tzOffset }, 1) } function __splitDate (str, mask, dateLocale, calendar, defaultModel) { const date = { year: null, month: null, day: null, hour: null, minute: null, second: null, millisecond: null, timezoneOffset: null, dateHash: null, timeHash: null }; defaultModel !== void 0 && Object.assign(date, defaultModel); if ( str === void 0 || str === null || str === '' || typeof str !== 'string' ) { return date } if (mask === void 0) { mask = defaultMask; } const langOpts = getDateLocale(dateLocale, Plugin$8.props), months = langOpts.months, monthsShort = langOpts.monthsShort; const { regex, map } = getRegexData(mask, langOpts); const match = str.match(regex); if (match === null) { return date } let tzString = ''; if (map.X !== void 0 || map.x !== void 0) { const stamp = parseInt(match[ map.X !== void 0 ? map.X : map.x ], 10); if (isNaN(stamp) === true || stamp < 0) { return date } const d = new Date(stamp * (map.X !== void 0 ? 1000 : 1)); date.year = d.getFullYear(); date.month = d.getMonth() + 1; date.day = d.getDate(); date.hour = d.getHours(); date.minute = d.getMinutes(); date.second = d.getSeconds(); date.millisecond = d.getMilliseconds(); } else { if (map.YYYY !== void 0) { date.year = parseInt(match[ map.YYYY ], 10); } else if (map.YY !== void 0) { const y = parseInt(match[ map.YY ], 10); date.year = y < 0 ? y : 2000 + y; } if (map.M !== void 0) { date.month = parseInt(match[ map.M ], 10); if (date.month < 1 || date.month > 12) { return date } } else if (map.MMM !== void 0) { date.month = monthsShort.indexOf(match[ map.MMM ]) + 1; } else if (map.MMMM !== void 0) { date.month = months.indexOf(match[ map.MMMM ]) + 1; } if (map.D !== void 0) { date.day = parseInt(match[ map.D ], 10); if (date.year === null || date.month === null || date.day < 1) { return date } const maxDay = calendar !== 'persian' ? (new Date(date.year, date.month, 0)).getDate() : jalaaliMonthLength(date.year, date.month); if (date.day > maxDay) { return date } } if (map.H !== void 0) { date.hour = parseInt(match[ map.H ], 10) % 24; } else if (map.h !== void 0) { date.hour = parseInt(match[ map.h ], 10) % 12; if ( (map.A && match[ map.A ] === 'PM') || (map.a && match[ map.a ] === 'pm') || (map.aa && match[ map.aa ] === 'p.m.') ) { date.hour += 12; } date.hour = date.hour % 24; } if (map.m !== void 0) { date.minute = parseInt(match[ map.m ], 10) % 60; } if (map.s !== void 0) { date.second = parseInt(match[ map.s ], 10) % 60; } if (map.S !== void 0) { date.millisecond = parseInt(match[ map.S ], 10) * 10 ** (3 - match[ map.S ].length); } if (map.Z !== void 0 || map.ZZ !== void 0) { tzString = (map.Z !== void 0 ? match[ map.Z ].replace(':', '') : match[ map.ZZ ]); date.timezoneOffset = (tzString[ 0 ] === '+' ? -1 : 1) * (60 * tzString.slice(1, 3) + 1 * tzString.slice(3, 5)); } } date.dateHash = pad(date.year, 6) + '/' + pad(date.month) + '/' + pad(date.day); date.timeHash = pad(date.hour) + ':' + pad(date.minute) + ':' + pad(date.second) + tzString; return date } function isValid (date) { return typeof date === 'number' ? true : isNaN(Date.parse(date)) === false } function buildDate (mod, utc) { return adjustDate(new Date(), mod, utc) } function getDayOfWeek (date) { const dow = new Date(date).getDay(); return dow === 0 ? 7 : dow } function getWeekOfYear (date) { // Remove time components of date const thursday = new Date(date.getFullYear(), date.getMonth(), date.getDate()); // Change date to Thursday same week thursday.setDate(thursday.getDate() - ((thursday.getDay() + 6) % 7) + 3); // Take January 4th as it is always in week 1 (see ISO 8601) const firstThursday = new Date(thursday.getFullYear(), 0, 4); // Change date to Thursday same week firstThursday.setDate(firstThursday.getDate() - ((firstThursday.getDay() + 6) % 7) + 3); // Check if daylight-saving-time-switch occurred and correct for it const ds = thursday.getTimezoneOffset() - firstThursday.getTimezoneOffset(); thursday.setHours(thursday.getHours() - ds); // Number of weeks between target Thursday and first Thursday const weekDiff = (thursday - firstThursday) / (MILLISECONDS_IN_DAY * 7); return 1 + Math.floor(weekDiff) } function getDayIdentifier (date) { return date.getFullYear() * 10000 + date.getMonth() * 100 + date.getDate() } function getDateIdentifier (date, onlyDate /* = false */) { const d = new Date(date); return onlyDate === true ? getDayIdentifier(d) : d.getTime() } function isBetweenDates (date, from, to, opts = {}) { const d1 = getDateIdentifier(from, opts.onlyDate), d2 = getDateIdentifier(to, opts.onlyDate), cur = getDateIdentifier(date, opts.onlyDate); return (cur > d1 || (opts.inclusiveFrom === true && cur === d1)) && (cur < d2 || (opts.inclusiveTo === true && cur === d2)) } function addToDate (date, mod) { return getChange(date, mod, 1) } function subtractFromDate (date, mod) { return getChange(date, mod, -1) } function startOfDate (date, unit, utc) { const t = new Date(date), prefix = `set${ utc === true ? 'UTC' : '' }`; switch (unit) { case 'year': case 'years': t[ `${ prefix }Month` ](0); case 'month': case 'months': t[ `${ prefix }Date` ](1); case 'day': case 'days': case 'date': t[ `${ prefix }Hours` ](0); case 'hour': case 'hours': t[ `${ prefix }Minutes` ](0); case 'minute': case 'minutes': t[ `${ prefix }Seconds` ](0); case 'second': case 'seconds': t[ `${ prefix }Milliseconds` ](0); } return t } function endOfDate (date, unit, utc) { const t = new Date(date), prefix = `set${ utc === true ? 'UTC' : '' }`; switch (unit) { case 'year': case 'years': t[ `${ prefix }Month` ](11); case 'month': case 'months': t[ `${ prefix }Date` ](daysInMonth(t)); case 'day': case 'days': case 'date': t[ `${ prefix }Hours` ](23); case 'hour': case 'hours': t[ `${ prefix }Minutes` ](59); case 'minute': case 'minutes': t[ `${ prefix }Seconds` ](59); case 'second': case 'seconds': t[ `${ prefix }Milliseconds` ](999); } return t } function getMaxDate (date /* , ...args */) { let t = new Date(date); Array.prototype.slice.call(arguments, 1).forEach(d => { t = Math.max(t, new Date(d)); }); return t } function getMinDate (date /*, ...args */) { let t = new Date(date); Array.prototype.slice.call(arguments, 1).forEach(d => { t = Math.min(t, new Date(d)); }); return t } function getDiff (t, sub, interval) { return ( (t.getTime() - t.getTimezoneOffset() * MILLISECONDS_IN_MINUTE) - (sub.getTime() - sub.getTimezoneOffset() * MILLISECONDS_IN_MINUTE) ) / interval } function getDateDiff (date, subtract, unit = 'days') { const t = new Date(date), sub = new Date(subtract); switch (unit) { case 'years': case 'year': return (t.getFullYear() - sub.getFullYear()) case 'months': case 'month': return (t.getFullYear() - sub.getFullYear()) * 12 + t.getMonth() - sub.getMonth() case 'days': case 'day': case 'date': return getDiff(startOfDate(t, 'day'), startOfDate(sub, 'day'), MILLISECONDS_IN_DAY) case 'hours': case 'hour': return getDiff(startOfDate(t, 'hour'), startOfDate(sub, 'hour'), MILLISECONDS_IN_HOUR) case 'minutes': case 'minute': return getDiff(startOfDate(t, 'minute'), startOfDate(sub, 'minute'), MILLISECONDS_IN_MINUTE) case 'seconds': case 'second': return getDiff(startOfDate(t, 'second'), startOfDate(sub, 'second'), 1000) } } function getDayOfYear (date) { return getDateDiff(date, startOfDate(date, 'year'), 'days') + 1 } function inferDateFormat (date) { return isDate(date) === true ? 'date' : (typeof date === 'number' ? 'number' : 'string') } function getDateBetween (date, min, max) { const t = new Date(date); if (min) { const low = new Date(min); if (t < low) { return low } } if (max) { const high = new Date(max); if (t > high) { return high } } return t } function isSameDate (date, date2, unit) { const t = new Date(date), d = new Date(date2); if (unit === void 0) { return t.getTime() === d.getTime() } switch (unit) { case 'second': case 'seconds': if (t.getSeconds() !== d.getSeconds()) { return false } case 'minute': // intentional fall-through case 'minutes': if (t.getMinutes() !== d.getMinutes()) { return false } case 'hour': // intentional fall-through case 'hours': if (t.getHours() !== d.getHours()) { return false } case 'day': // intentional fall-through case 'days': case 'date': if (t.getDate() !== d.getDate()) { return false } case 'month': // intentional fall-through case 'months': if (t.getMonth() !== d.getMonth()) { return false } case 'year': // intentional fall-through case 'years': if (t.getFullYear() !== d.getFullYear()) { return false } break default: throw new Error(`date isSameDate unknown unit ${ unit }`) } return true } function daysInMonth (date) { return (new Date(date.getFullYear(), date.getMonth() + 1, 0)).getDate() } function getOrdinal (n) { if (n >= 11 && n <= 13) { return `${ n }th` } switch (n % 10) { case 1: return `${ n }st` case 2: return `${ n }nd` case 3: return `${ n }rd` } return `${ n }th` } const formatter = { // Year: 00, 01, ..., 99 YY (date, dateLocale, forcedYear) { // workaround for < 1900 with new Date() const y = this.YYYY(date, dateLocale, forcedYear) % 100; return y >= 0 ? pad(y) : '-' + pad(Math.abs(y)) }, // Year: 1900, 1901, ..., 2099 YYYY (date, _dateLocale, forcedYear) { // workaround for < 1900 with new Date() return forcedYear !== void 0 && forcedYear !== null ? forcedYear : date.getFullYear() }, // Month: 1, 2, ..., 12 M (date) { return date.getMonth() + 1 }, // Month: 01, 02, ..., 12 MM (date) { return pad(date.getMonth() + 1) }, // Month Short Name: Jan, Feb, ... MMM (date, dateLocale) { return dateLocale.monthsShort[ date.getMonth() ] }, // Month Name: January, February, ... MMMM (date, dateLocale) { return dateLocale.months[ date.getMonth() ] }, // Quarter: 1, 2, 3, 4 Q (date) { return Math.ceil((date.getMonth() + 1) / 3) }, // Quarter: 1st, 2nd, 3rd, 4th Qo (date) { return getOrdinal(this.Q(date)) }, // Day of month: 1, 2, ..., 31 D (date) { return date.getDate() }, // Day of month: 1st, 2nd, ..., 31st Do (date) { return getOrdinal(date.getDate()) }, // Day of month: 01, 02, ..., 31 DD (date) { return pad(date.getDate()) }, // Day of year: 1, 2, ..., 366 DDD (date) { return getDayOfYear(date) }, // Day of year: 001, 002, ..., 366 DDDD (date) { return pad(getDayOfYear(date), 3) }, // Day of week: 0, 1, ..., 6 d (date) { return date.getDay() }, // Day of week: Su, Mo, ... dd (date, dateLocale) { return this.dddd(date, dateLocale).slice(0, 2) }, // Day of week: Sun, Mon, ... ddd (date, dateLocale) { return dateLocale.daysShort[ date.getDay() ] }, // Day of week: Sunday, Monday, ... dddd (date, dateLocale) { return dateLocale.days[ date.getDay() ] }, // Day of ISO week: 1, 2, ..., 7 E (date) { return date.getDay() || 7 }, // Week of Year: 1 2 ... 52 53 w (date) { return getWeekOfYear(date) }, // Week of Year: 01 02 ... 52 53 ww (date) { return pad(getWeekOfYear(date)) }, // Hour: 0, 1, ... 23 H (date) { return date.getHours() }, // Hour: 00, 01, ..., 23 HH (date) { return pad(date.getHours()) }, // Hour: 1, 2, ..., 12 h (date) { const hours = date.getHours(); return hours === 0 ? 12 : (hours > 12 ? hours % 12 : hours) }, // Hour: 01, 02, ..., 12 hh (date) { return pad(this.h(date)) }, // Minute: 0, 1, ..., 59 m (date) { return date.getMinutes() }, // Minute: 00, 01, ..., 59 mm (date) { return pad(date.getMinutes()) }, // Second: 0, 1, ..., 59 s (date) { return date.getSeconds() }, // Second: 00, 01, ..., 59 ss (date) { return pad(date.getSeconds()) }, // 1/10 of second: 0, 1, ..., 9 S (date) { return Math.floor(date.getMilliseconds() / 100) }, // 1/100 of second: 00, 01, ..., 99 SS (date) { return pad(Math.floor(date.getMilliseconds() / 10)) }, // Millisecond: 000, 001, ..., 999 SSS (date) { return pad(date.getMilliseconds(), 3) }, // Meridiem: AM, PM A (date) { return this.H(date) < 12 ? 'AM' : 'PM' }, // Meridiem: am, pm a (date) { return this.H(date) < 12 ? 'am' : 'pm' }, // Meridiem: a.m., p.m. aa (date) { return this.H(date) < 12 ? 'a.m.' : 'p.m.' }, // Timezone: -01:00, +00:00, ... +12:00 Z (date, _dateLocale, _forcedYear, forcedTimezoneOffset) { const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null ? date.getTimezoneOffset() : forcedTimezoneOffset; return formatTimezone(tzOffset, ':') }, // Timezone: -0100, +0000, ... +1200 ZZ (date, _dateLocale, _forcedYear, forcedTimezoneOffset) { const tzOffset = forcedTimezoneOffset === void 0 || forcedTimezoneOffset === null ? date.getTimezoneOffset() : forcedTimezoneOffset; return formatTimezone(tzOffset) }, // Seconds timestamp: 512969520 X (date) { return Math.floor(date.getTime() / 1000) }, // Milliseconds timestamp: 512969520900 x (date) { return date.getTime() } }; function formatDate (val, mask, dateLocale, __forcedYear, __forcedTimezoneOffset) { if ( (val !== 0 && !val) || val === Infinity || val === -Infinity ) { return } const date = new Date(val); if (isNaN(date)) { return } if (mask === void 0) { mask = defaultMask; } const locale = getDateLocale(dateLocale, Plugin$8.props); return mask.replace( token, (match, text) => ( match in formatter ? formatter[ match ](date, locale, __forcedYear, __forcedTimezoneOffset) : (text === void 0 ? match : text.split('\\]').join(']')) ) ) } function clone (date) { return isDate(date) === true ? new Date(date.getTime()) : date } var date = { isValid, extractDate, buildDate, getDayOfWeek, getWeekOfYear, isBetweenDates, addToDate, subtractFromDate, adjustDate, startOfDate, endOfDate, getMaxDate, getMinDate, getDateDiff, getDayOfYear, inferDateFormat, getDateBetween, isSameDate, daysInMonth, formatDate, clone }; const yearsInterval = 20; const views = [ 'Calendar', 'Years', 'Months' ]; const viewIsValid = v => views.includes(v); const yearMonthValidator = v => /^-?[\d]+\/[0-1]\d$/.test(v); const lineStr = ' \u2014 '; function getMonthHash (date) { return date.year + '/' + pad(date.month) } var QDate = createComponent({ name: 'QDate', props: { ...useDatetimeProps, ...useFormProps, ...useDarkProps, multiple: Boolean, range: Boolean, title: String, subtitle: String, mask: { // this mask is forced // when using persian calendar default: 'YYYY/MM/DD' }, defaultYearMonth: { type: String, validator: yearMonthValidator }, yearsInMonthView: Boolean, events: [ Array, Function ], eventColor: [ String, Function ], emitImmediately: Boolean, options: [ Array, Function ], navigationMinYearMonth: { type: String, validator: yearMonthValidator }, navigationMaxYearMonth: { type: String, validator: yearMonthValidator }, noUnset: Boolean, firstDayOfWeek: [ String, Number ], todayBtn: Boolean, minimal: Boolean, defaultView: { type: String, default: 'Calendar', validator: viewIsValid } }, emits: [ ...useDatetimeEmits, 'rangeStart', 'rangeEnd', 'navigation' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const { getCache } = useCache(); const { tabindex, headerClass, getLocale, getCurrentDate } = useDatetime(props, $q); let lastEmitValue; const formAttrs = useFormAttrs(props); const injectFormInput = useFormInject(formAttrs); const blurTargetRef = vue.ref(null); const innerMask = vue.ref(getMask()); const innerLocale = vue.ref(getLocale()); const mask = vue.computed(() => getMask()); const locale = vue.computed(() => getLocale()); const today = vue.computed(() => getCurrentDate()); // model of current calendar view: const viewModel = vue.ref(getViewModel(innerMask.value, innerLocale.value)); const view = vue.ref(props.defaultView); const direction = $q.lang.rtl === true ? 'right' : 'left'; const monthDirection = vue.ref(direction.value); const yearDirection = vue.ref(direction.value); const year = viewModel.value.year; const startYear = vue.ref(year - (year % yearsInterval) - (year < 0 ? yearsInterval : 0)); const editRange = vue.ref(null); const classes = vue.computed(() => { const type = props.landscape === true ? 'landscape' : 'portrait'; return `q-date q-date--${ type } q-date--${ type }-${ props.minimal === true ? 'minimal' : 'standard' }` + (isDark.value === true ? ' q-date--dark q-dark' : '') + (props.bordered === true ? ' q-date--bordered' : '') + (props.square === true ? ' q-date--square no-border-radius' : '') + (props.flat === true ? ' q-date--flat no-shadow' : '') + (props.disable === true ? ' disabled' : (props.readonly === true ? ' q-date--readonly' : '')) }); const computedColor = vue.computed(() => { return props.color || 'primary' }); const computedTextColor = vue.computed(() => { return props.textColor || 'white' }); const isImmediate = vue.computed(() => props.emitImmediately === true && props.multiple !== true && props.range !== true ); const normalizedModel = vue.computed(() => ( Array.isArray(props.modelValue) === true ? props.modelValue : (props.modelValue !== null && props.modelValue !== void 0 ? [ props.modelValue ] : []) )); const daysModel = vue.computed(() => normalizedModel.value .filter(date => typeof date === 'string') .map(date => decodeString(date, innerMask.value, innerLocale.value)) .filter(date => date.dateHash !== null && date.day !== null && date.month !== null && date.year !== null ) ); const rangeModel = vue.computed(() => { const fn = date => decodeString(date, innerMask.value, innerLocale.value); return normalizedModel.value .filter(date => isObject(date) === true && date.from !== void 0 && date.to !== void 0) .map(range => ({ from: fn(range.from), to: fn(range.to) })) .filter(range => range.from.dateHash !== null && range.to.dateHash !== null && range.from.dateHash < range.to.dateHash) }); const getNativeDateFn = vue.computed(() => ( props.calendar !== 'persian' ? model => new Date(model.year, model.month - 1, model.day) : model => { const gDate = toGregorian(model.year, model.month, model.day); return new Date(gDate.gy, gDate.gm - 1, gDate.gd) } )); const encodeObjectFn = vue.computed(() => ( props.calendar === 'persian' ? getDayHash : (date, mask, locale) => formatDate( new Date( date.year, date.month - 1, date.day, date.hour, date.minute, date.second, date.millisecond ), mask === void 0 ? innerMask.value : mask, locale === void 0 ? innerLocale.value : locale, date.year, date.timezoneOffset ) )); const daysInModel = vue.computed(() => daysModel.value.length + rangeModel.value.reduce( (acc, range) => acc + 1 + getDateDiff( getNativeDateFn.value(range.to), getNativeDateFn.value(range.from) ), 0 ) ); const headerTitle = vue.computed(() => { if (props.title !== void 0 && props.title !== null && props.title.length !== 0) { return props.title } if (editRange.value !== null) { const model = editRange.value.init; const date = getNativeDateFn.value(model); return innerLocale.value.daysShort[ date.getDay() ] + ', ' + innerLocale.value.monthsShort[ model.month - 1 ] + ' ' + model.day + lineStr + '?' } if (daysInModel.value === 0) { return lineStr } if (daysInModel.value > 1) { return `${ daysInModel.value } ${ innerLocale.value.pluralDay }` } const model = daysModel.value[ 0 ]; const date = getNativeDateFn.value(model); if (isNaN(date.valueOf()) === true) { return lineStr } if (innerLocale.value.headerTitle !== void 0) { return innerLocale.value.headerTitle(date, model) } return innerLocale.value.daysShort[ date.getDay() ] + ', ' + innerLocale.value.monthsShort[ model.month - 1 ] + ' ' + model.day }); const minSelectedModel = vue.computed(() => { const model = daysModel.value.concat(rangeModel.value.map(range => range.from)) .sort((a, b) => a.year - b.year || a.month - b.month); return model[ 0 ] }); const maxSelectedModel = vue.computed(() => { const model = daysModel.value.concat(rangeModel.value.map(range => range.to)) .sort((a, b) => b.year - a.year || b.month - a.month); return model[ 0 ] }); const headerSubtitle = vue.computed(() => { if (props.subtitle !== void 0 && props.subtitle !== null && props.subtitle.length !== 0) { return props.subtitle } if (daysInModel.value === 0) { return lineStr } if (daysInModel.value > 1) { const from = minSelectedModel.value; const to = maxSelectedModel.value; const month = innerLocale.value.monthsShort; return month[ from.month - 1 ] + ( from.year !== to.year ? ' ' + from.year + lineStr + month[ to.month - 1 ] + ' ' : ( from.month !== to.month ? lineStr + month[ to.month - 1 ] : '' ) ) + ' ' + to.year } return daysModel.value[ 0 ].year }); const dateArrow = vue.computed(() => { const val = [ $q.iconSet.datetime.arrowLeft, $q.iconSet.datetime.arrowRight ]; return $q.lang.rtl === true ? val.reverse() : val }); const computedFirstDayOfWeek = vue.computed(() => ( props.firstDayOfWeek !== void 0 ? Number(props.firstDayOfWeek) : innerLocale.value.firstDayOfWeek )); const daysOfWeek = vue.computed(() => { const days = innerLocale.value.daysShort, first = computedFirstDayOfWeek.value; return first > 0 ? days.slice(first, 7).concat(days.slice(0, first)) : days }); const daysInMonth = vue.computed(() => { const date = viewModel.value; return props.calendar !== 'persian' ? (new Date(date.year, date.month, 0)).getDate() : jalaaliMonthLength(date.year, date.month) }); const evtColor = vue.computed(() => ( typeof props.eventColor === 'function' ? props.eventColor : () => props.eventColor )); const minNav = vue.computed(() => { if (props.navigationMinYearMonth === void 0) { return null } const data = props.navigationMinYearMonth.split('/'); return { year: parseInt(data[ 0 ], 10), month: parseInt(data[ 1 ], 10) } }); const maxNav = vue.computed(() => { if (props.navigationMaxYearMonth === void 0) { return null } const data = props.navigationMaxYearMonth.split('/'); return { year: parseInt(data[ 0 ], 10), month: parseInt(data[ 1 ], 10) } }); const navBoundaries = vue.computed(() => { const data = { month: { prev: true, next: true }, year: { prev: true, next: true } }; if (minNav.value !== null && minNav.value.year >= viewModel.value.year) { data.year.prev = false; if (minNav.value.year === viewModel.value.year && minNav.value.month >= viewModel.value.month) { data.month.prev = false; } } if (maxNav.value !== null && maxNav.value.year <= viewModel.value.year) { data.year.next = false; if (maxNav.value.year === viewModel.value.year && maxNav.value.month <= viewModel.value.month) { data.month.next = false; } } return data }); const daysMap = vue.computed(() => { const map = {}; daysModel.value.forEach(entry => { const hash = getMonthHash(entry); if (map[ hash ] === void 0) { map[ hash ] = []; } map[ hash ].push(entry.day); }); return map }); const rangeMap = vue.computed(() => { const map = {}; rangeModel.value.forEach(entry => { const hashFrom = getMonthHash(entry.from); const hashTo = getMonthHash(entry.to); if (map[ hashFrom ] === void 0) { map[ hashFrom ] = []; } map[ hashFrom ].push({ from: entry.from.day, to: hashFrom === hashTo ? entry.to.day : void 0, range: entry }); if (hashFrom < hashTo) { let hash; const { year, month } = entry.from; const cur = month < 12 ? { year, month: month + 1 } : { year: year + 1, month: 1 }; while ((hash = getMonthHash(cur)) <= hashTo) { if (map[ hash ] === void 0) { map[ hash ] = []; } map[ hash ].push({ from: void 0, to: hash === hashTo ? entry.to.day : void 0, range: entry }); cur.month++; if (cur.month > 12) { cur.year++; cur.month = 1; } } } }); return map }); const rangeView = vue.computed(() => { if (editRange.value === null) { return } const { init, initHash, final, finalHash } = editRange.value; const [ from, to ] = initHash <= finalHash ? [ init, final ] : [ final, init ]; const fromHash = getMonthHash(from); const toHash = getMonthHash(to); if (fromHash !== viewMonthHash.value && toHash !== viewMonthHash.value) { return } const view = {}; if (fromHash === viewMonthHash.value) { view.from = from.day; view.includeFrom = true; } else { view.from = 1; } if (toHash === viewMonthHash.value) { view.to = to.day; view.includeTo = true; } else { view.to = daysInMonth.value; } return view }); const viewMonthHash = vue.computed(() => getMonthHash(viewModel.value)); const selectionDaysMap = vue.computed(() => { const map = {}; if (props.options === void 0) { for (let i = 1; i <= daysInMonth.value; i++) { map[ i ] = true; } return map } const fn = typeof props.options === 'function' ? props.options : date => props.options.includes(date); for (let i = 1; i <= daysInMonth.value; i++) { const dayHash = viewMonthHash.value + '/' + pad(i); map[ i ] = fn(dayHash); } return map }); const eventDaysMap = vue.computed(() => { const map = {}; if (props.events === void 0) { for (let i = 1; i <= daysInMonth.value; i++) { map[ i ] = false; } } else { const fn = typeof props.events === 'function' ? props.events : date => props.events.includes(date); for (let i = 1; i <= daysInMonth.value; i++) { const dayHash = viewMonthHash.value + '/' + pad(i); map[ i ] = fn(dayHash) === true && evtColor.value(dayHash); } } return map }); const viewDays = vue.computed(() => { let date, endDay; const { year, month } = viewModel.value; if (props.calendar !== 'persian') { date = new Date(year, month - 1, 1); endDay = (new Date(year, month - 1, 0)).getDate(); } else { const gDate = toGregorian(year, month, 1); date = new Date(gDate.gy, gDate.gm - 1, gDate.gd); let prevJM = month - 1; let prevJY = year; if (prevJM === 0) { prevJM = 12; prevJY--; } endDay = jalaaliMonthLength(prevJY, prevJM); } return { days: date.getDay() - computedFirstDayOfWeek.value - 1, endDay } }); const days = vue.computed(() => { const res = []; const { days, endDay } = viewDays.value; const len = days < 0 ? days + 7 : days; if (len < 6) { for (let i = endDay - len; i <= endDay; i++) { res.push({ i, fill: true }); } } const index = res.length; for (let i = 1; i <= daysInMonth.value; i++) { const day = { i, event: eventDaysMap.value[ i ], classes: [] }; if (selectionDaysMap.value[ i ] === true) { day.in = true; day.flat = true; } res.push(day); } // if current view has days in model if (daysMap.value[ viewMonthHash.value ] !== void 0) { daysMap.value[ viewMonthHash.value ].forEach(day => { const i = index + day - 1; Object.assign(res[ i ], { selected: true, unelevated: true, flat: false, color: computedColor.value, textColor: computedTextColor.value }); }); } // if current view has ranges in model if (rangeMap.value[ viewMonthHash.value ] !== void 0) { rangeMap.value[ viewMonthHash.value ].forEach(entry => { if (entry.from !== void 0) { const from = index + entry.from - 1; const to = index + (entry.to || daysInMonth.value) - 1; for (let day = from; day <= to; day++) { Object.assign(res[ day ], { range: entry.range, unelevated: true, color: computedColor.value, textColor: computedTextColor.value }); } Object.assign(res[ from ], { rangeFrom: true, flat: false }); entry.to !== void 0 && Object.assign(res[ to ], { rangeTo: true, flat: false }); } else if (entry.to !== void 0) { const to = index + entry.to - 1; for (let day = index; day <= to; day++) { Object.assign(res[ day ], { range: entry.range, unelevated: true, color: computedColor.value, textColor: computedTextColor.value }); } Object.assign(res[ to ], { flat: false, rangeTo: true }); } else { const to = index + daysInMonth.value - 1; for (let day = index; day <= to; day++) { Object.assign(res[ day ], { range: entry.range, unelevated: true, color: computedColor.value, textColor: computedTextColor.value }); } } }); } if (rangeView.value !== void 0) { const from = index + rangeView.value.from - 1; const to = index + rangeView.value.to - 1; for (let day = from; day <= to; day++) { res[ day ].color = computedColor.value; res[ day ].editRange = true; } if (rangeView.value.includeFrom === true) { res[ from ].editRangeFrom = true; } if (rangeView.value.includeTo === true) { res[ to ].editRangeTo = true; } } if (viewModel.value.year === today.value.year && viewModel.value.month === today.value.month) { res[ index + today.value.day - 1 ].today = true; } const left = res.length % 7; if (left > 0) { const afterDays = 7 - left; for (let i = 1; i <= afterDays; i++) { res.push({ i, fill: true }); } } res.forEach(day => { let cls = 'q-date__calendar-item '; if (day.fill === true) { cls += 'q-date__calendar-item--fill'; } else { cls += `q-date__calendar-item--${ day.in === true ? 'in' : 'out' }`; if (day.range !== void 0) { cls += ` q-date__range${ day.rangeTo === true ? '-to' : (day.rangeFrom === true ? '-from' : '') }`; } if (day.editRange === true) { cls += ` q-date__edit-range${ day.editRangeFrom === true ? '-from' : '' }${ day.editRangeTo === true ? '-to' : '' }`; } if (day.range !== void 0 || day.editRange === true) { cls += ` text-${ day.color }`; } } day.classes = cls; }); return res }); const attributes = vue.computed(() => ( props.disable === true ? { 'aria-disabled': 'true' } : (props.readonly === true ? { 'aria-readonly': 'true' } : {}) )); vue.watch(() => props.modelValue, v => { if (lastEmitValue === v) { lastEmitValue = 0; } else { const model = getViewModel(innerMask.value, innerLocale.value); updateViewModel(model.year, model.month, model); } }); vue.watch(view, () => { if (blurTargetRef.value !== null && proxy.$el.contains(document.activeElement) === true) { blurTargetRef.value.focus(); } }); vue.watch(() => viewModel.value.year + '|' + viewModel.value.month, () => { emit('navigation', { year: viewModel.value.year, month: viewModel.value.month }); }); vue.watch(mask, val => { updateValue(val, innerLocale.value, 'mask'); innerMask.value = val; }); vue.watch(locale, val => { updateValue(innerMask.value, val, 'locale'); innerLocale.value = val; }); function setToday () { const date = today.value; const month = daysMap.value[ getMonthHash(date) ]; if (month === void 0 || month.includes(date.day) === false) { addToModel(date); } setCalendarTo(date.year, date.month); } function setView (viewMode) { if (viewIsValid(viewMode) === true) { view.value = viewMode; } } function offsetCalendar (type, descending) { if ([ 'month', 'year' ].includes(type)) { const fn = type === 'month' ? goToMonth : goToYear; fn(descending === true ? -1 : 1); } } function setCalendarTo (year, month) { view.value = 'Calendar'; updateViewModel(year, month); } function setEditingRange (from, to) { if (props.range === false || !from) { editRange.value = null; return } const init = Object.assign({ ...viewModel.value }, from); const final = to !== void 0 ? Object.assign({ ...viewModel.value }, to) : init; editRange.value = { init, initHash: getDayHash(init), final, finalHash: getDayHash(final) }; setCalendarTo(init.year, init.month); } function getMask () { return props.calendar === 'persian' ? 'YYYY/MM/DD' : props.mask } function decodeString (date, mask, locale) { return __splitDate( date, mask, locale, props.calendar, { hour: 0, minute: 0, second: 0, millisecond: 0 } ) } function getViewModel (mask, locale) { const model = Array.isArray(props.modelValue) === true ? props.modelValue : (props.modelValue ? [ props.modelValue ] : []); if (model.length === 0) { return getDefaultViewModel() } const target = model[ model.length - 1 ]; const decoded = decodeString( target.from !== void 0 ? target.from : target, mask, locale ); return decoded.dateHash === null ? getDefaultViewModel() : decoded } function getDefaultViewModel () { let year, month; if (props.defaultYearMonth !== void 0) { const d = props.defaultYearMonth.split('/'); year = parseInt(d[ 0 ], 10); month = parseInt(d[ 1 ], 10); } else { // may come from data() where computed // props are not yet available const d = today.value !== void 0 ? today.value : getCurrentDate(); year = d.year; month = d.month; } return { year, month, day: 1, hour: 0, minute: 0, second: 0, millisecond: 0, dateHash: year + '/' + pad(month) + '/01' } } function goToMonth (offset) { let year = viewModel.value.year; let month = Number(viewModel.value.month) + offset; if (month === 13) { month = 1; year++; } else if (month === 0) { month = 12; year--; } updateViewModel(year, month); isImmediate.value === true && emitImmediately('month'); } function goToYear (offset) { const year = Number(viewModel.value.year) + offset; updateViewModel(year, viewModel.value.month); isImmediate.value === true && emitImmediately('year'); } function setYear (year) { updateViewModel(year, viewModel.value.month); view.value = props.defaultView === 'Years' ? 'Months' : 'Calendar'; isImmediate.value === true && emitImmediately('year'); } function setMonth (month) { updateViewModel(viewModel.value.year, month); view.value = 'Calendar'; isImmediate.value === true && emitImmediately('month'); } function toggleDate (date, monthHash) { const month = daysMap.value[ monthHash ]; const fn = month !== void 0 && month.includes(date.day) === true ? removeFromModel : addToModel; fn(date); } function getShortDate (date) { return { year: date.year, month: date.month, day: date.day } } function updateViewModel (year, month, time) { if (minNav.value !== null && year <= minNav.value.year) { year = minNav.value.year; if (month < minNav.value.month) { month = minNav.value.month; } } if (maxNav.value !== null && year >= maxNav.value.year) { year = maxNav.value.year; if (month > maxNav.value.month) { month = maxNav.value.month; } } if (time !== void 0) { const { hour, minute, second, millisecond, timezoneOffset, timeHash } = time; Object.assign(viewModel.value, { hour, minute, second, millisecond, timezoneOffset, timeHash }); } const newHash = year + '/' + pad(month) + '/01'; if (newHash !== viewModel.value.dateHash) { monthDirection.value = (viewModel.value.dateHash < newHash) === ($q.lang.rtl !== true) ? 'left' : 'right'; if (year !== viewModel.value.year) { yearDirection.value = monthDirection.value; } vue.nextTick(() => { startYear.value = year - year % yearsInterval - (year < 0 ? yearsInterval : 0); Object.assign(viewModel.value, { year, month, day: 1, dateHash: newHash }); }); } } function emitValue (val, action, date) { const value = val !== null && val.length === 1 && props.multiple === false ? val[ 0 ] : val; lastEmitValue = value; const { reason, details } = getEmitParams(action, date); emit('update:modelValue', value, reason, details); } function emitImmediately (reason) { const date = daysModel.value[ 0 ] !== void 0 && daysModel.value[ 0 ].dateHash !== null ? { ...daysModel.value[ 0 ] } : { ...viewModel.value }; // inherit day, hours, minutes, milliseconds... // nextTick required because of animation delay in viewModel vue.nextTick(() => { date.year = viewModel.value.year; date.month = viewModel.value.month; const maxDay = props.calendar !== 'persian' ? (new Date(date.year, date.month, 0)).getDate() : jalaaliMonthLength(date.year, date.month); date.day = Math.min(Math.max(1, date.day), maxDay); const value = encodeEntry(date); lastEmitValue = value; const { details } = getEmitParams('', date); emit('update:modelValue', value, reason, details); }); } function getEmitParams (action, date) { return date.from !== void 0 ? { reason: `${ action }-range`, details: { ...getShortDate(date.target), from: getShortDate(date.from), to: getShortDate(date.to) } } : { reason: `${ action }-day`, details: getShortDate(date) } } function encodeEntry (date, mask, locale) { return date.from !== void 0 ? { from: encodeObjectFn.value(date.from, mask, locale), to: encodeObjectFn.value(date.to, mask, locale) } : encodeObjectFn.value(date, mask, locale) } function addToModel (date) { let value; if (props.multiple === true) { if (date.from !== void 0) { // we also need to filter out intersections const fromHash = getDayHash(date.from); const toHash = getDayHash(date.to); const days = daysModel.value .filter(day => day.dateHash < fromHash || day.dateHash > toHash); const ranges = rangeModel.value .filter(({ from, to }) => to.dateHash < fromHash || from.dateHash > toHash); value = days.concat(ranges).concat(date).map(entry => encodeEntry(entry)); } else { const model = normalizedModel.value.slice(); model.push(encodeEntry(date)); value = model; } } else { value = encodeEntry(date); } emitValue(value, 'add', date); } function removeFromModel (date) { if (props.noUnset === true) { return } let model = null; if (props.multiple === true && Array.isArray(props.modelValue) === true) { const val = encodeEntry(date); if (date.from !== void 0) { model = props.modelValue.filter( date => ( date.from !== void 0 ? (date.from !== val.from && date.to !== val.to) : true ) ); } else { model = props.modelValue.filter(date => date !== val); } if (model.length === 0) { model = null; } } emitValue(model, 'remove', date); } function updateValue (mask, locale, reason) { const model = daysModel.value .concat(rangeModel.value) .map(entry => encodeEntry(entry, mask, locale)) .filter(entry => { return entry.from !== void 0 ? entry.from.dateHash !== null && entry.to.dateHash !== null : entry.dateHash !== null }); emit('update:modelValue', (props.multiple === true ? model : model[ 0 ]) || null, reason); } function getHeader () { if (props.minimal === true) { return } return vue.h('div', { class: 'q-date__header ' + headerClass.value }, [ vue.h('div', { class: 'relative-position' }, [ vue.h(vue.Transition, { name: 'q-transition--fade' }, () => vue.h('div', { key: 'h-yr-' + headerSubtitle.value, class: 'q-date__header-subtitle q-date__header-link ' + (view.value === 'Years' ? 'q-date__header-link--active' : 'cursor-pointer'), tabindex: tabindex.value, ...getCache('vY', { onClick () { view.value = 'Years'; }, onKeyup (e) { e.keyCode === 13 && (view.value = 'Years'); } }) }, [ headerSubtitle.value ])) ]), vue.h('div', { class: 'q-date__header-title relative-position flex no-wrap' }, [ vue.h('div', { class: 'relative-position col' }, [ vue.h(vue.Transition, { name: 'q-transition--fade' }, () => vue.h('div', { key: 'h-sub' + headerTitle.value, class: 'q-date__header-title-label q-date__header-link ' + (view.value === 'Calendar' ? 'q-date__header-link--active' : 'cursor-pointer'), tabindex: tabindex.value, ...getCache('vC', { onClick () { view.value = 'Calendar'; }, onKeyup (e) { e.keyCode === 13 && (view.value = 'Calendar'); } }) }, [ headerTitle.value ])) ]), props.todayBtn === true ? vue.h(QBtn, { class: 'q-date__header-today self-start', icon: $q.iconSet.datetime.today, flat: true, size: 'sm', round: true, tabindex: tabindex.value, onClick: setToday }) : null ]) ]) } function getNavigation ({ label, type, key, dir, goTo, boundaries, cls }) { return [ vue.h('div', { class: 'row items-center q-date__arrow' }, [ vue.h(QBtn, { round: true, dense: true, size: 'sm', flat: true, icon: dateArrow.value[ 0 ], tabindex: tabindex.value, disable: boundaries.prev === false, ...getCache('go-#' + type, { onClick () { goTo(-1); } }) }) ]), vue.h('div', { class: 'relative-position overflow-hidden flex flex-center' + cls }, [ vue.h(vue.Transition, { name: 'q-transition--jump-' + dir }, () => vue.h('div', { key }, [ vue.h(QBtn, { flat: true, dense: true, noCaps: true, label, tabindex: tabindex.value, ...getCache('view#' + type, { onClick: () => { view.value = type; } }) }) ])) ]), vue.h('div', { class: 'row items-center q-date__arrow' }, [ vue.h(QBtn, { round: true, dense: true, size: 'sm', flat: true, icon: dateArrow.value[ 1 ], tabindex: tabindex.value, disable: boundaries.next === false, ...getCache('go+#' + type, { onClick () { goTo(1); } }) }) ]) ] } const renderViews = { Calendar: () => ([ vue.h('div', { key: 'calendar-view', class: 'q-date__view q-date__calendar' }, [ vue.h('div', { class: 'q-date__navigation row items-center no-wrap' }, getNavigation({ label: innerLocale.value.months[ viewModel.value.month - 1 ], type: 'Months', key: viewModel.value.month, dir: monthDirection.value, goTo: goToMonth, boundaries: navBoundaries.value.month, cls: ' col' }).concat(getNavigation({ label: viewModel.value.year, type: 'Years', key: viewModel.value.year, dir: yearDirection.value, goTo: goToYear, boundaries: navBoundaries.value.year, cls: '' }))), vue.h('div', { class: 'q-date__calendar-weekdays row items-center no-wrap' }, daysOfWeek.value.map(day => vue.h('div', { class: 'q-date__calendar-item' }, [ vue.h('div', day) ]))), vue.h('div', { class: 'q-date__calendar-days-container relative-position overflow-hidden' }, [ vue.h(vue.Transition, { name: 'q-transition--slide-' + monthDirection.value }, () => vue.h('div', { key: viewMonthHash.value, class: 'q-date__calendar-days fit' }, days.value.map(day => vue.h('div', { class: day.classes }, [ day.in === true ? vue.h( QBtn, { class: day.today === true ? 'q-date__today' : '', dense: true, flat: day.flat, unelevated: day.unelevated, color: day.color, textColor: day.textColor, label: day.i, tabindex: tabindex.value, ...getCache('day#' + day.i, { onClick: () => { onDayClick(day.i); }, onMouseover: () => { onDayMouseover(day.i); } }) }, day.event !== false ? () => vue.h('div', { class: 'q-date__event bg-' + day.event }) : null ) : vue.h('div', '' + day.i) ])))) ]) ]) ]), Months () { const currentYear = viewModel.value.year === today.value.year; const isDisabled = month => { return ( (minNav.value !== null && viewModel.value.year === minNav.value.year && minNav.value.month > month) || (maxNav.value !== null && viewModel.value.year === maxNav.value.year && maxNav.value.month < month) ) }; const content = innerLocale.value.monthsShort.map((month, i) => { const active = viewModel.value.month === i + 1; return vue.h('div', { class: 'q-date__months-item flex flex-center' }, [ vue.h(QBtn, { class: currentYear === true && today.value.month === i + 1 ? 'q-date__today' : null, flat: active !== true, label: month, unelevated: active, color: active === true ? computedColor.value : null, textColor: active === true ? computedTextColor.value : null, tabindex: tabindex.value, disable: isDisabled(i + 1), ...getCache('month#' + i, { onClick: () => { setMonth(i + 1); } }) }) ]) }); props.yearsInMonthView === true && content.unshift( vue.h('div', { class: 'row no-wrap full-width' }, [ getNavigation({ label: viewModel.value.year, type: 'Years', key: viewModel.value.year, dir: yearDirection.value, goTo: goToYear, boundaries: navBoundaries.value.year, cls: ' col' }) ]) ); return vue.h('div', { key: 'months-view', class: 'q-date__view q-date__months flex flex-center' }, content) }, Years () { const start = startYear.value, stop = start + yearsInterval, years = []; const isDisabled = year => { return ( (minNav.value !== null && minNav.value.year > year) || (maxNav.value !== null && maxNav.value.year < year) ) }; for (let i = start; i <= stop; i++) { const active = viewModel.value.year === i; years.push( vue.h('div', { class: 'q-date__years-item flex flex-center' }, [ vue.h(QBtn, { key: 'yr' + i, class: today.value.year === i ? 'q-date__today' : null, flat: !active, label: i, dense: true, unelevated: active, color: active === true ? computedColor.value : null, textColor: active === true ? computedTextColor.value : null, tabindex: tabindex.value, disable: isDisabled(i), ...getCache('yr#' + i, { onClick: () => { setYear(i); } }) }) ]) ); } return vue.h('div', { class: 'q-date__view q-date__years flex flex-center' }, [ vue.h('div', { class: 'col-auto' }, [ vue.h(QBtn, { round: true, dense: true, flat: true, icon: dateArrow.value[ 0 ], tabindex: tabindex.value, disable: isDisabled(start), ...getCache('y-', { onClick: () => { startYear.value -= yearsInterval; } }) }) ]), vue.h('div', { class: 'q-date__years-content col self-stretch row items-center' }, years), vue.h('div', { class: 'col-auto' }, [ vue.h(QBtn, { round: true, dense: true, flat: true, icon: dateArrow.value[ 1 ], tabindex: tabindex.value, disable: isDisabled(stop), ...getCache('y+', { onClick: () => { startYear.value += yearsInterval; } }) }) ]) ]) } }; function onDayClick (dayIndex) { const day = { ...viewModel.value, day: dayIndex }; if (props.range === false) { toggleDate(day, viewMonthHash.value); return } if (editRange.value === null) { const dayProps = days.value.find(day => day.fill !== true && day.i === dayIndex); if (props.noUnset !== true && dayProps.range !== void 0) { removeFromModel({ target: day, from: dayProps.range.from, to: dayProps.range.to }); return } if (dayProps.selected === true) { removeFromModel(day); return } const initHash = getDayHash(day); editRange.value = { init: day, initHash, final: day, finalHash: initHash }; emit('rangeStart', getShortDate(day)); } else { const initHash = editRange.value.initHash, finalHash = getDayHash(day), payload = initHash <= finalHash ? { from: editRange.value.init, to: day } : { from: day, to: editRange.value.init }; editRange.value = null; addToModel(initHash === finalHash ? day : { target: day, ...payload }); emit('rangeEnd', { from: getShortDate(payload.from), to: getShortDate(payload.to) }); } } function onDayMouseover (dayIndex) { if (editRange.value !== null) { const final = { ...viewModel.value, day: dayIndex }; Object.assign(editRange.value, { final, finalHash: getDayHash(final) }); } } // expose public methods Object.assign(proxy, { setToday, setView, offsetCalendar, setCalendarTo, setEditingRange }); return () => { const content = [ vue.h('div', { class: 'q-date__content col relative-position' }, [ vue.h(vue.Transition, { name: 'q-transition--fade' }, renderViews[ view.value ]) ]) ]; const def = hSlot(slots.default); def !== void 0 && content.push( vue.h('div', { class: 'q-date__actions' }, def) ); if (props.name !== void 0 && props.disable !== true) { injectFormInput(content, 'push'); } return vue.h('div', { class: classes.value, ...attributes.value }, [ getHeader(), vue.h('div', { ref: blurTargetRef, class: 'q-date__main col column', tabindex: -1 }, content) ]) } } }); function useHistory (showing, hide, hideOnRouteChange) { let historyEntry; function removeFromHistory () { if (historyEntry !== void 0) { History.remove(historyEntry); historyEntry = void 0; } } vue.onBeforeUnmount(() => { showing.value === true && removeFromHistory(); }); return { removeFromHistory, addToHistory () { historyEntry = { condition: () => hideOnRouteChange.value === true, handler: hide }; History.add(historyEntry); } } } let registered = 0, scrollPositionX, scrollPositionY, maxScrollTop, vpPendingUpdate = false, bodyLeft, bodyTop, href, closeTimer = null; function onWheel (e) { if (shouldPreventScroll(e)) { stopAndPrevent(e); } } function shouldPreventScroll (e) { if (e.target === document.body || e.target.classList.contains('q-layout__backdrop')) { return true } const path = getEventPath(e), shift = e.shiftKey && !e.deltaX, scrollY = !shift && Math.abs(e.deltaX) <= Math.abs(e.deltaY), delta = shift || scrollY ? e.deltaY : e.deltaX; for (let index = 0; index < path.length; index++) { const el = path[ index ]; if (hasScrollbar(el, scrollY)) { return scrollY ? ( delta < 0 && el.scrollTop === 0 ? true : delta > 0 && el.scrollTop + el.clientHeight === el.scrollHeight ) : ( delta < 0 && el.scrollLeft === 0 ? true : delta > 0 && el.scrollLeft + el.clientWidth === el.scrollWidth ) } } return true } function onAppleScroll (e) { if (e.target === document) { // required, otherwise iOS blocks further scrolling // until the mobile scrollbar dissappears document.scrollingElement.scrollTop = document.scrollingElement.scrollTop; // eslint-disable-line } } function onAppleResize (evt) { if (vpPendingUpdate === true) { return } vpPendingUpdate = true; requestAnimationFrame(() => { vpPendingUpdate = false; const { height } = evt.target, { clientHeight, scrollTop } = document.scrollingElement; if (maxScrollTop === void 0 || height !== window.innerHeight) { maxScrollTop = clientHeight - height; document.scrollingElement.scrollTop = scrollTop; } if (scrollTop > maxScrollTop) { document.scrollingElement.scrollTop -= Math.ceil((scrollTop - maxScrollTop) / 8); } }); } function apply$1 (action) { const body = document.body, hasViewport = window.visualViewport !== void 0; if (action === 'add') { const { overflowY, overflowX } = window.getComputedStyle(body); scrollPositionX = getHorizontalScrollPosition(window); scrollPositionY = getVerticalScrollPosition(window); bodyLeft = body.style.left; bodyTop = body.style.top; href = window.location.href; body.style.left = `-${ scrollPositionX }px`; body.style.top = `-${ scrollPositionY }px`; if (overflowX !== 'hidden' && (overflowX === 'scroll' || body.scrollWidth > window.innerWidth)) { body.classList.add('q-body--force-scrollbar-x'); } if (overflowY !== 'hidden' && (overflowY === 'scroll' || body.scrollHeight > window.innerHeight)) { body.classList.add('q-body--force-scrollbar-y'); } body.classList.add('q-body--prevent-scroll'); document.qScrollPrevented = true; if (client.is.ios === true) { if (hasViewport === true) { window.scrollTo(0, 0); window.visualViewport.addEventListener('resize', onAppleResize, listenOpts.passiveCapture); window.visualViewport.addEventListener('scroll', onAppleResize, listenOpts.passiveCapture); window.scrollTo(0, 0); } else { window.addEventListener('scroll', onAppleScroll, listenOpts.passiveCapture); } } } if (client.is.desktop === true && client.is.mac === true) { // ref. https://developers.google.com/web/updates/2017/01/scrolling-intervention window[ `${ action }EventListener` ]('wheel', onWheel, listenOpts.notPassive); } if (action === 'remove') { if (client.is.ios === true) { if (hasViewport === true) { window.visualViewport.removeEventListener('resize', onAppleResize, listenOpts.passiveCapture); window.visualViewport.removeEventListener('scroll', onAppleResize, listenOpts.passiveCapture); } else { window.removeEventListener('scroll', onAppleScroll, listenOpts.passiveCapture); } } body.classList.remove('q-body--prevent-scroll'); body.classList.remove('q-body--force-scrollbar-x'); body.classList.remove('q-body--force-scrollbar-y'); document.qScrollPrevented = false; body.style.left = bodyLeft; body.style.top = bodyTop; // scroll back only if route has not changed if (window.location.href === href) { window.scrollTo(scrollPositionX, scrollPositionY); } maxScrollTop = void 0; } } function preventScroll (state) { let action = 'add'; if (state === true) { registered++; if (closeTimer !== null) { clearTimeout(closeTimer); closeTimer = null; return } if (registered > 1) { return } } else { if (registered === 0) { return } registered--; if (registered > 0) { return } action = 'remove'; if (client.is.ios === true && client.is.nativeMobile === true) { closeTimer !== null && clearTimeout(closeTimer); closeTimer = setTimeout(() => { apply$1(action); closeTimer = null; }, 100); return } } apply$1(action); } function usePreventScroll () { let currentState; return { preventBodyScroll (state) { if ( state !== currentState && (currentState !== void 0 || state === true) ) { currentState = state; preventScroll(state); } } } } let maximizedModals = 0; const positionClass$1 = { standard: 'fixed-full flex-center', top: 'fixed-top justify-center', bottom: 'fixed-bottom justify-center', right: 'fixed-right items-center', left: 'fixed-left items-center' }; const defaultTransitions = { standard: [ 'scale', 'scale' ], top: [ 'slide-down', 'slide-up' ], bottom: [ 'slide-up', 'slide-down' ], right: [ 'slide-left', 'slide-right' ], left: [ 'slide-right', 'slide-left' ] }; var QDialog = createComponent({ name: 'QDialog', inheritAttrs: false, props: { ...useModelToggleProps, ...useTransitionProps, transitionShow: String, // override useTransitionProps transitionHide: String, // override useTransitionProps persistent: Boolean, autoClose: Boolean, allowFocusOutside: Boolean, noEscDismiss: Boolean, noBackdropDismiss: Boolean, noRouteDismiss: Boolean, noRefocus: Boolean, noFocus: Boolean, noShake: Boolean, seamless: Boolean, maximized: Boolean, fullWidth: Boolean, fullHeight: Boolean, square: Boolean, position: { type: String, default: 'standard', validator: val => val === 'standard' || [ 'top', 'bottom', 'left', 'right' ].includes(val) } }, emits: [ ...useModelToggleEmits, 'shake', 'click', 'escapeKey' ], setup (props, { slots, emit, attrs }) { const vm = vue.getCurrentInstance(); const innerRef = vue.ref(null); const showing = vue.ref(false); const animating = vue.ref(false); let shakeTimeout = null, refocusTarget = null, isMaximized, avoidAutoClose; const hideOnRouteChange = vue.computed(() => props.persistent !== true && props.noRouteDismiss !== true && props.seamless !== true ); const { preventBodyScroll } = usePreventScroll(); const { registerTimeout } = useTimeout(); const { registerTick, removeTick } = useTick(); const { transitionProps, transitionStyle } = useTransition( props, () => defaultTransitions[ props.position ][ 0 ], () => defaultTransitions[ props.position ][ 1 ] ); const { showPortal, hidePortal, portalIsAccessible, renderPortal } = usePortal( vm, innerRef, renderPortalContent, 'dialog' ); const { hide } = useModelToggle({ showing, hideOnRouteChange, handleShow, handleHide, processOnMount: true }); const { addToHistory, removeFromHistory } = useHistory(showing, hide, hideOnRouteChange); const classes = vue.computed(() => 'q-dialog__inner flex no-pointer-events' + ` q-dialog__inner--${ props.maximized === true ? 'maximized' : 'minimized' }` + ` q-dialog__inner--${ props.position } ${ positionClass$1[ props.position ] }` + (animating.value === true ? ' q-dialog__inner--animating' : '') + (props.fullWidth === true ? ' q-dialog__inner--fullwidth' : '') + (props.fullHeight === true ? ' q-dialog__inner--fullheight' : '') + (props.square === true ? ' q-dialog__inner--square' : '') ); const useBackdrop = vue.computed(() => showing.value === true && props.seamless !== true); const onEvents = vue.computed(() => ( props.autoClose === true ? { onClick: onAutoClose } : {} )); const rootClasses = vue.computed(() => [ 'q-dialog fullscreen no-pointer-events ' + `q-dialog--${ useBackdrop.value === true ? 'modal' : 'seamless' }`, attrs.class ]); vue.watch(() => props.maximized, state => { showing.value === true && updateMaximized(state); }); vue.watch(useBackdrop, val => { preventBodyScroll(val); if (val === true) { addFocusout(onFocusChange); addEscapeKey(onEscapeKey); } else { removeFocusout(onFocusChange); removeEscapeKey(onEscapeKey); } }); function handleShow (evt) { addToHistory(); refocusTarget = props.noRefocus === false && document.activeElement !== null ? document.activeElement : null; updateMaximized(props.maximized); showPortal(); animating.value = true; if (props.noFocus !== true) { document.activeElement !== null && document.activeElement.blur(); registerTick(focus); } else { removeTick(); } // should removeTimeout() if this gets removed registerTimeout(() => { if (vm.proxy.$q.platform.is.ios === true) { if (props.seamless !== true && document.activeElement) { const { top, bottom } = document.activeElement.getBoundingClientRect(), { innerHeight } = window, height = window.visualViewport !== void 0 ? window.visualViewport.height : innerHeight; if (top > 0 && bottom > height / 2) { document.scrollingElement.scrollTop = Math.min( document.scrollingElement.scrollHeight - height, bottom >= innerHeight ? Infinity : Math.ceil(document.scrollingElement.scrollTop + bottom - height / 2) ); } document.activeElement.scrollIntoView(); } // required in order to avoid the "double-tap needed" issue avoidAutoClose = true; innerRef.value.click(); avoidAutoClose = false; } showPortal(true); // done showing portal animating.value = false; emit('show', evt); }, props.transitionDuration); } function handleHide (evt) { removeTick(); removeFromHistory(); cleanup(true); animating.value = true; hidePortal(); if (refocusTarget !== null) { ((evt && evt.type.indexOf('key') === 0 ? refocusTarget.closest('[tabindex]:not([tabindex^="-"])') : void 0 ) || refocusTarget).focus(); refocusTarget = null; } // should removeTimeout() if this gets removed registerTimeout(() => { hidePortal(true); // done hiding, now destroy animating.value = false; emit('hide', evt); }, props.transitionDuration); } function focus (selector) { addFocusFn(() => { let node = innerRef.value; if (node === null || node.contains(document.activeElement) === true) { return } node = (selector !== '' ? node.querySelector(selector) : null) || node.querySelector('[autofocus][tabindex], [data-autofocus][tabindex]') || node.querySelector('[autofocus] [tabindex], [data-autofocus] [tabindex]') || node.querySelector('[autofocus], [data-autofocus]') || node; node.focus({ preventScroll: true }); }); } function shake (focusTarget) { if (focusTarget && typeof focusTarget.focus === 'function') { focusTarget.focus({ preventScroll: true }); } else { focus(); } emit('shake'); const node = innerRef.value; if (node !== null) { node.classList.remove('q-animate--scale'); node.classList.add('q-animate--scale'); shakeTimeout !== null && clearTimeout(shakeTimeout); shakeTimeout = setTimeout(() => { shakeTimeout = null; if (innerRef.value !== null) { node.classList.remove('q-animate--scale'); // some platforms (like desktop Chrome) // require calling focus() again focus(); } }, 170); } } function onEscapeKey () { if (props.seamless !== true) { if (props.persistent === true || props.noEscDismiss === true) { props.maximized !== true && props.noShake !== true && shake(); } else { emit('escapeKey'); hide(); } } } function cleanup (hiding) { if (shakeTimeout !== null) { clearTimeout(shakeTimeout); shakeTimeout = null; } if (hiding === true || showing.value === true) { updateMaximized(false); if (props.seamless !== true) { preventBodyScroll(false); removeFocusout(onFocusChange); removeEscapeKey(onEscapeKey); } } if (hiding !== true) { refocusTarget = null; } } function updateMaximized (active) { if (active === true) { if (isMaximized !== true) { maximizedModals < 1 && document.body.classList.add('q-body--dialog'); maximizedModals++; isMaximized = true; } } else if (isMaximized === true) { if (maximizedModals < 2) { document.body.classList.remove('q-body--dialog'); } maximizedModals--; isMaximized = false; } } function onAutoClose (e) { if (avoidAutoClose !== true) { hide(e); emit('click', e); } } function onBackdropClick (e) { if (props.persistent !== true && props.noBackdropDismiss !== true) { hide(e); } else if (props.noShake !== true) { shake(); } } function onFocusChange (evt) { // the focus is not in a vue child component if ( props.allowFocusOutside !== true && portalIsAccessible.value === true && childHasFocus(innerRef.value, evt.target) !== true ) { focus('[tabindex]:not([tabindex="-1"])'); } } Object.assign(vm.proxy, { // expose public methods focus, shake, // private but needed by QSelect __updateRefocusTarget (target) { refocusTarget = target || null; } }); vue.onBeforeUnmount(cleanup); function renderPortalContent () { return vue.h('div', { role: 'dialog', 'aria-modal': useBackdrop.value === true ? 'true' : 'false', ...attrs, class: rootClasses.value }, [ vue.h(vue.Transition, { name: 'q-transition--fade', appear: true }, () => ( useBackdrop.value === true ? vue.h('div', { class: 'q-dialog__backdrop fixed-full', style: transitionStyle.value, 'aria-hidden': 'true', tabindex: -1, onClick: onBackdropClick }) : null )), vue.h( vue.Transition, transitionProps.value, () => ( showing.value === true ? vue.h('div', { ref: innerRef, class: classes.value, style: transitionStyle.value, tabindex: -1, ...onEvents.value }, hSlot(slots.default)) : null ) ) ]) } return renderPortal } }); const duration = 150; var QDrawer = createComponent({ name: 'QDrawer', inheritAttrs: false, props: { ...useModelToggleProps, ...useDarkProps, side: { type: String, default: 'left', validator: v => [ 'left', 'right' ].includes(v) }, width: { type: Number, default: 300 }, mini: Boolean, miniToOverlay: Boolean, miniWidth: { type: Number, default: 57 }, noMiniAnimation: Boolean, breakpoint: { type: Number, default: 1023 }, showIfAbove: Boolean, behavior: { type: String, validator: v => [ 'default', 'desktop', 'mobile' ].includes(v), default: 'default' }, bordered: Boolean, elevated: Boolean, overlay: Boolean, persistent: Boolean, noSwipeOpen: Boolean, noSwipeClose: Boolean, noSwipeBackdrop: Boolean }, emits: [ ...useModelToggleEmits, 'onLayout', 'miniState' ], setup (props, { slots, emit, attrs }) { const vm = vue.getCurrentInstance(); const { proxy: { $q } } = vm; const isDark = useDark(props, $q); const { preventBodyScroll } = usePreventScroll(); const { registerTimeout, removeTimeout } = useTimeout(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QDrawer needs to be child of QLayout'); return emptyRenderFn } let lastDesktopState, timerMini = null, layoutTotalWidthWatcher; const belowBreakpoint = vue.ref( props.behavior === 'mobile' || (props.behavior !== 'desktop' && $layout.totalWidth.value <= props.breakpoint) ); const isMini = vue.computed(() => props.mini === true && belowBreakpoint.value !== true ); const size = vue.computed(() => ( isMini.value === true ? props.miniWidth : props.width )); const showing = vue.ref( props.showIfAbove === true && belowBreakpoint.value === false ? true : props.modelValue === true ); const hideOnRouteChange = vue.computed(() => props.persistent !== true && (belowBreakpoint.value === true || onScreenOverlay.value === true) ); function handleShow (evt, noEvent) { addToHistory(); evt !== false && $layout.animate(); applyPosition(0); if (belowBreakpoint.value === true) { const otherInstance = $layout.instances[ otherSide.value ]; if (otherInstance !== void 0 && otherInstance.belowBreakpoint === true) { otherInstance.hide(false); } applyBackdrop(1); $layout.isContainer.value !== true && preventBodyScroll(true); } else { applyBackdrop(0); evt !== false && setScrollable(false); } registerTimeout(() => { evt !== false && setScrollable(true); noEvent !== true && emit('show', evt); }, duration); } function handleHide (evt, noEvent) { removeFromHistory(); evt !== false && $layout.animate(); applyBackdrop(0); applyPosition(stateDirection.value * size.value); cleanup(); if (noEvent !== true) { registerTimeout(() => { emit('hide', evt); }, duration); } else { removeTimeout(); } } const { show, hide } = useModelToggle({ showing, hideOnRouteChange, handleShow, handleHide }); const { addToHistory, removeFromHistory } = useHistory(showing, hide, hideOnRouteChange); const instance = { belowBreakpoint, hide }; const rightSide = vue.computed(() => props.side === 'right'); const stateDirection = vue.computed(() => ($q.lang.rtl === true ? -1 : 1) * (rightSide.value === true ? 1 : -1) ); const flagBackdropBg = vue.ref(0); const flagPanning = vue.ref(false); const flagMiniAnimate = vue.ref(false); const flagContentPosition = vue.ref( // starting with "hidden" for SSR size.value * stateDirection.value ); const otherSide = vue.computed(() => (rightSide.value === true ? 'left' : 'right')); const offset = vue.computed(() => ( showing.value === true && belowBreakpoint.value === false && props.overlay === false ? (props.miniToOverlay === true ? props.miniWidth : size.value) : 0 )); const fixed = vue.computed(() => props.overlay === true || props.miniToOverlay === true || $layout.view.value.indexOf(rightSide.value ? 'R' : 'L') > -1 || ($q.platform.is.ios === true && $layout.isContainer.value === true) ); const onLayout = vue.computed(() => props.overlay === false && showing.value === true && belowBreakpoint.value === false ); const onScreenOverlay = vue.computed(() => props.overlay === true && showing.value === true && belowBreakpoint.value === false ); const backdropClass = vue.computed(() => 'fullscreen q-drawer__backdrop' + (showing.value === false && flagPanning.value === false ? ' hidden' : '') ); const backdropStyle = vue.computed(() => ({ backgroundColor: `rgba(0,0,0,${ flagBackdropBg.value * 0.4 })` })); const headerSlot = vue.computed(() => ( rightSide.value === true ? $layout.rows.value.top[ 2 ] === 'r' : $layout.rows.value.top[ 0 ] === 'l' )); const footerSlot = vue.computed(() => ( rightSide.value === true ? $layout.rows.value.bottom[ 2 ] === 'r' : $layout.rows.value.bottom[ 0 ] === 'l' )); const aboveStyle = vue.computed(() => { const css = {}; if ($layout.header.space === true && headerSlot.value === false) { if (fixed.value === true) { css.top = `${ $layout.header.offset }px`; } else if ($layout.header.space === true) { css.top = `${ $layout.header.size }px`; } } if ($layout.footer.space === true && footerSlot.value === false) { if (fixed.value === true) { css.bottom = `${ $layout.footer.offset }px`; } else if ($layout.footer.space === true) { css.bottom = `${ $layout.footer.size }px`; } } return css }); const style = vue.computed(() => { const style = { width: `${ size.value }px`, transform: `translateX(${ flagContentPosition.value }px)` }; return belowBreakpoint.value === true ? style : Object.assign(style, aboveStyle.value) }); const contentClass = vue.computed(() => 'q-drawer__content fit ' + ($layout.isContainer.value !== true ? 'scroll' : 'overflow-auto') ); const classes = vue.computed(() => `q-drawer q-drawer--${ props.side }` + (flagMiniAnimate.value === true ? ' q-drawer--mini-animate' : '') + (props.bordered === true ? ' q-drawer--bordered' : '') + (isDark.value === true ? ' q-drawer--dark q-dark' : '') + ( flagPanning.value === true ? ' no-transition' : (showing.value === true ? '' : ' q-layout--prevent-focus') ) + ( belowBreakpoint.value === true ? ' fixed q-drawer--on-top q-drawer--mobile q-drawer--top-padding' : ` q-drawer--${ isMini.value === true ? 'mini' : 'standard' }` + (fixed.value === true || onLayout.value !== true ? ' fixed' : '') + (props.overlay === true || props.miniToOverlay === true ? ' q-drawer--on-top' : '') + (headerSlot.value === true ? ' q-drawer--top-padding' : '') ) ); const openDirective = vue.computed(() => { // if props.noSwipeOpen !== true const dir = $q.lang.rtl === true ? props.side : otherSide.value; return [ [ TouchPan, onOpenPan, void 0, { [ dir ]: true, mouse: true } ] ] }); const contentCloseDirective = vue.computed(() => { // if belowBreakpoint.value === true && props.noSwipeClose !== true const dir = $q.lang.rtl === true ? otherSide.value : props.side; return [ [ TouchPan, onClosePan, void 0, { [ dir ]: true, mouse: true } ] ] }); const backdropCloseDirective = vue.computed(() => { // if showing.value === true && props.noSwipeBackdrop !== true const dir = $q.lang.rtl === true ? otherSide.value : props.side; return [ [ TouchPan, onClosePan, void 0, { [ dir ]: true, mouse: true, mouseAllDir: true } ] ] }); function updateBelowBreakpoint () { updateLocal(belowBreakpoint, ( props.behavior === 'mobile' || (props.behavior !== 'desktop' && $layout.totalWidth.value <= props.breakpoint) )); } vue.watch(belowBreakpoint, val => { if (val === true) { // from lg to xs lastDesktopState = showing.value; showing.value === true && hide(false); } else if ( props.overlay === false && props.behavior !== 'mobile' && lastDesktopState !== false ) { // from xs to lg if (showing.value === true) { applyPosition(0); applyBackdrop(0); cleanup(); } else { show(false); } } }); vue.watch(() => props.side, (newSide, oldSide) => { if ($layout.instances[ oldSide ] === instance) { $layout.instances[ oldSide ] = void 0; $layout[ oldSide ].space = false; $layout[ oldSide ].offset = 0; } $layout.instances[ newSide ] = instance; $layout[ newSide ].size = size.value; $layout[ newSide ].space = onLayout.value; $layout[ newSide ].offset = offset.value; }); vue.watch($layout.totalWidth, () => { if ($layout.isContainer.value === true || document.qScrollPrevented !== true) { updateBelowBreakpoint(); } }); vue.watch( () => props.behavior + props.breakpoint, updateBelowBreakpoint ); vue.watch($layout.isContainer, val => { showing.value === true && preventBodyScroll(val !== true); val === true && updateBelowBreakpoint(); }); vue.watch($layout.scrollbarWidth, () => { applyPosition(showing.value === true ? 0 : void 0); }); vue.watch(offset, val => { updateLayout('offset', val); }); vue.watch(onLayout, val => { emit('onLayout', val); updateLayout('space', val); }); vue.watch(rightSide, () => { applyPosition(); }); vue.watch(size, val => { applyPosition(); updateSizeOnLayout(props.miniToOverlay, val); }); vue.watch(() => props.miniToOverlay, val => { updateSizeOnLayout(val, size.value); }); vue.watch(() => $q.lang.rtl, () => { applyPosition(); }); vue.watch(() => props.mini, () => { if (props.noMiniAnimation) return if (props.modelValue === true) { animateMini(); $layout.animate(); } }); vue.watch(isMini, val => { emit('miniState', val); }); function applyPosition (position) { if (position === void 0) { vue.nextTick(() => { position = showing.value === true ? 0 : size.value; applyPosition(stateDirection.value * position); }); } else { if ( $layout.isContainer.value === true && rightSide.value === true && (belowBreakpoint.value === true || Math.abs(position) === size.value) ) { position += stateDirection.value * $layout.scrollbarWidth.value; } flagContentPosition.value = position; } } function applyBackdrop (x) { flagBackdropBg.value = x; } function setScrollable (v) { const action = v === true ? 'remove' : ($layout.isContainer.value !== true ? 'add' : ''); action !== '' && document.body.classList[ action ]('q-body--drawer-toggle'); } function animateMini () { timerMini !== null && clearTimeout(timerMini); if (vm.proxy && vm.proxy.$el) { // need to speed it up and apply it immediately, // even faster than Vue's nextTick! vm.proxy.$el.classList.add('q-drawer--mini-animate'); } flagMiniAnimate.value = true; timerMini = setTimeout(() => { timerMini = null; flagMiniAnimate.value = false; if (vm && vm.proxy && vm.proxy.$el) { vm.proxy.$el.classList.remove('q-drawer--mini-animate'); } }, 150); } function onOpenPan (evt) { if (showing.value !== false) { // some browsers might capture and trigger this // even if Drawer has just been opened (but animation is still pending) return } const width = size.value, position = between(evt.distance.x, 0, width); if (evt.isFinal === true) { const opened = position >= Math.min(75, width); if (opened === true) { show(); } else { $layout.animate(); applyBackdrop(0); applyPosition(stateDirection.value * width); } flagPanning.value = false; return } applyPosition( ($q.lang.rtl === true ? rightSide.value !== true : rightSide.value) ? Math.max(width - position, 0) : Math.min(0, position - width) ); applyBackdrop( between(position / width, 0, 1) ); if (evt.isFirst === true) { flagPanning.value = true; } } function onClosePan (evt) { if (showing.value !== true) { // some browsers might capture and trigger this // even if Drawer has just been closed (but animation is still pending) return } const width = size.value, dir = evt.direction === props.side, position = ($q.lang.rtl === true ? dir !== true : dir) ? between(evt.distance.x, 0, width) : 0; if (evt.isFinal === true) { const opened = Math.abs(position) < Math.min(75, width); if (opened === true) { $layout.animate(); applyBackdrop(1); applyPosition(0); } else { hide(); } flagPanning.value = false; return } applyPosition(stateDirection.value * position); applyBackdrop(between(1 - position / width, 0, 1)); if (evt.isFirst === true) { flagPanning.value = true; } } function cleanup () { preventBodyScroll(false); setScrollable(true); } function updateLayout (prop, val) { $layout.update(props.side, prop, val); } function updateLocal (prop, val) { if (prop.value !== val) { prop.value = val; } } function updateSizeOnLayout (miniToOverlay, size) { updateLayout('size', miniToOverlay === true ? props.miniWidth : size); } $layout.instances[ props.side ] = instance; updateSizeOnLayout(props.miniToOverlay, size.value); updateLayout('space', onLayout.value); updateLayout('offset', offset.value); if ( props.showIfAbove === true && props.modelValue !== true && showing.value === true && props[ 'onUpdate:modelValue' ] !== void 0 ) { emit('update:modelValue', true); } vue.onMounted(() => { emit('onLayout', onLayout.value); emit('miniState', isMini.value); lastDesktopState = props.showIfAbove === true; const fn = () => { const action = showing.value === true ? handleShow : handleHide; action(false, true); }; if ($layout.totalWidth.value !== 0) { // make sure that all computed properties // have been updated before calling handleShow/handleHide() vue.nextTick(fn); return } layoutTotalWidthWatcher = vue.watch($layout.totalWidth, () => { layoutTotalWidthWatcher(); layoutTotalWidthWatcher = void 0; if (showing.value === false && props.showIfAbove === true && belowBreakpoint.value === false) { show(false); } else { fn(); } }); }); vue.onBeforeUnmount(() => { layoutTotalWidthWatcher !== void 0 && layoutTotalWidthWatcher(); if (timerMini !== null) { clearTimeout(timerMini); timerMini = null; } showing.value === true && cleanup(); if ($layout.instances[ props.side ] === instance) { $layout.instances[ props.side ] = void 0; updateLayout('size', 0); updateLayout('offset', 0); updateLayout('space', false); } }); return () => { const child = []; if (belowBreakpoint.value === true) { props.noSwipeOpen === false && child.push( vue.withDirectives( vue.h('div', { key: 'open', class: `q-drawer__opener fixed-${ props.side }`, 'aria-hidden': 'true' }), openDirective.value ) ); child.push( hDir( 'div', { ref: 'backdrop', class: backdropClass.value, style: backdropStyle.value, 'aria-hidden': 'true', onClick: hide }, void 0, 'backdrop', props.noSwipeBackdrop !== true && showing.value === true, () => backdropCloseDirective.value ) ); } const mini = isMini.value === true && slots.mini !== void 0; const content = [ vue.h('div', { ...attrs, key: '' + mini, // required otherwise Vue will not diff correctly class: [ contentClass.value, attrs.class ] }, mini === true ? slots.mini() : hSlot(slots.default) ) ]; if (props.elevated === true && showing.value === true) { content.push( vue.h('div', { class: 'q-layout__shadow absolute-full overflow-hidden no-pointer-events' }) ); } child.push( hDir( 'aside', { ref: 'content', class: classes.value, style: style.value }, content, 'contentclose', props.noSwipeClose !== true && belowBreakpoint.value === true, () => contentCloseDirective.value ) ); return vue.h('div', { class: 'q-drawer-container' }, child) } } }); function getBlockElement (el, parent) { if (parent && el === parent) { return null } const nodeName = el.nodeName.toLowerCase(); if ([ 'div', 'li', 'ul', 'ol', 'blockquote' ].includes(nodeName) === true) { return el } const style = window.getComputedStyle ? window.getComputedStyle(el) : el.currentStyle, display = style.display; if (display === 'block' || display === 'table') { return el } return getBlockElement(el.parentNode) } function isChildOf (el, parent, orSame) { return !el || el === document.body ? false : (orSame === true && el === parent) || (parent === document ? document.body : parent).contains(el.parentNode) } function createRange (node, chars, range) { if (!range) { range = document.createRange(); range.selectNode(node); range.setStart(node, 0); } if (chars.count === 0) { range.setEnd(node, chars.count); } else if (chars.count > 0) { if (node.nodeType === Node.TEXT_NODE) { if (node.textContent.length < chars.count) { chars.count -= node.textContent.length; } else { range.setEnd(node, chars.count); chars.count = 0; } } else { for (let lp = 0; chars.count !== 0 && lp < node.childNodes.length; lp++) { range = createRange(node.childNodes[ lp ], chars, range); } } } return range } const urlRegex = /^https?:\/\//; class Caret { constructor (el, eVm) { this.el = el; this.eVm = eVm; this._range = null; } get selection () { if (this.el) { const sel = document.getSelection(); // only when the selection in element if (isChildOf(sel.anchorNode, this.el, true) && isChildOf(sel.focusNode, this.el, true)) { return sel } } return null } get hasSelection () { return this.selection !== null ? this.selection.toString().length !== 0 : false } get range () { const sel = this.selection; if (sel !== null && sel.rangeCount) { return sel.getRangeAt(0) } return this._range } get parent () { const range = this.range; if (range !== null) { const node = range.startContainer; return node.nodeType === document.ELEMENT_NODE ? node : node.parentNode } return null } get blockParent () { const parent = this.parent; if (parent !== null) { return getBlockElement(parent, this.el) } return null } save (range = this.range) { if (range !== null) { this._range = range; } } restore (range = this._range) { const r = document.createRange(), sel = document.getSelection(); if (range !== null) { r.setStart(range.startContainer, range.startOffset); r.setEnd(range.endContainer, range.endOffset); sel.removeAllRanges(); sel.addRange(r); } else { sel.selectAllChildren(this.el); sel.collapseToEnd(); } } savePosition () { let charCount = -1, node; const selection = document.getSelection(), parentEl = this.el.parentNode; if (selection.focusNode && isChildOf(selection.focusNode, parentEl)) { node = selection.focusNode; charCount = selection.focusOffset; while (node && node !== parentEl) { if (node !== this.el && node.previousSibling) { node = node.previousSibling; charCount += node.textContent.length; } else { node = node.parentNode; } } } this.savedPos = charCount; } restorePosition (length = 0) { if (this.savedPos > 0 && this.savedPos < length) { const selection = window.getSelection(), range = createRange(this.el, { count: this.savedPos }); if (range) { range.collapse(false); selection.removeAllRanges(); selection.addRange(range); } } } hasParent (name, spanLevel) { const el = spanLevel ? this.parent : this.blockParent; return el !== null ? el.nodeName.toLowerCase() === name.toLowerCase() : false } hasParents (list, recursive, el = this.parent) { if (el === null) { return false } if (list.includes(el.nodeName.toLowerCase()) === true) { return true } return recursive === true ? this.hasParents(list, recursive, el.parentNode) : false } is (cmd, param) { if (this.selection === null) { return false } switch (cmd) { case 'formatBlock': return (param === 'DIV' && this.parent === this.el) || this.hasParent(param, param === 'PRE') case 'link': return this.hasParent('A', true) case 'fontSize': return document.queryCommandValue(cmd) === param case 'fontName': const res = document.queryCommandValue(cmd); return res === `"${ param }"` || res === param case 'fullscreen': return this.eVm.inFullscreen.value case 'viewsource': return this.eVm.isViewingSource.value case void 0: return false default: const state = document.queryCommandState(cmd); return param !== void 0 ? state === param : state } } getParentAttribute (attrib) { if (this.parent !== null) { return this.parent.getAttribute(attrib) } return null } can (name) { if (name === 'outdent') { return this.hasParents([ 'blockquote', 'li' ], true) } if (name === 'indent') { return this.hasParents([ 'li' ], true) } if (name === 'link') { return this.selection !== null || this.is('link') } } apply (cmd, param, done = noop) { if (cmd === 'formatBlock') { if ([ 'BLOCKQUOTE', 'H1', 'H2', 'H3', 'H4', 'H5', 'H6' ].includes(param) && this.is(cmd, param)) { cmd = 'outdent'; param = null; } if (param === 'PRE' && this.is(cmd, 'PRE')) { param = 'P'; } } else if (cmd === 'print') { done(); const win = window.open(); win.document.write(` Print - ${ document.title }
${ this.el.innerHTML }
`); win.print(); win.close(); return } else if (cmd === 'link') { const link = this.getParentAttribute('href'); if (link === null) { const selection = this.selectWord(this.selection); const url = selection ? selection.toString() : ''; if (!url.length) { if (!this.range || !this.range.cloneContents().querySelector('img')) { return } } this.eVm.editLinkUrl.value = urlRegex.test(url) ? url : 'https://'; document.execCommand('createLink', false, this.eVm.editLinkUrl.value); this.save(selection.getRangeAt(0)); } else { this.eVm.editLinkUrl.value = link; this.range.selectNodeContents(this.parent); this.save(); } return } else if (cmd === 'fullscreen') { this.eVm.toggleFullscreen(); done(); return } else if (cmd === 'viewsource') { this.eVm.isViewingSource.value = this.eVm.isViewingSource.value === false; this.eVm.setContent(this.eVm.props.modelValue); done(); return } document.execCommand(cmd, false, param); done(); } selectWord (sel) { if (sel === null || sel.isCollapsed !== true || /* IE 11 */ sel.modify === void 0) { return sel } // Detect if selection is backwards const range = document.createRange(); range.setStart(sel.anchorNode, sel.anchorOffset); range.setEnd(sel.focusNode, sel.focusOffset); const direction = range.collapsed ? [ 'backward', 'forward' ] : [ 'forward', 'backward' ]; range.detach(); // modify() works on the focus of the selection const endNode = sel.focusNode, endOffset = sel.focusOffset; sel.collapse(sel.anchorNode, sel.anchorOffset); sel.modify('move', direction[ 0 ], 'character'); sel.modify('move', direction[ 1 ], 'word'); sel.extend(endNode, endOffset); sel.modify('extend', direction[ 1 ], 'character'); sel.modify('extend', direction[ 0 ], 'word'); return sel } } var QTooltip = createComponent({ name: 'QTooltip', inheritAttrs: false, props: { ...useAnchorProps, ...useModelToggleProps, ...useTransitionProps, maxHeight: { type: String, default: null }, maxWidth: { type: String, default: null }, transitionShow: { default: 'jump-down' }, transitionHide: { default: 'jump-up' }, anchor: { type: String, default: 'bottom middle', validator: validatePosition }, self: { type: String, default: 'top middle', validator: validatePosition }, offset: { type: Array, default: () => [ 14, 14 ], validator: validateOffset }, scrollTarget: { default: void 0 }, delay: { type: Number, default: 0 }, hideDelay: { type: Number, default: 0 } }, emits: [ ...useModelToggleEmits ], setup (props, { slots, emit, attrs }) { let unwatchPosition, observer; const vm = vue.getCurrentInstance(); const { proxy: { $q } } = vm; const innerRef = vue.ref(null); const showing = vue.ref(false); const anchorOrigin = vue.computed(() => parsePosition(props.anchor, $q.lang.rtl)); const selfOrigin = vue.computed(() => parsePosition(props.self, $q.lang.rtl)); const hideOnRouteChange = vue.computed(() => props.persistent !== true); const { registerTick, removeTick } = useTick(); const { registerTimeout } = useTimeout(); const { transitionProps, transitionStyle } = useTransition(props); const { localScrollTarget, changeScrollEvent, unconfigureScrollTarget } = useScrollTarget(props, configureScrollTarget); const { anchorEl, canShow, anchorEvents } = useAnchor({ showing, configureAnchorEl }); const { show, hide } = useModelToggle({ showing, canShow, handleShow, handleHide, hideOnRouteChange, processOnMount: true }); Object.assign(anchorEvents, { delayShow, delayHide }); const { showPortal, hidePortal, renderPortal } = usePortal(vm, innerRef, renderPortalContent, 'tooltip'); // if we're on mobile, let's improve the experience // by closing it when user taps outside of it if ($q.platform.is.mobile === true) { const clickOutsideProps = { anchorEl, innerRef, onClickOutside (e) { hide(e); // prevent click if it's on a dialog backdrop if (e.target.classList.contains('q-dialog__backdrop')) { stopAndPrevent(e); } return true } }; const hasClickOutside = vue.computed(() => // it doesn't has external model // (null is the default value) props.modelValue === null // and it's not persistent && props.persistent !== true && showing.value === true ); vue.watch(hasClickOutside, val => { const fn = val === true ? addClickOutside : removeClickOutside; fn(clickOutsideProps); }); vue.onBeforeUnmount(() => { removeClickOutside(clickOutsideProps); }); } function handleShow (evt) { showPortal(); // should removeTick() if this gets removed registerTick(() => { observer = new MutationObserver(() => updatePosition()); observer.observe(innerRef.value, { attributes: false, childList: true, characterData: true, subtree: true }); updatePosition(); configureScrollTarget(); }); if (unwatchPosition === void 0) { unwatchPosition = vue.watch( () => $q.screen.width + '|' + $q.screen.height + '|' + props.self + '|' + props.anchor + '|' + $q.lang.rtl, updatePosition ); } // should removeTimeout() if this gets removed registerTimeout(() => { showPortal(true); // done showing portal emit('show', evt); }, props.transitionDuration); } function handleHide (evt) { removeTick(); hidePortal(); anchorCleanup(); // should removeTimeout() if this gets removed registerTimeout(() => { hidePortal(true); // done hiding, now destroy emit('hide', evt); }, props.transitionDuration); } function anchorCleanup () { if (observer !== void 0) { observer.disconnect(); observer = void 0; } if (unwatchPosition !== void 0) { unwatchPosition(); unwatchPosition = void 0; } unconfigureScrollTarget(); cleanEvt(anchorEvents, 'tooltipTemp'); } function updatePosition () { setPosition({ targetEl: innerRef.value, offset: props.offset, anchorEl: anchorEl.value, anchorOrigin: anchorOrigin.value, selfOrigin: selfOrigin.value, maxHeight: props.maxHeight, maxWidth: props.maxWidth }); } function delayShow (evt) { if ($q.platform.is.mobile === true) { clearSelection(); document.body.classList.add('non-selectable'); const target = anchorEl.value; const evts = [ 'touchmove', 'touchcancel', 'touchend', 'click' ] .map(e => ([ target, e, 'delayHide', 'passiveCapture' ])); addEvt(anchorEvents, 'tooltipTemp', evts); } registerTimeout(() => { show(evt); }, props.delay); } function delayHide (evt) { if ($q.platform.is.mobile === true) { cleanEvt(anchorEvents, 'tooltipTemp'); clearSelection(); // delay needed otherwise selection still occurs setTimeout(() => { document.body.classList.remove('non-selectable'); }, 10); } // should removeTimeout() if this gets removed registerTimeout(() => { hide(evt); }, props.hideDelay); } function configureAnchorEl () { if (props.noParentEvent === true || anchorEl.value === null) { return } const evts = $q.platform.is.mobile === true ? [ [ anchorEl.value, 'touchstart', 'delayShow', 'passive' ] ] : [ [ anchorEl.value, 'mouseenter', 'delayShow', 'passive' ], [ anchorEl.value, 'mouseleave', 'delayHide', 'passive' ] ]; addEvt(anchorEvents, 'anchor', evts); } function configureScrollTarget () { if (anchorEl.value !== null || props.scrollTarget !== void 0) { localScrollTarget.value = getScrollTarget(anchorEl.value, props.scrollTarget); const fn = props.noParentEvent === true ? updatePosition : hide; changeScrollEvent(localScrollTarget.value, fn); } } function getTooltipContent () { return showing.value === true ? vue.h('div', { ...attrs, ref: innerRef, class: [ 'q-tooltip q-tooltip--style q-position-engine no-pointer-events', attrs.class ], style: [ attrs.style, transitionStyle.value ], role: 'tooltip' }, hSlot(slots.default)) : null } function renderPortalContent () { return vue.h(vue.Transition, transitionProps.value, getTooltipContent) } vue.onBeforeUnmount(anchorCleanup); // expose public methods Object.assign(vm.proxy, { updatePosition }); return renderPortal } }); var QItem = createComponent({ name: 'QItem', props: { ...useDarkProps, ...useRouterLinkProps, tag: { type: String, default: 'div' }, active: { type: Boolean, default: null }, clickable: Boolean, dense: Boolean, insetLevel: Number, tabindex: [ String, Number ], focused: Boolean, manualFocus: Boolean }, emits: [ 'click', 'keyup' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const { hasLink, linkAttrs, linkClass, linkTag, navigateOnClick } = useRouterLink(); const rootRef = vue.ref(null); const blurTargetRef = vue.ref(null); const isActionable = vue.computed(() => props.clickable === true || hasLink.value === true || props.tag === 'label' ); const isClickable = vue.computed(() => props.disable !== true && isActionable.value === true ); const classes = vue.computed(() => 'q-item q-item-type row no-wrap' + (props.dense === true ? ' q-item--dense' : '') + (isDark.value === true ? ' q-item--dark' : '') + ( hasLink.value === true && props.active === null ? linkClass.value : ( props.active === true ? ` q-item--active${ props.activeClass !== void 0 ? ` ${ props.activeClass }` : '' }` : '' ) ) + (props.disable === true ? ' disabled' : '') + ( isClickable.value === true ? ' q-item--clickable q-link cursor-pointer ' + (props.manualFocus === true ? 'q-manual-focusable' : 'q-focusable q-hoverable') + (props.focused === true ? ' q-manual-focusable--focused' : '') : '' ) ); const style = vue.computed(() => { if (props.insetLevel === void 0) { return null } const dir = $q.lang.rtl === true ? 'Right' : 'Left'; return { [ 'padding' + dir ]: (16 + props.insetLevel * 56) + 'px' } }); function onClick (e) { if (isClickable.value === true) { if (blurTargetRef.value !== null) { if (e.qKeyEvent !== true && document.activeElement === rootRef.value) { blurTargetRef.value.focus(); } else if (document.activeElement === blurTargetRef.value) { rootRef.value.focus(); } } navigateOnClick(e); } } function onKeyup (e) { if (isClickable.value === true && isKeyCode(e, 13) === true) { stopAndPrevent(e); // for ripple e.qKeyEvent = true; // for click trigger const evt = new MouseEvent('click', e); evt.qKeyEvent = true; rootRef.value.dispatchEvent(evt); } emit('keyup', e); } function getContent () { const child = hUniqueSlot(slots.default, []); isClickable.value === true && child.unshift( vue.h('div', { class: 'q-focus-helper', tabindex: -1, ref: blurTargetRef }) ); return child } return () => { const data = { ref: rootRef, class: classes.value, style: style.value, role: 'listitem', onClick, onKeyup }; if (isClickable.value === true) { data.tabindex = props.tabindex || '0'; Object.assign(data, linkAttrs.value); } else if (isActionable.value === true) { data[ 'aria-disabled' ] = 'true'; } return vue.h( linkTag.value, data, getContent() ) } } }); var QItemSection = createComponent({ name: 'QItemSection', props: { avatar: Boolean, thumbnail: Boolean, side: Boolean, top: Boolean, noWrap: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => 'q-item__section column' + ` q-item__section--${ props.avatar === true || props.side === true || props.thumbnail === true ? 'side' : 'main' }` + (props.top === true ? ' q-item__section--top justify-start' : ' justify-center') + (props.avatar === true ? ' q-item__section--avatar' : '') + (props.thumbnail === true ? ' q-item__section--thumbnail' : '') + (props.noWrap === true ? ' q-item__section--nowrap' : '') ); return () => vue.h('div', { class: classes.value }, hSlot(slots.default)) } }); function run (e, btn, eVm) { if (btn.handler) { btn.handler(e, eVm, eVm.caret); } else { eVm.runCmd(btn.cmd, btn.param); } } function getGroup (children) { return vue.h('div', { class: 'q-editor__toolbar-group' }, children) } function getBtn (eVm, btn, clickHandler, active = false) { const toggled = active || (btn.type === 'toggle' ? (btn.toggled ? btn.toggled(eVm) : btn.cmd && eVm.caret.is(btn.cmd, btn.param)) : false), child = []; if (btn.tip && eVm.$q.platform.is.desktop) { const Key = btn.key ? vue.h('div', [ vue.h('small', `(CTRL + ${ String.fromCharCode(btn.key) })`) ]) : null; child.push( vue.h(QTooltip, { delay: 1000 }, () => [ vue.h('div', { innerHTML: btn.tip }), Key ]) ); } return vue.h(QBtn, { ...eVm.buttonProps.value, icon: btn.icon !== null ? btn.icon : void 0, color: toggled ? btn.toggleColor || eVm.props.toolbarToggleColor : btn.color || eVm.props.toolbarColor, textColor: toggled && !eVm.props.toolbarPush ? null : btn.textColor || eVm.props.toolbarTextColor, label: btn.label, disable: btn.disable ? (typeof btn.disable === 'function' ? btn.disable(eVm) : true) : false, size: 'sm', onClick (e) { clickHandler && clickHandler(); run(e, btn, eVm); } }, () => child) } function getDropdown (eVm, btn) { const onlyIcons = btn.list === 'only-icons'; let label = btn.label, icon = btn.icon !== null ? btn.icon : void 0, contentClass, Items; function closeDropdown () { Dropdown.component.proxy.hide(); } if (onlyIcons) { Items = btn.options.map(btn => { const active = btn.type === void 0 ? eVm.caret.is(btn.cmd, btn.param) : false; if (active) { label = btn.tip; icon = btn.icon !== null ? btn.icon : void 0; } return getBtn(eVm, btn, closeDropdown, active) }); contentClass = eVm.toolbarBackgroundClass.value; Items = [ getGroup(Items) ]; } else { const activeClass = eVm.props.toolbarToggleColor !== void 0 ? `text-${ eVm.props.toolbarToggleColor }` : null; const inactiveClass = eVm.props.toolbarTextColor !== void 0 ? `text-${ eVm.props.toolbarTextColor }` : null; const noIcons = btn.list === 'no-icons'; Items = btn.options.map(btn => { const disable = btn.disable ? btn.disable(eVm) : false; const active = btn.type === void 0 ? eVm.caret.is(btn.cmd, btn.param) : false; if (active) { label = btn.tip; icon = btn.icon !== null ? btn.icon : void 0; } const htmlTip = btn.htmlTip; return vue.h(QItem, { active, activeClass, clickable: true, disable, dense: true, onClick (e) { closeDropdown(); eVm.contentRef.value !== null && eVm.contentRef.value.focus(); eVm.caret.restore(); run(e, btn, eVm); } }, () => [ noIcons === true ? null : vue.h( QItemSection, { class: active ? activeClass : inactiveClass, side: true }, () => vue.h(QIcon, { name: btn.icon !== null ? btn.icon : void 0 }) ), vue.h( QItemSection, htmlTip ? () => vue.h('div', { class: 'text-no-wrap', innerHTML: btn.htmlTip }) : (btn.tip ? () => vue.h('div', { class: 'text-no-wrap' }, btn.tip) : void 0) ) ]) }); contentClass = [ eVm.toolbarBackgroundClass.value, inactiveClass ]; } const highlight = btn.highlight && label !== btn.label; const Dropdown = vue.h(QBtnDropdown, { ...eVm.buttonProps.value, noCaps: true, noWrap: true, color: highlight ? eVm.props.toolbarToggleColor : eVm.props.toolbarColor, textColor: highlight && !eVm.props.toolbarPush ? null : eVm.props.toolbarTextColor, label: btn.fixedLabel ? btn.label : label, icon: btn.fixedIcon ? (btn.icon !== null ? btn.icon : void 0) : icon, contentClass, onShow: evt => eVm.emit('dropdownShow', evt), onHide: evt => eVm.emit('dropdownHide', evt), onBeforeShow: evt => eVm.emit('dropdownBeforeShow', evt), onBeforeHide: evt => eVm.emit('dropdownBeforeHide', evt) }, () => Items); return Dropdown } function getToolbar (eVm) { if (eVm.caret) { return eVm.buttons.value .filter(f => { return !eVm.isViewingSource.value || f.find(fb => fb.cmd === 'viewsource') }) .map(group => getGroup( group.map(btn => { if (eVm.isViewingSource.value && btn.cmd !== 'viewsource') { return false } if (btn.type === 'slot') { return hSlot(eVm.slots[ btn.slot ]) } if (btn.type === 'dropdown') { return getDropdown(eVm, btn) } return getBtn(eVm, btn) }) )) } } function getFonts (defaultFont, defaultFontLabel, defaultFontIcon, fonts = {}) { const aliases = Object.keys(fonts); if (aliases.length === 0) { return {} } const def = { default_font: { cmd: 'fontName', param: defaultFont, icon: defaultFontIcon, tip: defaultFontLabel } }; aliases.forEach(alias => { const name = fonts[ alias ]; def[ alias ] = { cmd: 'fontName', param: name, icon: defaultFontIcon, tip: name, htmlTip: `${ name }` }; }); return def } function getLinkEditor (eVm) { if (eVm.caret) { const color = eVm.props.toolbarColor || eVm.props.toolbarTextColor; let link = eVm.editLinkUrl.value; const updateLink = () => { eVm.caret.restore(); if (link !== eVm.editLinkUrl.value) { document.execCommand('createLink', false, link === '' ? ' ' : link); } eVm.editLinkUrl.value = null; }; return [ vue.h('div', { class: `q-mx-xs text-${ color }` }, `${ eVm.$q.lang.editor.url }: `), vue.h('input', { key: 'qedt_btm_input', class: 'col q-editor__link-input', value: link, onInput: evt => { stop(evt); link = evt.target.value; }, onKeydown: evt => { if (shouldIgnoreKey(evt) === true) { return } switch (evt.keyCode) { case 13: // ENTER key prevent(evt); return updateLink() case 27: // ESCAPE key prevent(evt); eVm.caret.restore(); if (!eVm.editLinkUrl.value || eVm.editLinkUrl.value === 'https://') { document.execCommand('unlink'); } eVm.editLinkUrl.value = null; break } } }), getGroup([ vue.h(QBtn, { key: 'qedt_btm_rem', tabindex: -1, ...eVm.buttonProps.value, label: eVm.$q.lang.label.remove, noCaps: true, onClick: () => { eVm.caret.restore(); document.execCommand('unlink'); eVm.editLinkUrl.value = null; } }), vue.h(QBtn, { key: 'qedt_btm_upd', ...eVm.buttonProps.value, label: eVm.$q.lang.label.update, noCaps: true, onClick: updateLink }) ]) ] } } const listenerRE = /^on[A-Z]/; function useSplitAttrs (attrs, vnode) { const acc = { listeners: vue.ref({}), attributes: vue.ref({}) }; function update () { const attributes = {}; const listeners = {}; for (const key in attrs) { if (key !== 'class' && key !== 'style' && listenerRE.test(key) === false) { attributes[ key ] = attrs[ key ]; } } for (const key in vnode.props) { if (listenerRE.test(key) === true) { listeners[ key ] = vnode.props[ key ]; } } acc.attributes.value = attributes; acc.listeners.value = listeners; } vue.onBeforeUpdate(update); update(); return acc } const toString = Object.prototype.toString, hasOwn = Object.prototype.hasOwnProperty, notPlainObject = new Set( [ 'Boolean', 'Number', 'String', 'Function', 'Array', 'Date', 'RegExp' ] .map(name => '[object ' + name + ']') ); function isPlainObject (obj) { if (obj !== Object(obj) || notPlainObject.has(toString.call(obj)) === true) { return false } if ( obj.constructor && hasOwn.call(obj, 'constructor') === false && hasOwn.call(obj.constructor.prototype, 'isPrototypeOf') === false ) { return false } let key; for (key in obj) {} // eslint-disable-line return key === void 0 || hasOwn.call(obj, key) } function extend () { let options, name, src, copy, copyIsArray, clone, target = arguments[ 0 ] || {}, i = 1, deep = false; const length = arguments.length; if (typeof target === 'boolean') { deep = target; target = arguments[ 1 ] || {}; i = 2; } if (Object(target) !== target && typeof target !== 'function') { target = {}; } if (length === i) { target = this; i--; } for (; i < length; i++) { if ((options = arguments[ i ]) !== null) { for (name in options) { src = target[ name ]; copy = options[ name ]; if (target === copy) { continue } if ( deep === true && copy && ((copyIsArray = Array.isArray(copy)) || isPlainObject(copy) === true) ) { if (copyIsArray === true) { clone = Array.isArray(src) === true ? src : []; } else { clone = isPlainObject(src) === true ? src : {}; } target[ name ] = extend(deep, clone, copy); } else if (copy !== void 0) { target[ name ] = copy; } } } } return target } var QEditor = createComponent({ name: 'QEditor', props: { ...useDarkProps, ...useFullscreenProps, modelValue: { type: String, required: true }, readonly: Boolean, disable: Boolean, minHeight: { type: String, default: '10rem' }, maxHeight: String, height: String, definitions: Object, fonts: Object, placeholder: String, toolbar: { type: Array, validator: v => v.length === 0 || v.every(group => group.length), default () { return [ [ 'left', 'center', 'right', 'justify' ], [ 'bold', 'italic', 'underline', 'strike' ], [ 'undo', 'redo' ] ] } }, toolbarColor: String, toolbarBg: String, toolbarTextColor: String, toolbarToggleColor: { type: String, default: 'primary' }, toolbarOutline: Boolean, toolbarPush: Boolean, toolbarRounded: Boolean, paragraphTag: { type: String, validator: v => [ 'div', 'p' ].includes(v), default: 'div' }, contentStyle: Object, contentClass: [ Object, Array, String ], square: Boolean, flat: Boolean, dense: Boolean }, emits: [ ...useFullscreenEmits, 'update:modelValue', 'keydown', 'click', 'mouseup', 'keyup', 'touchend', 'focus', 'blur', 'dropdownShow', 'dropdownHide', 'dropdownBeforeShow', 'dropdownBeforeHide', 'linkShow', 'linkHide' ], setup (props, { slots, emit, attrs }) { const { proxy, vnode } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const { inFullscreen, toggleFullscreen } = useFullscreen(); const splitAttrs = useSplitAttrs(attrs, vnode); const rootRef = vue.ref(null); const contentRef = vue.ref(null); const editLinkUrl = vue.ref(null); const isViewingSource = vue.ref(false); const editable = vue.computed(() => !props.readonly && !props.disable); let defaultFont, offsetBottom; let lastEmit = props.modelValue; { document.execCommand('defaultParagraphSeparator', false, props.paragraphTag); defaultFont = window.getComputedStyle(document.body).fontFamily; } const toolbarBackgroundClass = vue.computed(() => ( props.toolbarBg ? ` bg-${ props.toolbarBg }` : '' )); const buttonProps = vue.computed(() => { const flat = props.toolbarOutline !== true && props.toolbarPush !== true; return { type: 'a', flat, noWrap: true, outline: props.toolbarOutline, push: props.toolbarPush, rounded: props.toolbarRounded, dense: true, color: props.toolbarColor, disable: !editable.value, size: 'sm' } }); const buttonDef = vue.computed(() => { const e = $q.lang.editor, i = $q.iconSet.editor; return { bold: { cmd: 'bold', icon: i.bold, tip: e.bold, key: 66 }, italic: { cmd: 'italic', icon: i.italic, tip: e.italic, key: 73 }, strike: { cmd: 'strikeThrough', icon: i.strikethrough, tip: e.strikethrough, key: 83 }, underline: { cmd: 'underline', icon: i.underline, tip: e.underline, key: 85 }, unordered: { cmd: 'insertUnorderedList', icon: i.unorderedList, tip: e.unorderedList }, ordered: { cmd: 'insertOrderedList', icon: i.orderedList, tip: e.orderedList }, subscript: { cmd: 'subscript', icon: i.subscript, tip: e.subscript, htmlTip: 'x2' }, superscript: { cmd: 'superscript', icon: i.superscript, tip: e.superscript, htmlTip: 'x2' }, link: { cmd: 'link', disable: eVm => eVm.caret && !eVm.caret.can('link'), icon: i.hyperlink, tip: e.hyperlink, key: 76 }, fullscreen: { cmd: 'fullscreen', icon: i.toggleFullscreen, tip: e.toggleFullscreen, key: 70 }, viewsource: { cmd: 'viewsource', icon: i.viewSource, tip: e.viewSource }, quote: { cmd: 'formatBlock', param: 'BLOCKQUOTE', icon: i.quote, tip: e.quote, key: 81 }, left: { cmd: 'justifyLeft', icon: i.left, tip: e.left }, center: { cmd: 'justifyCenter', icon: i.center, tip: e.center }, right: { cmd: 'justifyRight', icon: i.right, tip: e.right }, justify: { cmd: 'justifyFull', icon: i.justify, tip: e.justify }, print: { type: 'no-state', cmd: 'print', icon: i.print, tip: e.print, key: 80 }, outdent: { type: 'no-state', disable: eVm => eVm.caret && !eVm.caret.can('outdent'), cmd: 'outdent', icon: i.outdent, tip: e.outdent }, indent: { type: 'no-state', disable: eVm => eVm.caret && !eVm.caret.can('indent'), cmd: 'indent', icon: i.indent, tip: e.indent }, removeFormat: { type: 'no-state', cmd: 'removeFormat', icon: i.removeFormat, tip: e.removeFormat }, hr: { type: 'no-state', cmd: 'insertHorizontalRule', icon: i.hr, tip: e.hr }, undo: { type: 'no-state', cmd: 'undo', icon: i.undo, tip: e.undo, key: 90 }, redo: { type: 'no-state', cmd: 'redo', icon: i.redo, tip: e.redo, key: 89 }, h1: { cmd: 'formatBlock', param: 'H1', icon: i.heading1 || i.heading, tip: e.heading1, htmlTip: `

${ e.heading1 }

` }, h2: { cmd: 'formatBlock', param: 'H2', icon: i.heading2 || i.heading, tip: e.heading2, htmlTip: `

${ e.heading2 }

` }, h3: { cmd: 'formatBlock', param: 'H3', icon: i.heading3 || i.heading, tip: e.heading3, htmlTip: `

${ e.heading3 }

` }, h4: { cmd: 'formatBlock', param: 'H4', icon: i.heading4 || i.heading, tip: e.heading4, htmlTip: `

${ e.heading4 }

` }, h5: { cmd: 'formatBlock', param: 'H5', icon: i.heading5 || i.heading, tip: e.heading5, htmlTip: `
${ e.heading5 }
` }, h6: { cmd: 'formatBlock', param: 'H6', icon: i.heading6 || i.heading, tip: e.heading6, htmlTip: `
${ e.heading6 }
` }, p: { cmd: 'formatBlock', param: props.paragraphTag, icon: i.heading, tip: e.paragraph }, code: { cmd: 'formatBlock', param: 'PRE', icon: i.code, htmlTip: `${ e.code }` }, 'size-1': { cmd: 'fontSize', param: '1', icon: i.size1 || i.size, tip: e.size1, htmlTip: `${ e.size1 }` }, 'size-2': { cmd: 'fontSize', param: '2', icon: i.size2 || i.size, tip: e.size2, htmlTip: `${ e.size2 }` }, 'size-3': { cmd: 'fontSize', param: '3', icon: i.size3 || i.size, tip: e.size3, htmlTip: `${ e.size3 }` }, 'size-4': { cmd: 'fontSize', param: '4', icon: i.size4 || i.size, tip: e.size4, htmlTip: `${ e.size4 }` }, 'size-5': { cmd: 'fontSize', param: '5', icon: i.size5 || i.size, tip: e.size5, htmlTip: `${ e.size5 }` }, 'size-6': { cmd: 'fontSize', param: '6', icon: i.size6 || i.size, tip: e.size6, htmlTip: `${ e.size6 }` }, 'size-7': { cmd: 'fontSize', param: '7', icon: i.size7 || i.size, tip: e.size7, htmlTip: `${ e.size7 }` } } }); const buttons = vue.computed(() => { const userDef = props.definitions || {}; const def = props.definitions || props.fonts ? extend( true, {}, buttonDef.value, userDef, getFonts( defaultFont, $q.lang.editor.defaultFont, $q.iconSet.editor.font, props.fonts ) ) : buttonDef.value; return props.toolbar.map( group => group.map(token => { if (token.options) { return { type: 'dropdown', icon: token.icon, label: token.label, size: 'sm', dense: true, fixedLabel: token.fixedLabel, fixedIcon: token.fixedIcon, highlight: token.highlight, list: token.list, options: token.options.map(item => def[ item ]) } } const obj = def[ token ]; if (obj) { return obj.type === 'no-state' || (userDef[ token ] && ( obj.cmd === void 0 || (buttonDef.value[ obj.cmd ] && buttonDef.value[ obj.cmd ].type === 'no-state') )) ? obj : Object.assign({ type: 'toggle' }, obj) } else { return { type: 'slot', slot: token } } }) ) }); const eVm = { $q, props, slots, emit, // caret (will get injected after mount) inFullscreen, toggleFullscreen, runCmd, isViewingSource, editLinkUrl, toolbarBackgroundClass, buttonProps, contentRef, buttons, setContent }; vue.watch(() => props.modelValue, v => { if (lastEmit !== v) { lastEmit = v; setContent(v, true); } }); vue.watch(editLinkUrl, v => { emit(`link${ v ? 'Show' : 'Hide' }`); }); const hasToolbar = vue.computed(() => props.toolbar && props.toolbar.length !== 0); const keys = vue.computed(() => { const k = {}, add = btn => { if (btn.key) { k[ btn.key ] = { cmd: btn.cmd, param: btn.param }; } }; buttons.value.forEach(group => { group.forEach(token => { if (token.options) { token.options.forEach(add); } else { add(token); } }); }); return k }); const innerStyle = vue.computed(() => ( inFullscreen.value ? props.contentStyle : [ { minHeight: props.minHeight, height: props.height, maxHeight: props.maxHeight }, props.contentStyle ] )); const classes = vue.computed(() => `q-editor q-editor--${ isViewingSource.value === true ? 'source' : 'default' }` + (props.disable === true ? ' disabled' : '') + (inFullscreen.value === true ? ' fullscreen column' : '') + (props.square === true ? ' q-editor--square no-border-radius' : '') + (props.flat === true ? ' q-editor--flat' : '') + (props.dense === true ? ' q-editor--dense' : '') + (isDark.value === true ? ' q-editor--dark q-dark' : '') ); const innerClass = vue.computed(() => ([ props.contentClass, 'q-editor__content', { col: inFullscreen.value, 'overflow-auto': inFullscreen.value || props.maxHeight } ])); const attributes = vue.computed(() => ( props.disable === true ? { 'aria-disabled': 'true' } : (props.readonly === true ? { 'aria-readonly': 'true' } : {}) )); function onInput () { if (contentRef.value !== null) { const prop = `inner${ isViewingSource.value === true ? 'Text' : 'HTML' }`; const val = contentRef.value[ prop ]; if (val !== props.modelValue) { lastEmit = val; emit('update:modelValue', val); } } } function onKeydown (e) { emit('keydown', e); if (e.ctrlKey !== true || shouldIgnoreKey(e) === true) { refreshToolbar(); return } const key = e.keyCode; const target = keys.value[ key ]; if (target !== void 0) { const { cmd, param } = target; stopAndPrevent(e); runCmd(cmd, param, false); } } function onClick (e) { refreshToolbar(); emit('click', e); } function onBlur (e) { if (contentRef.value !== null) { const { scrollTop, scrollHeight } = contentRef.value; offsetBottom = scrollHeight - scrollTop; } eVm.caret.save(); emit('blur', e); } function onFocus (e) { vue.nextTick(() => { if (contentRef.value !== null && offsetBottom !== void 0) { contentRef.value.scrollTop = contentRef.value.scrollHeight - offsetBottom; } }); emit('focus', e); } function onFocusin (e) { const root = rootRef.value; if ( root !== null && root.contains(e.target) === true && ( e.relatedTarget === null || root.contains(e.relatedTarget) !== true ) ) { const prop = `inner${ isViewingSource.value === true ? 'Text' : 'HTML' }`; eVm.caret.restorePosition(contentRef.value[ prop ].length); refreshToolbar(); } } function onFocusout (e) { const root = rootRef.value; if ( root !== null && root.contains(e.target) === true && ( e.relatedTarget === null || root.contains(e.relatedTarget) !== true ) ) { eVm.caret.savePosition(); refreshToolbar(); } } function onPointerStart () { offsetBottom = void 0; } function onSelectionchange (e) { eVm.caret.save(); } function setContent (v, restorePosition) { if (contentRef.value !== null) { if (restorePosition === true) { eVm.caret.savePosition(); } const prop = `inner${ isViewingSource.value === true ? 'Text' : 'HTML' }`; contentRef.value[ prop ] = v; if (restorePosition === true) { eVm.caret.restorePosition(contentRef.value[ prop ].length); refreshToolbar(); } } } function runCmd (cmd, param, update = true) { focus(); eVm.caret.restore(); eVm.caret.apply(cmd, param, () => { focus(); eVm.caret.save(); if (update) { refreshToolbar(); } }); } function refreshToolbar () { setTimeout(() => { editLinkUrl.value = null; proxy.$forceUpdate(); }, 1); } function focus () { addFocusFn(() => { contentRef.value !== null && contentRef.value.focus({ preventScroll: true }); }); } function getContentEl () { return contentRef.value } vue.onMounted(() => { eVm.caret = proxy.caret = new Caret(contentRef.value, eVm); setContent(props.modelValue); refreshToolbar(); document.addEventListener('selectionchange', onSelectionchange); }); vue.onBeforeUnmount(() => { document.removeEventListener('selectionchange', onSelectionchange); }); // expose public methods Object.assign(proxy, { runCmd, refreshToolbar, focus, getContentEl }); return () => { let toolbars; if (hasToolbar.value) { const bars = [ vue.h('div', { key: 'qedt_top', class: 'q-editor__toolbar row no-wrap scroll-x' + toolbarBackgroundClass.value }, getToolbar(eVm)) ]; editLinkUrl.value !== null && bars.push( vue.h('div', { key: 'qedt_btm', class: 'q-editor__toolbar row no-wrap items-center scroll-x' + toolbarBackgroundClass.value }, getLinkEditor(eVm)) ); toolbars = vue.h('div', { key: 'toolbar_ctainer', class: 'q-editor__toolbars-container' }, bars); } return vue.h('div', { ref: rootRef, class: classes.value, style: { height: inFullscreen.value === true ? '100%' : null }, ...attributes.value, onFocusin, onFocusout }, [ toolbars, vue.h('div', { ref: contentRef, style: innerStyle.value, class: innerClass.value, contenteditable: editable.value, placeholder: props.placeholder, ...({}), ...splitAttrs.listeners.value, onInput, onKeydown, onClick, onBlur, onFocus, // clean saved scroll position onMousedown: onPointerStart, onTouchstartPassive: onPointerStart }) ]) } } }); var QItemLabel = createComponent({ name: 'QItemLabel', props: { overline: Boolean, caption: Boolean, header: Boolean, lines: [ Number, String ] }, setup (props, { slots }) { const parsedLines = vue.computed(() => parseInt(props.lines, 10)); const classes = vue.computed(() => 'q-item__label' + (props.overline === true ? ' q-item__label--overline text-overline' : '') + (props.caption === true ? ' q-item__label--caption text-caption' : '') + (props.header === true ? ' q-item__label--header' : '') + (parsedLines.value === 1 ? ' ellipsis' : '') ); const style = vue.computed(() => { return props.lines !== void 0 && parsedLines.value > 1 ? { overflow: 'hidden', display: '-webkit-box', '-webkit-box-orient': 'vertical', '-webkit-line-clamp': parsedLines.value } : null }); return () => vue.h('div', { style: style.value, class: classes.value }, hSlot(slots.default)) } }); var QSlideTransition = createComponent({ name: 'QSlideTransition', props: { appear: Boolean, duration: { type: Number, default: 300 } }, emits: [ 'show', 'hide' ], setup (props, { slots, emit }) { let animating = false, doneFn, element; let timer = null, timerFallback = null, animListener, lastEvent; function cleanup () { doneFn && doneFn(); doneFn = null; animating = false; if (timer !== null) { clearTimeout(timer); timer = null; } if (timerFallback !== null) { clearTimeout(timerFallback); timerFallback = null; } element !== void 0 && element.removeEventListener('transitionend', animListener); animListener = null; } function begin (el, height, done) { // here overflowY is 'hidden' if (height !== void 0) { el.style.height = `${ height }px`; } el.style.transition = `height ${ props.duration }ms cubic-bezier(.25, .8, .50, 1)`; animating = true; doneFn = done; } function end (el, event) { el.style.overflowY = null; el.style.height = null; el.style.transition = null; cleanup(); event !== lastEvent && emit(event); } function onEnter (el, done) { let pos = 0; element = el; // if animationg overflowY is already 'hidden' if (animating === true) { cleanup(); pos = el.offsetHeight === el.scrollHeight ? 0 : void 0; } else { lastEvent = 'hide'; el.style.overflowY = 'hidden'; } begin(el, pos, done); timer = setTimeout(() => { timer = null; el.style.height = `${ el.scrollHeight }px`; animListener = evt => { timerFallback = null; if (Object(evt) !== evt || evt.target === el) { end(el, 'show'); } }; el.addEventListener('transitionend', animListener); timerFallback = setTimeout(animListener, props.duration * 1.1); }, 100); } function onLeave (el, done) { let pos; element = el; if (animating === true) { cleanup(); } else { lastEvent = 'show'; // we need to set overflowY 'hidden' before calculating the height // or else we get small differences el.style.overflowY = 'hidden'; pos = el.scrollHeight; } begin(el, pos, done); timer = setTimeout(() => { timer = null; el.style.height = 0; animListener = evt => { timerFallback = null; if (Object(evt) !== evt || evt.target === el) { end(el, 'hide'); } }; el.addEventListener('transitionend', animListener); timerFallback = setTimeout(animListener, props.duration * 1.1); }, 100); } vue.onBeforeUnmount(() => { animating === true && cleanup(); }); return () => vue.h(vue.Transition, { css: false, appear: props.appear, onEnter, onLeave }, slots.default) } }); const insetMap = { true: 'inset', item: 'item-inset', 'item-thumbnail': 'item-thumbnail-inset' }; const margins = { xs: 2, sm: 4, md: 8, lg: 16, xl: 24 }; var QSeparator = createComponent({ name: 'QSeparator', props: { ...useDarkProps, spaced: [ Boolean, String ], inset: [ Boolean, String ], vertical: Boolean, color: String, size: String }, setup (props) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const orientation = vue.computed(() => ( props.vertical === true ? 'vertical' : 'horizontal' )); const orientClass = vue.computed(() => ` q-separator--${ orientation.value }`); const insetClass = vue.computed(() => ( props.inset !== false ? `${ orientClass.value }-${ insetMap[ props.inset ] }` : '' )); const classes = vue.computed(() => `q-separator${ orientClass.value }${ insetClass.value }` + (props.color !== void 0 ? ` bg-${ props.color }` : '') + (isDark.value === true ? ' q-separator--dark' : '') ); const style = vue.computed(() => { const acc = {}; if (props.size !== void 0) { acc[ props.vertical === true ? 'width' : 'height' ] = props.size; } if (props.spaced !== false) { const size = props.spaced === true ? `${ margins.md }px` : props.spaced in margins ? `${ margins[ props.spaced ] }px` : props.spaced; const dir = props.vertical === true ? [ 'Left', 'Right' ] : [ 'Top', 'Bottom' ]; acc[ `margin${ dir[ 0 ] }` ] = acc[ `margin${ dir[ 1 ] }` ] = size; } return acc }); return () => vue.h('hr', { class: classes.value, style: style.value, 'aria-orientation': orientation.value }) } }); const itemGroups = vue.shallowReactive({}); const LINK_PROPS = Object.keys(useRouterLinkProps); var QExpansionItem = createComponent({ name: 'QExpansionItem', props: { ...useRouterLinkProps, ...useModelToggleProps, ...useDarkProps, icon: String, label: String, labelLines: [ Number, String ], caption: String, captionLines: [ Number, String ], dense: Boolean, toggleAriaLabel: String, expandIcon: String, expandedIcon: String, expandIconClass: [ Array, String, Object ], duration: Number, headerInsetLevel: Number, contentInsetLevel: Number, expandSeparator: Boolean, defaultOpened: Boolean, hideExpandIcon: Boolean, expandIconToggle: Boolean, switchToggleSide: Boolean, denseToggle: Boolean, group: String, popup: Boolean, headerStyle: [ Array, String, Object ], headerClass: [ Array, String, Object ] }, emits: [ ...useModelToggleEmits, 'click', 'afterShow', 'afterHide' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const showing = vue.ref( props.modelValue !== null ? props.modelValue : props.defaultOpened ); const blurTargetRef = vue.ref(null); const targetUid = uid$3(); const { show, hide, toggle } = useModelToggle({ showing }); let uniqueId, exitGroup; const classes = vue.computed(() => 'q-expansion-item q-item-type' + ` q-expansion-item--${ showing.value === true ? 'expanded' : 'collapsed' }` + ` q-expansion-item--${ props.popup === true ? 'popup' : 'standard' }` ); const contentStyle = vue.computed(() => { if (props.contentInsetLevel === void 0) { return null } const dir = $q.lang.rtl === true ? 'Right' : 'Left'; return { [ 'padding' + dir ]: (props.contentInsetLevel * 56) + 'px' } }); const hasLink = vue.computed(() => props.disable !== true && ( props.href !== void 0 || (props.to !== void 0 && props.to !== null && props.to !== '') ) ); const linkProps = vue.computed(() => { const acc = {}; LINK_PROPS.forEach(key => { acc[ key ] = props[ key ]; }); return acc }); const isClickable = vue.computed(() => hasLink.value === true || props.expandIconToggle !== true ); const expansionIcon = vue.computed(() => ( props.expandedIcon !== void 0 && showing.value === true ? props.expandedIcon : props.expandIcon || $q.iconSet.expansionItem[ props.denseToggle === true ? 'denseIcon' : 'icon' ] )); const activeToggleIcon = vue.computed(() => props.disable !== true && (hasLink.value === true || props.expandIconToggle === true) ); const headerSlotScope = vue.computed(() => ({ expanded: showing.value === true, detailsId: props.targetUid, toggle, show, hide })); const toggleAriaAttrs = vue.computed(() => { const toggleAriaLabel = props.toggleAriaLabel !== void 0 ? props.toggleAriaLabel : $q.lang.label[ showing.value === true ? 'collapse' : 'expand' ](props.label); return { role: 'button', 'aria-expanded': showing.value === true ? 'true' : 'false', 'aria-controls': targetUid, 'aria-label': toggleAriaLabel } }); vue.watch(() => props.group, name => { exitGroup !== void 0 && exitGroup(); name !== void 0 && enterGroup(); }); function onHeaderClick (e) { hasLink.value !== true && toggle(e); emit('click', e); } function toggleIconKeyboard (e) { e.keyCode === 13 && toggleIcon(e, true); } function toggleIcon (e, keyboard) { keyboard !== true && blurTargetRef.value !== null && blurTargetRef.value.focus(); toggle(e); stopAndPrevent(e); } function onShow () { emit('afterShow'); } function onHide () { emit('afterHide'); } function enterGroup () { if (uniqueId === void 0) { uniqueId = uid$3(); } if (showing.value === true) { itemGroups[ props.group ] = uniqueId; } const show = vue.watch(showing, val => { if (val === true) { itemGroups[ props.group ] = uniqueId; } else if (itemGroups[ props.group ] === uniqueId) { delete itemGroups[ props.group ]; } }); const group = vue.watch( () => itemGroups[ props.group ], (val, oldVal) => { if (oldVal === uniqueId && val !== void 0 && val !== uniqueId) { hide(); } } ); exitGroup = () => { show(); group(); if (itemGroups[ props.group ] === uniqueId) { delete itemGroups[ props.group ]; } exitGroup = void 0; }; } function getToggleIcon () { const data = { class: [ 'q-focusable relative-position cursor-pointer' + `${ props.denseToggle === true && props.switchToggleSide === true ? ' items-end' : '' }`, props.expandIconClass ], side: props.switchToggleSide !== true, avatar: props.switchToggleSide }; const child = [ vue.h(QIcon, { class: 'q-expansion-item__toggle-icon' + (props.expandedIcon === void 0 && showing.value === true ? ' q-expansion-item__toggle-icon--rotated' : ''), name: expansionIcon.value }) ]; if (activeToggleIcon.value === true) { Object.assign(data, { tabindex: 0, ...toggleAriaAttrs.value, onClick: toggleIcon, onKeyup: toggleIconKeyboard }); child.unshift( vue.h('div', { ref: blurTargetRef, class: 'q-expansion-item__toggle-focus q-icon q-focus-helper q-focus-helper--rounded', tabindex: -1 }) ); } return vue.h(QItemSection, data, () => child) } function getHeaderChild () { let child; if (slots.header !== void 0) { child = [].concat(slots.header(headerSlotScope.value)); } else { child = [ vue.h(QItemSection, () => [ vue.h(QItemLabel, { lines: props.labelLines }, () => props.label || ''), props.caption ? vue.h(QItemLabel, { lines: props.captionLines, caption: true }, () => props.caption) : null ]) ]; props.icon && child[ props.switchToggleSide === true ? 'push' : 'unshift' ]( vue.h(QItemSection, { side: props.switchToggleSide === true, avatar: props.switchToggleSide !== true }, () => vue.h(QIcon, { name: props.icon })) ); } if (props.disable !== true && props.hideExpandIcon !== true) { child[ props.switchToggleSide === true ? 'unshift' : 'push' ]( getToggleIcon() ); } return child } function getHeader () { const data = { ref: 'item', style: props.headerStyle, class: props.headerClass, dark: isDark.value, disable: props.disable, dense: props.dense, insetLevel: props.headerInsetLevel }; if (isClickable.value === true) { data.clickable = true; data.onClick = onHeaderClick; Object.assign( data, hasLink.value === true ? linkProps.value : toggleAriaAttrs.value ); } return vue.h(QItem, data, getHeaderChild) } function getTransitionChild () { return vue.withDirectives( vue.h('div', { key: 'e-content', class: 'q-expansion-item__content relative-position', style: contentStyle.value, id: targetUid }, hSlot(slots.default)), [ [ vue.vShow, showing.value ] ] ) } function getContent () { const node = [ getHeader(), vue.h(QSlideTransition, { duration: props.duration, onShow, onHide }, getTransitionChild) ]; if (props.expandSeparator === true) { node.push( vue.h(QSeparator, { class: 'q-expansion-item__border q-expansion-item__border--top absolute-top', dark: isDark.value }), vue.h(QSeparator, { class: 'q-expansion-item__border q-expansion-item__border--bottom absolute-bottom', dark: isDark.value }) ); } return node } props.group !== void 0 && enterGroup(); vue.onBeforeUnmount(() => { exitGroup !== void 0 && exitGroup(); }); return () => vue.h('div', { class: classes.value }, [ vue.h('div', { class: 'q-expansion-item__container relative-position' }, getContent()) ]) } }); const labelPositions = [ 'top', 'right', 'bottom', 'left' ]; const useFabProps = { type: { type: String, default: 'a' }, outline: Boolean, push: Boolean, flat: Boolean, unelevated: Boolean, color: String, textColor: String, glossy: Boolean, square: Boolean, padding: String, label: { type: [ String, Number ], default: '' }, labelPosition: { type: String, default: 'right', validator: v => labelPositions.includes(v) }, externalLabel: Boolean, hideLabel: { type: Boolean }, labelClass: [ Array, String, Object ], labelStyle: [ Array, String, Object ], disable: Boolean, tabindex: [ Number, String ] }; function useFab (props, showing) { return { formClass: vue.computed(() => `q-fab--form-${ props.square === true ? 'square' : 'rounded' }` ), stacked: vue.computed(() => props.externalLabel === false && [ 'top', 'bottom' ].includes(props.labelPosition) ), labelProps: vue.computed(() => { if (props.externalLabel === true) { const hideLabel = props.hideLabel === null ? showing.value === false : props.hideLabel; return { action: 'push', data: { class: [ props.labelClass, 'q-fab__label q-tooltip--style q-fab__label--external' + ` q-fab__label--external-${ props.labelPosition }` + (hideLabel === true ? ' q-fab__label--external-hidden' : '') ], style: props.labelStyle } } } return { action: [ 'left', 'top' ].includes(props.labelPosition) ? 'unshift' : 'push', data: { class: [ props.labelClass, `q-fab__label q-fab__label--internal q-fab__label--internal-${ props.labelPosition }` + (props.hideLabel === true ? ' q-fab__label--internal-hidden' : '') ], style: props.labelStyle } } }) } } const directions = [ 'up', 'right', 'down', 'left' ]; const alignValues = [ 'left', 'center', 'right' ]; var QFab = createComponent({ name: 'QFab', props: { ...useFabProps, ...useModelToggleProps, icon: String, activeIcon: String, hideIcon: Boolean, hideLabel: { default: null }, direction: { type: String, default: 'right', validator: v => directions.includes(v) }, persistent: Boolean, verticalActionsAlign: { type: String, default: 'center', validator: v => alignValues.includes(v) } }, emits: useModelToggleEmits, setup (props, { slots }) { const triggerRef = vue.ref(null); const showing = vue.ref(props.modelValue === true); const targetUid = uid$3(); const { proxy: { $q } } = vue.getCurrentInstance(); const { formClass, labelProps } = useFab(props, showing); const hideOnRouteChange = vue.computed(() => props.persistent !== true); const { hide, toggle } = useModelToggle({ showing, hideOnRouteChange }); const slotScope = vue.computed(() => ({ opened: showing.value })); const classes = vue.computed(() => 'q-fab z-fab row inline justify-center' + ` q-fab--align-${ props.verticalActionsAlign } ${ formClass.value }` + (showing.value === true ? ' q-fab--opened' : ' q-fab--closed') ); const actionClass = vue.computed(() => 'q-fab__actions flex no-wrap inline' + ` q-fab__actions--${ props.direction }` + ` q-fab__actions--${ showing.value === true ? 'opened' : 'closed' }` ); const actionAttrs = vue.computed(() => { const attrs = { id: targetUid, role: 'menu' }; if (showing.value !== true) { attrs[ 'aria-hidden' ] = 'true'; } return attrs }); const iconHolderClass = vue.computed(() => 'q-fab__icon-holder ' + ` q-fab__icon-holder--${ showing.value === true ? 'opened' : 'closed' }` ); function getIcon (kebab, camel) { const slotFn = slots[ kebab ]; const classes = `q-fab__${ kebab } absolute-full`; return slotFn === void 0 ? vue.h(QIcon, { class: classes, name: props[ camel ] || $q.iconSet.fab[ camel ] }) : vue.h('div', { class: classes }, slotFn(slotScope.value)) } function getTriggerContent () { const child = []; props.hideIcon !== true && child.push( vue.h('div', { class: iconHolderClass.value }, [ getIcon('icon', 'icon'), getIcon('active-icon', 'activeIcon') ]) ); if (props.label !== '' || slots.label !== void 0) { child[ labelProps.value.action ]( vue.h('div', labelProps.value.data, slots.label !== void 0 ? slots.label(slotScope.value) : [ props.label ]) ); } return hMergeSlot(slots.tooltip, child) } vue.provide(fabKey, { showing, onChildClick (evt) { hide(evt); if (triggerRef.value !== null) { triggerRef.value.$el.focus(); } } }); return () => vue.h('div', { class: classes.value }, [ vue.h(QBtn, { ref: triggerRef, class: formClass.value, ...props, noWrap: true, stack: props.stacked, align: void 0, icon: void 0, label: void 0, noCaps: true, fab: true, 'aria-expanded': showing.value === true ? 'true' : 'false', 'aria-haspopup': 'true', 'aria-controls': targetUid, onClick: toggle }, getTriggerContent), vue.h('div', { class: actionClass.value, ...actionAttrs.value }, hSlot(slots.default)) ]) } }); const anchorMap = { start: 'self-end', center: 'self-center', end: 'self-start' }; const anchorValues = Object.keys(anchorMap); var QFabAction = createComponent({ name: 'QFabAction', props: { ...useFabProps, icon: { type: String, default: '' }, anchor: { type: String, validator: v => anchorValues.includes(v) }, to: [ String, Object ], replace: Boolean }, emits: [ 'click' ], setup (props, { slots, emit }) { const $fab = vue.inject(fabKey, () => ({ showing: { value: true }, onChildClick: noop })); const { formClass, labelProps } = useFab(props, $fab.showing); const classes = vue.computed(() => { const align = anchorMap[ props.anchor ]; return formClass.value + (align !== void 0 ? ` ${ align }` : '') }); const isDisabled = vue.computed(() => props.disable === true || $fab.showing.value !== true ); function click (e) { $fab.onChildClick(e); emit('click', e); } function getContent () { const child = []; if (slots.icon !== void 0) { child.push(slots.icon()); } else if (props.icon !== '') { child.push( vue.h(QIcon, { name: props.icon }) ); } if (props.label !== '' || slots.label !== void 0) { child[ labelProps.value.action ]( vue.h('div', labelProps.value.data, slots.label !== void 0 ? slots.label() : [ props.label ]) ); } return hMergeSlot(slots.default, child) } // expose public methods const vm = vue.getCurrentInstance(); Object.assign(vm.proxy, { click }); return () => vue.h(QBtn, { class: classes.value, ...props, noWrap: true, stack: props.stacked, icon: void 0, label: void 0, noCaps: true, fabMini: true, disable: isDisabled.value, onClick: click }, getContent) } }); function useFormChild ({ validate, resetValidation, requiresQForm }) { const $form = vue.inject(formKey, false); if ($form !== false) { const { props, proxy } = vue.getCurrentInstance(); // export public method (so it can be used in QForm) Object.assign(proxy, { validate, resetValidation }); vue.watch(() => props.disable, val => { if (val === true) { typeof resetValidation === 'function' && resetValidation(); $form.unbindComponent(proxy); } else { $form.bindComponent(proxy); } }); vue.onMounted(() => { // register to parent QForm props.disable !== true && $form.bindComponent(proxy); }); vue.onBeforeUnmount(() => { // un-register from parent QForm props.disable !== true && $form.unbindComponent(proxy); }); } else if (requiresQForm === true) { console.error('Parent QForm not found on useFormChild()!'); } } const lazyRulesValues = [ true, false, 'ondemand' ]; const useValidateProps = { modelValue: {}, error: { type: Boolean, default: null }, errorMessage: String, noErrorIcon: Boolean, rules: Array, reactiveRules: Boolean, lazyRules: { type: [ Boolean, String ], validator: v => lazyRulesValues.includes(v) } }; function useValidate (focused, innerLoading) { const { props, proxy } = vue.getCurrentInstance(); const innerError = vue.ref(false); const innerErrorMessage = vue.ref(null); const isDirtyModel = vue.ref(null); useFormChild({ validate, resetValidation }); let validateIndex = 0, unwatchRules; const hasRules = vue.computed(() => props.rules !== void 0 && props.rules !== null && props.rules.length !== 0 ); const hasActiveRules = vue.computed(() => props.disable !== true && hasRules.value === true ); const hasError = vue.computed(() => props.error === true || innerError.value === true ); const errorMessage = vue.computed(() => ( typeof props.errorMessage === 'string' && props.errorMessage.length !== 0 ? props.errorMessage : innerErrorMessage.value )); vue.watch(() => props.modelValue, () => { validateIfNeeded(); }); vue.watch(() => props.reactiveRules, val => { if (val === true) { if (unwatchRules === void 0) { unwatchRules = vue.watch(() => props.rules, () => { validateIfNeeded(true); }); } } else if (unwatchRules !== void 0) { unwatchRules(); unwatchRules = void 0; } }, { immediate: true }); vue.watch(focused, val => { if (val === true) { if (isDirtyModel.value === null) { isDirtyModel.value = false; } } else if (isDirtyModel.value === false) { isDirtyModel.value = true; if ( hasActiveRules.value === true && props.lazyRules !== 'ondemand' // Don't re-trigger if it's already in progress; // It might mean that focus switched to submit btn and // QForm's submit() has been called already (ENTER key) && innerLoading.value === false ) { debouncedValidate(); } } }); function resetValidation () { validateIndex++; innerLoading.value = false; isDirtyModel.value = null; innerError.value = false; innerErrorMessage.value = null; debouncedValidate.cancel(); } /* * Return value * - true (validation succeeded) * - false (validation failed) * - Promise (pending async validation) */ function validate (val = props.modelValue) { if (hasActiveRules.value !== true) { return true } const index = ++validateIndex; const setDirty = innerLoading.value !== true ? () => { isDirtyModel.value = true; } : () => {}; const update = (err, msg) => { err === true && setDirty(); innerError.value = err; innerErrorMessage.value = msg || null; innerLoading.value = false; }; const promises = []; for (let i = 0; i < props.rules.length; i++) { const rule = props.rules[ i ]; let res; if (typeof rule === 'function') { res = rule(val, testPattern); } else if (typeof rule === 'string' && testPattern[ rule ] !== void 0) { res = testPattern[ rule ](val); } if (res === false || typeof res === 'string') { update(true, res); return false } else if (res !== true && res !== void 0) { promises.push(res); } } if (promises.length === 0) { update(false); return true } innerLoading.value = true; return Promise.all(promises).then( res => { if (res === void 0 || Array.isArray(res) === false || res.length === 0) { index === validateIndex && update(false); return true } const msg = res.find(r => r === false || typeof r === 'string'); index === validateIndex && update(msg !== void 0, msg); return msg === void 0 }, e => { if (index === validateIndex) { console.error(e); update(true); } return false } ) } function validateIfNeeded (changedRules) { if ( hasActiveRules.value === true && props.lazyRules !== 'ondemand' && (isDirtyModel.value === true || (props.lazyRules !== true && changedRules !== true)) ) { debouncedValidate(); } } const debouncedValidate = debounce(validate, 0); vue.onBeforeUnmount(() => { unwatchRules !== void 0 && unwatchRules(); debouncedValidate.cancel(); }); // expose public methods & props Object.assign(proxy, { resetValidation, validate }); injectProp(proxy, 'hasError', () => hasError.value); return { isDirtyModel, hasRules, hasError, errorMessage, validate, resetValidation } } function getTargetUid (val) { return val === void 0 ? `f_${ uid$3() }` : val } function fieldValueIsFilled (val) { return val !== void 0 && val !== null && ('' + val).length !== 0 } const useFieldProps = { ...useDarkProps, ...useValidateProps, label: String, stackLabel: Boolean, hint: String, hideHint: Boolean, prefix: String, suffix: String, labelColor: String, color: String, bgColor: String, filled: Boolean, outlined: Boolean, borderless: Boolean, standout: [ Boolean, String ], square: Boolean, loading: Boolean, labelSlot: Boolean, bottomSlots: Boolean, hideBottomSpace: Boolean, rounded: Boolean, dense: Boolean, itemAligned: Boolean, counter: Boolean, clearable: Boolean, clearIcon: String, disable: Boolean, readonly: Boolean, autofocus: Boolean, for: String, maxlength: [ Number, String ] }; const useFieldEmits = [ 'update:modelValue', 'clear', 'focus', 'blur', 'popupShow', 'popupHide' ]; function useFieldState () { const { props, attrs, proxy, vnode } = vue.getCurrentInstance(); const isDark = useDark(props, proxy.$q); return { isDark, editable: vue.computed(() => props.disable !== true && props.readonly !== true ), innerLoading: vue.ref(false), focused: vue.ref(false), hasPopupOpen: false, splitAttrs: useSplitAttrs(attrs, vnode), targetUid: vue.ref(getTargetUid(props.for)), rootRef: vue.ref(null), targetRef: vue.ref(null), controlRef: vue.ref(null) /** * user supplied additionals: * innerValue - computed * floatingLabel - computed * inputRef - computed * fieldClass - computed * hasShadow - computed * controlEvents - Object with fn(e) * getControl - fn * getInnerAppend - fn * getControlChild - fn * getShadowControl - fn * showPopup - fn */ } } function useField (state) { const { props, emit, slots, attrs, proxy } = vue.getCurrentInstance(); const { $q } = proxy; let focusoutTimer = null; if (state.hasValue === void 0) { state.hasValue = vue.computed(() => fieldValueIsFilled(props.modelValue)); } if (state.emitValue === void 0) { state.emitValue = value => { emit('update:modelValue', value); }; } if (state.controlEvents === void 0) { state.controlEvents = { onFocusin: onControlFocusin, onFocusout: onControlFocusout }; } Object.assign(state, { clearValue, onControlFocusin, onControlFocusout, focus }); if (state.computedCounter === void 0) { state.computedCounter = vue.computed(() => { if (props.counter !== false) { const len = typeof props.modelValue === 'string' || typeof props.modelValue === 'number' ? ('' + props.modelValue).length : (Array.isArray(props.modelValue) === true ? props.modelValue.length : 0); const max = props.maxlength !== void 0 ? props.maxlength : props.maxValues; return len + (max !== void 0 ? ' / ' + max : '') } }); } const { isDirtyModel, hasRules, hasError, errorMessage, resetValidation } = useValidate(state.focused, state.innerLoading); const floatingLabel = state.floatingLabel !== void 0 ? vue.computed(() => props.stackLabel === true || state.focused.value === true || state.floatingLabel.value === true) : vue.computed(() => props.stackLabel === true || state.focused.value === true || state.hasValue.value === true); const shouldRenderBottom = vue.computed(() => props.bottomSlots === true || props.hint !== void 0 || hasRules.value === true || props.counter === true || props.error !== null ); const styleType = vue.computed(() => { if (props.filled === true) { return 'filled' } if (props.outlined === true) { return 'outlined' } if (props.borderless === true) { return 'borderless' } if (props.standout) { return 'standout' } return 'standard' }); const classes = vue.computed(() => `q-field row no-wrap items-start q-field--${ styleType.value }` + (state.fieldClass !== void 0 ? ` ${ state.fieldClass.value }` : '') + (props.rounded === true ? ' q-field--rounded' : '') + (props.square === true ? ' q-field--square' : '') + (floatingLabel.value === true ? ' q-field--float' : '') + (hasLabel.value === true ? ' q-field--labeled' : '') + (props.dense === true ? ' q-field--dense' : '') + (props.itemAligned === true ? ' q-field--item-aligned q-item-type' : '') + (state.isDark.value === true ? ' q-field--dark' : '') + (state.getControl === void 0 ? ' q-field--auto-height' : '') + (state.focused.value === true ? ' q-field--focused' : '') + (hasError.value === true ? ' q-field--error' : '') + (hasError.value === true || state.focused.value === true ? ' q-field--highlighted' : '') + (props.hideBottomSpace !== true && shouldRenderBottom.value === true ? ' q-field--with-bottom' : '') + (props.disable === true ? ' q-field--disabled' : (props.readonly === true ? ' q-field--readonly' : '')) ); const contentClass = vue.computed(() => 'q-field__control relative-position row no-wrap' + (props.bgColor !== void 0 ? ` bg-${ props.bgColor }` : '') + ( hasError.value === true ? ' text-negative' : ( typeof props.standout === 'string' && props.standout.length !== 0 && state.focused.value === true ? ` ${ props.standout }` : (props.color !== void 0 ? ` text-${ props.color }` : '') ) ) ); const hasLabel = vue.computed(() => props.labelSlot === true || props.label !== void 0 ); const labelClass = vue.computed(() => 'q-field__label no-pointer-events absolute ellipsis' + (props.labelColor !== void 0 && hasError.value !== true ? ` text-${ props.labelColor }` : '') ); const controlSlotScope = vue.computed(() => ({ id: state.targetUid.value, editable: state.editable.value, focused: state.focused.value, floatingLabel: floatingLabel.value, modelValue: props.modelValue, emitValue: state.emitValue })); const attributes = vue.computed(() => { const acc = { for: state.targetUid.value }; if (props.disable === true) { acc[ 'aria-disabled' ] = 'true'; } else if (props.readonly === true) { acc[ 'aria-readonly' ] = 'true'; } return acc }); vue.watch(() => props.for, val => { // don't transform targetUid into a computed // prop as it will break SSR state.targetUid.value = getTargetUid(val); }); function focusHandler () { const el = document.activeElement; let target = state.targetRef !== void 0 && state.targetRef.value; if (target && (el === null || el.id !== state.targetUid.value)) { target.hasAttribute('tabindex') === true || (target = target.querySelector('[tabindex]')); if (target && target !== el) { target.focus({ preventScroll: true }); } } } function focus () { addFocusFn(focusHandler); } function blur () { removeFocusFn(focusHandler); const el = document.activeElement; if (el !== null && state.rootRef.value.contains(el)) { el.blur(); } } function onControlFocusin (e) { if (focusoutTimer !== null) { clearTimeout(focusoutTimer); focusoutTimer = null; } if (state.editable.value === true && state.focused.value === false) { state.focused.value = true; emit('focus', e); } } function onControlFocusout (e, then) { focusoutTimer !== null && clearTimeout(focusoutTimer); focusoutTimer = setTimeout(() => { focusoutTimer = null; if ( document.hasFocus() === true && ( state.hasPopupOpen === true || state.controlRef === void 0 || state.controlRef.value === null || state.controlRef.value.contains(document.activeElement) !== false ) ) { return } if (state.focused.value === true) { state.focused.value = false; emit('blur', e); } then !== void 0 && then(); }); } function clearValue (e) { // prevent activating the field but keep focus on desktop stopAndPrevent(e); if ($q.platform.is.mobile !== true) { const el = (state.targetRef !== void 0 && state.targetRef.value) || state.rootRef.value; el.focus(); } else if (state.rootRef.value.contains(document.activeElement) === true) { document.activeElement.blur(); } if (props.type === 'file') { // TODO vue3 // do not let focus be triggered // as it will make the native file dialog // appear for another selection state.inputRef.value.value = null; } emit('update:modelValue', null); emit('clear', props.modelValue); vue.nextTick(() => { resetValidation(); if ($q.platform.is.mobile !== true) { isDirtyModel.value = false; } }); } function getContent () { const node = []; slots.prepend !== void 0 && node.push( vue.h('div', { class: 'q-field__prepend q-field__marginal row no-wrap items-center', key: 'prepend', onClick: prevent }, slots.prepend()) ); node.push( vue.h('div', { class: 'q-field__control-container col relative-position row no-wrap q-anchor--skip' }, getControlContainer()) ); hasError.value === true && props.noErrorIcon === false && node.push( getInnerAppendNode('error', [ vue.h(QIcon, { name: $q.iconSet.field.error, color: 'negative' }) ]) ); if (props.loading === true || state.innerLoading.value === true) { node.push( getInnerAppendNode( 'inner-loading-append', slots.loading !== void 0 ? slots.loading() : [ vue.h(QSpinner, { color: props.color }) ] ) ); } else if (props.clearable === true && state.hasValue.value === true && state.editable.value === true) { node.push( getInnerAppendNode('inner-clearable-append', [ vue.h(QIcon, { class: 'q-field__focusable-action', tag: 'button', name: props.clearIcon || $q.iconSet.field.clear, tabindex: 0, type: 'button', 'aria-hidden': null, role: null, onClick: clearValue }) ]) ); } slots.append !== void 0 && node.push( vue.h('div', { class: 'q-field__append q-field__marginal row no-wrap items-center', key: 'append', onClick: prevent }, slots.append()) ); state.getInnerAppend !== void 0 && node.push( getInnerAppendNode('inner-append', state.getInnerAppend()) ); state.getControlChild !== void 0 && node.push( state.getControlChild() ); return node } function getControlContainer () { const node = []; props.prefix !== void 0 && props.prefix !== null && node.push( vue.h('div', { class: 'q-field__prefix no-pointer-events row items-center' }, props.prefix) ); if (state.getShadowControl !== void 0 && state.hasShadow.value === true) { node.push( state.getShadowControl() ); } if (state.getControl !== void 0) { node.push(state.getControl()); } // internal usage only: else if (slots.rawControl !== void 0) { node.push(slots.rawControl()); } else if (slots.control !== void 0) { node.push( vue.h('div', { ref: state.targetRef, class: 'q-field__native row', tabindex: -1, ...state.splitAttrs.attributes.value, 'data-autofocus': props.autofocus === true || void 0 }, slots.control(controlSlotScope.value)) ); } hasLabel.value === true && node.push( vue.h('div', { class: labelClass.value }, hSlot(slots.label, props.label)) ); props.suffix !== void 0 && props.suffix !== null && node.push( vue.h('div', { class: 'q-field__suffix no-pointer-events row items-center' }, props.suffix) ); return node.concat(hSlot(slots.default)) } function getBottom () { let msg, key; if (hasError.value === true) { if (errorMessage.value !== null) { msg = [ vue.h('div', { role: 'alert' }, errorMessage.value) ]; key = `q--slot-error-${ errorMessage.value }`; } else { msg = hSlot(slots.error); key = 'q--slot-error'; } } else if (props.hideHint !== true || state.focused.value === true) { if (props.hint !== void 0) { msg = [ vue.h('div', props.hint) ]; key = `q--slot-hint-${ props.hint }`; } else { msg = hSlot(slots.hint); key = 'q--slot-hint'; } } const hasCounter = props.counter === true || slots.counter !== void 0; if (props.hideBottomSpace === true && hasCounter === false && msg === void 0) { return } const main = vue.h('div', { key, class: 'q-field__messages col' }, msg); return vue.h('div', { class: 'q-field__bottom row items-start q-field__bottom--' + (props.hideBottomSpace !== true ? 'animated' : 'stale'), onClick: prevent }, [ props.hideBottomSpace === true ? main : vue.h(vue.Transition, { name: 'q-transition--field-message' }, () => main), hasCounter === true ? vue.h('div', { class: 'q-field__counter' }, slots.counter !== void 0 ? slots.counter() : state.computedCounter.value) : null ]) } function getInnerAppendNode (key, content) { return content === null ? null : vue.h('div', { key, class: 'q-field__append q-field__marginal row no-wrap items-center q-anchor--skip' }, content) } let shouldActivate = false; vue.onDeactivated(() => { shouldActivate = true; }); vue.onActivated(() => { shouldActivate === true && props.autofocus === true && proxy.focus(); }); vue.onMounted(() => { if (isRuntimeSsrPreHydration.value === true && props.for === void 0) { state.targetUid.value = getTargetUid(); } props.autofocus === true && proxy.focus(); }); vue.onBeforeUnmount(() => { focusoutTimer !== null && clearTimeout(focusoutTimer); }); // expose public methods Object.assign(proxy, { focus, blur }); return function renderField () { const labelAttrs = state.getControl === void 0 && slots.control === void 0 ? { ...state.splitAttrs.attributes.value, 'data-autofocus': props.autofocus === true || void 0, ...attributes.value } : attributes.value; return vue.h('label', { ref: state.rootRef, class: [ classes.value, attrs.class ], style: attrs.style, ...labelAttrs }, [ slots.before !== void 0 ? vue.h('div', { class: 'q-field__before q-field__marginal row no-wrap items-center', onClick: prevent }, slots.before()) : null, vue.h('div', { class: 'q-field__inner relative-position col self-stretch' }, [ vue.h('div', { ref: state.controlRef, class: contentClass.value, tabindex: -1, ...state.controlEvents }, getContent()), shouldRenderBottom.value === true ? getBottom() : null ]), slots.after !== void 0 ? vue.h('div', { class: 'q-field__after q-field__marginal row no-wrap items-center', onClick: prevent }, slots.after()) : null ]) } } var QField = createComponent({ name: 'QField', inheritAttrs: false, props: useFieldProps, emits: useFieldEmits, setup () { return useField(useFieldState()) } }); function filterFiles (files, rejectedFiles, failedPropValidation, filterFn) { const acceptedFiles = []; files.forEach(file => { if (filterFn(file) === true) { acceptedFiles.push(file); } else { rejectedFiles.push({ failedPropValidation, file }); } }); return acceptedFiles } function stopAndPreventDrag (e) { e && e.dataTransfer && (e.dataTransfer.dropEffect = 'copy'); stopAndPrevent(e); } const useFileProps = { multiple: Boolean, accept: String, capture: String, maxFileSize: [ Number, String ], maxTotalSize: [ Number, String ], maxFiles: [ Number, String ], filter: Function }; const useFileEmits = [ 'rejected' ]; function useFile ({ editable, dnd, getFileInput, addFilesToQueue }) { const { props, emit, proxy } = vue.getCurrentInstance(); const dndRef = vue.ref(null); const extensions = vue.computed(() => ( props.accept !== void 0 ? props.accept.split(',').map(ext => { ext = ext.trim(); if (ext === '*') { // support "*" return '*/' } else if (ext.endsWith('/*')) { // support "image/*" or "*/*" ext = ext.slice(0, ext.length - 1); } return ext.toUpperCase() }) : null )); const maxFilesNumber = vue.computed(() => parseInt(props.maxFiles, 10)); const maxTotalSizeNumber = vue.computed(() => parseInt(props.maxTotalSize, 10)); function pickFiles (e) { if (editable.value) { if (e !== Object(e)) { e = { target: null }; } if (e.target !== null && e.target.matches('input[type="file"]') === true) { // stop propagation if it's not a real pointer event e.clientX === 0 && e.clientY === 0 && stop(e); } else { const input = getFileInput(); input && input !== e.target && input.click(e); } } } function addFiles (files) { if (editable.value && files) { addFilesToQueue(null, files); } } function processFiles (e, filesToProcess, currentFileList, append) { let files = Array.from(filesToProcess || e.target.files); const rejectedFiles = []; const done = () => { if (rejectedFiles.length !== 0) { emit('rejected', rejectedFiles); } }; // filter file types if (props.accept !== void 0 && extensions.value.indexOf('*/') === -1) { files = filterFiles(files, rejectedFiles, 'accept', file => { return extensions.value.some(ext => ( file.type.toUpperCase().startsWith(ext) || file.name.toUpperCase().endsWith(ext) )) }); if (files.length === 0) { return done() } } // filter max file size if (props.maxFileSize !== void 0) { const maxFileSize = parseInt(props.maxFileSize, 10); files = filterFiles(files, rejectedFiles, 'max-file-size', file => { return file.size <= maxFileSize }); if (files.length === 0) { return done() } } // Cordova/iOS allows selecting multiple files even when the // multiple attribute is not specified. We also normalize drag'n'dropped // files here: if (props.multiple !== true && files.length !== 0) { files = [ files[ 0 ] ]; } // Compute key to use for each file files.forEach(file => { file.__key = file.webkitRelativePath + file.lastModified + file.name + file.size; }); if (append === true) { // Avoid duplicate files const filenameMap = currentFileList.map(entry => entry.__key); files = filterFiles(files, rejectedFiles, 'duplicate', file => { return filenameMap.includes(file.__key) === false }); } if (files.length === 0) { return done() } if (props.maxTotalSize !== void 0) { let size = append === true ? currentFileList.reduce((total, file) => total + file.size, 0) : 0; files = filterFiles(files, rejectedFiles, 'max-total-size', file => { size += file.size; return size <= maxTotalSizeNumber.value }); if (files.length === 0) { return done() } } // do we have custom filter function? if (typeof props.filter === 'function') { const filteredFiles = props.filter(files); files = filterFiles(files, rejectedFiles, 'filter', file => { return filteredFiles.includes(file) }); } if (props.maxFiles !== void 0) { let filesNumber = append === true ? currentFileList.length : 0; files = filterFiles(files, rejectedFiles, 'max-files', () => { filesNumber++; return filesNumber <= maxFilesNumber.value }); if (files.length === 0) { return done() } } done(); if (files.length !== 0) { return files } } function onDragover (e) { stopAndPreventDrag(e); dnd.value !== true && (dnd.value = true); } function onDragleave (e) { stopAndPrevent(e); // Safari bug: relatedTarget is null for over 10 years // https://bugs.webkit.org/show_bug.cgi?id=66547 const gone = e.relatedTarget !== null || client.is.safari !== true ? e.relatedTarget !== dndRef.value : document.elementsFromPoint(e.clientX, e.clientY).includes(dndRef.value) === false; gone === true && (dnd.value = false); } function onDrop (e) { stopAndPreventDrag(e); const files = e.dataTransfer.files; if (files.length !== 0) { addFilesToQueue(null, files); } dnd.value = false; } function getDndNode (type) { if (dnd.value === true) { return vue.h('div', { ref: dndRef, class: `q-${ type }__dnd absolute-full`, onDragenter: stopAndPreventDrag, onDragover: stopAndPreventDrag, onDragleave, onDrop }) } } // expose public methods Object.assign(proxy, { pickFiles, addFiles }); return { pickFiles, addFiles, onDragover, onDragleave, processFiles, getDndNode, maxFilesNumber, maxTotalSizeNumber } } function useFileFormDomProps (props, typeGuard) { function getFormDomProps () { const model = props.modelValue; try { const dt = 'DataTransfer' in window ? new DataTransfer() : ('ClipboardEvent' in window ? new ClipboardEvent('').clipboardData : void 0 ); if (Object(model) === model) { ('length' in model ? Array.from(model) : [ model ] ).forEach(file => { dt.items.add(file); }); } return { files: dt.files } } catch (e) { return { files: void 0 } } } return typeGuard === true ? vue.computed(() => { if (props.type !== 'file') { return } return getFormDomProps() }) : vue.computed(getFormDomProps) } var QFile = createComponent({ name: 'QFile', inheritAttrs: false, props: { ...useFieldProps, ...useFormProps, ...useFileProps, /* SSR does not know about File & FileList */ modelValue: [ File, FileList, Array ], append: Boolean, useChips: Boolean, displayValue: [ String, Number ], tabindex: { type: [ String, Number ], default: 0 }, counterLabel: Function, inputClass: [ Array, String, Object ], inputStyle: [ Array, String, Object ] }, emits: [ ...useFieldEmits, ...useFileEmits ], setup (props, { slots, emit, attrs }) { const { proxy } = vue.getCurrentInstance(); const state = useFieldState(); const inputRef = vue.ref(null); const dnd = vue.ref(false); const nameProp = useFormInputNameAttr(props); const { pickFiles, onDragover, onDragleave, processFiles, getDndNode } = useFile({ editable: state.editable, dnd, getFileInput, addFilesToQueue }); const formDomProps = useFileFormDomProps(props); const innerValue = vue.computed(() => ( Object(props.modelValue) === props.modelValue ? ('length' in props.modelValue ? Array.from(props.modelValue) : [ props.modelValue ]) : [] )); const hasValue = vue.computed(() => fieldValueIsFilled(innerValue.value)); const selectedString = vue.computed(() => innerValue.value .map(file => file.name) .join(', ') ); const totalSize = vue.computed(() => humanStorageSize( innerValue.value.reduce((acc, file) => acc + file.size, 0) ) ); const counterProps = vue.computed(() => ({ totalSize: totalSize.value, filesNumber: innerValue.value.length, maxFiles: props.maxFiles })); const inputAttrs = vue.computed(() => ({ tabindex: -1, type: 'file', title: '', // try to remove default tooltip, accept: props.accept, capture: props.capture, name: nameProp.value, ...attrs, id: state.targetUid.value, disabled: state.editable.value !== true })); const fieldClass = vue.computed(() => 'q-file q-field--auto-height' + (dnd.value === true ? ' q-file--dnd' : '') ); const isAppending = vue.computed(() => props.multiple === true && props.append === true ); function removeAtIndex (index) { const files = innerValue.value.slice(); files.splice(index, 1); emitValue(files); } function removeFile (file) { const index = innerValue.value.indexOf(file); if (index > -1) { removeAtIndex(index); } } function emitValue (files) { emit('update:modelValue', props.multiple === true ? files : files[ 0 ]); } function onKeydown (e) { // prevent form submit if ENTER is pressed e.keyCode === 13 && prevent(e); } function onKeyup (e) { // only on ENTER and SPACE to match native input field if (e.keyCode === 13 || e.keyCode === 32) { pickFiles(e); } } function getFileInput () { return inputRef.value } function addFilesToQueue (e, fileList) { const files = processFiles(e, fileList, innerValue.value, isAppending.value); const fileInput = getFileInput(); if (fileInput !== void 0 && fileInput !== null) { fileInput.value = ''; } // if nothing to do... if (files === void 0) { return } // protect against input @change being called in a loop // like it happens on Safari, so don't emit same thing: if ( props.multiple === true ? props.modelValue && files.every(f => innerValue.value.includes(f)) : props.modelValue === files[ 0 ] ) { return } emitValue( isAppending.value === true ? innerValue.value.concat(files) : files ); } function getFiller () { return [ vue.h('input', { class: [ props.inputClass, 'q-file__filler' ], style: props.inputStyle }) ] } function getSelection () { if (slots.file !== void 0) { return innerValue.value.length === 0 ? getFiller() : innerValue.value.map( (file, index) => slots.file({ index, file, ref: this }) ) } if (slots.selected !== void 0) { return innerValue.value.length === 0 ? getFiller() : slots.selected({ files: innerValue.value, ref: this }) } if (props.useChips === true) { return innerValue.value.length === 0 ? getFiller() : innerValue.value.map((file, i) => vue.h(QChip, { key: 'file-' + i, removable: state.editable.value, dense: true, textColor: props.color, tabindex: props.tabindex, onRemove: () => { removeAtIndex(i); } }, () => vue.h('span', { class: 'ellipsis', textContent: file.name }))) } const textContent = props.displayValue !== void 0 ? props.displayValue : selectedString.value; return textContent.length !== 0 ? [ vue.h('div', { class: props.inputClass, style: props.inputStyle, textContent }) ] : getFiller() } function getInput () { const data = { ref: inputRef, ...inputAttrs.value, ...formDomProps.value, class: 'q-field__input fit absolute-full cursor-pointer', onChange: addFilesToQueue }; if (props.multiple === true) { data.multiple = true; } return vue.h('input', data) } Object.assign(state, { fieldClass, emitValue, hasValue, inputRef, innerValue, floatingLabel: vue.computed(() => hasValue.value === true || fieldValueIsFilled(props.displayValue) ), computedCounter: vue.computed(() => { if (props.counterLabel !== void 0) { return props.counterLabel(counterProps.value) } const max = props.maxFiles; return `${ innerValue.value.length }${ max !== void 0 ? ' / ' + max : '' } (${ totalSize.value })` }), getControlChild: () => getDndNode('file'), getControl: () => { const data = { ref: state.targetRef, class: 'q-field__native row items-center cursor-pointer', tabindex: props.tabindex }; if (state.editable.value === true) { Object.assign(data, { onDragover, onDragleave, onKeydown, onKeyup }); } return vue.h('div', data, [ getInput() ].concat(getSelection())) } }); // expose public methods Object.assign(proxy, { removeAtIndex, removeFile, getNativeElement: () => inputRef.value // deprecated }); injectProp(proxy, 'nativeEl', () => inputRef.value); return useField(state) } }); var QFooter = createComponent({ name: 'QFooter', props: { modelValue: { type: Boolean, default: true }, reveal: Boolean, bordered: Boolean, elevated: Boolean, heightHint: { type: [ String, Number ], default: 50 } }, emits: [ 'reveal', 'focusin' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QFooter needs to be child of QLayout'); return emptyRenderFn } const size = vue.ref(parseInt(props.heightHint, 10)); const revealed = vue.ref(true); const windowHeight = vue.ref( isRuntimeSsrPreHydration.value === true || $layout.isContainer.value === true ? 0 : window.innerHeight ); const fixed = vue.computed(() => props.reveal === true || $layout.view.value.indexOf('F') > -1 || ($q.platform.is.ios && $layout.isContainer.value === true) ); const containerHeight = vue.computed(() => ( $layout.isContainer.value === true ? $layout.containerHeight.value : windowHeight.value )); const offset = vue.computed(() => { if (props.modelValue !== true) { return 0 } if (fixed.value === true) { return revealed.value === true ? size.value : 0 } const offset = $layout.scroll.value.position + containerHeight.value + size.value - $layout.height.value; return offset > 0 ? offset : 0 }); const hidden = vue.computed(() => props.modelValue !== true || (fixed.value === true && revealed.value !== true) ); const revealOnFocus = vue.computed(() => props.modelValue === true && hidden.value === true && props.reveal === true ); const classes = vue.computed(() => 'q-footer q-layout__section--marginal ' + (fixed.value === true ? 'fixed' : 'absolute') + '-bottom' + (props.bordered === true ? ' q-footer--bordered' : '') + (hidden.value === true ? ' q-footer--hidden' : '') + ( props.modelValue !== true ? ' q-layout--prevent-focus' + (fixed.value !== true ? ' hidden' : '') : '' ) ); const style = vue.computed(() => { const view = $layout.rows.value.bottom, css = {}; if (view[ 0 ] === 'l' && $layout.left.space === true) { css[ $q.lang.rtl === true ? 'right' : 'left' ] = `${ $layout.left.size }px`; } if (view[ 2 ] === 'r' && $layout.right.space === true) { css[ $q.lang.rtl === true ? 'left' : 'right' ] = `${ $layout.right.size }px`; } return css }); function updateLayout (prop, val) { $layout.update('footer', prop, val); } function updateLocal (prop, val) { if (prop.value !== val) { prop.value = val; } } function onResize ({ height }) { updateLocal(size, height); updateLayout('size', height); } function updateRevealed () { if (props.reveal !== true) { return } const { direction, position, inflectionPoint } = $layout.scroll.value; updateLocal(revealed, ( direction === 'up' || position - inflectionPoint < 100 || $layout.height.value - containerHeight.value - position - size.value < 300 )); } function onFocusin (evt) { if (revealOnFocus.value === true) { updateLocal(revealed, true); } emit('focusin', evt); } vue.watch(() => props.modelValue, val => { updateLayout('space', val); updateLocal(revealed, true); $layout.animate(); }); vue.watch(offset, val => { updateLayout('offset', val); }); vue.watch(() => props.reveal, val => { val === false && updateLocal(revealed, props.modelValue); }); vue.watch(revealed, val => { $layout.animate(); emit('reveal', val); }); vue.watch([ size, $layout.scroll, $layout.height ], updateRevealed); vue.watch(() => $q.screen.height, val => { $layout.isContainer.value !== true && updateLocal(windowHeight, val); }); const instance = {}; $layout.instances.footer = instance; props.modelValue === true && updateLayout('size', size.value); updateLayout('space', props.modelValue); updateLayout('offset', offset.value); vue.onBeforeUnmount(() => { if ($layout.instances.footer === instance) { $layout.instances.footer = void 0; updateLayout('size', 0); updateLayout('offset', 0); updateLayout('space', false); } }); return () => { const child = hMergeSlot(slots.default, [ vue.h(QResizeObserver, { debounce: 0, onResize }) ]); props.elevated === true && child.push( vue.h('div', { class: 'q-layout__shadow absolute-full overflow-hidden no-pointer-events' }) ); return vue.h('footer', { class: classes.value, style: style.value, onFocusin }, child) } } }); var QForm = createComponent({ name: 'QForm', props: { autofocus: Boolean, noErrorFocus: Boolean, noResetFocus: Boolean, greedy: Boolean, onSubmit: Function }, emits: [ 'reset', 'validationSuccess', 'validationError' ], setup (props, { slots, emit }) { const vm = vue.getCurrentInstance(); const rootRef = vue.ref(null); let validateIndex = 0; const registeredComponents = []; function validate (shouldFocus) { const focus = typeof shouldFocus === 'boolean' ? shouldFocus : props.noErrorFocus !== true; const index = ++validateIndex; const emitEvent = (res, ref) => { emit('validation' + (res === true ? 'Success' : 'Error'), ref); }; const validateComponent = comp => { const valid = comp.validate(); return typeof valid.then === 'function' ? valid.then( valid => ({ valid, comp }), err => ({ valid: false, comp, err }) ) : Promise.resolve({ valid, comp }) }; const errorsPromise = props.greedy === true ? Promise .all(registeredComponents.map(validateComponent)) .then(res => res.filter(r => r.valid !== true)) : registeredComponents .reduce( (acc, comp) => acc.then(() => { return validateComponent(comp).then(r => { if (r.valid === false) { return Promise.reject(r) } }) }), Promise.resolve() ) .catch(error => [ error ]); return errorsPromise.then(errors => { if (errors === void 0 || errors.length === 0) { index === validateIndex && emitEvent(true); return true } // if not outdated already if (index === validateIndex) { const { comp, err } = errors[ 0 ]; err !== void 0 && console.error(err); emitEvent(false, comp); if (focus === true) { // Try to focus first mounted and active component const activeError = errors.find(({ comp }) => ( typeof comp.focus === 'function' && vmIsDestroyed(comp.$) === false )); if (activeError !== void 0) { activeError.comp.focus(); } } } return false }) } function resetValidation () { validateIndex++; registeredComponents.forEach(comp => { typeof comp.resetValidation === 'function' && comp.resetValidation(); }); } function submit (evt) { evt !== void 0 && stopAndPrevent(evt); const index = validateIndex + 1; validate().then(val => { // if not outdated && validation succeeded if (index === validateIndex && val === true) { if (props.onSubmit !== void 0) { emit('submit', evt); } else if (evt !== void 0 && evt.target !== void 0 && typeof evt.target.submit === 'function') { evt.target.submit(); } } }); } function reset (evt) { evt !== void 0 && stopAndPrevent(evt); emit('reset'); vue.nextTick(() => { // allow userland to reset values before resetValidation(); if (props.autofocus === true && props.noResetFocus !== true) { focus(); } }); } function focus () { addFocusFn(() => { if (rootRef.value === null) { return } const target = rootRef.value.querySelector('[autofocus][tabindex], [data-autofocus][tabindex]') || rootRef.value.querySelector('[autofocus] [tabindex], [data-autofocus] [tabindex]') || rootRef.value.querySelector('[autofocus], [data-autofocus]') || Array.prototype.find.call(rootRef.value.querySelectorAll('[tabindex]'), el => el.tabIndex > -1); target !== null && target !== void 0 && target.focus({ preventScroll: true }); }); } vue.provide(formKey, { bindComponent (vmProxy) { registeredComponents.push(vmProxy); }, unbindComponent (vmProxy) { const index = registeredComponents.indexOf(vmProxy); if (index > -1) { registeredComponents.splice(index, 1); } } }); let shouldActivate = false; vue.onDeactivated(() => { shouldActivate = true; }); vue.onActivated(() => { shouldActivate === true && props.autofocus === true && focus(); }); vue.onMounted(() => { props.autofocus === true && focus(); }); // expose public methods Object.assign(vm.proxy, { validate, resetValidation, submit, reset, focus, getValidationComponents: () => registeredComponents }); return () => vue.h('form', { class: 'q-form', ref: rootRef, onSubmit: submit, onReset: reset }, hSlot(slots.default)) } }); var QFormChildMixin = { inject: { [ formKey ]: { default: noop } }, watch: { disable (val) { const $form = this.$.provides[ formKey ]; if ($form !== void 0) { if (val === true) { this.resetValidation(); $form.unbindComponent(this); } else { $form.bindComponent(this); } } } }, methods: { validate () {}, resetValidation () {} }, mounted () { // register to parent QForm const $form = this.$.provides[ formKey ]; $form !== void 0 && this.disable !== true && $form.bindComponent(this); }, beforeUnmount () { // un-register from parent QForm const $form = this.$.provides[ formKey ]; $form !== void 0 && this.disable !== true && $form.unbindComponent(this); } }; var QHeader = createComponent({ name: 'QHeader', props: { modelValue: { type: Boolean, default: true }, reveal: Boolean, revealOffset: { type: Number, default: 250 }, bordered: Boolean, elevated: Boolean, heightHint: { type: [ String, Number ], default: 50 } }, emits: [ 'reveal', 'focusin' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QHeader needs to be child of QLayout'); return emptyRenderFn } const size = vue.ref(parseInt(props.heightHint, 10)); const revealed = vue.ref(true); const fixed = vue.computed(() => props.reveal === true || $layout.view.value.indexOf('H') > -1 || ($q.platform.is.ios && $layout.isContainer.value === true) ); const offset = vue.computed(() => { if (props.modelValue !== true) { return 0 } if (fixed.value === true) { return revealed.value === true ? size.value : 0 } const offset = size.value - $layout.scroll.value.position; return offset > 0 ? offset : 0 }); const hidden = vue.computed(() => props.modelValue !== true || (fixed.value === true && revealed.value !== true) ); const revealOnFocus = vue.computed(() => props.modelValue === true && hidden.value === true && props.reveal === true ); const classes = vue.computed(() => 'q-header q-layout__section--marginal ' + (fixed.value === true ? 'fixed' : 'absolute') + '-top' + (props.bordered === true ? ' q-header--bordered' : '') + (hidden.value === true ? ' q-header--hidden' : '') + (props.modelValue !== true ? ' q-layout--prevent-focus' : '') ); const style = vue.computed(() => { const view = $layout.rows.value.top, css = {}; if (view[ 0 ] === 'l' && $layout.left.space === true) { css[ $q.lang.rtl === true ? 'right' : 'left' ] = `${ $layout.left.size }px`; } if (view[ 2 ] === 'r' && $layout.right.space === true) { css[ $q.lang.rtl === true ? 'left' : 'right' ] = `${ $layout.right.size }px`; } return css }); function updateLayout (prop, val) { $layout.update('header', prop, val); } function updateLocal (prop, val) { if (prop.value !== val) { prop.value = val; } } function onResize ({ height }) { updateLocal(size, height); updateLayout('size', height); } function onFocusin (evt) { if (revealOnFocus.value === true) { updateLocal(revealed, true); } emit('focusin', evt); } vue.watch(() => props.modelValue, val => { updateLayout('space', val); updateLocal(revealed, true); $layout.animate(); }); vue.watch(offset, val => { updateLayout('offset', val); }); vue.watch(() => props.reveal, val => { val === false && updateLocal(revealed, props.modelValue); }); vue.watch(revealed, val => { $layout.animate(); emit('reveal', val); }); vue.watch($layout.scroll, scroll => { props.reveal === true && updateLocal(revealed, scroll.direction === 'up' || scroll.position <= props.revealOffset || scroll.position - scroll.inflectionPoint < 100 ); }); const instance = {}; $layout.instances.header = instance; props.modelValue === true && updateLayout('size', size.value); updateLayout('space', props.modelValue); updateLayout('offset', offset.value); vue.onBeforeUnmount(() => { if ($layout.instances.header === instance) { $layout.instances.header = void 0; updateLayout('size', 0); updateLayout('offset', 0); updateLayout('space', false); } }); return () => { const child = hUniqueSlot(slots.default, []); props.elevated === true && child.push( vue.h('div', { class: 'q-layout__shadow absolute-full overflow-hidden no-pointer-events' }) ); child.push( vue.h(QResizeObserver, { debounce: 0, onResize }) ); return vue.h('header', { class: classes.value, style: style.value, onFocusin }, child) } } }); const useRatioProps = { ratio: [ String, Number ] }; function useRatio (props, naturalRatio) { // return ratioStyle return vue.computed(() => { const ratio = Number( props.ratio || (naturalRatio !== void 0 ? naturalRatio.value : void 0) ); return isNaN(ratio) !== true && ratio > 0 ? { paddingBottom: `${ 100 / ratio }%` } : null }) } const defaultRatio = 16 / 9; var QImg = createComponent({ name: 'QImg', props: { ...useRatioProps, src: String, srcset: String, sizes: String, alt: String, crossorigin: String, decoding: String, referrerpolicy: String, draggable: Boolean, loading: { type: String, default: 'lazy' }, fetchpriority: { type: String, default: 'auto' }, width: String, height: String, initialRatio: { type: [ Number, String ], default: defaultRatio }, placeholderSrc: String, fit: { type: String, default: 'cover' }, position: { type: String, default: '50% 50%' }, imgClass: String, imgStyle: Object, noSpinner: Boolean, noNativeMenu: Boolean, noTransition: Boolean, spinnerColor: String, spinnerSize: String }, emits: [ 'load', 'error' ], setup (props, { slots, emit }) { const naturalRatio = vue.ref(props.initialRatio); const ratioStyle = useRatio(props, naturalRatio); let loadTimer = null, isDestroyed = false; const images = [ vue.ref(null), vue.ref(getPlaceholderSrc()) ]; const position = vue.ref(0); const isLoading = vue.ref(false); const hasError = vue.ref(false); const classes = vue.computed(() => `q-img q-img--${ props.noNativeMenu === true ? 'no-' : '' }menu` ); const style = vue.computed(() => ({ width: props.width, height: props.height })); const imgClass = vue.computed(() => `q-img__image ${ props.imgClass !== void 0 ? props.imgClass + ' ' : '' }` + `q-img__image--with${ props.noTransition === true ? 'out' : '' }-transition` ); const imgStyle = vue.computed(() => ({ ...props.imgStyle, objectFit: props.fit, objectPosition: props.position })); vue.watch(() => getCurrentSrc(), addImage); function getCurrentSrc () { return props.src || props.srcset || props.sizes ? { src: props.src, srcset: props.srcset, sizes: props.sizes } : null } function getPlaceholderSrc () { return props.placeholderSrc !== void 0 ? { src: props.placeholderSrc } : null } function addImage (imgProps) { if (loadTimer !== null) { clearTimeout(loadTimer); loadTimer = null; } hasError.value = false; if (imgProps === null) { isLoading.value = false; images[ position.value ^ 1 ].value = getPlaceholderSrc(); } else { isLoading.value = true; } images[ position.value ].value = imgProps; } function onLoad ({ target }) { if (isDestroyed === true) { return } if (loadTimer !== null) { clearTimeout(loadTimer); loadTimer = null; } naturalRatio.value = target.naturalHeight === 0 ? 0.5 : target.naturalWidth / target.naturalHeight; waitForCompleteness(target, 1); } function waitForCompleteness (target, count) { // protect against running forever if (isDestroyed === true || count === 1000) { return } if (target.complete === true) { onReady(target); } else { loadTimer = setTimeout(() => { loadTimer = null; waitForCompleteness(target, count + 1); }, 50); } } function onReady (img) { if (isDestroyed === true) { return } position.value = position.value ^ 1; images[ position.value ].value = null; isLoading.value = false; hasError.value = false; emit('load', img.currentSrc || img.src); } function onError (err) { if (loadTimer !== null) { clearTimeout(loadTimer); loadTimer = null; } isLoading.value = false; hasError.value = true; images[ position.value ].value = null; images[ position.value ^ 1 ].value = getPlaceholderSrc(); emit('error', err); } function getImage (index) { const img = images[ index ].value; const data = { key: 'img_' + index, class: imgClass.value, style: imgStyle.value, crossorigin: props.crossorigin, decoding: props.decoding, referrerpolicy: props.referrerpolicy, height: props.height, width: props.width, loading: props.loading, fetchpriority: props.fetchpriority, 'aria-hidden': 'true', draggable: props.draggable, ...img }; if (position.value === index) { data.class += ' q-img__image--waiting'; Object.assign(data, { onLoad, onError }); } else { data.class += ' q-img__image--loaded'; } return vue.h( 'div', { class: 'q-img__container absolute-full', key: 'img' + index }, vue.h('img', data) ) } function getContent () { if (isLoading.value !== true) { return vue.h('div', { key: 'content', class: 'q-img__content absolute-full q-anchor--skip' }, hSlot(slots[ hasError.value === true ? 'error' : 'default' ])) } return vue.h('div', { key: 'loading', class: 'q-img__loading absolute-full flex flex-center' }, ( slots.loading !== void 0 ? slots.loading() : ( props.noSpinner === true ? void 0 : [ vue.h(QSpinner, { color: props.spinnerColor, size: props.spinnerSize }) ] ) )) } { { addImage(getCurrentSrc()); } vue.onBeforeUnmount(() => { isDestroyed = true; if (loadTimer !== null) { clearTimeout(loadTimer); loadTimer = null; } }); } return () => { const content = []; if (ratioStyle.value !== null) { content.push( vue.h('div', { key: 'filler', style: ratioStyle.value }) ); } if (hasError.value !== true) { if (images[ 0 ].value !== null) { content.push(getImage(0)); } if (images[ 1 ].value !== null) { content.push(getImage(1)); } } content.push( vue.h(vue.Transition, { name: 'q-transition--fade' }, getContent) ); return vue.h('div', { class: classes.value, style: style.value, role: 'img', 'aria-label': props.alt }, content) } } }); const { passive: passive$3 } = listenOpts; var QInfiniteScroll = createComponent({ name: 'QInfiniteScroll', props: { offset: { type: Number, default: 500 }, debounce: { type: [ String, Number ], default: 100 }, scrollTarget: { default: void 0 }, initialIndex: Number, disable: Boolean, reverse: Boolean }, emits: [ 'load' ], setup (props, { slots, emit }) { const isFetching = vue.ref(false); const isWorking = vue.ref(true); const rootRef = vue.ref(null); const loadingRef = vue.ref(null); let index = props.initialIndex || 0; let localScrollTarget, poll; const classes = vue.computed(() => 'q-infinite-scroll__loading' + (isFetching.value === true ? '' : ' invisible') ); function immediatePoll () { if (props.disable === true || isFetching.value === true || isWorking.value === false) { return } const scrollHeight = getScrollHeight(localScrollTarget), scrollPosition = getVerticalScrollPosition(localScrollTarget), containerHeight = height(localScrollTarget); if (props.reverse === false) { if (Math.round(scrollPosition + containerHeight + props.offset) >= Math.round(scrollHeight)) { trigger(); } } else if (Math.round(scrollPosition) <= props.offset) { trigger(); } } function trigger () { if (props.disable === true || isFetching.value === true || isWorking.value === false) { return } index++; isFetching.value = true; const heightBefore = getScrollHeight(localScrollTarget); emit('load', index, isDone => { if (isWorking.value === true) { isFetching.value = false; vue.nextTick(() => { if (props.reverse === true) { const heightAfter = getScrollHeight(localScrollTarget), scrollPosition = getVerticalScrollPosition(localScrollTarget), heightDifference = heightAfter - heightBefore; setVerticalScrollPosition(localScrollTarget, scrollPosition + heightDifference); } if (isDone === true) { stop(); } else if (rootRef.value) { rootRef.value.closest('body') && poll(); } }); } }); } function reset () { index = 0; } function resume () { if (isWorking.value === false) { isWorking.value = true; localScrollTarget.addEventListener('scroll', poll, passive$3); } immediatePoll(); } function stop () { if (isWorking.value === true) { isWorking.value = false; isFetching.value = false; localScrollTarget.removeEventListener('scroll', poll, passive$3); if (poll !== void 0 && poll.cancel !== void 0) { poll.cancel(); } } } function updateScrollTarget () { if (localScrollTarget && isWorking.value === true) { localScrollTarget.removeEventListener('scroll', poll, passive$3); } localScrollTarget = getScrollTarget(rootRef.value, props.scrollTarget); if (isWorking.value === true) { localScrollTarget.addEventListener('scroll', poll, passive$3); if (props.reverse === true) { const scrollHeight = getScrollHeight(localScrollTarget), containerHeight = height(localScrollTarget); setVerticalScrollPosition(localScrollTarget, scrollHeight - containerHeight); } immediatePoll(); } } function setIndex (newIndex) { index = newIndex; } function setDebounce (val) { val = parseInt(val, 10); const oldPoll = poll; poll = val <= 0 ? immediatePoll : debounce(immediatePoll, isNaN(val) === true ? 100 : val); if (localScrollTarget && isWorking.value === true) { if (oldPoll !== void 0) { localScrollTarget.removeEventListener('scroll', oldPoll, passive$3); } localScrollTarget.addEventListener('scroll', poll, passive$3); } } function updateSvgAnimations (isRetry) { if (renderLoadingSlot.value === true) { if (loadingRef.value === null) { isRetry !== true && vue.nextTick(() => { updateSvgAnimations(true); }); return } // we need to pause svg animations (if any) when hiding // otherwise the browser will keep on recalculating the style const action = `${ isFetching.value === true ? 'un' : '' }pauseAnimations`; Array.from(loadingRef.value.getElementsByTagName('svg')).forEach(el => { el[ action ](); }); } } const renderLoadingSlot = vue.computed(() => props.disable !== true && isWorking.value === true); vue.watch([ isFetching, renderLoadingSlot ], () => { updateSvgAnimations(); }); vue.watch(() => props.disable, val => { if (val === true) { stop(); } else { resume(); } }); vue.watch(() => props.reverse, () => { if (isFetching.value === false && isWorking.value === true) { immediatePoll(); } }); vue.watch(() => props.scrollTarget, updateScrollTarget); vue.watch(() => props.debounce, setDebounce); let scrollPos = false; vue.onActivated(() => { if (scrollPos !== false && localScrollTarget) { setVerticalScrollPosition(localScrollTarget, scrollPos); } }); vue.onDeactivated(() => { scrollPos = localScrollTarget ? getVerticalScrollPosition(localScrollTarget) : false; }); vue.onBeforeUnmount(() => { if (isWorking.value === true) { localScrollTarget.removeEventListener('scroll', poll, passive$3); } }); vue.onMounted(() => { setDebounce(props.debounce); updateScrollTarget(); isFetching.value === false && updateSvgAnimations(); }); // expose public methods const vm = vue.getCurrentInstance(); Object.assign(vm.proxy, { poll: () => { poll !== void 0 && poll(); }, trigger, stop, reset, resume, setIndex }); return () => { const child = hUniqueSlot(slots.default, []); if (renderLoadingSlot.value === true) { child[ props.reverse === false ? 'push' : 'unshift' ]( vue.h('div', { ref: loadingRef, class: classes.value }, hSlot(slots.loading)) ); } return vue.h('div', { class: 'q-infinite-scroll', ref: rootRef }, child) } } }); var QInnerLoading = createComponent({ name: 'QInnerLoading', props: { ...useDarkProps, ...useTransitionProps, showing: Boolean, color: String, size: { type: [ String, Number ], default: 42 }, label: String, labelClass: String, labelStyle: [ String, Array, Object ] }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const { transitionProps, transitionStyle } = useTransition(props); const classes = vue.computed(() => 'q-inner-loading absolute-full column flex-center' + (isDark.value === true ? ' q-inner-loading--dark' : '') ); const labelClass = vue.computed(() => 'q-inner-loading__label' + (props.labelClass !== void 0 ? ` ${ props.labelClass }` : '') ); function getInner () { const child = [ vue.h(QSpinner, { size: props.size, color: props.color }) ]; if (props.label !== void 0) { child.push( vue.h('div', { class: labelClass.value, style: props.labelStyle }, [ props.label ]) ); } return child } function getContent () { return props.showing === true ? vue.h( 'div', { class: classes.value, style: transitionStyle.value }, slots.default !== void 0 ? slots.default() : getInner() ) : null } return () => vue.h(vue.Transition, transitionProps.value, getContent) } }); // leave NAMED_MASKS at top of file (code referenced from docs) const NAMED_MASKS = { date: '####/##/##', datetime: '####/##/## ##:##', time: '##:##', fulltime: '##:##:##', phone: '(###) ### - ####', card: '#### #### #### ####' }; const TOKENS = { '#': { pattern: '[\\d]', negate: '[^\\d]' }, S: { pattern: '[a-zA-Z]', negate: '[^a-zA-Z]' }, N: { pattern: '[0-9a-zA-Z]', negate: '[^0-9a-zA-Z]' }, A: { pattern: '[a-zA-Z]', negate: '[^a-zA-Z]', transform: v => v.toLocaleUpperCase() }, a: { pattern: '[a-zA-Z]', negate: '[^a-zA-Z]', transform: v => v.toLocaleLowerCase() }, X: { pattern: '[0-9a-zA-Z]', negate: '[^0-9a-zA-Z]', transform: v => v.toLocaleUpperCase() }, x: { pattern: '[0-9a-zA-Z]', negate: '[^0-9a-zA-Z]', transform: v => v.toLocaleLowerCase() } }; const KEYS = Object.keys(TOKENS); KEYS.forEach(key => { TOKENS[ key ].regex = new RegExp(TOKENS[ key ].pattern); }); const tokenRegexMask = new RegExp('\\\\([^.*+?^${}()|([\\]])|([.*+?^${}()|[\\]])|([' + KEYS.join('') + '])|(.)', 'g'), escRegex = /[.*+?^${}()|[\]\\]/g; const MARKER = String.fromCharCode(1); const useMaskProps = { mask: String, reverseFillMask: Boolean, fillMask: [ Boolean, String ], unmaskedValue: Boolean }; function useMask (props, emit, emitValue, inputRef) { let maskMarked, maskReplaced, computedMask, computedUnmask, pastedTextStart, selectionAnchor; const hasMask = vue.ref(null); const innerValue = vue.ref(getInitialMaskedValue()); function getIsTypeText () { return props.autogrow === true || [ 'textarea', 'text', 'search', 'url', 'tel', 'password' ].includes(props.type) } vue.watch(() => props.type + props.autogrow, updateMaskInternals); vue.watch(() => props.mask, v => { if (v !== void 0) { updateMaskValue(innerValue.value, true); } else { const val = unmaskValue(innerValue.value); updateMaskInternals(); props.modelValue !== val && emit('update:modelValue', val); } }); vue.watch(() => props.fillMask + props.reverseFillMask, () => { hasMask.value === true && updateMaskValue(innerValue.value, true); }); vue.watch(() => props.unmaskedValue, () => { hasMask.value === true && updateMaskValue(innerValue.value); }); function getInitialMaskedValue () { updateMaskInternals(); if (hasMask.value === true) { const masked = maskValue(unmaskValue(props.modelValue)); return props.fillMask !== false ? fillWithMask(masked) : masked } return props.modelValue } function getPaddedMaskMarked (size) { if (size < maskMarked.length) { return maskMarked.slice(-size) } let pad = '', localMaskMarked = maskMarked; const padPos = localMaskMarked.indexOf(MARKER); if (padPos > -1) { for (let i = size - localMaskMarked.length; i > 0; i--) { pad += MARKER; } localMaskMarked = localMaskMarked.slice(0, padPos) + pad + localMaskMarked.slice(padPos); } return localMaskMarked } function updateMaskInternals () { hasMask.value = props.mask !== void 0 && props.mask.length !== 0 && getIsTypeText(); if (hasMask.value === false) { computedUnmask = void 0; maskMarked = ''; maskReplaced = ''; return } const localComputedMask = NAMED_MASKS[ props.mask ] === void 0 ? props.mask : NAMED_MASKS[ props.mask ], fillChar = typeof props.fillMask === 'string' && props.fillMask.length !== 0 ? props.fillMask.slice(0, 1) : '_', fillCharEscaped = fillChar.replace(escRegex, '\\$&'), unmask = [], extract = [], mask = []; let firstMatch = props.reverseFillMask === true, unmaskChar = '', negateChar = ''; localComputedMask.replace(tokenRegexMask, (_, char1, esc, token, char2) => { if (token !== void 0) { const c = TOKENS[ token ]; mask.push(c); negateChar = c.negate; if (firstMatch === true) { extract.push('(?:' + negateChar + '+)?(' + c.pattern + '+)?(?:' + negateChar + '+)?(' + c.pattern + '+)?'); firstMatch = false; } extract.push('(?:' + negateChar + '+)?(' + c.pattern + ')?'); } else if (esc !== void 0) { unmaskChar = '\\' + (esc === '\\' ? '' : esc); mask.push(esc); unmask.push('([^' + unmaskChar + ']+)?' + unmaskChar + '?'); } else { const c = char1 !== void 0 ? char1 : char2; unmaskChar = c === '\\' ? '\\\\\\\\' : c.replace(escRegex, '\\\\$&'); mask.push(c); unmask.push('([^' + unmaskChar + ']+)?' + unmaskChar + '?'); } }); const unmaskMatcher = new RegExp( '^' + unmask.join('') + '(' + (unmaskChar === '' ? '.' : '[^' + unmaskChar + ']') + '+)?' + (unmaskChar === '' ? '' : '[' + unmaskChar + ']*') + '$' ), extractLast = extract.length - 1, extractMatcher = extract.map((re, index) => { if (index === 0 && props.reverseFillMask === true) { return new RegExp('^' + fillCharEscaped + '*' + re) } else if (index === extractLast) { return new RegExp( '^' + re + '(' + (negateChar === '' ? '.' : negateChar) + '+)?' + (props.reverseFillMask === true ? '$' : fillCharEscaped + '*') ) } return new RegExp('^' + re) }); computedMask = mask; computedUnmask = val => { const unmaskMatch = unmaskMatcher.exec(props.reverseFillMask === true ? val : val.slice(0, mask.length + 1)); if (unmaskMatch !== null) { val = unmaskMatch.slice(1).join(''); } const extractMatch = [], extractMatcherLength = extractMatcher.length; for (let i = 0, str = val; i < extractMatcherLength; i++) { const m = extractMatcher[ i ].exec(str); if (m === null) { break } str = str.slice(m.shift().length); extractMatch.push(...m); } if (extractMatch.length !== 0) { return extractMatch.join('') } return val }; maskMarked = mask.map(v => (typeof v === 'string' ? v : MARKER)).join(''); maskReplaced = maskMarked.split(MARKER).join(fillChar); } function updateMaskValue (rawVal, updateMaskInternalsFlag, inputType) { const inp = inputRef.value, end = inp.selectionEnd, endReverse = inp.value.length - end, unmasked = unmaskValue(rawVal); // Update here so unmask uses the original fillChar updateMaskInternalsFlag === true && updateMaskInternals(); const preMasked = maskValue(unmasked), masked = props.fillMask !== false ? fillWithMask(preMasked) : preMasked, changed = innerValue.value !== masked; // We want to avoid "flickering" so we set value immediately inp.value !== masked && (inp.value = masked); changed === true && (innerValue.value = masked); document.activeElement === inp && vue.nextTick(() => { if (masked === maskReplaced) { const cursor = props.reverseFillMask === true ? maskReplaced.length : 0; inp.setSelectionRange(cursor, cursor, 'forward'); return } if (inputType === 'insertFromPaste' && props.reverseFillMask !== true) { const maxEnd = inp.selectionEnd; let cursor = end - 1; // each non-marker char means we move once to right for (let i = pastedTextStart; i <= cursor && i < maxEnd; i++) { if (maskMarked[ i ] !== MARKER) { cursor++; } } moveCursor.right(inp, cursor); return } if ([ 'deleteContentBackward', 'deleteContentForward' ].indexOf(inputType) > -1) { const cursor = props.reverseFillMask === true ? ( end === 0 ? (masked.length > preMasked.length ? 1 : 0) : Math.max(0, masked.length - (masked === maskReplaced ? 0 : Math.min(preMasked.length, endReverse) + 1)) + 1 ) : end; inp.setSelectionRange(cursor, cursor, 'forward'); return } if (props.reverseFillMask === true) { if (changed === true) { const cursor = Math.max(0, masked.length - (masked === maskReplaced ? 0 : Math.min(preMasked.length, endReverse + 1))); if (cursor === 1 && end === 1) { inp.setSelectionRange(cursor, cursor, 'forward'); } else { moveCursor.rightReverse(inp, cursor); } } else { const cursor = masked.length - endReverse; inp.setSelectionRange(cursor, cursor, 'backward'); } } else { if (changed === true) { const cursor = Math.max(0, maskMarked.indexOf(MARKER), Math.min(preMasked.length, end) - 1); moveCursor.right(inp, cursor); } else { const cursor = end - 1; moveCursor.right(inp, cursor); } } }); const val = props.unmaskedValue === true ? unmaskValue(masked) : masked; String(props.modelValue) !== val && emitValue(val, true); } function moveCursorForPaste (inp, start, end) { const preMasked = maskValue(unmaskValue(inp.value)); start = Math.max(0, maskMarked.indexOf(MARKER), Math.min(preMasked.length, start)); pastedTextStart = start; inp.setSelectionRange(start, end, 'forward'); } const moveCursor = { left (inp, cursor) { const noMarkBefore = maskMarked.slice(cursor - 1).indexOf(MARKER) === -1; let i = Math.max(0, cursor - 1); for (; i >= 0; i--) { if (maskMarked[ i ] === MARKER) { cursor = i; noMarkBefore === true && cursor++; break } } if ( i < 0 && maskMarked[ cursor ] !== void 0 && maskMarked[ cursor ] !== MARKER ) { return moveCursor.right(inp, 0) } cursor >= 0 && inp.setSelectionRange(cursor, cursor, 'backward'); }, right (inp, cursor) { const limit = inp.value.length; let i = Math.min(limit, cursor + 1); for (; i <= limit; i++) { if (maskMarked[ i ] === MARKER) { cursor = i; break } else if (maskMarked[ i - 1 ] === MARKER) { cursor = i; } } if ( i > limit && maskMarked[ cursor - 1 ] !== void 0 && maskMarked[ cursor - 1 ] !== MARKER ) { return moveCursor.left(inp, limit) } inp.setSelectionRange(cursor, cursor, 'forward'); }, leftReverse (inp, cursor) { const localMaskMarked = getPaddedMaskMarked(inp.value.length); let i = Math.max(0, cursor - 1); for (; i >= 0; i--) { if (localMaskMarked[ i - 1 ] === MARKER) { cursor = i; break } else if (localMaskMarked[ i ] === MARKER) { cursor = i; if (i === 0) { break } } } if ( i < 0 && localMaskMarked[ cursor ] !== void 0 && localMaskMarked[ cursor ] !== MARKER ) { return moveCursor.rightReverse(inp, 0) } cursor >= 0 && inp.setSelectionRange(cursor, cursor, 'backward'); }, rightReverse (inp, cursor) { const limit = inp.value.length, localMaskMarked = getPaddedMaskMarked(limit), noMarkBefore = localMaskMarked.slice(0, cursor + 1).indexOf(MARKER) === -1; let i = Math.min(limit, cursor + 1); for (; i <= limit; i++) { if (localMaskMarked[ i - 1 ] === MARKER) { cursor = i; cursor > 0 && noMarkBefore === true && cursor--; break } } if ( i > limit && localMaskMarked[ cursor - 1 ] !== void 0 && localMaskMarked[ cursor - 1 ] !== MARKER ) { return moveCursor.leftReverse(inp, limit) } inp.setSelectionRange(cursor, cursor, 'forward'); } }; function onMaskedClick (e) { emit('click', e); selectionAnchor = void 0; } function onMaskedKeydown (e) { emit('keydown', e); if (shouldIgnoreKey(e) === true) { return } const inp = inputRef.value, start = inp.selectionStart, end = inp.selectionEnd; if (!e.shiftKey) { selectionAnchor = void 0; } if (e.keyCode === 37 || e.keyCode === 39) { // Left / Right if (e.shiftKey && selectionAnchor === void 0) { selectionAnchor = inp.selectionDirection === 'forward' ? start : end; } const fn = moveCursor[ (e.keyCode === 39 ? 'right' : 'left') + (props.reverseFillMask === true ? 'Reverse' : '') ]; e.preventDefault(); fn(inp, selectionAnchor === start ? end : start); if (e.shiftKey) { const cursor = inp.selectionStart; inp.setSelectionRange(Math.min(selectionAnchor, cursor), Math.max(selectionAnchor, cursor), 'forward'); } } else if ( e.keyCode === 8 // Backspace && props.reverseFillMask !== true && start === end ) { moveCursor.left(inp, start); inp.setSelectionRange(inp.selectionStart, end, 'backward'); } else if ( e.keyCode === 46 // Delete && props.reverseFillMask === true && start === end ) { moveCursor.rightReverse(inp, end); inp.setSelectionRange(start, inp.selectionEnd, 'forward'); } } function maskValue (val) { if (val === void 0 || val === null || val === '') { return '' } if (props.reverseFillMask === true) { return maskValueReverse(val) } const mask = computedMask; let valIndex = 0, output = ''; for (let maskIndex = 0; maskIndex < mask.length; maskIndex++) { const valChar = val[ valIndex ], maskDef = mask[ maskIndex ]; if (typeof maskDef === 'string') { output += maskDef; valChar === maskDef && valIndex++; } else if (valChar !== void 0 && maskDef.regex.test(valChar)) { output += maskDef.transform !== void 0 ? maskDef.transform(valChar) : valChar; valIndex++; } else { return output } } return output } function maskValueReverse (val) { const mask = computedMask, firstTokenIndex = maskMarked.indexOf(MARKER); let valIndex = val.length - 1, output = ''; for (let maskIndex = mask.length - 1; maskIndex >= 0 && valIndex > -1; maskIndex--) { const maskDef = mask[ maskIndex ]; let valChar = val[ valIndex ]; if (typeof maskDef === 'string') { output = maskDef + output; valChar === maskDef && valIndex--; } else if (valChar !== void 0 && maskDef.regex.test(valChar)) { do { output = (maskDef.transform !== void 0 ? maskDef.transform(valChar) : valChar) + output; valIndex--; valChar = val[ valIndex ]; // eslint-disable-next-line no-unmodified-loop-condition } while (firstTokenIndex === maskIndex && valChar !== void 0 && maskDef.regex.test(valChar)) } else { return output } } return output } function unmaskValue (val) { return typeof val !== 'string' || computedUnmask === void 0 ? (typeof val === 'number' ? computedUnmask('' + val) : val) : computedUnmask(val) } function fillWithMask (val) { if (maskReplaced.length - val.length <= 0) { return val } return props.reverseFillMask === true && val.length !== 0 ? maskReplaced.slice(0, -val.length) + val : val + maskReplaced.slice(val.length) } return { innerValue, hasMask, moveCursorForPaste, updateMaskValue, onMaskedKeydown, onMaskedClick } } const isJapanese = /[\u3000-\u303f\u3040-\u309f\u30a0-\u30ff\uff00-\uff9f\u4e00-\u9faf\u3400-\u4dbf]/; const isChinese = /[\u4e00-\u9fff\u3400-\u4dbf\u{20000}-\u{2a6df}\u{2a700}-\u{2b73f}\u{2b740}-\u{2b81f}\u{2b820}-\u{2ceaf}\uf900-\ufaff\u3300-\u33ff\ufe30-\ufe4f\uf900-\ufaff\u{2f800}-\u{2fa1f}]/u; const isKorean = /[\u3131-\u314e\u314f-\u3163\uac00-\ud7a3]/; const isPlainText = /[a-z0-9_ -]$/i; function useKeyComposition (onInput) { return function onComposition (e) { if (e.type === 'compositionend' || e.type === 'change') { if (e.target.qComposing !== true) { return } e.target.qComposing = false; onInput(e); } else if ( e.type === 'compositionupdate' && e.target.qComposing !== true && typeof e.data === 'string' ) { const isComposing = client.is.firefox === true ? isPlainText.test(e.data) === false : isJapanese.test(e.data) === true || isChinese.test(e.data) === true || isKorean.test(e.data) === true; if (isComposing === true) { e.target.qComposing = true; } } } } var QInput = createComponent({ name: 'QInput', inheritAttrs: false, props: { ...useFieldProps, ...useMaskProps, ...useFormProps, modelValue: { required: false }, shadowText: String, type: { type: String, default: 'text' }, debounce: [ String, Number ], autogrow: Boolean, // makes a textarea inputClass: [ Array, String, Object ], inputStyle: [ Array, String, Object ] }, emits: [ ...useFieldEmits, 'paste', 'change', 'keydown', 'click', 'animationend' ], setup (props, { emit, attrs }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const temp = {}; let emitCachedValue = NaN, typedNumber, stopValueWatcher, emitTimer = null, emitValueFn; const inputRef = vue.ref(null); const nameProp = useFormInputNameAttr(props); const { innerValue, hasMask, moveCursorForPaste, updateMaskValue, onMaskedKeydown, onMaskedClick } = useMask(props, emit, emitValue, inputRef); const formDomProps = useFileFormDomProps(props, /* type guard */ true); const hasValue = vue.computed(() => fieldValueIsFilled(innerValue.value)); const onComposition = useKeyComposition(onInput); const state = useFieldState(); const isTextarea = vue.computed(() => props.type === 'textarea' || props.autogrow === true ); const isTypeText = vue.computed(() => isTextarea.value === true || [ 'text', 'search', 'url', 'tel', 'password' ].includes(props.type) ); const onEvents = vue.computed(() => { const evt = { ...state.splitAttrs.listeners.value, onInput, onPaste, // Safari < 10.2 & UIWebView doesn't fire compositionend when // switching focus before confirming composition choice // this also fixes the issue where some browsers e.g. iOS Chrome // fires "change" instead of "input" on autocomplete. onChange, onBlur: onFinishEditing, onFocus: stop }; evt.onCompositionstart = evt.onCompositionupdate = evt.onCompositionend = onComposition; if (hasMask.value === true) { evt.onKeydown = onMaskedKeydown; // reset selection anchor on pointer selection evt.onClick = onMaskedClick; } if (props.autogrow === true) { evt.onAnimationend = onAnimationend; } return evt }); const inputAttrs = vue.computed(() => { const attrs = { tabindex: 0, 'data-autofocus': props.autofocus === true || void 0, rows: props.type === 'textarea' ? 6 : void 0, 'aria-label': props.label, name: nameProp.value, ...state.splitAttrs.attributes.value, id: state.targetUid.value, maxlength: props.maxlength, disabled: props.disable === true, readonly: props.readonly === true }; if (isTextarea.value === false) { attrs.type = props.type; } if (props.autogrow === true) { attrs.rows = 1; } return attrs }); // some browsers lose the native input value // so we need to reattach it dynamically // (like type="password" <-> type="text"; see #12078) vue.watch(() => props.type, () => { if (inputRef.value) { inputRef.value.value = props.modelValue; } }); vue.watch(() => props.modelValue, v => { if (hasMask.value === true) { if (stopValueWatcher === true) { stopValueWatcher = false; if (String(v) === emitCachedValue) { return } } updateMaskValue(v); } else if (innerValue.value !== v) { innerValue.value = v; if ( props.type === 'number' && temp.hasOwnProperty('value') === true ) { if (typedNumber === true) { typedNumber = false; } else { delete temp.value; } } } // textarea only props.autogrow === true && vue.nextTick(adjustHeight); }); vue.watch(() => props.autogrow, val => { // textarea only if (val === true) { vue.nextTick(adjustHeight); } // if it has a number of rows set respect it else if (inputRef.value !== null && attrs.rows > 0) { inputRef.value.style.height = 'auto'; } }); vue.watch(() => props.dense, () => { props.autogrow === true && vue.nextTick(adjustHeight); }); function focus () { addFocusFn(() => { const el = document.activeElement; if ( inputRef.value !== null && inputRef.value !== el && (el === null || el.id !== state.targetUid.value) ) { inputRef.value.focus({ preventScroll: true }); } }); } function select () { inputRef.value !== null && inputRef.value.select(); } function onPaste (e) { if (hasMask.value === true && props.reverseFillMask !== true) { const inp = e.target; moveCursorForPaste(inp, inp.selectionStart, inp.selectionEnd); } emit('paste', e); } function onInput (e) { if (!e || !e.target) { return } if (props.type === 'file') { emit('update:modelValue', e.target.files); return } const val = e.target.value; if (e.target.qComposing === true) { temp.value = val; return } if (hasMask.value === true) { updateMaskValue(val, false, e.inputType); } else { emitValue(val); if (isTypeText.value === true && e.target === document.activeElement) { const { selectionStart, selectionEnd } = e.target; if (selectionStart !== void 0 && selectionEnd !== void 0) { vue.nextTick(() => { if (e.target === document.activeElement && val.indexOf(e.target.value) === 0) { e.target.setSelectionRange(selectionStart, selectionEnd); } }); } } } // we need to trigger it immediately too, // to avoid "flickering" props.autogrow === true && adjustHeight(); } function onAnimationend (e) { emit('animationend', e); adjustHeight(); } function emitValue (val, stopWatcher) { emitValueFn = () => { emitTimer = null; if ( props.type !== 'number' && temp.hasOwnProperty('value') === true ) { delete temp.value; } if (props.modelValue !== val && emitCachedValue !== val) { emitCachedValue = val; stopWatcher === true && (stopValueWatcher = true); emit('update:modelValue', val); vue.nextTick(() => { emitCachedValue === val && (emitCachedValue = NaN); }); } emitValueFn = void 0; }; if (props.type === 'number') { typedNumber = true; temp.value = val; } if (props.debounce !== void 0) { emitTimer !== null && clearTimeout(emitTimer); temp.value = val; emitTimer = setTimeout(emitValueFn, props.debounce); } else { emitValueFn(); } } // textarea only function adjustHeight () { requestAnimationFrame(() => { const inp = inputRef.value; if (inp !== null) { const parentStyle = inp.parentNode.style; // chrome does not keep scroll #15498 const { scrollTop } = inp; // chrome calculates a smaller scrollHeight when in a .column container const { overflowY, maxHeight } = $q.platform.is.firefox === true ? {} : window.getComputedStyle(inp); // on firefox or if overflowY is specified as scroll #14263, #14344 // we don't touch overflow // firefox is not so bad in the end const changeOverflow = overflowY !== void 0 && overflowY !== 'scroll'; // reset height of textarea to a small size to detect the real height // but keep the total control size the same changeOverflow === true && (inp.style.overflowY = 'hidden'); parentStyle.marginBottom = (inp.scrollHeight - 1) + 'px'; inp.style.height = '1px'; inp.style.height = inp.scrollHeight + 'px'; // we should allow scrollbars only // if there is maxHeight and content is taller than maxHeight changeOverflow === true && (inp.style.overflowY = parseInt(maxHeight, 10) < inp.scrollHeight ? 'auto' : 'hidden'); parentStyle.marginBottom = ''; inp.scrollTop = scrollTop; } }); } function onChange (e) { onComposition(e); if (emitTimer !== null) { clearTimeout(emitTimer); emitTimer = null; } emitValueFn !== void 0 && emitValueFn(); emit('change', e.target.value); } function onFinishEditing (e) { e !== void 0 && stop(e); if (emitTimer !== null) { clearTimeout(emitTimer); emitTimer = null; } emitValueFn !== void 0 && emitValueFn(); typedNumber = false; stopValueWatcher = false; delete temp.value; // we need to use setTimeout instead of this.$nextTick // to avoid a bug where focusout is not emitted for type date/time/week/... props.type !== 'file' && setTimeout(() => { if (inputRef.value !== null) { inputRef.value.value = innerValue.value !== void 0 ? innerValue.value : ''; } }); } function getCurValue () { return temp.hasOwnProperty('value') === true ? temp.value : (innerValue.value !== void 0 ? innerValue.value : '') } vue.onBeforeUnmount(() => { onFinishEditing(); }); vue.onMounted(() => { // textarea only props.autogrow === true && adjustHeight(); }); Object.assign(state, { innerValue, fieldClass: vue.computed(() => `q-${ isTextarea.value === true ? 'textarea' : 'input' }` + (props.autogrow === true ? ' q-textarea--autogrow' : '') ), hasShadow: vue.computed(() => props.type !== 'file' && typeof props.shadowText === 'string' && props.shadowText.length !== 0 ), inputRef, emitValue, hasValue, floatingLabel: vue.computed(() => ( hasValue.value === true && (props.type !== 'number' || isNaN(innerValue.value) === false) ) || fieldValueIsFilled(props.displayValue) ), getControl: () => { return vue.h(isTextarea.value === true ? 'textarea' : 'input', { ref: inputRef, class: [ 'q-field__native q-placeholder', props.inputClass ], style: props.inputStyle, ...inputAttrs.value, ...onEvents.value, ...( props.type !== 'file' ? { value: getCurValue() } : formDomProps.value ) }) }, getShadowControl: () => { return vue.h('div', { class: 'q-field__native q-field__shadow absolute-bottom no-pointer-events' + (isTextarea.value === true ? '' : ' text-no-wrap') }, [ vue.h('span', { class: 'invisible' }, getCurValue()), vue.h('span', props.shadowText) ]) } }); const renderFn = useField(state); // expose public methods Object.assign(proxy, { focus, select, getNativeElement: () => inputRef.value // deprecated }); injectProp(proxy, 'nativeEl', () => inputRef.value); return renderFn } }); const defaultCfg$1 = { threshold: 0, root: null, rootMargin: '0px' }; function update$3 (el, ctx, value) { let handler, cfg, changed; if (typeof value === 'function') { handler = value; cfg = defaultCfg$1; changed = ctx.cfg === void 0; } else { handler = value.handler; cfg = Object.assign({}, defaultCfg$1, value.cfg); changed = ctx.cfg === void 0 || isDeepEqual(ctx.cfg, cfg) === false; } if (ctx.handler !== handler) { ctx.handler = handler; } if (changed === true) { ctx.cfg = cfg; ctx.observer !== void 0 && ctx.observer.unobserve(el); ctx.observer = new IntersectionObserver(([ entry ]) => { if (typeof ctx.handler === 'function') { // if observed element is part of a vue transition // then we need to be careful... if ( entry.rootBounds === null && document.body.contains(el) === true ) { ctx.observer.unobserve(el); ctx.observer.observe(el); return } const res = ctx.handler(entry, ctx.observer); if ( res === false || (ctx.once === true && entry.isIntersecting === true) ) { destroy$1(el); } } }, cfg); ctx.observer.observe(el); } } function destroy$1 (el) { const ctx = el.__qvisible; if (ctx !== void 0) { ctx.observer !== void 0 && ctx.observer.unobserve(el); delete el.__qvisible; } } var Intersection = createDirective({ name: 'intersection', mounted (el, { modifiers, value }) { const ctx = { once: modifiers.once === true }; update$3(el, ctx, value); el.__qvisible = ctx; }, updated (el, binding) { const ctx = el.__qvisible; ctx !== void 0 && update$3(el, ctx, binding.value); }, beforeUnmount: destroy$1 } ); var QIntersection = createComponent({ name: 'QIntersection', props: { tag: { type: String, default: 'div' }, once: Boolean, transition: String, transitionDuration: { type: [ String, Number ], default: 300 }, ssrPrerender: Boolean, margin: String, threshold: [ Number, Array ], root: { default: null }, disable: Boolean, onVisibility: Function }, setup (props, { slots, emit }) { const showing = vue.ref(isRuntimeSsrPreHydration.value === true ? props.ssrPrerender : false); const intersectionProps = vue.computed(() => ( props.root !== void 0 || props.margin !== void 0 || props.threshold !== void 0 ? { handler: trigger, cfg: { root: props.root, rootMargin: props.margin, threshold: props.threshold } } : trigger )); const hasDirective = vue.computed(() => props.disable !== true && (isRuntimeSsrPreHydration.value !== true || props.once !== true || props.ssrPrerender !== true) ); const directives = vue.computed(() => { // if hasDirective.value === true return [ [ Intersection, intersectionProps.value, void 0, { once: props.once } ] ] }); const transitionStyle = vue.computed( () => `--q-transition-duration: ${ props.transitionDuration }ms` ); function trigger (entry) { if (showing.value !== entry.isIntersecting) { showing.value = entry.isIntersecting; props.onVisibility !== void 0 && emit('visibility', showing.value); } } function getContent () { if (showing.value === true) { return [ vue.h('div', { key: 'content', style: transitionStyle.value }, hSlot(slots.default)) ] } if (slots.hidden !== void 0) { return [ vue.h('div', { key: 'hidden', style: transitionStyle.value }, slots.hidden()) ] } } return () => { const child = props.transition ? [ vue.h(vue.Transition, { name: 'q-transition--' + props.transition }, getContent) ] : getContent(); return hDir( props.tag, { class: 'q-intersection' }, child, 'main', hasDirective.value, () => directives.value ) } } }); var QList = createComponent({ name: 'QList', props: { ...useDarkProps, bordered: Boolean, dense: Boolean, separator: Boolean, padding: Boolean, tag: { type: String, default: 'div' } }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const classes = vue.computed(() => 'q-list' + (props.bordered === true ? ' q-list--bordered' : '') + (props.dense === true ? ' q-list--dense' : '') + (props.separator === true ? ' q-list--separator' : '') + (isDark.value === true ? ' q-list--dark' : '') + (props.padding === true ? ' q-list--padding' : '') ); return () => vue.h(props.tag, { class: classes.value }, hSlot(slots.default)) } }); // PGDOWN, LEFT, DOWN, PGUP, RIGHT, UP const keyCodes$1 = [ 34, 37, 40, 33, 39, 38 ]; const commonPropsName = Object.keys(useCircularCommonProps); var QKnob = createComponent({ name: 'QKnob', props: { ...useFormProps, ...useCircularCommonProps, modelValue: { type: Number, required: true }, innerMin: Number, innerMax: Number, step: { type: Number, default: 1, validator: v => v >= 0 }, tabindex: { type: [ Number, String ], default: 0 }, disable: Boolean, readonly: Boolean }, emits: [ 'update:modelValue', 'change', 'dragValue' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const model = vue.ref(props.modelValue); const dragging = vue.ref(false); const innerMin = vue.computed(() => ( isNaN(props.innerMin) === true || props.innerMin < props.min ? props.min : props.innerMin )); const innerMax = vue.computed(() => ( isNaN(props.innerMax) === true || props.innerMax > props.max ? props.max : props.innerMax )); let centerPosition; function normalizeModel () { model.value = props.modelValue === null ? innerMin.value : between(props.modelValue, innerMin.value, innerMax.value); updateValue(true); } vue.watch( () => `${ props.modelValue }|${ innerMin.value }|${ innerMax.value }`, normalizeModel ); normalizeModel(); const editable = vue.computed(() => props.disable === false && props.readonly === false); const classes = vue.computed(() => 'q-knob non-selectable' + ( editable.value === true ? ' q-knob--editable' : (props.disable === true ? ' disabled' : '') ) ); const decimals = vue.computed(() => (String(props.step).trim().split('.')[ 1 ] || '').length); const step = vue.computed(() => (props.step === 0 ? 1 : props.step)); const instantFeedback = vue.computed(() => props.instantFeedback === true || dragging.value === true); const onEvents = $q.platform.is.mobile === true ? vue.computed(() => (editable.value === true ? { onClick } : {})) : vue.computed(() => ( editable.value === true ? { onMousedown, onClick, onKeydown, onKeyup } : {} )); const attrs = vue.computed(() => ( editable.value === true ? { tabindex: props.tabindex } : { [ `aria-${ props.disable === true ? 'disabled' : 'readonly' }` ]: 'true' } )); const circularProps = vue.computed(() => { const agg = {}; commonPropsName.forEach(name => { agg[ name ] = props[ name ]; }); return agg }); function pan (event) { if (event.isFinal) { updatePosition(event.evt, true); dragging.value = false; } else if (event.isFirst) { updateCenterPosition(); dragging.value = true; updatePosition(event.evt); } else { updatePosition(event.evt); } } const directives = vue.computed(() => { return [ [ TouchPan, pan, void 0, { prevent: true, stop: true, mouse: true } ] ] }); function updateCenterPosition () { const { top, left, width, height } = proxy.$el.getBoundingClientRect(); centerPosition = { top: top + height / 2, left: left + width / 2 }; } function onMousedown (evt) { updateCenterPosition(); updatePosition(evt); } function onClick (evt) { updateCenterPosition(); updatePosition(evt, true); } function onKeydown (evt) { if (!keyCodes$1.includes(evt.keyCode)) { return } stopAndPrevent(evt); const stepVal = ([ 34, 33 ].includes(evt.keyCode) ? 10 : 1) * step.value, offset = [ 34, 37, 40 ].includes(evt.keyCode) ? -stepVal : stepVal; model.value = between( parseFloat((model.value + offset).toFixed(decimals.value)), innerMin.value, innerMax.value ); updateValue(); } function updatePosition (evt, change) { const pos = position(evt), height = Math.abs(pos.top - centerPosition.top), distance = Math.sqrt( height ** 2 + Math.abs(pos.left - centerPosition.left) ** 2 ); let angle = Math.asin(height / distance) * (180 / Math.PI); if (pos.top < centerPosition.top) { angle = centerPosition.left < pos.left ? 90 - angle : 270 + angle; } else { angle = centerPosition.left < pos.left ? angle + 90 : 270 - angle; } if ($q.lang.rtl === true) { angle = normalizeToInterval(-angle - props.angle, 0, 360); } else if (props.angle) { angle = normalizeToInterval(angle - props.angle, 0, 360); } if (props.reverse === true) { angle = 360 - angle; } let newModel = props.min + (angle / 360) * (props.max - props.min); if (step.value !== 0) { const modulo = newModel % step.value; newModel = newModel - modulo + (Math.abs(modulo) >= step.value / 2 ? (modulo < 0 ? -1 : 1) * step.value : 0); newModel = parseFloat(newModel.toFixed(decimals.value)); } newModel = between(newModel, innerMin.value, innerMax.value); emit('dragValue', newModel); if (model.value !== newModel) { model.value = newModel; } updateValue(change); } function onKeyup (evt) { if (keyCodes$1.includes(evt.keyCode)) { updateValue(true); } } function updateValue (change) { props.modelValue !== model.value && emit('update:modelValue', model.value); change === true && emit('change', model.value); } const formAttrs = useFormAttrs(props); function getNameInput () { return vue.h('input', formAttrs.value) } return () => { const data = { class: classes.value, role: 'slider', 'aria-valuemin': innerMin.value, 'aria-valuemax': innerMax.value, 'aria-valuenow': props.modelValue, ...attrs.value, ...circularProps.value, value: model.value, instantFeedback: instantFeedback.value, ...onEvents.value }; const child = { default: slots.default }; if (editable.value === true && props.name !== void 0) { child.internal = getNameInput; } return hDir( QCircularProgress, data, child, 'knob', editable.value, () => directives.value ) } } }); const { passive: passive$2 } = listenOpts; const axisValues = [ 'both', 'horizontal', 'vertical' ]; var QScrollObserver = createComponent({ name: 'QScrollObserver', props: { axis: { type: String, validator: v => axisValues.includes(v), default: 'vertical' }, debounce: [ String, Number ], scrollTarget: { default: void 0 } }, emits: [ 'scroll' ], setup (props, { emit }) { const scroll = { position: { top: 0, left: 0 }, direction: 'down', directionChanged: false, delta: { top: 0, left: 0 }, inflectionPoint: { top: 0, left: 0 } }; let clearTimer = null, localScrollTarget, parentEl; vue.watch(() => props.scrollTarget, () => { unconfigureScrollTarget(); configureScrollTarget(); }); function emitEvent () { clearTimer !== null && clearTimer(); const top = Math.max(0, getVerticalScrollPosition(localScrollTarget)); const left = getHorizontalScrollPosition(localScrollTarget); const delta = { top: top - scroll.position.top, left: left - scroll.position.left }; if ( (props.axis === 'vertical' && delta.top === 0) || (props.axis === 'horizontal' && delta.left === 0) ) { return } const curDir = Math.abs(delta.top) >= Math.abs(delta.left) ? (delta.top < 0 ? 'up' : 'down') : (delta.left < 0 ? 'left' : 'right'); scroll.position = { top, left }; scroll.directionChanged = scroll.direction !== curDir; scroll.delta = delta; if (scroll.directionChanged === true) { scroll.direction = curDir; scroll.inflectionPoint = scroll.position; } emit('scroll', { ...scroll }); } function configureScrollTarget () { localScrollTarget = getScrollTarget(parentEl, props.scrollTarget); localScrollTarget.addEventListener('scroll', trigger, passive$2); trigger(true); } function unconfigureScrollTarget () { if (localScrollTarget !== void 0) { localScrollTarget.removeEventListener('scroll', trigger, passive$2); localScrollTarget = void 0; } } function trigger (immediately) { if (immediately === true || props.debounce === 0 || props.debounce === '0') { emitEvent(); } else if (clearTimer === null) { const [ timer, fn ] = props.debounce ? [ setTimeout(emitEvent, props.debounce), clearTimeout ] : [ requestAnimationFrame(emitEvent), cancelAnimationFrame ]; clearTimer = () => { fn(timer); clearTimer = null; }; } } const { proxy } = vue.getCurrentInstance(); vue.watch(() => proxy.$q.lang.rtl, emitEvent); vue.onMounted(() => { parentEl = proxy.$el.parentNode; configureScrollTarget(); }); vue.onBeforeUnmount(() => { clearTimer !== null && clearTimer(); unconfigureScrollTarget(); }); // expose public methods Object.assign(proxy, { trigger, getPosition: () => scroll }); return noop } }); var QLayout = 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 } } = vue.getCurrentInstance(); const rootRef = vue.ref(null); // page related const height = vue.ref($q.screen.height); const width = vue.ref(props.container === true ? 0 : $q.screen.width); const scroll = vue.ref({ position: 0, direction: 'down', inflectionPoint: 0 }); // container only prop const containerHeight = vue.ref(0); const scrollbarWidth = vue.ref(isRuntimeSsrPreHydration.value === true ? 0 : getScrollbarWidth()); const classes = vue.computed(() => 'q-layout q-layout--' + (props.container === true ? 'containerized' : 'standard') ); const style = vue.computed(() => ( props.container === false ? { minHeight: $q.screen.height + 'px' } : null )); // used by container only const targetStyle = vue.computed(() => ( scrollbarWidth.value !== 0 ? { [ $q.lang.rtl === true ? 'left' : 'right' ]: `${ scrollbarWidth.value }px` } : null )); const targetChildStyle = vue.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; 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; 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 }) { if (containerHeight.value !== height) { containerHeight.value = height; updateScrollbarWidth(); } } function updateScrollbarWidth () { if (props.container === true) { const width = height.value > containerHeight.value ? getScrollbarWidth() : 0; if (scrollbarWidth.value !== width) { scrollbarWidth.value = width; } } } let animateTimer = null; const $layout = { instances: {}, view: vue.computed(() => props.view), isContainer: vue.computed(() => props.container), rootRef, height, containerHeight, scrollbarWidth, totalWidth: vue.computed(() => width.value + scrollbarWidth.value), rows: vue.computed(() => { const rows = props.view.toLowerCase().split(' '); return { top: rows[ 0 ].split(''), middle: rows[ 1 ].split(''), bottom: rows[ 2 ].split('') } }), header: vue.reactive({ size: 0, offset: 0, space: false }), right: vue.reactive({ size: 300, offset: 0, space: false }), footer: vue.reactive({ size: 0, offset: 0, space: false }), left: vue.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; } }; vue.provide(layoutKey, $layout); // prevent scrollbar flicker while resizing window height // if no page scrollbar is already present if (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); } vue.watch( () => (props.container !== true ? 'add' : 'remove'), updateScrollEvent ); props.container !== true && updateScrollEvent('add'); vue.onUnmounted(() => { updateScrollEvent('remove'); }); } return () => { const content = hMergeSlot(slots.default, [ vue.h(QScrollObserver, { onScroll: onPageScroll }), vue.h(QResizeObserver, { onResize: onPageResize }) ]); const layout = vue.h('div', { class: classes.value, style: style.value, ref: props.container === true ? void 0 : rootRef, tabindex: -1 }, content); if (props.container === true) { return vue.h('div', { class: 'q-layout-container overflow-hidden', ref: rootRef }, [ vue.h(QResizeObserver, { onResize: onContainerResize }), vue.h('div', { class: 'absolute-full', style: targetStyle.value }, [ vue.h('div', { class: 'scroll', style: targetChildStyle.value }, [ layout ]) ]) ]) } return layout } } }); const separatorValues = [ 'horizontal', 'vertical', 'cell', 'none' ]; var QMarkupTable = createComponent({ name: 'QMarkupTable', props: { ...useDarkProps, dense: Boolean, flat: Boolean, bordered: Boolean, square: Boolean, wrapCells: Boolean, separator: { type: String, default: 'horizontal', validator: v => separatorValues.includes(v) } }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const classes = vue.computed(() => 'q-markup-table q-table__container q-table__card' + ` q-table--${ props.separator }-separator` + (isDark.value === true ? ' q-table--dark q-table__card--dark q-dark' : '') + (props.dense === true ? ' q-table--dense' : '') + (props.flat === true ? ' q-table--flat' : '') + (props.bordered === true ? ' q-table--bordered' : '') + (props.square === true ? ' q-table--square' : '') + (props.wrapCells === false ? ' q-table--no-wrap' : '') ); return () => vue.h('div', { class: classes.value }, [ vue.h('table', { class: 'q-table' }, hSlot(slots.default)) ]) } }); var QNoSsr = createComponent({ name: 'QNoSsr', props: { tag: { type: String, default: 'div' }, placeholder: String }, setup (props, { slots }) { const canRender = useCanRender(); return () => { const data = {}; if (canRender.value === true) { const node = hSlot(slots.default); return node === void 0 ? node : (node.length > 1 ? vue.h(props.tag, data, node) : node[ 0 ]) } data.class = 'q-no-ssr-placeholder'; const node = hSlot(slots.placeholder); if (node !== void 0) { return node.length > 1 ? vue.h(props.tag, data, node) : node[ 0 ] } if (props.placeholder !== void 0) { return vue.h(props.tag, data, props.placeholder) } } } }); const svg$m = vue.h('svg', { key: 'svg', class: 'q-radio__bg absolute non-selectable', viewBox: '0 0 24 24' }, [ vue.h('path', { d: 'M12,22a10,10 0 0 1 -10,-10a10,10 0 0 1 10,-10a10,10 0 0 1 10,10a10,10 0 0 1 -10,10m0,-22a12,12 0 0 0 -12,12a12,12 0 0 0 12,12a12,12 0 0 0 12,-12a12,12 0 0 0 -12,-12' }), vue.h('path', { class: 'q-radio__check', d: 'M12,6a6,6 0 0 0 -6,6a6,6 0 0 0 6,6a6,6 0 0 0 6,-6a6,6 0 0 0 -6,-6' }) ]); var QRadio = createComponent({ name: 'QRadio', props: { ...useDarkProps, ...useSizeProps, ...useFormProps, modelValue: { required: true }, val: { required: true }, label: String, leftLabel: Boolean, checkedIcon: String, uncheckedIcon: String, color: String, keepColor: Boolean, dense: Boolean, disable: Boolean, tabindex: [ String, Number ] }, emits: [ 'update:modelValue' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const isDark = useDark(props, proxy.$q); const sizeStyle = useSize(props, optionSizes); const rootRef = vue.ref(null); const { refocusTargetEl, refocusTarget } = useRefocusTarget(props, rootRef); const isTrue = vue.computed(() => vue.toRaw(props.modelValue) === vue.toRaw(props.val)); const classes = vue.computed(() => 'q-radio cursor-pointer no-outline row inline no-wrap items-center' + (props.disable === true ? ' disabled' : '') + (isDark.value === true ? ' q-radio--dark' : '') + (props.dense === true ? ' q-radio--dense' : '') + (props.leftLabel === true ? ' reverse' : '') ); const innerClass = vue.computed(() => { const color = props.color !== void 0 && ( props.keepColor === true || isTrue.value === true ) ? ` text-${ props.color }` : ''; return 'q-radio__inner relative-position ' + `q-radio__inner--${ isTrue.value === true ? 'truthy' : 'falsy' }${ color }` }); const icon = vue.computed(() => (isTrue.value === true ? props.checkedIcon : props.uncheckedIcon ) || null ); const tabindex = vue.computed(() => ( props.disable === true ? -1 : props.tabindex || 0 )); const formAttrs = vue.computed(() => { const prop = { type: 'radio' }; props.name !== void 0 && Object.assign(prop, { // see https://vuejs.org/guide/extras/render-function.html#creating-vnodes (.prop) '.checked': isTrue.value === true, '^checked': isTrue.value === true ? 'checked' : void 0, name: props.name, value: props.val }); return prop }); const injectFormInput = useFormInject(formAttrs); function onClick (e) { if (e !== void 0) { stopAndPrevent(e); refocusTarget(e); } if (props.disable !== true && isTrue.value !== true) { emit('update:modelValue', props.val, e); } } function onKeydown (e) { if (e.keyCode === 13 || e.keyCode === 32) { stopAndPrevent(e); } } function onKeyup (e) { if (e.keyCode === 13 || e.keyCode === 32) { onClick(e); } } // expose public methods Object.assign(proxy, { set: onClick }); return () => { const content = icon.value !== null ? [ vue.h('div', { key: 'icon', class: 'q-radio__icon-container absolute-full flex flex-center no-wrap' }, [ vue.h(QIcon, { class: 'q-radio__icon', name: icon.value }) ]) ] : [ svg$m ]; props.disable !== true && injectFormInput( content, 'unshift', ' q-radio__native q-ma-none q-pa-none' ); const child = [ vue.h('div', { class: innerClass.value, style: sizeStyle.value, 'aria-hidden': 'true' }, content) ]; if (refocusTargetEl.value !== null) { child.push(refocusTargetEl.value); } const label = props.label !== void 0 ? hMergeSlot(slots.default, [ props.label ]) : hSlot(slots.default); label !== void 0 && child.push( vue.h('div', { class: 'q-radio__label q-anchor--skip' }, label) ); return vue.h('div', { ref: rootRef, class: classes.value, tabindex: tabindex.value, role: 'radio', 'aria-label': props.label, 'aria-checked': isTrue.value === true ? 'true' : 'false', 'aria-disabled': props.disable === true ? 'true' : void 0, onClick, onKeydown, onKeyup }, child) } } }); var QToggle = createComponent({ name: 'QToggle', props: { ...useCheckboxProps, icon: String, iconColor: String }, emits: useCheckboxEmits, setup (props) { function getInner (isTrue, isIndeterminate) { const icon = vue.computed(() => (isTrue.value === true ? props.checkedIcon : (isIndeterminate.value === true ? props.indeterminateIcon : props.uncheckedIcon) ) || props.icon ); const color = vue.computed(() => (isTrue.value === true ? props.iconColor : null)); return () => [ vue.h('div', { class: 'q-toggle__track' }), vue.h('div', { class: 'q-toggle__thumb absolute flex flex-center no-wrap' }, icon.value !== void 0 ? [ vue.h(QIcon, { name: icon.value, color: color.value }) ] : void 0 ) ] } return useCheckbox('toggle', getInner) } }); const components$1 = { radio: QRadio, checkbox: QCheckbox, toggle: QToggle }; const typeValues = Object.keys(components$1); var QOptionGroup = createComponent({ name: 'QOptionGroup', props: { ...useDarkProps, modelValue: { required: true }, options: { type: Array, validator: opts => opts.every(opt => 'value' in opt && 'label' in opt) }, name: String, type: { default: 'radio', validator: v => typeValues.includes(v) }, color: String, keepColor: Boolean, dense: Boolean, size: String, leftLabel: Boolean, inline: Boolean, disable: Boolean }, emits: [ 'update:modelValue' ], setup (props, { emit, slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const arrayModel = Array.isArray(props.modelValue); if (props.type === 'radio') { if (arrayModel === true) { console.error('q-option-group: model should not be array'); } } else if (arrayModel === false) { console.error('q-option-group: model should be array in your case'); } const isDark = useDark(props, $q); const component = vue.computed(() => components$1[ props.type ]); const classes = vue.computed(() => 'q-option-group q-gutter-x-sm' + (props.inline === true ? ' q-option-group--inline' : '') ); const attrs = vue.computed(() => { const attrs = { role: 'group' }; if (props.type === 'radio') { attrs.role = 'radiogroup'; if (props.disable === true) { attrs[ 'aria-disabled' ] = 'true'; } } return attrs }); function onUpdateModelValue (value) { emit('update:modelValue', value); } return () => vue.h('div', { class: classes.value, ...attrs.value }, props.options.map((opt, i) => { // TODO: (Qv3) Make the 'opt' a separate property instead of // the whole scope for consistency and flexibility // (e.g. { opt } instead of opt) const child = slots[ 'label-' + i ] !== void 0 ? () => slots[ 'label-' + i ](opt) : ( slots.label !== void 0 ? () => slots.label(opt) : void 0 ); return vue.h('div', [ vue.h(component.value, { modelValue: props.modelValue, val: opt.value, name: opt.name === void 0 ? props.name : opt.name, disable: props.disable || opt.disable, label: child === void 0 ? opt.label : null, leftLabel: opt.leftLabel === void 0 ? props.leftLabel : opt.leftLabel, color: opt.color === void 0 ? props.color : opt.color, checkedIcon: opt.checkedIcon, uncheckedIcon: opt.uncheckedIcon, dark: opt.dark || isDark.value, size: opt.size === void 0 ? props.size : opt.size, dense: props.dense, keepColor: opt.keepColor === void 0 ? props.keepColor : opt.keepColor, 'onUpdate:modelValue': onUpdateModelValue }, child) ]) })) } }); var QPage = createComponent({ name: 'QPage', props: { padding: Boolean, styleFn: Function }, setup (props, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QPage needs to be a deep child of QLayout'); return emptyRenderFn } const $pageContainer = vue.inject(pageContainerKey, emptyRenderFn); if ($pageContainer === emptyRenderFn) { console.error('QPage needs to be child of QPageContainer'); return emptyRenderFn } const style = vue.computed(() => { const offset = ($layout.header.space === true ? $layout.header.size : 0) + ($layout.footer.space === true ? $layout.footer.size : 0); if (typeof props.styleFn === 'function') { const height = $layout.isContainer.value === true ? $layout.containerHeight.value : $q.screen.height; return props.styleFn(offset, height) } return { minHeight: $layout.isContainer.value === true ? ($layout.containerHeight.value - offset) + 'px' : ( $q.screen.height === 0 ? (offset !== 0 ? `calc(100vh - ${ offset }px)` : '100vh') : ($q.screen.height - offset) + 'px' ) } }); const classes = vue.computed(() => `q-page${ props.padding === true ? ' q-layout-padding' : '' }` ); return () => vue.h('main', { class: classes.value, style: style.value }, hSlot(slots.default)) } }); var QPageContainer = createComponent({ name: 'QPageContainer', setup (_, { slots }) { const { proxy: { $q } } = vue.getCurrentInstance(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QPageContainer needs to be child of QLayout'); return emptyRenderFn } vue.provide(pageContainerKey, true); const style = vue.computed(() => { const css = {}; if ($layout.header.space === true) { css.paddingTop = `${ $layout.header.size }px`; } if ($layout.right.space === true) { css[ `padding${ $q.lang.rtl === true ? 'Left' : 'Right' }` ] = `${ $layout.right.size }px`; } if ($layout.footer.space === true) { css.paddingBottom = `${ $layout.footer.size }px`; } if ($layout.left.space === true) { css[ `padding${ $q.lang.rtl === true ? 'Right' : 'Left' }` ] = `${ $layout.left.size }px`; } return css }); return () => vue.h('div', { class: 'q-page-container', style: style.value }, hSlot(slots.default)) } }); const usePageStickyProps = { position: { type: String, default: 'bottom-right', validator: v => [ 'top-right', 'top-left', 'bottom-right', 'bottom-left', 'top', 'right', 'bottom', 'left' ].includes(v) }, offset: { type: Array, validator: v => v.length === 2 }, expand: Boolean }; function usePageSticky () { const { props, proxy: { $q } } = vue.getCurrentInstance(); const $layout = vue.inject(layoutKey, emptyRenderFn); if ($layout === emptyRenderFn) { console.error('QPageSticky needs to be child of QLayout'); return emptyRenderFn } const attach = vue.computed(() => { const pos = props.position; return { top: pos.indexOf('top') > -1, right: pos.indexOf('right') > -1, bottom: pos.indexOf('bottom') > -1, left: pos.indexOf('left') > -1, vertical: pos === 'top' || pos === 'bottom', horizontal: pos === 'left' || pos === 'right' } }); const top = vue.computed(() => $layout.header.offset); const right = vue.computed(() => $layout.right.offset); const bottom = vue.computed(() => $layout.footer.offset); const left = vue.computed(() => $layout.left.offset); const style = vue.computed(() => { let posX = 0, posY = 0; const side = attach.value; const dir = $q.lang.rtl === true ? -1 : 1; if (side.top === true && top.value !== 0) { posY = `${ top.value }px`; } else if (side.bottom === true && bottom.value !== 0) { posY = `${ -bottom.value }px`; } if (side.left === true && left.value !== 0) { posX = `${ dir * left.value }px`; } else if (side.right === true && right.value !== 0) { posX = `${ -dir * right.value }px`; } const css = { transform: `translate(${ posX }, ${ posY })` }; if (props.offset) { css.margin = `${ props.offset[ 1 ] }px ${ props.offset[ 0 ] }px`; } if (side.vertical === true) { if (left.value !== 0) { css[ $q.lang.rtl === true ? 'right' : 'left' ] = `${ left.value }px`; } if (right.value !== 0) { css[ $q.lang.rtl === true ? 'left' : 'right' ] = `${ right.value }px`; } } else if (side.horizontal === true) { if (top.value !== 0) { css.top = `${ top.value }px`; } if (bottom.value !== 0) { css.bottom = `${ bottom.value }px`; } } return css }); const classes = vue.computed(() => `q-page-sticky row flex-center fixed-${ props.position }` + ` q-page-sticky--${ props.expand === true ? 'expand' : 'shrink' }` ); function getStickyContent (slots) { const content = hSlot(slots.default); return vue.h('div', { class: classes.value, style: style.value }, props.expand === true ? content : [ vue.h('div', content) ] ) } return { $layout, getStickyContent } } var QPageScroller = createComponent({ name: 'QPageScroller', props: { ...usePageStickyProps, scrollOffset: { type: Number, default: 1000 }, reverse: Boolean, duration: { type: Number, default: 300 }, offset: { default: () => [ 18, 18 ] } }, emits: [ 'click' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const { $layout, getStickyContent } = usePageSticky(); const rootRef = vue.ref(null); let heightWatcher; const scrollHeight = vue.computed(() => $layout.height.value - ( $layout.isContainer.value === true ? $layout.containerHeight.value : $q.screen.height )); function isVisible () { return props.reverse === true ? scrollHeight.value - $layout.scroll.value.position > props.scrollOffset : $layout.scroll.value.position > props.scrollOffset } const showing = vue.ref(isVisible()); function updateVisibility () { const newVal = isVisible(); if (showing.value !== newVal) { showing.value = newVal; } } function updateReverse () { if (props.reverse === true) { if (heightWatcher === void 0) { heightWatcher = vue.watch(scrollHeight, updateVisibility); } } else { cleanup(); } } vue.watch($layout.scroll, updateVisibility); vue.watch(() => props.reverse, updateReverse); function cleanup () { if (heightWatcher !== void 0) { heightWatcher(); heightWatcher = void 0; } } function onClick (e) { const target = getScrollTarget( $layout.isContainer.value === true ? rootRef.value : $layout.rootRef.value ); setVerticalScrollPosition( target, props.reverse === true ? $layout.height.value : 0, props.duration ); emit('click', e); } function getContent () { return showing.value === true ? vue.h('div', { ref: rootRef, class: 'q-page-scroller', onClick }, getStickyContent(slots)) : null } updateReverse(); vue.onBeforeUnmount(cleanup); return () => vue.h( vue.Transition, { name: 'q-transition--fade' }, getContent ) } }); var QPageSticky = createComponent({ name: 'QPageSticky', props: usePageStickyProps, setup (_, { slots }) { const { getStickyContent } = usePageSticky(); return () => getStickyContent(slots) } }); function getBool (val, otherwise) { return [ true, false ].includes(val) ? val : otherwise } var QPagination = createComponent({ name: 'QPagination', props: { ...useDarkProps, modelValue: { type: Number, required: true }, min: { type: [ Number, String ], default: 1 }, max: { type: [ Number, String ], required: true }, maxPages: { type: [ Number, String ], default: 0, validator: v => ( (typeof v === 'string' ? parseInt(v, 10) : v) >= 0 ) }, inputStyle: [ Array, String, Object ], inputClass: [ Array, String, Object ], size: String, disable: Boolean, input: Boolean, iconPrev: String, iconNext: String, iconFirst: String, iconLast: String, toFn: Function, boundaryLinks: { type: Boolean, default: null }, boundaryNumbers: { type: Boolean, default: null }, directionLinks: { type: Boolean, default: null }, ellipses: { type: Boolean, default: null }, ripple: { type: [ Boolean, Object ], default: null }, round: Boolean, rounded: Boolean, flat: Boolean, outline: Boolean, unelevated: Boolean, push: Boolean, glossy: Boolean, color: { type: String, default: 'primary' }, textColor: String, activeDesign: { type: String, default: '', values: v => v === '' || btnDesignOptions.includes(v) }, activeColor: String, activeTextColor: String, gutter: String, padding: { type: String, default: '3px 2px' } }, emits: [ 'update:modelValue' ], setup (props, { emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const minProp = vue.computed(() => parseInt(props.min, 10)); const maxProp = vue.computed(() => parseInt(props.max, 10)); const maxPagesProp = vue.computed(() => parseInt(props.maxPages, 10)); const inputPlaceholder = vue.computed(() => model.value + ' / ' + maxProp.value); const boundaryLinksProp = vue.computed(() => getBool(props.boundaryLinks, props.input)); const boundaryNumbersProp = vue.computed(() => getBool(props.boundaryNumbers, !props.input)); const directionLinksProp = vue.computed(() => getBool(props.directionLinks, props.input)); const ellipsesProp = vue.computed(() => getBool(props.ellipses, !props.input)); const newPage = vue.ref(null); const model = vue.computed({ get: () => props.modelValue, set: val => { val = parseInt(val, 10); if (props.disable || isNaN(val)) { return } const value = between(val, minProp.value, maxProp.value); if (props.modelValue !== value) { emit('update:modelValue', value); } } }); vue.watch(() => `${ minProp.value }|${ maxProp.value }`, () => { model.value = props.modelValue; }); const classes = vue.computed(() => 'q-pagination row no-wrap items-center' + (props.disable === true ? ' disabled' : '') ); const gutterProp = vue.computed(() => ( props.gutter in btnPadding ? `${ btnPadding[ props.gutter ] }px` : props.gutter || null )); const gutterStyle = vue.computed(() => ( gutterProp.value !== null ? `--q-pagination-gutter-parent:-${ gutterProp.value };--q-pagination-gutter-child:${ gutterProp.value }` : null )); const icons = vue.computed(() => { const ico = [ props.iconFirst || $q.iconSet.pagination.first, props.iconPrev || $q.iconSet.pagination.prev, props.iconNext || $q.iconSet.pagination.next, props.iconLast || $q.iconSet.pagination.last ]; return $q.lang.rtl === true ? ico.reverse() : ico }); const attrs = vue.computed(() => ({ 'aria-disabled': props.disable === true ? 'true' : 'false', role: 'navigation' })); const btnDesignProp = vue.computed(() => getBtnDesign(props, 'flat')); const btnProps = vue.computed(() => ({ [ btnDesignProp.value ]: true, round: props.round, rounded: props.rounded, padding: props.padding, color: props.color, textColor: props.textColor, size: props.size, ripple: props.ripple !== null ? props.ripple : true })); const btnActiveDesignProp = vue.computed(() => { // we also reset non-active design const acc = { [ btnDesignProp.value ]: false }; if (props.activeDesign !== '') { acc[ props.activeDesign ] = true; } return acc }); const activeBtnProps = vue.computed(() => ({ ...btnActiveDesignProp.value, color: props.activeColor || props.color, textColor: props.activeTextColor || props.textColor })); const btnConfig = vue.computed(() => { let maxPages = Math.max( maxPagesProp.value, 1 + (ellipsesProp.value ? 2 : 0) + (boundaryNumbersProp.value ? 2 : 0) ); const acc = { pgFrom: minProp.value, pgTo: maxProp.value, ellipsesStart: false, ellipsesEnd: false, boundaryStart: false, boundaryEnd: false, marginalStyle: { minWidth: `${ Math.max(2, String(maxProp.value).length) }em` } }; if (maxPagesProp.value && maxPages < (maxProp.value - minProp.value + 1)) { maxPages = 1 + Math.floor(maxPages / 2) * 2; acc.pgFrom = Math.max(minProp.value, Math.min(maxProp.value - maxPages + 1, props.modelValue - Math.floor(maxPages / 2))); acc.pgTo = Math.min(maxProp.value, acc.pgFrom + maxPages - 1); if (boundaryNumbersProp.value) { acc.boundaryStart = true; acc.pgFrom++; } if (ellipsesProp.value && acc.pgFrom > (minProp.value + (boundaryNumbersProp.value ? 1 : 0))) { acc.ellipsesStart = true; acc.pgFrom++; } if (boundaryNumbersProp.value) { acc.boundaryEnd = true; acc.pgTo--; } if (ellipsesProp.value && acc.pgTo < (maxProp.value - (boundaryNumbersProp.value ? 1 : 0))) { acc.ellipsesEnd = true; acc.pgTo--; } } return acc }); function set (value) { model.value = value; } function setByOffset (offset) { model.value = model.value + offset; } const inputEvents = vue.computed(() => { function updateModel () { model.value = newPage.value; newPage.value = null; } return { 'onUpdate:modelValue': val => { newPage.value = val; }, onKeyup: e => { isKeyCode(e, 13) === true && updateModel(); }, onBlur: updateModel } }); function getBtn (cfg, page, active) { const data = { 'aria-label': page, 'aria-current': 'false', ...btnProps.value, ...cfg }; if (active === true) { Object.assign(data, { 'aria-current': 'true', ...activeBtnProps.value }); } if (page !== void 0) { if (props.toFn !== void 0) { data.to = props.toFn(page); } else { data.onClick = () => { set(page); }; } } return vue.h(QBtn, data) } // expose public methods Object.assign(proxy, { set, setByOffset }); return () => { const contentStart = []; const contentEnd = []; let contentMiddle; if (boundaryLinksProp.value === true) { contentStart.push( getBtn({ key: 'bls', disable: props.disable || props.modelValue <= minProp.value, icon: icons.value[ 0 ] }, minProp.value) ); contentEnd.unshift( getBtn({ key: 'ble', disable: props.disable || props.modelValue >= maxProp.value, icon: icons.value[ 3 ] }, maxProp.value) ); } if (directionLinksProp.value === true) { contentStart.push( getBtn({ key: 'bdp', disable: props.disable || props.modelValue <= minProp.value, icon: icons.value[ 1 ] }, props.modelValue - 1) ); contentEnd.unshift( getBtn({ key: 'bdn', disable: props.disable || props.modelValue >= maxProp.value, icon: icons.value[ 2 ] }, props.modelValue + 1) ); } if (props.input !== true) { // has buttons instead of inputbox contentMiddle = []; const { pgFrom, pgTo, marginalStyle: style } = btnConfig.value; if (btnConfig.value.boundaryStart === true) { const active = minProp.value === props.modelValue; contentStart.push( getBtn({ key: 'bns', style, disable: props.disable, label: minProp.value }, minProp.value, active) ); } if (btnConfig.value.boundaryEnd === true) { const active = maxProp.value === props.modelValue; contentEnd.unshift( getBtn({ key: 'bne', style, disable: props.disable, label: maxProp.value }, maxProp.value, active) ); } if (btnConfig.value.ellipsesStart === true) { contentStart.push( getBtn({ key: 'bes', style, disable: props.disable, label: '…', ripple: false }, pgFrom - 1) ); } if (btnConfig.value.ellipsesEnd === true) { contentEnd.unshift( getBtn({ key: 'bee', style, disable: props.disable, label: '…', ripple: false }, pgTo + 1) ); } for (let i = pgFrom; i <= pgTo; i++) { contentMiddle.push( getBtn({ key: `bpg${ i }`, style, disable: props.disable, label: i }, i, i === props.modelValue) ); } } return vue.h('div', { class: classes.value, ...attrs.value }, [ vue.h('div', { class: 'q-pagination__content row no-wrap items-center', style: gutterStyle.value }, [ ...contentStart, props.input === true ? vue.h(QInput, { class: 'inline', style: { width: `${ inputPlaceholder.value.length / 1.5 }em` }, type: 'number', dense: true, value: newPage.value, disable: props.disable, dark: isDark.value, borderless: true, inputClass: props.inputClass, inputStyle: props.inputStyle, placeholder: inputPlaceholder.value, min: minProp.value, max: maxProp.value, ...inputEvents.value }) : vue.h('div', { class: 'q-pagination__middle row justify-center' }, contentMiddle), ...contentEnd ]) ]) } } }); function frameDebounce (fn) { let wait = false, frame, callArgs; function debounced (/* ...args */) { callArgs = arguments; if (wait === true) { return } wait = true; frame = requestAnimationFrame(() => { fn.apply(this, callArgs); callArgs = void 0; wait = false; }); } debounced.cancel = () => { window.cancelAnimationFrame(frame); wait = false; }; return debounced } const { passive: passive$1 } = listenOpts; var QParallax = createComponent({ name: 'QParallax', props: { src: String, height: { type: Number, default: 500 }, speed: { type: Number, default: 1, validator: v => v >= 0 && v <= 1 }, scrollTarget: { default: void 0 }, onScroll: Function }, setup (props, { slots, emit }) { const percentScrolled = vue.ref(0); const rootRef = vue.ref(null); const mediaParentRef = vue.ref(null); const mediaRef = vue.ref(null); let isWorking, mediaEl, mediaHeight, resizeHandler, observer, localScrollTarget; vue.watch(() => props.height, () => { isWorking === true && updatePos(); }); vue.watch(() => props.scrollTarget, () => { if (isWorking === true) { stop(); start(); } }); let update = percentage => { percentScrolled.value = percentage; props.onScroll !== void 0 && emit('scroll', percentage); }; function updatePos () { let containerTop, containerHeight, containerBottom; if (localScrollTarget === window) { containerTop = 0; containerBottom = containerHeight = window.innerHeight; } else { containerTop = offset(localScrollTarget).top; containerHeight = height(localScrollTarget); containerBottom = containerTop + containerHeight; } const top = offset(rootRef.value).top; const bottom = top + props.height; if (observer !== void 0 || (bottom > containerTop && top < containerBottom)) { const percent = (containerBottom - top) / (props.height + containerHeight); setPos((mediaHeight - props.height) * percent * props.speed); update(percent); } } let setPos = offset => { // apply it immediately without any delay mediaEl.style.transform = `translate3d(-50%,${ Math.round(offset) }px,0)`; }; function onResize () { mediaHeight = mediaEl.naturalHeight || mediaEl.videoHeight || height(mediaEl); isWorking === true && updatePos(); } function start () { isWorking = true; localScrollTarget = getScrollTarget(rootRef.value, props.scrollTarget); localScrollTarget.addEventListener('scroll', updatePos, passive$1); window.addEventListener('resize', resizeHandler, passive$1); updatePos(); } function stop () { if (isWorking === true) { isWorking = false; localScrollTarget.removeEventListener('scroll', updatePos, passive$1); window.removeEventListener('resize', resizeHandler, passive$1); localScrollTarget = void 0; setPos.cancel(); update.cancel(); resizeHandler.cancel(); } } vue.onMounted(() => { setPos = frameDebounce(setPos); update = frameDebounce(update); resizeHandler = frameDebounce(onResize); mediaEl = slots.media !== void 0 ? mediaParentRef.value.children[ 0 ] : mediaRef.value; mediaEl.onload = mediaEl.onloadstart = mediaEl.loadedmetadata = onResize; onResize(); mediaEl.style.display = 'initial'; if (window.IntersectionObserver !== void 0) { observer = new IntersectionObserver(entries => { const fn = entries[ 0 ].isIntersecting === true ? start : stop; fn(); }); observer.observe(rootRef.value); } else { start(); } }); vue.onBeforeUnmount(() => { stop(); observer !== void 0 && observer.disconnect(); mediaEl.onload = mediaEl.onloadstart = mediaEl.loadedmetadata = null; }); return () => { return vue.h('div', { ref: rootRef, class: 'q-parallax', style: { height: `${ props.height }px` } }, [ vue.h('div', { ref: mediaParentRef, class: 'q-parallax__media absolute-full' }, slots.media !== void 0 ? slots.media() : [ vue.h('img', { ref: mediaRef, src: props.src }) ]), vue.h( 'div', { class: 'q-parallax__content absolute-full column flex-center' }, slots.content !== void 0 ? slots.content({ percentScrolled: percentScrolled.value }) : hSlot(slots.default) ) ]) } } }); // adapted from https://stackoverflow.com/a/40294058 function cloneDeep (data, hash = new WeakMap()) { if (Object(data) !== data) return data if (hash.has(data)) return hash.get(data) const result = data instanceof Date ? new Date(data) : (data instanceof RegExp ? new RegExp(data.source, data.flags) : (data instanceof Set ? new Set() : (data instanceof Map ? new Map() : (typeof data.constructor !== 'function' ? Object.create(null) : (data.prototype !== void 0 && typeof data.prototype.constructor === 'function' ? data : new data.constructor() ) ) ) ) ); if (typeof data.constructor === 'function' && typeof data.valueOf === 'function') { const val = data.valueOf(); if (Object(val) !== val) { const result = new data.constructor(val); hash.set(data, result); return result } } hash.set(data, result); if (data instanceof Set) { data.forEach(val => { result.add(cloneDeep(val, hash)); }); } else if (data instanceof Map) { data.forEach((val, key) => { result.set(key, cloneDeep(val, hash)); }); } return Object.assign( result, ...Object.keys(data).map(key => ({ [ key ]: cloneDeep(data[ key ], hash) })) ) } var QPopupEdit = createComponent({ name: 'QPopupEdit', props: { modelValue: { required: true }, title: String, buttons: Boolean, labelSet: String, labelCancel: String, color: { type: String, default: 'primary' }, validate: { type: Function, default: () => true }, autoSave: Boolean, /* menu props overrides */ cover: { type: Boolean, default: true }, /* end of menu props */ disable: Boolean }, emits: [ 'update:modelValue', 'save', 'cancel', 'beforeShow', 'show', 'beforeHide', 'hide' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const menuRef = vue.ref(null); const initialValue = vue.ref(''); const currentModel = vue.ref(''); let validated = false; const scope = vue.computed(() => { return injectProp({ initialValue: initialValue.value, validate: props.validate, set, cancel, updatePosition }, 'value', () => currentModel.value, val => { currentModel.value = val; }) }); function set () { if (props.validate(currentModel.value) === false) { return } if (hasModelChanged() === true) { emit('save', currentModel.value, initialValue.value); emit('update:modelValue', currentModel.value); } closeMenu(); } function cancel () { if (hasModelChanged() === true) { emit('cancel', currentModel.value, initialValue.value); } closeMenu(); } function updatePosition () { vue.nextTick(() => { menuRef.value.updatePosition(); }); } function hasModelChanged () { return isDeepEqual(currentModel.value, initialValue.value) === false } function closeMenu () { validated = true; menuRef.value.hide(); } function onBeforeShow () { validated = false; initialValue.value = cloneDeep(props.modelValue); currentModel.value = cloneDeep(props.modelValue); emit('beforeShow'); } function onShow () { emit('show'); } function onBeforeHide () { if (validated === false && hasModelChanged() === true) { if (props.autoSave === true && props.validate(currentModel.value) === true) { emit('save', currentModel.value, initialValue.value); emit('update:modelValue', currentModel.value); } else { emit('cancel', currentModel.value, initialValue.value); } } emit('beforeHide'); } function onHide () { emit('hide'); } function getContent () { const child = slots.default !== void 0 ? [].concat(slots.default(scope.value)) : []; props.title && child.unshift( vue.h('div', { class: 'q-dialog__title q-mt-sm q-mb-sm' }, props.title) ); props.buttons === true && child.push( vue.h('div', { class: 'q-popup-edit__buttons row justify-center no-wrap' }, [ vue.h(QBtn, { flat: true, color: props.color, label: props.labelCancel || $q.lang.label.cancel, onClick: cancel }), vue.h(QBtn, { flat: true, color: props.color, label: props.labelSet || $q.lang.label.set, onClick: set }) ]) ); return child } // expose public methods Object.assign(proxy, { set, cancel, show (e) { menuRef.value !== null && menuRef.value.show(e); }, hide (e) { menuRef.value !== null && menuRef.value.hide(e); }, updatePosition }); return () => { if (props.disable === true) { return } return vue.h(QMenu, { ref: menuRef, class: 'q-popup-edit', cover: props.cover, onBeforeShow, onShow, onBeforeHide, onHide, onEscapeKey: cancel }, getContent) } } }); var QPopupProxy = createComponent({ name: 'QPopupProxy', props: { ...useAnchorProps, breakpoint: { type: [ String, Number ], default: 450 } }, emits: [ 'show', 'hide' ], setup (props, { slots, emit, attrs }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const showing = vue.ref(false); const popupRef = vue.ref(null); const breakpoint = vue.computed(() => parseInt(props.breakpoint, 10)); const { canShow } = useAnchor({ showing }); function getType () { return $q.screen.width < breakpoint.value || $q.screen.height < breakpoint.value ? 'dialog' : 'menu' } const type = vue.ref(getType()); const popupProps = vue.computed(() => ( type.value === 'menu' ? { maxHeight: '99vh' } : {}) ); vue.watch(() => getType(), val => { if (showing.value !== true) { type.value = val; } }); function onShow (evt) { showing.value = true; emit('show', evt); } function onHide (evt) { showing.value = false; type.value = getType(); emit('hide', evt); } // expose public methods Object.assign(proxy, { show (evt) { canShow(evt) === true && popupRef.value.show(evt); }, hide (evt) { popupRef.value.hide(evt); }, toggle (evt) { popupRef.value.toggle(evt); } }); injectProp(proxy, 'currentComponent', () => ({ type: type.value, ref: popupRef.value })); return () => { const data = { ref: popupRef, ...popupProps.value, ...attrs, onShow, onHide }; let component; if (type.value === 'dialog') { component = QDialog; } else { component = QMenu; Object.assign(data, { target: props.target, contextMenu: props.contextMenu, noParentEvent: true, separateClosePopup: true }); } return vue.h(component, data, slots.default) } } }); const defaultSizes = { xs: 2, sm: 4, md: 6, lg: 10, xl: 14 }; function width (val, reverse, $q) { return { transform: reverse === true ? `translateX(${ $q.lang.rtl === true ? '-' : '' }100%) scale3d(${ -val },1,1)` : `scale3d(${ val },1,1)` } } var QLinearProgress = createComponent({ name: 'QLinearProgress', props: { ...useDarkProps, ...useSizeProps, value: { type: Number, default: 0 }, buffer: Number, color: String, trackColor: String, reverse: Boolean, stripe: Boolean, indeterminate: Boolean, query: Boolean, rounded: Boolean, animationSpeed: { type: [ String, Number ], default: 2100 }, instantFeedback: Boolean }, setup (props, { slots }) { const { proxy } = vue.getCurrentInstance(); const isDark = useDark(props, proxy.$q); const sizeStyle = useSize(props, defaultSizes); const motion = vue.computed(() => props.indeterminate === true || props.query === true); const widthReverse = vue.computed(() => props.reverse !== props.query); const style = vue.computed(() => ({ ...(sizeStyle.value !== null ? sizeStyle.value : {}), '--q-linear-progress-speed': `${ props.animationSpeed }ms` })); const classes = vue.computed(() => 'q-linear-progress' + (props.color !== void 0 ? ` text-${ props.color }` : '') + (props.reverse === true || props.query === true ? ' q-linear-progress--reverse' : '') + (props.rounded === true ? ' rounded-borders' : '') ); const trackStyle = vue.computed(() => width(props.buffer !== void 0 ? props.buffer : 1, widthReverse.value, proxy.$q)); const transitionSuffix = vue.computed(() => `with${ props.instantFeedback === true ? 'out' : '' }-transition`); const trackClass = vue.computed(() => 'q-linear-progress__track absolute-full' + ` q-linear-progress__track--${ transitionSuffix.value }` + ` q-linear-progress__track--${ isDark.value === true ? 'dark' : 'light' }` + (props.trackColor !== void 0 ? ` bg-${ props.trackColor }` : '') ); const modelStyle = vue.computed(() => width(motion.value === true ? 1 : props.value, widthReverse.value, proxy.$q)); const modelClass = vue.computed(() => 'q-linear-progress__model absolute-full' + ` q-linear-progress__model--${ transitionSuffix.value }` + ` q-linear-progress__model--${ motion.value === true ? 'in' : '' }determinate` ); const stripeStyle = vue.computed(() => ({ width: `${ props.value * 100 }%` })); const stripeClass = vue.computed(() => `q-linear-progress__stripe absolute-${ props.reverse === true ? 'right' : 'left' }` + ` q-linear-progress__stripe--${ transitionSuffix.value }` ); return () => { const child = [ vue.h('div', { class: trackClass.value, style: trackStyle.value }), vue.h('div', { class: modelClass.value, style: modelStyle.value }) ]; props.stripe === true && motion.value === false && child.push( vue.h('div', { class: stripeClass.value, style: stripeStyle.value }) ); return vue.h('div', { class: classes.value, style: style.value, role: 'progressbar', 'aria-valuemin': 0, 'aria-valuemax': 1, 'aria-valuenow': props.indeterminate === true ? void 0 : props.value }, hMergeSlot(slots.default, child)) } } }); const PULLER_HEIGHT = 40, OFFSET_TOP = 20; var QPullToRefresh = createComponent({ name: 'QPullToRefresh', props: { color: String, bgColor: String, icon: String, noMouse: Boolean, disable: Boolean, scrollTarget: { default: void 0 } }, emits: [ 'refresh' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const state = vue.ref('pull'); const pullRatio = vue.ref(0); const pulling = vue.ref(false); const pullPosition = vue.ref(-PULLER_HEIGHT); const animating = vue.ref(false); const positionCSS = vue.ref({}); const style = vue.computed(() => ({ opacity: pullRatio.value, transform: `translateY(${ pullPosition.value }px) rotate(${ pullRatio.value * 360 }deg)` })); const classes = vue.computed(() => 'q-pull-to-refresh__puller row flex-center' + (animating.value === true ? ' q-pull-to-refresh__puller--animating' : '') + (props.bgColor !== void 0 ? ` bg-${ props.bgColor }` : '') ); function pull (event) { if (event.isFinal === true) { if (pulling.value === true) { pulling.value = false; if (state.value === 'pulled') { state.value = 'refreshing'; animateTo({ pos: OFFSET_TOP }); trigger(); } else if (state.value === 'pull') { animateTo({ pos: -PULLER_HEIGHT, ratio: 0 }); } } return } if (animating.value === true || state.value === 'refreshing') { return false } if (event.isFirst === true) { if (getVerticalScrollPosition(localScrollTarget) !== 0 || event.direction !== 'down') { if (pulling.value === true) { pulling.value = false; state.value = 'pull'; animateTo({ pos: -PULLER_HEIGHT, ratio: 0 }); } return false } pulling.value = true; const { top, left } = $el.getBoundingClientRect(); positionCSS.value = { top: top + 'px', left: left + 'px', width: window.getComputedStyle($el).getPropertyValue('width') }; } prevent(event.evt); const distance = Math.min(140, Math.max(0, event.distance.y)); pullPosition.value = distance - PULLER_HEIGHT; pullRatio.value = between(distance / (OFFSET_TOP + PULLER_HEIGHT), 0, 1); const newState = pullPosition.value > OFFSET_TOP ? 'pulled' : 'pull'; if (state.value !== newState) { state.value = newState; } } const directives = vue.computed(() => { // if props.disable === false const modifiers = { down: true }; if (props.noMouse !== true) { modifiers.mouse = true; } return [ [ TouchPan, pull, void 0, modifiers ] ] }); const contentClass = vue.computed(() => `q-pull-to-refresh__content${ pulling.value === true ? ' no-pointer-events' : '' }` ); function trigger () { emit('refresh', () => { animateTo({ pos: -PULLER_HEIGHT, ratio: 0 }, () => { state.value = 'pull'; }); }); } let $el, localScrollTarget, timer = null; function animateTo ({ pos, ratio }, done) { animating.value = true; pullPosition.value = pos; if (ratio !== void 0) { pullRatio.value = ratio; } timer !== null && clearTimeout(timer); timer = setTimeout(() => { timer = null; animating.value = false; done && done(); }, 300); } function updateScrollTarget () { localScrollTarget = getScrollTarget($el, props.scrollTarget); } vue.watch(() => props.scrollTarget, updateScrollTarget); vue.onMounted(() => { $el = proxy.$el; updateScrollTarget(); }); vue.onBeforeUnmount(() => { timer !== null && clearTimeout(timer); }); // expose public methods Object.assign(proxy, { trigger, updateScrollTarget }); return () => { const child = [ vue.h('div', { class: contentClass.value }, hSlot(slots.default)), vue.h('div', { class: 'q-pull-to-refresh__puller-container fixed row flex-center no-pointer-events z-top', style: positionCSS.value }, [ vue.h('div', { class: classes.value, style: style.value }, [ state.value !== 'refreshing' ? vue.h(QIcon, { name: props.icon || $q.iconSet.pullToRefresh.icon, color: props.color, size: '32px' }) : vue.h(QSpinner, { size: '24px', color: props.color }) ]) ]) ]; return hDir( 'div', { class: 'q-pull-to-refresh' }, child, 'main', props.disable === false, () => directives.value ) } } }); const dragType = { MIN: 0, RANGE: 1, MAX: 2 }; var QRange = createComponent({ name: 'QRange', props: { ...useSliderProps, modelValue: { type: Object, default: () => ({ min: null, max: null }), validator: v => 'min' in v && 'max' in v }, dragRange: Boolean, dragOnlyRange: Boolean, leftLabelColor: String, leftLabelTextColor: String, rightLabelColor: String, rightLabelTextColor: String, leftLabelValue: [ String, Number ], rightLabelValue: [ String, Number ], leftThumbColor: String, rightThumbColor: String }, emits: useSliderEmits, setup (props, { emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const { state, methods } = useSlider({ updateValue, updatePosition, getDragging, formAttrs: vue.computed(() => ({ type: 'hidden', name: props.name, value: `${ props.modelValue.min }|${ props.modelValue.max }` })) }); const rootRef = vue.ref(null); const curMinRatio = vue.ref(0); const curMaxRatio = vue.ref(0); const model = vue.ref({ min: 0, max: 0 }); function normalizeModel () { model.value.min = props.modelValue.min === null ? state.innerMin.value : between(props.modelValue.min, state.innerMin.value, state.innerMax.value); model.value.max = props.modelValue.max === null ? state.innerMax.value : between(props.modelValue.max, state.innerMin.value, state.innerMax.value); } vue.watch( () => `${ props.modelValue.min }|${ props.modelValue.max }|${ state.innerMin.value }|${ state.innerMax.value }`, normalizeModel ); normalizeModel(); const modelMinRatio = vue.computed(() => methods.convertModelToRatio(model.value.min)); const modelMaxRatio = vue.computed(() => methods.convertModelToRatio(model.value.max)); const ratioMin = vue.computed(() => ( state.active.value === true ? curMinRatio.value : modelMinRatio.value )); const ratioMax = vue.computed(() => ( state.active.value === true ? curMaxRatio.value : modelMaxRatio.value )); const selectionBarStyle = vue.computed(() => { const acc = { [ state.positionProp.value ]: `${ 100 * ratioMin.value }%`, [ state.sizeProp.value ]: `${ 100 * (ratioMax.value - ratioMin.value) }%` }; if (props.selectionImg !== void 0) { acc.backgroundImage = `url(${ props.selectionImg }) !important`; } return acc }); const trackContainerEvents = vue.computed(() => { if (state.editable.value !== true) { return {} } if ($q.platform.is.mobile === true) { return { onClick: methods.onMobileClick } } const evt = { onMousedown: methods.onActivate }; if (props.dragRange === true || props.dragOnlyRange === true) { Object.assign(evt, { onFocus: () => { state.focus.value = 'both'; }, onBlur: methods.onBlur, onKeydown, onKeyup: methods.onKeyup }); } return evt }); function getEvents (side) { return $q.platform.is.mobile !== true && state.editable.value === true && props.dragOnlyRange !== true ? { onFocus: () => { state.focus.value = side; }, onBlur: methods.onBlur, onKeydown, onKeyup: methods.onKeyup } : {} } const thumbTabindex = vue.computed(() => (props.dragOnlyRange !== true ? state.tabindex.value : null)); const trackContainerTabindex = vue.computed(() => ( $q.platform.is.mobile !== true && (props.dragRange || props.dragOnlyRange === true) ? state.tabindex.value : null )); const minThumbRef = vue.ref(null); const minEvents = vue.computed(() => getEvents('min')); const getMinThumb = methods.getThumbRenderFn({ focusValue: 'min', getNodeData: () => ({ ref: minThumbRef, key: 'tmin', ...minEvents.value, tabindex: thumbTabindex.value }), ratio: ratioMin, label: vue.computed(() => ( props.leftLabelValue !== void 0 ? props.leftLabelValue : model.value.min )), thumbColor: vue.computed(() => props.leftThumbColor || props.thumbColor || props.color), labelColor: vue.computed(() => props.leftLabelColor || props.labelColor), labelTextColor: vue.computed(() => props.leftLabelTextColor || props.labelTextColor) }); const maxEvents = vue.computed(() => getEvents('max')); const getMaxThumb = methods.getThumbRenderFn({ focusValue: 'max', getNodeData: () => ({ ...maxEvents.value, key: 'tmax', tabindex: thumbTabindex.value }), ratio: ratioMax, label: vue.computed(() => ( props.rightLabelValue !== void 0 ? props.rightLabelValue : model.value.max )), thumbColor: vue.computed(() => props.rightThumbColor || props.thumbColor || props.color), labelColor: vue.computed(() => props.rightLabelColor || props.labelColor), labelTextColor: vue.computed(() => props.rightLabelTextColor || props.labelTextColor) }); function updateValue (change) { if (model.value.min !== props.modelValue.min || model.value.max !== props.modelValue.max) { emit('update:modelValue', { ...model.value }); } change === true && emit('change', { ...model.value }); } function getDragging (event) { const { left, top, width, height } = rootRef.value.getBoundingClientRect(), sensitivity = props.dragOnlyRange === true ? 0 : (props.vertical === true ? minThumbRef.value.offsetHeight / (2 * height) : minThumbRef.value.offsetWidth / (2 * width) ); const dragging = { left, top, width, height, valueMin: model.value.min, valueMax: model.value.max, ratioMin: modelMinRatio.value, ratioMax: modelMaxRatio.value }; const ratio = methods.getDraggingRatio(event, dragging); if (props.dragOnlyRange !== true && ratio < dragging.ratioMin + sensitivity) { dragging.type = dragType.MIN; } else if (props.dragOnlyRange === true || ratio < dragging.ratioMax - sensitivity) { if (props.dragRange === true || props.dragOnlyRange === true) { dragging.type = dragType.RANGE; Object.assign(dragging, { offsetRatio: ratio, offsetModel: methods.convertRatioToModel(ratio), rangeValue: dragging.valueMax - dragging.valueMin, rangeRatio: dragging.ratioMax - dragging.ratioMin }); } else { dragging.type = dragging.ratioMax - ratio < ratio - dragging.ratioMin ? dragType.MAX : dragType.MIN; } } else { dragging.type = dragType.MAX; } return dragging } function updatePosition (event, dragging = state.dragging.value) { let pos; const ratio = methods.getDraggingRatio(event, dragging); const localModel = methods.convertRatioToModel(ratio); switch (dragging.type) { case dragType.MIN: if (ratio <= dragging.ratioMax) { pos = { minR: ratio, maxR: dragging.ratioMax, min: localModel, max: dragging.valueMax }; state.focus.value = 'min'; } else { pos = { minR: dragging.ratioMax, maxR: ratio, min: dragging.valueMax, max: localModel }; state.focus.value = 'max'; } break case dragType.MAX: if (ratio >= dragging.ratioMin) { pos = { minR: dragging.ratioMin, maxR: ratio, min: dragging.valueMin, max: localModel }; state.focus.value = 'max'; } else { pos = { minR: ratio, maxR: dragging.ratioMin, min: localModel, max: dragging.valueMin }; state.focus.value = 'min'; } break case dragType.RANGE: const ratioDelta = ratio - dragging.offsetRatio, minR = between(dragging.ratioMin + ratioDelta, 0, 1 - dragging.rangeRatio), modelDelta = localModel - dragging.offsetModel, min = between(dragging.valueMin + modelDelta, props.min, props.max - dragging.rangeValue); pos = { minR, maxR: minR + dragging.rangeRatio, min: parseFloat(min.toFixed(state.decimals.value)), max: parseFloat((min + dragging.rangeValue).toFixed(state.decimals.value)) }; state.focus.value = 'both'; break } // If either of the values to be emitted are null, set them to the defaults the user has entered. model.value = model.value.min === null || model.value.max === null ? { min: pos.min || props.min, max: pos.max || props.max } : { min: pos.min, max: pos.max }; if (props.snap !== true || props.step === 0) { curMinRatio.value = pos.minR; curMaxRatio.value = pos.maxR; } else { curMinRatio.value = methods.convertModelToRatio(model.value.min); curMaxRatio.value = methods.convertModelToRatio(model.value.max); } } function onKeydown (evt) { if (!keyCodes$2.includes(evt.keyCode)) { return } stopAndPrevent(evt); const stepVal = ([ 34, 33 ].includes(evt.keyCode) ? 10 : 1) * state.step.value, offset = ( ([ 34, 37, 40 ].includes(evt.keyCode) ? -1 : 1) * (state.isReversed.value === true ? -1 : 1) * (props.vertical === true ? -1 : 1) * stepVal ); if (state.focus.value === 'both') { const interval = model.value.max - model.value.min; const min = between( parseFloat((model.value.min + offset).toFixed(state.decimals.value)), state.innerMin.value, state.innerMax.value - interval ); model.value = { min, max: parseFloat((min + interval).toFixed(state.decimals.value)) }; } else if (state.focus.value === false) { return } else { const which = state.focus.value; model.value = { ...model.value, [ which ]: between( parseFloat((model.value[ which ] + offset).toFixed(state.decimals.value)), which === 'min' ? state.innerMin.value : model.value.min, which === 'max' ? state.innerMax.value : model.value.max ) }; } updateValue(); } return () => { const content = methods.getContent( selectionBarStyle, trackContainerTabindex, trackContainerEvents, node => { node.push( getMinThumb(), getMaxThumb() ); } ); return vue.h('div', { ref: rootRef, class: 'q-range ' + state.classes.value + ( props.modelValue.min === null || props.modelValue.max === null ? ' q-slider--no-value' : '' ), ...state.attributes.value, 'aria-valuenow': props.modelValue.min + '|' + props.modelValue.max }, content) } } }); var QRating = createComponent({ name: 'QRating', props: { ...useSizeProps, ...useFormProps, modelValue: { type: Number, required: true }, max: { type: [ String, Number ], default: 5 }, icon: [ String, Array ], iconHalf: [ String, Array ], iconSelected: [ String, Array ], iconAriaLabel: [ String, Array ], color: [ String, Array ], colorHalf: [ String, Array ], colorSelected: [ String, Array ], noReset: Boolean, noDimming: Boolean, readonly: Boolean, disable: Boolean }, emits: [ 'update:modelValue' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const sizeStyle = useSize(props); const formAttrs = useFormAttrs(props); const injectFormInput = useFormInject(formAttrs); const mouseModel = vue.ref(0); let iconRefs = {}; const editable = vue.computed(() => props.readonly !== true && props.disable !== true ); const classes = vue.computed(() => 'q-rating row inline items-center' + ` q-rating--${ editable.value === true ? '' : 'non-' }editable` + (props.noDimming === true ? ' q-rating--no-dimming' : '') + (props.disable === true ? ' disabled' : '') + ( props.color !== void 0 && Array.isArray(props.color) === false ? ` text-${ props.color }` : '' ) ); const iconData = vue.computed(() => { const iconLen = Array.isArray(props.icon) === true ? props.icon.length : 0, selIconLen = Array.isArray(props.iconSelected) === true ? props.iconSelected.length : 0, halfIconLen = Array.isArray(props.iconHalf) === true ? props.iconHalf.length : 0, colorLen = Array.isArray(props.color) === true ? props.color.length : 0, selColorLen = Array.isArray(props.colorSelected) === true ? props.colorSelected.length : 0, halfColorLen = Array.isArray(props.colorHalf) === true ? props.colorHalf.length : 0; return { iconLen, icon: iconLen > 0 ? props.icon[ iconLen - 1 ] : props.icon, selIconLen, selIcon: selIconLen > 0 ? props.iconSelected[ selIconLen - 1 ] : props.iconSelected, halfIconLen, halfIcon: halfIconLen > 0 ? props.iconHalf[ selIconLen - 1 ] : props.iconHalf, colorLen, color: colorLen > 0 ? props.color[ colorLen - 1 ] : props.color, selColorLen, selColor: selColorLen > 0 ? props.colorSelected[ selColorLen - 1 ] : props.colorSelected, halfColorLen, halfColor: halfColorLen > 0 ? props.colorHalf[ halfColorLen - 1 ] : props.colorHalf } }); const iconLabel = vue.computed(() => { if (typeof props.iconAriaLabel === 'string') { const label = props.iconAriaLabel.length !== 0 ? `${ props.iconAriaLabel } ` : ''; return i => `${ label }${ i }` } if (Array.isArray(props.iconAriaLabel) === true) { const iMax = props.iconAriaLabel.length; if (iMax > 0) { return i => props.iconAriaLabel[ Math.min(i, iMax) - 1 ] } } return (i, label) => `${ label } ${ i }` }); const stars = vue.computed(() => { const acc = [], icons = iconData.value, ceil = Math.ceil(props.modelValue), tabindex = editable.value === true ? 0 : null; const halfIndex = props.iconHalf === void 0 || ceil === props.modelValue ? -1 : ceil; for (let i = 1; i <= props.max; i++) { const active = (mouseModel.value === 0 && props.modelValue >= i) || (mouseModel.value > 0 && mouseModel.value >= i), half = halfIndex === i && mouseModel.value < i, exSelected = mouseModel.value > 0 && (half === true ? ceil : props.modelValue) >= i && mouseModel.value < i, color = half === true ? (i <= icons.halfColorLen ? props.colorHalf[ i - 1 ] : icons.halfColor) : ( icons.selColor !== void 0 && active === true ? (i <= icons.selColorLen ? props.colorSelected[ i - 1 ] : icons.selColor) : (i <= icons.colorLen ? props.color[ i - 1 ] : icons.color) ), name = ( half === true ? (i <= icons.halfIconLen ? props.iconHalf[ i - 1 ] : icons.halfIcon) : ( icons.selIcon !== void 0 && (active === true || exSelected === true) ? (i <= icons.selIconLen ? props.iconSelected[ i - 1 ] : icons.selIcon) : (i <= icons.iconLen ? props.icon[ i - 1 ] : icons.icon) ) ) || $q.iconSet.rating.icon; acc.push({ name: ( half === true ? (i <= icons.halfIconLen ? props.iconHalf[ i - 1 ] : icons.halfIcon) : ( icons.selIcon !== void 0 && (active === true || exSelected === true) ? (i <= icons.selIconLen ? props.iconSelected[ i - 1 ] : icons.selIcon) : (i <= icons.iconLen ? props.icon[ i - 1 ] : icons.icon) ) ) || $q.iconSet.rating.icon, attrs: { tabindex, role: 'radio', 'aria-checked': props.modelValue === i ? 'true' : 'false', 'aria-label': iconLabel.value(i, name) }, iconClass: 'q-rating__icon' + (active === true || half === true ? ' q-rating__icon--active' : '') + (exSelected === true ? ' q-rating__icon--exselected' : '') + (mouseModel.value === i ? ' q-rating__icon--hovered' : '') + (color !== void 0 ? ` text-${ color }` : '') }); } return acc }); const attributes = vue.computed(() => { const attrs = { role: 'radiogroup' }; if (props.disable === true) { attrs[ 'aria-disabled' ] = 'true'; } if (props.readonly === true) { attrs[ 'aria-readonly' ] = 'true'; } return attrs }); function set (value) { if (editable.value === true) { const model = between(parseInt(value, 10), 1, parseInt(props.max, 10)), newVal = props.noReset !== true && props.modelValue === model ? 0 : model; newVal !== props.modelValue && emit('update:modelValue', newVal); mouseModel.value = 0; } } function setHoverValue (value) { if (editable.value === true) { mouseModel.value = value; } } function onKeyup (e, i) { switch (e.keyCode) { case 13: case 32: set(i); return stopAndPrevent(e) case 37: // LEFT ARROW case 40: // DOWN ARROW if (iconRefs[ `rt${ i - 1 }` ]) { iconRefs[ `rt${ i - 1 }` ].focus(); } return stopAndPrevent(e) case 39: // RIGHT ARROW case 38: // UP ARROW if (iconRefs[ `rt${ i + 1 }` ]) { iconRefs[ `rt${ i + 1 }` ].focus(); } return stopAndPrevent(e) } } function resetMouseModel () { mouseModel.value = 0; } vue.onBeforeUpdate(() => { iconRefs = {}; }); return () => { const child = []; stars.value.forEach(({ iconClass, name, attrs }, index) => { const i = index + 1; child.push( vue.h('div', { key: i, ref: el => { iconRefs[ `rt${ i }` ] = el; }, class: 'q-rating__icon-container flex flex-center', ...attrs, onClick () { set(i); }, onMouseover () { setHoverValue(i); }, onMouseout: resetMouseModel, onFocus () { setHoverValue(i); }, onBlur: resetMouseModel, onKeyup (e) { onKeyup(e, i); } }, hMergeSlot( slots[ `tip-${ i }` ], [ vue.h(QIcon, { class: iconClass, name }) ] )) ); }); if (props.name !== void 0 && props.disable !== true) { injectFormInput(child, 'push'); } return vue.h('div', { class: classes.value, style: sizeStyle.value, ...attributes.value }, child) } } }); var QResponsive = createComponent({ name: 'QResponsive', props: useRatioProps, setup (props, { slots }) { const ratioStyle = useRatio(props); return () => vue.h('div', { class: 'q-responsive' }, [ vue.h('div', { class: 'q-responsive__filler overflow-hidden' }, [ vue.h('div', { style: ratioStyle.value }) ]), vue.h('div', { class: 'q-responsive__content absolute-full fit' }, hSlot(slots.default)) ]) } }); const axisList = [ 'vertical', 'horizontal' ]; const dirProps = { vertical: { offset: 'offsetY', scroll: 'scrollTop', dir: 'down', dist: 'y' }, horizontal: { offset: 'offsetX', scroll: 'scrollLeft', dir: 'right', dist: 'x' } }; const panOpts = { prevent: true, mouse: true, mouseAllDir: true }; const getMinThumbSize = size => (size >= 250 ? 50 : Math.ceil(size / 5)); var QScrollArea = createComponent({ name: 'QScrollArea', props: { ...useDarkProps, thumbStyle: Object, verticalThumbStyle: Object, horizontalThumbStyle: Object, barStyle: [ Array, String, Object ], verticalBarStyle: [ Array, String, Object ], horizontalBarStyle: [ Array, String, Object ], contentStyle: [ Array, String, Object ], contentActiveStyle: [ Array, String, Object ], delay: { type: [ String, Number ], default: 1000 }, visible: { type: Boolean, default: null }, tabindex: [ String, Number ], onScroll: Function }, setup (props, { slots, emit }) { // state management const tempShowing = vue.ref(false); const panning = vue.ref(false); const hover = vue.ref(false); // other... const container = { vertical: vue.ref(0), horizontal: vue.ref(0) }; const scroll = { vertical: { ref: vue.ref(null), position: vue.ref(0), size: vue.ref(0) }, horizontal: { ref: vue.ref(null), position: vue.ref(0), size: vue.ref(0) } }; const { proxy } = vue.getCurrentInstance(); const isDark = useDark(props, proxy.$q); let timer = null, panRefPos; const targetRef = vue.ref(null); const classes = vue.computed(() => 'q-scrollarea' + (isDark.value === true ? ' q-scrollarea--dark' : '') ); scroll.vertical.percentage = vue.computed(() => { const diff = scroll.vertical.size.value - container.vertical.value; if (diff <= 0) { return 0 } const p = between(scroll.vertical.position.value / diff, 0, 1); return Math.round(p * 10000) / 10000 }); scroll.vertical.thumbHidden = vue.computed(() => ( (props.visible === null ? hover.value : props.visible) !== true && tempShowing.value === false && panning.value === false ) || scroll.vertical.size.value <= container.vertical.value + 1 ); scroll.vertical.thumbStart = vue.computed(() => scroll.vertical.percentage.value * (container.vertical.value - scroll.vertical.thumbSize.value) ); scroll.vertical.thumbSize = vue.computed(() => Math.round( between( container.vertical.value * container.vertical.value / scroll.vertical.size.value, getMinThumbSize(container.vertical.value), container.vertical.value ) ) ); scroll.vertical.style = vue.computed(() => { return { ...props.thumbStyle, ...props.verticalThumbStyle, top: `${ scroll.vertical.thumbStart.value }px`, height: `${ scroll.vertical.thumbSize.value }px` } }); scroll.vertical.thumbClass = vue.computed(() => 'q-scrollarea__thumb q-scrollarea__thumb--v absolute-right' + (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '') ); scroll.vertical.barClass = vue.computed(() => 'q-scrollarea__bar q-scrollarea__bar--v absolute-right' + (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '') ); scroll.horizontal.percentage = vue.computed(() => { const diff = scroll.horizontal.size.value - container.horizontal.value; if (diff <= 0) { return 0 } const p = between(Math.abs(scroll.horizontal.position.value) / diff, 0, 1); return Math.round(p * 10000) / 10000 }); scroll.horizontal.thumbHidden = vue.computed(() => ( (props.visible === null ? hover.value : props.visible) !== true && tempShowing.value === false && panning.value === false ) || scroll.horizontal.size.value <= container.horizontal.value + 1 ); scroll.horizontal.thumbStart = vue.computed(() => scroll.horizontal.percentage.value * (container.horizontal.value - scroll.horizontal.thumbSize.value) ); scroll.horizontal.thumbSize = vue.computed(() => Math.round( between( container.horizontal.value * container.horizontal.value / scroll.horizontal.size.value, getMinThumbSize(container.horizontal.value), container.horizontal.value ) ) ); scroll.horizontal.style = vue.computed(() => { return { ...props.thumbStyle, ...props.horizontalThumbStyle, [ proxy.$q.lang.rtl === true ? 'right' : 'left' ]: `${ scroll.horizontal.thumbStart.value }px`, width: `${ scroll.horizontal.thumbSize.value }px` } }); scroll.horizontal.thumbClass = vue.computed(() => 'q-scrollarea__thumb q-scrollarea__thumb--h absolute-bottom' + (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '') ); scroll.horizontal.barClass = vue.computed(() => 'q-scrollarea__bar q-scrollarea__bar--h absolute-bottom' + (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '') ); const mainStyle = vue.computed(() => ( scroll.vertical.thumbHidden.value === true && scroll.horizontal.thumbHidden.value === true ? props.contentStyle : props.contentActiveStyle )); const thumbVertDir = [ [ TouchPan, e => { onPanThumb(e, 'vertical'); }, void 0, { vertical: true, ...panOpts } ] ]; const thumbHorizDir = [ [ TouchPan, e => { onPanThumb(e, 'horizontal'); }, void 0, { horizontal: true, ...panOpts } ] ]; function getScroll () { const info = {}; axisList.forEach(axis => { const data = scroll[ axis ]; info[ axis + 'Position' ] = data.position.value; info[ axis + 'Percentage' ] = data.percentage.value; info[ axis + 'Size' ] = data.size.value; info[ axis + 'ContainerSize' ] = container[ axis ].value; }); return info } // we have lots of listeners, so // ensure we're not emitting same info // multiple times const emitScroll = debounce(() => { const info = getScroll(); info.ref = proxy; emit('scroll', info); }, 0); function localSetScrollPosition (axis, offset, duration) { if (axisList.includes(axis) === false) { console.error('[QScrollArea]: wrong first param of setScrollPosition (vertical/horizontal)'); return } const fn = axis === 'vertical' ? setVerticalScrollPosition : setHorizontalScrollPosition; fn(targetRef.value, offset, duration); } function updateContainer ({ height, width }) { let change = false; if (container.vertical.value !== height) { container.vertical.value = height; change = true; } if (container.horizontal.value !== width) { container.horizontal.value = width; change = true; } change === true && startTimer(); } function updateScroll ({ position }) { let change = false; if (scroll.vertical.position.value !== position.top) { scroll.vertical.position.value = position.top; change = true; } if (scroll.horizontal.position.value !== position.left) { scroll.horizontal.position.value = position.left; change = true; } change === true && startTimer(); } function updateScrollSize ({ height, width }) { if (scroll.horizontal.size.value !== width) { scroll.horizontal.size.value = width; startTimer(); } if (scroll.vertical.size.value !== height) { scroll.vertical.size.value = height; startTimer(); } } function onPanThumb (e, axis) { const data = scroll[ axis ]; if (e.isFirst === true) { if (data.thumbHidden.value === true) { return } panRefPos = data.position.value; panning.value = true; } else if (panning.value !== true) { return } if (e.isFinal === true) { panning.value = false; } const dProp = dirProps[ axis ]; const containerSize = container[ axis ].value; const multiplier = (data.size.value - containerSize) / (containerSize - data.thumbSize.value); const distance = e.distance[ dProp.dist ]; const pos = panRefPos + (e.direction === dProp.dir ? 1 : -1) * distance * multiplier; setScroll(pos, axis); } function onMousedown (evt, axis) { const data = scroll[ axis ]; if (data.thumbHidden.value !== true) { const offset = evt[ dirProps[ axis ].offset ]; if (offset < data.thumbStart.value || offset > data.thumbStart.value + data.thumbSize.value) { const pos = offset - data.thumbSize.value / 2; setScroll(pos / container[ axis ].value * data.size.value, axis); } // activate thumb pan if (data.ref.value !== null) { data.ref.value.dispatchEvent(new MouseEvent(evt.type, evt)); } } } function onVerticalMousedown (evt) { onMousedown(evt, 'vertical'); } function onHorizontalMousedown (evt) { onMousedown(evt, 'horizontal'); } function startTimer () { tempShowing.value = true; timer !== null && clearTimeout(timer); timer = setTimeout(() => { timer = null; tempShowing.value = false; }, props.delay); props.onScroll !== void 0 && emitScroll(); } function setScroll (offset, axis) { targetRef.value[ dirProps[ axis ].scroll ] = offset; } function onMouseenter () { hover.value = true; } function onMouseleave () { hover.value = false; } let scrollPosition = null; vue.watch(() => proxy.$q.lang.rtl, rtl => { if (targetRef.value !== null) { setHorizontalScrollPosition( targetRef.value, Math.abs(scroll.horizontal.position.value) * (rtl === true ? -1 : 1) ); } }); vue.onDeactivated(() => { scrollPosition = { top: scroll.vertical.position.value, left: scroll.horizontal.position.value }; }); vue.onActivated(() => { if (scrollPosition === null) { return } const scrollTarget = targetRef.value; if (scrollTarget !== null) { setHorizontalScrollPosition(scrollTarget, scrollPosition.left); setVerticalScrollPosition(scrollTarget, scrollPosition.top); } }); vue.onBeforeUnmount(emitScroll.cancel); // expose public methods Object.assign(proxy, { getScrollTarget: () => targetRef.value, getScroll, getScrollPosition: () => ({ top: scroll.vertical.position.value, left: scroll.horizontal.position.value }), getScrollPercentage: () => ({ top: scroll.vertical.percentage.value, left: scroll.horizontal.percentage.value }), setScrollPosition: localSetScrollPosition, setScrollPercentage (axis, percentage, duration) { localSetScrollPosition( axis, percentage * (scroll[ axis ].size.value - container[ axis ].value) * (axis === 'horizontal' && proxy.$q.lang.rtl === true ? -1 : 1), duration ); } }); return () => { return vue.h('div', { class: classes.value, onMouseenter, onMouseleave }, [ vue.h('div', { ref: targetRef, class: 'q-scrollarea__container scroll relative-position fit hide-scrollbar', tabindex: props.tabindex !== void 0 ? props.tabindex : void 0 }, [ vue.h('div', { class: 'q-scrollarea__content absolute', style: mainStyle.value }, hMergeSlot(slots.default, [ vue.h(QResizeObserver, { debounce: 0, onResize: updateScrollSize }) ])), vue.h(QScrollObserver, { axis: 'both', onScroll: updateScroll }) ]), vue.h(QResizeObserver, { debounce: 0, onResize: updateContainer }), vue.h('div', { class: scroll.vertical.barClass.value, style: [ props.barStyle, props.verticalBarStyle ], 'aria-hidden': 'true', onMousedown: onVerticalMousedown }), vue.h('div', { class: scroll.horizontal.barClass.value, style: [ props.barStyle, props.horizontalBarStyle ], 'aria-hidden': 'true', onMousedown: onHorizontalMousedown }), vue.withDirectives( vue.h('div', { ref: scroll.vertical.ref, class: scroll.vertical.thumbClass.value, style: scroll.vertical.style.value, 'aria-hidden': 'true' }), thumbVertDir ), vue.withDirectives( vue.h('div', { ref: scroll.horizontal.ref, class: scroll.horizontal.thumbClass.value, style: scroll.horizontal.style.value, 'aria-hidden': 'true' }), thumbHorizDir ) ]) } } }); const aggBucketSize = 1000; const scrollToEdges = [ 'start', 'center', 'end', 'start-force', 'center-force', 'end-force' ]; const filterProto = Array.prototype.filter; const setOverflowAnchor = window.getComputedStyle(document.body).overflowAnchor === void 0 ? noop : function (contentEl, index) { if (contentEl === null) { return } if (contentEl._qOverflowAnimationFrame !== void 0) { cancelAnimationFrame(contentEl._qOverflowAnimationFrame); } contentEl._qOverflowAnimationFrame = requestAnimationFrame(() => { if (contentEl === null) { return } contentEl._qOverflowAnimationFrame = void 0; const children = contentEl.children || []; filterProto .call(children, el => el.dataset && el.dataset.qVsAnchor !== void 0) .forEach(el => { delete el.dataset.qVsAnchor; }); const el = children[ index ]; if (el && el.dataset) { el.dataset.qVsAnchor = ''; } }); }; function sumFn (acc, h) { return acc + h } function getScrollDetails ( parent, child, beforeRef, afterRef, horizontal, rtl, stickyStart, stickyEnd ) { const parentCalc = parent === window ? document.scrollingElement || document.documentElement : parent, propElSize = horizontal === true ? 'offsetWidth' : 'offsetHeight', details = { scrollStart: 0, scrollViewSize: -stickyStart - stickyEnd, scrollMaxSize: 0, offsetStart: -stickyStart, offsetEnd: -stickyEnd }; if (horizontal === true) { if (parent === window) { details.scrollStart = window.pageXOffset || window.scrollX || document.body.scrollLeft || 0; details.scrollViewSize += document.documentElement.clientWidth; } else { details.scrollStart = parentCalc.scrollLeft; details.scrollViewSize += parentCalc.clientWidth; } details.scrollMaxSize = parentCalc.scrollWidth; if (rtl === true) { details.scrollStart = (rtlHasScrollBug === true ? details.scrollMaxSize - details.scrollViewSize : 0) - details.scrollStart; } } else { if (parent === window) { details.scrollStart = window.pageYOffset || window.scrollY || document.body.scrollTop || 0; details.scrollViewSize += document.documentElement.clientHeight; } else { details.scrollStart = parentCalc.scrollTop; details.scrollViewSize += parentCalc.clientHeight; } details.scrollMaxSize = parentCalc.scrollHeight; } if (beforeRef !== null) { for (let el = beforeRef.previousElementSibling; el !== null; el = el.previousElementSibling) { if (el.classList.contains('q-virtual-scroll--skip') === false) { details.offsetStart += el[ propElSize ]; } } } if (afterRef !== null) { for (let el = afterRef.nextElementSibling; el !== null; el = el.nextElementSibling) { if (el.classList.contains('q-virtual-scroll--skip') === false) { details.offsetEnd += el[ propElSize ]; } } } if (child !== parent) { const parentRect = parentCalc.getBoundingClientRect(), childRect = child.getBoundingClientRect(); if (horizontal === true) { details.offsetStart += childRect.left - parentRect.left; details.offsetEnd -= childRect.width; } else { details.offsetStart += childRect.top - parentRect.top; details.offsetEnd -= childRect.height; } if (parent !== window) { details.offsetStart += details.scrollStart; } details.offsetEnd += details.scrollMaxSize - details.offsetStart; } return details } function setScroll (parent, scroll, horizontal, rtl) { if (scroll === 'end') { scroll = (parent === window ? document.body : parent)[ horizontal === true ? 'scrollWidth' : 'scrollHeight' ]; } if (parent === window) { if (horizontal === true) { if (rtl === true) { scroll = (rtlHasScrollBug === true ? document.body.scrollWidth - document.documentElement.clientWidth : 0) - scroll; } window.scrollTo(scroll, window.pageYOffset || window.scrollY || document.body.scrollTop || 0); } else { window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, scroll); } } else if (horizontal === true) { if (rtl === true) { scroll = (rtlHasScrollBug === true ? parent.scrollWidth - parent.offsetWidth : 0) - scroll; } parent.scrollLeft = scroll; } else { parent.scrollTop = scroll; } } function sumSize (sizeAgg, size, from, to) { if (from >= to) { return 0 } const lastTo = size.length, fromAgg = Math.floor(from / aggBucketSize), toAgg = Math.floor((to - 1) / aggBucketSize) + 1; let total = sizeAgg.slice(fromAgg, toAgg).reduce(sumFn, 0); if (from % aggBucketSize !== 0) { total -= size.slice(fromAgg * aggBucketSize, from).reduce(sumFn, 0); } if (to % aggBucketSize !== 0 && to !== lastTo) { total -= size.slice(to, toAgg * aggBucketSize).reduce(sumFn, 0); } return total } const commonVirtScrollProps = { virtualScrollSliceSize: { type: [ Number, String ], default: null }, virtualScrollSliceRatioBefore: { type: [ Number, String ], default: 1 }, virtualScrollSliceRatioAfter: { type: [ Number, String ], default: 1 }, virtualScrollItemSize: { type: [ Number, String ], default: 24 }, virtualScrollStickySizeStart: { type: [ Number, String ], default: 0 }, virtualScrollStickySizeEnd: { type: [ Number, String ], default: 0 }, tableColspan: [ Number, String ] }; const commonVirtPropsList = Object.keys(commonVirtScrollProps); const useVirtualScrollProps = { virtualScrollHorizontal: Boolean, onVirtualScroll: Function, ...commonVirtScrollProps }; function useVirtualScroll ({ virtualScrollLength, getVirtualScrollTarget, getVirtualScrollEl, virtualScrollItemSizeComputed // optional }) { const vm = vue.getCurrentInstance(); const { props, emit, proxy } = vm; const { $q } = proxy; let prevScrollStart, prevToIndex, localScrollViewSize, virtualScrollSizesAgg = [], virtualScrollSizes; const virtualScrollPaddingBefore = vue.ref(0); const virtualScrollPaddingAfter = vue.ref(0); const virtualScrollSliceSizeComputed = vue.ref({}); const beforeRef = vue.ref(null); const afterRef = vue.ref(null); const contentRef = vue.ref(null); const virtualScrollSliceRange = vue.ref({ from: 0, to: 0 }); const colspanAttr = vue.computed(() => (props.tableColspan !== void 0 ? props.tableColspan : 100)); if (virtualScrollItemSizeComputed === void 0) { virtualScrollItemSizeComputed = vue.computed(() => props.virtualScrollItemSize); } const needsReset = vue.computed(() => virtualScrollItemSizeComputed.value + ';' + props.virtualScrollHorizontal); const needsSliceRecalc = vue.computed(() => needsReset.value + ';' + props.virtualScrollSliceRatioBefore + ';' + props.virtualScrollSliceRatioAfter ); vue.watch(needsSliceRecalc, () => { setVirtualScrollSize(); }); vue.watch(needsReset, reset); function reset () { localResetVirtualScroll(prevToIndex, true); } function refresh (toIndex) { localResetVirtualScroll(toIndex === void 0 ? prevToIndex : toIndex); } function scrollTo (toIndex, edge) { const scrollEl = getVirtualScrollTarget(); if (scrollEl === void 0 || scrollEl === null || scrollEl.nodeType === 8) { return } const scrollDetails = getScrollDetails( scrollEl, getVirtualScrollEl(), beforeRef.value, afterRef.value, props.virtualScrollHorizontal, $q.lang.rtl, props.virtualScrollStickySizeStart, props.virtualScrollStickySizeEnd ); localScrollViewSize !== scrollDetails.scrollViewSize && setVirtualScrollSize(scrollDetails.scrollViewSize); setVirtualScrollSliceRange( scrollEl, scrollDetails, Math.min(virtualScrollLength.value - 1, Math.max(0, parseInt(toIndex, 10) || 0)), 0, scrollToEdges.indexOf(edge) > -1 ? edge : (prevToIndex > -1 && toIndex > prevToIndex ? 'end' : 'start') ); } function localOnVirtualScrollEvt () { const scrollEl = getVirtualScrollTarget(); if (scrollEl === void 0 || scrollEl === null || scrollEl.nodeType === 8) { return } const scrollDetails = getScrollDetails( scrollEl, getVirtualScrollEl(), beforeRef.value, afterRef.value, props.virtualScrollHorizontal, $q.lang.rtl, props.virtualScrollStickySizeStart, props.virtualScrollStickySizeEnd ), listLastIndex = virtualScrollLength.value - 1, listEndOffset = scrollDetails.scrollMaxSize - scrollDetails.offsetStart - scrollDetails.offsetEnd - virtualScrollPaddingAfter.value; if (prevScrollStart === scrollDetails.scrollStart) { return } if (scrollDetails.scrollMaxSize <= 0) { setVirtualScrollSliceRange(scrollEl, scrollDetails, 0, 0); return } localScrollViewSize !== scrollDetails.scrollViewSize && setVirtualScrollSize(scrollDetails.scrollViewSize); updateVirtualScrollSizes(virtualScrollSliceRange.value.from); const scrollMaxStart = Math.floor(scrollDetails.scrollMaxSize - Math.max(scrollDetails.scrollViewSize, scrollDetails.offsetEnd) - Math.min(virtualScrollSizes[ listLastIndex ], scrollDetails.scrollViewSize / 2)); if (scrollMaxStart > 0 && Math.ceil(scrollDetails.scrollStart) >= scrollMaxStart) { setVirtualScrollSliceRange( scrollEl, scrollDetails, listLastIndex, scrollDetails.scrollMaxSize - scrollDetails.offsetEnd - virtualScrollSizesAgg.reduce(sumFn, 0) ); return } let toIndex = 0, listOffset = scrollDetails.scrollStart - scrollDetails.offsetStart, offset = listOffset; if (listOffset <= listEndOffset && listOffset + scrollDetails.scrollViewSize >= virtualScrollPaddingBefore.value) { listOffset -= virtualScrollPaddingBefore.value; toIndex = virtualScrollSliceRange.value.from; offset = listOffset; } else { for (let j = 0; listOffset >= virtualScrollSizesAgg[ j ] && toIndex < listLastIndex; j++) { listOffset -= virtualScrollSizesAgg[ j ]; toIndex += aggBucketSize; } } while (listOffset > 0 && toIndex < listLastIndex) { listOffset -= virtualScrollSizes[ toIndex ]; if (listOffset > -scrollDetails.scrollViewSize) { toIndex++; offset = listOffset; } else { offset = virtualScrollSizes[ toIndex ] + listOffset; } } setVirtualScrollSliceRange( scrollEl, scrollDetails, toIndex, offset ); } function setVirtualScrollSliceRange (scrollEl, scrollDetails, toIndex, offset, align) { const alignForce = typeof align === 'string' && align.indexOf('-force') > -1; const alignEnd = alignForce === true ? align.replace('-force', '') : align; const alignRange = alignEnd !== void 0 ? alignEnd : 'start'; let from = Math.max(0, toIndex - virtualScrollSliceSizeComputed.value[ alignRange ]), to = from + virtualScrollSliceSizeComputed.value.total; if (to > virtualScrollLength.value) { to = virtualScrollLength.value; from = Math.max(0, to - virtualScrollSliceSizeComputed.value.total); } prevScrollStart = scrollDetails.scrollStart; const rangeChanged = from !== virtualScrollSliceRange.value.from || to !== virtualScrollSliceRange.value.to; if (rangeChanged === false && alignEnd === void 0) { emitScroll(toIndex); return } const { activeElement } = document; const contentEl = contentRef.value; if ( rangeChanged === true && contentEl !== null && contentEl !== activeElement && contentEl.contains(activeElement) === true ) { contentEl.addEventListener('focusout', onBlurRefocusFn); setTimeout(() => { contentEl !== null && contentEl.removeEventListener('focusout', onBlurRefocusFn); }); } setOverflowAnchor(contentEl, toIndex - from); const sizeBefore = alignEnd !== void 0 ? virtualScrollSizes.slice(from, toIndex).reduce(sumFn, 0) : 0; if (rangeChanged === true) { // vue key matching algorithm works only if // the array of VNodes changes on only one of the ends // so we first change one end and then the other const tempTo = to >= virtualScrollSliceRange.value.from && from <= virtualScrollSliceRange.value.to ? virtualScrollSliceRange.value.to : to; virtualScrollSliceRange.value = { from, to: tempTo }; virtualScrollPaddingBefore.value = sumSize(virtualScrollSizesAgg, virtualScrollSizes, 0, from); virtualScrollPaddingAfter.value = sumSize(virtualScrollSizesAgg, virtualScrollSizes, to, virtualScrollLength.value); requestAnimationFrame(() => { if (virtualScrollSliceRange.value.to !== to && prevScrollStart === scrollDetails.scrollStart) { virtualScrollSliceRange.value = { from: virtualScrollSliceRange.value.from, to }; virtualScrollPaddingAfter.value = sumSize(virtualScrollSizesAgg, virtualScrollSizes, to, virtualScrollLength.value); } }); } requestAnimationFrame(() => { // if the scroll was changed give up // (another call to setVirtualScrollSliceRange before animation frame) if (prevScrollStart !== scrollDetails.scrollStart) { return } if (rangeChanged === true) { updateVirtualScrollSizes(from); } const sizeAfter = virtualScrollSizes.slice(from, toIndex).reduce(sumFn, 0), posStart = sizeAfter + scrollDetails.offsetStart + virtualScrollPaddingBefore.value, posEnd = posStart + virtualScrollSizes[ toIndex ]; let scrollPosition = posStart + offset; if (alignEnd !== void 0) { const sizeDiff = sizeAfter - sizeBefore; const scrollStart = scrollDetails.scrollStart + sizeDiff; scrollPosition = alignForce !== true && scrollStart < posStart && posEnd < scrollStart + scrollDetails.scrollViewSize ? scrollStart : ( alignEnd === 'end' ? posEnd - scrollDetails.scrollViewSize : posStart - (alignEnd === 'start' ? 0 : Math.round((scrollDetails.scrollViewSize - virtualScrollSizes[ toIndex ]) / 2)) ); } prevScrollStart = scrollPosition; setScroll( scrollEl, scrollPosition, props.virtualScrollHorizontal, $q.lang.rtl ); emitScroll(toIndex); }); } function updateVirtualScrollSizes (from) { const contentEl = contentRef.value; if (contentEl) { const children = filterProto.call( contentEl.children, el => el.classList && el.classList.contains('q-virtual-scroll--skip') === false ), childrenLength = children.length, sizeFn = props.virtualScrollHorizontal === true ? el => el.getBoundingClientRect().width : el => el.offsetHeight; let index = from, size, diff; for (let i = 0; i < childrenLength;) { size = sizeFn(children[ i ]); i++; while (i < childrenLength && children[ i ].classList.contains('q-virtual-scroll--with-prev') === true) { size += sizeFn(children[ i ]); i++; } diff = size - virtualScrollSizes[ index ]; if (diff !== 0) { virtualScrollSizes[ index ] += diff; virtualScrollSizesAgg[ Math.floor(index / aggBucketSize) ] += diff; } index++; } } } function onBlurRefocusFn () { contentRef.value !== null && contentRef.value !== void 0 && contentRef.value.focus(); } function localResetVirtualScroll (toIndex, fullReset) { const defaultSize = 1 * virtualScrollItemSizeComputed.value; if (fullReset === true || Array.isArray(virtualScrollSizes) === false) { virtualScrollSizes = []; } const oldVirtualScrollSizesLength = virtualScrollSizes.length; virtualScrollSizes.length = virtualScrollLength.value; for (let i = virtualScrollLength.value - 1; i >= oldVirtualScrollSizesLength; i--) { virtualScrollSizes[ i ] = defaultSize; } const jMax = Math.floor((virtualScrollLength.value - 1) / aggBucketSize); virtualScrollSizesAgg = []; for (let j = 0; j <= jMax; j++) { let size = 0; const iMax = Math.min((j + 1) * aggBucketSize, virtualScrollLength.value); for (let i = j * aggBucketSize; i < iMax; i++) { size += virtualScrollSizes[ i ]; } virtualScrollSizesAgg.push(size); } prevToIndex = -1; prevScrollStart = void 0; virtualScrollPaddingBefore.value = sumSize(virtualScrollSizesAgg, virtualScrollSizes, 0, virtualScrollSliceRange.value.from); virtualScrollPaddingAfter.value = sumSize(virtualScrollSizesAgg, virtualScrollSizes, virtualScrollSliceRange.value.to, virtualScrollLength.value); if (toIndex >= 0) { updateVirtualScrollSizes(virtualScrollSliceRange.value.from); vue.nextTick(() => { scrollTo(toIndex); }); } else { onVirtualScrollEvt(); } } function setVirtualScrollSize (scrollViewSize) { if (scrollViewSize === void 0 && typeof window !== 'undefined') { const scrollEl = getVirtualScrollTarget(); if (scrollEl !== void 0 && scrollEl !== null && scrollEl.nodeType !== 8) { scrollViewSize = getScrollDetails( scrollEl, getVirtualScrollEl(), beforeRef.value, afterRef.value, props.virtualScrollHorizontal, $q.lang.rtl, props.virtualScrollStickySizeStart, props.virtualScrollStickySizeEnd ).scrollViewSize; } } localScrollViewSize = scrollViewSize; const virtualScrollSliceRatioBefore = parseFloat(props.virtualScrollSliceRatioBefore) || 0; const virtualScrollSliceRatioAfter = parseFloat(props.virtualScrollSliceRatioAfter) || 0; const multiplier = 1 + virtualScrollSliceRatioBefore + virtualScrollSliceRatioAfter; const view = scrollViewSize === void 0 || scrollViewSize <= 0 ? 1 : Math.ceil(scrollViewSize / virtualScrollItemSizeComputed.value); const baseSize = Math.max( 1, view, Math.ceil((props.virtualScrollSliceSize > 0 ? props.virtualScrollSliceSize : 10) / multiplier) ); virtualScrollSliceSizeComputed.value = { total: Math.ceil(baseSize * multiplier), start: Math.ceil(baseSize * virtualScrollSliceRatioBefore), center: Math.ceil(baseSize * (0.5 + virtualScrollSliceRatioBefore)), end: Math.ceil(baseSize * (1 + virtualScrollSliceRatioBefore)), view }; } function padVirtualScroll (tag, content) { const paddingSize = props.virtualScrollHorizontal === true ? 'width' : 'height'; const style = { [ '--q-virtual-scroll-item-' + paddingSize ]: virtualScrollItemSizeComputed.value + 'px' }; return [ tag === 'tbody' ? vue.h(tag, { class: 'q-virtual-scroll__padding', key: 'before', ref: beforeRef }, [ vue.h('tr', [ vue.h('td', { style: { [ paddingSize ]: `${ virtualScrollPaddingBefore.value }px`, ...style }, colspan: colspanAttr.value }) ]) ]) : vue.h(tag, { class: 'q-virtual-scroll__padding', key: 'before', ref: beforeRef, style: { [ paddingSize ]: `${ virtualScrollPaddingBefore.value }px`, ...style } }), vue.h(tag, { class: 'q-virtual-scroll__content', key: 'content', ref: contentRef, tabindex: -1 }, content.flat()), tag === 'tbody' ? vue.h(tag, { class: 'q-virtual-scroll__padding', key: 'after', ref: afterRef }, [ vue.h('tr', [ vue.h('td', { style: { [ paddingSize ]: `${ virtualScrollPaddingAfter.value }px`, ...style }, colspan: colspanAttr.value }) ]) ]) : vue.h(tag, { class: 'q-virtual-scroll__padding', key: 'after', ref: afterRef, style: { [ paddingSize ]: `${ virtualScrollPaddingAfter.value }px`, ...style } }) ] } function emitScroll (index) { if (prevToIndex !== index) { props.onVirtualScroll !== void 0 && emit('virtualScroll', { index, from: virtualScrollSliceRange.value.from, to: virtualScrollSliceRange.value.to - 1, direction: index < prevToIndex ? 'decrease' : 'increase', ref: proxy }); prevToIndex = index; } } setVirtualScrollSize(); const onVirtualScrollEvt = debounce( localOnVirtualScrollEvt, $q.platform.is.ios === true ? 120 : 35 ); vue.onBeforeMount(() => { setVirtualScrollSize(); }); let shouldActivate = false; vue.onDeactivated(() => { shouldActivate = true; }); vue.onActivated(() => { if (shouldActivate !== true) { return } const scrollEl = getVirtualScrollTarget(); if (prevScrollStart !== void 0 && scrollEl !== void 0 && scrollEl !== null && scrollEl.nodeType !== 8) { setScroll( scrollEl, prevScrollStart, props.virtualScrollHorizontal, $q.lang.rtl ); } else { scrollTo(prevToIndex); } }); vue.onBeforeUnmount(() => { onVirtualScrollEvt.cancel(); }); // expose public methods Object.assign(proxy, { scrollTo, reset, refresh }); return { virtualScrollSliceRange, virtualScrollSliceSizeComputed, setVirtualScrollSize, onVirtualScrollEvt, localResetVirtualScroll, padVirtualScroll, scrollTo, reset, refresh } } const validateNewValueMode = v => [ 'add', 'add-unique', 'toggle' ].includes(v); const reEscapeList = '.*+?^${}()|[]\\'; const fieldPropsList = Object.keys(useFieldProps); var QSelect = createComponent({ name: 'QSelect', inheritAttrs: false, props: { ...useVirtualScrollProps, ...useFormProps, ...useFieldProps, modelValue: { required: true }, multiple: Boolean, displayValue: [ String, Number ], displayValueHtml: Boolean, dropdownIcon: String, options: { type: Array, default: () => [] }, optionValue: [ Function, String ], optionLabel: [ Function, String ], optionDisable: [ Function, String ], hideSelected: Boolean, hideDropdownIcon: Boolean, fillInput: Boolean, maxValues: [ Number, String ], optionsDense: Boolean, optionsDark: { type: Boolean, default: null }, optionsSelectedClass: String, optionsHtml: Boolean, optionsCover: Boolean, menuShrink: Boolean, menuAnchor: String, menuSelf: String, menuOffset: Array, popupContentClass: String, popupContentStyle: [ String, Array, Object ], useInput: Boolean, useChips: Boolean, newValueMode: { type: String, validator: validateNewValueMode }, mapOptions: Boolean, emitValue: Boolean, inputDebounce: { type: [ Number, String ], default: 500 }, inputClass: [ Array, String, Object ], inputStyle: [ Array, String, Object ], tabindex: { type: [ String, Number ], default: 0 }, autocomplete: String, transitionShow: String, transitionHide: String, transitionDuration: [ String, Number ], behavior: { type: String, validator: v => [ 'default', 'menu', 'dialog' ].includes(v), default: 'default' }, virtualScrollItemSize: { type: [ Number, String ], default: void 0 }, onNewValue: Function, onFilter: Function }, emits: [ ...useFieldEmits, 'add', 'remove', 'inputValue', 'newValue', 'keyup', 'keypress', 'keydown', 'filterAbort' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const menu = vue.ref(false); const dialog = vue.ref(false); const optionIndex = vue.ref(-1); const inputValue = vue.ref(''); const dialogFieldFocused = vue.ref(false); const innerLoadingIndicator = vue.ref(false); let inputTimer = null, innerValueCache, hasDialog, userInputValue, filterId = null, defaultInputValue, transitionShowComputed, searchBuffer, searchBufferExp; const inputRef = vue.ref(null); const targetRef = vue.ref(null); const menuRef = vue.ref(null); const dialogRef = vue.ref(null); const menuContentRef = vue.ref(null); const nameProp = useFormInputNameAttr(props); const onComposition = useKeyComposition(onInput); const virtualScrollLength = vue.computed(() => ( Array.isArray(props.options) ? props.options.length : 0 )); const virtualScrollItemSizeComputed = vue.computed(() => ( props.virtualScrollItemSize === void 0 ? (props.optionsDense === true ? 24 : 48) : props.virtualScrollItemSize )); const { virtualScrollSliceRange, virtualScrollSliceSizeComputed, localResetVirtualScroll, padVirtualScroll, onVirtualScrollEvt, scrollTo, setVirtualScrollSize } = useVirtualScroll({ virtualScrollLength, getVirtualScrollTarget, getVirtualScrollEl, virtualScrollItemSizeComputed }); const state = useFieldState(); const innerValue = vue.computed(() => { const mapNull = props.mapOptions === true && props.multiple !== true, val = props.modelValue !== void 0 && (props.modelValue !== null || mapNull === true) ? (props.multiple === true && Array.isArray(props.modelValue) ? props.modelValue : [ props.modelValue ]) : []; if (props.mapOptions === true && Array.isArray(props.options) === true) { const cache = props.mapOptions === true && innerValueCache !== void 0 ? innerValueCache : []; const values = val.map(v => getOption(v, cache)); return props.modelValue === null && mapNull === true ? values.filter(v => v !== null) : values } return val }); const innerFieldProps = vue.computed(() => { const acc = {}; fieldPropsList.forEach(key => { const val = props[ key ]; if (val !== void 0) { acc[ key ] = val; } }); return acc }); const isOptionsDark = vue.computed(() => ( props.optionsDark === null ? state.isDark.value : props.optionsDark )); const hasValue = vue.computed(() => fieldValueIsFilled(innerValue.value)); const computedInputClass = vue.computed(() => { let cls = 'q-field__input q-placeholder col'; if (props.hideSelected === true || innerValue.value.length === 0) { return [ cls, props.inputClass ] } cls += ' q-field__input--padding'; return props.inputClass === void 0 ? cls : [ cls, props.inputClass ] }); const menuContentClass = vue.computed(() => (props.virtualScrollHorizontal === true ? 'q-virtual-scroll--horizontal' : '') + (props.popupContentClass ? ' ' + props.popupContentClass : '') ); const noOptions = vue.computed(() => virtualScrollLength.value === 0); const selectedString = vue.computed(() => innerValue.value .map(opt => getOptionLabel.value(opt)) .join(', ') ); const ariaCurrentValue = vue.computed(() => (props.displayValue !== void 0 ? props.displayValue : selectedString.value )); const needsHtmlFn = vue.computed(() => ( props.optionsHtml === true ? () => true : opt => opt !== void 0 && opt !== null && opt.html === true )); const valueAsHtml = vue.computed(() => ( props.displayValueHtml === true || ( props.displayValue === void 0 && ( props.optionsHtml === true || innerValue.value.some(needsHtmlFn.value) ) ) )); const tabindex = vue.computed(() => (state.focused.value === true ? props.tabindex : -1)); const comboboxAttrs = vue.computed(() => { const attrs = { tabindex: props.tabindex, role: 'combobox', 'aria-label': props.label, 'aria-readonly': props.readonly === true ? 'true' : 'false', 'aria-autocomplete': props.useInput === true ? 'list' : 'none', 'aria-expanded': menu.value === true ? 'true' : 'false', 'aria-controls': `${ state.targetUid.value }_lb` }; if (optionIndex.value >= 0) { attrs[ 'aria-activedescendant' ] = `${ state.targetUid.value }_${ optionIndex.value }`; } return attrs }); const listboxAttrs = vue.computed(() => ({ id: `${ state.targetUid.value }_lb`, role: 'listbox', 'aria-multiselectable': props.multiple === true ? 'true' : 'false' })); const selectedScope = vue.computed(() => { return innerValue.value.map((opt, i) => ({ index: i, opt, html: needsHtmlFn.value(opt), selected: true, removeAtIndex: removeAtIndexAndFocus, toggleOption, tabindex: tabindex.value })) }); const optionScope = vue.computed(() => { if (virtualScrollLength.value === 0) { return [] } const { from, to } = virtualScrollSliceRange.value; return props.options.slice(from, to).map((opt, i) => { const disable = isOptionDisabled.value(opt) === true; const index = from + i; const itemProps = { clickable: true, active: false, activeClass: computedOptionsSelectedClass.value, manualFocus: true, focused: false, disable, tabindex: -1, dense: props.optionsDense, dark: isOptionsDark.value, role: 'option', id: `${ state.targetUid.value }_${ index }`, onClick: () => { toggleOption(opt); } }; if (disable !== true) { isOptionSelected(opt) === true && (itemProps.active = true); optionIndex.value === index && (itemProps.focused = true); itemProps[ 'aria-selected' ] = itemProps.active === true ? 'true' : 'false'; if ($q.platform.is.desktop === true) { itemProps.onMousemove = () => { menu.value === true && setOptionIndex(index); }; } } return { index, opt, html: needsHtmlFn.value(opt), label: getOptionLabel.value(opt), selected: itemProps.active, focused: itemProps.focused, toggleOption, setOptionIndex, itemProps } }) }); const dropdownArrowIcon = vue.computed(() => ( props.dropdownIcon !== void 0 ? props.dropdownIcon : $q.iconSet.arrow.dropdown )); const squaredMenu = vue.computed(() => props.optionsCover === false && props.outlined !== true && props.standout !== true && props.borderless !== true && props.rounded !== true ); const computedOptionsSelectedClass = vue.computed(() => ( props.optionsSelectedClass !== void 0 ? props.optionsSelectedClass : (props.color !== void 0 ? `text-${ props.color }` : '') )); // returns method to get value of an option; // takes into account 'option-value' prop const getOptionValue = vue.computed(() => getPropValueFn(props.optionValue, 'value')); // returns method to get label of an option; // takes into account 'option-label' prop const getOptionLabel = vue.computed(() => getPropValueFn(props.optionLabel, 'label')); // returns method to tell if an option is disabled; // takes into account 'option-disable' prop const isOptionDisabled = vue.computed(() => getPropValueFn(props.optionDisable, 'disable')); const innerOptionsValue = vue.computed(() => innerValue.value.map(opt => getOptionValue.value(opt))); const inputControlEvents = vue.computed(() => { const evt = { onInput, // Safari < 10.2 & UIWebView doesn't fire compositionend when // switching focus before confirming composition choice // this also fixes the issue where some browsers e.g. iOS Chrome // fires "change" instead of "input" on autocomplete. onChange: onComposition, onKeydown: onTargetKeydown, onKeyup: onTargetAutocomplete, onKeypress: onTargetKeypress, onFocus: selectInputText, onClick (e) { hasDialog === true && stop(e); } }; evt.onCompositionstart = evt.onCompositionupdate = evt.onCompositionend = onComposition; return evt }); vue.watch(innerValue, val => { innerValueCache = val; if ( props.useInput === true && props.fillInput === true && props.multiple !== true // Prevent re-entering in filter while filtering // Also prevent clearing inputValue while filtering && state.innerLoading.value !== true && ((dialog.value !== true && menu.value !== true) || hasValue.value !== true) ) { userInputValue !== true && resetInputValue(); if (dialog.value === true || menu.value === true) { filter(''); } } }, { immediate: true }); vue.watch(() => props.fillInput, resetInputValue); vue.watch(menu, updateMenu); vue.watch(virtualScrollLength, rerenderMenu); function getEmittingOptionValue (opt) { return props.emitValue === true ? getOptionValue.value(opt) : opt } function removeAtIndex (index) { if (index > -1 && index < innerValue.value.length) { if (props.multiple === true) { const model = props.modelValue.slice(); emit('remove', { index, value: model.splice(index, 1)[ 0 ] }); emit('update:modelValue', model); } else { emit('update:modelValue', null); } } } function removeAtIndexAndFocus (index) { removeAtIndex(index); state.focus(); } function add (opt, unique) { const val = getEmittingOptionValue(opt); if (props.multiple !== true) { props.fillInput === true && updateInputValue( getOptionLabel.value(opt), true, true ); emit('update:modelValue', val); return } if (innerValue.value.length === 0) { emit('add', { index: 0, value: val }); emit('update:modelValue', props.multiple === true ? [ val ] : val); return } if (unique === true && isOptionSelected(opt) === true) { return } if (props.maxValues !== void 0 && props.modelValue.length >= props.maxValues) { return } const model = props.modelValue.slice(); emit('add', { index: model.length, value: val }); model.push(val); emit('update:modelValue', model); } function toggleOption (opt, keepOpen) { if (state.editable.value !== true || opt === void 0 || isOptionDisabled.value(opt) === true) { return } const optValue = getOptionValue.value(opt); if (props.multiple !== true) { if (keepOpen !== true) { updateInputValue( props.fillInput === true ? getOptionLabel.value(opt) : '', true, true ); hidePopup(); } targetRef.value !== null && targetRef.value.focus(); if ( innerValue.value.length === 0 || isDeepEqual(getOptionValue.value(innerValue.value[ 0 ]), optValue) !== true ) { emit('update:modelValue', props.emitValue === true ? optValue : opt); } return } (hasDialog !== true || dialogFieldFocused.value === true) && state.focus(); selectInputText(); if (innerValue.value.length === 0) { const val = props.emitValue === true ? optValue : opt; emit('add', { index: 0, value: val }); emit('update:modelValue', props.multiple === true ? [ val ] : val); return } const model = props.modelValue.slice(), index = innerOptionsValue.value.findIndex(v => isDeepEqual(v, optValue)); if (index > -1) { emit('remove', { index, value: model.splice(index, 1)[ 0 ] }); } else { if (props.maxValues !== void 0 && model.length >= props.maxValues) { return } const val = props.emitValue === true ? optValue : opt; emit('add', { index: model.length, value: val }); model.push(val); } emit('update:modelValue', model); } function setOptionIndex (index) { if ($q.platform.is.desktop !== true) { return } const val = index > -1 && index < virtualScrollLength.value ? index : -1; if (optionIndex.value !== val) { optionIndex.value = val; } } function moveOptionSelection (offset = 1, skipInputValue) { if (menu.value === true) { let index = optionIndex.value; do { index = normalizeToInterval( index + offset, -1, virtualScrollLength.value - 1 ); } while (index !== -1 && index !== optionIndex.value && isOptionDisabled.value(props.options[ index ]) === true) if (optionIndex.value !== index) { setOptionIndex(index); scrollTo(index); if (skipInputValue !== true && props.useInput === true && props.fillInput === true) { setInputValue(index >= 0 ? getOptionLabel.value(props.options[ index ]) : defaultInputValue ); } } } } function getOption (value, valueCache) { const fn = opt => isDeepEqual(getOptionValue.value(opt), value); return props.options.find(fn) || valueCache.find(fn) || value } function getPropValueFn (propValue, defaultVal) { const val = propValue !== void 0 ? propValue : defaultVal; return typeof val === 'function' ? val : opt => (opt !== null && typeof opt === 'object' && val in opt ? opt[ val ] : opt) } function isOptionSelected (opt) { const val = getOptionValue.value(opt); return innerOptionsValue.value.find(v => isDeepEqual(v, val)) !== void 0 } function selectInputText (e) { if ( props.useInput === true && targetRef.value !== null && (e === void 0 || (targetRef.value === e.target && e.target.value === selectedString.value)) ) { targetRef.value.select(); } } function onTargetKeyup (e) { // if ESC and we have an opened menu // then stop propagation (might be caught by a QDialog // and so it will also close the QDialog, which is wrong) if (isKeyCode(e, 27) === true && menu.value === true) { stop(e); // on ESC we need to close the dialog also hidePopup(); resetInputValue(); } emit('keyup', e); } function onTargetAutocomplete (e) { const { value } = e.target; if (e.keyCode !== void 0) { onTargetKeyup(e); return } e.target.value = ''; if (inputTimer !== null) { clearTimeout(inputTimer); inputTimer = null; } resetInputValue(); if (typeof value === 'string' && value.length !== 0) { const needle = value.toLocaleLowerCase(); const findFn = extractFn => { const option = props.options.find(opt => extractFn.value(opt).toLocaleLowerCase() === needle); if (option === void 0) { return false } if (innerValue.value.indexOf(option) === -1) { toggleOption(option); } else { hidePopup(); } return true }; const fillFn = afterFilter => { if (findFn(getOptionValue) === true) { return } if (findFn(getOptionLabel) === true || afterFilter === true) { return } filter(value, true, () => fillFn(true)); }; fillFn(); } else { state.clearValue(e); } } function onTargetKeypress (e) { emit('keypress', e); } function onTargetKeydown (e) { emit('keydown', e); if (shouldIgnoreKey(e) === true) { return } const newValueModeValid = inputValue.value.length !== 0 && (props.newValueMode !== void 0 || props.onNewValue !== void 0); const tabShouldSelect = e.shiftKey !== true && props.multiple !== true && (optionIndex.value > -1 || newValueModeValid === true); // escape if (e.keyCode === 27) { prevent(e); // prevent clearing the inputValue return } // tab if (e.keyCode === 9 && tabShouldSelect === false) { closeMenu(); return } if ( e.target === void 0 || e.target.id !== state.targetUid.value || state.editable.value !== true ) { return } // down if ( e.keyCode === 40 && state.innerLoading.value !== true && menu.value === false ) { stopAndPrevent(e); showPopup(); return } // backspace if ( e.keyCode === 8 && props.hideSelected !== true && inputValue.value.length === 0 ) { if (props.multiple === true && Array.isArray(props.modelValue) === true) { removeAtIndex(props.modelValue.length - 1); } else if (props.multiple !== true && props.modelValue !== null) { emit('update:modelValue', null); } return } // home, end - 36, 35 if ( (e.keyCode === 35 || e.keyCode === 36) && (typeof inputValue.value !== 'string' || inputValue.value.length === 0) ) { stopAndPrevent(e); optionIndex.value = -1; moveOptionSelection(e.keyCode === 36 ? 1 : -1, props.multiple); } // pg up, pg down - 33, 34 if ( (e.keyCode === 33 || e.keyCode === 34) && virtualScrollSliceSizeComputed.value !== void 0 ) { stopAndPrevent(e); optionIndex.value = Math.max( -1, Math.min( virtualScrollLength.value, optionIndex.value + (e.keyCode === 33 ? -1 : 1) * virtualScrollSliceSizeComputed.value.view ) ); moveOptionSelection(e.keyCode === 33 ? 1 : -1, props.multiple); } // up, down if (e.keyCode === 38 || e.keyCode === 40) { stopAndPrevent(e); moveOptionSelection(e.keyCode === 38 ? -1 : 1, props.multiple); } const optionsLength = virtualScrollLength.value; // clear search buffer if expired if (searchBuffer === void 0 || searchBufferExp < Date.now()) { searchBuffer = ''; } // keyboard search when not having use-input if ( optionsLength > 0 && props.useInput !== true && e.key !== void 0 && e.key.length === 1 // printable char && e.altKey === false // not kbd shortcut && e.ctrlKey === false // not kbd shortcut && e.metaKey === false // not kbd shortcut, especially on macOS with Command key && (e.keyCode !== 32 || searchBuffer.length !== 0) // space in middle of search ) { menu.value !== true && showPopup(e); const char = e.key.toLocaleLowerCase(), keyRepeat = searchBuffer.length === 1 && searchBuffer[ 0 ] === char; searchBufferExp = Date.now() + 1500; if (keyRepeat === false) { stopAndPrevent(e); searchBuffer += char; } const searchRe = new RegExp('^' + searchBuffer.split('').map(l => (reEscapeList.indexOf(l) > -1 ? '\\' + l : l)).join('.*'), 'i'); let index = optionIndex.value; if (keyRepeat === true || index < 0 || searchRe.test(getOptionLabel.value(props.options[ index ])) !== true) { do { index = normalizeToInterval(index + 1, -1, optionsLength - 1); } while (index !== optionIndex.value && ( isOptionDisabled.value(props.options[ index ]) === true || searchRe.test(getOptionLabel.value(props.options[ index ])) !== true )) } if (optionIndex.value !== index) { vue.nextTick(() => { setOptionIndex(index); scrollTo(index); if (index >= 0 && props.useInput === true && props.fillInput === true) { setInputValue(getOptionLabel.value(props.options[ index ])); } }); } return } // enter, space (when not using use-input and not in search), or tab (when not using multiple and option selected) // same target is checked above if ( e.keyCode !== 13 && (e.keyCode !== 32 || props.useInput === true || searchBuffer !== '') && (e.keyCode !== 9 || tabShouldSelect === false) ) { return } e.keyCode !== 9 && stopAndPrevent(e); if (optionIndex.value > -1 && optionIndex.value < optionsLength) { toggleOption(props.options[ optionIndex.value ]); return } if (newValueModeValid === true) { const done = (val, mode) => { if (mode) { if (validateNewValueMode(mode) !== true) { return } } else { mode = props.newValueMode; } if (val === void 0 || val === null) { return } updateInputValue('', props.multiple !== true, true); const fn = mode === 'toggle' ? toggleOption : add; fn(val, mode === 'add-unique'); if (props.multiple !== true) { targetRef.value !== null && targetRef.value.focus(); hidePopup(); } }; if (props.onNewValue !== void 0) { emit('newValue', inputValue.value, done); } else { done(inputValue.value); } if (props.multiple !== true) { return } } if (menu.value === true) { closeMenu(); } else if (state.innerLoading.value !== true) { showPopup(); } } function getVirtualScrollEl () { return hasDialog === true ? menuContentRef.value : ( menuRef.value !== null && menuRef.value.contentEl !== null ? menuRef.value.contentEl : void 0 ) } function getVirtualScrollTarget () { return getVirtualScrollEl() } function getSelection () { if (props.hideSelected === true) { return [] } if (slots[ 'selected-item' ] !== void 0) { return selectedScope.value.map(scope => slots[ 'selected-item' ](scope)).slice() } if (slots.selected !== void 0) { return [].concat(slots.selected()) } if (props.useChips === true) { return selectedScope.value.map((scope, i) => vue.h(QChip, { key: 'option-' + i, removable: state.editable.value === true && isOptionDisabled.value(scope.opt) !== true, dense: true, textColor: props.color, tabindex: tabindex.value, onRemove () { scope.removeAtIndex(i); } }, () => vue.h('span', { class: 'ellipsis', [ scope.html === true ? 'innerHTML' : 'textContent' ]: getOptionLabel.value(scope.opt) }))) } return [ vue.h('span', { [ valueAsHtml.value === true ? 'innerHTML' : 'textContent' ]: ariaCurrentValue.value }) ] } function getAllOptions () { if (noOptions.value === true) { return slots[ 'no-option' ] !== void 0 ? slots[ 'no-option' ]({ inputValue: inputValue.value }) : void 0 } const fn = slots.option !== void 0 ? slots.option : scope => { return vue.h(QItem, { key: scope.index, ...scope.itemProps }, () => { return vue.h( QItemSection, () => vue.h( QItemLabel, () => vue.h('span', { [ scope.html === true ? 'innerHTML' : 'textContent' ]: scope.label }) ) ) }) }; let options = padVirtualScroll('div', optionScope.value.map(fn)); if (slots[ 'before-options' ] !== void 0) { options = slots[ 'before-options' ]().concat(options); } return hMergeSlot(slots[ 'after-options' ], options) } function getInput (fromDialog, isTarget) { const attrs = isTarget === true ? { ...comboboxAttrs.value, ...state.splitAttrs.attributes.value } : void 0; const data = { ref: isTarget === true ? targetRef : void 0, key: 'i_t', class: computedInputClass.value, style: props.inputStyle, value: inputValue.value !== void 0 ? inputValue.value : '', // required for Android in order to show ENTER key when in form type: 'search', ...attrs, id: isTarget === true ? state.targetUid.value : void 0, maxlength: props.maxlength, autocomplete: props.autocomplete, 'data-autofocus': fromDialog === true || props.autofocus === true || void 0, disabled: props.disable === true, readonly: props.readonly === true, ...inputControlEvents.value }; if (fromDialog !== true && hasDialog === true) { if (Array.isArray(data.class) === true) { data.class = [ ...data.class, 'no-pointer-events' ]; } else { data.class += ' no-pointer-events'; } } return vue.h('input', data) } function onInput (e) { if (inputTimer !== null) { clearTimeout(inputTimer); inputTimer = null; } if (e && e.target && e.target.qComposing === true) { return } setInputValue(e.target.value || ''); // mark it here as user input so that if updateInputValue is called // before filter is called the indicator is reset userInputValue = true; defaultInputValue = inputValue.value; if ( state.focused.value !== true && (hasDialog !== true || dialogFieldFocused.value === true) ) { state.focus(); } if (props.onFilter !== void 0) { inputTimer = setTimeout(() => { inputTimer = null; filter(inputValue.value); }, props.inputDebounce); } } function setInputValue (val) { if (inputValue.value !== val) { inputValue.value = val; emit('inputValue', val); } } function updateInputValue (val, noFiltering, internal) { userInputValue = internal !== true; if (props.useInput === true) { setInputValue(val); if (noFiltering === true || internal !== true) { defaultInputValue = val; } noFiltering !== true && filter(val); } } function filter (val, keepClosed, afterUpdateFn) { if (props.onFilter === void 0 || (keepClosed !== true && state.focused.value !== true)) { return } if (state.innerLoading.value === true) { emit('filterAbort'); } else { state.innerLoading.value = true; innerLoadingIndicator.value = true; } if ( val !== '' && props.multiple !== true && innerValue.value.length !== 0 && userInputValue !== true && val === getOptionLabel.value(innerValue.value[ 0 ]) ) { val = ''; } const localFilterId = setTimeout(() => { menu.value === true && (menu.value = false); }, 10); filterId !== null && clearTimeout(filterId); filterId = localFilterId; emit( 'filter', val, (fn, afterFn) => { if ((keepClosed === true || state.focused.value === true) && filterId === localFilterId) { clearTimeout(filterId); typeof fn === 'function' && fn(); // hide indicator to allow arrow to animate innerLoadingIndicator.value = false; vue.nextTick(() => { state.innerLoading.value = false; if (state.editable.value === true) { if (keepClosed === true) { menu.value === true && hidePopup(); } else if (menu.value === true) { updateMenu(true); } else { menu.value = true; } } typeof afterFn === 'function' && vue.nextTick(() => { afterFn(proxy); }); typeof afterUpdateFn === 'function' && vue.nextTick(() => { afterUpdateFn(proxy); }); }); } }, () => { if (state.focused.value === true && filterId === localFilterId) { clearTimeout(filterId); state.innerLoading.value = false; innerLoadingIndicator.value = false; } menu.value === true && (menu.value = false); } ); } function getMenu () { return vue.h(QMenu, { ref: menuRef, class: menuContentClass.value, style: props.popupContentStyle, modelValue: menu.value, fit: props.menuShrink !== true, cover: props.optionsCover === true && noOptions.value !== true && props.useInput !== true, anchor: props.menuAnchor, self: props.menuSelf, offset: props.menuOffset, dark: isOptionsDark.value, noParentEvent: true, noRefocus: true, noFocus: true, square: squaredMenu.value, transitionShow: props.transitionShow, transitionHide: props.transitionHide, transitionDuration: props.transitionDuration, separateClosePopup: true, ...listboxAttrs.value, onScrollPassive: onVirtualScrollEvt, onBeforeShow: onControlPopupShow, onBeforeHide: onMenuBeforeHide, onShow: onMenuShow }, getAllOptions) } function onMenuBeforeHide (e) { onControlPopupHide(e); closeMenu(); } function onMenuShow () { setVirtualScrollSize(); } function onDialogFieldFocus (e) { stop(e); targetRef.value !== null && targetRef.value.focus(); dialogFieldFocused.value = true; window.scrollTo(window.pageXOffset || window.scrollX || document.body.scrollLeft || 0, 0); } function onDialogFieldBlur (e) { stop(e); vue.nextTick(() => { dialogFieldFocused.value = false; }); } function getDialog () { const content = [ vue.h(QField, { class: `col-auto ${ state.fieldClass.value }`, ...innerFieldProps.value, for: state.targetUid.value, dark: isOptionsDark.value, square: true, loading: innerLoadingIndicator.value, itemAligned: false, filled: true, stackLabel: inputValue.value.length !== 0, ...state.splitAttrs.listeners.value, onFocus: onDialogFieldFocus, onBlur: onDialogFieldBlur }, { ...slots, rawControl: () => state.getControl(true), before: void 0, after: void 0 }) ]; menu.value === true && content.push( vue.h('div', { ref: menuContentRef, class: menuContentClass.value + ' scroll', style: props.popupContentStyle, ...listboxAttrs.value, onClick: prevent, onScrollPassive: onVirtualScrollEvt }, getAllOptions()) ); return vue.h(QDialog, { ref: dialogRef, modelValue: dialog.value, position: props.useInput === true ? 'top' : void 0, transitionShow: transitionShowComputed, transitionHide: props.transitionHide, transitionDuration: props.transitionDuration, onBeforeShow: onControlPopupShow, onBeforeHide: onDialogBeforeHide, onHide: onDialogHide, onShow: onDialogShow }, () => vue.h('div', { class: 'q-select__dialog' + (isOptionsDark.value === true ? ' q-select__dialog--dark q-dark' : '') + (dialogFieldFocused.value === true ? ' q-select__dialog--focused' : '') }, content)) } function onDialogBeforeHide (e) { onControlPopupHide(e); if (dialogRef.value !== null) { dialogRef.value.__updateRefocusTarget( state.rootRef.value.querySelector('.q-field__native > [tabindex]:last-child') ); } state.focused.value = false; } function onDialogHide (e) { hidePopup(); state.focused.value === false && emit('blur', e); resetInputValue(); } function onDialogShow () { const el = document.activeElement; if ( (el === null || el.id !== state.targetUid.value) && targetRef.value !== null && targetRef.value !== el ) { targetRef.value.focus(); } setVirtualScrollSize(); } function closeMenu () { if (dialog.value === true) { return } optionIndex.value = -1; if (menu.value === true) { menu.value = false; } if (state.focused.value === false) { if (filterId !== null) { clearTimeout(filterId); filterId = null; } if (state.innerLoading.value === true) { emit('filterAbort'); state.innerLoading.value = false; innerLoadingIndicator.value = false; } } } function showPopup (e) { if (state.editable.value !== true) { return } if (hasDialog === true) { state.onControlFocusin(e); dialog.value = true; vue.nextTick(() => { state.focus(); }); } else { state.focus(); } if (props.onFilter !== void 0) { filter(inputValue.value); } else if (noOptions.value !== true || slots[ 'no-option' ] !== void 0) { menu.value = true; } } function hidePopup () { dialog.value = false; closeMenu(); } function resetInputValue () { props.useInput === true && updateInputValue( props.multiple !== true && props.fillInput === true && innerValue.value.length !== 0 ? getOptionLabel.value(innerValue.value[ 0 ]) || '' : '', true, true ); } function updateMenu (show) { let optionIndex = -1; if (show === true) { if (innerValue.value.length !== 0) { const val = getOptionValue.value(innerValue.value[ 0 ]); optionIndex = props.options.findIndex(v => isDeepEqual(getOptionValue.value(v), val)); } localResetVirtualScroll(optionIndex); } setOptionIndex(optionIndex); } function rerenderMenu (newLength, oldLength) { if (menu.value === true && state.innerLoading.value === false) { localResetVirtualScroll(-1, true); vue.nextTick(() => { if (menu.value === true && state.innerLoading.value === false) { if (newLength > oldLength) { localResetVirtualScroll(); } else { updateMenu(true); } } }); } } function updateMenuPosition () { if (dialog.value === false && menuRef.value !== null) { menuRef.value.updatePosition(); } } function onControlPopupShow (e) { e !== void 0 && stop(e); emit('popupShow', e); state.hasPopupOpen = true; state.onControlFocusin(e); } function onControlPopupHide (e) { e !== void 0 && stop(e); emit('popupHide', e); state.hasPopupOpen = false; state.onControlFocusout(e); } function updatePreState () { hasDialog = $q.platform.is.mobile !== true && props.behavior !== 'dialog' ? false : props.behavior !== 'menu' && ( props.useInput === true ? slots[ 'no-option' ] !== void 0 || props.onFilter !== void 0 || noOptions.value === false : true ); transitionShowComputed = $q.platform.is.ios === true && hasDialog === true && props.useInput === true ? 'fade' : props.transitionShow; } vue.onBeforeUpdate(updatePreState); vue.onUpdated(updateMenuPosition); updatePreState(); vue.onBeforeUnmount(() => { inputTimer !== null && clearTimeout(inputTimer); }); // expose public methods Object.assign(proxy, { showPopup, hidePopup, removeAtIndex, add, toggleOption, getOptionIndex: () => optionIndex.value, setOptionIndex, moveOptionSelection, filter, updateMenuPosition, updateInputValue, isOptionSelected, getEmittingOptionValue, isOptionDisabled: (...args) => isOptionDisabled.value.apply(null, args) === true, getOptionValue: (...args) => getOptionValue.value.apply(null, args), getOptionLabel: (...args) => getOptionLabel.value.apply(null, args) }); Object.assign(state, { innerValue, fieldClass: vue.computed(() => `q-select q-field--auto-height q-select--with${ props.useInput !== true ? 'out' : '' }-input` + ` q-select--with${ props.useChips !== true ? 'out' : '' }-chips` + ` q-select--${ props.multiple === true ? 'multiple' : 'single' }` ), inputRef, targetRef, hasValue, showPopup, floatingLabel: vue.computed(() => (props.hideSelected !== true && hasValue.value === true) || typeof inputValue.value === 'number' || inputValue.value.length !== 0 || fieldValueIsFilled(props.displayValue) ), getControlChild: () => { if ( state.editable.value !== false && ( dialog.value === true // dialog always has menu displayed, so need to render it || noOptions.value !== true || slots[ 'no-option' ] !== void 0 ) ) { return hasDialog === true ? getDialog() : getMenu() } else if (state.hasPopupOpen === true) { // explicitly set it otherwise TAB will not blur component state.hasPopupOpen = false; } }, controlEvents: { onFocusin (e) { state.onControlFocusin(e); }, onFocusout (e) { state.onControlFocusout(e, () => { resetInputValue(); closeMenu(); }); }, onClick (e) { // label from QField will propagate click on the input prevent(e); if (hasDialog !== true && menu.value === true) { closeMenu(); targetRef.value !== null && targetRef.value.focus(); return } showPopup(e); } }, getControl: fromDialog => { const child = getSelection(); const isTarget = fromDialog === true || dialog.value !== true || hasDialog !== true; if (props.useInput === true) { child.push(getInput(fromDialog, isTarget)); } // there can be only one (when dialog is opened the control in dialog should be target) else if (state.editable.value === true) { const attrs = isTarget === true ? comboboxAttrs.value : void 0; child.push( vue.h('input', { ref: isTarget === true ? targetRef : void 0, key: 'd_t', class: 'q-select__focus-target', id: isTarget === true ? state.targetUid.value : void 0, value: ariaCurrentValue.value, readonly: true, 'data-autofocus': fromDialog === true || props.autofocus === true || void 0, ...attrs, onKeydown: onTargetKeydown, onKeyup: onTargetKeyup, onKeypress: onTargetKeypress }) ); if (isTarget === true && typeof props.autocomplete === 'string' && props.autocomplete.length !== 0) { child.push( vue.h('input', { class: 'q-select__autocomplete-input', autocomplete: props.autocomplete, tabindex: -1, onKeyup: onTargetAutocomplete }) ); } } if (nameProp.value !== void 0 && props.disable !== true && innerOptionsValue.value.length !== 0) { const opts = innerOptionsValue.value.map(value => vue.h('option', { value, selected: true })); child.push( vue.h('select', { class: 'hidden', name: nameProp.value, multiple: props.multiple }, opts) ); } const attrs = props.useInput === true || isTarget !== true ? void 0 : state.splitAttrs.attributes.value; return vue.h('div', { class: 'q-field__native row items-center', ...attrs, ...state.splitAttrs.listeners.value }, child) }, getInnerAppend: () => ( props.loading !== true && innerLoadingIndicator.value !== true && props.hideDropdownIcon !== true ? [ vue.h(QIcon, { class: 'q-select__dropdown-icon' + (menu.value === true ? ' rotate-180' : ''), name: dropdownArrowIcon.value }) ] : null ) }); return useField(state) } }); const skeletonTypes = [ 'text', 'rect', 'circle', 'QBtn', 'QBadge', 'QChip', 'QToolbar', 'QCheckbox', 'QRadio', 'QToggle', 'QSlider', 'QRange', 'QInput', 'QAvatar' ]; const skeletonAnimations = [ 'wave', 'pulse', 'pulse-x', 'pulse-y', 'fade', 'blink', 'none' ]; var QSkeleton = createComponent({ name: 'QSkeleton', props: { ...useDarkProps, tag: { type: String, default: 'div' }, type: { type: String, validator: v => skeletonTypes.includes(v), default: 'rect' }, animation: { type: String, validator: v => skeletonAnimations.includes(v), default: 'wave' }, animationSpeed: { type: [ String, Number ], default: 1500 }, square: Boolean, bordered: Boolean, size: String, width: String, height: String }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const style = vue.computed(() => { const size = props.size !== void 0 ? [ props.size, props.size ] : [ props.width, props.height ]; return { '--q-skeleton-speed': `${ props.animationSpeed }ms`, width: size[ 0 ], height: size[ 1 ] } }); const classes = vue.computed(() => `q-skeleton q-skeleton--${ isDark.value === true ? 'dark' : 'light' } q-skeleton--type-${ props.type }` + (props.animation !== 'none' ? ` q-skeleton--anim q-skeleton--anim-${ props.animation }` : '') + (props.square === true ? ' q-skeleton--square' : '') + (props.bordered === true ? ' q-skeleton--bordered' : '') ); return () => vue.h(props.tag, { class: classes.value, style: style.value }, hSlot(slots.default)) } }); const slotsDef = [ [ 'left', 'center', 'start', 'width' ], [ 'right', 'center', 'end', 'width' ], [ 'top', 'start', 'center', 'height' ], [ 'bottom', 'end', 'center', 'height' ] ]; var QSlideItem = createComponent({ name: 'QSlideItem', props: { ...useDarkProps, leftColor: String, rightColor: String, topColor: String, bottomColor: String, onSlide: Function }, emits: [ 'action', 'top', 'right', 'bottom', 'left' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const { getCacheWithFn } = useCache(); const contentRef = vue.ref(null); let timer = null, pan = {}, dirRefs = {}, dirContentRefs = {}; const langDir = vue.computed(() => ( $q.lang.rtl === true ? { left: 'right', right: 'left' } : { left: 'left', right: 'right' } )); const classes = vue.computed(() => 'q-slide-item q-item-type overflow-hidden' + (isDark.value === true ? ' q-slide-item--dark q-dark' : '') ); function reset () { contentRef.value.style.transform = 'translate(0,0)'; } function emitSlide (side, ratio, isReset) { props.onSlide !== void 0 && emit('slide', { side, ratio, isReset }); } function onPan (evt) { const node = contentRef.value; if (evt.isFirst) { pan = { dir: null, size: { left: 0, right: 0, top: 0, bottom: 0 }, scale: 0 }; node.classList.add('no-transition'); slotsDef.forEach(slotName => { if (slots[ slotName[ 0 ] ] !== void 0) { const node = dirContentRefs[ slotName[ 0 ] ]; node.style.transform = 'scale(1)'; pan.size[ slotName[ 0 ] ] = node.getBoundingClientRect()[ slotName[ 3 ] ]; } }); pan.axis = (evt.direction === 'up' || evt.direction === 'down') ? 'Y' : 'X'; } else if (evt.isFinal) { node.classList.remove('no-transition'); if (pan.scale === 1) { node.style.transform = `translate${ pan.axis }(${ pan.dir * 100 }%)`; timer !== null && clearTimeout(timer); timer = setTimeout(() => { timer = null; emit(pan.showing, { reset }); emit('action', { side: pan.showing, reset }); }, 230); } else { node.style.transform = 'translate(0,0)'; emitSlide(pan.showing, 0, true); } return } else { evt.direction = pan.axis === 'X' ? evt.offset.x < 0 ? 'left' : 'right' : evt.offset.y < 0 ? 'up' : 'down'; } if ( (slots.left === void 0 && evt.direction === langDir.value.right) || (slots.right === void 0 && evt.direction === langDir.value.left) || (slots.top === void 0 && evt.direction === 'down') || (slots.bottom === void 0 && evt.direction === 'up') ) { node.style.transform = 'translate(0,0)'; return } let showing, dir, dist; if (pan.axis === 'X') { dir = evt.direction === 'left' ? -1 : 1; showing = dir === 1 ? langDir.value.left : langDir.value.right; dist = evt.distance.x; } else { dir = evt.direction === 'up' ? -2 : 2; showing = dir === 2 ? 'top' : 'bottom'; dist = evt.distance.y; } if (pan.dir !== null && Math.abs(dir) !== Math.abs(pan.dir)) { return } if (pan.dir !== dir) { [ 'left', 'right', 'top', 'bottom' ].forEach(d => { if (dirRefs[ d ]) { dirRefs[ d ].style.visibility = showing === d ? 'visible' : 'hidden'; } }); pan.showing = showing; pan.dir = dir; } pan.scale = Math.max(0, Math.min(1, (dist - 40) / pan.size[ showing ])); node.style.transform = `translate${ pan.axis }(${ dist * dir / Math.abs(dir) }px)`; dirContentRefs[ showing ].style.transform = `scale(${ pan.scale })`; emitSlide(showing, pan.scale, false); } vue.onBeforeUpdate(() => { dirRefs = {}; dirContentRefs = {}; }); vue.onBeforeUnmount(() => { timer !== null && clearTimeout(timer); }); // expose public methods Object.assign(proxy, { reset }); return () => { const content = [], slotsList = { left: slots[ langDir.value.right ] !== void 0, right: slots[ langDir.value.left ] !== void 0, up: slots.bottom !== void 0, down: slots.top !== void 0 }, dirs = Object.keys(slotsList).filter(key => slotsList[ key ] === true); slotsDef.forEach(slotName => { const dir = slotName[ 0 ]; if (slots[ dir ] !== void 0) { content.push( vue.h('div', { ref: el => { dirRefs[ dir ] = el; }, class: `q-slide-item__${ dir } absolute-full row no-wrap items-${ slotName[ 1 ] } justify-${ slotName[ 2 ] }` + (props[ dir + 'Color' ] !== void 0 ? ` bg-${ props[ dir + 'Color' ] }` : '') }, [ vue.h('div', { ref: el => { dirContentRefs[ dir ] = el; } }, slots[ dir ]()) ]) ); } }); const node = vue.h('div', { key: `${ dirs.length === 0 ? 'only-' : '' } content`, ref: contentRef, class: 'q-slide-item__content' }, hSlot(slots.default)); if (dirs.length === 0) { content.push(node); } else { content.push( vue.withDirectives(node, getCacheWithFn('dir#' + dirs.join(''), () => { const modifiers = { prevent: true, stop: true, mouse: true }; dirs.forEach(dir => { modifiers[ dir ] = true; }); return [ [ TouchPan, onPan, void 0, modifiers ] ] })) ); } return vue.h('div', { class: classes.value }, content) } } }); const space = vue.h('div', { class: 'q-space' }); var QSpace = createComponent({ name: 'QSpace', setup () { return () => space } }); const svg$l = [ vue.h('g', { transform: 'matrix(1 0 0 -1 0 80)' }, [ vue.h('rect', { width: '10', height: '20', rx: '3' }, [ vue.h('animate', { attributeName: 'height', begin: '0s', dur: '4.3s', values: '20;45;57;80;64;32;66;45;64;23;66;13;64;56;34;34;2;23;76;79;20', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '15', width: '10', height: '80', rx: '3' }, [ vue.h('animate', { attributeName: 'height', begin: '0s', dur: '2s', values: '80;55;33;5;75;23;73;33;12;14;60;80', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '30', width: '10', height: '50', rx: '3' }, [ vue.h('animate', { attributeName: 'height', begin: '0s', dur: '1.4s', values: '50;34;78;23;56;23;34;76;80;54;21;50', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '45', width: '10', height: '30', rx: '3' }, [ vue.h('animate', { attributeName: 'height', begin: '0s', dur: '2s', values: '30;45;13;80;56;72;45;76;34;23;67;30', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerAudio = createComponent({ name: 'QSpinnerAudio', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, fill: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 55 80', xmlns: 'http://www.w3.org/2000/svg' }, svg$l) } }); const svg$k = [ vue.h('g', { transform: 'translate(1 1)', 'stroke-width': '2', fill: 'none', 'fill-rule': 'evenodd' }, [ vue.h('circle', { cx: '5', cy: '50', r: '5' }, [ vue.h('animate', { attributeName: 'cy', begin: '0s', dur: '2.2s', values: '50;5;50;50', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'cx', begin: '0s', dur: '2.2s', values: '5;27;49;5', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '27', cy: '5', r: '5' }, [ vue.h('animate', { attributeName: 'cy', begin: '0s', dur: '2.2s', from: '5', to: '5', values: '5;50;50;5', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'cx', begin: '0s', dur: '2.2s', from: '27', to: '27', values: '27;49;5;27', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '49', cy: '50', r: '5' }, [ vue.h('animate', { attributeName: 'cy', begin: '0s', dur: '2.2s', values: '50;50;5;50', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'cx', from: '49', to: '49', begin: '0s', dur: '2.2s', values: '49;5;27;49', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerBall = createComponent({ name: 'QSpinnerBall', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, stroke: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 57 57', xmlns: 'http://www.w3.org/2000/svg' }, svg$k) } }); const svg$j = [ vue.h('rect', { y: '10', width: '15', height: '120', rx: '6' }, [ vue.h('animate', { attributeName: 'height', begin: '0.5s', dur: '1s', values: '120;110;100;90;80;70;60;50;40;140;120', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'y', begin: '0.5s', dur: '1s', values: '10;15;20;25;30;35;40;45;50;0;10', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '30', y: '10', width: '15', height: '120', rx: '6' }, [ vue.h('animate', { attributeName: 'height', begin: '0.25s', dur: '1s', values: '120;110;100;90;80;70;60;50;40;140;120', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'y', begin: '0.25s', dur: '1s', values: '10;15;20;25;30;35;40;45;50;0;10', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '60', width: '15', height: '140', rx: '6' }, [ vue.h('animate', { attributeName: 'height', begin: '0s', dur: '1s', values: '120;110;100;90;80;70;60;50;40;140;120', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'y', begin: '0s', dur: '1s', values: '10;15;20;25;30;35;40;45;50;0;10', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '90', y: '10', width: '15', height: '120', rx: '6' }, [ vue.h('animate', { attributeName: 'height', begin: '0.25s', dur: '1s', values: '120;110;100;90;80;70;60;50;40;140;120', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'y', begin: '0.25s', dur: '1s', values: '10;15;20;25;30;35;40;45;50;0;10', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('rect', { x: '120', y: '10', width: '15', height: '120', rx: '6' }, [ vue.h('animate', { attributeName: 'height', begin: '0.5s', dur: '1s', values: '120;110;100;90;80;70;60;50;40;140;120', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'y', begin: '0.5s', dur: '1s', values: '10;15;20;25;30;35;40;45;50;0;10', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]; var QSpinnerBars = createComponent({ name: 'QSpinnerBars', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, fill: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 135 140', xmlns: 'http://www.w3.org/2000/svg' }, svg$j) } }); const svg$i = [ vue.h('rect', { x: '25', y: '25', width: '50', height: '50', fill: 'none', 'stroke-width': '4', stroke: 'currentColor' }, [ vue.h('animateTransform', { id: 'spinnerBox', attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '180 50 50', dur: '0.5s', begin: 'rectBox.end' }) ]), vue.h('rect', { x: '27', y: '27', width: '46', height: '50', fill: 'currentColor' }, [ vue.h('animate', { id: 'rectBox', attributeName: 'height', begin: '0s;spinnerBox.end', dur: '1.3s', from: '50', to: '0', fill: 'freeze' }) ]) ]; var QSpinnerBox = createComponent({ name: 'QSpinnerBox', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$i) } }); const svg$h = [ vue.h('circle', { cx: '50', cy: '50', r: '48', fill: 'none', 'stroke-width': '4', 'stroke-miterlimit': '10', stroke: 'currentColor' }), vue.h('line', { 'stroke-linecap': 'round', 'stroke-width': '4', 'stroke-miterlimit': '10', stroke: 'currentColor', x1: '50', y1: '50', x2: '85', y2: '50.5' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '2s', repeatCount: 'indefinite' }) ]), vue.h('line', { 'stroke-linecap': 'round', 'stroke-width': '4', 'stroke-miterlimit': '10', stroke: 'currentColor', x1: '50', y1: '50', x2: '49.5', y2: '74' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '15s', repeatCount: 'indefinite' }) ]) ]; var QSpinnerClock = createComponent({ name: 'QSpinnerClock', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$h) } }); const svg$g = [ vue.h('rect', { x: '0', y: '0', width: ' 100', height: '100', fill: 'none' }), vue.h('path', { d: 'M78,19H22c-6.6,0-12,5.4-12,12v31c0,6.6,5.4,12,12,12h37.2c0.4,3,1.8,5.6,3.7,7.6c2.4,2.5,5.1,4.1,9.1,4 c-1.4-2.1-2-7.2-2-10.3c0-0.4,0-0.8,0-1.3h8c6.6,0,12-5.4,12-12V31C90,24.4,84.6,19,78,19z', fill: 'currentColor' }), vue.h('circle', { cx: '30', cy: '47', r: '5', fill: '#fff' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', values: '0;1;1', keyTimes: '0;0.2;1', dur: '1s', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '50', cy: '47', r: '5', fill: '#fff' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', values: '0;0;1;1', keyTimes: '0;0.2;0.4;1', dur: '1s', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '70', cy: '47', r: '5', fill: '#fff' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', values: '0;0;1;1', keyTimes: '0;0.4;0.6;1', dur: '1s', repeatCount: 'indefinite' }) ]) ]; var QSpinnerComment = createComponent({ name: 'QSpinnerComment', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid' }, svg$g) } }); const svg$f = [ vue.h('rect', { x: '0', y: '0', width: ' 100', height: '100', fill: 'none' }), vue.h('g', { transform: 'translate(25 25)' }, [ vue.h('rect', { x: '-20', y: '-20', width: ' 40', height: '40', fill: 'currentColor', opacity: '0.9' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '1.5', to: '1', repeatCount: 'indefinite', begin: '0s', dur: '1s', calcMode: 'spline', keySplines: '0.2 0.8 0.2 0.8', keyTimes: '0;1' }) ]) ]), vue.h('g', { transform: 'translate(75 25)' }, [ vue.h('rect', { x: '-20', y: '-20', width: ' 40', height: '40', fill: 'currentColor', opacity: '0.8' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '1.5', to: '1', repeatCount: 'indefinite', begin: '0.1s', dur: '1s', calcMode: 'spline', keySplines: '0.2 0.8 0.2 0.8', keyTimes: '0;1' }) ]) ]), vue.h('g', { transform: 'translate(25 75)' }, [ vue.h('rect', { x: '-20', y: '-20', width: ' 40', height: '40', fill: 'currentColor', opacity: '0.7' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '1.5', to: '1', repeatCount: 'indefinite', begin: '0.3s', dur: '1s', calcMode: 'spline', keySplines: '0.2 0.8 0.2 0.8', keyTimes: '0;1' }) ]) ]), vue.h('g', { transform: 'translate(75 75)' }, [ vue.h('rect', { x: '-20', y: '-20', width: ' 40', height: '40', fill: 'currentColor', opacity: '0.6' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '1.5', to: '1', repeatCount: 'indefinite', begin: '0.2s', dur: '1s', calcMode: 'spline', keySplines: '0.2 0.8 0.2 0.8', keyTimes: '0;1' }) ]) ]) ]; var QSpinnerCube = createComponent({ name: 'QSpinnerCube', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, xmlns: 'http://www.w3.org/2000/svg', viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid' }, svg$f) } }); const svg$e = [ vue.h('circle', { cx: '15', cy: '15', r: '15' }, [ vue.h('animate', { attributeName: 'r', from: '15', to: '15', begin: '0s', dur: '0.8s', values: '15;9;15', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'fill-opacity', from: '1', to: '1', begin: '0s', dur: '0.8s', values: '1;.5;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '60', cy: '15', r: '9', 'fill-opacity': '.3' }, [ vue.h('animate', { attributeName: 'r', from: '9', to: '9', begin: '0s', dur: '0.8s', values: '9;15;9', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'fill-opacity', from: '.5', to: '.5', begin: '0s', dur: '0.8s', values: '.5;1;.5', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '105', cy: '15', r: '15' }, [ vue.h('animate', { attributeName: 'r', from: '15', to: '15', begin: '0s', dur: '0.8s', values: '15;9;15', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'fill-opacity', from: '1', to: '1', begin: '0s', dur: '0.8s', values: '1;.5;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]; var QSpinnerDots = createComponent({ name: 'QSpinnerDots', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, fill: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 120 30', xmlns: 'http://www.w3.org/2000/svg' }, svg$e) } }); const svg$d = [ vue.h('g', { transform: 'translate(20 50)' }, [ vue.h('rect', { x: '-10', y: '-30', width: ' 20', height: '60', fill: 'currentColor', opacity: '0.6' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '2', to: '1', begin: '0s', repeatCount: 'indefinite', dur: '1s', calcMode: 'spline', keySplines: '0.1 0.9 0.4 1', keyTimes: '0;1', values: '2;1' }) ]) ]), vue.h('g', { transform: 'translate(50 50)' }, [ vue.h('rect', { x: '-10', y: '-30', width: ' 20', height: '60', fill: 'currentColor', opacity: '0.8' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '2', to: '1', begin: '0.1s', repeatCount: 'indefinite', dur: '1s', calcMode: 'spline', keySplines: '0.1 0.9 0.4 1', keyTimes: '0;1', values: '2;1' }) ]) ]), vue.h('g', { transform: 'translate(80 50)' }, [ vue.h('rect', { x: '-10', y: '-30', width: ' 20', height: '60', fill: 'currentColor', opacity: '0.9' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'scale', from: '2', to: '1', begin: '0.2s', repeatCount: 'indefinite', dur: '1s', calcMode: 'spline', keySplines: '0.1 0.9 0.4 1', keyTimes: '0;1', values: '2;1' }) ]) ]) ]; var QSpinnerFacebook = createComponent({ name: 'QSpinnerFacebook', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', xmlns: 'http://www.w3.org/2000/svg', preserveAspectRatio: 'xMidYMid' }, svg$d) } }); const svg$c = [ vue.h('g', { transform: 'translate(-20,-20)' }, [ vue.h('path', { d: 'M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z', fill: 'currentColor' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '90 50 50', to: '0 50 50', dur: '1s', repeatCount: 'indefinite' }) ]) ]), vue.h('g', { transform: 'translate(20,20) rotate(15 50 50)' }, [ vue.h('path', { d: 'M79.9,52.6C80,51.8,80,50.9,80,50s0-1.8-0.1-2.6l-5.1-0.4c-0.3-2.4-0.9-4.6-1.8-6.7l4.2-2.9c-0.7-1.6-1.6-3.1-2.6-4.5 L70,35c-1.4-1.9-3.1-3.5-4.9-4.9l2.2-4.6c-1.4-1-2.9-1.9-4.5-2.6L59.8,27c-2.1-0.9-4.4-1.5-6.7-1.8l-0.4-5.1C51.8,20,50.9,20,50,20 s-1.8,0-2.6,0.1l-0.4,5.1c-2.4,0.3-4.6,0.9-6.7,1.8l-2.9-4.1c-1.6,0.7-3.1,1.6-4.5,2.6l2.1,4.6c-1.9,1.4-3.5,3.1-5,4.9l-4.5-2.1 c-1,1.4-1.9,2.9-2.6,4.5l4.1,2.9c-0.9,2.1-1.5,4.4-1.8,6.8l-5,0.4C20,48.2,20,49.1,20,50s0,1.8,0.1,2.6l5,0.4 c0.3,2.4,0.9,4.7,1.8,6.8l-4.1,2.9c0.7,1.6,1.6,3.1,2.6,4.5l4.5-2.1c1.4,1.9,3.1,3.5,5,4.9l-2.1,4.6c1.4,1,2.9,1.9,4.5,2.6l2.9-4.1 c2.1,0.9,4.4,1.5,6.7,1.8l0.4,5.1C48.2,80,49.1,80,50,80s1.8,0,2.6-0.1l0.4-5.1c2.3-0.3,4.6-0.9,6.7-1.8l2.9,4.2 c1.6-0.7,3.1-1.6,4.5-2.6L65,69.9c1.9-1.4,3.5-3,4.9-4.9l4.6,2.2c1-1.4,1.9-2.9,2.6-4.5L73,59.8c0.9-2.1,1.5-4.4,1.8-6.7L79.9,52.6 z M50,65c-8.3,0-15-6.7-15-15c0-8.3,6.7-15,15-15s15,6.7,15,15C65,58.3,58.3,65,50,65z', fill: 'currentColor' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '90 50 50', dur: '1s', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerGears = createComponent({ name: 'QSpinnerGears', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$c) } }); const svg$b = [ vue.h('circle', { cx: '12.5', cy: '12.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '0s', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '12.5', cy: '52.5', r: '12.5', 'fill-opacity': '.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '100ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '52.5', cy: '12.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '300ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '52.5', cy: '52.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '600ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '92.5', cy: '12.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '800ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '92.5', cy: '52.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '400ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '12.5', cy: '92.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '700ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '52.5', cy: '92.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '500ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '92.5', cy: '92.5', r: '12.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '200ms', dur: '1s', values: '1;.2;1', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]; var QSpinnerGrid = createComponent({ name: 'QSpinnerGrid', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, fill: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 105 105', xmlns: 'http://www.w3.org/2000/svg' }, svg$b) } }); const svg$a = [ vue.h('path', { d: 'M30.262 57.02L7.195 40.723c-5.84-3.976-7.56-12.06-3.842-18.063 3.715-6 11.467-7.65 17.306-3.68l4.52 3.76 2.6-5.274c3.716-6.002 11.47-7.65 17.304-3.68 5.84 3.97 7.56 12.054 3.842 18.062L34.49 56.118c-.897 1.512-2.793 1.915-4.228.9z', 'fill-opacity': '.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '0s', dur: '1.4s', values: '0.5;1;0.5', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('path', { d: 'M105.512 56.12l-14.44-24.272c-3.716-6.008-1.996-14.093 3.843-18.062 5.835-3.97 13.588-2.322 17.306 3.68l2.6 5.274 4.52-3.76c5.84-3.97 13.593-2.32 17.308 3.68 3.718 6.003 1.998 14.088-3.842 18.064L109.74 57.02c-1.434 1.014-3.33.61-4.228-.9z', 'fill-opacity': '.5' }, [ vue.h('animate', { attributeName: 'fill-opacity', begin: '0.7s', dur: '1.4s', values: '0.5;1;0.5', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('path', { d: 'M67.408 57.834l-23.01-24.98c-5.864-6.15-5.864-16.108 0-22.248 5.86-6.14 15.37-6.14 21.234 0L70 16.168l4.368-5.562c5.863-6.14 15.375-6.14 21.235 0 5.863 6.14 5.863 16.098 0 22.247l-23.007 24.98c-1.43 1.556-3.757 1.556-5.188 0z' }) ]; var QSpinnerHearts = createComponent({ name: 'QSpinnerHearts', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, fill: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 140 64', xmlns: 'http://www.w3.org/2000/svg' }, svg$a) } }); const svg$9 = [ vue.h('g', [ vue.h('path', { fill: 'none', stroke: 'currentColor', 'stroke-width': '5', 'stroke-miterlimit': '10', d: 'M58.4,51.7c-0.9-0.9-1.4-2-1.4-2.3s0.5-0.4,1.4-1.4 C70.8,43.8,79.8,30.5,80,15.5H70H30H20c0.2,15,9.2,28.1,21.6,32.3c0.9,0.9,1.4,1.2,1.4,1.5s-0.5,1.6-1.4,2.5 C29.2,56.1,20.2,69.5,20,85.5h10h40h10C79.8,69.5,70.8,55.9,58.4,51.7z' }), vue.h('clipPath', { id: 'uil-hourglass-clip1' }, [ vue.h('rect', { x: '15', y: '20', width: ' 70', height: '25' }, [ vue.h('animate', { attributeName: 'height', from: '25', to: '0', dur: '1s', repeatCount: 'indefinite', values: '25;0;0', keyTimes: '0;0.5;1' }), vue.h('animate', { attributeName: 'y', from: '20', to: '45', dur: '1s', repeatCount: 'indefinite', values: '20;45;45', keyTimes: '0;0.5;1' }) ]) ]), vue.h('clipPath', { id: 'uil-hourglass-clip2' }, [ vue.h('rect', { x: '15', y: '55', width: ' 70', height: '25' }, [ vue.h('animate', { attributeName: 'height', from: '0', to: '25', dur: '1s', repeatCount: 'indefinite', values: '0;25;25', keyTimes: '0;0.5;1' }), vue.h('animate', { attributeName: 'y', from: '80', to: '55', dur: '1s', repeatCount: 'indefinite', values: '80;55;55', keyTimes: '0;0.5;1' }) ]) ]), vue.h('path', { d: 'M29,23c3.1,11.4,11.3,19.5,21,19.5S67.9,34.4,71,23H29z', 'clip-path': 'url(#uil-hourglass-clip1)', fill: 'currentColor' }), vue.h('path', { d: 'M71.6,78c-3-11.6-11.5-20-21.5-20s-18.5,8.4-21.5,20H71.6z', 'clip-path': 'url(#uil-hourglass-clip2)', fill: 'currentColor' }), vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '180 50 50', repeatCount: 'indefinite', dur: '1s', values: '0 50 50;0 50 50;180 50 50', keyTimes: '0;0.7;1' }) ]) ]; var QSpinnerHourglass = createComponent({ name: 'QSpinnerHourglass', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$9) } }); const svg$8 = [ vue.h('path', { d: 'M24.3,30C11.4,30,5,43.3,5,50s6.4,20,19.3,20c19.3,0,32.1-40,51.4-40C88.6,30,95,43.3,95,50s-6.4,20-19.3,20C56.4,70,43.6,30,24.3,30z', fill: 'none', stroke: 'currentColor', 'stroke-width': '8', 'stroke-dasharray': '10.691205342610678 10.691205342610678', 'stroke-dashoffset': '0' }, [ vue.h('animate', { attributeName: 'stroke-dashoffset', from: '0', to: '21.382410685221355', begin: '0', dur: '2s', repeatCount: 'indefinite', fill: 'freeze' }) ]) ]; var QSpinnerInfinity = createComponent({ name: 'QSpinnerInfinity', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid' }, svg$8) } }); const svg$7 = [ vue.h('g', { 'stroke-width': '4', 'stroke-linecap': 'round' }, [ vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(180)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '1;.85;.7;.65;.55;.45;.35;.25;.15;.1;0;1', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(210)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '0;1;.85;.7;.65;.55;.45;.35;.25;.15;.1;0', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(240)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.1;0;1;.85;.7;.65;.55;.45;.35;.25;.15;.1', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(270)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.15;.1;0;1;.85;.7;.65;.55;.45;.35;.25;.15', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(300)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.25;.15;.1;0;1;.85;.7;.65;.55;.45;.35;.25', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(330)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.35;.25;.15;.1;0;1;.85;.7;.65;.55;.45;.35', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(0)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.45;.35;.25;.15;.1;0;1;.85;.7;.65;.55;.45', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(30)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.55;.45;.35;.25;.15;.1;0;1;.85;.7;.65;.55', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(60)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.65;.55;.45;.35;.25;.15;.1;0;1;.85;.7;.65', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(90)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.7;.65;.55;.45;.35;.25;.15;.1;0;1;.85;.7', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(120)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '.85;.7;.65;.55;.45;.35;.25;.15;.1;0;1;.85', repeatCount: 'indefinite' }) ]), vue.h('line', { y1: '17', y2: '29', transform: 'translate(32,32) rotate(150)' }, [ vue.h('animate', { attributeName: 'stroke-opacity', dur: '750ms', values: '1;.85;.7;.65;.55;.45;.35;.25;.15;.1;0;1', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerIos = createComponent({ name: 'QSpinnerIos', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, stroke: 'currentColor', fill: 'currentColor', viewBox: '0 0 64 64' }, svg$7) } }); const svg$6 = [ vue.h('circle', { cx: '50', cy: '50', r: '44', fill: 'none', 'stroke-width': '4', 'stroke-opacity': '.5', stroke: 'currentColor' }), vue.h('circle', { cx: '8', cy: '54', r: '6', fill: 'currentColor', 'stroke-width': '3', stroke: 'currentColor' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 48', to: '360 50 52', dur: '2s', repeatCount: 'indefinite' }) ]) ]; var QSpinnerOrbit = createComponent({ name: 'QSpinnerOrbit', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$6) } }); const svg$5 = [ vue.h('g', { transform: 'translate(1 1)', 'stroke-width': '2', fill: 'none', 'fill-rule': 'evenodd' }, [ vue.h('circle', { 'stroke-opacity': '.5', cx: '18', cy: '18', r: '18' }), vue.h('path', { d: 'M36 18c0-9.94-8.06-18-18-18' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 18 18', to: '360 18 18', dur: '1s', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerOval = createComponent({ name: 'QSpinnerOval', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, stroke: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 38 38', xmlns: 'http://www.w3.org/2000/svg' }, svg$5) } }); const svg$4 = [ vue.h('path', { d: 'M0 50A50 50 0 0 1 50 0L50 50L0 50', fill: 'currentColor', opacity: '0.5' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '0.8s', repeatCount: 'indefinite' }) ]), vue.h('path', { d: 'M50 0A50 50 0 0 1 100 50L50 50L50 0', fill: 'currentColor', opacity: '0.5' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '1.6s', repeatCount: 'indefinite' }) ]), vue.h('path', { d: 'M100 50A50 50 0 0 1 50 100L50 50L100 50', fill: 'currentColor', opacity: '0.5' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '2.4s', repeatCount: 'indefinite' }) ]), vue.h('path', { d: 'M50 100A50 50 0 0 1 0 50L50 50L50 100', fill: 'currentColor', opacity: '0.5' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 50 50', to: '360 50 50', dur: '3.2s', repeatCount: 'indefinite' }) ]) ]; var QSpinnerPie = createComponent({ name: 'QSpinnerPie', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$4) } }); const svg$3 = [ vue.h('g', { fill: 'none', 'fill-rule': 'evenodd', 'stroke-width': '2' }, [ vue.h('circle', { cx: '22', cy: '22', r: '1' }, [ vue.h('animate', { attributeName: 'r', begin: '0s', dur: '1.8s', values: '1; 20', calcMode: 'spline', keyTimes: '0; 1', keySplines: '0.165, 0.84, 0.44, 1', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-opacity', begin: '0s', dur: '1.8s', values: '1; 0', calcMode: 'spline', keyTimes: '0; 1', keySplines: '0.3, 0.61, 0.355, 1', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '22', cy: '22', r: '1' }, [ vue.h('animate', { attributeName: 'r', begin: '-0.9s', dur: '1.8s', values: '1; 20', calcMode: 'spline', keyTimes: '0; 1', keySplines: '0.165, 0.84, 0.44, 1', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-opacity', begin: '-0.9s', dur: '1.8s', values: '1; 0', calcMode: 'spline', keyTimes: '0; 1', keySplines: '0.3, 0.61, 0.355, 1', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerPuff = createComponent({ name: 'QSpinnerPuff', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, stroke: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 44 44', xmlns: 'http://www.w3.org/2000/svg' }, svg$3) } }); const svg$2 = [ vue.h('g', { transform: 'scale(0.55)' }, [ vue.h('circle', { cx: '30', cy: '150', r: '30', fill: 'currentColor' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', dur: '1s', begin: '0', repeatCount: 'indefinite', keyTimes: '0;0.5;1', values: '0;1;1' }) ]), vue.h('path', { d: 'M90,150h30c0-49.7-40.3-90-90-90v30C63.1,90,90,116.9,90,150z', fill: 'currentColor' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', dur: '1s', begin: '0.1', repeatCount: 'indefinite', keyTimes: '0;0.5;1', values: '0;1;1' }) ]), vue.h('path', { d: 'M150,150h30C180,67.2,112.8,0,30,0v30C96.3,30,150,83.7,150,150z', fill: 'currentColor' }, [ vue.h('animate', { attributeName: 'opacity', from: '0', to: '1', dur: '1s', begin: '0.2', repeatCount: 'indefinite', keyTimes: '0;0.5;1', values: '0;1;1' }) ]) ]) ]; var QSpinnerRadio = createComponent({ name: 'QSpinnerRadio', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 100 100', preserveAspectRatio: 'xMidYMid', xmlns: 'http://www.w3.org/2000/svg' }, svg$2) } }); const svg$1 = [ vue.h('g', { fill: 'none', 'fill-rule': 'evenodd', transform: 'translate(1 1)', 'stroke-width': '2' }, [ vue.h('circle', { cx: '22', cy: '22', r: '6' }, [ vue.h('animate', { attributeName: 'r', begin: '1.5s', dur: '3s', values: '6;22', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-opacity', begin: '1.5s', dur: '3s', values: '1;0', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-width', begin: '1.5s', dur: '3s', values: '2;0', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '22', cy: '22', r: '6' }, [ vue.h('animate', { attributeName: 'r', begin: '3s', dur: '3s', values: '6;22', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-opacity', begin: '3s', dur: '3s', values: '1;0', calcMode: 'linear', repeatCount: 'indefinite' }), vue.h('animate', { attributeName: 'stroke-width', begin: '3s', dur: '3s', values: '2;0', calcMode: 'linear', repeatCount: 'indefinite' }) ]), vue.h('circle', { cx: '22', cy: '22', r: '8' }, [ vue.h('animate', { attributeName: 'r', begin: '0s', dur: '1.5s', values: '6;1;2;3;4;5;6', calcMode: 'linear', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerRings = createComponent({ name: 'QSpinnerRings', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, stroke: 'currentColor', width: cSize.value, height: cSize.value, viewBox: '0 0 45 45', xmlns: 'http://www.w3.org/2000/svg' }, svg$1) } }); const svg = [ vue.h('defs', [ vue.h('linearGradient', { x1: '8.042%', y1: '0%', x2: '65.682%', y2: '23.865%', id: 'a' }, [ vue.h('stop', { 'stop-color': 'currentColor', 'stop-opacity': '0', offset: '0%' }), vue.h('stop', { 'stop-color': 'currentColor', 'stop-opacity': '.631', offset: '63.146%' }), vue.h('stop', { 'stop-color': 'currentColor', offset: '100%' }) ]) ]), vue.h('g', { transform: 'translate(1 1)', fill: 'none', 'fill-rule': 'evenodd' }, [ vue.h('path', { d: 'M36 18c0-9.94-8.06-18-18-18', stroke: 'url(#a)', 'stroke-width': '2' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 18 18', to: '360 18 18', dur: '0.9s', repeatCount: 'indefinite' }) ]), vue.h('circle', { fill: 'currentColor', cx: '36', cy: '18', r: '1' }, [ vue.h('animateTransform', { attributeName: 'transform', type: 'rotate', from: '0 18 18', to: '360 18 18', dur: '0.9s', repeatCount: 'indefinite' }) ]) ]) ]; var QSpinnerTail = createComponent({ name: 'QSpinnerTail', props: useSpinnerProps, setup (props) { const { cSize, classes } = useSpinner(props); return () => vue.h('svg', { class: classes.value, width: cSize.value, height: cSize.value, viewBox: '0 0 38 38', xmlns: 'http://www.w3.org/2000/svg' }, svg) } }); var QSplitter = createComponent({ name: 'QSplitter', props: { ...useDarkProps, modelValue: { type: Number, required: true }, reverse: Boolean, unit: { type: String, default: '%', validator: v => [ '%', 'px' ].includes(v) }, limits: { type: Array, validator: v => { if (v.length !== 2) return false if (typeof v[ 0 ] !== 'number' || typeof v[ 1 ] !== 'number') return false return v[ 0 ] >= 0 && v[ 0 ] <= v[ 1 ] } }, emitImmediately: Boolean, horizontal: Boolean, disable: Boolean, beforeClass: [ Array, String, Object ], afterClass: [ Array, String, Object ], separatorClass: [ Array, String, Object ], separatorStyle: [ Array, String, Object ] }, emits: [ 'update:modelValue' ], setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const isDark = useDark(props, $q); const rootRef = vue.ref(null); const sideRefs = { before: vue.ref(null), after: vue.ref(null) }; const classes = vue.computed(() => 'q-splitter no-wrap ' + `${ props.horizontal === true ? 'q-splitter--horizontal column' : 'q-splitter--vertical row' }` + ` q-splitter--${ props.disable === true ? 'disabled' : 'workable' }` + (isDark.value === true ? ' q-splitter--dark' : '') ); const propName = vue.computed(() => (props.horizontal === true ? 'height' : 'width')); const side = vue.computed(() => (props.reverse !== true ? 'before' : 'after')); const computedLimits = vue.computed(() => ( props.limits !== void 0 ? props.limits : (props.unit === '%' ? [ 10, 90 ] : [ 50, Infinity ]) )); function getCSSValue (value) { return (props.unit === '%' ? value : Math.round(value)) + props.unit } const styles = vue.computed(() => ({ [ side.value ]: { [ propName.value ]: getCSSValue(props.modelValue) } })); let __dir, __maxValue, __value, __multiplier, __normalized; function pan (evt) { if (evt.isFirst === true) { const size = rootRef.value.getBoundingClientRect()[ propName.value ]; __dir = props.horizontal === true ? 'up' : 'left'; __maxValue = props.unit === '%' ? 100 : size; __value = Math.min(__maxValue, computedLimits.value[ 1 ], Math.max(computedLimits.value[ 0 ], props.modelValue)); __multiplier = (props.reverse !== true ? 1 : -1) * (props.horizontal === true ? 1 : ($q.lang.rtl === true ? -1 : 1)) * (props.unit === '%' ? (size === 0 ? 0 : 100 / size) : 1); rootRef.value.classList.add('q-splitter--active'); return } if (evt.isFinal === true) { if (__normalized !== props.modelValue) { emit('update:modelValue', __normalized); } rootRef.value.classList.remove('q-splitter--active'); return } const val = __value + __multiplier * (evt.direction === __dir ? -1 : 1) * evt.distance[ props.horizontal === true ? 'y' : 'x' ]; __normalized = Math.min(__maxValue, computedLimits.value[ 1 ], Math.max(computedLimits.value[ 0 ], val)); sideRefs[ side.value ].value.style[ propName.value ] = getCSSValue(__normalized); if (props.emitImmediately === true && props.modelValue !== __normalized) { emit('update:modelValue', __normalized); } } const sepDirective = vue.computed(() => { // if props.disable !== true return [ [ TouchPan, pan, void 0, { [ props.horizontal === true ? 'vertical' : 'horizontal' ]: true, prevent: true, stop: true, mouse: true, mouseAllDir: true } ] ] }); function normalize (val, limits) { if (val < limits[ 0 ]) { emit('update:modelValue', limits[ 0 ]); } else if (val > limits[ 1 ]) { emit('update:modelValue', limits[ 1 ]); } } vue.watch(() => props.modelValue, v => { normalize(v, computedLimits.value); }); vue.watch(() => props.limits, () => { vue.nextTick(() => { normalize(props.modelValue, computedLimits.value); }); }); return () => { const child = [ vue.h('div', { ref: sideRefs.before, class: [ 'q-splitter__panel q-splitter__before' + (props.reverse === true ? ' col' : ''), props.beforeClass ], style: styles.value.before }, hSlot(slots.before)), vue.h('div', { class: [ 'q-splitter__separator', props.separatorClass ], style: props.separatorStyle, 'aria-disabled': props.disable === true ? 'true' : void 0 }, [ hDir( 'div', { class: 'q-splitter__separator-area absolute-full' }, hSlot(slots.separator), 'sep', props.disable !== true, () => sepDirective.value ) ]), vue.h('div', { ref: sideRefs.after, class: [ 'q-splitter__panel q-splitter__after' + (props.reverse === true ? '' : ' col'), props.afterClass ], style: styles.value.after }, hSlot(slots.after)) ]; return vue.h('div', { class: classes.value, ref: rootRef }, hMergeSlot(slots.default, child)) } } }); var StepHeader = createComponent({ name: 'StepHeader', props: { stepper: {}, step: {}, goToPanel: Function }, setup (props, { attrs }) { const { proxy: { $q } } = vue.getCurrentInstance(); const blurRef = vue.ref(null); const isActive = vue.computed(() => props.stepper.modelValue === props.step.name); const isDisable = vue.computed(() => { const opt = props.step.disable; return opt === true || opt === '' }); const isError = vue.computed(() => { const opt = props.step.error; return opt === true || opt === '' }); const isDone = vue.computed(() => { const opt = props.step.done; return isDisable.value === false && (opt === true || opt === '') }); const headerNav = vue.computed(() => { const opt = props.step.headerNav, nav = opt === true || opt === '' || opt === void 0; return isDisable.value === false && props.stepper.headerNav && nav }); const hasPrefix = vue.computed(() => { return props.step.prefix && (isActive.value === false || props.stepper.activeIcon === 'none') && (isError.value === false || props.stepper.errorIcon === 'none') && (isDone.value === false || props.stepper.doneIcon === 'none') }); const icon = vue.computed(() => { const defaultIcon = props.step.icon || props.stepper.inactiveIcon; if (isActive.value === true) { const icon = props.step.activeIcon || props.stepper.activeIcon; return icon === 'none' ? defaultIcon : icon || $q.iconSet.stepper.active } if (isError.value === true) { const icon = props.step.errorIcon || props.stepper.errorIcon; return icon === 'none' ? defaultIcon : icon || $q.iconSet.stepper.error } if (isDisable.value === false && isDone.value === true) { const icon = props.step.doneIcon || props.stepper.doneIcon; return icon === 'none' ? defaultIcon : icon || $q.iconSet.stepper.done } return defaultIcon }); const color = vue.computed(() => { const errorColor = isError.value === true ? props.step.errorColor || props.stepper.errorColor : void 0; if (isActive.value === true) { const color = props.step.activeColor || props.stepper.activeColor || props.step.color; return color !== void 0 ? color : errorColor } if (errorColor !== void 0) { return errorColor } if (isDisable.value === false && isDone.value === true) { return props.step.doneColor || props.stepper.doneColor || props.step.color || props.stepper.inactiveColor } return props.step.color || props.stepper.inactiveColor }); const classes = vue.computed(() => { return 'q-stepper__tab col-grow flex items-center no-wrap relative-position' + (color.value !== void 0 ? ` text-${ color.value }` : '') + (isError.value === true ? ' q-stepper__tab--error q-stepper__tab--error-with-' + (hasPrefix.value === true ? 'prefix' : 'icon') : '') + (isActive.value === true ? ' q-stepper__tab--active' : '') + (isDone.value === true ? ' q-stepper__tab--done' : '') + (headerNav.value === true ? ' q-stepper__tab--navigation q-focusable q-hoverable' : '') + (isDisable.value === true ? ' q-stepper__tab--disabled' : '') }); const ripple = vue.computed(() => ( props.stepper.headerNav !== true ? false : headerNav.value )); function onActivate () { blurRef.value !== null && blurRef.value.focus(); isActive.value === false && props.goToPanel(props.step.name); } function onKeyup (e) { if (e.keyCode === 13 && isActive.value === false) { props.goToPanel(props.step.name); } } return () => { const data = { class: classes.value }; if (headerNav.value === true) { data.onClick = onActivate; data.onKeyup = onKeyup; Object.assign(data, isDisable.value === true ? { tabindex: -1, 'aria-disabled': 'true' } : { tabindex: attrs.tabindex || 0 } ); } const child = [ vue.h('div', { class: 'q-focus-helper', tabindex: -1, ref: blurRef }), vue.h('div', { class: 'q-stepper__dot row flex-center q-stepper__line relative-position' }, [ vue.h('span', { class: 'row flex-center' }, [ hasPrefix.value === true ? props.step.prefix : vue.h(QIcon, { name: icon.value }) ]) ]) ]; if (props.step.title !== void 0 && props.step.title !== null) { const content = [ vue.h('div', { class: 'q-stepper__title' }, props.step.title) ]; if (props.step.caption !== void 0 && props.step.caption !== null) { content.push( vue.h('div', { class: 'q-stepper__caption' }, props.step.caption) ); } child.push( vue.h('div', { class: 'q-stepper__label q-stepper__line relative-position' }, content) ); } return vue.withDirectives( vue.h('div', data, child), [ [ Ripple, ripple.value ] ] ) } } }); function getStepWrapper (slots) { return vue.h('div', { class: 'q-stepper__step-content' }, [ vue.h('div', { class: 'q-stepper__step-inner' }, hSlot(slots.default)) ]) } const PanelWrapper = { setup (_, { slots }) { return () => getStepWrapper(slots) } }; var QStep = createComponent({ name: 'QStep', props: { ...usePanelChildProps, icon: String, color: String, title: { type: String, required: true }, caption: String, prefix: [ String, Number ], doneIcon: String, doneColor: String, activeIcon: String, activeColor: String, errorIcon: String, errorColor: String, headerNav: { type: Boolean, default: true }, done: Boolean, error: Boolean, onScroll: [ Function, Array ] }, setup (props, { slots, emit }) { const { proxy: { $q } } = vue.getCurrentInstance(); const $stepper = vue.inject(stepperKey, emptyRenderFn); if ($stepper === emptyRenderFn) { console.error('QStep needs to be a child of QStepper'); return emptyRenderFn } const { getCacheWithFn } = useCache(); const rootRef = vue.ref(null); const isActive = vue.computed(() => $stepper.value.modelValue === props.name); const scrollEvent = vue.computed(() => ( ($q.platform.is.ios !== true && $q.platform.is.chrome === true) || isActive.value !== true || $stepper.value.vertical !== true ? {} : { onScroll (e) { const { target } = e; if (target.scrollTop > 0) { target.scrollTop = 0; } props.onScroll !== void 0 && emit('scroll', e); } } )); const contentKey = vue.computed(() => ( typeof props.name === 'string' || typeof props.name === 'number' ? props.name : String(props.name) )); function getStepContent () { const vertical = $stepper.value.vertical; if (vertical === true && $stepper.value.keepAlive === true) { return vue.h( vue.KeepAlive, $stepper.value.keepAliveProps.value, isActive.value === true ? [ vue.h( $stepper.value.needsUniqueKeepAliveWrapper.value === true ? getCacheWithFn(contentKey.value, () => ({ ...PanelWrapper, name: contentKey.value })) : PanelWrapper, { key: contentKey.value }, slots.default ) ] : void 0 ) } return vertical !== true || isActive.value === true ? getStepWrapper(slots) : void 0 } return () => vue.h( 'div', { ref: rootRef, class: 'q-stepper__step', role: 'tabpanel', ...scrollEvent.value }, $stepper.value.vertical === true ? [ vue.h(StepHeader, { stepper: $stepper.value, step: props, goToPanel: $stepper.value.goToPanel }), $stepper.value.animated === true ? vue.h(QSlideTransition, getStepContent) : getStepContent() ] : [ getStepContent() ] ) } }); const camelRE = /(-\w)/g; function camelizeProps (props) { const acc = {}; for (const key in props) { const newKey = key.replace(camelRE, m => m[ 1 ].toUpperCase()); acc[ newKey ] = props[ key ]; } return acc } var QStepper = createComponent({ name: 'QStepper', props: { ...useDarkProps, ...usePanelProps, flat: Boolean, bordered: Boolean, alternativeLabels: Boolean, headerNav: Boolean, contracted: Boolean, headerClass: String, inactiveColor: String, inactiveIcon: String, doneIcon: String, doneColor: String, activeIcon: String, activeColor: String, errorIcon: String, errorColor: String }, emits: usePanelEmits, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); const { updatePanelsList, isValidPanelName, updatePanelIndex, getPanelContent, getPanels, panelDirectives, goToPanel, keepAliveProps, needsUniqueKeepAliveWrapper } = usePanel(); vue.provide(stepperKey, vue.computed(() => ({ goToPanel, keepAliveProps, needsUniqueKeepAliveWrapper, ...props }))); const classes = vue.computed(() => `q-stepper q-stepper--${ props.vertical === true ? 'vertical' : 'horizontal' }` + (props.flat === true ? ' q-stepper--flat' : '') + (props.bordered === true ? ' q-stepper--bordered' : '') + (isDark.value === true ? ' q-stepper--dark q-dark' : '') ); const headerClasses = vue.computed(() => 'q-stepper__header row items-stretch justify-between' + ` q-stepper__header--${ props.alternativeLabels === true ? 'alternative' : 'standard' }-labels` + (props.flat === false || props.bordered === true ? ' q-stepper__header--border' : '') + (props.contracted === true ? ' q-stepper__header--contracted' : '') + (props.headerClass !== void 0 ? ` ${ props.headerClass }` : '') ); function getContent () { const top = hSlot(slots.message, []); if (props.vertical === true) { isValidPanelName(props.modelValue) && updatePanelIndex(); const content = vue.h('div', { class: 'q-stepper__content' }, hSlot(slots.default)); return top === void 0 ? [ content ] : top.concat(content) } return [ vue.h( 'div', { class: headerClasses.value }, getPanels().map(panel => { const step = camelizeProps(panel.props); return vue.h(StepHeader, { key: step.name, stepper: props, step, goToPanel }) }) ), top, hDir( 'div', { class: 'q-stepper__content q-panel-parent' }, getPanelContent(), 'cont', props.swipeable, () => panelDirectives.value ) ] } return () => { updatePanelsList(slots); return vue.h('div', { class: classes.value }, hMergeSlot(slots.navigation, getContent())) } } }); var QStepperNavigation = createComponent({ name: 'QStepperNavigation', setup (_, { slots }) { return () => vue.h('div', { class: 'q-stepper__nav' }, hSlot(slots.default)) } }); var QTh = createComponent({ name: 'QTh', props: { props: Object, autoWidth: Boolean }, emits: [ 'click' ], setup (props, { slots, emit }) { const vm = vue.getCurrentInstance(); const { proxy: { $q } } = vm; const onClick = evt => { emit('click', evt); }; return () => { if (props.props === void 0) { return vue.h('th', { class: props.autoWidth === true ? 'q-table--col-auto-width' : '', onClick }, hSlot(slots.default)) } let col, child; const name = vm.vnode.key; if (name) { col = props.props.colsMap[ name ]; if (col === void 0) { return } } else { col = props.props.col; } if (col.sortable === true) { const action = col.align === 'right' ? 'unshift' : 'push'; child = hUniqueSlot(slots.default, []); child[ action ]( vue.h(QIcon, { class: col.__iconClass, name: $q.iconSet.table.arrowUp }) ); } else { child = hSlot(slots.default); } const data = { class: col.__thClass + (props.autoWidth === true ? ' q-table--col-auto-width' : ''), style: col.headerStyle, onClick: evt => { col.sortable === true && props.props.sort(col); onClick(evt); } }; return vue.h('th', data, child) } } }); function getTableMiddle (props, content) { return vue.h('div', props, [ vue.h('table', { class: 'q-table' }, content) ]) } const comps = { list: QList, table: QMarkupTable }; const typeOptions = [ 'list', 'table', '__qtable' ]; var QVirtualScroll = createComponent({ name: 'QVirtualScroll', props: { ...useVirtualScrollProps, type: { type: String, default: 'list', validator: v => typeOptions.includes(v) }, items: { type: Array, default: () => [] }, itemsFn: Function, itemsSize: Number, scrollTarget: { default: void 0 } }, setup (props, { slots, attrs }) { let localScrollTarget; const rootRef = vue.ref(null); const virtualScrollLength = vue.computed(() => ( props.itemsSize >= 0 && props.itemsFn !== void 0 ? parseInt(props.itemsSize, 10) : (Array.isArray(props.items) ? props.items.length : 0) )); const { virtualScrollSliceRange, localResetVirtualScroll, padVirtualScroll, onVirtualScrollEvt } = useVirtualScroll({ virtualScrollLength, getVirtualScrollTarget, getVirtualScrollEl }); const virtualScrollScope = vue.computed(() => { if (virtualScrollLength.value === 0) { return [] } const mapFn = (item, i) => ({ index: virtualScrollSliceRange.value.from + i, item }); return props.itemsFn === void 0 ? props.items.slice(virtualScrollSliceRange.value.from, virtualScrollSliceRange.value.to).map(mapFn) : props.itemsFn(virtualScrollSliceRange.value.from, virtualScrollSliceRange.value.to - virtualScrollSliceRange.value.from).map(mapFn) }); const classes = vue.computed(() => 'q-virtual-scroll q-virtual-scroll' + (props.virtualScrollHorizontal === true ? '--horizontal' : '--vertical') + (props.scrollTarget !== void 0 ? '' : ' scroll') ); const attributes = vue.computed(() => ( props.scrollTarget !== void 0 ? {} : { tabindex: 0 } )); vue.watch(virtualScrollLength, () => { localResetVirtualScroll(); }); vue.watch(() => props.scrollTarget, () => { unconfigureScrollTarget(); configureScrollTarget(); }); function getVirtualScrollEl () { return rootRef.value.$el || rootRef.value } function getVirtualScrollTarget () { return localScrollTarget } function configureScrollTarget () { localScrollTarget = getScrollTarget(getVirtualScrollEl(), props.scrollTarget); localScrollTarget.addEventListener('scroll', onVirtualScrollEvt, listenOpts.passive); } function unconfigureScrollTarget () { if (localScrollTarget !== void 0) { localScrollTarget.removeEventListener('scroll', onVirtualScrollEvt, listenOpts.passive); localScrollTarget = void 0; } } function __getVirtualChildren () { let child = padVirtualScroll( props.type === 'list' ? 'div' : 'tbody', virtualScrollScope.value.map(slots.default) ); if (slots.before !== void 0) { child = slots.before().concat(child); } return hMergeSlot(slots.after, child) } vue.onBeforeMount(() => { localResetVirtualScroll(); }); vue.onMounted(() => { configureScrollTarget(); }); vue.onActivated(() => { configureScrollTarget(); }); vue.onDeactivated(() => { unconfigureScrollTarget(); }); vue.onBeforeUnmount(() => { unconfigureScrollTarget(); }); return () => { if (slots.default === void 0) { console.error('QVirtualScroll: default scoped slot is required for rendering'); return } return props.type === '__qtable' ? getTableMiddle( { ref: rootRef, class: 'q-table__middle ' + classes.value }, __getVirtualChildren() ) : vue.h(comps[ props.type ], { ...attrs, ref: rootRef, class: [ attrs.class, classes.value ], ...attributes.value }, __getVirtualChildren) } } }); function sortDate (a, b) { return (new Date(a)) - (new Date(b)) } const useTableSortProps = { sortMethod: Function, binaryStateSort: Boolean, columnSortOrder: { type: String, validator: v => v === 'ad' || v === 'da', default: 'ad' } }; function useTableSort (props, computedPagination, colList, setPagination) { const columnToSort = vue.computed(() => { const { sortBy } = computedPagination.value; return sortBy ? colList.value.find(def => def.name === sortBy) || null : null }); const computedSortMethod = vue.computed(() => ( props.sortMethod !== void 0 ? props.sortMethod : (data, sortBy, descending) => { const col = colList.value.find(def => def.name === sortBy); if (col === void 0 || col.field === void 0) { return data } const dir = descending === true ? -1 : 1, val = typeof col.field === 'function' ? v => col.field(v) : v => v[ col.field ]; return data.sort((a, b) => { let A = val(a), B = val(b); if (A === null || A === void 0) { return -1 * dir } if (B === null || B === void 0) { return 1 * dir } if (col.sort !== void 0) { return col.sort(A, B, a, b) * dir } if (isNumber(A) === true && isNumber(B) === true) { return (A - B) * dir } if (isDate(A) === true && isDate(B) === true) { return sortDate(A, B) * dir } if (typeof A === 'boolean' && typeof B === 'boolean') { return (A - B) * dir } [ A, B ] = [ A, B ].map(s => (s + '').toLocaleString().toLowerCase()); return A < B ? -1 * dir : (A === B ? 0 : dir) }) } )); function sort (col /* String(col name) or Object(col definition) */) { let sortOrder = props.columnSortOrder; if (isObject(col) === true) { if (col.sortOrder) { sortOrder = col.sortOrder; } col = col.name; } else { const def = colList.value.find(def => def.name === col); if (def !== void 0 && def.sortOrder) { sortOrder = def.sortOrder; } } let { sortBy, descending } = computedPagination.value; if (sortBy !== col) { sortBy = col; descending = sortOrder === 'da'; } else if (props.binaryStateSort === true) { descending = !descending; } else if (descending === true) { if (sortOrder === 'ad') { sortBy = null; } else { descending = false; } } else { // ascending if (sortOrder === 'ad') { descending = true; } else { sortBy = null; } } setPagination({ sortBy, descending, page: 1 }); } return { columnToSort, computedSortMethod, sort } } const useTableFilterProps = { filter: [ String, Object ], filterMethod: Function }; function useTableFilter (props, setPagination) { const computedFilterMethod = vue.computed(() => ( props.filterMethod !== void 0 ? props.filterMethod : (rows, terms, cols, cellValue) => { const lowerTerms = terms ? terms.toLowerCase() : ''; return rows.filter( row => cols.some(col => { const val = cellValue(col, row) + ''; const haystack = (val === 'undefined' || val === 'null') ? '' : val.toLowerCase(); return haystack.indexOf(lowerTerms) !== -1 }) ) } )); vue.watch( () => props.filter, () => { vue.nextTick(() => { setPagination({ page: 1 }, true); }); }, { deep: true } ); return { computedFilterMethod } } function samePagination (oldPag, newPag) { for (const prop in newPag) { if (newPag[ prop ] !== oldPag[ prop ]) { return false } } return true } function fixPagination (p) { if (p.page < 1) { p.page = 1; } if (p.rowsPerPage !== void 0 && p.rowsPerPage < 1) { p.rowsPerPage = 0; } return p } const useTablePaginationProps = { pagination: Object, rowsPerPageOptions: { type: Array, default: () => [ 5, 7, 10, 15, 20, 25, 50, 0 ] }, 'onUpdate:pagination': [ Function, Array ] }; function useTablePaginationState (vm, getCellValue) { const { props, emit } = vm; const innerPagination = vue.ref( Object.assign({ sortBy: null, descending: false, page: 1, rowsPerPage: props.rowsPerPageOptions.length !== 0 ? props.rowsPerPageOptions[ 0 ] : 5 }, props.pagination) ); const computedPagination = vue.computed(() => { const pag = props[ 'onUpdate:pagination' ] !== void 0 ? { ...innerPagination.value, ...props.pagination } : innerPagination.value; return fixPagination(pag) }); const isServerSide = vue.computed(() => computedPagination.value.rowsNumber !== void 0); function sendServerRequest (pagination) { requestServerInteraction({ pagination, filter: props.filter }); } function requestServerInteraction (prop = {}) { vue.nextTick(() => { emit('request', { pagination: prop.pagination || computedPagination.value, filter: prop.filter || props.filter, getCellValue }); }); } function setPagination (val, forceServerRequest) { const newPagination = fixPagination({ ...computedPagination.value, ...val }); if (samePagination(computedPagination.value, newPagination) === true) { if (isServerSide.value === true && forceServerRequest === true) { sendServerRequest(newPagination); } return } if (isServerSide.value === true) { sendServerRequest(newPagination); return } if ( props.pagination !== void 0 && props[ 'onUpdate:pagination' ] !== void 0 ) { emit('update:pagination', newPagination); } else { innerPagination.value = newPagination; } } return { innerPagination, computedPagination, isServerSide, requestServerInteraction, setPagination } } function useTablePagination (vm, innerPagination, computedPagination, isServerSide, setPagination, filteredSortedRowsNumber) { const { props, emit, proxy: { $q } } = vm; const computedRowsNumber = vue.computed(() => ( isServerSide.value === true ? computedPagination.value.rowsNumber || 0 : filteredSortedRowsNumber.value )); const firstRowIndex = vue.computed(() => { const { page, rowsPerPage } = computedPagination.value; return (page - 1) * rowsPerPage }); const lastRowIndex = vue.computed(() => { const { page, rowsPerPage } = computedPagination.value; return page * rowsPerPage }); const isFirstPage = vue.computed(() => computedPagination.value.page === 1); const pagesNumber = vue.computed(() => ( computedPagination.value.rowsPerPage === 0 ? 1 : Math.max( 1, Math.ceil(computedRowsNumber.value / computedPagination.value.rowsPerPage) ) )); const isLastPage = vue.computed(() => ( lastRowIndex.value === 0 ? true : computedPagination.value.page >= pagesNumber.value )); const computedRowsPerPageOptions = vue.computed(() => { const opts = props.rowsPerPageOptions.includes(innerPagination.value.rowsPerPage) ? props.rowsPerPageOptions : [ innerPagination.value.rowsPerPage ].concat(props.rowsPerPageOptions); return opts.map(count => ({ label: count === 0 ? $q.lang.table.allRows : '' + count, value: count })) }); vue.watch(pagesNumber, (lastPage, oldLastPage) => { if (lastPage === oldLastPage) { return } const currentPage = computedPagination.value.page; if (lastPage && !currentPage) { setPagination({ page: 1 }); } else if (lastPage < currentPage) { setPagination({ page: lastPage }); } }); function firstPage () { setPagination({ page: 1 }); } function prevPage () { const { page } = computedPagination.value; if (page > 1) { setPagination({ page: page - 1 }); } } function nextPage () { const { page, rowsPerPage } = computedPagination.value; if (lastRowIndex.value > 0 && page * rowsPerPage < computedRowsNumber.value) { setPagination({ page: page + 1 }); } } function lastPage () { setPagination({ page: pagesNumber.value }); } if (props[ 'onUpdate:pagination' ] !== void 0) { emit('update:pagination', { ...computedPagination.value }); } return { firstRowIndex, lastRowIndex, isFirstPage, isLastPage, pagesNumber, computedRowsPerPageOptions, computedRowsNumber, firstPage, prevPage, nextPage, lastPage } } const useTableRowSelectionProps = { selection: { type: String, default: 'none', validator: v => [ 'single', 'multiple', 'none' ].includes(v) }, selected: { type: Array, default: () => [] } }; const useTableRowSelectionEmits = [ 'update:selected', 'selection' ]; function useTableRowSelection (props, emit, computedRows, getRowKey) { const selectedKeys = vue.computed(() => { const keys = {}; props.selected.map(getRowKey.value).forEach(key => { keys[ key ] = true; }); return keys }); const hasSelectionMode = vue.computed(() => { return props.selection !== 'none' }); const singleSelection = vue.computed(() => { return props.selection === 'single' }); const multipleSelection = vue.computed(() => { return props.selection === 'multiple' }); const allRowsSelected = vue.computed(() => computedRows.value.length !== 0 && computedRows.value.every( row => selectedKeys.value[ getRowKey.value(row) ] === true ) ); const someRowsSelected = vue.computed(() => allRowsSelected.value !== true && computedRows.value.some(row => selectedKeys.value[ getRowKey.value(row) ] === true) ); const rowsSelectedNumber = vue.computed(() => props.selected.length); function isRowSelected (key) { return selectedKeys.value[ key ] === true } function clearSelection () { emit('update:selected', []); } function updateSelection (keys, rows, added, evt) { emit('selection', { rows, added, keys, evt }); const payload = singleSelection.value === true ? (added === true ? rows : []) : ( added === true ? props.selected.concat(rows) : props.selected.filter( row => keys.includes(getRowKey.value(row)) === false ) ); emit('update:selected', payload); } return { hasSelectionMode, singleSelection, multipleSelection, allRowsSelected, someRowsSelected, rowsSelectedNumber, isRowSelected, clearSelection, updateSelection } } function getVal (val) { return Array.isArray(val) ? val.slice() : [] } const useTableRowExpandProps = { expanded: Array // v-model:expanded }; const useTableRowExpandEmits = [ 'update:expanded' ]; function useTableRowExpand (props, emit) { const innerExpanded = vue.ref(getVal(props.expanded)); vue.watch(() => props.expanded, val => { innerExpanded.value = getVal(val); }); function isRowExpanded (key) { return innerExpanded.value.includes(key) } function setExpanded (val) { if (props.expanded !== void 0) { emit('update:expanded', val); } else { innerExpanded.value = val; } } function updateExpanded (key, add) { const target = innerExpanded.value.slice(); const index = target.indexOf(key); if (add === true) { if (index === -1) { target.push(key); setExpanded(target); } } else if (index !== -1) { target.splice(index, 1); setExpanded(target); } } return { isRowExpanded, setExpanded, updateExpanded } } const useTableColumnSelectionProps = { visibleColumns: Array }; function useTableColumnSelection (props, computedPagination, hasSelectionMode) { const colList = vue.computed(() => { if (props.columns !== void 0) { return props.columns } // we infer columns from first row const row = props.rows[ 0 ]; return row !== void 0 ? Object.keys(row).map(name => ({ name, label: name.toUpperCase(), field: name, align: isNumber(row[ name ]) ? 'right' : 'left', sortable: true })) : [] }); const computedCols = vue.computed(() => { const { sortBy, descending } = computedPagination.value; const cols = props.visibleColumns !== void 0 ? colList.value.filter(col => col.required === true || props.visibleColumns.includes(col.name) === true) : colList.value; return cols.map(col => { const align = col.align || 'right'; const alignClass = `text-${ align }`; return { ...col, align, __iconClass: `q-table__sort-icon q-table__sort-icon--${ align }`, __thClass: alignClass + (col.headerClasses !== void 0 ? ' ' + col.headerClasses : '') + (col.sortable === true ? ' sortable' : '') + (col.name === sortBy ? ` sorted ${ descending === true ? 'sort-desc' : '' }` : ''), __tdStyle: col.style !== void 0 ? ( typeof col.style !== 'function' ? () => col.style : col.style ) : () => null, __tdClass: col.classes !== void 0 ? ( typeof col.classes !== 'function' ? () => alignClass + ' ' + col.classes : row => alignClass + ' ' + col.classes(row) ) : () => alignClass } }) }); const computedColsMap = vue.computed(() => { const names = {}; computedCols.value.forEach(col => { names[ col.name ] = col; }); return names }); const computedColspan = vue.computed(() => { return props.tableColspan !== void 0 ? props.tableColspan : computedCols.value.length + (hasSelectionMode.value === true ? 1 : 0) }); return { colList, computedCols, computedColsMap, computedColspan } } const bottomClass = 'q-table__bottom row items-center'; const commonVirtPropsObj = {}; commonVirtPropsList.forEach(p => { commonVirtPropsObj[ p ] = {}; }); var QTable = createComponent({ name: 'QTable', props: { rows: { type: Array, default: () => [] }, rowKey: { type: [ String, Function ], default: 'id' }, columns: Array, loading: Boolean, iconFirstPage: String, iconPrevPage: String, iconNextPage: String, iconLastPage: String, title: String, hideHeader: Boolean, grid: Boolean, gridHeader: Boolean, dense: Boolean, flat: Boolean, bordered: Boolean, square: Boolean, separator: { type: String, default: 'horizontal', validator: v => [ 'horizontal', 'vertical', 'cell', 'none' ].includes(v) }, wrapCells: Boolean, virtualScroll: Boolean, virtualScrollTarget: { default: void 0 }, ...commonVirtPropsObj, noDataLabel: String, noResultsLabel: String, loadingLabel: String, selectedRowsLabel: Function, rowsPerPageLabel: String, paginationLabel: Function, color: { type: String, default: 'grey-8' }, titleClass: [ String, Array, Object ], tableStyle: [ String, Array, Object ], tableClass: [ String, Array, Object ], tableHeaderStyle: [ String, Array, Object ], tableHeaderClass: [ String, Array, Object ], cardContainerClass: [ String, Array, Object ], cardContainerStyle: [ String, Array, Object ], cardStyle: [ String, Array, Object ], cardClass: [ String, Array, Object ], hideBottom: Boolean, hideSelectedBanner: Boolean, hideNoData: Boolean, hidePagination: Boolean, onRowClick: Function, onRowDblclick: Function, onRowContextmenu: Function, ...useDarkProps, ...useFullscreenProps, ...useTableColumnSelectionProps, ...useTableFilterProps, ...useTablePaginationProps, ...useTableRowExpandProps, ...useTableRowSelectionProps, ...useTableSortProps }, emits: [ 'request', 'virtualScroll', ...useFullscreenEmits, ...useTableRowExpandEmits, ...useTableRowSelectionEmits ], setup (props, { slots, emit }) { const vm = vue.getCurrentInstance(); const { proxy: { $q } } = vm; const isDark = useDark(props, $q); const { inFullscreen, toggleFullscreen } = useFullscreen(); const getRowKey = vue.computed(() => ( typeof props.rowKey === 'function' ? props.rowKey : row => row[ props.rowKey ] )); const rootRef = vue.ref(null); const virtScrollRef = vue.ref(null); const hasVirtScroll = vue.computed(() => props.grid !== true && props.virtualScroll === true); const cardDefaultClass = vue.computed(() => ' q-table__card' + (isDark.value === true ? ' q-table__card--dark q-dark' : '') + (props.square === true ? ' q-table--square' : '') + (props.flat === true ? ' q-table--flat' : '') + (props.bordered === true ? ' q-table--bordered' : '') ); const __containerClass = vue.computed(() => `q-table__container q-table--${ props.separator }-separator column no-wrap` + (props.grid === true ? ' q-table--grid' : cardDefaultClass.value) + (isDark.value === true ? ' q-table--dark' : '') + (props.dense === true ? ' q-table--dense' : '') + (props.wrapCells === false ? ' q-table--no-wrap' : '') + (inFullscreen.value === true ? ' fullscreen scroll' : '') ); const containerClass = vue.computed(() => __containerClass.value + (props.loading === true ? ' q-table--loading' : '') ); vue.watch( () => props.tableStyle + props.tableClass + props.tableHeaderStyle + props.tableHeaderClass + __containerClass.value, () => { hasVirtScroll.value === true && virtScrollRef.value !== null && virtScrollRef.value.reset(); } ); const { innerPagination, computedPagination, isServerSide, requestServerInteraction, setPagination } = useTablePaginationState(vm, getCellValue); const { computedFilterMethod } = useTableFilter(props, setPagination); const { isRowExpanded, setExpanded, updateExpanded } = useTableRowExpand(props, emit); const filteredSortedRows = vue.computed(() => { let rows = props.rows; if (isServerSide.value === true || rows.length === 0) { return rows } const { sortBy, descending } = computedPagination.value; if (props.filter) { rows = computedFilterMethod.value(rows, props.filter, computedCols.value, getCellValue); } if (columnToSort.value !== null) { rows = computedSortMethod.value( props.rows === rows ? rows.slice() : rows, sortBy, descending ); } return rows }); const filteredSortedRowsNumber = vue.computed(() => filteredSortedRows.value.length); const computedRows = vue.computed(() => { let rows = filteredSortedRows.value; if (isServerSide.value === true) { return rows } const { rowsPerPage } = computedPagination.value; if (rowsPerPage !== 0) { if (firstRowIndex.value === 0 && props.rows !== rows) { if (rows.length > lastRowIndex.value) { rows = rows.slice(0, lastRowIndex.value); } } else { rows = rows.slice(firstRowIndex.value, lastRowIndex.value); } } return rows }); const { hasSelectionMode, singleSelection, multipleSelection, allRowsSelected, someRowsSelected, rowsSelectedNumber, isRowSelected, clearSelection, updateSelection } = useTableRowSelection(props, emit, computedRows, getRowKey); const { colList, computedCols, computedColsMap, computedColspan } = useTableColumnSelection(props, computedPagination, hasSelectionMode); const { columnToSort, computedSortMethod, sort } = useTableSort(props, computedPagination, colList, setPagination); const { firstRowIndex, lastRowIndex, isFirstPage, isLastPage, pagesNumber, computedRowsPerPageOptions, computedRowsNumber, firstPage, prevPage, nextPage, lastPage } = useTablePagination(vm, innerPagination, computedPagination, isServerSide, setPagination, filteredSortedRowsNumber); const nothingToDisplay = vue.computed(() => computedRows.value.length === 0); const virtProps = vue.computed(() => { const acc = {}; commonVirtPropsList .forEach(p => { acc[ p ] = props[ p ]; }); if (acc.virtualScrollItemSize === void 0) { acc.virtualScrollItemSize = props.dense === true ? 28 : 48; } return acc }); function resetVirtualScroll () { hasVirtScroll.value === true && virtScrollRef.value.reset(); } function getBody () { if (props.grid === true) { return getGridBody() } const header = props.hideHeader !== true ? getTHead : null; if (hasVirtScroll.value === true) { const topRow = slots[ 'top-row' ]; const bottomRow = slots[ 'bottom-row' ]; const virtSlots = { default: props => getTBodyTR(props.item, slots.body, props.index) }; if (topRow !== void 0) { const topContent = vue.h('tbody', topRow({ cols: computedCols.value })); virtSlots.before = header === null ? () => topContent : () => [ header() ].concat(topContent); } else if (header !== null) { virtSlots.before = header; } if (bottomRow !== void 0) { virtSlots.after = () => vue.h('tbody', bottomRow({ cols: computedCols.value })); } return vue.h(QVirtualScroll, { ref: virtScrollRef, class: props.tableClass, style: props.tableStyle, ...virtProps.value, scrollTarget: props.virtualScrollTarget, items: computedRows.value, type: '__qtable', tableColspan: computedColspan.value, onVirtualScroll: onVScroll }, virtSlots) } const child = [ getTBody() ]; if (header !== null) { child.unshift(header()); } return getTableMiddle({ class: [ 'q-table__middle scroll', props.tableClass ], style: props.tableStyle }, child) } function scrollTo (toIndex, edge) { if (virtScrollRef.value !== null) { virtScrollRef.value.scrollTo(toIndex, edge); return } toIndex = parseInt(toIndex, 10); const rowEl = rootRef.value.querySelector(`tbody tr:nth-of-type(${ toIndex + 1 })`); if (rowEl !== null) { const scrollTarget = rootRef.value.querySelector('.q-table__middle.scroll'); const offsetTop = rowEl.offsetTop - props.virtualScrollStickySizeStart; const direction = offsetTop < scrollTarget.scrollTop ? 'decrease' : 'increase'; scrollTarget.scrollTop = offsetTop; emit('virtualScroll', { index: toIndex, from: 0, to: innerPagination.value.rowsPerPage - 1, direction }); } } function onVScroll (info) { emit('virtualScroll', info); } function getProgress () { return [ vue.h(QLinearProgress, { class: 'q-table__linear-progress', color: props.color, dark: isDark.value, indeterminate: true, trackColor: 'transparent' }) ] } function getTBodyTR (row, bodySlot, pageIndex) { const key = getRowKey.value(row), selected = isRowSelected(key); if (bodySlot !== void 0) { return bodySlot( getBodyScope({ key, row, pageIndex, __trClass: selected ? 'selected' : '' }) ) } const bodyCell = slots[ 'body-cell' ], child = computedCols.value.map(col => { const bodyCellCol = slots[ `body-cell-${ col.name }` ], slot = bodyCellCol !== void 0 ? bodyCellCol : bodyCell; return slot !== void 0 ? slot(getBodyCellScope({ key, row, pageIndex, col })) : vue.h('td', { class: col.__tdClass(row), style: col.__tdStyle(row) }, getCellValue(col, row)) }); if (hasSelectionMode.value === true) { const slot = slots[ 'body-selection' ]; const content = slot !== void 0 ? slot(getBodySelectionScope({ key, row, pageIndex })) : [ vue.h(QCheckbox, { modelValue: selected, color: props.color, dark: isDark.value, dense: props.dense, 'onUpdate:modelValue': (adding, evt) => { updateSelection([ key ], [ row ], adding, evt); } }) ]; child.unshift( vue.h('td', { class: 'q-table--col-auto-width' }, content) ); } const data = { key, class: { selected } }; if (props.onRowClick !== void 0) { data.class[ 'cursor-pointer' ] = true; data.onClick = evt => { emit('RowClick', evt, row, pageIndex); }; } if (props.onRowDblclick !== void 0) { data.class[ 'cursor-pointer' ] = true; data.onDblclick = evt => { emit('RowDblclick', evt, row, pageIndex); }; } if (props.onRowContextmenu !== void 0) { data.class[ 'cursor-pointer' ] = true; data.onContextmenu = evt => { emit('RowContextmenu', evt, row, pageIndex); }; } return vue.h('tr', data, child) } function getTBody () { const body = slots.body, topRow = slots[ 'top-row' ], bottomRow = slots[ 'bottom-row' ]; let child = computedRows.value.map( (row, pageIndex) => getTBodyTR(row, body, pageIndex) ); if (topRow !== void 0) { child = topRow({ cols: computedCols.value }).concat(child); } if (bottomRow !== void 0) { child = child.concat(bottomRow({ cols: computedCols.value })); } return vue.h('tbody', child) } function getBodyScope (data) { injectBodyCommonScope(data); data.cols = data.cols.map( col => injectProp({ ...col }, 'value', () => getCellValue(col, data.row)) ); return data } function getBodyCellScope (data) { injectBodyCommonScope(data); injectProp(data, 'value', () => getCellValue(data.col, data.row)); return data } function getBodySelectionScope (data) { injectBodyCommonScope(data); return data } function injectBodyCommonScope (data) { Object.assign(data, { cols: computedCols.value, colsMap: computedColsMap.value, sort, rowIndex: firstRowIndex.value + data.pageIndex, color: props.color, dark: isDark.value, dense: props.dense }); hasSelectionMode.value === true && injectProp( data, 'selected', () => isRowSelected(data.key), (adding, evt) => { updateSelection([ data.key ], [ data.row ], adding, evt); } ); injectProp( data, 'expand', () => isRowExpanded(data.key), adding => { updateExpanded(data.key, adding); } ); } function getCellValue (col, row) { const val = typeof col.field === 'function' ? col.field(row) : row[ col.field ]; return col.format !== void 0 ? col.format(val, row) : val } const marginalsScope = vue.computed(() => ({ pagination: computedPagination.value, pagesNumber: pagesNumber.value, isFirstPage: isFirstPage.value, isLastPage: isLastPage.value, firstPage, prevPage, nextPage, lastPage, inFullscreen: inFullscreen.value, toggleFullscreen })); function getTopDiv () { const top = slots.top, topLeft = slots[ 'top-left' ], topRight = slots[ 'top-right' ], topSelection = slots[ 'top-selection' ], hasSelection = hasSelectionMode.value === true && topSelection !== void 0 && rowsSelectedNumber.value > 0, topClass = 'q-table__top relative-position row items-center'; if (top !== void 0) { return vue.h('div', { class: topClass }, [ top(marginalsScope.value) ]) } let child; if (hasSelection === true) { child = topSelection(marginalsScope.value).slice(); } else { child = []; if (topLeft !== void 0) { child.push( vue.h('div', { class: 'q-table__control' }, [ topLeft(marginalsScope.value) ]) ); } else if (props.title) { child.push( vue.h('div', { class: 'q-table__control' }, [ vue.h('div', { class: [ 'q-table__title', props.titleClass ] }, props.title) ]) ); } } if (topRight !== void 0) { child.push( vue.h('div', { class: 'q-table__separator col' }) ); child.push( vue.h('div', { class: 'q-table__control' }, [ topRight(marginalsScope.value) ]) ); } if (child.length === 0) { return } return vue.h('div', { class: topClass }, child) } const headerSelectedValue = vue.computed(() => ( someRowsSelected.value === true ? null : allRowsSelected.value )); function getTHead () { const child = getTHeadTR(); if (props.loading === true && slots.loading === void 0) { child.push( vue.h('tr', { class: 'q-table__progress' }, [ vue.h('th', { class: 'relative-position', colspan: computedColspan.value }, getProgress()) ]) ); } return vue.h('thead', child) } function getTHeadTR () { const header = slots.header, headerCell = slots[ 'header-cell' ]; if (header !== void 0) { return header( getHeaderScope({ header: true }) ).slice() } const child = computedCols.value.map(col => { const headerCellCol = slots[ `header-cell-${ col.name }` ], slot = headerCellCol !== void 0 ? headerCellCol : headerCell, props = getHeaderScope({ col }); return slot !== void 0 ? slot(props) : vue.h(QTh, { key: col.name, props }, () => col.label) }); if (singleSelection.value === true && props.grid !== true) { child.unshift( vue.h('th', { class: 'q-table--col-auto-width' }, ' ') ); } else if (multipleSelection.value === true) { const slot = slots[ 'header-selection' ]; const content = slot !== void 0 ? slot(getHeaderScope({})) : [ vue.h(QCheckbox, { color: props.color, modelValue: headerSelectedValue.value, dark: isDark.value, dense: props.dense, 'onUpdate:modelValue': onMultipleSelectionSet }) ]; child.unshift( vue.h('th', { class: 'q-table--col-auto-width' }, content) ); } return [ vue.h('tr', { class: props.tableHeaderClass, style: props.tableHeaderStyle }, child) ] } function getHeaderScope (data) { Object.assign(data, { cols: computedCols.value, sort, colsMap: computedColsMap.value, color: props.color, dark: isDark.value, dense: props.dense }); if (multipleSelection.value === true) { injectProp( data, 'selected', () => headerSelectedValue.value, onMultipleSelectionSet ); } return data } function onMultipleSelectionSet (val) { if (someRowsSelected.value === true) { val = false; } updateSelection( computedRows.value.map(getRowKey.value), computedRows.value, val ); } const navIcon = vue.computed(() => { const ico = [ props.iconFirstPage || $q.iconSet.table.firstPage, props.iconPrevPage || $q.iconSet.table.prevPage, props.iconNextPage || $q.iconSet.table.nextPage, props.iconLastPage || $q.iconSet.table.lastPage ]; return $q.lang.rtl === true ? ico.reverse() : ico }); function getBottomDiv () { if (props.hideBottom === true) { return } if (nothingToDisplay.value === true) { if (props.hideNoData === true) { return } const message = props.loading === true ? props.loadingLabel || $q.lang.table.loading : (props.filter ? props.noResultsLabel || $q.lang.table.noResults : props.noDataLabel || $q.lang.table.noData); const noData = slots[ 'no-data' ]; const children = noData !== void 0 ? [ noData({ message, icon: $q.iconSet.table.warning, filter: props.filter }) ] : [ vue.h(QIcon, { class: 'q-table__bottom-nodata-icon', name: $q.iconSet.table.warning }), message ]; return vue.h('div', { class: bottomClass + ' q-table__bottom--nodata' }, children) } const bottom = slots.bottom; if (bottom !== void 0) { return vue.h('div', { class: bottomClass }, [ bottom(marginalsScope.value) ]) } const child = props.hideSelectedBanner !== true && hasSelectionMode.value === true && rowsSelectedNumber.value > 0 ? [ vue.h('div', { class: 'q-table__control' }, [ vue.h('div', [ (props.selectedRowsLabel || $q.lang.table.selectedRecords)(rowsSelectedNumber.value) ]) ]) ] : []; if (props.hidePagination !== true) { return vue.h('div', { class: bottomClass + ' justify-end' }, getPaginationDiv(child)) } if (child.length !== 0) { return vue.h('div', { class: bottomClass }, child) } } function onPagSelection (pag) { setPagination({ page: 1, rowsPerPage: pag.value }); } function getPaginationDiv (child) { let control; const { rowsPerPage } = computedPagination.value, paginationLabel = props.paginationLabel || $q.lang.table.pagination, paginationSlot = slots.pagination, hasOpts = props.rowsPerPageOptions.length > 1; child.push( vue.h('div', { class: 'q-table__separator col' }) ); if (hasOpts === true) { child.push( vue.h('div', { class: 'q-table__control' }, [ vue.h('span', { class: 'q-table__bottom-item' }, [ props.rowsPerPageLabel || $q.lang.table.recordsPerPage ]), vue.h(QSelect, { class: 'q-table__select inline q-table__bottom-item', color: props.color, modelValue: rowsPerPage, options: computedRowsPerPageOptions.value, displayValue: rowsPerPage === 0 ? $q.lang.table.allRows : rowsPerPage, dark: isDark.value, borderless: true, dense: true, optionsDense: true, optionsCover: true, 'onUpdate:modelValue': onPagSelection }) ]) ); } if (paginationSlot !== void 0) { control = paginationSlot(marginalsScope.value); } else { control = [ vue.h('span', rowsPerPage !== 0 ? { class: 'q-table__bottom-item' } : {}, [ rowsPerPage ? paginationLabel(firstRowIndex.value + 1, Math.min(lastRowIndex.value, computedRowsNumber.value), computedRowsNumber.value) : paginationLabel(1, filteredSortedRowsNumber.value, computedRowsNumber.value) ]) ]; if (rowsPerPage !== 0 && pagesNumber.value > 1) { const btnProps = { color: props.color, round: true, dense: true, flat: true }; if (props.dense === true) { btnProps.size = 'sm'; } pagesNumber.value > 2 && control.push( vue.h(QBtn, { key: 'pgFirst', ...btnProps, icon: navIcon.value[ 0 ], disable: isFirstPage.value, onClick: firstPage }) ); control.push( vue.h(QBtn, { key: 'pgPrev', ...btnProps, icon: navIcon.value[ 1 ], disable: isFirstPage.value, onClick: prevPage }), vue.h(QBtn, { key: 'pgNext', ...btnProps, icon: navIcon.value[ 2 ], disable: isLastPage.value, onClick: nextPage }) ); pagesNumber.value > 2 && control.push( vue.h(QBtn, { key: 'pgLast', ...btnProps, icon: navIcon.value[ 3 ], disable: isLastPage.value, onClick: lastPage }) ); } } child.push( vue.h('div', { class: 'q-table__control' }, control) ); return child } function getGridHeader () { const child = props.gridHeader === true ? [ vue.h('table', { class: 'q-table' }, [ getTHead() ]) ] : ( props.loading === true && slots.loading === void 0 ? getProgress() : void 0 ); return vue.h('div', { class: 'q-table__middle' }, child) } function getGridBody () { const item = slots.item !== void 0 ? slots.item : scope => { const child = scope.cols.map( col => vue.h('div', { class: 'q-table__grid-item-row' }, [ vue.h('div', { class: 'q-table__grid-item-title' }, [ col.label ]), vue.h('div', { class: 'q-table__grid-item-value' }, [ col.value ]) ]) ); if (hasSelectionMode.value === true) { const slot = slots[ 'body-selection' ]; const content = slot !== void 0 ? slot(scope) : [ vue.h(QCheckbox, { modelValue: scope.selected, color: props.color, dark: isDark.value, dense: props.dense, 'onUpdate:modelValue': (adding, evt) => { updateSelection([ scope.key ], [ scope.row ], adding, evt); } }) ]; child.unshift( vue.h('div', { class: 'q-table__grid-item-row' }, content), vue.h(QSeparator, { dark: isDark.value }) ); } const data = { class: [ 'q-table__grid-item-card' + cardDefaultClass.value, props.cardClass ], style: props.cardStyle }; if ( props.onRowClick !== void 0 || props.onRowDblclick !== void 0 ) { data.class[ 0 ] += ' cursor-pointer'; if (props.onRowClick !== void 0) { data.onClick = evt => { emit('RowClick', evt, scope.row, scope.pageIndex); }; } if (props.onRowDblclick !== void 0) { data.onDblclick = evt => { emit('RowDblclick', evt, scope.row, scope.pageIndex); }; } } return vue.h('div', { class: 'q-table__grid-item col-xs-12 col-sm-6 col-md-4 col-lg-3' + (scope.selected === true ? ' q-table__grid-item--selected' : '') }, [ vue.h('div', data, child) ]) }; return vue.h('div', { class: [ 'q-table__grid-content row', props.cardContainerClass ], style: props.cardContainerStyle }, computedRows.value.map((row, pageIndex) => { return item(getBodyScope({ key: getRowKey.value(row), row, pageIndex })) })) } // expose public methods and needed computed props Object.assign(vm.proxy, { requestServerInteraction, setPagination, firstPage, prevPage, nextPage, lastPage, isRowSelected, clearSelection, isRowExpanded, setExpanded, sort, resetVirtualScroll, scrollTo, getCellValue }); injectMultipleProps(vm.proxy, { filteredSortedRows: () => filteredSortedRows.value, computedRows: () => computedRows.value, computedRowsNumber: () => computedRowsNumber.value }); return () => { const child = [ getTopDiv() ]; const data = { ref: rootRef, class: containerClass.value }; if (props.grid === true) { child.push(getGridHeader()); } else { Object.assign(data, { class: [ data.class, props.cardClass ], style: props.cardStyle }); } child.push( getBody(), getBottomDiv() ); if (props.loading === true && slots.loading !== void 0) { child.push( slots.loading() ); } return vue.h('div', data, child) } } }); var QTr = createComponent({ name: 'QTr', props: { props: Object, noHover: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => 'q-tr' + (props.props === void 0 || props.props.header === true ? '' : ' ' + props.props.__trClass) + (props.noHover === true ? ' q-tr--no-hover' : '') ); return () => vue.h('tr', { class: classes.value }, hSlot(slots.default)) } }); var QTd = createComponent({ name: 'QTd', props: { props: Object, autoWidth: Boolean, noHover: Boolean }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const classes = vue.computed(() => 'q-td' + (props.autoWidth === true ? ' q-table--col-auto-width' : '') + (props.noHover === true ? ' q-td--no-hover' : '') + ' ' ); return () => { if (props.props === void 0) { return vue.h('td', { class: classes.value }, hSlot(slots.default)) } const name = vm.vnode.key; const col = ( (props.props.colsMap !== void 0 ? props.props.colsMap[ name ] : null) || props.props.col ); if (col === void 0) { return } const { row } = props.props; return vue.h('td', { class: classes.value + col.__tdClass(row), style: col.__tdStyle(row) }, hSlot(slots.default)) } } }); var QRouteTab = createComponent({ name: 'QRouteTab', props: { ...useRouterLinkProps, ...useTabProps }, emits: useTabEmits, setup (props, { slots, emit }) { const routeData = useRouterLink({ useDisableForRouterLinkProps: false }); const { renderTab, $tabs } = useTab( props, slots, emit, { exact: vue.computed(() => props.exact), ...routeData } ); vue.watch(() => `${ props.name } | ${ props.exact } | ${ (routeData.resolvedLink.value || {}).href }`, () => { $tabs.verifyRouteModel(); }); return () => renderTab(routeData.linkTag.value, routeData.linkAttrs.value) } }); function getViewByModel (model, withSeconds) { if (model.hour !== null) { if (model.minute === null) { return 'minute' } else if (withSeconds === true && model.second === null) { return 'second' } } return 'hour' } function getCurrentTime () { const d = new Date(); return { hour: d.getHours(), minute: d.getMinutes(), second: d.getSeconds(), millisecond: d.getMilliseconds() } } var QTime = createComponent({ name: 'QTime', props: { ...useDarkProps, ...useFormProps, ...useDatetimeProps, mask: { default: null }, format24h: { type: Boolean, default: null }, defaultDate: { type: String, validator: v => /^-?[\d]+\/[0-1]\d\/[0-3]\d$/.test(v) }, options: Function, hourOptions: Array, minuteOptions: Array, secondOptions: Array, withSeconds: Boolean, nowBtn: Boolean }, emits: useDatetimeEmits, setup (props, { slots, emit }) { const vm = vue.getCurrentInstance(); const { $q } = vm.proxy; const isDark = useDark(props, $q); const { tabindex, headerClass, getLocale, getCurrentDate } = useDatetime(props, $q); const formAttrs = useFormAttrs(props); const injectFormInput = useFormInject(formAttrs); let draggingClockRect, dragCache; const clockRef = vue.ref(null); const mask = vue.computed(() => getMask()); const locale = vue.computed(() => getLocale()); const defaultDateModel = vue.computed(() => getDefaultDateModel()); const model = __splitDate( props.modelValue, mask.value, // initial mask locale.value, // initial locale props.calendar, defaultDateModel.value ); const view = vue.ref(getViewByModel(model)); const innerModel = vue.ref(model); const isAM = vue.ref(model.hour === null || model.hour < 12); const classes = vue.computed(() => `q-time q-time--${ props.landscape === true ? 'landscape' : 'portrait' }` + (isDark.value === true ? ' q-time--dark q-dark' : '') + (props.disable === true ? ' disabled' : (props.readonly === true ? ' q-time--readonly' : '')) + (props.bordered === true ? ' q-time--bordered' : '') + (props.square === true ? ' q-time--square no-border-radius' : '') + (props.flat === true ? ' q-time--flat no-shadow' : '') ); const stringModel = vue.computed(() => { const time = innerModel.value; return { hour: time.hour === null ? '--' : ( computedFormat24h.value === true ? pad(time.hour) : String( isAM.value === true ? (time.hour === 0 ? 12 : time.hour) : (time.hour > 12 ? time.hour - 12 : time.hour) ) ), minute: time.minute === null ? '--' : pad(time.minute), second: time.second === null ? '--' : pad(time.second) } }); const computedFormat24h = vue.computed(() => ( props.format24h !== null ? props.format24h : $q.lang.date.format24h )); const pointerStyle = vue.computed(() => { const forHour = view.value === 'hour', divider = forHour === true ? 12 : 60, amount = innerModel.value[ view.value ], degrees = Math.round(amount * (360 / divider)) - 180; let transform = `rotate(${ degrees }deg) translateX(-50%)`; if ( forHour === true && computedFormat24h.value === true && innerModel.value.hour >= 12 ) { transform += ' scale(.7)'; } return { transform } }); const minLink = vue.computed(() => innerModel.value.hour !== null); const secLink = vue.computed(() => minLink.value === true && innerModel.value.minute !== null); const hourInSelection = vue.computed(() => ( props.hourOptions !== void 0 ? val => props.hourOptions.includes(val) : ( props.options !== void 0 ? val => props.options(val, null, null) : null ) )); const minuteInSelection = vue.computed(() => ( props.minuteOptions !== void 0 ? val => props.minuteOptions.includes(val) : ( props.options !== void 0 ? val => props.options(innerModel.value.hour, val, null) : null ) )); const secondInSelection = vue.computed(() => ( props.secondOptions !== void 0 ? val => props.secondOptions.includes(val) : ( props.options !== void 0 ? val => props.options(innerModel.value.hour, innerModel.value.minute, val) : null ) )); const validHours = vue.computed(() => { if (hourInSelection.value === null) { return null } const am = getValidValues(0, 11, hourInSelection.value); const pm = getValidValues(12, 11, hourInSelection.value); return { am, pm, values: am.values.concat(pm.values) } }); const validMinutes = vue.computed(() => ( minuteInSelection.value !== null ? getValidValues(0, 59, minuteInSelection.value) : null )); const validSeconds = vue.computed(() => ( secondInSelection.value !== null ? getValidValues(0, 59, secondInSelection.value) : null )); const viewValidOptions = vue.computed(() => { switch (view.value) { case 'hour': return validHours.value case 'minute': return validMinutes.value case 'second': return validSeconds.value } }); const positions = vue.computed(() => { let start, end, offset = 0, step = 1; const values = viewValidOptions.value !== null ? viewValidOptions.value.values : void 0; if (view.value === 'hour') { if (computedFormat24h.value === true) { start = 0; end = 23; } else { start = 0; end = 11; if (isAM.value === false) { offset = 12; } } } else { start = 0; end = 55; step = 5; } const pos = []; for (let val = start, index = start; val <= end; val += step, index++) { const actualVal = val + offset, disable = values !== void 0 && values.includes(actualVal) === false, label = view.value === 'hour' && val === 0 ? (computedFormat24h.value === true ? '00' : '12') : val; pos.push({ val: actualVal, index, disable, label }); } return pos }); const clockDirectives = vue.computed(() => { return [ [ TouchPan, onPan, void 0, { stop: true, prevent: true, mouse: true } ] ] }); vue.watch(() => props.modelValue, v => { const model = __splitDate( v, mask.value, locale.value, props.calendar, defaultDateModel.value ); if ( model.dateHash !== innerModel.value.dateHash || model.timeHash !== innerModel.value.timeHash ) { innerModel.value = model; if (model.hour === null) { view.value = 'hour'; } else { isAM.value = model.hour < 12; } } }); vue.watch([ mask, locale ], () => { vue.nextTick(() => { updateValue(); }); }); function setNow () { const date = { ...getCurrentDate(), ...getCurrentTime() }; updateValue(date); Object.assign(innerModel.value, date); // reset any pending changes to innerModel view.value = 'hour'; } function getValidValues (start, count, testFn) { const values = Array.apply(null, { length: count + 1 }) .map((_, index) => { const i = index + start; return { index: i, val: testFn(i) === true // force boolean } }) .filter(v => v.val === true) .map(v => v.index); return { min: values[ 0 ], max: values[ values.length - 1 ], values, threshold: count + 1 } } function getWheelDist (a, b, threshold) { const diff = Math.abs(a - b); return Math.min(diff, threshold - diff) } function getNormalizedClockValue (val, { min, max, values, threshold }) { if (val === min) { return min } if (val < min || val > max) { return getWheelDist(val, min, threshold) <= getWheelDist(val, max, threshold) ? min : max } const index = values.findIndex(v => val <= v), before = values[ index - 1 ], after = values[ index ]; return val - before <= after - val ? before : after } function getMask () { return props.calendar !== 'persian' && props.mask !== null ? props.mask : `HH:mm${ props.withSeconds === true ? ':ss' : '' }` } function getDefaultDateModel () { if (typeof props.defaultDate !== 'string') { const date = getCurrentDate(true); date.dateHash = getDayHash(date); return date } return __splitDate(props.defaultDate, 'YYYY/MM/DD', void 0, props.calendar) } function shouldAbortInteraction () { return vmIsDestroyed(vm) === true // if we have limited options, can we actually set any? || ( viewValidOptions.value !== null && ( viewValidOptions.value.values.length === 0 || ( view.value === 'hour' && computedFormat24h.value !== true && validHours.value[ isAM.value === true ? 'am' : 'pm' ].values.length === 0 ) ) ) } function getClockRect () { const clock = clockRef.value, { top, left, width } = clock.getBoundingClientRect(), dist = width / 2; return { top: top + dist, left: left + dist, dist: dist * 0.7 } } function onPan (event) { if (shouldAbortInteraction() === true) { return } if (event.isFirst === true) { draggingClockRect = getClockRect(); dragCache = updateClock(event.evt, draggingClockRect); return } dragCache = updateClock(event.evt, draggingClockRect, dragCache); if (event.isFinal === true) { draggingClockRect = false; dragCache = null; goToNextView(); } } function goToNextView () { if (view.value === 'hour') { view.value = 'minute'; } else if (props.withSeconds && view.value === 'minute') { view.value = 'second'; } } function updateClock (evt, clockRect, cacheVal) { const pos = position(evt), height = Math.abs(pos.top - clockRect.top), distance = Math.sqrt( Math.pow(Math.abs(pos.top - clockRect.top), 2) + Math.pow(Math.abs(pos.left - clockRect.left), 2) ); let val, angle = Math.asin(height / distance) * (180 / Math.PI); if (pos.top < clockRect.top) { angle = clockRect.left < pos.left ? 90 - angle : 270 + angle; } else { angle = clockRect.left < pos.left ? angle + 90 : 270 - angle; } if (view.value === 'hour') { val = angle / 30; if (validHours.value !== null) { const am = computedFormat24h.value !== true ? isAM.value === true : ( validHours.value.am.values.length !== 0 && validHours.value.pm.values.length !== 0 ? distance >= clockRect.dist : validHours.value.am.values.length !== 0 ); val = getNormalizedClockValue( val + (am === true ? 0 : 12), validHours.value[ am === true ? 'am' : 'pm' ] ); } else { val = Math.round(val); if (computedFormat24h.value === true) { if (distance < clockRect.dist) { if (val < 12) { val += 12; } } else if (val === 12) { val = 0; } } else if (isAM.value === true && val === 12) { val = 0; } else if (isAM.value === false && val !== 12) { val += 12; } } if (computedFormat24h.value === true) { isAM.value = val < 12; } } else { val = Math.round(angle / 6) % 60; if (view.value === 'minute' && validMinutes.value !== null) { val = getNormalizedClockValue(val, validMinutes.value); } else if (view.value === 'second' && validSeconds.value !== null) { val = getNormalizedClockValue(val, validSeconds.value); } } if (cacheVal !== val) { setModel[ view.value ](val); } return val } const setView = { hour () { view.value = 'hour'; }, minute () { view.value = 'minute'; }, second () { view.value = 'second'; } }; function setAmOnKey (e) { e.keyCode === 13 && setAm(); } function setPmOnKey (e) { e.keyCode === 13 && setPm(); } function onClick (evt) { if (shouldAbortInteraction() !== true) { // onMousedown() has already updated the offset // (on desktop only, through mousedown event) if ($q.platform.is.desktop !== true) { updateClock(evt, getClockRect()); } goToNextView(); } } function onMousedown (evt) { if (shouldAbortInteraction() !== true) { updateClock(evt, getClockRect()); } } function onKeyupHour (e) { if (e.keyCode === 13) { // ENTER view.value = 'hour'; } else if ([ 37, 39 ].includes(e.keyCode)) { const payload = e.keyCode === 37 ? -1 : 1; if (validHours.value !== null) { const values = computedFormat24h.value === true ? validHours.value.values : validHours.value[ isAM.value === true ? 'am' : 'pm' ].values; if (values.length === 0) { return } if (innerModel.value.hour === null) { setHour(values[ 0 ]); } else { const index = ( values.length + values.indexOf(innerModel.value.hour) + payload ) % values.length; setHour(values[ index ]); } } else { const wrap = computedFormat24h.value === true ? 24 : 12, offset = computedFormat24h.value !== true && isAM.value === false ? 12 : 0, val = innerModel.value.hour === null ? -payload : innerModel.value.hour; setHour(offset + (24 + val + payload) % wrap); } } } function onKeyupMinute (e) { if (e.keyCode === 13) { // ENTER view.value = 'minute'; } else if ([ 37, 39 ].includes(e.keyCode)) { const payload = e.keyCode === 37 ? -1 : 1; if (validMinutes.value !== null) { const values = validMinutes.value.values; if (values.length === 0) { return } if (innerModel.value.minute === null) { setMinute(values[ 0 ]); } else { const index = ( values.length + values.indexOf(innerModel.value.minute) + payload ) % values.length; setMinute(values[ index ]); } } else { const val = innerModel.value.minute === null ? -payload : innerModel.value.minute; setMinute((60 + val + payload) % 60); } } } function onKeyupSecond (e) { if (e.keyCode === 13) { // ENTER view.value = 'second'; } else if ([ 37, 39 ].includes(e.keyCode)) { const payload = e.keyCode === 37 ? -1 : 1; if (validSeconds.value !== null) { const values = validSeconds.value.values; if (values.length === 0) { return } if (innerModel.value.seconds === null) { setSecond(values[ 0 ]); } else { const index = ( values.length + values.indexOf(innerModel.value.second) + payload ) % values.length; setSecond(values[ index ]); } } else { const val = innerModel.value.second === null ? -payload : innerModel.value.second; setSecond((60 + val + payload) % 60); } } } function setHour (hour) { if (innerModel.value.hour !== hour) { innerModel.value.hour = hour; verifyAndUpdate(); } } function setMinute (minute) { if (innerModel.value.minute !== minute) { innerModel.value.minute = minute; verifyAndUpdate(); } } function setSecond (second) { if (innerModel.value.second !== second) { innerModel.value.second = second; verifyAndUpdate(); } } const setModel = { hour: setHour, minute: setMinute, second: setSecond }; function setAm () { if (isAM.value === false) { isAM.value = true; if (innerModel.value.hour !== null) { innerModel.value.hour -= 12; verifyAndUpdate(); } } } function setPm () { if (isAM.value === true) { isAM.value = false; if (innerModel.value.hour !== null) { innerModel.value.hour += 12; verifyAndUpdate(); } } } function verifyAndUpdate () { if (hourInSelection.value !== null && hourInSelection.value(innerModel.value.hour) !== true) { innerModel.value = __splitDate(); view.value = 'hour'; return } if (minuteInSelection.value !== null && minuteInSelection.value(innerModel.value.minute) !== true) { innerModel.value.minute = null; innerModel.value.second = null; view.value = 'minute'; return } if (props.withSeconds === true && secondInSelection.value !== null && secondInSelection.value(innerModel.value.second) !== true) { innerModel.value.second = null; view.value = 'second'; return } if (innerModel.value.hour === null || innerModel.value.minute === null || (props.withSeconds === true && innerModel.value.second === null)) { return } updateValue(); } function updateValue (obj) { const date = Object.assign({ ...innerModel.value }, obj); const val = props.calendar === 'persian' ? pad(date.hour) + ':' + pad(date.minute) + (props.withSeconds === true ? ':' + pad(date.second) : '') : formatDate( new Date( date.year, date.month === null ? null : date.month - 1, date.day, date.hour, date.minute, date.second, date.millisecond ), mask.value, locale.value, date.year, date.timezoneOffset ); date.changed = val !== props.modelValue; emit('update:modelValue', val, date); } function getHeader () { const label = [ vue.h('div', { class: 'q-time__link ' + (view.value === 'hour' ? 'q-time__link--active' : 'cursor-pointer'), tabindex: tabindex.value, onClick: setView.hour, onKeyup: onKeyupHour }, stringModel.value.hour), vue.h('div', ':'), vue.h( 'div', minLink.value === true ? { class: 'q-time__link ' + (view.value === 'minute' ? 'q-time__link--active' : 'cursor-pointer'), tabindex: tabindex.value, onKeyup: onKeyupMinute, onClick: setView.minute } : { class: 'q-time__link' }, stringModel.value.minute ) ]; if (props.withSeconds === true) { label.push( vue.h('div', ':'), vue.h( 'div', secLink.value === true ? { class: 'q-time__link ' + (view.value === 'second' ? 'q-time__link--active' : 'cursor-pointer'), tabindex: tabindex.value, onKeyup: onKeyupSecond, onClick: setView.second } : { class: 'q-time__link' }, stringModel.value.second ) ); } const child = [ vue.h('div', { class: 'q-time__header-label row items-center no-wrap', dir: 'ltr' }, label) ]; computedFormat24h.value === false && child.push( vue.h('div', { class: 'q-time__header-ampm column items-between no-wrap' }, [ vue.h('div', { class: 'q-time__link ' + (isAM.value === true ? 'q-time__link--active' : 'cursor-pointer'), tabindex: tabindex.value, onClick: setAm, onKeyup: setAmOnKey }, 'AM'), vue.h('div', { class: 'q-time__link ' + (isAM.value !== true ? 'q-time__link--active' : 'cursor-pointer'), tabindex: tabindex.value, onClick: setPm, onKeyup: setPmOnKey }, 'PM') ]) ); return vue.h('div', { class: 'q-time__header flex flex-center no-wrap ' + headerClass.value }, child) } function getClock () { const current = innerModel.value[ view.value ]; return vue.h('div', { class: 'q-time__content col relative-position' }, [ vue.h(vue.Transition, { name: 'q-transition--scale' }, () => vue.h('div', { key: 'clock' + view.value, class: 'q-time__container-parent absolute-full' }, [ vue.h('div', { ref: clockRef, class: 'q-time__container-child fit overflow-hidden' }, [ vue.withDirectives( vue.h('div', { class: 'q-time__clock cursor-pointer non-selectable', onClick, onMousedown }, [ vue.h('div', { class: 'q-time__clock-circle fit' }, [ vue.h('div', { class: 'q-time__clock-pointer' + (innerModel.value[ view.value ] === null ? ' hidden' : (props.color !== void 0 ? ` text-${ props.color }` : '')), style: pointerStyle.value }), positions.value.map(pos => vue.h('div', { class: `q-time__clock-position row flex-center q-time__clock-pos-${ pos.index }` + (pos.val === current ? ' q-time__clock-position--active ' + headerClass.value : (pos.disable === true ? ' q-time__clock-position--disable' : '')) }, [ vue.h('span', pos.label) ])) ]) ]), clockDirectives.value ) ]) ])), props.nowBtn === true ? vue.h(QBtn, { class: 'q-time__now-button absolute', icon: $q.iconSet.datetime.now, unelevated: true, size: 'sm', round: true, color: props.color, textColor: props.textColor, tabindex: tabindex.value, onClick: setNow }) : null ]) } // expose public method vm.proxy.setNow = setNow; return () => { const child = [ getClock() ]; const def = hSlot(slots.default); def !== void 0 && child.push( vue.h('div', { class: 'q-time__actions' }, def) ); if (props.name !== void 0 && props.disable !== true) { injectFormInput(child, 'push'); } return vue.h('div', { class: classes.value, tabindex: -1 }, [ getHeader(), vue.h('div', { class: 'q-time__main col overflow-auto' }, child) ]) } } }); var QTimeline = createComponent({ name: 'QTimeline', props: { ...useDarkProps, color: { type: String, default: 'primary' }, side: { type: String, default: 'right', validator: v => [ 'left', 'right' ].includes(v) }, layout: { type: String, default: 'dense', validator: v => [ 'dense', 'comfortable', 'loose' ].includes(v) } }, setup (props, { slots }) { const vm = vue.getCurrentInstance(); const isDark = useDark(props, vm.proxy.$q); vue.provide(timelineKey, props); const classes = vue.computed(() => `q-timeline q-timeline--${ props.layout } q-timeline--${ props.layout }--${ props.side }` + (isDark.value === true ? ' q-timeline--dark' : '') ); return () => vue.h('ul', { class: classes.value }, hSlot(slots.default)) } }); var QTimelineEntry = createComponent({ name: 'QTimelineEntry', props: { heading: Boolean, tag: { type: String, default: 'h3' }, side: { type: String, default: 'right', validator: v => [ 'left', 'right' ].includes(v) }, icon: String, avatar: String, color: String, title: String, subtitle: String, body: String }, setup (props, { slots }) { const $timeline = vue.inject(timelineKey, emptyRenderFn); if ($timeline === emptyRenderFn) { console.error('QTimelineEntry needs to be child of QTimeline'); return emptyRenderFn } const classes = vue.computed(() => `q-timeline__entry q-timeline__entry--${ props.side }` + (props.icon !== void 0 || props.avatar !== void 0 ? ' q-timeline__entry--icon' : '') ); const dotClass = vue.computed(() => `q-timeline__dot text-${ props.color || $timeline.color }` ); const reverse = vue.computed(() => $timeline.layout === 'comfortable' && $timeline.side === 'left' ); return () => { const child = hUniqueSlot(slots.default, []); if (props.body !== void 0) { child.unshift(props.body); } if (props.heading === true) { const content = [ vue.h('div'), vue.h('div'), vue.h( props.tag, { class: 'q-timeline__heading-title' }, child ) ]; return vue.h('div', { class: 'q-timeline__heading' }, reverse.value === true ? content.reverse() : content) } let dot; if (props.icon !== void 0) { dot = [ vue.h(QIcon, { class: 'row items-center justify-center', name: props.icon }) ]; } else if (props.avatar !== void 0) { dot = [ vue.h('img', { class: 'q-timeline__dot-img', src: props.avatar }) ]; } const content = [ vue.h('div', { class: 'q-timeline__subtitle' }, [ vue.h('span', {}, hSlot(slots.subtitle, [ props.subtitle ])) ]), vue.h('div', { class: dotClass.value }, dot), vue.h('div', { class: 'q-timeline__content' }, [ vue.h('h6', { class: 'q-timeline__title' }, hSlot(slots.title, [ props.title ])) ].concat(child)) ]; return vue.h('li', { class: classes.value }, reverse.value === true ? content.reverse() : content) } } }); var QToolbar = createComponent({ name: 'QToolbar', props: { inset: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => 'q-toolbar row no-wrap items-center' + (props.inset === true ? ' q-toolbar--inset' : '') ); return () => vue.h('div', { class: classes.value, role: 'toolbar' }, hSlot(slots.default)) } }); var QToolbarTitle = createComponent({ name: 'QToolbarTitle', props: { shrink: Boolean }, setup (props, { slots }) { const classes = vue.computed(() => 'q-toolbar__title ellipsis' + (props.shrink === true ? ' col-shrink' : '') ); return () => vue.h('div', { class: classes.value }, hSlot(slots.default)) } }); const tickStrategyOptions = [ 'none', 'strict', 'leaf', 'leaf-filtered' ]; var QTree = createComponent({ name: 'QTree', props: { ...useDarkProps, nodes: { type: Array, required: true }, nodeKey: { type: String, required: true }, labelKey: { type: String, default: 'label' }, childrenKey: { type: String, default: 'children' }, dense: Boolean, color: String, controlColor: String, textColor: String, selectedColor: String, icon: String, tickStrategy: { type: String, default: 'none', validator: v => tickStrategyOptions.includes(v) }, ticked: Array, // v-model:ticked expanded: Array, // v-model:expanded selected: {}, // v-model:selected noSelectionUnset: Boolean, defaultExpandAll: Boolean, accordion: Boolean, filter: String, filterMethod: Function, duration: Number, noConnectors: Boolean, noTransition: Boolean, noNodesLabel: String, noResultsLabel: String }, emits: [ 'update:expanded', 'update:ticked', 'update:selected', 'lazyLoad', 'afterShow', 'afterHide' ], setup (props, { slots, emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const lazy = vue.ref({}); const innerTicked = vue.ref(props.ticked || []); const innerExpanded = vue.ref(props.expanded || []); let blurTargets = {}; vue.onBeforeUpdate(() => { blurTargets = {}; }); const classes = vue.computed(() => `q-tree q-tree--${ props.dense === true ? 'dense' : 'standard' }` + (props.noConnectors === true ? ' q-tree--no-connectors' : '') + (isDark.value === true ? ' q-tree--dark' : '') + (props.color !== void 0 ? ` text-${ props.color }` : '') ); const hasSelection = vue.computed(() => props.selected !== void 0); const computedIcon = vue.computed(() => props.icon || $q.iconSet.tree.icon); const computedControlColor = vue.computed(() => props.controlColor || props.color); const textColorClass = vue.computed(() => ( props.textColor !== void 0 ? ` text-${ props.textColor }` : '' )); const selectedColorClass = vue.computed(() => { const color = props.selectedColor || props.color; return color ? ` text-${ color }` : '' }); const computedFilterMethod = vue.computed(() => ( props.filterMethod !== void 0 ? props.filterMethod : (node, filter) => { const filt = filter.toLowerCase(); return node[ props.labelKey ] && node[ props.labelKey ].toLowerCase().indexOf(filt) > -1 } )); const meta = vue.computed(() => { const meta = {}; const travel = (node, parent) => { const tickStrategy = node.tickStrategy || (parent ? parent.tickStrategy : props.tickStrategy); const key = node[ props.nodeKey ], isParent = node[ props.childrenKey ] && Array.isArray(node[ props.childrenKey ]) && node[ props.childrenKey ].length !== 0, selectable = node.disabled !== true && hasSelection.value === true && node.selectable !== false, expandable = node.disabled !== true && node.expandable !== false, hasTicking = tickStrategy !== 'none', strictTicking = tickStrategy === 'strict', leafFilteredTicking = tickStrategy === 'leaf-filtered', leafTicking = tickStrategy === 'leaf' || tickStrategy === 'leaf-filtered'; let tickable = node.disabled !== true && node.tickable !== false; if (leafTicking === true && tickable === true && parent && parent.tickable !== true) { tickable = false; } let localLazy = node.lazy; if ( localLazy === true && lazy.value[ key ] !== void 0 && Array.isArray(node[ props.childrenKey ]) === true ) { localLazy = lazy.value[ key ]; } const m = { key, parent, isParent, lazy: localLazy, disabled: node.disabled, link: node.disabled !== true && (selectable === true || (expandable === true && (isParent === true || localLazy === true))), children: [], matchesFilter: props.filter ? computedFilterMethod.value(node, props.filter) : true, selected: key === props.selected && selectable === true, selectable, expanded: isParent === true ? innerExpanded.value.includes(key) : false, expandable, noTick: node.noTick === true || (strictTicking !== true && localLazy && localLazy !== 'loaded'), tickable, tickStrategy, hasTicking, strictTicking, leafFilteredTicking, leafTicking, ticked: strictTicking === true ? innerTicked.value.includes(key) : (isParent === true ? false : innerTicked.value.includes(key)) }; meta[ key ] = m; if (isParent === true) { m.children = node[ props.childrenKey ].map(n => travel(n, m)); if (props.filter) { if (m.matchesFilter !== true) { m.matchesFilter = m.children.some(n => n.matchesFilter); } else if ( m.noTick !== true && m.disabled !== true && m.tickable === true && leafFilteredTicking === true && m.children.every(n => n.matchesFilter !== true || n.noTick === true || n.tickable !== true) === true ) { m.tickable = false; } } if (m.matchesFilter === true) { if (m.noTick !== true && strictTicking !== true && m.children.every(n => n.noTick) === true) { m.noTick = true; } if (leafTicking) { m.ticked = false; m.indeterminate = m.children.some(node => node.indeterminate === true); m.tickable = m.tickable === true && m.children.some(node => node.tickable); if (m.indeterminate !== true) { const sel = m.children .reduce((acc, meta) => (meta.ticked === true ? acc + 1 : acc), 0); if (sel === m.children.length) { m.ticked = true; } else if (sel > 0) { m.indeterminate = true; } } if (m.indeterminate === true) { m.indeterminateNextState = m.children .every(meta => meta.tickable !== true || meta.ticked !== true); } } } } return m }; props.nodes.forEach(node => travel(node, null)); return meta }); vue.watch(() => props.ticked, val => { innerTicked.value = val; }); vue.watch(() => props.expanded, val => { innerExpanded.value = val; }); function getNodeByKey (key) { const reduce = [].reduce; const find = (result, node) => { if (result || !node) { return result } if (Array.isArray(node) === true) { return reduce.call(Object(node), find, result) } if (node[ props.nodeKey ] === key) { return node } if (node[ props.childrenKey ]) { return find(null, node[ props.childrenKey ]) } }; return find(null, props.nodes) } function getTickedNodes () { return innerTicked.value.map(key => getNodeByKey(key)) } function getExpandedNodes () { return innerExpanded.value.map(key => getNodeByKey(key)) } function isExpanded (key) { return key && meta.value[ key ] ? meta.value[ key ].expanded : false } function collapseAll () { if (props.expanded !== void 0) { emit('update:expanded', []); } else { innerExpanded.value = []; } } function expandAll () { const expanded = []; const travel = node => { if (node[ props.childrenKey ] && node[ props.childrenKey ].length !== 0) { if (node.expandable !== false && node.disabled !== true) { expanded.push(node[ props.nodeKey ]); node[ props.childrenKey ].forEach(travel); } } }; props.nodes.forEach(travel); if (props.expanded !== void 0) { emit('update:expanded', expanded); } else { innerExpanded.value = expanded; } } function setExpanded (key, state, node = getNodeByKey(key), m = meta.value[ key ]) { if (m.lazy && m.lazy !== 'loaded') { if (m.lazy === 'loading') { return } lazy.value[ key ] = 'loading'; if (Array.isArray(node[ props.childrenKey ]) !== true) { node[ props.childrenKey ] = []; } emit('lazyLoad', { node, key, done: children => { lazy.value[ key ] = 'loaded'; node[ props.childrenKey ] = Array.isArray(children) === true ? children : []; vue.nextTick(() => { const localMeta = meta.value[ key ]; if (localMeta && localMeta.isParent === true) { localSetExpanded(key, true); } }); }, fail: () => { delete lazy.value[ key ]; if (node[ props.childrenKey ].length === 0) { delete node[ props.childrenKey ]; } } }); } else if (m.isParent === true && m.expandable === true) { localSetExpanded(key, state); } } function localSetExpanded (key, state) { let target = innerExpanded.value; const shouldEmit = props.expanded !== void 0; if (shouldEmit === true) { target = target.slice(); } if (state) { if (props.accordion) { if (meta.value[ key ]) { const collapse = []; if (meta.value[ key ].parent) { meta.value[ key ].parent.children.forEach(m => { if (m.key !== key && m.expandable === true) { collapse.push(m.key); } }); } else { props.nodes.forEach(node => { const k = node[ props.nodeKey ]; if (k !== key) { collapse.push(k); } }); } if (collapse.length !== 0) { target = target.filter(k => collapse.includes(k) === false); } } } target = target.concat([ key ]) .filter((key, index, self) => self.indexOf(key) === index); } else { target = target.filter(k => k !== key); } if (shouldEmit === true) { emit('update:expanded', target); } else { innerExpanded.value = target; } } function isTicked (key) { return key && meta.value[ key ] ? meta.value[ key ].ticked : false } function setTicked (keys, state) { let target = innerTicked.value; const shouldEmit = props.ticked !== void 0; if (shouldEmit === true) { target = target.slice(); } if (state) { target = target.concat(keys) .filter((key, index, self) => self.indexOf(key) === index); } else { target = target.filter(k => keys.includes(k) === false); } if (shouldEmit === true) { emit('update:ticked', target); } } function getSlotScope (node, meta, key) { const scope = { tree: proxy, node, key, color: props.color, dark: isDark.value }; injectProp( scope, 'expanded', () => { return meta.expanded }, val => { val !== meta.expanded && setExpanded(key, val); } ); injectProp( scope, 'ticked', () => { return meta.ticked }, val => { val !== meta.ticked && setTicked([ key ], val); } ); return scope } function getChildren (nodes) { return ( props.filter ? nodes.filter(n => meta.value[ n[ props.nodeKey ] ].matchesFilter) : nodes ).map(child => getNode(child)) } function getNodeMedia (node) { if (node.icon !== void 0) { return vue.h(QIcon, { class: 'q-tree__icon q-mr-sm', name: node.icon, color: node.iconColor }) } const src = node.img || node.avatar; if (src) { return vue.h('img', { class: `q-tree__${ node.img ? 'img' : 'avatar' } q-mr-sm`, src }) } } function onShow () { emit('afterShow'); } function onHide () { emit('afterHide'); } function getNode (node) { const key = node[ props.nodeKey ], m = meta.value[ key ], header = node.header ? slots[ `header-${ node.header }` ] || slots[ 'default-header' ] : slots[ 'default-header' ]; const children = m.isParent === true ? getChildren(node[ props.childrenKey ]) : []; const isParent = children.length !== 0 || (m.lazy && m.lazy !== 'loaded'); let body = node.body ? slots[ `body-${ node.body }` ] || slots[ 'default-body' ] : slots[ 'default-body' ]; const slotScope = header !== void 0 || body !== void 0 ? getSlotScope(node, m, key) : null; if (body !== void 0) { body = vue.h('div', { class: 'q-tree__node-body relative-position' }, [ vue.h('div', { class: textColorClass.value }, [ body(slotScope) ]) ]); } return vue.h('div', { key, class: 'q-tree__node relative-position' + ` q-tree__node--${ isParent === true ? 'parent' : 'child' }` }, [ vue.h('div', { class: 'q-tree__node-header relative-position row no-wrap items-center' + (m.link === true ? ' q-tree__node--link q-hoverable q-focusable' : '') + (m.selected === true ? ' q-tree__node--selected' : '') + (m.disabled === true ? ' q-tree__node--disabled' : ''), tabindex: m.link === true ? 0 : -1, onClick: (e) => { onClick(node, m, e); }, onKeypress (e) { if (shouldIgnoreKey(e) !== true) { if (e.keyCode === 13) { onClick(node, m, e, true); } else if (e.keyCode === 32) { onExpandClick(node, m, e, true); } } } }, [ vue.h('div', { class: 'q-focus-helper', tabindex: -1, ref: el => { blurTargets[ m.key ] = el; } }), m.lazy === 'loading' ? vue.h(QSpinner, { class: 'q-tree__spinner', color: computedControlColor.value }) : ( isParent === true ? vue.h(QIcon, { class: 'q-tree__arrow' + (m.expanded === true ? ' q-tree__arrow--rotate' : ''), name: computedIcon.value, onClick (e) { onExpandClick(node, m, e); } }) : null ), m.hasTicking === true && m.noTick !== true ? vue.h(QCheckbox, { class: 'q-tree__tickbox', modelValue: m.indeterminate === true ? null : m.ticked, color: computedControlColor.value, dark: isDark.value, dense: true, keepColor: true, disable: m.tickable !== true, onKeydown: stopAndPrevent, 'onUpdate:modelValue': v => { onTickedClick(m, v); } }) : null, vue.h('div', { class: 'q-tree__node-header-content col row no-wrap items-center' + (m.selected === true ? selectedColorClass.value : textColorClass.value) }, [ header ? header(slotScope) : [ getNodeMedia(node), vue.h('div', node[ props.labelKey ]) ] ]) ]), isParent === true ? ( props.noTransition === true ? vue.h('div', { class: 'q-tree__node-collapsible' + textColorClass.value, key: `${ key }__q` }, [ body, vue.h('div', { class: 'q-tree__children' + (m.disabled === true ? ' q-tree__node--disabled' : '') }, m.expanded ? children : null) ]) : vue.h(QSlideTransition, { duration: props.duration, onShow, onHide }, () => vue.withDirectives( vue.h('div', { class: 'q-tree__node-collapsible' + textColorClass.value, key: `${ key }__q` }, [ body, vue.h('div', { class: 'q-tree__children' + (m.disabled === true ? ' q-tree__node--disabled' : '') }, children) ]), [ [ vue.vShow, m.expanded ] ] )) ) : body ]) } function blur (key) { const blurTarget = blurTargets[ key ]; blurTarget && blurTarget.focus(); } function onClick (node, meta, e, keyboard) { keyboard !== true && meta.selectable !== false && blur(meta.key); if (hasSelection.value && meta.selectable) { if (props.noSelectionUnset === false) { emit('update:selected', meta.key !== props.selected ? meta.key : null); } else if (meta.key !== props.selected) { emit('update:selected', meta.key === void 0 ? null : meta.key); } } else { onExpandClick(node, meta, e, keyboard); } if (typeof node.handler === 'function') { node.handler(node); } } function onExpandClick (node, meta, e, keyboard) { if (e !== void 0) { stopAndPrevent(e); } keyboard !== true && meta.selectable !== false && blur(meta.key); setExpanded(meta.key, !meta.expanded, node, meta); } function onTickedClick (meta, state) { if (meta.indeterminate === true) { state = meta.indeterminateNextState; } if (meta.strictTicking) { setTicked([ meta.key ], state); } else if (meta.leafTicking) { const keys = []; const travel = meta => { if (meta.isParent) { if (state !== true && meta.noTick !== true && meta.tickable === true) { keys.push(meta.key); } if (meta.leafTicking === true) { meta.children.forEach(travel); } } else if ( meta.noTick !== true && meta.tickable === true && (meta.leafFilteredTicking !== true || meta.matchesFilter === true) ) { keys.push(meta.key); } }; travel(meta); setTicked(keys, state); } } props.defaultExpandAll === true && expandAll(); // expose public methods Object.assign(proxy, { getNodeByKey, getTickedNodes, getExpandedNodes, isExpanded, collapseAll, expandAll, setExpanded, isTicked, setTicked }); return () => { const children = getChildren(props.nodes); return vue.h( 'div', { class: classes.value }, children.length === 0 ? ( props.filter ? props.noResultsLabel || $q.lang.tree.noResults : props.noNodesLabel || $q.lang.tree.noNodes ) : children ) } } }); function getProgressLabel (p) { return (p * 100).toFixed(2) + '%' } const coreProps = { ...useDarkProps, ...useFileProps, label: String, color: String, textColor: String, square: Boolean, flat: Boolean, bordered: Boolean, noThumbnails: Boolean, autoUpload: Boolean, hideUploadBtn: Boolean, disable: Boolean, readonly: Boolean }; const coreEmits = [ ...useFileEmits, 'start', 'finish', 'added', 'removed' ]; function getRenderer (getPlugin, expose) { const vm = vue.getCurrentInstance(); const { props, slots, emit, proxy } = vm; const { $q } = proxy; const isDark = useDark(props, $q); function updateFileStatus (file, status, uploadedSize) { file.__status = status; if (status === 'idle') { file.__uploaded = 0; file.__progress = 0; file.__sizeLabel = humanStorageSize(file.size); file.__progressLabel = '0.00%'; return } if (status === 'failed') { proxy.$forceUpdate(); return } file.__uploaded = status === 'uploaded' ? file.size : uploadedSize; file.__progress = status === 'uploaded' ? 1 : Math.min(0.9999, file.__uploaded / file.size); file.__progressLabel = getProgressLabel(file.__progress); proxy.$forceUpdate(); } const editable = vue.computed(() => props.disable !== true && props.readonly !== true); const dnd = vue.ref(false); const rootRef = vue.ref(null); const inputRef = vue.ref(null); const state = { files: vue.ref([]), queuedFiles: vue.ref([]), uploadedFiles: vue.ref([]), uploadedSize: vue.ref(0), updateFileStatus, isAlive: () => vmIsDestroyed(vm) === false }; const { pickFiles, addFiles, onDragover, onDragleave, processFiles, getDndNode, maxFilesNumber, maxTotalSizeNumber } = useFile({ editable, dnd, getFileInput, addFilesToQueue }); Object.assign(state, getPlugin({ props, slots, emit, helpers: state, exposeApi: obj => { Object.assign(state, obj); } })); if (state.isBusy === void 0) { state.isBusy = vue.ref(false); } const uploadSize = vue.ref(0); const uploadProgress = vue.computed(() => ( uploadSize.value === 0 ? 0 : state.uploadedSize.value / uploadSize.value )); const uploadProgressLabel = vue.computed(() => getProgressLabel(uploadProgress.value)); const uploadSizeLabel = vue.computed(() => humanStorageSize(uploadSize.value)); const canAddFiles = vue.computed(() => editable.value === true && state.isUploading.value !== true // if single selection and no files are queued: && (props.multiple === true || state.queuedFiles.value.length === 0) // if max-files is set and current number of files does not exceeds it: && (props.maxFiles === void 0 || state.files.value.length < maxFilesNumber.value) // if max-total-size is set and current upload size does not exceeds it: && (props.maxTotalSize === void 0 || uploadSize.value < maxTotalSizeNumber.value) ); const canUpload = vue.computed(() => editable.value === true && state.isBusy.value !== true && state.isUploading.value !== true && state.queuedFiles.value.length !== 0 ); vue.provide(uploaderKey, renderInput); const classes = vue.computed(() => 'q-uploader column no-wrap' + (isDark.value === true ? ' q-uploader--dark q-dark' : '') + (props.bordered === true ? ' q-uploader--bordered' : '') + (props.square === true ? ' q-uploader--square no-border-radius' : '') + (props.flat === true ? ' q-uploader--flat no-shadow' : '') + (props.disable === true ? ' disabled q-uploader--disable' : '') + (dnd.value === true ? ' q-uploader--dnd' : '') ); const colorClass = vue.computed(() => 'q-uploader__header' + (props.color !== void 0 ? ` bg-${ props.color }` : '') + (props.textColor !== void 0 ? ` text-${ props.textColor }` : '') ); vue.watch(state.isUploading, (newVal, oldVal) => { if (oldVal === false && newVal === true) { emit('start'); } else if (oldVal === true && newVal === false) { emit('finish'); } }); function reset () { if (props.disable === false) { state.abort(); state.uploadedSize.value = 0; uploadSize.value = 0; revokeImgURLs(); state.files.value = []; state.queuedFiles.value = []; state.uploadedFiles.value = []; } } function removeUploadedFiles () { if (props.disable === false) { batchRemoveFiles([ 'uploaded' ], () => { state.uploadedFiles.value = []; }); } } function removeQueuedFiles () { batchRemoveFiles([ 'idle', 'failed' ], ({ size }) => { uploadSize.value -= size; state.queuedFiles.value = []; }); } function batchRemoveFiles (statusList, cb) { if (props.disable === true) { return } const removed = { files: [], size: 0 }; const localFiles = state.files.value.filter(f => { if (statusList.indexOf(f.__status) === -1) { return true } removed.size += f.size; removed.files.push(f); f.__img !== void 0 && window.URL.revokeObjectURL(f.__img.src); return false }); if (removed.files.length !== 0) { state.files.value = localFiles; cb(removed); emit('removed', removed.files); } } function removeFile (file) { if (props.disable) { return } if (file.__status === 'uploaded') { state.uploadedFiles.value = state.uploadedFiles.value.filter(f => f.__key !== file.__key); } else if (file.__status === 'uploading') { file.__abort(); } else { uploadSize.value -= file.size; } state.files.value = state.files.value.filter(f => { if (f.__key !== file.__key) { return true } f.__img !== void 0 && window.URL.revokeObjectURL(f.__img.src); return false }); state.queuedFiles.value = state.queuedFiles.value.filter(f => f.__key !== file.__key); emit('removed', [ file ]); } function revokeImgURLs () { state.files.value.forEach(f => { f.__img !== void 0 && window.URL.revokeObjectURL(f.__img.src); }); } function getFileInput () { return inputRef.value || rootRef.value.getElementsByClassName('q-uploader__input')[ 0 ] } function addFilesToQueue (e, fileList) { const localFiles = processFiles(e, fileList, state.files.value, true); const fileInput = getFileInput(); if (fileInput !== void 0 && fileInput !== null) { fileInput.value = ''; } if (localFiles === void 0) { return } localFiles.forEach(file => { state.updateFileStatus(file, 'idle'); uploadSize.value += file.size; if (props.noThumbnails !== true && file.type.toUpperCase().startsWith('IMAGE')) { const img = new Image(); img.src = window.URL.createObjectURL(file); file.__img = img; } }); state.files.value = state.files.value.concat(localFiles); state.queuedFiles.value = state.queuedFiles.value.concat(localFiles); emit('added', localFiles); props.autoUpload === true && state.upload(); } function upload () { canUpload.value === true && state.upload(); } function getBtn (show, icon, fn) { if (show === true) { const data = { type: 'a', key: icon, icon: $q.iconSet.uploader[ icon ], flat: true, dense: true }; let child = void 0; if (icon === 'add') { data.onClick = pickFiles; child = renderInput; } else { data.onClick = fn; } return vue.h(QBtn, data, child) } } function renderInput () { return vue.h('input', { ref: inputRef, class: 'q-uploader__input overflow-hidden absolute-full', tabindex: -1, type: 'file', title: '', // try to remove default tooltip accept: props.accept, multiple: props.multiple === true ? 'multiple' : void 0, capture: props.capture, onMousedown: stop, // need to stop refocus from QBtn onClick: pickFiles, onChange: addFilesToQueue }) } function getHeader () { if (slots.header !== void 0) { return slots.header(publicApi) } return [ vue.h('div', { class: 'q-uploader__header-content column' }, [ vue.h('div', { class: 'flex flex-center no-wrap q-gutter-xs' }, [ getBtn(state.queuedFiles.value.length !== 0, 'removeQueue', removeQueuedFiles), getBtn(state.uploadedFiles.value.length !== 0, 'removeUploaded', removeUploadedFiles), state.isUploading.value === true ? vue.h(QSpinner, { class: 'q-uploader__spinner' }) : null, vue.h('div', { class: 'col column justify-center' }, [ props.label !== void 0 ? vue.h('div', { class: 'q-uploader__title' }, [ props.label ]) : null, vue.h('div', { class: 'q-uploader__subtitle' }, [ uploadSizeLabel.value + ' / ' + uploadProgressLabel.value ]) ]), getBtn(canAddFiles.value, 'add'), getBtn(props.hideUploadBtn === false && canUpload.value === true, 'upload', state.upload), getBtn(state.isUploading.value, 'clear', state.abort) ]) ]) ] } function getList () { if (slots.list !== void 0) { return slots.list(publicApi) } return state.files.value.map(file => vue.h('div', { key: file.__key, class: 'q-uploader__file relative-position' + (props.noThumbnails !== true && file.__img !== void 0 ? ' q-uploader__file--img' : '') + ( file.__status === 'failed' ? ' q-uploader__file--failed' : (file.__status === 'uploaded' ? ' q-uploader__file--uploaded' : '') ), style: props.noThumbnails !== true && file.__img !== void 0 ? { backgroundImage: 'url("' + file.__img.src + '")' } : null }, [ vue.h('div', { class: 'q-uploader__file-header row flex-center no-wrap' }, [ file.__status === 'failed' ? vue.h(QIcon, { class: 'q-uploader__file-status', name: $q.iconSet.type.negative, color: 'negative' }) : null, vue.h('div', { class: 'q-uploader__file-header-content col' }, [ vue.h('div', { class: 'q-uploader__title' }, [ file.name ]), vue.h('div', { class: 'q-uploader__subtitle row items-center no-wrap' }, [ file.__sizeLabel + ' / ' + file.__progressLabel ]) ]), file.__status === 'uploading' ? vue.h(QCircularProgress, { value: file.__progress, min: 0, max: 1, indeterminate: file.__progress === 0 }) : vue.h(QBtn, { round: true, dense: true, flat: true, icon: $q.iconSet.uploader[ file.__status === 'uploaded' ? 'done' : 'clear' ], onClick: () => { removeFile(file); } }) ]) ])) } vue.onBeforeUnmount(() => { state.isUploading.value === true && state.abort(); state.files.value.length !== 0 && revokeImgURLs(); }); const publicApi = {}; for (const key in state) { if (vue.isRef(state[ key ]) === true) { injectProp(publicApi, key, () => state[ key ].value); } else { // method or non-computed prop publicApi[ key ] = state[ key ]; } } Object.assign(publicApi, { upload, reset, removeUploadedFiles, removeQueuedFiles, removeFile, pickFiles, addFiles }); injectMultipleProps(publicApi, { canAddFiles: () => canAddFiles.value, canUpload: () => canUpload.value, uploadSizeLabel: () => uploadSizeLabel.value, uploadProgressLabel: () => uploadProgressLabel.value }); // expose public api (methods & computed props) expose({ ...state, upload, reset, removeUploadedFiles, removeQueuedFiles, removeFile, pickFiles, addFiles, canAddFiles, canUpload, uploadSizeLabel, uploadProgressLabel }); return () => { const children = [ vue.h('div', { class: colorClass.value }, getHeader()), vue.h('div', { class: 'q-uploader__list scroll' }, getList()), getDndNode('uploader') ]; state.isBusy.value === true && children.push( vue.h('div', { class: 'q-uploader__overlay absolute-full flex flex-center' }, [ vue.h(QSpinner) ]) ); const data = { ref: rootRef, class: classes.value }; if (canAddFiles.value === true) { Object.assign(data, { onDragover, onDragleave }); } return vue.h('div', data, children) } } const trueFn = () => true; function getEmitsObject (emitsArray) { const emitsObject = {}; emitsArray.forEach(val => { emitsObject[ val ] = trueFn; }); return emitsObject } const coreEmitsObject = getEmitsObject(coreEmits); var createUploaderComponent = ({ name, props, emits, injectPlugin }) => createComponent({ name, props: { ...coreProps, ...props }, emits: isObject(emits) === true ? { ...coreEmitsObject, ...emits } : [ ...coreEmits, ...emits ], setup (_, { expose }) { return getRenderer(injectPlugin, expose) } }); function getFn (prop) { return typeof prop === 'function' ? prop : () => prop } const props$2 = { url: [ Function, String ], method: { type: [ Function, String ], default: 'POST' }, fieldName: { type: [ Function, String ], default: () => { return file => file.name } }, headers: [ Function, Array ], formFields: [ Function, Array ], withCredentials: [ Function, Boolean ], sendRaw: [ Function, Boolean ], batch: [ Function, Boolean ], factory: Function }; const emits$1 = [ 'factoryFailed', 'uploaded', 'failed', 'uploading' ]; function injectPlugin ({ props, emit, helpers }) { const xhrs = vue.ref([]); const promises = vue.ref([]); const workingThreads = vue.ref(0); const xhrProps = vue.computed(() => ({ url: getFn(props.url), method: getFn(props.method), headers: getFn(props.headers), formFields: getFn(props.formFields), fieldName: getFn(props.fieldName), withCredentials: getFn(props.withCredentials), sendRaw: getFn(props.sendRaw), batch: getFn(props.batch) })); const isUploading = vue.computed(() => workingThreads.value > 0); const isBusy = vue.computed(() => promises.value.length !== 0); let abortPromises; function abort () { xhrs.value.forEach(x => { x.abort(); }); if (promises.value.length !== 0) { abortPromises = true; } } function upload () { const queue = helpers.queuedFiles.value.slice(0); helpers.queuedFiles.value = []; if (xhrProps.value.batch(queue)) { runFactory(queue); } else { queue.forEach(file => { runFactory([ file ]); }); } } function runFactory (files) { workingThreads.value++; if (typeof props.factory !== 'function') { performUpload(files, {}); return } const res = props.factory(files); if (!res) { emit( 'factoryFailed', new Error('QUploader: factory() does not return properly'), files ); workingThreads.value--; } else if (typeof res.catch === 'function' && typeof res.then === 'function') { promises.value.push(res); const failed = err => { if (helpers.isAlive() === true) { promises.value = promises.value.filter(p => p !== res); if (promises.value.length === 0) { abortPromises = false; } helpers.queuedFiles.value = helpers.queuedFiles.value.concat(files); files.forEach(f => { helpers.updateFileStatus(f, 'failed'); }); emit('factoryFailed', err, files); workingThreads.value--; } }; res.then(factory => { if (abortPromises === true) { failed(new Error('Aborted')); } else if (helpers.isAlive() === true) { promises.value = promises.value.filter(p => p !== res); performUpload(files, factory); } }).catch(failed); } else { performUpload(files, res || {}); } } function performUpload (files, factory) { const form = new FormData(), xhr = new XMLHttpRequest(); const getProp = (name, arg) => { return factory[ name ] !== void 0 ? getFn(factory[ name ])(arg) : xhrProps.value[ name ](arg) }; const url = getProp('url', files); if (!url) { console.error('q-uploader: invalid or no URL specified'); workingThreads.value--; return } const fields = getProp('formFields', files); fields !== void 0 && fields.forEach(field => { form.append(field.name, field.value); }); let uploadIndex = 0, uploadIndexSize = 0, localUploadedSize = 0, maxUploadSize = 0, aborted; xhr.upload.addEventListener('progress', e => { if (aborted === true) { return } const loaded = Math.min(maxUploadSize, e.loaded); helpers.uploadedSize.value += loaded - localUploadedSize; localUploadedSize = loaded; let size = localUploadedSize - uploadIndexSize; for (let i = uploadIndex; size > 0 && i < files.length; i++) { const file = files[ i ], uploaded = size > file.size; if (uploaded) { size -= file.size; uploadIndex++; uploadIndexSize += file.size; helpers.updateFileStatus(file, 'uploading', file.size); } else { helpers.updateFileStatus(file, 'uploading', size); return } } }, false); xhr.onreadystatechange = () => { if (xhr.readyState < 4) { return } if (xhr.status && xhr.status < 400) { helpers.uploadedFiles.value = helpers.uploadedFiles.value.concat(files); files.forEach(f => { helpers.updateFileStatus(f, 'uploaded'); }); emit('uploaded', { files, xhr }); } else { aborted = true; helpers.uploadedSize.value -= localUploadedSize; helpers.queuedFiles.value = helpers.queuedFiles.value.concat(files); files.forEach(f => { helpers.updateFileStatus(f, 'failed'); }); emit('failed', { files, xhr }); } workingThreads.value--; xhrs.value = xhrs.value.filter(x => x !== xhr); }; xhr.open( getProp('method', files), url ); if (getProp('withCredentials', files) === true) { xhr.withCredentials = true; } const headers = getProp('headers', files); headers !== void 0 && headers.forEach(head => { xhr.setRequestHeader(head.name, head.value); }); const sendRaw = getProp('sendRaw', files); files.forEach(file => { helpers.updateFileStatus(file, 'uploading', 0); if (sendRaw !== true) { form.append(getProp('fieldName', file), file, file.name); } file.xhr = xhr; file.__abort = () => { xhr.abort(); }; maxUploadSize += file.size; }); emit('uploading', { files, xhr }); xhrs.value.push(xhr); if (sendRaw === true) { xhr.send(new Blob(files)); } else { xhr.send(form); } } return { isUploading, isBusy, abort, upload } } var xhrUploaderPlugin = { name: 'QUploader', props: props$2, emits: emits$1, injectPlugin }; var QUploader = createUploaderComponent(xhrUploaderPlugin); var QUploaderAddTrigger = createComponent({ name: 'QUploaderAddTrigger', setup () { const $trigger = vue.inject(uploaderKey, emptyRenderFn); if ($trigger === emptyRenderFn) { console.error('QUploaderAddTrigger needs to be child of QUploader'); } return $trigger } }); var QVideo = createComponent({ name: 'QVideo', props: { ...useRatioProps, src: { type: String, required: true }, title: String, fetchpriority: { type: String, default: 'auto' }, loading: { type: String, default: 'eager' }, referrerpolicy: { type: String, default: 'strict-origin-when-cross-origin' } }, setup (props) { const ratioStyle = useRatio(props); const classes = vue.computed(() => 'q-video' + (props.ratio !== void 0 ? ' q-video--responsive' : '') ); return () => vue.h('div', { class: classes.value, style: ratioStyle.value }, [ vue.h('iframe', { src: props.src, title: props.title, fetchpriority: props.fetchpriority, loading: props.loading, referrerpolicy: props.referrerpolicy, frameborder: '0', allowfullscreen: true }) ]) } }); var components = /*#__PURE__*/Object.freeze({ __proto__: null, QAjaxBar: QAjaxBar, QAvatar: QAvatar, QBadge: QBadge, QBanner: QBanner, QBar: QBar, QBreadcrumbs: QBreadcrumbs, QBreadcrumbsEl: QBreadcrumbsEl, QBtn: QBtn, QBtnDropdown: QBtnDropdown, QBtnGroup: QBtnGroup, QBtnToggle: QBtnToggle, QCard: QCard, QCardSection: QCardSection, QCardActions: QCardActions, QCarousel: QCarousel, QCarouselSlide: QCarouselSlide, QCarouselControl: QCarouselControl, QChatMessage: QChatMessage, QCheckbox: QCheckbox, QChip: QChip, QCircularProgress: QCircularProgress, QColor: QColor, QDate: QDate, QDialog: QDialog, QDrawer: QDrawer, QEditor: QEditor, QExpansionItem: QExpansionItem, QFab: QFab, QFabAction: QFabAction, QField: QField, QFile: QFile, QFooter: QFooter, QForm: QForm, QFormChildMixin: QFormChildMixin, QHeader: QHeader, QIcon: QIcon, QImg: QImg, QInfiniteScroll: QInfiniteScroll, QInnerLoading: QInnerLoading, QInput: QInput, QIntersection: QIntersection, QList: QList, QItem: QItem, QItemSection: QItemSection, QItemLabel: QItemLabel, QKnob: QKnob, QLayout: QLayout, QMarkupTable: QMarkupTable, QMenu: QMenu, QNoSsr: QNoSsr, QOptionGroup: QOptionGroup, QPage: QPage, QPageContainer: QPageContainer, QPageScroller: QPageScroller, QPageSticky: QPageSticky, QPagination: QPagination, QParallax: QParallax, QPopupEdit: QPopupEdit, QPopupProxy: QPopupProxy, QLinearProgress: QLinearProgress, QPullToRefresh: QPullToRefresh, QRadio: QRadio, QRange: QRange, QRating: QRating, QResizeObserver: QResizeObserver, QResponsive: QResponsive, QScrollArea: QScrollArea, QScrollObserver: QScrollObserver, QSelect: QSelect, QSeparator: QSeparator, QSkeleton: QSkeleton, QSlideItem: QSlideItem, QSlideTransition: QSlideTransition, QSlider: QSlider, QSpace: QSpace, QSpinner: QSpinner, QSpinnerAudio: QSpinnerAudio, QSpinnerBall: QSpinnerBall, QSpinnerBars: QSpinnerBars, QSpinnerBox: QSpinnerBox, QSpinnerClock: QSpinnerClock, QSpinnerComment: QSpinnerComment, QSpinnerCube: QSpinnerCube, QSpinnerDots: QSpinnerDots, QSpinnerFacebook: QSpinnerFacebook, QSpinnerGears: QSpinnerGears, QSpinnerGrid: QSpinnerGrid, QSpinnerHearts: QSpinnerHearts, QSpinnerHourglass: QSpinnerHourglass, QSpinnerInfinity: QSpinnerInfinity, QSpinnerIos: QSpinnerIos, QSpinnerOrbit: QSpinnerOrbit, QSpinnerOval: QSpinnerOval, QSpinnerPie: QSpinnerPie, QSpinnerPuff: QSpinnerPuff, QSpinnerRadio: QSpinnerRadio, QSpinnerRings: QSpinnerRings, QSpinnerTail: QSpinnerTail, QSplitter: QSplitter, QStep: QStep, QStepper: QStepper, QStepperNavigation: QStepperNavigation, QTabPanels: QTabPanels, QTabPanel: QTabPanel, QTable: QTable, QTh: QTh, QTr: QTr, QTd: QTd, QTabs: QTabs, QTab: QTab, QRouteTab: QRouteTab, QTime: QTime, QTimeline: QTimeline, QTimelineEntry: QTimelineEntry, QToggle: QToggle, QToolbar: QToolbar, QToolbarTitle: QToolbarTitle, QTooltip: QTooltip, QTree: QTree, QUploader: QUploader, QUploaderAddTrigger: QUploaderAddTrigger, QVideo: QVideo, QVirtualScroll: QVirtualScroll }); /* * depth * < 0 --> close all chain * 0 --> disabled * > 0 --> close chain up to N parent */ function getDepth (value) { if (value === false) { return 0 } if (value === true || value === void 0) { return 1 } const depth = parseInt(value, 10); return isNaN(depth) ? 0 : depth } var ClosePopup = createDirective({ name: 'close-popup', beforeMount (el, { value }) { const ctx = { depth: getDepth(value), handler (evt) { // allow @click to be emitted ctx.depth !== 0 && setTimeout(() => { const proxy = getPortalProxy(el); if (proxy !== void 0) { closePortals(proxy, evt, ctx.depth); } }); }, handlerKey (evt) { isKeyCode(evt, 13) === true && ctx.handler(evt); } }; el.__qclosepopup = ctx; el.addEventListener('click', ctx.handler); el.addEventListener('keyup', ctx.handlerKey); }, updated (el, { value, oldValue }) { if (value !== oldValue) { el.__qclosepopup.depth = getDepth(value); } }, beforeUnmount (el) { const ctx = el.__qclosepopup; el.removeEventListener('click', ctx.handler); el.removeEventListener('keyup', ctx.handlerKey); delete el.__qclosepopup; } } ); let id = 0; let offsetBase = void 0; function getAbsolutePosition (el, resize) { if (offsetBase === void 0) { offsetBase = document.createElement('div'); offsetBase.style.cssText = 'position: absolute; left: 0; top: 0'; document.body.appendChild(offsetBase); } const boundingRect = el.getBoundingClientRect(); const baseRect = offsetBase.getBoundingClientRect(); const { marginLeft, marginRight, marginTop, marginBottom } = window.getComputedStyle(el); const marginH = parseInt(marginLeft, 10) + parseInt(marginRight, 10); const marginV = parseInt(marginTop, 10) + parseInt(marginBottom, 10); return { left: boundingRect.left - baseRect.left, top: boundingRect.top - baseRect.top, width: boundingRect.right - boundingRect.left, height: boundingRect.bottom - boundingRect.top, widthM: boundingRect.right - boundingRect.left + (resize === true ? 0 : marginH), heightM: boundingRect.bottom - boundingRect.top + (resize === true ? 0 : marginV), marginH: resize === true ? marginH : 0, marginV: resize === true ? marginV : 0 } } function getAbsoluteSize (el) { return { width: el.scrollWidth, height: el.scrollHeight } } // firefox rulez const styleEdges = [ 'Top', 'Right', 'Bottom', 'Left' ]; const styleBorderRadiuses = [ 'borderTopLeftRadius', 'borderTopRightRadius', 'borderBottomRightRadius', 'borderBottomLeftRadius' ]; const reStyleSkipKey = /-block|-inline|block-|inline-/; const reStyleSkipRule = /(-block|-inline|block-|inline-).*:/; function getComputedStyle$1 (el, props) { const style = window.getComputedStyle(el); const fixed = {}; for (let i = 0; i < props.length; i++) { const prop = props[ i ]; if (style[ prop ] === '') { if (prop === 'cssText') { const styleLen = style.length; let val = ''; for (let i = 0; i < styleLen; i++) { if (reStyleSkipKey.test(style[ i ]) !== true) { val += style[ i ] + ': ' + style[ style[ i ] ] + '; '; } } fixed[ prop ] = val; } else if ([ 'borderWidth', 'borderStyle', 'borderColor' ].indexOf(prop) > -1) { const suffix = prop.replace('border', ''); let val = ''; for (let j = 0; j < styleEdges.length; j++) { const subProp = 'border' + styleEdges[ j ] + suffix; val += style[ subProp ] + ' '; } fixed[ prop ] = val; } else if (prop === 'borderRadius') { let val1 = ''; let val2 = ''; for (let j = 0; j < styleBorderRadiuses.length; j++) { const val = style[ styleBorderRadiuses[ j ] ].split(' '); val1 += val[ 0 ] + ' '; val2 += (val[ 1 ] === void 0 ? val[ 0 ] : val[ 1 ]) + ' '; } fixed[ prop ] = val1 + '/ ' + val2; } else { fixed[ prop ] = style[ prop ]; } } else { if (prop === 'cssText') { fixed[ prop ] = style[ prop ] .split(';') .filter(val => reStyleSkipRule.test(val) !== true) .join(';'); } else { fixed[ prop ] = style[ prop ]; } } } return fixed } const zIndexPositions = [ 'absolute', 'fixed', 'relative', 'sticky' ]; function getMaxZIndex (elStart) { let el = elStart; let maxIndex = 0; while (el !== null && el !== document) { const { position, zIndex } = window.getComputedStyle(el); const zIndexNum = Number(zIndex); if ( zIndexNum > maxIndex && (el === elStart || zIndexPositions.includes(position) === true) ) { maxIndex = zIndexNum; } el = el.parentNode; } return maxIndex } function normalizeElements (opts) { return { from: opts.from, to: opts.to !== void 0 ? opts.to : opts.from } } function normalizeOptions (options) { if (typeof options === 'number') { options = { duration: options }; } else if (typeof options === 'function') { options = { onEnd: options }; } return { ...options, waitFor: options.waitFor === void 0 ? 0 : options.waitFor, duration: isNaN(options.duration) === true ? 300 : parseInt(options.duration, 10), easing: typeof options.easing === 'string' && options.easing.length !== 0 ? options.easing : 'ease-in-out', delay: isNaN(options.delay) === true ? 0 : parseInt(options.delay, 10), fill: typeof options.fill === 'string' && options.fill.length !== 0 ? options.fill : 'none', resize: options.resize === true, // account for UMD too where modifiers will be lowercased to work useCSS: options.useCSS === true || options.usecss === true, // account for UMD too where modifiers will be lowercased to work hideFromClone: options.hideFromClone === true || options.hidefromclone === true, // account for UMD too where modifiers will be lowercased to work keepToClone: options.keepToClone === true || options.keeptoclone === true, tween: options.tween === true, tweenFromOpacity: isNaN(options.tweenFromOpacity) === true ? 0.6 : parseFloat(options.tweenFromOpacity), tweenToOpacity: isNaN(options.tweenToOpacity) === true ? 0.5 : parseFloat(options.tweenToOpacity) } } function getElement (element) { const type = typeof element; return type === 'function' ? element() : ( type === 'string' ? document.querySelector(element) : element ) } function isValidElement (element) { return element && element.ownerDocument === document && element.parentNode !== null } function morph (_options) { let cancel = () => false; let cancelStatus = false; let endElementTo = true; const elements = normalizeElements(_options); const options = normalizeOptions(_options); const elFrom = getElement(elements.from); if (isValidElement(elFrom) !== true) { // we return a cancel function that return false, meaning the cancel function failed return cancel } // we clean other morphs running on this element typeof elFrom.qMorphCancel === 'function' && elFrom.qMorphCancel(); let animationFromClone = void 0; let animationFromTween = void 0; let animationToClone = void 0; let animationTo = void 0; const elFromParent = elFrom.parentNode; const elFromNext = elFrom.nextElementSibling; // we get the dimensions and characteristics // of the parent of the initial element before changes const elFromPosition = getAbsolutePosition(elFrom, options.resize); const { width: elFromParentWidthBefore, height: elFromParentHeightBefore } = getAbsoluteSize(elFromParent); const { borderWidth: elFromBorderWidth, borderStyle: elFromBorderStyle, borderColor: elFromBorderColor, borderRadius: elFromBorderRadius, backgroundColor: elFromBackground, transform: elFromTransform, position: elFromPositioningType, cssText: elFromCssText } = getComputedStyle$1(elFrom, [ 'borderWidth', 'borderStyle', 'borderColor', 'borderRadius', 'backgroundColor', 'transform', 'position', 'cssText' ]); const elFromClassSaved = elFrom.classList.toString(); const elFromStyleSaved = elFrom.style.cssText; // we make a clone of the initial element and // use it to display until the final element is ready // and to change the occupied space during animation const elFromClone = elFrom.cloneNode(true); const elFromTween = options.tween === true ? elFrom.cloneNode(true) : void 0; if (elFromTween !== void 0) { elFromTween.className = elFromTween.classList.toString().split(' ').filter(c => /^bg-/.test(c) === false).join(' '); } // if the initial element is not going to be removed do not show the placeholder options.hideFromClone === true && elFromClone.classList.add('q-morph--internal'); // prevent interaction with placeholder elFromClone.setAttribute('aria-hidden', 'true'); elFromClone.style.transition = 'none'; elFromClone.style.animation = 'none'; elFromClone.style.pointerEvents = 'none'; elFromParent.insertBefore(elFromClone, elFromNext); // we mark the element with its cleanup function elFrom.qMorphCancel = () => { cancelStatus = true; // we clean the clone of the initial element elFromClone.remove(); elFromTween !== void 0 && elFromTween.remove(); options.hideFromClone === true && elFromClone.classList.remove('q-morph--internal'); // we remove the cleanup function from the element elFrom.qMorphCancel = void 0; }; // will be called after Vue catches up with the changes done by _options.onToggle() function const calculateFinalState = () => { const elTo = getElement(elements.to); if (cancelStatus === true || isValidElement(elTo) !== true) { typeof elFrom.qMorphCancel === 'function' && elFrom.qMorphCancel(); return } // we clean other morphs running on this element elFrom !== elTo && typeof elTo.qMorphCancel === 'function' && elTo.qMorphCancel(); // we hide the final element and the clone of the initial element // we don't hide the final element if we want both it and the animated one visible options.keepToClone !== true && elTo.classList.add('q-morph--internal'); elFromClone.classList.add('q-morph--internal'); // we get the dimensions of the parent of the initial element after changes // the difference is how much we should animate the clone const { width: elFromParentWidthAfter, height: elFromParentHeightAfter } = getAbsoluteSize(elFromParent); // we get the dimensions of the parent of the final element before changes const { width: elToParentWidthBefore, height: elToParentHeightBefore } = getAbsoluteSize(elTo.parentNode); // then we show the clone of the initial element if we don't want it hidden options.hideFromClone !== true && elFromClone.classList.remove('q-morph--internal'); // we mark the element with its cleanup function elTo.qMorphCancel = () => { cancelStatus = true; // we clean the clone of the initial element elFromClone.remove(); elFromTween !== void 0 && elFromTween.remove(); options.hideFromClone === true && elFromClone.classList.remove('q-morph--internal'); // we show the final element options.keepToClone !== true && elTo.classList.remove('q-morph--internal'); // we remove the cleanup function from the elements elFrom.qMorphCancel = void 0; elTo.qMorphCancel = void 0; }; // will be called after waitFor (give time to render the final element) const animate = () => { if (cancelStatus === true) { typeof elTo.qMorphCancel === 'function' && elTo.qMorphCancel(); return } // now the animation starts, so we only need the clone // of the initial element as a spacer // we also hide it to calculate the dimensions of the // parent of the final element after the changes if (options.hideFromClone !== true) { elFromClone.classList.add('q-morph--internal'); elFromClone.innerHTML = ''; elFromClone.style.left = 0; elFromClone.style.right = 'unset'; elFromClone.style.top = 0; elFromClone.style.bottom = 'unset'; elFromClone.style.transform = 'none'; } // we show the final element if (options.keepToClone !== true) { elTo.classList.remove('q-morph--internal'); } // we get the dimensions of the parent of the final element after changes // the difference is how much we should animate the clone const elToParent = elTo.parentNode; const { width: elToParentWidthAfter, height: elToParentHeightAfter } = getAbsoluteSize(elToParent); const elToClone = elTo.cloneNode(options.keepToClone); elToClone.setAttribute('aria-hidden', 'true'); if (options.keepToClone !== true) { elToClone.style.left = 0; elToClone.style.right = 'unset'; elToClone.style.top = 0; elToClone.style.bottom = 'unset'; elToClone.style.transform = 'none'; elToClone.style.pointerEvents = 'none'; } elToClone.classList.add('q-morph--internal'); // if elFrom is the same as elTo the next element is elFromClone const elToNext = elTo === elFrom && elFromParent === elToParent ? elFromClone : elTo.nextElementSibling; elToParent.insertBefore(elToClone, elToNext); const { borderWidth: elToBorderWidth, borderStyle: elToBorderStyle, borderColor: elToBorderColor, borderRadius: elToBorderRadius, backgroundColor: elToBackground, transform: elToTransform, position: elToPositioningType, cssText: elToCssText } = getComputedStyle$1(elTo, [ 'borderWidth', 'borderStyle', 'borderColor', 'borderRadius', 'backgroundColor', 'transform', 'position', 'cssText' ]); const elToClassSaved = elTo.classList.toString(); const elToStyleSaved = elTo.style.cssText; // we set the computed styles on the element (to be able to remove classes) elTo.style.cssText = elToCssText; elTo.style.transform = 'none'; elTo.style.animation = 'none'; elTo.style.transition = 'none'; // we strip the background classes (background color can no longer be animated if !important is used) elTo.className = elToClassSaved.split(' ').filter(c => /^bg-/.test(c) === false).join(' '); const elToPosition = getAbsolutePosition(elTo, options.resize); const deltaX = elFromPosition.left - elToPosition.left; const deltaY = elFromPosition.top - elToPosition.top; const scaleX = elFromPosition.width / (elToPosition.width > 0 ? elToPosition.width : 10); const scaleY = elFromPosition.height / (elToPosition.height > 0 ? elToPosition.height : 100); const elFromParentWidthDiff = elFromParentWidthBefore - elFromParentWidthAfter; const elFromParentHeightDiff = elFromParentHeightBefore - elFromParentHeightAfter; const elToParentWidthDiff = elToParentWidthAfter - elToParentWidthBefore; const elToParentHeightDiff = elToParentHeightAfter - elToParentHeightBefore; const elFromCloneWidth = Math.max(elFromPosition.widthM, elFromParentWidthDiff); const elFromCloneHeight = Math.max(elFromPosition.heightM, elFromParentHeightDiff); const elToCloneWidth = Math.max(elToPosition.widthM, elToParentWidthDiff); const elToCloneHeight = Math.max(elToPosition.heightM, elToParentHeightDiff); const elSharedSize = elFrom === elTo && [ 'absolute', 'fixed' ].includes(elToPositioningType) === false && [ 'absolute', 'fixed' ].includes(elFromPositioningType) === false; // if the final element has fixed position or if a parent // has fixed position we need to animate it as fixed let elToNeedsFixedPosition = elToPositioningType === 'fixed'; let parent = elToParent; while (elToNeedsFixedPosition !== true && parent !== document) { elToNeedsFixedPosition = window.getComputedStyle(parent).position === 'fixed'; parent = parent.parentNode; } // we show the spacer for the initial element if (options.hideFromClone !== true) { elFromClone.style.display = 'block'; elFromClone.style.flex = '0 0 auto'; elFromClone.style.opacity = 0; elFromClone.style.minWidth = 'unset'; elFromClone.style.maxWidth = 'unset'; elFromClone.style.minHeight = 'unset'; elFromClone.style.maxHeight = 'unset'; elFromClone.classList.remove('q-morph--internal'); } // we show the spacer for the final element if (options.keepToClone !== true) { elToClone.style.display = 'block'; elToClone.style.flex = '0 0 auto'; elToClone.style.opacity = 0; elToClone.style.minWidth = 'unset'; elToClone.style.maxWidth = 'unset'; elToClone.style.minHeight = 'unset'; elToClone.style.maxHeight = 'unset'; } elToClone.classList.remove('q-morph--internal'); // we apply classes specified by user if (typeof options.classes === 'string') { elTo.className += ' ' + options.classes; } // we apply styles specified by user if (typeof options.style === 'string') { elTo.style.cssText += ' ' + options.style; } else if (isObject(options.style) === true) { for (const prop in options.style) { elTo.style[ prop ] = options.style[ prop ]; } } const elFromZIndex = getMaxZIndex(elFromClone); const elToZIndex = getMaxZIndex(elTo); // we position the morphing element // if we use fixed position for the final element we need to adjust for scroll const documentScroll = elToNeedsFixedPosition === true ? document.documentElement : { scrollLeft: 0, scrollTop: 0 }; elTo.style.position = elToNeedsFixedPosition === true ? 'fixed' : 'absolute'; elTo.style.left = `${ elToPosition.left - documentScroll.scrollLeft }px`; elTo.style.right = 'unset'; elTo.style.top = `${ elToPosition.top - documentScroll.scrollTop }px`; elTo.style.margin = 0; if (options.resize === true) { elTo.style.minWidth = 'unset'; elTo.style.maxWidth = 'unset'; elTo.style.minHeight = 'unset'; elTo.style.maxHeight = 'unset'; elTo.style.overflow = 'hidden'; elTo.style.overflowX = 'hidden'; elTo.style.overflowY = 'hidden'; } document.body.appendChild(elTo); if (elFromTween !== void 0) { elFromTween.style.cssText = elFromCssText; elFromTween.style.transform = 'none'; elFromTween.style.animation = 'none'; elFromTween.style.transition = 'none'; elFromTween.style.position = elTo.style.position; elFromTween.style.left = `${ elFromPosition.left - documentScroll.scrollLeft }px`; elFromTween.style.right = 'unset'; elFromTween.style.top = `${ elFromPosition.top - documentScroll.scrollTop }px`; elFromTween.style.margin = 0; elFromTween.style.pointerEvents = 'none'; if (options.resize === true) { elFromTween.style.minWidth = 'unset'; elFromTween.style.maxWidth = 'unset'; elFromTween.style.minHeight = 'unset'; elFromTween.style.maxHeight = 'unset'; elFromTween.style.overflow = 'hidden'; elFromTween.style.overflowX = 'hidden'; elFromTween.style.overflowY = 'hidden'; } document.body.appendChild(elFromTween); } const commonCleanup = aborted => { // we put the element back in it's place // and restore the styles and classes if (elFrom === elTo && endElementTo !== true) { elTo.style.cssText = elFromStyleSaved; elTo.className = elFromClassSaved; } else { elTo.style.cssText = elToStyleSaved; elTo.className = elToClassSaved; } elToClone.parentNode === elToParent && elToParent.insertBefore(elTo, elToClone); // we clean the spacers elFromClone.remove(); elToClone.remove(); elFromTween !== void 0 && elFromTween.remove(); // cancel will be no longer available cancel = () => false; elFrom.qMorphCancel = void 0; elTo.qMorphCancel = void 0; // we are ready if (typeof options.onEnd === 'function') { options.onEnd(endElementTo === true ? 'to' : 'from', aborted === true); } }; if (options.useCSS !== true && typeof elTo.animate === 'function') { const resizeFrom = options.resize === true ? { transform: `translate(${ deltaX }px, ${ deltaY }px)`, width: `${ elFromCloneWidth }px`, height: `${ elFromCloneHeight }px` } : { transform: `translate(${ deltaX }px, ${ deltaY }px) scale(${ scaleX }, ${ scaleY })` }; const resizeTo = options.resize === true ? { width: `${ elToCloneWidth }px`, height: `${ elToCloneHeight }px` } : {}; const resizeFromTween = options.resize === true ? { width: `${ elFromCloneWidth }px`, height: `${ elFromCloneHeight }px` } : {}; const resizeToTween = options.resize === true ? { transform: `translate(${ -1 * deltaX }px, ${ -1 * deltaY }px)`, width: `${ elToCloneWidth }px`, height: `${ elToCloneHeight }px` } : { transform: `translate(${ -1 * deltaX }px, ${ -1 * deltaY }px) scale(${ 1 / scaleX }, ${ 1 / scaleY })` }; const tweenFrom = elFromTween !== void 0 ? { opacity: options.tweenToOpacity } : { backgroundColor: elFromBackground }; const tweenTo = elFromTween !== void 0 ? { opacity: 1 } : { backgroundColor: elToBackground }; animationTo = elTo.animate([ { margin: 0, borderWidth: elFromBorderWidth, borderStyle: elFromBorderStyle, borderColor: elFromBorderColor, borderRadius: elFromBorderRadius, zIndex: elFromZIndex, transformOrigin: '0 0', ...resizeFrom, ...tweenFrom }, { margin: 0, borderWidth: elToBorderWidth, borderStyle: elToBorderStyle, borderColor: elToBorderColor, borderRadius: elToBorderRadius, zIndex: elToZIndex, transformOrigin: '0 0', transform: elToTransform, ...resizeTo, ...tweenTo } ], { duration: options.duration, easing: options.easing, fill: options.fill, delay: options.delay }); animationFromTween = elFromTween === void 0 ? void 0 : elFromTween.animate([ { opacity: options.tweenFromOpacity, margin: 0, borderWidth: elFromBorderWidth, borderStyle: elFromBorderStyle, borderColor: elFromBorderColor, borderRadius: elFromBorderRadius, zIndex: elFromZIndex, transformOrigin: '0 0', transform: elFromTransform, ...resizeFromTween }, { opacity: 0, margin: 0, borderWidth: elToBorderWidth, borderStyle: elToBorderStyle, borderColor: elToBorderColor, borderRadius: elToBorderRadius, zIndex: elToZIndex, transformOrigin: '0 0', ...resizeToTween } ], { duration: options.duration, easing: options.easing, fill: options.fill, delay: options.delay }); animationFromClone = options.hideFromClone === true || elSharedSize === true ? void 0 : elFromClone.animate([ { margin: `${ elFromParentHeightDiff < 0 ? elFromParentHeightDiff / 2 : 0 }px ${ elFromParentWidthDiff < 0 ? elFromParentWidthDiff / 2 : 0 }px`, width: `${ elFromCloneWidth + elFromPosition.marginH }px`, height: `${ elFromCloneHeight + elFromPosition.marginV }px` }, { margin: 0, width: 0, height: 0 } ], { duration: options.duration, easing: options.easing, fill: options.fill, delay: options.delay }); animationToClone = options.keepToClone === true ? void 0 : elToClone.animate([ elSharedSize === true ? { margin: `${ elFromParentHeightDiff < 0 ? elFromParentHeightDiff / 2 : 0 }px ${ elFromParentWidthDiff < 0 ? elFromParentWidthDiff / 2 : 0 }px`, width: `${ elFromCloneWidth + elFromPosition.marginH }px`, height: `${ elFromCloneHeight + elFromPosition.marginV }px` } : { margin: 0, width: 0, height: 0 }, { margin: `${ elToParentHeightDiff < 0 ? elToParentHeightDiff / 2 : 0 }px ${ elToParentWidthDiff < 0 ? elToParentWidthDiff / 2 : 0 }px`, width: `${ elToCloneWidth + elToPosition.marginH }px`, height: `${ elToCloneHeight + elToPosition.marginV }px` } ], { duration: options.duration, easing: options.easing, fill: options.fill, delay: options.delay }); const cleanup = abort => { animationFromClone !== void 0 && animationFromClone.cancel(); animationFromTween !== void 0 && animationFromTween.cancel(); animationToClone !== void 0 && animationToClone.cancel(); animationTo.cancel(); animationTo.removeEventListener('finish', cleanup); animationTo.removeEventListener('cancel', cleanup); commonCleanup(abort); // we clean the animations animationFromClone = void 0; animationFromTween = void 0; animationToClone = void 0; animationTo = void 0; }; elFrom.qMorphCancel = () => { elFrom.qMorphCancel = void 0; cancelStatus = true; cleanup(); }; elTo.qMorphCancel = () => { elTo.qMorphCancel = void 0; cancelStatus = true; cleanup(); }; animationTo.addEventListener('finish', cleanup); animationTo.addEventListener('cancel', cleanup); cancel = abort => { // we are not in a morph that we can cancel if (cancelStatus === true || animationTo === void 0) { return false } if (abort === true) { cleanup(true); return true } endElementTo = endElementTo !== true; animationFromClone !== void 0 && animationFromClone.reverse(); animationFromTween !== void 0 && animationFromTween.reverse(); animationToClone !== void 0 && animationToClone.reverse(); animationTo.reverse(); return true }; } else { const qAnimId = `q-morph-anim-${ ++id }`; const style = document.createElement('style'); const resizeFrom = options.resize === true ? ` transform: translate(${ deltaX }px, ${ deltaY }px); width: ${ elFromCloneWidth }px; height: ${ elFromCloneHeight }px; ` : `transform: translate(${ deltaX }px, ${ deltaY }px) scale(${ scaleX }, ${ scaleY });`; const resizeTo = options.resize === true ? ` width: ${ elToCloneWidth }px; height: ${ elToCloneHeight }px; ` : ''; const resizeFromTween = options.resize === true ? ` width: ${ elFromCloneWidth }px; height: ${ elFromCloneHeight }px; ` : ''; const resizeToTween = options.resize === true ? ` transform: translate(${ -1 * deltaX }px, ${ -1 * deltaY }px); width: ${ elToCloneWidth }px; height: ${ elToCloneHeight }px; ` : `transform: translate(${ -1 * deltaX }px, ${ -1 * deltaY }px) scale(${ 1 / scaleX }, ${ 1 / scaleY });`; const tweenFrom = elFromTween !== void 0 ? `opacity: ${ options.tweenToOpacity };` : `background-color: ${ elFromBackground };`; const tweenTo = elFromTween !== void 0 ? 'opacity: 1;' : `background-color: ${ elToBackground };`; const keyframesFromTween = elFromTween === void 0 ? '' : ` @keyframes ${ qAnimId }-from-tween { 0% { opacity: ${ options.tweenFromOpacity }; margin: 0; border-width: ${ elFromBorderWidth }; border-style: ${ elFromBorderStyle }; border-color: ${ elFromBorderColor }; border-radius: ${ elFromBorderRadius }; z-index: ${ elFromZIndex }; transform-origin: 0 0; transform: ${ elFromTransform }; ${ resizeFromTween } } 100% { opacity: 0; margin: 0; border-width: ${ elToBorderWidth }; border-style: ${ elToBorderStyle }; border-color: ${ elToBorderColor }; border-radius: ${ elToBorderRadius }; z-index: ${ elToZIndex }; transform-origin: 0 0; ${ resizeToTween } } } `; const keyframesFrom = options.hideFromClone === true || elSharedSize === true ? '' : ` @keyframes ${ qAnimId }-from { 0% { margin: ${ elFromParentHeightDiff < 0 ? elFromParentHeightDiff / 2 : 0 }px ${ elFromParentWidthDiff < 0 ? elFromParentWidthDiff / 2 : 0 }px; width: ${ elFromCloneWidth + elFromPosition.marginH }px; height: ${ elFromCloneHeight + elFromPosition.marginV }px; } 100% { margin: 0; width: 0; height: 0; } } `; const keyframeToStart = elSharedSize === true ? ` margin: ${ elFromParentHeightDiff < 0 ? elFromParentHeightDiff / 2 : 0 }px ${ elFromParentWidthDiff < 0 ? elFromParentWidthDiff / 2 : 0 }px; width: ${ elFromCloneWidth + elFromPosition.marginH }px; height: ${ elFromCloneHeight + elFromPosition.marginV }px; ` : ` margin: 0; width: 0; height: 0; `; const keyframesTo = options.keepToClone === true ? '' : ` @keyframes ${ qAnimId }-to { 0% { ${ keyframeToStart } } 100% { margin: ${ elToParentHeightDiff < 0 ? elToParentHeightDiff / 2 : 0 }px ${ elToParentWidthDiff < 0 ? elToParentWidthDiff / 2 : 0 }px; width: ${ elToCloneWidth + elToPosition.marginH }px; height: ${ elToCloneHeight + elToPosition.marginV }px; } } `; style.innerHTML = ` @keyframes ${ qAnimId } { 0% { margin: 0; border-width: ${ elFromBorderWidth }; border-style: ${ elFromBorderStyle }; border-color: ${ elFromBorderColor }; border-radius: ${ elFromBorderRadius }; background-color: ${ elFromBackground }; z-index: ${ elFromZIndex }; transform-origin: 0 0; ${ resizeFrom } ${ tweenFrom } } 100% { margin: 0; border-width: ${ elToBorderWidth }; border-style: ${ elToBorderStyle }; border-color: ${ elToBorderColor }; border-radius: ${ elToBorderRadius }; background-color: ${ elToBackground }; z-index: ${ elToZIndex }; transform-origin: 0 0; transform: ${ elToTransform }; ${ resizeTo } ${ tweenTo } } } ${ keyframesFrom } ${ keyframesFromTween } ${ keyframesTo } `; document.head.appendChild(style); let animationDirection = 'normal'; elFromClone.style.animation = `${ options.duration }ms ${ options.easing } ${ options.delay }ms ${ animationDirection } ${ options.fill } ${ qAnimId }-from`; if (elFromTween !== void 0) { elFromTween.style.animation = `${ options.duration }ms ${ options.easing } ${ options.delay }ms ${ animationDirection } ${ options.fill } ${ qAnimId }-from-tween`; } elToClone.style.animation = `${ options.duration }ms ${ options.easing } ${ options.delay }ms ${ animationDirection } ${ options.fill } ${ qAnimId }-to`; elTo.style.animation = `${ options.duration }ms ${ options.easing } ${ options.delay }ms ${ animationDirection } ${ options.fill } ${ qAnimId }`; const cleanup = evt => { if (evt === Object(evt) && evt.animationName !== qAnimId) { return } elTo.removeEventListener('animationend', cleanup); elTo.removeEventListener('animationcancel', cleanup); commonCleanup(); // we clean the animations style.remove(); }; elFrom.qMorphCancel = () => { elFrom.qMorphCancel = void 0; cancelStatus = true; cleanup(); }; elTo.qMorphCancel = () => { elTo.qMorphCancel = void 0; cancelStatus = true; cleanup(); }; elTo.addEventListener('animationend', cleanup); elTo.addEventListener('animationcancel', cleanup); cancel = abort => { // we are not in a morph that we can cancel if (cancelStatus === true || !elTo || !elFromClone || !elToClone) { return false } if (abort === true) { cleanup(); return true } endElementTo = endElementTo !== true; animationDirection = animationDirection === 'normal' ? 'reverse' : 'normal'; elFromClone.style.animationDirection = animationDirection; elFromTween.style.animationDirection = animationDirection; elToClone.style.animationDirection = animationDirection; elTo.style.animationDirection = animationDirection; return true }; } }; if ( options.waitFor > 0 || options.waitFor === 'transitionend' || (options.waitFor === Object(options.waitFor) && typeof options.waitFor.then === 'function') ) { const delayPromise = options.waitFor > 0 ? new Promise(resolve => setTimeout(resolve, options.waitFor)) : ( options.waitFor === 'transitionend' ? new Promise(resolve => { const endFn = () => { if (timer !== null) { clearTimeout(timer); timer = null; } if (elTo) { elTo.removeEventListener('transitionend', endFn); elTo.removeEventListener('transitioncancel', endFn); } resolve(); }; let timer = setTimeout(endFn, 400); elTo.addEventListener('transitionend', endFn); elTo.addEventListener('transitioncancel', endFn); }) : options.waitFor ); delayPromise .then(animate) .catch(() => { typeof elTo.qMorphCancel === 'function' && elTo.qMorphCancel(); }); } else { animate(); } }; typeof _options.onToggle === 'function' && _options.onToggle(); requestAnimationFrame(calculateFinalState); // we return the cancel function // returns: // false if the cancel cannot be performed (the morph ended already or has not started) // true else return abort => cancel(abort) } const morphGroups = {}; const props$1 = [ 'duration', 'delay', 'easing', 'fill', 'classes', 'style', 'duration', 'resize', 'useCSS', 'hideFromClone', 'keepToClone', 'tween', 'tweenFromOpacity', 'tweenToOpacity', 'waitFor', 'onEnd' ]; const mods = [ 'resize', 'useCSS', 'hideFromClone', 'keepToClone', 'tween' ]; function changeClass (ctx, action) { if (ctx.clsAction !== action) { ctx.clsAction = action; ctx.el.classList[ action ]('q-morph--invisible'); } } function trigger (group) { if (group.animating === true || group.queue.length < 2) { return } const [ from, to ] = group.queue; group.animating = true; from.animating = true; to.animating = true; changeClass(from, 'remove'); changeClass(to, 'remove'); const cancelFn = morph({ from: from.el, to: to.el, onToggle () { changeClass(from, 'add'); changeClass(to, 'remove'); }, ...to.opts, onEnd (dir, aborted) { to.opts.onEnd !== void 0 && to.opts.onEnd(dir, aborted); if (aborted === true) { return } from.animating = false; to.animating = false; group.animating = false; group.cancel = void 0; group.queue.shift(); trigger(group); } }); group.cancel = () => { cancelFn(true); // abort group.cancel = void 0; }; } function updateModifiers (mod, ctx) { const opts = ctx.opts; mods.forEach(name => { opts[ name ] = mod[ name ] === true; }); } function insertArgs (arg, ctx) { const opts = typeof arg === 'string' && arg.length !== 0 ? arg.split(':') : []; ctx.name = opts[ 0 ]; ctx.group = opts[ 1 ]; Object.assign(ctx.opts, { duration: isNaN(opts[ 2 ]) === true ? 300 : parseFloat(opts[ 2 ]), waitFor: opts[ 3 ] }); } function updateArgs (arg, ctx) { if (arg.group !== void 0) { ctx.group = arg.group; } if (arg.name !== void 0) { ctx.name = arg.name; } const opts = ctx.opts; props$1.forEach(name => { if (arg[ name ] !== void 0) { opts[ name ] = arg[ name ]; } }); } function updateModel (name, ctx) { if (ctx.name === name) { const group = morphGroups[ ctx.group ]; // if group is not registered if (group === void 0) { morphGroups[ ctx.group ] = { name: ctx.group, model: name, queue: [ ctx ], animating: false }; changeClass(ctx, 'remove'); } // if model changed else if (group.model !== name) { group.model = name; group.queue.push(ctx); if (group.animating === false && group.queue.length === 2) { trigger(group); } } return } if (ctx.animating === false) { changeClass(ctx, 'add'); } } function updateValue (ctx, value) { let model; if (Object(value) === value) { model = '' + value.model; updateArgs(value, ctx); updateModifiers(value, ctx); } else { model = '' + value; } if (model !== ctx.model) { ctx.model = model; updateModel(model, ctx); } else if (ctx.animating === false && ctx.clsAction !== void 0) { // ensure HMR ctx.el.classList[ ctx.clsAction ]('q-morph--invisible'); } } var Morph = createDirective({ name: 'morph', mounted (el, binding) { const ctx = { el, animating: false, opts: {} }; updateModifiers(binding.modifiers, ctx); insertArgs(binding.arg, ctx); updateValue(ctx, binding.value); el.__qmorph = ctx; }, updated (el, binding) { updateValue(el.__qmorph, binding.value); }, beforeUnmount (el) { const ctx = el.__qmorph; const group = morphGroups[ ctx.group ]; if (group !== void 0) { const index = group.queue.indexOf(ctx); if (index !== -1) { group.queue = group.queue.filter(item => item !== ctx); if (group.queue.length === 0) { group.cancel !== void 0 && group.cancel(); delete morphGroups[ ctx.group ]; } } } if (ctx.clsAction === 'add') { el.classList.remove('q-morph--invisible'); } delete el.__qmorph; } } ); const defaultCfg = { childList: true, subtree: true, attributes: true, characterData: true, attributeOldValue: true, characterDataOldValue: true }; function update$2 (el, ctx, value) { ctx.handler = value; ctx.observer !== void 0 && ctx.observer.disconnect(); ctx.observer = new MutationObserver(list => { if (typeof ctx.handler === 'function') { const res = ctx.handler(list); if (res === false || ctx.once === true) { destroy(el); } } }); ctx.observer.observe(el, ctx.opts); } function destroy (el) { const ctx = el.__qmutation; if (ctx !== void 0) { ctx.observer !== void 0 && ctx.observer.disconnect(); delete el.__qmutation; } } var Mutation = createDirective({ name: 'mutation', mounted (el, { modifiers: { once, ...mod }, value }) { const ctx = { once, opts: Object.keys(mod).length === 0 ? defaultCfg : mod }; update$2(el, ctx, value); el.__qmutation = ctx; }, updated (el, { oldValue, value }) { const ctx = el.__qmutation; if (ctx !== void 0 && oldValue !== value) { update$2(el, ctx, value); } }, beforeUnmount: destroy } ); const { passive } = listenOpts; function update$1 (ctx, { value, oldValue }) { if (typeof value !== 'function') { ctx.scrollTarget.removeEventListener('scroll', ctx.scroll, passive); return } ctx.handler = value; if (typeof oldValue !== 'function') { ctx.scrollTarget.addEventListener('scroll', ctx.scroll, passive); ctx.scroll(); } } var ScrollFire = createDirective({ name: 'scroll-fire', mounted (el, binding) { const ctx = { scrollTarget: getScrollTarget(el), scroll: debounce(() => { let containerBottom, elBottom; if (ctx.scrollTarget === window) { elBottom = el.getBoundingClientRect().bottom; containerBottom = window.innerHeight; } else { elBottom = offset(el).top + height(el); containerBottom = offset(ctx.scrollTarget).top + height(ctx.scrollTarget); } if (elBottom > 0 && elBottom < containerBottom) { ctx.scrollTarget.removeEventListener('scroll', ctx.scroll, passive); ctx.handler(el); } }, 25) }; update$1(ctx, binding); el.__qscrollfire = ctx; }, updated (el, binding) { if (binding.value !== binding.oldValue) { update$1(el.__qscrollfire, binding); } }, beforeUnmount (el) { const ctx = el.__qscrollfire; ctx.scrollTarget.removeEventListener('scroll', ctx.scroll, passive); ctx.scroll.cancel(); delete el.__qscrollfire; } } ); function update (ctx, { value, oldValue }) { if (typeof value !== 'function') { ctx.scrollTarget.removeEventListener('scroll', ctx.scroll, listenOpts.passive); return } ctx.handler = value; if (typeof oldValue !== 'function') { ctx.scrollTarget.addEventListener('scroll', ctx.scroll, listenOpts.passive); } } var Scroll = createDirective({ name: 'scroll', mounted (el, binding) { const ctx = { scrollTarget: getScrollTarget(el), scroll () { ctx.handler( getVerticalScrollPosition(ctx.scrollTarget), getHorizontalScrollPosition(ctx.scrollTarget) ); } }; update(ctx, binding); el.__qscroll = ctx; }, updated (el, binding) { if (el.__qscroll !== void 0 && binding.oldValue !== binding.value) { update(el.__qscroll, binding); } }, beforeUnmount (el) { const ctx = el.__qscroll; ctx.scrollTarget.removeEventListener('scroll', ctx.scroll, listenOpts.passive); delete el.__qscroll; } } ); var TouchHold = createDirective({ name: 'touch-hold', beforeMount (el, binding) { const { modifiers } = binding; // early return, we don't need to do anything if (modifiers.mouse !== true && client.has.touch !== true) { return } const ctx = { handler: binding.value, noop, mouseStart (evt) { if (typeof ctx.handler === 'function' && leftClick(evt) === true) { addEvt(ctx, 'temp', [ [ document, 'mousemove', 'move', 'passiveCapture' ], [ document, 'click', 'end', 'notPassiveCapture' ] ]); ctx.start(evt, true); } }, touchStart (evt) { if (evt.target !== void 0 && typeof ctx.handler === 'function') { const target = evt.target; addEvt(ctx, 'temp', [ [ target, 'touchmove', 'move', 'passiveCapture' ], [ target, 'touchcancel', 'end', 'notPassiveCapture' ], [ target, 'touchend', 'end', 'notPassiveCapture' ] ]); ctx.start(evt); } }, start (evt, mouseEvent) { ctx.origin = position(evt); const startTime = Date.now(); if (client.is.mobile === true) { document.body.classList.add('non-selectable'); clearSelection(); ctx.styleCleanup = withDelay => { ctx.styleCleanup = void 0; const remove = () => { document.body.classList.remove('non-selectable'); }; if (withDelay === true) { clearSelection(); setTimeout(remove, 10); } else { remove(); } }; } ctx.triggered = false; ctx.sensitivity = mouseEvent === true ? ctx.mouseSensitivity : ctx.touchSensitivity; ctx.timer = setTimeout(() => { ctx.timer = void 0; clearSelection(); ctx.triggered = true; ctx.handler({ evt, touch: mouseEvent !== true, mouse: mouseEvent === true, position: ctx.origin, duration: Date.now() - startTime }); }, ctx.duration); }, move (evt) { const { top, left } = position(evt); if ( ctx.timer !== void 0 && ( Math.abs(left - ctx.origin.left) >= ctx.sensitivity || Math.abs(top - ctx.origin.top) >= ctx.sensitivity ) ) { clearTimeout(ctx.timer); ctx.timer = void 0; } }, end (evt) { cleanEvt(ctx, 'temp'); // delay needed otherwise selection still occurs ctx.styleCleanup !== void 0 && ctx.styleCleanup(ctx.triggered); if (ctx.triggered === true) { evt !== void 0 && stopAndPrevent(evt); } else if (ctx.timer !== void 0) { clearTimeout(ctx.timer); ctx.timer = void 0; } } }; // duration in ms, touch in pixels, mouse in pixels const data = [ 600, 5, 7 ]; if (typeof binding.arg === 'string' && binding.arg.length !== 0) { binding.arg.split(':').forEach((val, index) => { const v = parseInt(val, 10); v && (data[ index ] = v); }); } [ ctx.duration, ctx.touchSensitivity, ctx.mouseSensitivity ] = data; el.__qtouchhold = ctx; if (modifiers.mouse === true) { // account for UMD too where modifiers will be lowercased to work const capture = modifiers.mouseCapture === true || modifiers.mousecapture === true ? 'Capture' : ''; addEvt(ctx, 'main', [ [ el, 'mousedown', 'mouseStart', `passive${ capture }` ] ]); } client.has.touch === true && addEvt(ctx, 'main', [ [ el, 'touchstart', 'touchStart', `passive${ modifiers.capture === true ? 'Capture' : '' }` ], [ el, 'touchend', 'noop', 'notPassiveCapture' ] ]); }, updated (el, binding) { const ctx = el.__qtouchhold; if (ctx !== void 0 && binding.oldValue !== binding.value) { typeof binding.value !== 'function' && ctx.end(); ctx.handler = binding.value; } }, beforeUnmount (el) { const ctx = el.__qtouchhold; if (ctx !== void 0) { cleanEvt(ctx, 'main'); cleanEvt(ctx, 'temp'); ctx.timer !== void 0 && clearTimeout(ctx.timer); ctx.styleCleanup !== void 0 && ctx.styleCleanup(); delete el.__qtouchhold; } } } ); const keyCodes = { esc: 27, tab: 9, enter: 13, space: 32, up: 38, left: 37, right: 39, down: 40, delete: [ 8, 46 ] }, keyRegex = new RegExp(`^([\\d+]+|${ Object.keys(keyCodes).join('|') })$`, 'i'); function shouldEnd (evt, origin) { const { top, left } = position(evt); return Math.abs(left - origin.left) >= 7 || Math.abs(top - origin.top) >= 7 } var TouchRepeat = createDirective({ name: 'touch-repeat', beforeMount (el, { modifiers, value, arg }) { const keyboard = Object.keys(modifiers).reduce((acc, key) => { if (keyRegex.test(key) === true) { const keyCode = isNaN(parseInt(key, 10)) ? keyCodes[ key.toLowerCase() ] : parseInt(key, 10); keyCode >= 0 && acc.push(keyCode); } return acc }, []); // early return, we don't need to do anything if ( modifiers.mouse !== true && client.has.touch !== true && keyboard.length === 0 ) { return } const durations = typeof arg === 'string' && arg.length !== 0 ? arg.split(':').map(val => parseInt(val, 10)) : [ 0, 600, 300 ]; const durationsLast = durations.length - 1; const ctx = { keyboard, handler: value, noop, mouseStart (evt) { if (ctx.event === void 0 && typeof ctx.handler === 'function' && leftClick(evt) === true) { addEvt(ctx, 'temp', [ [ document, 'mousemove', 'move', 'passiveCapture' ], [ document, 'click', 'end', 'notPassiveCapture' ] ]); ctx.start(evt, true); } }, keyboardStart (evt) { if (typeof ctx.handler === 'function' && isKeyCode(evt, keyboard) === true) { if (durations[ 0 ] === 0 || ctx.event !== void 0) { stopAndPrevent(evt); el.focus(); if (ctx.event !== void 0) { return } } addEvt(ctx, 'temp', [ [ document, 'keyup', 'end', 'notPassiveCapture' ], [ document, 'click', 'end', 'notPassiveCapture' ] ]); ctx.start(evt, false, true); } }, touchStart (evt) { if (evt.target !== void 0 && typeof ctx.handler === 'function') { const target = evt.target; addEvt(ctx, 'temp', [ [ target, 'touchmove', 'move', 'passiveCapture' ], [ target, 'touchcancel', 'end', 'notPassiveCapture' ], [ target, 'touchend', 'end', 'notPassiveCapture' ] ]); ctx.start(evt); } }, start (evt, mouseEvent, keyboardEvent) { if (keyboardEvent !== true) { ctx.origin = position(evt); } function styleCleanup (withDelay) { ctx.styleCleanup = void 0; document.documentElement.style.cursor = ''; const remove = () => { document.body.classList.remove('non-selectable'); }; if (withDelay === true) { clearSelection(); setTimeout(remove, 10); } else { remove(); } } if (client.is.mobile === true) { document.body.classList.add('non-selectable'); clearSelection(); ctx.styleCleanup = styleCleanup; } ctx.event = { touch: mouseEvent !== true && keyboardEvent !== true, mouse: mouseEvent === true, keyboard: keyboardEvent === true, startTime: Date.now(), repeatCount: 0 }; const fn = () => { ctx.timer = void 0; if (ctx.event === void 0) { return } if (ctx.event.repeatCount === 0) { ctx.event.evt = evt; if (keyboardEvent === true) { ctx.event.keyCode = evt.keyCode; } else { ctx.event.position = position(evt); } if (client.is.mobile !== true) { document.documentElement.style.cursor = 'pointer'; document.body.classList.add('non-selectable'); clearSelection(); ctx.styleCleanup = styleCleanup; } } ctx.event.duration = Date.now() - ctx.event.startTime; ctx.event.repeatCount += 1; ctx.handler(ctx.event); const index = durationsLast < ctx.event.repeatCount ? durationsLast : ctx.event.repeatCount; ctx.timer = setTimeout(fn, durations[ index ]); }; if (durations[ 0 ] === 0) { fn(); } else { ctx.timer = setTimeout(fn, durations[ 0 ]); } }, move (evt) { if (ctx.event !== void 0 && ctx.timer !== void 0 && shouldEnd(evt, ctx.origin) === true) { clearTimeout(ctx.timer); ctx.timer = void 0; } }, end (evt) { if (ctx.event === void 0) { return } ctx.styleCleanup !== void 0 && ctx.styleCleanup(true); evt !== void 0 && ctx.event.repeatCount > 0 && stopAndPrevent(evt); cleanEvt(ctx, 'temp'); if (ctx.timer !== void 0) { clearTimeout(ctx.timer); ctx.timer = void 0; } ctx.event = void 0; } }; el.__qtouchrepeat = ctx; if (modifiers.mouse === true) { // account for UMD too where modifiers will be lowercased to work const capture = modifiers.mouseCapture === true || modifiers.mousecapture === true ? 'Capture' : ''; addEvt(ctx, 'main', [ [ el, 'mousedown', 'mouseStart', `passive${ capture }` ] ]); } client.has.touch === true && addEvt(ctx, 'main', [ [ el, 'touchstart', 'touchStart', `passive${ modifiers.capture === true ? 'Capture' : '' }` ], [ el, 'touchend', 'noop', 'passiveCapture' ] ]); if (keyboard.length !== 0) { // account for UMD too where modifiers will be lowercased to work const capture = modifiers.keyCapture === true || modifiers.keycapture === true ? 'Capture' : ''; addEvt(ctx, 'main', [ [ el, 'keydown', 'keyboardStart', `notPassive${ capture }` ] ]); } }, updated (el, { oldValue, value }) { const ctx = el.__qtouchrepeat; if (ctx !== void 0 && oldValue !== value) { typeof value !== 'function' && ctx.end(); ctx.handler = value; } }, beforeUnmount (el) { const ctx = el.__qtouchrepeat; if (ctx !== void 0) { ctx.timer !== void 0 && clearTimeout(ctx.timer); cleanEvt(ctx, 'main'); cleanEvt(ctx, 'temp'); ctx.styleCleanup !== void 0 && ctx.styleCleanup(); delete el.__qtouchrepeat; } } } ); var directives = /*#__PURE__*/Object.freeze({ __proto__: null, ClosePopup: ClosePopup, Intersection: Intersection, Morph: Morph, Mutation: Mutation, Ripple: Ripple, ScrollFire: ScrollFire, Scroll: Scroll, TouchHold: TouchHold, TouchPan: TouchPan, TouchRepeat: TouchRepeat, TouchSwipe: TouchSwipe }); function getCssVar (propName, element = document.body) { if (typeof propName !== 'string') { throw new TypeError('Expected a string as propName') } if (!(element instanceof Element)) { throw new TypeError('Expected a DOM element') } return getComputedStyle(element).getPropertyValue(`--q-${ propName }`).trim() || null } let metaValue; function getProp () { return client.is.winphone ? 'msapplication-navbutton-color' : ( client.is.safari ? 'apple-mobile-web-app-status-bar-style' : 'theme-color' // Chrome, Firefox OS, Opera, Vivaldi, ... ) } function getMetaTag (v) { const els = document.getElementsByTagName('META'); for (const i in els) { if (els[ i ].name === v) { return els[ i ] } } } function setColor (hexColor) { if (metaValue === void 0) { // cache it metaValue = getProp(); } let metaTag = getMetaTag(metaValue); const newTag = metaTag === void 0; if (newTag) { metaTag = document.createElement('meta'); metaTag.setAttribute('name', metaValue); } metaTag.setAttribute('content', hexColor); if (newTag) { document.head.appendChild(metaTag); } } var AddressbarColor = { set: client.is.mobile === true && ( client.is.nativeMobile === true || client.is.winphone === true || client.is.safari === true || client.is.webkit === true || client.is.vivaldi === true ) ? hexColor => { const val = hexColor || getCssVar('primary'); if (client.is.nativeMobile === true && window.StatusBar) { window.StatusBar.backgroundColorByHexString(val); } else { setColor(val); } } : noop, install ({ $q }) { $q.addressbarColor = this; $q.config.addressbarColor && this.set($q.config.addressbarColor); } }; const prefixes = {}; function assignFn (fn) { Object.assign(Plugin$6, { request: fn, exit: fn, toggle: fn }); } function getFullscreenElement () { return ( document.fullscreenElement || document.mozFullScreenElement || document.webkitFullscreenElement || document.msFullscreenElement || null ) } function updateEl () { const newEl = Plugin$6.activeEl = Plugin$6.isActive === false ? null : getFullscreenElement(); changeGlobalNodesTarget( newEl === null || newEl === document.documentElement ? document.body : newEl ); } function togglePluginState () { Plugin$6.isActive = Plugin$6.isActive === false; updateEl(); } // needed for consistency across browsers function promisify (target, fn) { try { const res = target[ fn ](); return res === void 0 ? Promise.resolve() : res } catch (err) { return Promise.reject(err) } } const Plugin$6 = defineReactivePlugin({ isActive: false, activeEl: null }, { isCapable: false, install ({ $q }) { $q.fullscreen = this; } }); { prefixes.request = [ 'requestFullscreen', 'msRequestFullscreen', 'mozRequestFullScreen', 'webkitRequestFullscreen' ].find(request => document.documentElement[ request ] !== void 0); Plugin$6.isCapable = prefixes.request !== void 0; if (Plugin$6.isCapable === false) { // it means the browser does NOT support it assignFn(() => Promise.reject('Not capable')); } else { Object.assign(Plugin$6, { request (target) { const el = target || document.documentElement; const { activeEl } = Plugin$6; if (el === activeEl) { return Promise.resolve() } const queue = activeEl !== null && el.contains(activeEl) === true ? Plugin$6.exit() : Promise.resolve(); return queue.finally(() => promisify(el, prefixes.request)) }, exit () { return Plugin$6.isActive === true ? promisify(document, prefixes.exit) : Promise.resolve() }, toggle (target) { return Plugin$6.isActive === true ? Plugin$6.exit() : Plugin$6.request(target) } }); prefixes.exit = [ 'exitFullscreen', 'msExitFullscreen', 'mozCancelFullScreen', 'webkitExitFullscreen' ].find(exit => document[ exit ]); Plugin$6.isActive = Boolean(getFullscreenElement()); Plugin$6.isActive === true && updateEl() ;[ 'onfullscreenchange', 'onmsfullscreenchange', 'onwebkitfullscreenchange' ].forEach(evt => { document[ evt ] = togglePluginState; }); } } const Plugin$5 = defineReactivePlugin({ appVisible: true }, { install ({ $q }) { injectProp($q, 'appVisible', () => this.appVisible); } }); { let prop, evt; if (typeof document.hidden !== 'undefined') { // Opera 12.10 and Firefox 18 and later support prop = 'hidden'; evt = 'visibilitychange'; } else if (typeof document.msHidden !== 'undefined') { prop = 'msHidden'; evt = 'msvisibilitychange'; } else if (typeof document.webkitHidden !== 'undefined') { prop = 'webkitHidden'; evt = 'webkitvisibilitychange'; } if (evt && typeof document[ prop ] !== 'undefined') { const update = () => { Plugin$5.appVisible = !document[ prop ]; }; document.addEventListener(evt, update, false); } } var BottomSheet$1 = createComponent({ name: 'BottomSheetPlugin', props: { ...useDarkProps, title: String, message: String, actions: Array, grid: Boolean, cardClass: [ String, Array, Object ], cardStyle: [ String, Array, Object ] }, emits: [ 'ok', 'hide' ], setup (props, { emit }) { const { proxy } = vue.getCurrentInstance(); const isDark = useDark(props, proxy.$q); const dialogRef = vue.ref(null); function show () { dialogRef.value.show(); } function hide () { dialogRef.value.hide(); } function onOk (action) { emit('ok', action); hide(); } function onHide () { emit('hide'); } function getGrid () { return props.actions.map(action => { const img = action.avatar || action.img; return action.label === void 0 ? vue.h(QSeparator, { class: 'col-all', dark: isDark.value }) : vue.h('div', { class: [ 'q-bottom-sheet__item q-hoverable q-focusable cursor-pointer relative-position', action.class ], style: action.style, tabindex: 0, role: 'listitem', onClick () { onOk(action); }, onKeyup (e) { e.keyCode === 13 && onOk(action); } }, [ vue.h('div', { class: 'q-focus-helper' }), action.icon ? vue.h(QIcon, { name: action.icon, color: action.color }) : ( img ? vue.h('img', { class: action.avatar ? 'q-bottom-sheet__avatar' : '', src: img }) : vue.h('div', { class: 'q-bottom-sheet__empty-icon' }) ), vue.h('div', action.label) ]) }) } function getList () { return props.actions.map(action => { const img = action.avatar || action.img; return action.label === void 0 ? vue.h(QSeparator, { spaced: true, dark: isDark.value }) : vue.h(QItem, { class: [ 'q-bottom-sheet__item', action.classes ], style: action.style, tabindex: 0, clickable: true, dark: isDark.value, onClick () { onOk(action); } }, () => [ vue.h( QItemSection, { avatar: true }, () => ( action.icon ? vue.h(QIcon, { name: action.icon, color: action.color }) : ( img ? vue.h('img', { class: action.avatar ? 'q-bottom-sheet__avatar' : '', src: img }) : null ) ) ), vue.h(QItemSection, () => action.label) ]) }) } function getCardContent () { const child = []; props.title && child.push( vue.h(QCardSection, { class: 'q-dialog__title' }, () => props.title) ); props.message && child.push( vue.h(QCardSection, { class: 'q-dialog__message' }, () => props.message) ); child.push( props.grid === true ? vue.h('div', { class: 'row items-stretch justify-start', role: 'list' }, getGrid()) : vue.h('div', { role: 'list' }, getList()) ); return child } function getContent () { return [ vue.h(QCard, { class: [ `q-bottom-sheet q-bottom-sheet--${ props.grid === true ? 'grid' : 'list' }` + (isDark.value === true ? ' q-bottom-sheet--dark q-dark' : ''), props.cardClass ], style: props.cardStyle }, getCardContent) ] } // expose public methods Object.assign(proxy, { show, hide }); return () => vue.h(QDialog, { ref: dialogRef, position: 'bottom', onHide }, getContent) } }); function merge (target, source) { for (const key in source) { if (key !== 'spinner' && Object(source[ key ]) === source[ key ]) { target[ key ] = Object(target[ key ]) !== target[ key ] ? {} : { ...target[ key ] }; merge(target[ key ], source[ key ]); } else { target[ key ] = source[ key ]; } } } function globalDialog (DefaultComponent, supportsCustomComponent, parentApp) { return pluginProps => { let DialogComponent, props; const isCustom = supportsCustomComponent === true && pluginProps.component !== void 0; if (isCustom === true) { const { component, componentProps } = pluginProps; DialogComponent = (typeof component === 'string') ? parentApp.component(component) : component; props = componentProps || {}; } else { const { class: klass, style, ...otherProps } = pluginProps; DialogComponent = DefaultComponent; props = otherProps; klass !== void 0 && (otherProps.cardClass = klass); style !== void 0 && (otherProps.cardStyle = style); } let vm, emittedOK = false; const dialogRef = vue.ref(null); const el = createGlobalNode(false, 'dialog'); const applyState = cmd => { if (dialogRef.value !== null && dialogRef.value[ cmd ] !== void 0) { dialogRef.value[ cmd ](); return } const target = vm.$.subTree; if (target && target.component) { // account for "script setup" way of declaring component if (target.component.proxy && target.component.proxy[ cmd ]) { target.component.proxy[ cmd ](); return } // account for "script setup" + async component way of declaring component if ( target.component.subTree && target.component.subTree.component && target.component.subTree.component.proxy && target.component.subTree.component.proxy[ cmd ] ) { target.component.subTree.component.proxy[ cmd ](); return } } console.error('[Quasar] Incorrectly defined Dialog component'); }; const okFns = [], cancelFns = [], API = { onOk (fn) { okFns.push(fn); return API }, onCancel (fn) { cancelFns.push(fn); return API }, onDismiss (fn) { okFns.push(fn); cancelFns.push(fn); return API }, hide () { applyState('hide'); return API }, update (componentProps) { if (vm !== null) { if (isCustom === true) { Object.assign(props, componentProps); } else { const { class: klass, style, ...cfg } = componentProps; klass !== void 0 && (cfg.cardClass = klass); style !== void 0 && (cfg.cardStyle = style); merge(props, cfg); } vm.$forceUpdate(); } return API } }; const onOk = data => { emittedOK = true; okFns.forEach(fn => { fn(data); }); }; const onHide = () => { app.unmount(el); removeGlobalNode(el); app = null; vm = null; if (emittedOK !== true) { cancelFns.forEach(fn => { fn(); }); } }; let app = createChildApp({ name: 'QGlobalDialog', setup: () => () => vue.h(DialogComponent, { ...props, ref: dialogRef, onOk, onHide, onVnodeMounted (...args) { if (typeof props.onVnodeMounted === 'function') { props.onVnodeMounted(...args); } vue.nextTick(() => applyState('show')); } }) }, parentApp); vm = app.mount(el); return API } } var BottomSheet = { install ({ $q, parentApp }) { $q.bottomSheet = globalDialog(BottomSheet$1, false, parentApp); if (this.__installed !== true) { this.create = $q.bottomSheet; } } }; function encode$1 (string) { return encodeURIComponent(string) } function decode$1 (string) { return decodeURIComponent(string) } function stringifyCookieValue (value) { return encode$1(value === Object(value) ? JSON.stringify(value) : '' + value) } function read (string) { if (string === '') { return string } if (string.indexOf('"') === 0) { // This is a quoted cookie as according to RFC2068, unescape... string = string.slice(1, -1).replace(/\\"/g, '"').replace(/\\\\/g, '\\'); } // Replace server-side written pluses with spaces. // If we can't decode the cookie, ignore it, it's unusable. // If we can't parse the cookie, ignore it, it's unusable. string = decode$1(string.replace(/\+/g, ' ')); try { const parsed = JSON.parse(string); if (parsed === Object(parsed) || Array.isArray(parsed) === true) { string = parsed; } } catch (e) {} return string } function getString (msOffset) { const time = new Date(); time.setMilliseconds(time.getMilliseconds() + msOffset); return time.toUTCString() } function parseExpireString (str) { let timestamp = 0; const days = str.match(/(\d+)d/); const hours = str.match(/(\d+)h/); const minutes = str.match(/(\d+)m/); const seconds = str.match(/(\d+)s/); if (days) { timestamp += days[ 1 ] * 864e+5; } if (hours) { timestamp += hours[ 1 ] * 36e+5; } if (minutes) { timestamp += minutes[ 1 ] * 6e+4; } if (seconds) { timestamp += seconds[ 1 ] * 1000; } return timestamp === 0 ? str : getString(timestamp) } function set (key, val, opts = {}, ssr) { let expire, expireValue; if (opts.expires !== void 0) { // if it's a Date Object if (Object.prototype.toString.call(opts.expires) === '[object Date]') { expire = opts.expires.toUTCString(); } // if it's a String (eg. "15m", "1h", "13d", "1d 15m", "31s") // possible units: d (days), h (hours), m (minutes), s (seconds) else if (typeof opts.expires === 'string') { expire = parseExpireString(opts.expires); } // otherwise it must be a Number (defined in days) else { expireValue = parseFloat(opts.expires); expire = isNaN(expireValue) === false ? getString(expireValue * 864e+5) : opts.expires; } } const keyValue = `${ encode$1(key) }=${ stringifyCookieValue(val) }`; const cookie = [ keyValue, expire !== void 0 ? '; Expires=' + expire : '', // use expires attribute, max-age is not supported by IE opts.path ? '; Path=' + opts.path : '', opts.domain ? '; Domain=' + opts.domain : '', opts.sameSite ? '; SameSite=' + opts.sameSite : '', opts.httpOnly ? '; HttpOnly' : '', opts.secure ? '; Secure' : '', opts.other ? '; ' + opts.other : '' ].join(''); if (ssr) { if (ssr.req.qCookies) { ssr.req.qCookies.push(cookie); } else { ssr.req.qCookies = [ cookie ]; } ssr.res.setHeader('Set-Cookie', ssr.req.qCookies); // make temporary update so future get() // within same SSR timeframe would return the set value let all = ssr.req.headers.cookie || ''; if (expire !== void 0 && expireValue < 0) { const val = get(key, ssr); if (val !== undefined) { all = all .replace(`${ key }=${ val }; `, '') .replace(`; ${ key }=${ val }`, '') .replace(`${ key }=${ val }`, ''); } } else { all = all ? `${ keyValue }; ${ all }` : cookie; } ssr.req.headers.cookie = all; } else { document.cookie = cookie; } } function get (key, ssr) { const cookieSource = ssr ? ssr.req.headers : document, cookies = cookieSource.cookie ? cookieSource.cookie.split('; ') : [], l = cookies.length; let result = key ? null : {}, i = 0, parts, name, cookie; for (; i < l; i++) { parts = cookies[ i ].split('='); name = decode$1(parts.shift()); cookie = parts.join('='); if (!key) { result[ name ] = cookie; } else if (key === name) { result = read(cookie); break } } return result } function remove (key, options, ssr) { set( key, '', { expires: -1, ...options }, ssr ); } function has (key, ssr) { return get(key, ssr) !== null } function getObject (ssr) { return { get: key => get(key, ssr), set: (key, val, opts) => set(key, val, opts, ssr), has: key => has(key, ssr), remove: (key, options) => remove(key, options, ssr), getAll: () => get(null, ssr) } } const Plugin$4 = { install ({ $q, ssrContext }) { $q.cookies = this; } }; { Object.assign(Plugin$4, getObject()); } var DialogPlugin = createComponent({ name: 'DialogPlugin', props: { ...useDarkProps, title: String, message: String, prompt: Object, options: Object, progress: [ Boolean, Object ], html: Boolean, ok: { type: [ String, Object, Boolean ], default: true }, cancel: [ String, Object, Boolean ], focus: { type: String, default: 'ok', validator: v => [ 'ok', 'cancel', 'none' ].includes(v) }, stackButtons: Boolean, color: String, cardClass: [ String, Array, Object ], cardStyle: [ String, Array, Object ] }, emits: [ 'ok', 'hide' ], setup (props, { emit }) { const { proxy } = vue.getCurrentInstance(); const { $q } = proxy; const isDark = useDark(props, $q); const dialogRef = vue.ref(null); const model = vue.ref( props.prompt !== void 0 ? props.prompt.model : (props.options !== void 0 ? props.options.model : void 0) ); const classes = vue.computed(() => 'q-dialog-plugin' + (isDark.value === true ? ' q-dialog-plugin--dark q-dark' : '') + (props.progress !== false ? ' q-dialog-plugin--progress' : '') ); const vmColor = vue.computed(() => props.color || (isDark.value === true ? 'amber' : 'primary') ); const spinner = vue.computed(() => ( props.progress === false ? null : ( isObject(props.progress) === true ? { component: props.progress.spinner || QSpinner, props: { color: props.progress.color || vmColor.value } } : { component: QSpinner, props: { color: vmColor.value } } ) )); const hasForm = vue.computed(() => props.prompt !== void 0 || props.options !== void 0 ); const formProps = vue.computed(() => { if (hasForm.value !== true) { return {} } const { model, isValid, items, ...formProps } = props.prompt !== void 0 ? props.prompt : props.options; return formProps }); const okLabel = vue.computed(() => ( isObject(props.ok) === true ? $q.lang.label.ok : ( props.ok === true ? $q.lang.label.ok : props.ok ) )); const cancelLabel = vue.computed(() => ( isObject(props.cancel) === true ? $q.lang.label.cancel : ( props.cancel === true ? $q.lang.label.cancel : props.cancel ) )); const okDisabled = vue.computed(() => { if (props.prompt !== void 0) { return props.prompt.isValid !== void 0 && props.prompt.isValid(model.value) !== true } if (props.options !== void 0) { return props.options.isValid !== void 0 && props.options.isValid(model.value) !== true } return false }); const okProps = vue.computed(() => ({ color: vmColor.value, label: okLabel.value, ripple: false, disable: okDisabled.value, ...(isObject(props.ok) === true ? props.ok : { flat: true }), 'data-autofocus': (props.focus === 'ok' && hasForm.value !== true) || void 0, onClick: onOk })); const cancelProps = vue.computed(() => ({ color: vmColor.value, label: cancelLabel.value, ripple: false, ...(isObject(props.cancel) === true ? props.cancel : { flat: true }), 'data-autofocus': (props.focus === 'cancel' && hasForm.value !== true) || void 0, onClick: onCancel })); vue.watch(() => props.prompt && props.prompt.model, onUpdateModel); vue.watch(() => props.options && props.options.model, onUpdateModel); function show () { dialogRef.value.show(); } function hide () { dialogRef.value.hide(); } function onOk () { emit('ok', vue.toRaw(model.value)); hide(); } function onCancel () { hide(); } function onDialogHide () { emit('hide'); } function onUpdateModel (val) { model.value = val; } function onInputKeyup (evt) { // if ENTER key if ( okDisabled.value !== true && props.prompt.type !== 'textarea' && isKeyCode(evt, 13) === true ) { onOk(); } } function getSection (classes, text) { return props.html === true ? vue.h(QCardSection, { class: classes, innerHTML: text }) : vue.h(QCardSection, { class: classes }, () => text) } function getPrompt () { return [ vue.h(QInput, { color: vmColor.value, dense: true, autofocus: true, dark: isDark.value, ...formProps.value, modelValue: model.value, 'onUpdate:modelValue': onUpdateModel, onKeyup: onInputKeyup }) ] } function getOptions () { return [ vue.h(QOptionGroup, { color: vmColor.value, options: props.options.items, dark: isDark.value, ...formProps.value, modelValue: model.value, 'onUpdate:modelValue': onUpdateModel }) ] } function getButtons () { const child = []; props.cancel && child.push( vue.h(QBtn, cancelProps.value) ); props.ok && child.push( vue.h(QBtn, okProps.value) ); return vue.h(QCardActions, { class: props.stackButtons === true ? 'items-end' : '', vertical: props.stackButtons, align: 'right' }, () => child) } function getCardContent () { const child = []; props.title && child.push( getSection('q-dialog__title', props.title) ); props.progress !== false && child.push( vue.h( QCardSection, { class: 'q-dialog__progress' }, () => vue.h(spinner.value.component, spinner.value.props) ) ); props.message && child.push( getSection('q-dialog__message', props.message) ); if (props.prompt !== void 0) { child.push( vue.h( QCardSection, { class: 'scroll q-dialog-plugin__form' }, getPrompt ) ); } else if (props.options !== void 0) { child.push( vue.h(QSeparator, { dark: isDark.value }), vue.h( QCardSection, { class: 'scroll q-dialog-plugin__form' }, getOptions ), vue.h(QSeparator, { dark: isDark.value }) ); } if (props.ok || props.cancel) { child.push(getButtons()); } return child } function getContent () { return [ vue.h(QCard, { class: [ classes.value, props.cardClass ], style: props.cardStyle, dark: isDark.value }, getCardContent) ] } // expose public methods Object.assign(proxy, { show, hide }); return () => vue.h(QDialog, { ref: dialogRef, onHide: onDialogHide }, getContent) } }); var Dialog = { install ({ $q, parentApp }) { $q.dialog = globalDialog(DialogPlugin, true, parentApp); if (this.__installed !== true) { this.create = $q.dialog; } } }; const barRef = vue.ref(null); const Plugin$3 = defineReactivePlugin({ isActive: false }, { start: noop, stop: noop, increment: noop, setDefaults: noop, install ({ $q, parentApp }) { $q.loadingBar = this; if (this.__installed === true) { if ($q.config.loadingBar !== void 0) { this.setDefaults($q.config.loadingBar); } return } const props = vue.ref( $q.config.loadingBar !== void 0 ? { ...$q.config.loadingBar } : {} ); function onStart () { Plugin$3.isActive = true; } function onStop () { Plugin$3.isActive = false; } const el = createGlobalNode('q-loading-bar'); createChildApp({ name: 'LoadingBar', // hide App from Vue devtools devtools: { hide: true }, setup: () => () => vue.h(QAjaxBar, { ...props.value, onStart, onStop, ref: barRef }) }, parentApp).mount(el); Object.assign(this, { start (speed) { barRef.value.start(speed); }, stop () { barRef.value.stop(); }, increment () { barRef.value.increment.apply(null, arguments); }, setDefaults (opts) { if (isObject(opts) === true) { Object.assign(props.value, opts); } } }); } }); let app, vm, uid$1 = 0, timeout = null, props = {}, activeGroups = {}; const originalDefaults = { group: '__default_quasar_group__', delay: 0, message: false, html: false, spinnerSize: 80, spinnerColor: '', messageColor: '', backgroundColor: '', boxClass: '', spinner: QSpinner, customClass: '' }; const defaults$1 = { ...originalDefaults }; function registerProps (opts) { if (opts && opts.group !== void 0 && activeGroups[ opts.group ] !== void 0) { return Object.assign(activeGroups[ opts.group ], opts) } const newProps = isObject(opts) === true && opts.ignoreDefaults === true ? { ...originalDefaults, ...opts } : { ...defaults$1, ...opts }; activeGroups[ newProps.group ] = newProps; return newProps } const Plugin$2 = defineReactivePlugin({ isActive: false }, { show (opts) { props = registerProps(opts); const { group } = props; Plugin$2.isActive = true; if (app !== void 0) { props.uid = uid$1; vm.$forceUpdate(); } else { props.uid = ++uid$1; timeout !== null && clearTimeout(timeout); timeout = setTimeout(() => { timeout = null; const el = createGlobalNode('q-loading'); app = createChildApp({ name: 'QLoading', setup () { vue.onMounted(() => { preventScroll(true); }); function onAfterLeave () { // might be called to finalize // previous leave, even if it was cancelled if (Plugin$2.isActive !== true && app !== void 0) { preventScroll(false); app.unmount(el); removeGlobalNode(el); app = void 0; vm = void 0; } } function getContent () { if (Plugin$2.isActive !== true) { return null } const content = [ vue.h(props.spinner, { class: 'q-loading__spinner', color: props.spinnerColor, size: props.spinnerSize }) ]; props.message && content.push( vue.h('div', { class: 'q-loading__message' + (props.messageColor ? ` text-${ props.messageColor }` : ''), [ props.html === true ? 'innerHTML' : 'textContent' ]: props.message }) ); return vue.h('div', { class: 'q-loading fullscreen flex flex-center z-max ' + props.customClass.trim(), key: props.uid }, [ vue.h('div', { class: 'q-loading__backdrop' + (props.backgroundColor ? ` bg-${ props.backgroundColor }` : '') }), vue.h('div', { class: 'q-loading__box column items-center ' + props.boxClass }, content) ]) } return () => vue.h(vue.Transition, { name: 'q-transition--fade', appear: true, onAfterLeave }, getContent) } }, Plugin$2.__parentApp); vm = app.mount(el); }, props.delay); } return paramProps => { // if we don't have params (or not an Object param) then we need to hide this group if (paramProps === void 0 || Object(paramProps) !== paramProps) { Plugin$2.hide(group); return } // else we have params so we need to update this group Plugin$2.show({ ...paramProps, group }); } }, hide (group) { if (Plugin$2.isActive === true) { if (group === void 0) { // clear out any active groups activeGroups = {}; } else if (activeGroups[ group ] === void 0) { // we've already hidden it so nothing to do return } else { // remove active group delete activeGroups[ group ]; const keys = Object.keys(activeGroups); // if there are other groups registered then // show last registered one since that one is still active if (keys.length !== 0) { // get last registered group const lastGroup = keys[ keys.length - 1 ]; Plugin$2.show({ group: lastGroup }); return } } if (timeout !== null) { clearTimeout(timeout); timeout = null; } Plugin$2.isActive = false; } }, setDefaults (opts) { { isObject(opts) === true && Object.assign(defaults$1, opts); } }, install ({ $q, parentApp }) { $q.loading = this; { Plugin$2.__parentApp = parentApp; if ($q.config.loading !== void 0) { this.setDefaults($q.config.loading); } } } }); let updateId = null, currentClientMeta; const clientList = []; function normalize (meta) { if (meta.title) { meta.title = meta.titleTemplate ? meta.titleTemplate(meta.title) : meta.title; delete meta.titleTemplate; } [ [ 'meta', 'content' ], [ 'link', 'href' ] ].forEach(type => { const metaType = meta[ type[ 0 ] ], metaProp = type[ 1 ]; for (const name in metaType) { const metaLink = metaType[ name ]; if (metaLink.template) { if (Object.keys(metaLink).length === 1) { delete metaType[ name ]; } else { metaLink[ metaProp ] = metaLink.template(metaLink[ metaProp ] || ''); delete metaLink.template; } } } }); } function changed (old, def) { if (Object.keys(old).length !== Object.keys(def).length) { return true } for (const key in old) { if (old[ key ] !== def[ key ]) { return true } } } function bodyFilter (name) { return [ 'class', 'style' ].includes(name) === false } function htmlFilter (name) { return [ 'lang', 'dir' ].includes(name) === false } function diff (meta, other) { const add = {}, remove = {}; if (meta === void 0) { return { add: other, remove } } if (meta.title !== other.title) { add.title = other.title; } [ 'meta', 'link', 'script', 'htmlAttr', 'bodyAttr' ].forEach(type => { const old = meta[ type ], cur = other[ type ]; remove[ type ] = []; if (old === void 0 || old === null) { add[ type ] = cur; return } add[ type ] = {}; for (const key in old) { if (cur.hasOwnProperty(key) === false) { remove[ type ].push(key); } } for (const key in cur) { if (old.hasOwnProperty(key) === false) { add[ type ][ key ] = cur[ key ]; } else if (changed(old[ key ], cur[ key ]) === true) { remove[ type ].push(key); add[ type ][ key ] = cur[ key ]; } } }); return { add, remove } } function apply ({ add, remove }) { if (add.title) { document.title = add.title; } if (Object.keys(remove).length !== 0) { [ 'meta', 'link', 'script' ].forEach(type => { remove[ type ].forEach(name => { document.head.querySelector(`${ type }[data-qmeta="${ name }"]`).remove(); }); }); remove.htmlAttr.filter(htmlFilter).forEach(name => { document.documentElement.removeAttribute(name); }); remove.bodyAttr.filter(bodyFilter).forEach(name => { document.body.removeAttribute(name); }); } [ 'meta', 'link', 'script' ].forEach(type => { const metaType = add[ type ]; for (const name in metaType) { const tag = document.createElement(type); for (const att in metaType[ name ]) { if (att !== 'innerHTML') { tag.setAttribute(att, metaType[ name ][ att ]); } } tag.setAttribute('data-qmeta', name); if (type === 'script') { tag.innerHTML = metaType[ name ].innerHTML || ''; } document.head.appendChild(tag); } }); Object.keys(add.htmlAttr).filter(htmlFilter).forEach(name => { document.documentElement.setAttribute(name, add.htmlAttr[ name ] || ''); }); Object.keys(add.bodyAttr).filter(bodyFilter).forEach(name => { document.body.setAttribute(name, add.bodyAttr[ name ] || ''); }); } function updateClientMeta () { updateId = null; const data = { title: '', titleTemplate: null, meta: {}, link: {}, script: {}, htmlAttr: {}, bodyAttr: {} }; for (let i = 0; i < clientList.length; i++) { const { active, val } = clientList[ i ]; if (active === true) { extend(true, data, val); } } normalize(data); apply(diff(currentClientMeta, data)); currentClientMeta = data; } function planClientUpdate () { updateId !== null && clearTimeout(updateId); updateId = setTimeout(updateClientMeta, 50); } var Meta = { install (opts) { if (this.__installed !== true && isRuntimeSsrPreHydration.value === true) { currentClientMeta = window.__Q_META__; document.getElementById('qmeta-init').remove(); } } }; let uid = 0; const defaults = {}; const groups = {}; const notificationsList = {}; const positionClass = {}; const emptyRE = /^\s*$/; const notifRefs = []; const positionList = [ 'top-left', 'top-right', 'bottom-left', 'bottom-right', 'top', 'bottom', 'left', 'right', 'center' ]; const badgePositions = [ 'top-left', 'top-right', 'bottom-left', 'bottom-right' ]; const notifTypes = { positive: { icon: $q => $q.iconSet.type.positive, color: 'positive' }, negative: { icon: $q => $q.iconSet.type.negative, color: 'negative' }, warning: { icon: $q => $q.iconSet.type.warning, color: 'warning', textColor: 'dark' }, info: { icon: $q => $q.iconSet.type.info, color: 'info' }, ongoing: { group: false, timeout: 0, spinner: true, color: 'grey-8' } }; function addNotification (config, $q, originalApi) { if (!config) { return logError('parameter required') } let Api; const notif = { textColor: 'white' }; if (config.ignoreDefaults !== true) { Object.assign(notif, defaults); } if (isObject(config) === false) { if (notif.type) { Object.assign(notif, notifTypes[ notif.type ]); } config = { message: config }; } Object.assign(notif, notifTypes[ config.type || notif.type ], config); if (typeof notif.icon === 'function') { notif.icon = notif.icon($q); } if (!notif.spinner) { notif.spinner = false; } else { if (notif.spinner === true) { notif.spinner = QSpinner; } notif.spinner = vue.markRaw(notif.spinner); } notif.meta = { hasMedia: Boolean(notif.spinner !== false || notif.icon || notif.avatar), hasText: hasContent(notif.message) || hasContent(notif.caption) }; if (notif.position) { if (positionList.includes(notif.position) === false) { return logError('wrong position', config) } } else { notif.position = 'bottom'; } if (notif.timeout === void 0) { notif.timeout = 5000; } else { const t = parseInt(notif.timeout, 10); if (isNaN(t) || t < 0) { return logError('wrong timeout', config) } notif.timeout = t; } if (notif.timeout === 0) { notif.progress = false; } else if (notif.progress === true) { notif.meta.progressClass = 'q-notification__progress' + ( notif.progressClass ? ` ${ notif.progressClass }` : '' ); notif.meta.progressStyle = { animationDuration: `${ notif.timeout + 1000 }ms` }; } const actions = ( Array.isArray(config.actions) === true ? config.actions : [] ).concat( config.ignoreDefaults !== true && Array.isArray(defaults.actions) === true ? defaults.actions : [] ).concat( notifTypes[ config.type ] !== void 0 && Array.isArray(notifTypes[ config.type ].actions) === true ? notifTypes[ config.type ].actions : [] ); const { closeBtn } = notif; closeBtn && actions.push({ label: typeof closeBtn === 'string' ? closeBtn : $q.lang.label.close }); notif.actions = actions.map(({ handler, noDismiss, ...item }) => ({ flat: true, ...item, onClick: typeof handler === 'function' ? () => { handler(); noDismiss !== true && dismiss(); } : () => { dismiss(); } })); if (notif.multiLine === void 0) { notif.multiLine = notif.actions.length > 1; } Object.assign(notif.meta, { class: 'q-notification row items-stretch' + ` q-notification--${ notif.multiLine === true ? 'multi-line' : 'standard' }` + (notif.color !== void 0 ? ` bg-${ notif.color }` : '') + (notif.textColor !== void 0 ? ` text-${ notif.textColor }` : '') + (notif.classes !== void 0 ? ` ${ notif.classes }` : ''), wrapperClass: 'q-notification__wrapper col relative-position border-radius-inherit ' + (notif.multiLine === true ? 'column no-wrap justify-center' : 'row items-center'), contentClass: 'q-notification__content row items-center' + (notif.multiLine === true ? '' : ' col'), leftClass: notif.meta.hasText === true ? 'additional' : 'single', attrs: { role: 'alert', ...notif.attrs } }); if (notif.group === false) { notif.group = void 0; notif.meta.group = void 0; } else { if (notif.group === void 0 || notif.group === true) { // do not replace notifications with different buttons notif.group = [ notif.message, notif.caption, notif.multiline ].concat( notif.actions.map(props => `${ props.label }*${ props.icon }`) ).join('|'); } notif.meta.group = notif.group + '|' + notif.position; } if (notif.actions.length === 0) { notif.actions = void 0; } else { notif.meta.actionsClass = 'q-notification__actions row items-center ' + (notif.multiLine === true ? 'justify-end' : 'col-auto') + (notif.meta.hasMedia === true ? ' q-notification__actions--with-media' : ''); } if (originalApi !== void 0) { // reset timeout if any if (originalApi.notif.meta.timer) { clearTimeout(originalApi.notif.meta.timer); originalApi.notif.meta.timer = void 0; } // retain uid notif.meta.uid = originalApi.notif.meta.uid; // replace notif const index = notificationsList[ notif.position ].value.indexOf(originalApi.notif); notificationsList[ notif.position ].value[ index ] = notif; } else { const original = groups[ notif.meta.group ]; // woohoo, it's a new notification if (original === void 0) { notif.meta.uid = uid++; notif.meta.badge = 1; if ([ 'left', 'right', 'center' ].indexOf(notif.position) !== -1) { notificationsList[ notif.position ].value.splice( Math.floor(notificationsList[ notif.position ].value.length / 2), 0, notif ); } else { const action = notif.position.indexOf('top') > -1 ? 'unshift' : 'push'; notificationsList[ notif.position ].value[ action ](notif); } if (notif.group !== void 0) { groups[ notif.meta.group ] = notif; } } // ok, so it's NOT a new one else { // reset timeout if any if (original.meta.timer) { clearTimeout(original.meta.timer); original.meta.timer = void 0; } if (notif.badgePosition !== void 0) { if (badgePositions.includes(notif.badgePosition) === false) { return logError('wrong badgePosition', config) } } else { notif.badgePosition = `top-${ notif.position.indexOf('left') > -1 ? 'right' : 'left' }`; } notif.meta.uid = original.meta.uid; notif.meta.badge = original.meta.badge + 1; notif.meta.badgeClass = `q-notification__badge q-notification__badge--${ notif.badgePosition }` + (notif.badgeColor !== void 0 ? ` bg-${ notif.badgeColor }` : '') + (notif.badgeTextColor !== void 0 ? ` text-${ notif.badgeTextColor }` : '') + (notif.badgeClass ? ` ${ notif.badgeClass }` : ''); const index = notificationsList[ notif.position ].value.indexOf(original); notificationsList[ notif.position ].value[ index ] = groups[ notif.meta.group ] = notif; } } const dismiss = () => { removeNotification(notif); Api = void 0; }; if (notif.timeout > 0) { notif.meta.timer = setTimeout(() => { notif.meta.timer = void 0; dismiss(); }, notif.timeout + /* show duration */ 1000); } // only non-groupable can be updated if (notif.group !== void 0) { return props => { if (props !== void 0) { logError('trying to update a grouped one which is forbidden', config); } else { dismiss(); } } } Api = { dismiss, config, notif }; if (originalApi !== void 0) { Object.assign(originalApi, Api); return } return props => { // if notification wasn't previously dismissed if (Api !== void 0) { // if no params, then we must dismiss the notification if (props === void 0) { Api.dismiss(); } // otherwise we're updating it else { const newNotif = Object.assign({}, Api.config, props, { group: false, position: notif.position }); addNotification(newNotif, $q, Api); } } } } function removeNotification (notif) { if (notif.meta.timer) { clearTimeout(notif.meta.timer); notif.meta.timer = void 0; } const index = notificationsList[ notif.position ].value.indexOf(notif); if (index !== -1) { if (notif.group !== void 0) { delete groups[ notif.meta.group ]; } const el = notifRefs[ '' + notif.meta.uid ]; if (el) { const { width, height } = getComputedStyle(el); el.style.left = `${ el.offsetLeft }px`; el.style.width = width; el.style.height = height; } notificationsList[ notif.position ].value.splice(index, 1); if (typeof notif.onDismiss === 'function') { notif.onDismiss(); } } } function hasContent (str) { return str !== void 0 && str !== null && emptyRE.test(str) !== true } function logError (error, config) { console.error(`Notify: ${ error }`, config); return false } function getComponent () { return createComponent({ name: 'QNotifications', // hide App from Vue devtools devtools: { hide: true }, setup () { return () => vue.h('div', { class: 'q-notifications' }, positionList.map(pos => { return vue.h(vue.TransitionGroup, { key: pos, class: positionClass[ pos ], tag: 'div', name: `q-notification--${ pos }` }, () => notificationsList[ pos ].value.map(notif => { const meta = notif.meta; const mainChild = []; if (meta.hasMedia === true) { if (notif.spinner !== false) { mainChild.push( vue.h(notif.spinner, { class: 'q-notification__spinner q-notification__spinner--' + meta.leftClass, color: notif.spinnerColor, size: notif.spinnerSize }) ); } else if (notif.icon) { mainChild.push( vue.h(QIcon, { class: 'q-notification__icon q-notification__icon--' + meta.leftClass, name: notif.icon, color: notif.iconColor, size: notif.iconSize, role: 'img' }) ); } else if (notif.avatar) { mainChild.push( vue.h(QAvatar, { class: 'q-notification__avatar q-notification__avatar--' + meta.leftClass }, () => vue.h('img', { src: notif.avatar, 'aria-hidden': 'true' })) ); } } if (meta.hasText === true) { let msgChild; const msgData = { class: 'q-notification__message col' }; if (notif.html === true) { msgData.innerHTML = notif.caption ? `
${ notif.message }
${ notif.caption }
` : notif.message; } else { const msgNode = [ notif.message ]; msgChild = notif.caption ? [ vue.h('div', msgNode), vue.h('div', { class: 'q-notification__caption' }, [ notif.caption ]) ] : msgNode; } mainChild.push( vue.h('div', msgData, msgChild) ); } const child = [ vue.h('div', { class: meta.contentClass }, mainChild) ]; notif.progress === true && child.push( vue.h('div', { key: `${ meta.uid }|p|${ meta.badge }`, class: meta.progressClass, style: meta.progressStyle }) ); notif.actions !== void 0 && child.push( vue.h('div', { class: meta.actionsClass }, notif.actions.map(props => vue.h(QBtn, props))) ); meta.badge > 1 && child.push( vue.h('div', { key: `${ meta.uid }|${ meta.badge }`, class: notif.meta.badgeClass, style: notif.badgeStyle }, [ meta.badge ]) ); return vue.h('div', { ref: el => { notifRefs[ '' + meta.uid ] = el; }, key: meta.uid, class: meta.class, ...meta.attrs }, [ vue.h('div', { class: meta.wrapperClass }, child) ]) })) })) } }) } var Notify = { setDefaults (opts) { { isObject(opts) === true && Object.assign(defaults, opts); } }, registerType (typeName, typeOpts) { if (isObject(typeOpts) === true) { notifTypes[ typeName ] = typeOpts; } }, install ({ $q, parentApp }) { $q.notify = this.create = opts => addNotification(opts, $q); $q.notify.setDefaults = this.setDefaults; $q.notify.registerType = this.registerType; if ($q.config.notify !== void 0) { this.setDefaults($q.config.notify); } if (this.__installed !== true) { positionList.forEach(pos => { notificationsList[ pos ] = vue.ref([]); const vert = [ 'left', 'center', 'right' ].includes(pos) === true ? 'center' : (pos.indexOf('top') > -1 ? 'top' : 'bottom'), align = pos.indexOf('left') > -1 ? 'start' : (pos.indexOf('right') > -1 ? 'end' : 'center'), classes = [ 'left', 'right' ].includes(pos) ? `items-${ pos === 'left' ? 'start' : 'end' } justify-center` : (pos === 'center' ? 'flex-center' : `items-${ align }`); positionClass[ pos ] = `q-notifications__list q-notifications__list--${ vert } fixed column no-wrap ${ classes }`; }); const el = createGlobalNode('q-notify'); createChildApp(getComponent(), parentApp).mount(el); } } }; function encode (value) { if (isDate(value) === true) { return '__q_date|' + value.toUTCString() } if (isRegexp(value) === true) { return '__q_expr|' + value.source } if (typeof value === 'number') { return '__q_numb|' + value } if (typeof value === 'boolean') { return '__q_bool|' + (value ? '1' : '0') } if (typeof value === 'string') { return '__q_strn|' + value } if (typeof value === 'function') { return '__q_strn|' + value.toString() } if (value === Object(value)) { return '__q_objt|' + JSON.stringify(value) } // hmm, we don't know what to do with it, // so just return it as is return value } function decode (value) { const length = value.length; if (length < 9) { // then it wasn't encoded by us return value } const type = value.substring(0, 8); const source = value.substring(9); switch (type) { case '__q_date': return new Date(source) case '__q_expr': return new RegExp(source) case '__q_numb': return Number(source) case '__q_bool': return Boolean(source === '1') case '__q_strn': return '' + source case '__q_objt': return JSON.parse(source) default: // hmm, we reached here, we don't know the type, // then it means it wasn't encoded by us, so just // return whatever value it is return value } } function getEmptyStorage () { const getVal = () => null; return { has: () => false, getLength: () => 0, getItem: getVal, getIndex: getVal, getKey: getVal, getAll: () => {}, getAllKeys: () => [], set: noop, remove: noop, clear: noop, isEmpty: () => true } } function getStorage (type) { const webStorage = window[ type + 'Storage' ], get = key => { const item = webStorage.getItem(key); return item ? decode(item) : null }; return { has: key => webStorage.getItem(key) !== null, getLength: () => webStorage.length, getItem: get, getIndex: index => { return index < webStorage.length ? get(webStorage.key(index)) : null }, getKey: index => { return index < webStorage.length ? webStorage.key(index) : null }, getAll: () => { let key; const result = {}, len = webStorage.length; for (let i = 0; i < len; i++) { key = webStorage.key(i); result[ key ] = get(key); } return result }, getAllKeys: () => { const result = [], len = webStorage.length; for (let i = 0; i < len; i++) { result.push(webStorage.key(i)); } return result }, set: (key, value) => { webStorage.setItem(key, encode(value)); }, remove: key => { webStorage.removeItem(key); }, clear: () => { webStorage.clear(); }, isEmpty: () => webStorage.length === 0 } } const storage$1 = client.has.webStorage === false ? getEmptyStorage() : getStorage('local'); const Plugin$1 = { install ({ $q }) { $q.localStorage = storage$1; } }; Object.assign(Plugin$1, storage$1); const storage = client.has.webStorage === false ? getEmptyStorage() : getStorage('session'); const Plugin = { install ({ $q }) { $q.sessionStorage = storage; } }; Object.assign(Plugin, storage); var plugins = /*#__PURE__*/Object.freeze({ __proto__: null, AddressbarColor: AddressbarColor, AppFullscreen: Plugin$6, AppVisibility: Plugin$5, BottomSheet: BottomSheet, Cookies: Plugin$4, Dark: Plugin$9, Dialog: Dialog, LoadingBar: Plugin$3, Loading: Plugin$2, Meta: Meta, Notify: Notify, Platform: Platform, Screen: Screen, LocalStorage: Plugin$1, SessionStorage: Plugin }); function fallback (text) { const area = document.createElement('textarea'); area.value = text; area.contentEditable = 'true'; area.style.position = 'fixed'; // avoid scrolling to bottom const fn = () => {}; addFocusout(fn); document.body.appendChild(area); area.focus(); area.select(); const res = document.execCommand('copy'); area.remove(); removeFocusout(fn); return res } function copyToClipboard (text) { return navigator.clipboard !== void 0 ? navigator.clipboard.writeText(text) : new Promise((resolve, reject) => { const res = fallback(text); if (res) { resolve(true); } else { reject(res); } }) } var createMetaMixin = metaOptions => { const mixin = { activated () { this.__qMeta.active = true; planClientUpdate(); }, deactivated () { this.__qMeta.active = false; planClientUpdate(); }, unmounted () { clientList.splice(clientList.indexOf(this.__qMeta), 1); planClientUpdate(); this.__qMeta = void 0; } }; if (typeof metaOptions === 'function') { Object.assign(mixin, { computed: { __qMetaOptions () { return metaOptions.call(this) || {} } }, watch: { __qMetaOptions (val) { this.__qMeta.val = val; this.__qMeta.active === true && planClientUpdate(); } }, created () { this.__qMeta = { active: true, val: this.__qMetaOptions }; clientList.push(this.__qMeta); planClientUpdate(); } }); } else { mixin.created = function () { this.__qMeta = { active: true, val: metaOptions }; clientList.push(this.__qMeta); planClientUpdate(); }; } return mixin }; /** * Forked from tiny-emitter * Copyright (c) 2017 Scott Corgan */ class EventBus { constructor () { this.__stack = {}; } on (name, callback, ctx) { (this.__stack[ name ] || (this.__stack[ name ] = [])).push({ fn: callback, ctx }); return this // chainable } once (name, callback, ctx) { const listener = () => { this.off(name, listener); callback.apply(ctx, arguments); }; listener.__callback = callback; return this.on(name, listener, ctx) // chainable } emit (name) { const list = this.__stack[ name ]; if (list !== void 0) { const params = [].slice.call(arguments, 1); list.forEach(entry => { entry.fn.apply(entry.ctx, params); }); } return this // chainable } off (name, callback) { const list = this.__stack[ name ]; if (list === void 0) { return this // chainable } if (callback === void 0) { delete this.__stack[ name ]; return this // chainable } const liveEvents = list.filter( entry => entry.fn !== callback && entry.fn.__callback !== callback ); if (liveEvents.length !== 0) { this.__stack[ name ] = liveEvents; } else { delete this.__stack[ name ]; } return this // chainable } } function clean (link) { // allow time for iOS setTimeout(() => { window.URL.revokeObjectURL(link.href); }, 10000); link.remove(); } /** * Forces browser to download file with specified content * * @param {*} fileName - String * @param {*} rawData - String | ArrayBuffer | ArrayBufferView | Blob * @param {*} opts - String (mimeType) or Object * Object form: { mimeType?: String, byteOrderMark?: String | Uint8Array, encoding?: String } * @returns Boolean | Error * * mimeType - Examples: 'application/octect-stream' (default), 'text/plain', 'application/json', * 'text/plain;charset=UTF-8', 'video/mp4', 'image/png', 'application/pdf' * https://developer.mozilla.org/en-US/docs/Web/HTTP/Basics_of_HTTP/MIME_types * * byteOrderMark - (BOM) Example: '\uFEFF' * https://en.wikipedia.org/wiki/Byte_order_mark * * encoding - Performs a TextEncoder.encode() over the rawData; * Example: 'windows-1252' (ANSI, a subset of ISO-8859-1) * https://developer.mozilla.org/en-US/docs/Web/API/TextEncoder */ function exportFile (fileName, rawData, opts = {}) { const { mimeType, byteOrderMark, encoding } = typeof opts === 'string' ? { mimeType: opts } : opts; const data = encoding !== void 0 ? (new TextEncoder(encoding)).encode([ rawData ]) : rawData; const blobData = byteOrderMark !== void 0 ? [ byteOrderMark, data ] : [ data ]; const blob = new Blob(blobData, { type: mimeType || 'application/octet-stream' }); const link = document.createElement('a'); link.href = window.URL.createObjectURL(blob); link.setAttribute('download', fileName); // Check for "download" attribute support; // If not supported, open this in new window if (typeof link.download === 'undefined') { link.setAttribute('target', '_blank'); } link.classList.add('hidden'); link.style.position = 'fixed'; // avoid scrolling to bottom document.body.appendChild(link); try { link.click(); clean(link); return true } catch (err) { clean(link); return err } } function parseFeatures (winFeatures) { const cfg = Object.assign({ noopener: true }, winFeatures); const feat = []; for (const key in cfg) { const value = cfg[ key ]; if (value === true) { feat.push(key); } else if (isNumber(value) || (typeof value === 'string' && value !== '')) { feat.push(key + '=' + value); } } return feat.join(',') } function openWindow (url, reject, windowFeatures) { let open = window.open; if (Platform.is.cordova === true) { if (cordova !== void 0 && cordova.InAppBrowser !== void 0 && cordova.InAppBrowser.open !== void 0) { open = cordova.InAppBrowser.open; } else if (navigator !== void 0 && navigator.app !== void 0) { return navigator.app.loadUrl(url, { openExternal: true }) } } const win = open(url, '_blank', parseFeatures(windowFeatures)); if (win) { Platform.is.desktop && win.focus(); return win } else { reject && reject(); } } var openUrl = (url, reject, windowFeatures) => { if ( Platform.is.ios === true && window.SafariViewController !== void 0 ) { window.SafariViewController.isAvailable(available => { if (available) { window.SafariViewController.show( { url }, noop, reject ); } else { openWindow(url, reject, windowFeatures); } }); return } return openWindow(url, reject, windowFeatures) }; function parsePromises (sequentialPromises) { const isList = Array.isArray(sequentialPromises); if (isList === true) { const totalJobs = sequentialPromises.length; return { isList, totalJobs, resultAggregator: Array(totalJobs).fill(null) } } const resultKeys = Object.keys(sequentialPromises); const resultAggregator = {}; resultKeys.forEach(keyName => { resultAggregator[ keyName ] = null; }); return { isList, totalJobs: resultKeys.length, resultAggregator, resultKeys } } /** * Run a list of Promises sequentially, optionally on multiple threads. * * @param {*} sequentialPromises - Array of Functions or Object with Functions as values * Array of Function form: [ (resultAggregator: Array) => Promise, ... ] * Object form: { [key: string]: (resultAggregator: object) => Promise, ... } * @param {*} opts - Optional options Object * Object form: { threadsNumber?: number, abortOnFail?: boolean } * Default: { threadsNumber: 1, abortOnFail: true } * When configuring threadsNumber AND using http requests, be * aware of the maximum threads that the hosting browser * supports (usually 5); any number of threads above that * won't add any real benefits * @returns Promise | Object> * With opts.abortOnFail set to true (which is default): * When sequentialPromises param is Array: * The Promise resolves with an Array of Objects of the following form: * [ { key: number, status: 'fulfilled', value: any }, ... ] * The Promise rejects with an Object of the following form: * { key: number, status: 'rejected', reason: Error, resultAggregator: array } * When sequentialPromises param is Object: * The Promise resolves with an Object of the following form: * { [key: string]: { key: string, status: 'fulfilled', value: any }, ... } * The Promise rejects with an Object of the following form: * { key: string, status: 'rejected', reason: Error, resultAggregator: object } * With opts.abortOnFail set to false: * The Promise is never rejected (no catch() needed) * The Promise resolves with: * An Array of Objects (when sequentialPromises param is also an Array) of the following form: * [ { key: number, status: 'fulfilled', value: any } | { status: 'rejected', reason: Error }, ... ] * An Object (when sequentialPromises param is also an Object) of the following form: * { [key: string]: { key: string, status: 'fulfilled', value: any } | { key: string, status: 'rejected', reason: Error }, ... } */ function runSequentialPromises ( sequentialPromises, { threadsNumber = 1, abortOnFail = true } = {} ) { let jobIndex = -1, hasAborted = false; const { isList, totalJobs, resultAggregator, resultKeys } = parsePromises(sequentialPromises); const getPromiseThread = () => new Promise((resolve, reject) => { function runNextPromise () { const currentJobIndex = ++jobIndex; if (hasAborted === true || currentJobIndex >= totalJobs) { resolve(); return } const key = isList === true ? currentJobIndex : resultKeys[ currentJobIndex ]; sequentialPromises[ key ](resultAggregator) .then(value => { if (hasAborted === true) { resolve(); return // early exit } resultAggregator[ key ] = { key, status: 'fulfilled', value }; // timeout so it doesn't interfere with the .catch() below setTimeout(runNextPromise); }) .catch(reason => { if (hasAborted === true) { resolve(); return // early exit } const result = { key, status: 'rejected', reason }; resultAggregator[ key ] = result; if (abortOnFail === true) { hasAborted = true; reject({ ...result, resultAggregator }); return // early exit } // timeout so no interference setTimeout(runNextPromise); }); } runNextPromise(); }); const threads = Array(threadsNumber).fill(getPromiseThread()); return Promise.all(threads).then(() => resultAggregator) } var utils = /*#__PURE__*/Object.freeze({ __proto__: null, clone: cloneDeep, colors: colors, copyToClipboard: copyToClipboard, createMetaMixin: createMetaMixin, createUploaderComponent: createUploaderComponent, date: date, debounce: debounce, dom: dom, EventBus: EventBus, event: event, exportFile: exportFile, extend: extend, format: format, frameDebounce: frameDebounce, getCssVar: getCssVar, noop: noop, is: is, morph: morph, openURL: openUrl, patterns: patterns, runSequentialPromises: runSequentialPromises, scroll: scroll, setCssVar: setCssVar, throttle: throttle, uid: uid$3 }); // To be used for the custom component // used on a Dialog plugin function useDialogPluginComponent () { const { emit, proxy } = vue.getCurrentInstance(); // we need a Vue reference to the QDialog // component so we can handle it; // { meta.val = val; meta.active === true && planClientUpdate(); }); } else { meta.val = metaOptions; } clientList.push(meta); planClientUpdate(); vue.onActivated(() => { meta.active = true; planClientUpdate(); }); vue.onDeactivated(() => { meta.active = false; planClientUpdate(); }); vue.onUnmounted(() => { clientList.splice(clientList.indexOf(meta), 1); planClientUpdate(); }); } } /** * Returns the $q instance. * Equivalent to `this.$q` inside templates. */ function useQuasar () { return vue.inject(quasarKey) } var composables = /*#__PURE__*/Object.freeze({ __proto__: null, useDialogPluginComponent: useDialogPluginComponent, useFormChild: useFormChild, useMeta: useMeta, useQuasar: useQuasar }); /** * UMD entry-point */ var index_umd = { version: '2.12.2', install (app, opts) { installQuasar(app, { components, directives, plugins, ...opts }); }, lang: Plugin$8, iconSet: Plugin$7, ...components, ...directives, ...plugins, ...composables, ...utils }; return index_umd; }));