UNPKG

quasar

Version:

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

40,661 lines 1.05 MB
/*!
 * Quasar Framework v2.11.9
 * (c) 2015-present Razvan Stoenescu
 * Released under the MIT License.
 */

import { ref, reactive, createApp, markRaw, defineComponent, getCurrentInstance, computed, onMounted, onBeforeUnmount, h, withDirectives, unref, Transition, nextTick, watch, onUnmounted, Teleport, onDeactivated, KeepAlive, onBeforeMount, toRaw, provide, onActivated, inject, onBeforeUpdate, shallowReactive, vShow, onUpdated, isRef, TransitionGroup } from 'vue';

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 = ref(
    __QUASAR_SSR_CLIENT__ && (
      __QUASAR_SSR_PWA__ ? document.body.getAttribute('data-server-rendered') !== null : true
    )
  );
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 = 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 = 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 = 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.11.9' };

    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 => markRaw(defineComponent(raw));
const createDirective = raw => 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 } = getCurrentInstance();

    const progress = ref(0);
    const onScreen = ref(false);
    const animate = ref(true);

    let sessions = 0, timer = null, speed;

    const classes = computed(() =>
      `q-loading-bar q-loading-bar--${ props.position }`
      + (props.color !== void 0 ? ` bg-${ props.color }` : '')
      + (animate.value === true ? '' : ' no-transition')
    );

    const horizontal = computed(() => props.position === 'top' || props.position === 'bottom');
    const sizeProp = computed(() => (horizontal.value === true ? 'height' : 'width'));

    const style = 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 = 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;

    onMounted(() => {
      if (props.skipHijack !== true) {
        hijacked = true;
        highjackAjax({
          start,
          stop,
          hijackFilter: computed(() => props.hijackFilter || null)
        });
      }
    });

    onBeforeUnmount(() => {
      timer !== null && clearTimeout(timer);
      hijacked === true && restoreAjax(start);
    });

    // expose public methods
    Object.assign(proxy, { start, stop, increment });

    return () => 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 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 = h(tag, data, children);

  return condition === true
    ? 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-(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 } } = getCurrentInstance();
    const sizeStyle = useSize(props);

    const classes = 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 = 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 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 h(props.tag, data, hSlot(slots.default))
      }

      if (type.value.img === true) {
        return h('span', data, hMergeSlot(slots.default, [
          h('img', { src: type.value.src })
        ]))
      }

      if (type.value.svg === true) {
        return h('span', data, hMergeSlot(slots.default, [
          h('svg', {
            viewBox: type.value.viewBox || '0 0 24 24'
          }, type.value.nodes)
        ]))
      }

      if (type.value.svguse === true) {
        return h('span', data, hMergeSlot(slots.default, [
          h('svg', {
            viewBox: type.value.viewBox
          }, [
            h('use', { 'xlink:href': type.value.src })
          ])
        ]))
      }

      if (type.value.cls !== void 0) {
        data.class += ' ' + type.value.cls;
      }

      return 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 = 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 = computed(() => (
      props.fontSize
        ? { fontSize: props.fontSize }
        : null
    ));

    return () => {
      const icon = props.icon !== void 0
        ? [ h(QIcon, { name: props.icon }) ]
        : void 0;

      return h('div', {
        class: classes.value,
        style: sizeStyle.value
      }, [
        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 = computed(() => {
      return props.align !== void 0
        ? { verticalAlign: props.align }
        : null
    });

    const classes = 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 () => 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 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 } } = getCurrentInstance();
    const isDark = useDark(props, $q);

    const classes = 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 = computed(() =>
      'q-banner__actions row items-center justify-end'
      + ` col-${ props.inlineActions === true ? 'auto' : 'all' }`
    );

    return () => {
      const child = [
        h('div', {
          class: 'q-banner__avatar col-auto row items-center self-start'
        }, hSlot(slots.avatar)),

        h('div', {
          class: 'q-banner__content col text-body2'
        }, hSlot(slots.default))
      ];

      const actions = hSlot(slots.action);
      actions !== void 0 && child.push(
        h('div', { class: actionClass.value }, actions)
      );

      return 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 } } = getCurrentInstance();
    const isDark = useDark(props, $q);

    const classes = computed(() =>
      'q-bar row no-wrap items-center'
      + ` q-bar--${ props.dense === true ? 'dense' : 'standard' } `
      + ` q-bar--${ isDark.value === true ? 'dark' : 'light' }`
    );

    return () => 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 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 = computed(() =>
      `flex items-center ${ alignClass.value }${ props.gutter === 'none' ? '' : ` q-gutter-${ props.gutter }` }`
    );

    const sepClass = computed(() => (props.separatorColor ? ` text-${ props.separatorColor }` : ''));
    const activeClass = 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(
            h('div', {
              class: `flex items-center${ cls }`
            }, [ comp ])
          );

          if (middle === true) {
            child.push(
              h('div', {
                class: 'q-breadcrumbs__separator' + sepClass.value
              }, separator())
            );
          }
        }
        else {
          child.push(comp);
        }
      });

      return h('div', {
        class: 'q-breadcrumbs'
      }, [
        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 <a> link
  href: String,
  target: String,

  // state
  disable: Boolean
};

// external props: type, tag

function useRouterLink ({ fallbackTag, useDisableForRouterLinkProps = true } = {}) {
  const vm = getCurrentInstance();
  const { props, proxy, emit } = vm;

  const hasRouter = vmHasRouter(vm);
  const hasHrefLink = computed(() => props.disable !== true && props.href !== void 0);

  // for perf reasons, we use minimum amount of runtime work
  const hasRouterLinkProps = useDisableForRouterLinkProps === true
    ? computed(() =>
      hasRouter === true
      && props.disable !== true
      && hasHrefLink.value !== true
      && props.to !== void 0 && props.to !== null && props.to !== ''
    )
    : computed(() =>
      hasRouter === true
      && hasHrefLink.value !== true
      && props.to !== void 0 && props.to !== null && props.to !== ''
    );

  const resolvedLink = computed(() => (
    hasRouterLinkProps.value === true
      ? getLink(props.to)
      : null
  ));

  const hasRouterLink = computed(() => resolvedLink.value !== null);
  const hasLink = computed(() => hasHrefLink.value === true || hasRouterLink.value === true);

  const linkTag = computed(() => (
    props.type === 'a' || hasLink.value === true
      ? 'a'
      : (props.tag || fallbackTag || 'div')
  ));

  const linkAttrs = computed(() => (
    hasHrefLink.value === true
      ? {
          href: props.href,
          target: props.target
        }
      : (
          hasRouterLink.value === true
            ? {
                href: resolvedLink.value.href,
                target: props.target
              }
            : {}
        )
  ));

  const linkActiveIndex = 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 = computed(() =>
    hasRouterLink.value === true
    && linkActiveIndex.value !== -1
    && includesParams(proxy.$route.params, resolvedLink.value.params)
  );

  const linkIsExactActive = computed(() =>
    linkIsActive.value === true
      && linkActiveIndex.value === proxy.$route.matched.length - 1
      && isSameRouteLocationParams(proxy.$route.params, resolvedLink.value.params)
  );

  const linkClass = 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<RouterError | false | undefined>
   */
  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 = 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 = computed(() =>
      'q-breadcrumbs__el-icon'
      + (props.label !== void 0 ? ' q-breadcrumbs__el-icon--with-label' : '')
    );

    return () => {
      const child = [];

      props.icon !== void 0 && child.push(
        h(QIcon, {
          class: iconClass.value,
          name: props.icon
        })
      );

      props.label !== void 0 && child.push(props.label);

      return h(
        linkTag.value,
        { ...data.value },
        hMergeSlot(slots.default, child)
      )
    }
  }
});

const useSpinnerProps = {
  size: {
    type: [ Number, String ],
    default: '1em'
  },
  color: String
};

function useSpinner (props) {
  return {
    cSize: computed(() => (
      props.size in useSizeDefaults
        ? `${ useSizeDefaults[ props.size ] }px`
        : props.size
    )),

    classes: 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 () => h('svg', {
      class: classes.value + ' q-spinner-mat',
      width: cSize.value,
      height: cSize.value,
      viewBox: '25 25 50 50'
    }, [
      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 = 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 = 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 = computed(() =>
    props.rounded === true || props.fab === true || props.fabMini === true
  );

  const isActionable = computed(() =>
    props.disable !== true && props.loading !== true
  );

  const tabIndex = computed(() => (
    isActionable.value === true ? props.tabindex || 0 : -1
  ));

  const design = computed(() => getBtnDesign(props, 'standard'));

  const attributes = 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 = 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 = 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 } = getCurrentInstance();

    const {
      classes, style, innerClasses,
      attributes,
      hasLink, linkTag, navigateOnClick,
      isActionable
    } = useBtn(props);

    const rootRef = ref(null);
    const blurTargetRef = ref(null);

    let localTouchTargetEl = null, avoidMouseRipple, mouseTimer = null;

    const hasLabel = computed(() =>
      props.label !== void 0 && props.label !== null && props.label !== ''
    );

    const ripple = computed(() => (
      props.disable === true || props.ripple === false
        ? false
        : {
            keyCodes: hasLink.value === true ? [ 13, 32 ] : [ 13 ],
            ...(props.ripple === true ? {} : props.ripple)
          }
    ));

    const rippleProps = computed(() => ({ center: props.round }));

    const percentageStyle = computed(() => {
      const val = Math.max(0, Math.min(100, props.percentage));
      return val > 0
        ? { transition: 'transform 0.6s', transform: `translateX(${ val - 100 }%)` }
        : {}
    });

    const onEvents = 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 <a> tags
        onClick: stopAndPrevent
      }
    });

    const nodeProps = 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;
    }

    onBeforeUnmount(() => {
      cleanup(true);
    });

    // expose public methods
    Object.assign(proxy, { click: onClick });

    return () => {
      let inner = [];

      props.icon !== void 0 && inner.push(
        h(QIcon, {
          name: props.icon,
          left: props.stack === false && hasLabel.value === true,
          role: 'img',
          'aria-hidden': 'true'
        })
      );

      hasLabel.value === true && inner.push(
        h('span', { class: 'block' }, [ props.label ])
      );

      inner = hMergeSlot(slots.default, inner);

      if (props.iconRight !== void 0 && props.round === false) {
        inner.push(
          h(QIcon, {
            name: props.iconRight,
            right: props.stack === false && hasLabel.value === true,
            role: 'img',
            'aria-hidden': 'true'
          })
        );
      }

      const child = [
        h('span', {
          class: 'q-focus-helper',
          ref: blurTargetRef
        })
      ];

      if (props.loading === true && props.percentage !== void 0) {
        child.push(
          h('span', {
            class: 'q-btn__progress absolute-full overflow-hidden' + (props.darkPercentage === true ? ' q-btn__progress--dark' : '')
          }, [
            h('span', {
              class: 'q-btn__progress-indicator fit block',
              style: percentageStyle.value
            })
          ])
        );
      }

      child.push(
        h('span', {
          class: 'q-btn__content text-center col items-center q-anchor--skip ' + innerClasses.value
        }, inner)
      );

      props.loading !== null && child.push(
        h(Transition, {
          name: 'q-transition--fade'
        }, () => (
          props.loading === true
            ? [
                h('span', {
                  key: 'loading',
                  class: 'absolute-full flex flex-center'
                }, slots.loading !== void 0 ? slots.loading() : [ h(QSpinner) ])
              ]
            : null
        ))
      );

      return withDirectives(
        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 = 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 () => 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 } = getCurrentInstance();

  const anchorEl = ref(null);

  let touchTimer = null;

  function canShow (evt) {
    // abort with no parent configured or on multi-touch
    return anchorEl.value === null
      ? false
      : (evt === void 0 || evt.touches === void 0 || evt.touches.length <= 1)
  }

  const anchorEvents = {};

  if (configureAnchorEl === void 0) {
    // default configureAnchorEl is designed for
    // QMenu & QPopupProxy (which is why it's handled here)

    Object.assign(anchorEvents, {
      hide (evt) {
        proxy.hide(evt);
      },

      toggle (evt) {
        proxy.toggle(evt);
        evt.qAnchorHandled = true;
      },

      toggleKey (evt) {
        isKeyCode(evt, 13) === true && anchorEvents.toggle(evt);
      },

      contextClick (evt) {
        proxy.hide(evt);
        prevent(evt);
        nextTick(() => {
          proxy.show(evt);
          evt.qAnchorHandled = true;
        });
      },

      prevent,

      mobileTouch (evt) {
        anchorEvents.mobileCleanup(evt);

        if (canShow(evt) !== true) {
          return
        }

        proxy.hide(evt);
        anchorEl.value.classList.add('non-selectable');

        const target = evt.target;
        addEvt(anchorEvents, 'anchor', [
          [ target, 'touchmove', 'mobileCleanup', 'passive' ],
          [ target, 'touchend', 'mobileCleanup', 'passive' ],
          [ target, 'touchcancel', 'mobileCleanup', 'passive' ],
          [ anchorEl.value, 'contextmenu', 'prevent', 'notPassive' ]
        ]);

        touchTimer = setTimeout(() => {
          touchTimer = null;
          proxy.show(evt);
          evt.qAnchorHandled = true;
        }, 300);
      },

      mobileCleanup (evt) {
        anchorEl.value.classList.remove('non-selectable');

        if (touchTimer !== null) {
          clearTimeout(touchTimer);
          touchTimer = null;
        }

        if (showing.value === true && evt !== void 0) {
          clearSelection();
        }
      }
    });

    configureAnchorEl = function (context = props.contextMenu) {
      if (props.noParentEvent === true || anchorEl.value === null) { return }

      let evts;

      if (context === true) {
        if (proxy.$q.platform.is.mobile === true) {
          evts = [
            [ anchorEl.value, 'touchstart', 'mobileTouch', 'passive' ]
          ];
        }
        else {
          evts = [
            [ anchorEl.value, 'mousedown', 'hide', 'passive' ],
            [ anchorEl.value, 'contextmenu', 'contextClick', 'notPassive' ]
          ];
        }
      }
      else {
        evts = [
          [ anchorEl.value, 'click', 'toggle', 'passive' ],
          [ anchorEl.value, 'keyup', 'toggleKey', 'passive' ]
        ];
      }

      addEvt(anchorEvents, 'anchor', evts);
    };
  }

  function unconfigureAnchorEl () {
    cleanEvt(anchorEvents, 'anchor');
  }

  function setAnchorEl (el) {
    anchorEl.value = el;
    while (anchorEl.value.classList.contains('q-anchor--skip')) {
      anchorEl.value = anchorEl.value.parentNode;
    }
    configureAnchorEl();
  }

  function pickAnchorEl () {
    if (props.target === false || props.target === '' || proxy.$el.parentNode === null) {
      anchorEl.value = null;
    }
    else if (props.target === true) {
      setAnchorEl(proxy.$el.parentNode);
    }
    else {
      let el = props.target;

      if (typeof props.target === 'string') {
        try {
          el = document.querySelector(props.target);
        }
        catch (err) {
          el = void 0;
        }
      }

      if (el !== void 0 && el !== null) {
        anchorEl.value = el.$el || el;
        configureAnchorEl();
      }
      else {
        anchorEl.value = null;
        console.error(`Anchor: target "${ props.target }" not found`);
      }
    }
  }

  watch(() => props.contextMenu, val => {
    if (anchorEl.value !== null) {
      unconfigureAnchorEl();
      configureAnchorEl(val);
    }
  });

  watch(() => props.target, () => {
    if (anchorEl.value !== null) {
      unconfigureAnchorEl();
    }

    pickAnchorEl();
  });

  watch(() => props.noParentEvent, val => {
    if (anchorEl.value !== null) {
      if (val === true) {
        unconfigureAnchorEl();
      }
      else {
        configureAnchorEl();
      }
    }
  });

  onMounted(() => {
    pickAnchorEl();

    if (avoidEmit !== true && props.modelValue === true && anchorEl.value === null) {
      emit('update:modelValue', false);
    }
  });

  onBeforeUnmount(() => {
    touchTimer !== null && clearTimeout(touchTimer);
    unconfigureAnchorEl();
  });

  return {
    anchorEl,
    canShow,
    anchorEvents
  }
}

function useScrollTarget (
  props,
  configureScrollTarget
) {
  const localScrollTarget = 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 = watch(() => props.noParentEvent, () => {
    if (localScrollTarget.value !== null) {
      unconfigureScrollTarget();
      configureScrollTarget();
    }
  });

  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 = 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;
      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;
      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);
    }
  }

  watch(() => props.modelValue, processModelChange);

  if (hideOnRouteChange !== void 0 && vmHasRouter(vm) === true) {
    watch(() => proxy.$route.fullPath, () => {
      if (hideOnRouteChange.value === true && showing.value === true) {
        hide();
      }
    });
  }

  processOnMount === true && 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 = ref(false);

  // showing & not in any show/hide transition
  const portalIsAccessible = 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;
    }
  }

  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
              ? [ h(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: 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: computed(() => `--q-transition-duration: ${ props.transitionDuration }ms`)
  }
}

/*
 * Usage:
 *    registerTick(fn)
 *    removeTick()
 */

function useTick () {
  let tickFn;
  const vm = getCurrentInstance();

  function removeTick () {
    tickFn = void 0;
  }

  onDeactivated(removeTick);
  onBeforeUnmount(removeTick);

  return {
    removeTick,

    registerTick (fn) {
      tickFn = fn;

      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 = getCurrentInstance();

  function removeTimeout () {
    if (timer !== null) {
      clearTimeout(timer);
      timer = null;
    }
  }

  onDeactivated(removeTimeout);
  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 ].$;

    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 (el) {
  return {
    top: 0,
    center: el.offsetHeight / 2,
    bottom: el.offsetHeight,
    left: 0,
    middle: el.offsetWidth / 2,
    right: el.offsetWidth
  }
}

function getTopLeftProps (anchorProps, targetProps, cfg) {
  return {
    top: anchorProps[ cfg.anchorOrigin.vertical ] - targetProps[ cfg.selfOrigin.vertical ],
    left: anchorProps[ cfg.anchorOrigin.horizontal ] - targetProps[ cfg.selfOrigin.horizontal ]
  }
}

// cfg: { el, anchorEl, anchorOrigin, selfOrigin, offset, absoluteOffset, cover, fit, maxHeight, maxWidth }
function setPosition (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 } = cfg.el;

  const anchorProps = cfg.absoluteOffset === void 0
    ? getAnchorProps(cfg.anchorEl, cfg.cover === true ? [ 0, 0 ] : cfg.offset)
    : getAbsoluteAnchorProps(cfg.anchorEl, cfg.absoluteOffset, cfg.offset);

  let elStyle = {
    maxHeight: cfg.maxHeight,
    maxWidth: cfg.maxWidth,
    visibility: 'visible'
  };

  if (cfg.fit === true || cfg.cover === true) {
    elStyle.minWidth = anchorProps.width + 'px';
    if (cfg.cover === true) {
      elStyle.minHeight = anchorProps.height + 'px';
    }
  }

  Object.assign(cfg.el.style, elStyle);

  const targetProps = getTargetProps(cfg.el);
  let props = getTopLeftProps(anchorProps, targetProps, cfg);

  if (cfg.absoluteOffset === void 0 || cfg.offset === void 0) {
    applyBoundaries(props, anchorProps, targetProps, cfg.anchorOrigin, cfg.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, cfg.anchorOrigin, cfg.selfOrigin);

    let hasChanged = false;

    // did it flip vertically?
    if (props.top !== top) {
      hasChanged = true;
      const offsetY = 2 * cfg.offset[ 1 ];
      anchorProps.center = anchorProps.top -= offsetY;
      anchorProps.bottom -= offsetY + 2;
    }

    // did it flip horizontally?
    if (props.left !== left) {
      hasChanged = true;
      const offsetX = 2 * cfg.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, cfg);

      // and re-apply boundaries
      applyBoundaries(props, anchorProps, targetProps, cfg.anchorOrigin, cfg.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(cfg.el.style, elStyle);

  // restore scroll position
  if (cfg.el.scrollTop !== scrollTop) {
    cfg.el.scrollTop = scrollTop;
  }
  if (cfg.el.scrollLeft !== scrollLeft) {
    cfg.el.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 = getCurrentInstance();
    const { proxy } = vm;
    const { $q } = proxy;

    const innerRef = ref(null);
    const showing = ref(false);

    const hideOnRouteChange = 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 = computed(() =>
      parsePosition(
        props.anchor || (
          props.cover === true ? 'center middle' : 'bottom start'
        ),
        $q.lang.rtl
      )
    );

    const selfOrigin = computed(() => (
      props.cover === true
        ? anchorOrigin.value
        : parsePosition(props.self || 'top start', $q.lang.rtl)
    ));

    const menuClass = computed(() =>
      (props.square === true ? ' q-menu--square' : '')
      + (isDark.value === true ? ' q-menu--dark q-dark' : '')
    );

    const onEvents = computed(() => (
      props.autoClose === true
        ? { onClick: onAutoClose }
        : {}
    ));

    const handlesFocus = computed(() =>
      showing.value === true && props.persistent !== true
    );

    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 = 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 () {
      const el = innerRef.value;

      if (el === null || anchorEl.value === null) {
        return
      }

      setPosition({
        el,
        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 h(
        Transition,
        transitionProps.value,
        () => (
          showing.value === true
            ? 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
        )
      )
    }

    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 } = getCurrentInstance();

    const showing = ref(props.modelValue);
    const menuRef = ref(null);
    const targetUid = uid$3();

    const ariaAttrs = 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 = computed(() =>
      'q-btn-dropdown__arrow'
      + (showing.value === true && props.noIconAnimation === false ? ' rotate-180' : '')
      + (props.split === false ? ' q-btn-dropdown__arrow-container' : '')
    );

    const btnDesignAttr = computed(() => getBtnDesignAttr(props));
    const btnProps = computed(() => passBtnProps(props));

    watch(() => props.modelValue, val => {
      menuRef.value !== null && menuRef.value[ val ? 'show' : 'hide' ]();
    });

    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
    });

    onMounted(() => {
      props.modelValue === true && show();
    });

    return () => {
      const Arrow = [
        h(QIcon, {
          class: iconClass.value,
          name: props.dropdownIcon || proxy.$q.iconSet.arrow.dropdown
        })
      ];

      props.disableDropdown !== true && Arrow.push(
        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 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 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
      }, () => [
        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
        }),

        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 computed(() => ({
    type: 'hidden',
    name: props.name,
    value: props.modelValue
  }))
}

function useFormInject (formAttrs = {}) {
  return (child, action, className) => {
    child[ action ](
      h('input', {
        class: 'hidden' + (className || ''),
        ...formAttrs.value
      })
    );
  }
}

function useFormInputNameAttr (props) {
  return 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 = computed(() =>
      props.options.find(opt => opt.value === props.modelValue) !== void 0
    );

    const formAttrs = computed(() => ({
      type: 'hidden',
      name: props.name,
      value: props.modelValue
    }));

    const injectFormInput = useFormInject(formAttrs);

    const btnDesignAttr = computed(() => getBtnDesignAttr(props));

    const btnOptionDesign = computed(() => ({
      rounded: props.rounded,
      dense: props.dense,
      ...btnDesignAttr.value
    }));

    const btnOptions = 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 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 () => 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 } } = getCurrentInstance();
    const isDark = useDark(props, $q);

    const classes = 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 () => 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 = computed(() =>
      'q-card__section'
      + ` q-card__section--${ props.horizontal === true ? 'horiz row no-wrap' : 'vert' }`
    );

    return () => 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 = computed(() =>
      `q-card__actions ${ alignClass.value }`
      + ` q-card__actions--${ props.vertical === true ? 'vert column' : 'horiz row' }`
    );

    return () => 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
}

function shouldStart (evt, ctx) {
  return ctx.event === void 0
    && evt.target !== void 0
    && evt.target.draggable !== true
    && typeof ctx.handler === 'function'
    && evt.target.nodeName.toUpperCase() !== 'INPUT'
    && (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
              }
            }
            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 () => 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 } = getCurrentInstance();
  const { getCacheWithFn } = useCache();

  let panels, forcedPanelTransition;

  const panelIndex = ref(null);
  const panelTransition = 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 = computed(() => {
    // if props.swipeable
    return [ [
      TouchSwipe,
      onSwipe,
      void 0,
      {
        horizontal: props.vertical !== true,
        vertical: props.vertical,
        mouse: true
      }
    ] ]
  });

  const transitionPrev = computed(() =>
    props.transitionPrev || `slide-${ props.vertical === true ? 'down' : 'right' }`
  );

  const transitionNext = computed(() =>
    props.transitionNext || `slide-${ props.vertical === true ? 'up' : 'left' }`
  );

  const transitionStyle = computed(
    () => `--q-transition-duration: ${ props.transitionDuration }ms`
  );

  const contentKey = computed(() => (
    typeof props.modelValue === 'string' || typeof props.modelValue === 'number'
      ? props.modelValue
      : String(props.modelValue)
  ));

  const keepAliveProps = computed(() => ({
    include: props.keepAliveInclude,
    exclude: props.keepAliveExclude,
    max: props.keepAliveMax
  }));

  const needsUniqueKeepAliveWrapper = computed(() =>
    props.keepAliveInclude !== void 0
    || props.keepAliveExclude !== void 0
  );

  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);
      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
      ? [
          h(KeepAlive, keepAliveProps.value, [
            h(
              needsUniqueKeepAliveWrapper.value === true
                ? getCacheWithFn(contentKey.value, () => ({ ...PanelWrapper$1, name: contentKey.value }))
                : PanelWrapper$1,
              { key: contentKey.value, style: transitionStyle.value },
              () => panel
            )
          ])
        ]
      : [
          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
      ? [ h(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 = getCurrentInstance();
  const { props, emit, proxy } = vm;

  let historyEntry, fullscreenFillerNode, container;
  const inFullscreen = ref(false);

  vmHasRouter(vm) === true && watch(() => proxy.$route.fullPath, () => {
    props.noRouteFullscreenExit !== true && exitFullscreen();
  });

  watch(() => props.fullscreen, v => {
    if (inFullscreen.value !== v) {
      toggleFullscreen();
    }
  });

  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(); });
      }
    }
  }

  onBeforeMount(() => {
    fullscreenFillerNode = document.createElement('span');
  });

  onMounted(() => {
    props.fullscreen === true && setFullscreen();
  });

  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 } } = 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 = computed(() => (
      inFullscreen.value !== true && props.height !== void 0
        ? { height: props.height }
        : {}
    ));

    const direction = computed(() => (props.vertical === true ? 'vertical' : 'horizontal'));

    const classes = 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 = 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 = computed(() => props.navigationIcon || $q.iconSet.carousel.navigationIcon);
    const navActiveIcon = computed(() => props.navigationActiveIcon || navIcon.value);
    const navigationPosition = computed(() => props.navigationPosition
      || (props.vertical === true ? 'right' : 'bottom')
    );

    const controlProps = computed(() => ({
      color: props.controlColor,
      textColor: props.controlTextColor,
      round: true,
      [ props.controlType ]: true,
      dense: true
    }));

    watch(() => props.modelValue, () => {
      if (props.autoplay) {
        startTimer();
      }
    });

    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);
    }

    onMounted(() => {
      props.autoplay && startTimer();
    });

    onBeforeUnmount(() => {
      timer !== null && clearTimeout(timer);
    });

    function getNavigationContainer (type, mapping) {
      return 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 }` : '')
      }, [
        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 => 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 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(
            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`
            }, [
              h(QBtn, {
                icon: arrowIcons.value[ 0 ],
                ...controlProps.value,
                onClick: previousPanel
              })
            ])
          );
        }

        if (props.infinite === true || panelIndex.value < panelsLen - 1) {
          node.push(
            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`
            }, [
              h(QBtn, {
                icon: arrowIcons.value[ 1 ],
                ...controlProps.value,
                onClick: nextPanel
              })
            ])
          );
        }
      }

      return hMergeSlot(slots.control, node)
    }

    return () => {
      panelsLen = updatePanelsList(slots);

      return 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 = computed(() => (
      props.imgSrc
        ? { backgroundImage: `url("${ props.imgSrc }")` }
        : {}
    ));

    return () => 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 = computed(() => `q-carousel__control absolute absolute-${ props.position }`);
    const style = computed(() => ({
      margin: `${ props.offset[ 1 ] }px ${ props.offset[ 0 ] }px`
    }));

    return () => 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 = computed(() => (props.sent === true ? 'sent' : 'received'));

    const textClass = computed(() =>
      `q-message-text-content q-message-text-content--${ op.value }`
      + (props.textColor !== void 0 ? ` text-${ props.textColor }` : '')
    );

    const messageClass = computed(() =>
      `q-message-text q-message-text--${ op.value }`
      + (props.bgColor !== void 0 ? ` text-${ props.bgColor }` : '')
    );

    const containerClass = computed(() =>
      'q-message-container row items-end no-wrap'
      + (props.sent === true ? ' reverse' : '')
    );

    const sizeClass = computed(() => (props.size !== void 0 ? `col-${ props.size }` : ''));

    const domProps = 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, h('div', { class: 'q-message-stamp' }, slots.stamp()) ]
      }

      if (props.stamp) {
        return [
          node,
          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 => h('div', [ text ]))
        : text => h('div', { [ domProps.value.msg ]: text });

      return contentList.map((msg, index) => h('div', {
        key: index,
        class: messageClass.value
      }, [
        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(
          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(
          h('div', { class: `q-message-name q-message-name--${ op.value }` }, slots.name())
        );
      }
      else if (props.name !== void 0) {
        msg.push(
          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(
        h('div', { class: sizeClass.value }, msg)
      );

      const child = [];

      if (slots.label !== void 0) {
        child.push(
          h('div', { class: 'q-message-label' }, slots.label())
        );
      }
      else if (props.label !== void 0) {
        child.push(
          h('div', {
            class: 'q-message-label',
            [ domProps.value.label ]: props.label
          })
        );
      }

      child.push(
        h('div', { class: containerClass.value }, container)
      );

      return h('div', {
        class: `q-message q-message-${ op.value }`
      }, child)
    }
  }
});

function useRefocusTarget (props, rootRef) {
  const refocusRef = ref(null);

  const refocusTargetEl = computed(() => {
    if (props.disable === true) {
      return null
    }

    return 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 } = getCurrentInstance();
  const { $q } = proxy;

  const isDark = useDark(props, $q);

  const rootRef = ref(null);
  const { refocusTargetEl, refocusTarget } = useRefocusTarget(props, rootRef);
  const sizeStyle = useSize(props, optionSizes);

  const modelIsArray = computed(() =>
    props.val !== void 0 && Array.isArray(props.modelValue)
  );

  const index = computed(() => {
    const val = toRaw(props.val);
    return modelIsArray.value === true
      ? props.modelValue.findIndex(opt => toRaw(opt) === val)
      : -1
  });

  const isTrue = computed(() => (
    modelIsArray.value === true
      ? index.value > -1
      : toRaw(props.modelValue) === toRaw(props.trueValue)
  ));

  const isFalse = computed(() => (
    modelIsArray.value === true
      ? index.value === -1
      : toRaw(props.modelValue) === toRaw(props.falseValue)
  ));

  const isIndeterminate = computed(() =>
    isTrue.value === false && isFalse.value === false
  );

  const tabindex = computed(() => (
    props.disable === true ? -1 : props.tabindex || 0
  ));

  const classes = 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 = 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 = 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 = 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 = [
      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(
      h('div', {
        class: `q-${ type }__label q-anchor--skip`
      }, label)
    );

    return h('div', {
      ref: rootRef,
      class: classes.value,
      ...attributes.value,
      onClick,
      onKeydown,
      onKeyup
    }, child)
  }
}

const bgNode = h('div', {
  key: 'svg',
  class: 'q-checkbox__bg absolute'
}, [
  h('svg', {
    class: 'q-checkbox__svg fit absolute-full',
    viewBox: '0 0 24 24'
  }, [
    h('path', {
      class: 'q-checkbox__truthy',
      fill: 'none',
      d: 'M1.73,12.91 8.1,19.28 22.79,4.59'
    }),

    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 = computed(() =>
        (isTrue.value === true
          ? props.checkedIcon
          : (isIndeterminate.value === true
              ? props.indeterminateIcon
              : props.uncheckedIcon
            )
        ) || null
      );

      return () => (
        icon.value !== null
          ? [
              h('div', {
                key: 'icon',
                class: 'q-checkbox__icon-container absolute-full flex flex-center no-wrap'
              }, [
                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 } } = getCurrentInstance();

    const isDark = useDark(props, $q);
    const sizeStyle = useSize(props, defaultSizes$1);

    const hasLeftIcon = computed(() => props.selected === true || props.icon !== void 0);

    const leftIcon = computed(() => (
      props.selected === true
        ? props.iconSelected || $q.iconSet.chip.selected
        : props.icon
    ));

    const removeIcon = computed(() => props.iconRemove || $q.iconSet.chip.remove);

    const isClickable = computed(() =>
      props.disable === false
      && (props.clickable === true || props.selected !== null)
    );

    const classes = 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 = 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(
        h('div', { class: 'q-focus-helper' })
      );

      hasLeftIcon.value === true && child.push(
        h(QIcon, {
          class: 'q-chip__icon q-chip__icon--left',
          name: leftIcon.value
        })
      );

      const label = props.label !== void 0
        ? [ h('div', { class: 'ellipsis' }, [ props.label ]) ]
        : void 0;

      child.push(
        h('div', {
          class: 'q-chip__content col row no-wrap items-center q-anchor--skip'
        }, hMergeSlotSafely(slots.default, label))
      );

      props.iconRight && child.push(
        h(QIcon, {
          class: 'q-chip__icon q-chip__icon--right',
          name: props.iconRight
        })
      );

      props.removable === true && child.push(
        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 } } = getCurrentInstance();
    const sizeStyle = useSize(props);

    const svgStyle = 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 = computed(() => (
      props.instantFeedback !== true && props.indeterminate !== true
        ? { transition: `stroke-dashoffset ${ props.animationSpeed }ms ease 0s, stroke ${ props.animationSpeed }ms ease` }
        : ''
    ));

    const viewBox = computed(() => diameter / (1 - props.thickness / 2));

    const viewBoxAttr = computed(() =>
      `${ viewBox.value / 2 } ${ viewBox.value / 2 } ${ viewBox.value } ${ viewBox.value }`
    );

    const normalized = computed(() => between(props.value, props.min, props.max));

    const strokeDashOffset = computed(() => circumference * (
      1 - (normalized.value - props.min) / (props.max - props.min)
    ));

    const strokeWidth = computed(() => props.thickness / 2 * viewBox.value);

    function getCircle ({ thickness, offset, color, cls, rounded }) {
      return 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(
        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 = [
        h('svg', {
          class: 'q-circular-progress__svg',
          style: svgStyle.value,
          viewBox: viewBoxAttr.value,
          'aria-hidden': 'true'
        }, svgChild)
      ];

      props.showValue === true && child.push(
        h('div', {
          class: 'q-circular-progress__text absolute-full row flex-center content-center',
          style: { fontSize: props.fontSize }
        }, slots.default !== void 0 ? slots.default() : [ h('div', normalized.value) ])
      );

      return 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 }) => 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 } } = getCurrentInstance();
  const isDark = useDark(props, $q);

  const injectFormInput = useFormInject(formAttrs);

  const active = ref(false);
  const preventFocus = ref(false);
  const focus = ref(false);
  const dragging = ref(false);

  const axis = computed(() => (props.vertical === true ? '--v' : '--h'));
  const labelSide = computed(() => '-' + (props.switchLabelSide === true ? 'switched' : 'standard'));

  const isReversed = computed(() => (
    props.vertical === true
      ? props.reverse === true
      : props.reverse !== ($q.lang.rtl === true)
  ));

  const innerMin = computed(() => (
    isNaN(props.innerMin) === true || props.innerMin < props.min
      ? props.min
      : props.innerMin
  ));
  const innerMax = computed(() => (
    isNaN(props.innerMax) === true || props.innerMax > props.max
      ? props.max
      : props.innerMax
  ));

  const editable = computed(() => (
    props.disable !== true && props.readonly !== true
    && innerMin.value < innerMax.value
  ));

  const decimals = computed(() => (String(props.step).trim().split('.')[ 1 ] || '').length);
  const step = computed(() => (props.step === 0 ? 1 : props.step));
  const tabindex = computed(() => (editable.value === true ? props.tabindex || 0 : -1));

  const trackLen = computed(() => props.max - props.min);
  const innerBarLen = computed(() => innerMax.value - innerMin.value);

  const innerMinRatio = computed(() => convertModelToRatio(innerMin.value));
  const innerMaxRatio = computed(() => convertModelToRatio(innerMax.value));

  const positionProp = computed(() => (
    props.vertical === true
      ? (isReversed.value === true ? 'bottom' : 'top')
      : (isReversed.value === true ? 'right' : 'left')
  ));

  const sizeProp = computed(() => (props.vertical === true ? 'height' : 'width'));
  const thicknessProp = computed(() => (props.vertical === true ? 'width' : 'height'));
  const orientation = computed(() => (props.vertical === true ? 'vertical' : 'horizontal'));

  const attributes = 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 = 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 = computed(() => {
    const color = props.selectionColor || props.color;
    return 'q-slider__selection absolute'
      + (color !== void 0 ? ` text-${ color }` : '')
  });
  const markerClass = computed(() => getAxisClass('markers') + ' absolute overflow-hidden');
  const trackContainerClass = computed(() => getAxisClass('track-container'));
  const pinClass = computed(() => getPositionClass('pin'));
  const labelClass = computed(() => getPositionClass('label'));
  const textContainerClass = computed(() => getPositionClass('text-container'));
  const markerLabelsContainerClass = computed(() =>
    getPositionClass('marker-labels-container')
    + (props.markerLabelsClass !== void 0 ? ` ${ props.markerLabelsClass }` : '')
  );

  const trackClass = computed(() =>
    'q-slider__track relative-position no-outline'
    + (props.trackColor !== void 0 ? ` bg-${ props.trackColor }` : '')
  );
  const trackStyle = computed(() => {
    const acc = { [ thicknessProp.value ]: props.trackSize };
    if (props.trackImg !== void 0) {
      acc.backgroundImage = `url(${ props.trackImg }) !important`;
    }
    return acc
  });

  const innerBarClass = computed(() =>
    'q-slider__inner absolute'
    + (props.innerTrackColor !== void 0 ? ` bg-${ props.innerTrackColor }` : '')
  );
  const innerBarStyle = 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 = computed(() => (
    isNumber(props.markers) === true ? props.markers : step.value)
  );

  const markerTicks = 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 = computed(() => {
    const prefix = ` ${ markerPrefixClass }${ axis.value }-`;
    return markerPrefixClass
      + `${ prefix }${ props.switchMarkerLabelsSide === true ? 'switched' : 'standard' }`
      + `${ prefix }${ isReversed.value === true ? 'rtl' : 'ltr' }`
  });

  const markerLabelsList = 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 = computed(() => ({
    markerList: markerLabelsList.value,
    markerMap: markerLabelsMap.value,
    classes: markerLabelClass.value, // TODO ts definition
    getStyle: getMarkerLabelStyle
  }));

  const markerStyle = 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 = 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 = 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 = computed(() => (
      preventFocus.value === false && (focus.value === thumb.focusValue || focus.value === 'both')
        ? ' q-slider--focus'
        : ''
    ));

    const classes = 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 = computed(() => ({
      width: props.thumbSize,
      height: props.thumbSize,
      [ positionProp.value ]: `${ 100 * thumb.ratio.value }%`,
      zIndex: focus.value === thumb.focusValue ? 2 : void 0
    }));

    const pinColor = computed(() => (
      thumb.labelColor.value !== void 0
        ? ` text-${ thumb.labelColor.value }`
        : ''
    ));

    const textContainerStyle = computed(() => getTextContainerStyle(thumb.ratio.value));

    const textClass = computed(() => (
      'q-slider__text'
      + (thumb.labelTextColor.value !== void 0 ? ` text-${ thumb.labelTextColor.value }` : '')
    ));

    return () => {
      const thumbContent = [
        h('svg', {
          class: 'q-slider__thumb-shape absolute-full',
          viewBox: '0 0 20 20',
          'aria-hidden': 'true'
        }, [
          h('path', { d: props.thumbPath })
        ]),

        h('div', { class: 'q-slider__focus-ring fit' })
      ];

      if (props.label === true || props.labelAlways === true) {
        thumbContent.push(
          h('div', {
            class: pinClass.value + ' absolute fit no-pointer-events' + pinColor.value
          }, [
            h('div', {
              class: labelClass.value,
              style: { minWidth: props.thumbSize }
            }, [
              h('div', {
                class: textContainerClass.value,
                style: textContainerStyle.value
              }, [
                h('span', { class: textClass.value }, thumb.label.value)
              ])
            ])
          ])
        );

        if (props.name !== void 0 && props.disable !== true) {
          injectFormInput(thumbContent, 'push');
        }
      }

      return h('div', {
        class: classes.value,
        style: style.value,
        ...thumb.getNodeData()
      }, thumbContent)
    }
  }

  function getContent (selectionBarStyle, trackContainerTabindex, trackContainerEvents, injectThumb) {
    const trackContent = [];

    props.innerTrackColor !== 'transparent' && trackContent.push(
      h('div', {
        key: 'inner',
        class: innerBarClass.value,
        style: innerBarStyle.value
      })
    );

    props.selectionColor !== 'transparent' && trackContent.push(
      h('div', {
        key: 'selection',
        class: selectionBarClass.value,
        style: selectionBarStyle.value
      })
    );

    props.markers !== false && trackContent.push(
      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
        },
        [
          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 ](
        h('div', {
          key: 'markerL',
          class: markerLabelsContainerClass.value
        }, getMarkerLabelsContent())
      );
    }

    return content
  }

  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 } } = getCurrentInstance();

    const { state, methods } = useSlider({
      updateValue, updatePosition, getDragging,
      formAttrs: useFormAttrs(props)
    });

    const rootRef = ref(null);
    const curRatio = ref(0);
    const model = ref(0);

    function normalizeModel () {
      model.value = props.modelValue === null
        ? state.innerMin.value
        : between(props.modelValue, state.innerMin.value, state.innerMax.value);
    }

    watch(
      () => `${ props.modelValue }|${ state.innerMin.value }|${ state.innerMax.value }`,
      normalizeModel
    );

    normalizeModel();

    const modelRatio = computed(() => methods.convertModelToRatio(model.value));
    const ratio = computed(() => (state.active.value === true ? curRatio.value : modelRatio.value));

    const selectionBarStyle = 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: computed(() => (
        props.labelValue !== void 0
          ? props.labelValue
          : model.value
      )),
      thumbColor: computed(() => props.thumbColor || props.color),
      labelColor: computed(() => props.labelColor),
      labelTextColor: computed(() => props.labelTextColor)
    });

    const trackContainerEvents = 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 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 = ref(!isRuntimeSsrPreHydration.value);

  if (canRender.value === false) {
    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 } = 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) {
          nextTick(() => { init(true); });
        }
      };

      onMounted(() => { init(); });

      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();
        }
      }

      onMounted(() => {
        nextTick(() => {
          targetEl = proxy.$el;
          targetEl && onObjLoad();
        });
      });

      onBeforeUnmount(cleanup);

      // expose public method
      proxy.trigger = trigger;

      return () => {
        if (canRender.value === true) {
          return 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
if (!__QUASAR_SSR__) {
  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 } = 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 = ref(null);
    const contentRef = ref(null);

    const currentModel = ref(props.modelValue);
    const scrollable = ref(false);
    const leftArrow = ref(true);
    const rightArrow = ref(false);
    const justify = ref(false);

    const tabDataList = [];
    const tabDataListLen = ref(0);
    const hasFocus = ref(false);

    let animateTimer = null, scrollTimer = null, unwatchRoute;

    const tabProps = 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 = 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 = computed(() => {
      const align = scrollable.value === true
        ? 'left'
        : (justify.value === true ? 'justify' : props.align);

      return `q-tabs__content--align-${ align }`
    });

    const classes = 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 = 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 = computed(() => (
      props.vertical === true
        ? { container: 'height', content: 'offsetHeight', scroll: 'scrollHeight' }
        : { container: 'width', content: 'offsetWidth', scroll: 'scrollWidth' }
    ));

    const isRTL = computed(() => props.vertical !== true && $q.lang.rtl === true);
    const rtlPosCorrection = computed(() => rtlHasScrollBug === false && isRTL.value === true);

    watch(isRTL, updateArrows);

    watch(() => props.modelValue, name => {
      updateModel({ name, setCurrent: true, skipEmit: true });
    });

    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 = 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 = 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)
    };

    provide(tabsKey, $tabs);

    function cleanup () {
      animateTimer !== null && clearTimeout(animateTimer);
      stopAnimScroll();
      unwatchRoute !== void 0 && unwatchRoute();
    }

    let hadRouteWatcher;

    onBeforeUnmount(cleanup);

    onDeactivated(() => {
      hadRouteWatcher = unwatchRoute !== void 0;
      cleanup();
    });

    onActivated(() => {
      hadRouteWatcher === true && watchRoute();
      recalculateScroll();
    });

    return () => {
      return h('div', {
        ref: rootRef,
        class: classes.value,
        role: 'tablist',
        onFocusin,
        onFocusout
      }, [
        h(QResizeObserver, { onResize: updateContainer }),

        h('div', {
          ref: contentRef,
          class: innerClass.value,
          onScroll: updateArrows
        }, hSlot(slots.default)),

        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
        }),

        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 = inject(tabsKey, emptyRenderFn);
  if ($tabs === emptyRenderFn) {
    console.error('QTab/QRouteTab component needs to be child of QTabs');
    return emptyRenderFn
  }

  const { proxy } = getCurrentInstance();

  const blurTargetRef = ref(null);
  const rootRef = ref(null);
  const tabIndicatorRef = ref(null);

  const ripple = computed(() => (
    props.disable === true || props.ripple === false
      ? false
      : Object.assign(
        { keyCodes: [ 13, 32 ], early: true },
        props.ripple === true ? {} : props.ripple
      )
  ));

  const isActive = computed(() => $tabs.currentModel.value === props.name);

  const classes = 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 = 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 = 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 = h('div', {
        ref: tabIndicatorRef,
        class: [
          'q-tab__indicator',
          $tabs.tabProps.value.indicatorClass
        ]
      });

    props.icon !== void 0 && content.push(
      h(QIcon, {
        class: 'q-tab__icon',
        name: props.icon
      })
    );

    props.label !== void 0 && content.push(
      h('div', { class: 'q-tab__label' }, props.label)
    );

    props.alert !== false && content.push(
      props.alertIcon !== void 0
        ? h(QIcon, {
          class: 'q-tab__alert-icon',
          color: props.alert !== true
            ? props.alert
            : void 0,
          name: props.alertIcon
        })
        : h('div', {
          class: 'q-tab__alert'
            + (props.alert !== true ? ` text-${ props.alert }` : '')
        })
    );

    narrow === true && content.push(indicator);

    const node = [
      h('div', { class: 'q-focus-helper', tabindex: -1, ref: blurTargetRef }),
      h('div', { class: innerClass.value }, hMergeSlot(slots.default, content))
    ];

    narrow === false && node.push(indicator);

    return node
  }

  const tabData = {
    name: computed(() => props.name),
    rootRef,
    tabIndicatorRef,
    routeData
  };

  onBeforeUnmount(() => {
    $tabs.unregisterTab(tabData);
  });

  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 withDirectives(
      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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const { updatePanelsList, getPanelContent, panelDirectives } = usePanel();

    const classes = 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 () => 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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);
    const { getCache } = useCache();

    const spectrumRef = ref(null);
    const errorIconRef = ref(null);

    const forceHex = computed(() => (
      props.formatModel === 'auto'
        ? null
        : props.formatModel.indexOf('hex') > -1
    ));

    const forceAlpha = computed(() => (
      props.formatModel === 'auto'
        ? null
        : props.formatModel.indexOf('a') > -1
    ));

    const topView = 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 = ref(props.defaultView);
    const model = ref(parseModel(props.modelValue || props.defaultValue));

    const editable = computed(() => props.disable !== true && props.readonly !== true);

    const isHex = computed(() =>
      props.modelValue === void 0
      || props.modelValue === null
      || props.modelValue === ''
      || props.modelValue.startsWith('#')
    );

    const isOutputHex = computed(() => (
      forceHex.value !== null
        ? forceHex.value
        : isHex.value
    ));

    const formAttrs = computed(() => ({
      type: 'hidden',
      name: props.name,
      value: model.value[ isOutputHex.value === true ? 'hex' : 'rgb' ]
    }));

    const injectFormInput = useFormInject(formAttrs);

    const hasAlpha = computed(() => (
      forceAlpha.value !== null
        ? forceAlpha.value
        : model.value.a !== void 0
    ));

    const currentBgColor = computed(() => ({
      backgroundColor: model.value.rgb || '#000'
    }));

    const headerClass = 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 = computed(() => ({
      background: `hsl(${ model.value.h },100%,50%)`
    }));

    const spectrumPointerStyle = computed(() => ({
      top: `${ 100 - model.value.v }%`,
      [ $q.lang.rtl === true ? 'right' : 'left' ]: `${ model.value.s }%`
    }));

    const computedPalette = computed(() => (
      props.palette !== void 0 && props.palette.length > 0
        ? props.palette
        : palette
    ));

    const classes = 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 = computed(() => {
      if (props.disable === true) {
        return { 'aria-disabled': 'true' }
      }
      if (props.readonly === true) {
        return { 'aria-readonly': 'true' }
      }
      return {}
    });

    const spectrumDirective = computed(() => {
      // if editable.value === true
      return [ [
        TouchPan,
        onSpectrumPan,
        void 0,
        { prevent: true, stop: true, mouse: true }
      ] ]
    });

    watch(() => props.modelValue, v => {
      const localModel = parseModel(v || props.defaultValue);
      if (localModel.hex !== model.value.hex) {
        model.value = localModel;
      }
    });

    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;
        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;
        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(
        h(QTabs, {
          class: 'q-color-picker__header-tabs',
          modelValue: topView.value,
          dense: true,
          align: 'justify',
          ...getCache('topVTab', {
            'onUpdate:modelValue': val => { topView.value = val; }
          })
        }, () => [
          h(QTab, {
            label: 'HEX' + (hasAlpha.value === true ? 'A' : ''),
            name: 'hex',
            ripple: false
          }),

          h(QTab, {
            label: 'RGB' + (hasAlpha.value === true ? 'A' : ''),
            name: 'rgb',
            ripple: false
          })
        ])
      );

      child.push(
        h('div', {
          class: 'q-color-picker__header-banner row flex-center no-wrap'
        }, [
          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);
              }
            })
          }),

          h(QIcon, {
            ref: errorIconRef,
            class: 'q-color-picker__error-icon absolute no-pointer-events',
            name: $q.iconSet.type.negative
          })
        ])
      );

      return h('div', {
        class: 'q-color-picker__header relative-position overflow-hidden'
      }, [
        h('div', { class: 'q-color-picker__header-bg absolute-full' }),

        h('div', {
          class: headerClass.value,
          style: currentBgColor.value
        }, child)
      ])
    }

    function getContent () {
      return h(QTabPanels, {
        modelValue: view.value,
        animated: true
      }, () => [
        h(QTabPanel, {
          class: 'q-color-picker__spectrum-tab overflow-hidden',
          name: 'spectrum'
        }, getSpectrumTab),

        h(QTabPanel, {
          class: 'q-pa-md q-color-picker__tune-tab',
          name: 'tune'
        }, getTuneTab),

        h(QTabPanel, {
          class: 'q-color-picker__palette-tab',
          name: 'palette'
        }, getPaletteTab)
      ])
    }

    function getFooter () {
      return h('div', {
        class: 'q-color-picker__footer relative-position overflow-hidden'
      }, [
        h(QTabs, {
          class: 'absolute-full',
          modelValue: view.value,
          dense: true,
          align: 'justify',
          ...getCache('ftIn', {
            'onUpdate:modelValue': val => { view.value = val; }
          })
        }, () => [
          h(QTab, {
            icon: $q.iconSet.colorPicker.spectrum,
            name: 'spectrum',
            ripple: false
          }),

          h(QTab, {
            icon: $q.iconSet.colorPicker.tune,
            name: 'tune',
            ripple: false
          }),

          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 = [
        h('div', { style: { paddingBottom: '100%' } }),
        h('div', { class: 'q-color-picker__spectrum-white absolute-full' }),
        h('div', { class: 'q-color-picker__spectrum-black absolute-full' }),
        h('div', {
          class: 'absolute',
          style: spectrumPointerStyle.value
        }, [
          model.value.hex !== void 0
            ? h('div', { class: 'q-color-picker__spectrum-circle' })
            : null
        ])
      ];

      const sliders = [
        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(
        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),
        h('div', { class: 'q-color-picker__sliders' }, sliders)
      ]
    }

    function getTuneTab () {
      return [
        h('div', { class: 'row items-center no-wrap' }, [
          h('div', 'R'),
          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)
            })
          }),
          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)
            })
          })
        ]),

        h('div', { class: 'row items-center no-wrap' }, [
          h('div', 'G'),
          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)
            })
          }),
          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)
            })
          })
        ]),

        h('div', { class: 'row items-center no-wrap' }, [
          h('div', 'B'),
          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)
            })
          }),
          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 ? h('div', { class: 'row items-center no-wrap' }, [
          h('div', 'A'),
          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)
            })
          }),
          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 => h('div', {
        class: 'q-color-picker__cube col-auto',
        style: { backgroundColor: color },
        ...(
          editable.value === true
            ? getCache('palette#' + color, {
              onClick: () => { onPalettePick(color); }
            })
            : {}
        )
      });

      return [
        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 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 = computed(() => {
    return props.disable !== true && props.readonly !== true
  });

  const tabindex = computed(() => {
    return editable.value === true ? 0 : -1
  });

  const headerClass = 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 } = 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 = ref(null);
    const innerMask = ref(getMask());
    const innerLocale = ref(getLocale());

    const mask = computed(() => getMask());
    const locale = computed(() => getLocale());

    const today = computed(() => getCurrentDate());

    // model of current calendar view:
    const viewModel = ref(getViewModel(innerMask.value, innerLocale.value));

    const view = ref(props.defaultView);

    const direction = $q.lang.rtl === true ? 'right' : 'left';
    const monthDirection = ref(direction.value);
    const yearDirection = ref(direction.value);

    const year = viewModel.value.year;
    const startYear = ref(year - (year % yearsInterval) - (year < 0 ? yearsInterval : 0));
    const editRange = ref(null);

    const classes = 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 = computed(() => {
      return props.color || 'primary'
    });

    const computedTextColor = computed(() => {
      return props.textColor || 'white'
    });

    const isImmediate = computed(() =>
      props.emitImmediately === true
      && props.multiple !== true
      && props.range !== true
    );

    const normalizedModel = computed(() => (
      Array.isArray(props.modelValue) === true
        ? props.modelValue
        : (props.modelValue !== null && props.modelValue !== void 0 ? [ props.modelValue ] : [])
    ));

    const daysModel = 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 = 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 = 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 = 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 = computed(() =>
      daysModel.value.length + rangeModel.value.reduce(
        (acc, range) => acc + 1 + getDateDiff(
          getNativeDateFn.value(range.to),
          getNativeDateFn.value(range.from)
        ),
        0
      )
    );

    const headerTitle = 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 = 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 = 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 = 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 = computed(() => {
      const val = [ $q.iconSet.datetime.arrowLeft, $q.iconSet.datetime.arrowRight ];
      return $q.lang.rtl === true ? val.reverse() : val
    });

    const computedFirstDayOfWeek = computed(() => (
      props.firstDayOfWeek !== void 0
        ? Number(props.firstDayOfWeek)
        : innerLocale.value.firstDayOfWeek
    ));

    const daysOfWeek = computed(() => {
      const
        days = innerLocale.value.daysShort,
        first = computedFirstDayOfWeek.value;

      return first > 0
        ? days.slice(first, 7).concat(days.slice(0, first))
        : days
    });

    const daysInMonth = computed(() => {
      const date = viewModel.value;
      return props.calendar !== 'persian'
        ? (new Date(date.year, date.month, 0)).getDate()
        : jalaaliMonthLength(date.year, date.month)
    });

    const evtColor = computed(() => (
      typeof props.eventColor === 'function'
        ? props.eventColor
        : () => props.eventColor
    ));

    const minNav = 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 = 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 = 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 = 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 = 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 = 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 = computed(() => getMonthHash(viewModel.value));

    const selectionDaysMap = 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 = 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 = 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 = 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 = computed(() => (
      props.disable === true
        ? { 'aria-disabled': 'true' }
        : (props.readonly === true ? { 'aria-readonly': 'true' } : {})
    ));

    watch(() => props.modelValue, v => {
      if (lastEmitValue === v) {
        lastEmitValue = 0;
      }
      else {
        const model = getViewModel(innerMask.value, innerLocale.value);
        updateViewModel(model.year, model.month, model);
      }
    });

    watch(view, () => {
      if (blurTargetRef.value !== null && proxy.$el.contains(document.activeElement) === true) {
        blurTargetRef.value.focus();
      }
    });

    watch(() => viewModel.value.year + '|' + viewModel.value.month, () => {
      emit('navigation', { year: viewModel.value.year, month: viewModel.value.month });
    });

    watch(mask, val => {
      updateValue(val, innerLocale.value, 'mask');
      innerMask.value = val;
    });

    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;
        }

        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
      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 h('div', {
        class: 'q-date__header ' + headerClass.value
      }, [
        h('div', {
          class: 'relative-position'
        }, [
          h(Transition, {
            name: 'q-transition--fade'
          }, () => 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 ]))
        ]),

        h('div', {
          class: 'q-date__header-title relative-position flex no-wrap'
        }, [
          h('div', {
            class: 'relative-position col'
          }, [
            h(Transition, {
              name: 'q-transition--fade'
            }, () => 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 ? 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 [
        h('div', {
          class: 'row items-center q-date__arrow'
        }, [
          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); } })
          })
        ]),

        h('div', {
          class: 'relative-position overflow-hidden flex flex-center' + cls
        }, [
          h(Transition, {
            name: 'q-transition--jump-' + dir
          }, () => h('div', { key }, [
            h(QBtn, {
              flat: true,
              dense: true,
              noCaps: true,
              label,
              tabindex: tabindex.value,
              ...getCache('view#' + type, { onClick: () => { view.value = type; } })
            })
          ]))
        ]),

        h('div', {
          class: 'row items-center q-date__arrow'
        }, [
          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: () => ([
        h('div', {
          key: 'calendar-view',
          class: 'q-date__view q-date__calendar'
        }, [
          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: ''
          }))),

          h('div', {
            class: 'q-date__calendar-weekdays row items-center no-wrap'
          }, daysOfWeek.value.map(day => h('div', { class: 'q-date__calendar-item' }, [ h('div', day) ]))),

          h('div', {
            class: 'q-date__calendar-days-container relative-position overflow-hidden'
          }, [
            h(Transition, {
              name: 'q-transition--slide-' + monthDirection.value
            }, () => h('div', {
              key: viewMonthHash.value,
              class: 'q-date__calendar-days fit'
            }, days.value.map(day => h('div', { class: day.classes }, [
              day.in === true
                ? 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
                    ? () => h('div', { class: 'q-date__event bg-' + day.event })
                    : null
                )
                : 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 h('div', {
            class: 'q-date__months-item flex flex-center'
          }, [
            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(
          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 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(
            h('div', {
              class: 'q-date__years-item flex flex-center'
            }, [
              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 h('div', {
          class: 'q-date__view q-date__years flex flex-center'
        }, [
          h('div', {
            class: 'col-auto'
          }, [
            h(QBtn, {
              round: true,
              dense: true,
              flat: true,
              icon: dateArrow.value[ 0 ],
              tabindex: tabindex.value,
              disable: isDisabled(start),
              ...getCache('y-', { onClick: () => { startYear.value -= yearsInterval; } })
            })
          ]),

          h('div', {
            class: 'q-date__years-content col self-stretch row items-center'
          }, years),

          h('div', {
            class: 'col-auto'
          }, [
            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 = [
        h('div', {
          class: 'q-date__content col relative-position'
        }, [
          h(Transition, {
            name: 'q-transition--fade'
          }, renderViews[ view.value ])
        ])
      ];

      const def = hSlot(slots.default);
      def !== void 0 && content.push(
        h('div', { class: 'q-date__actions' }, def)
      );

      if (props.name !== void 0 && props.disable !== true) {
        injectFormInput(content, 'push');
      }

      return h('div', {
        class: classes.value,
        ...attributes.value
      }, [
        getHeader(),

        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;
    }
  }

  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 = getCurrentInstance();

    const innerRef = ref(null);
    const showing = ref(false);
    const animating = ref(false);

    let shakeTimeout = null, refocusTarget = null, isMaximized, avoidAutoClose;

    const hideOnRouteChange = 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 = 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 = computed(() => showing.value === true && props.seamless !== true);

    const onEvents = computed(() => (
      props.autoClose === true
        ? { onClick: onAutoClose }
        : {}
    ));

    const rootClasses = computed(() => [
      'q-dialog fullscreen no-pointer-events '
        + `q-dialog--${ useBackdrop.value === true ? 'modal' : 'seamless' }`,
      attrs.class
    ]);

    watch(() => props.maximized, state => {
      showing.value === true && updateMaximized(state);
    });

    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;
      }
    });

    onBeforeUnmount(cleanup);

    function renderPortalContent () {
      return h('div', {
        role: 'dialog',
        'aria-modal': useBackdrop.value === true ? 'true' : 'false',
        ...attrs,
        class: rootClasses.value
      }, [
        h(Transition, {
          name: 'q-transition--fade',
          appear: true
        }, () => (
          useBackdrop.value === true
            ? h('div', {
              class: 'q-dialog__backdrop fixed-full',
              style: transitionStyle.value,
              'aria-hidden': 'true',
              tabindex: -1,
              onClick: onBackdropClick
            })
            : null
        )),

        h(
          Transition,
          transitionProps.value,
          () => (
            showing.value === true
              ? 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
    },

    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 = getCurrentInstance();
    const { proxy: { $q } } = vm;

    const isDark = useDark(props, $q);
    const { preventBodyScroll } = usePreventScroll();
    const { registerTimeout, removeTimeout } = useTimeout();

    const $layout = inject(layoutKey, emptyRenderFn);
    if ($layout === emptyRenderFn) {
      console.error('QDrawer needs to be child of QLayout');
      return emptyRenderFn
    }

    let lastDesktopState, timerMini = null, layoutTotalWidthWatcher;

    const belowBreakpoint = ref(
      props.behavior === 'mobile'
      || (props.behavior !== 'desktop' && $layout.totalWidth.value <= props.breakpoint)
    );

    const isMini = computed(() =>
      props.mini === true && belowBreakpoint.value !== true
    );

    const size = computed(() => (
      isMini.value === true
        ? props.miniWidth
        : props.width
    ));

    const showing = ref(
      props.showIfAbove === true && belowBreakpoint.value === false
        ? true
        : props.modelValue === true
    );

    const hideOnRouteChange = 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 = computed(() => props.side === 'right');

    const stateDirection = computed(() =>
      ($q.lang.rtl === true ? -1 : 1) * (rightSide.value === true ? 1 : -1)
    );

    const flagBackdropBg = ref(0);
    const flagPanning = ref(false);
    const flagMiniAnimate = ref(false);
    const flagContentPosition = ref( // starting with "hidden" for SSR
      size.value * stateDirection.value
    );

    const otherSide = computed(() => (rightSide.value === true ? 'left' : 'right'));
    const offset = computed(() => (
      showing.value === true && belowBreakpoint.value === false && props.overlay === false
        ? (props.miniToOverlay === true ? props.miniWidth : size.value)
        : 0
    ));

    const fixed = 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 = computed(() =>
      props.overlay === false
      && showing.value === true
      && belowBreakpoint.value === false
    );

    const onScreenOverlay = computed(() =>
      props.overlay === true
      && showing.value === true
      && belowBreakpoint.value === false
    );

    const backdropClass = computed(() =>
      'fullscreen q-drawer__backdrop'
      + (showing.value === false && flagPanning.value === false ? ' hidden' : '')
    );

    const backdropStyle = computed(() => ({
      backgroundColor: `rgba(0,0,0,${ flagBackdropBg.value * 0.4 })`
    }));

    const headerSlot = computed(() => (
      rightSide.value === true
        ? $layout.rows.value.top[ 2 ] === 'r'
        : $layout.rows.value.top[ 0 ] === 'l'
    ));

    const footerSlot = computed(() => (
      rightSide.value === true
        ? $layout.rows.value.bottom[ 2 ] === 'r'
        : $layout.rows.value.bottom[ 0 ] === 'l'
    ));

    const aboveStyle = 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 = computed(() => {
      const style = {
        width: `${ size.value }px`,
        transform: `translateX(${ flagContentPosition.value }px)`
      };

      return belowBreakpoint.value === true
        ? style
        : Object.assign(style, aboveStyle.value)
    });

    const contentClass = computed(() =>
      'q-drawer__content fit '
      + ($layout.isContainer.value !== true ? 'scroll' : 'overflow-auto')
    );

    const classes = 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 = 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 = 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 = 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)
      ));
    }

    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);
        }
      }
    });

    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;
    });

    watch($layout.totalWidth, () => {
      if ($layout.isContainer.value === true || document.qScrollPrevented !== true) {
        updateBelowBreakpoint();
      }
    });

    watch(
      () => props.behavior + props.breakpoint,
      updateBelowBreakpoint
    );

    watch($layout.isContainer, val => {
      showing.value === true && preventBodyScroll(val !== true);
      val === true && updateBelowBreakpoint();
    });

    watch($layout.scrollbarWidth, () => {
      applyPosition(showing.value === true ? 0 : void 0);
    });

    watch(offset, val => { updateLayout('offset', val); });

    watch(onLayout, val => {
      emit('onLayout', val);
      updateLayout('space', val);
    });

    watch(rightSide, () => { applyPosition(); });

    watch(size, val => {
      applyPosition();
      updateSizeOnLayout(props.miniToOverlay, val);
    });

    watch(() => props.miniToOverlay, val => {
      updateSizeOnLayout(val, size.value);
    });

    watch(() => $q.lang.rtl, () => { applyPosition(); });

    watch(() => props.mini, () => {
      if (props.modelValue === true) {
        animateMini();
        $layout.animate();
      }
    });

    watch(isMini, val => { emit('miniState', val); });

    function applyPosition (position) {
      if (position === void 0) {
        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);
    }

    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()
        nextTick(fn);
        return
      }

      layoutTotalWidthWatcher = watch($layout.totalWidth, () => {
        layoutTotalWidthWatcher();
        layoutTotalWidthWatcher = void 0;

        if (showing.value === false && props.showIfAbove === true && belowBreakpoint.value === false) {
          show(false);
        }
        else {
          fn();
        }
      });
    });

    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(
          withDirectives(
            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 = [
        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(
          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 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(`
        <!doctype html>
        <html>
          <head>
            <title>Print - ${ document.title }</title>
          </head>
          <body>
            <div>${ this.el.innerHTML }</div>
          </body>
        </html>
      `);
      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 = getCurrentInstance();
    const { proxy: { $q } } = vm;

    const innerRef = ref(null);
    const showing = ref(false);

    const anchorOrigin = computed(() => parsePosition(props.anchor, $q.lang.rtl));
    const selfOrigin = computed(() => parsePosition(props.self, $q.lang.rtl));
    const hideOnRouteChange = 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 = 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
      );

      watch(hasClickOutside, val => {
        const fn = val === true ? addClickOutside : removeClickOutside;
        fn(clickOutsideProps);
      });

      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 = 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 () {
      const el = innerRef.value;

      if (anchorEl.value === null || !el) {
        return
      }

      setPosition({
        el,
        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
        ? 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 h(Transition, transitionProps.value, getTooltipContent)
    }

    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 } } = getCurrentInstance();

    const isDark = useDark(props, $q);
    const { hasLink, linkAttrs, linkClass, linkTag, navigateOnClick } = useRouterLink();

    const rootRef = ref(null);
    const blurTargetRef = ref(null);

    const isActionable = computed(() =>
      props.clickable === true
        || hasLink.value === true
        || props.tag === 'label'
    );

    const isClickable = computed(() =>
      props.disable !== true && isActionable.value === true
    );

    const classes = 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 = 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(
        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 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 = 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 () => 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 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
      ? h('div', [
        h('small', `(CTRL + ${ String.fromCharCode(btn.key) })`)
      ])
      : null;
    child.push(
      h(QTooltip, { delay: 1000 }, () => [
        h('div', { innerHTML: btn.tip }),
        Key
      ])
    );
  }

  return 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 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
          : h(
            QItemSection,
            {
              class: active ? activeClass : inactiveClass,
              side: true
            },
            () => h(QIcon, { name: btn.icon !== null ? btn.icon : void 0 })
          ),

        h(
          QItemSection,
          htmlTip
            ? () => h('div', { class: 'text-no-wrap', innerHTML: btn.htmlTip })
            : (btn.tip ? () => h('div', { class: 'text-no-wrap' }, btn.tip) : void 0)
        )
      ])
    });
    contentClass = [ eVm.toolbarBackgroundClass.value, inactiveClass ];
  }

  const highlight = btn.highlight && label !== btn.label;
  const Dropdown = 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: `<font face="${ name }">${ name }</font>`
    };
  });

  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 [
      h('div', { class: `q-mx-xs text-${ color }` }, `${ eVm.$q.lang.editor.url }: `),
      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([
        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;
          }
        }),
        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: ref({}),
    attributes: 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;
  }

  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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);
    const { inFullscreen, toggleFullscreen } = useFullscreen();
    const splitAttrs = useSplitAttrs(attrs, vnode);

    const rootRef = ref(null);
    const contentRef = ref(null);

    const editLinkUrl = ref(null);
    const isViewingSource = ref(false);

    const editable = computed(() => !props.readonly && !props.disable);

    let defaultFont, offsetBottom;
    let lastEmit = props.modelValue; // eslint-disable-line

    {
      document.execCommand('defaultParagraphSeparator', false, props.paragraphTag);
      defaultFont = window.getComputedStyle(document.body).fontFamily;
    }

    const toolbarBackgroundClass = computed(() => (
      props.toolbarBg ? ` bg-${ props.toolbarBg }` : ''
    ));

    const buttonProps = 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 = 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: 'x<subscript>2</subscript>' },
        superscript: { cmd: 'superscript', icon: i.superscript, tip: e.superscript, htmlTip: 'x<superscript>2</superscript>' },
        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: `<h1 class="q-ma-none">${ e.heading1 }</h1>` },
        h2: { cmd: 'formatBlock', param: 'H2', icon: i.heading2 || i.heading, tip: e.heading2, htmlTip: `<h2 class="q-ma-none">${ e.heading2 }</h2>` },
        h3: { cmd: 'formatBlock', param: 'H3', icon: i.heading3 || i.heading, tip: e.heading3, htmlTip: `<h3 class="q-ma-none">${ e.heading3 }</h3>` },
        h4: { cmd: 'formatBlock', param: 'H4', icon: i.heading4 || i.heading, tip: e.heading4, htmlTip: `<h4 class="q-ma-none">${ e.heading4 }</h4>` },
        h5: { cmd: 'formatBlock', param: 'H5', icon: i.heading5 || i.heading, tip: e.heading5, htmlTip: `<h5 class="q-ma-none">${ e.heading5 }</h5>` },
        h6: { cmd: 'formatBlock', param: 'H6', icon: i.heading6 || i.heading, tip: e.heading6, htmlTip: `<h6 class="q-ma-none">${ e.heading6 }</h6>` },
        p: { cmd: 'formatBlock', param: props.paragraphTag, icon: i.heading, tip: e.paragraph },
        code: { cmd: 'formatBlock', param: 'PRE', icon: i.code, htmlTip: `<code>${ e.code }</code>` },

        'size-1': { cmd: 'fontSize', param: '1', icon: i.size1 || i.size, tip: e.size1, htmlTip: `<font size="1">${ e.size1 }</font>` },
        'size-2': { cmd: 'fontSize', param: '2', icon: i.size2 || i.size, tip: e.size2, htmlTip: `<font size="2">${ e.size2 }</font>` },
        'size-3': { cmd: 'fontSize', param: '3', icon: i.size3 || i.size, tip: e.size3, htmlTip: `<font size="3">${ e.size3 }</font>` },
        'size-4': { cmd: 'fontSize', param: '4', icon: i.size4 || i.size, tip: e.size4, htmlTip: `<font size="4">${ e.size4 }</font>` },
        'size-5': { cmd: 'fontSize', param: '5', icon: i.size5 || i.size, tip: e.size5, htmlTip: `<font size="5">${ e.size5 }</font>` },
        'size-6': { cmd: 'fontSize', param: '6', icon: i.size6 || i.size, tip: e.size6, htmlTip: `<font size="6">${ e.size6 }</font>` },
        'size-7': { cmd: 'fontSize', param: '7', icon: i.size7 || i.size, tip: e.size7, htmlTip: `<font size="7">${ e.size7 }</font>` }
      }
    });

    const buttons = 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
    };

    watch(() => props.modelValue, v => {
      if (lastEmit !== v) {
        lastEmit = v;
        setContent(v, true);
      }
    });

    watch(editLinkUrl, v => {
      emit(`link-${ v ? 'Show' : 'Hide' }`);
    });

    const hasToolbar = computed(() => props.toolbar && props.toolbar.length > 0);

    const keys = 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 = computed(() => (
      inFullscreen.value
        ? props.contentStyle
        : [
            {
              minHeight: props.minHeight,
              height: props.height,
              maxHeight: props.maxHeight
            },
            props.contentStyle
          ]
    ));

    const classes = 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 = computed(() => ([
      props.contentClass,
      'q-editor__content',
      { col: inFullscreen.value, 'overflow-auto': inFullscreen.value || props.maxHeight }
    ]));

    const attributes = 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) {
      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
    }

    onMounted(() => {
      eVm.caret = proxy.caret = new Caret(contentRef.value, eVm);
      setContent(props.modelValue);
      refreshToolbar();

      document.addEventListener('selectionchange', onSelectionchange);
    });

    onBeforeUnmount(() => {
      document.removeEventListener('selectionchange', onSelectionchange);
    });

    // expose public methods
    Object.assign(proxy, {
      runCmd, refreshToolbar, focus, getContentEl
    });

    return () => {
      let toolbars;

      if (hasToolbar.value) {
        const bars = [
          h('div', {
            key: 'qedt_top',
            class: 'q-editor__toolbar row no-wrap scroll-x'
              + toolbarBackgroundClass.value
          }, getToolbar(eVm))
        ];

        editLinkUrl.value !== null && bars.push(
          h('div', {
            key: 'qedt_btm',
            class: 'q-editor__toolbar row no-wrap items-center scroll-x'
              + toolbarBackgroundClass.value
          }, getLinkEditor(eVm))
        );

        toolbars = h('div', {
          key: 'toolbar_ctainer',
          class: 'q-editor__toolbars-container'
        }, bars);
      }

      return h('div', {
        ref: rootRef,
        class: classes.value,
        style: { height: inFullscreen.value === true ? '100%' : null },
        ...attributes.value,
        onFocusin,
        onFocusout
      }, [
        toolbars,

        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 = computed(() => parseInt(props.lines, 10));

    const classes = 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 = 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 () => 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);
    }

    onBeforeUnmount(() => {
      animating === true && cleanup();
    });

    return () => h(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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const orientation = computed(() => (
      props.vertical === true
        ? 'vertical'
        : 'horizontal'
    ));

    const orientClass = computed(() => ` q-separator--${ orientation.value }`);

    const insetClass = computed(() => (
      props.inset !== false
        ? `${ orientClass.value }-${ insetMap[ props.inset ] }`
        : ''
    ));

    const classes = computed(() =>
      `q-separator${ orientClass.value }${ insetClass.value }`
      + (props.color !== void 0 ? ` bg-${ props.color }` : '')
      + (isDark.value === true ? ' q-separator--dark' : '')
    );

    const style = 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 () => h('hr', {
      class: classes.value,
      style: style.value,
      'aria-orientation': orientation.value
    })
  }
});

const itemGroups = 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 } } = getCurrentInstance();
    const isDark = useDark(props, $q);

    const showing = ref(
      props.modelValue !== null
        ? props.modelValue
        : props.defaultOpened
    );

    const blurTargetRef = ref(null);
    const targetUid = uid$3();

    const { show, hide, toggle } = useModelToggle({ showing });

    let uniqueId, exitGroup;

    const classes = 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 = 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 = computed(() =>
      props.disable !== true && (
        props.href !== void 0
        || (props.to !== void 0 && props.to !== null && props.to !== '')
      )
    );

    const linkProps = computed(() => {
      const acc = {};
      LINK_PROPS.forEach(key => {
        acc[ key ] = props[ key ];
      });
      return acc
    });

    const isClickable = computed(() =>
      hasLink.value === true || props.expandIconToggle !== true
    );

    const expansionIcon = computed(() => (
      props.expandedIcon !== void 0 && showing.value === true
        ? props.expandedIcon
        : props.expandIcon || $q.iconSet.expansionItem[ props.denseToggle === true ? 'denseIcon' : 'icon' ]
    ));

    const activeToggleIcon = computed(() =>
      props.disable !== true && (hasLink.value === true || props.expandIconToggle === true)
    );

    const headerSlotScope = computed(() => ({
      expanded: showing.value === true,
      detailsId: props.targetUid,
      toggle,
      show,
      hide
    }));

    const toggleAriaAttrs = 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
      }
    });

    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 = watch(showing, val => {
        if (val === true) {
          itemGroups[ props.group ] = uniqueId;
        }
        else if (itemGroups[ props.group ] === uniqueId) {
          delete itemGroups[ props.group ];
        }
      });

      const group = 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 = [
        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(
          h('div', {
            ref: blurTargetRef,
            class: 'q-expansion-item__toggle-focus q-icon q-focus-helper q-focus-helper--rounded',
            tabindex: -1
          })
        );
      }

      return h(QItemSection, data, () => child)
    }

    function getHeaderChild () {
      let child;

      if (slots.header !== void 0) {
        child = [].concat(slots.header(headerSlotScope.value));
      }
      else {
        child = [
          h(QItemSection, () => [
            h(QItemLabel, { lines: props.labelLines }, () => props.label || ''),

            props.caption
              ? h(QItemLabel, { lines: props.captionLines, caption: true }, () => props.caption)
              : null
          ])
        ];

        props.icon && child[ props.switchToggleSide === true ? 'push' : 'unshift' ](
          h(QItemSection, {
            side: props.switchToggleSide === true,
            avatar: props.switchToggleSide !== true
          }, () => 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 h(QItem, data, getHeaderChild)
    }

    function getTransitionChild () {
      return withDirectives(
        h('div', {
          key: 'e-content',
          class: 'q-expansion-item__content relative-position',
          style: contentStyle.value,
          id: targetUid
        }, hSlot(slots.default)),
        [ [
          vShow,
          showing.value
        ] ]
      )
    }

    function getContent () {
      const node = [
        getHeader(),

        h(QSlideTransition, {
          duration: props.duration,
          onShow,
          onHide
        }, getTransitionChild)
      ];

      if (props.expandSeparator === true) {
        node.push(
          h(QSeparator, {
            class: 'q-expansion-item__border q-expansion-item__border--top absolute-top',
            dark: isDark.value
          }),
          h(QSeparator, {
            class: 'q-expansion-item__border q-expansion-item__border--bottom absolute-bottom',
            dark: isDark.value
          })
        );
      }

      return node
    }

    props.group !== void 0 && enterGroup();

    onBeforeUnmount(() => {
      exitGroup !== void 0 && exitGroup();
    });

    return () => h('div', { class: classes.value }, [
      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: computed(() =>
      `q-fab--form-${ props.square === true ? 'square' : 'rounded' }`
    ),

    stacked: computed(() =>
      props.externalLabel === false
      && [ 'top', 'bottom' ].includes(props.labelPosition)
    ),

    labelProps: 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 = ref(null);
    const showing = ref(props.modelValue === true);
    const targetUid = uid$3();

    const { proxy: { $q } } = getCurrentInstance();
    const { formClass, labelProps } = useFab(props, showing);

    const hideOnRouteChange = computed(() => props.persistent !== true);

    const { hide, toggle } = useModelToggle({
      showing,
      hideOnRouteChange
    });

    const slotScope = computed(() => ({ opened: showing.value }));

    const classes = 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 = computed(() =>
      'q-fab__actions flex no-wrap inline'
      + ` q-fab__actions--${ props.direction }`
      + ` q-fab__actions--${ showing.value === true ? 'opened' : 'closed' }`
    );

    const actionAttrs = computed(() => {
      const attrs = {
        id: targetUid,
        role: 'menu'
      };

      if (showing.value !== true) {
        attrs[ 'aria-hidden' ] = 'true';
      }

      return attrs
    });

    const iconHolderClass = 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
        ? h(QIcon, { class: classes, name: props[ camel ] || $q.iconSet.fab[ camel ] })
        : h('div', { class: classes }, slotFn(slotScope.value))
    }

    function getTriggerContent () {
      const child = [];

      props.hideIcon !== true && child.push(
        h('div', { class: iconHolderClass.value }, [
          getIcon('icon', 'icon'),
          getIcon('active-icon', 'activeIcon')
        ])
      );

      if (props.label !== '' || slots.label !== void 0) {
        child[ labelProps.value.action ](
          h('div', labelProps.value.data, slots.label !== void 0 ? slots.label(slotScope.value) : [ props.label ])
        );
      }

      return hMergeSlot(slots.tooltip, child)
    }

    provide(fabKey, {
      showing,

      onChildClick (evt) {
        hide(evt);

        if (triggerRef.value !== null) {
          triggerRef.value.$el.focus();
        }
      }
    });

    return () => h('div', {
      class: classes.value
    }, [
      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),

      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 = inject(fabKey, () => ({
      showing: { value: true },
      onChildClick: noop
    }));

    const { formClass, labelProps } = useFab(props, $fab.showing);

    const classes = computed(() => {
      const align = anchorMap[ props.anchor ];
      return formClass.value + (align !== void 0 ? ` ${ align }` : '')
    });

    const isDisabled = 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(
          h(QIcon, { name: props.icon })
        );
      }

      if (props.label !== '' || slots.label !== void 0) {
        child[ labelProps.value.action ](
          h('div', labelProps.value.data, slots.label !== void 0 ? slots.label() : [ props.label ])
        );
      }

      return hMergeSlot(slots.default, child)
    }

    // expose public methods
    const vm = getCurrentInstance();
    Object.assign(vm.proxy, { click });

    return () => 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 = inject(formKey, false);

  if ($form !== false) {
    const { props, proxy } = getCurrentInstance();

    // export public method (so it can be used in QForm)
    Object.assign(proxy, { validate, resetValidation });

    watch(() => props.disable, val => {
      if (val === true) {
        typeof resetValidation === 'function' && resetValidation();
        $form.unbindComponent(proxy);
      }
      else {
        $form.bindComponent(proxy);
      }
    });

    onMounted(() => {
      // register to parent QForm
      props.disable !== true && $form.bindComponent(proxy);
    });

    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 } = getCurrentInstance();

  const innerError = ref(false);
  const innerErrorMessage = ref(null);
  const isDirtyModel = ref(null);

  useFormChild({ validate, resetValidation });

  let validateIndex = 0, unwatchRules;

  const hasRules = computed(() =>
    props.rules !== void 0
    && props.rules !== null
    && props.rules.length > 0
  );

  const hasActiveRules = computed(() =>
    props.disable !== true
    && hasRules.value === true
  );

  const hasError = computed(() =>
    props.error === true || innerError.value === true
  );

  const errorMessage = computed(() => (
    typeof props.errorMessage === 'string' && props.errorMessage.length > 0
      ? props.errorMessage
      : innerErrorMessage.value
  ));

  watch(() => props.modelValue, () => {
    validateIfNeeded();
  });

  watch(() => props.reactiveRules, val => {
    if (val === true) {
      if (unwatchRules === void 0) {
        unwatchRules = watch(() => props.rules, () => {
          validateIfNeeded(true);
        });
      }
    }
    else if (unwatchRules !== void 0) {
      unwatchRules();
      unwatchRules = void 0;
    }
  }, { immediate: true });

  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);

  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 } = getCurrentInstance();

  const isDark = useDark(props, proxy.$q);

  return {
    isDark,

    editable: computed(() =>
      props.disable !== true && props.readonly !== true
    ),

    innerLoading: ref(false),
    focused: ref(false),
    hasPopupOpen: false,

    splitAttrs: useSplitAttrs(attrs, vnode),
    targetUid: ref(getTargetUid(props.for)),

    rootRef: ref(null),
    targetRef: ref(null),
    controlRef: 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 } = getCurrentInstance();
  const { $q } = proxy;

  let focusoutTimer = null;

  if (state.hasValue === void 0) {
    state.hasValue = 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 = 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
    ? computed(() => props.stackLabel === true || state.focused.value === true || state.floatingLabel.value === true)
    : computed(() => props.stackLabel === true || state.focused.value === true || state.hasValue.value === true);

  const shouldRenderBottom = computed(() =>
    props.bottomSlots === true
    || props.hint !== void 0
    || hasRules.value === true
    || props.counter === true
    || props.error !== null
  );

  const styleType = 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 = 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 = 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 = computed(() =>
    props.labelSlot === true || props.label !== void 0
  );

  const labelClass = computed(() =>
    'q-field__label no-pointer-events absolute ellipsis'
    + (props.labelColor !== void 0 && hasError.value !== true ? ` text-${ props.labelColor }` : '')
  );

  const controlSlotScope = computed(() => ({
    id: state.targetUid.value,
    editable: state.editable.value,
    focused: state.focused.value,
    floatingLabel: floatingLabel.value,
    modelValue: props.modelValue,
    emitValue: state.emitValue
  }));

  const attributes = 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
  });

  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);

    nextTick(() => {
      resetValidation();

      if ($q.platform.is.mobile !== true) {
        isDirtyModel.value = false;
      }
    });
  }

  function getContent () {
    const node = [];

    slots.prepend !== void 0 && node.push(
      h('div', {
        class: 'q-field__prepend q-field__marginal row no-wrap items-center',
        key: 'prepend',
        onClick: prevent
      }, slots.prepend())
    );

    node.push(
      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', [
        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()
            : [ h(QSpinner, { color: props.color }) ]
        )
      );
    }
    else if (props.clearable === true && state.hasValue.value === true && state.editable.value === true) {
      node.push(
        getInnerAppendNode('inner-clearable-append', [
          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(
      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(
      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(
        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(
      h('div', {
        class: labelClass.value
      }, hSlot(slots.label, props.label))
    );

    props.suffix !== void 0 && props.suffix !== null && node.push(
      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 = [ 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 = [ 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 = h('div', {
      key,
      class: 'q-field__messages col'
    }, msg);

    return h('div', {
      class: 'q-field__bottom row items-start q-field__bottom--'
        + (props.hideBottomSpace !== true ? 'animated' : 'stale'),
      onClick: prevent
    }, [
      props.hideBottomSpace === true
        ? main
        : h(Transition, { name: 'q-transition--field-message' }, () => main),

      hasCounter === true
        ? h('div', {
          class: 'q-field__counter'
        }, slots.counter !== void 0 ? slots.counter() : state.computedCounter.value)
        : null
    ])
  }

  function getInnerAppendNode (key, content) {
    return content === null
      ? null
      : h('div', {
        key,
        class: 'q-field__append q-field__marginal row no-wrap items-center q-anchor--skip'
      }, content)
  }

  let shouldActivate = false;

  onDeactivated(() => {
    shouldActivate = true;
  });

  onActivated(() => {
    shouldActivate === true && props.autofocus === true && proxy.focus();
  });

  onMounted(() => {
    if (isRuntimeSsrPreHydration.value === true && props.for === void 0) {
      state.targetUid.value = getTargetUid();
    }

    props.autofocus === true && proxy.focus();
  });

  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 h('label', {
      ref: state.rootRef,
      class: [
        classes.value,
        attrs.class
      ],
      style: attrs.style,
      ...labelAttrs
    }, [
      slots.before !== void 0
        ? h('div', {
          class: 'q-field__before q-field__marginal row no-wrap items-center',
          onClick: prevent
        }, slots.before())
        : null,

      h('div', {
        class: 'q-field__inner relative-position col self-stretch'
      }, [
        h('div', {
          ref: state.controlRef,
          class: contentClass.value,
          tabindex: -1,
          ...state.controlEvents
        }, getContent()),

        shouldRenderBottom.value === true
          ? getBottom()
          : null
      ]),

      slots.after !== void 0
        ? 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 } = getCurrentInstance();

  const dndRef = ref(null);

  const extensions = 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 = computed(() => parseInt(props.maxFiles, 10));
  const maxTotalSizeNumber = 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 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
    ? computed(() => {
      if (props.type !== 'file') {
        return
      }

      return getFormDomProps()
    })
    : 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 } = getCurrentInstance();

    const state = useFieldState();

    const inputRef = ref(null);
    const dnd = 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 = computed(() => (
      Object(props.modelValue) === props.modelValue
        ? ('length' in props.modelValue ? Array.from(props.modelValue) : [ props.modelValue ])
        : []
    ));

    const hasValue = computed(() => fieldValueIsFilled(innerValue.value));

    const selectedString = computed(() =>
      innerValue.value
        .map(file => file.name)
        .join(', ')
    );

    const totalSize = computed(() =>
      humanStorageSize(
        innerValue.value.reduce((acc, file) => acc + file.size, 0)
      )
    );

    const counterProps = computed(() => ({
      totalSize: totalSize.value,
      filesNumber: innerValue.value.length,
      maxFiles: props.maxFiles
    }));

    const inputAttrs = 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 = computed(() =>
      'q-file q-field--auto-height'
      + (dnd.value === true ? ' q-file--dnd' : '')
    );

    const isAppending = 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.findIndex(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 [
        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) => h(QChip, {
            key: 'file-' + i,
            removable: state.editable.value,
            dense: true,
            textColor: props.color,
            tabindex: props.tabindex,
            onRemove: () => { removeAtIndex(i); }
          }, () => h('span', {
            class: 'ellipsis',
            textContent: file.name
          })))
      }

      const textContent = props.displayValue !== void 0
        ? props.displayValue
        : selectedString.value;

      return textContent.length > 0
        ? [
            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 h('input', data)
    }

    Object.assign(state, {
      fieldClass,
      emitValue,
      hasValue,
      inputRef,
      innerValue,

      floatingLabel: computed(() =>
        hasValue.value === true
        || fieldValueIsFilled(props.displayValue)
      ),

      computedCounter: 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 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 } } = getCurrentInstance();

    const $layout = inject(layoutKey, emptyRenderFn);
    if ($layout === emptyRenderFn) {
      console.error('QFooter needs to be child of QLayout');
      return emptyRenderFn
    }

    const size = ref(parseInt(props.heightHint, 10));
    const revealed = ref(true);
    const windowHeight = ref(
      isRuntimeSsrPreHydration.value === true || $layout.isContainer.value === true
        ? 0
        : window.innerHeight
    );

    const fixed = computed(() =>
      props.reveal === true
      || $layout.view.value.indexOf('F') > -1
      || ($q.platform.is.ios && $layout.isContainer.value === true)
    );

    const containerHeight = computed(() => (
      $layout.isContainer.value === true
        ? $layout.containerHeight.value
        : windowHeight.value
    ));

    const offset = 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 = computed(() =>
      props.modelValue !== true || (fixed.value === true && revealed.value !== true)
    );

    const revealOnFocus = computed(() =>
      props.modelValue === true && hidden.value === true && props.reveal === true
    );

    const classes = 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 = 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);
    }

    watch(() => props.modelValue, val => {
      updateLayout('space', val);
      updateLocal(revealed, true);
      $layout.animate();
    });

    watch(offset, val => {
      updateLayout('offset', val);
    });

    watch(() => props.reveal, val => {
      val === false && updateLocal(revealed, props.modelValue);
    });

    watch(revealed, val => {
      $layout.animate();
      emit('reveal', val);
    });

    watch([ size, $layout.scroll, $layout.height ], updateRevealed);

    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);

    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, [
        h(QResizeObserver, {
          debounce: 0,
          onResize
        })
      ]);

      props.elevated === true && child.push(
        h('div', {
          class: 'q-layout__shadow absolute-full overflow-hidden no-pointer-events'
        })
      );

      return 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 = getCurrentInstance();
    const rootRef = 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');

      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 });
      });
    }

    provide(formKey, {
      bindComponent (vmProxy) {
        registeredComponents.push(vmProxy);
      },

      unbindComponent (vmProxy) {
        const index = registeredComponents.indexOf(vmProxy);
        if (index > -1) {
          registeredComponents.splice(index, 1);
        }
      }
    });

    let shouldActivate = false;

    onDeactivated(() => {
      shouldActivate = true;
    });

    onActivated(() => {
      shouldActivate === true && props.autofocus === true && focus();
    });

    onMounted(() => {
      props.autofocus === true && focus();
    });

    // expose public methods
    Object.assign(vm.proxy, {
      validate,
      resetValidation,
      submit,
      reset,
      focus,
      getValidationComponents: () => registeredComponents
    });

    return () => 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 } } = getCurrentInstance();

    const $layout = inject(layoutKey, emptyRenderFn);
    if ($layout === emptyRenderFn) {
      console.error('QHeader needs to be child of QLayout');
      return emptyRenderFn
    }

    const size = ref(parseInt(props.heightHint, 10));
    const revealed = ref(true);

    const fixed = computed(() =>
      props.reveal === true
      || $layout.view.value.indexOf('H') > -1
      || ($q.platform.is.ios && $layout.isContainer.value === true)
    );

    const offset = 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 = computed(() => props.modelValue !== true
      || (fixed.value === true && revealed.value !== true)
    );

    const revealOnFocus = computed(() =>
      props.modelValue === true && hidden.value === true && props.reveal === true
    );

    const classes = 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 = 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);
    }

    watch(() => props.modelValue, val => {
      updateLayout('space', val);
      updateLocal(revealed, true);
      $layout.animate();
    });

    watch(offset, val => {
      updateLayout('offset', val);
    });

    watch(() => props.reveal, val => {
      val === false && updateLocal(revealed, props.modelValue);
    });

    watch(revealed, val => {
      $layout.animate();
      emit('reveal', val);
    });

    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);

    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(
        h('div', {
          class: 'q-layout__shadow absolute-full overflow-hidden no-pointer-events'
        })
      );

      child.push(
        h(QResizeObserver, {
          debounce: 0,
          onResize
        })
      );

      return h('header', {
        class: classes.value,
        style: style.value,
        onFocusin
      }, child)
    }
  }
});

const useRatioProps = {
  ratio: [ String, Number ]
};

function useRatio (props, naturalRatio) {
  // return ratioStyle
  return 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 = ref(props.initialRatio);
    const ratioStyle = useRatio(props, naturalRatio);

    let loadTimer = null, isDestroyed = false;

    const images = [
      ref(null),
      ref(getPlaceholderSrc())
    ];

    const position = ref(0);

    const isLoading = ref(false);
    const hasError = ref(false);

    const classes = computed(() =>
      `q-img q-img--${ props.noNativeMenu === true ? 'no-' : '' }menu`
    );

    const style = computed(() => ({
      width: props.width,
      height: props.height
    }));

    const imgClass = computed(() =>
      `q-img__image ${ props.imgClass !== void 0 ? props.imgClass + ' ' : '' }`
      + `q-img__image--with${ props.noTransition === true ? 'out' : '' }-transition`
    );

    const imgStyle = computed(() => ({
      ...props.imgStyle,
      objectFit: props.fit,
      objectPosition: props.position
    }));

    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 h(
        'div',
        { class: 'q-img__container absolute-full', key: 'img' + index },
        h('img', data)
      )
    }

    function getContent () {
      if (isLoading.value !== true) {
        return h('div', {
          key: 'content',
          class: 'q-img__content absolute-full q-anchor--skip'
        }, hSlot(slots[ hasError.value === true ? 'error' : 'default' ]))
      }

      return h('div', {
        key: 'loading',
        class: 'q-img__loading absolute-full flex flex-center'
      }, (
        slots.loading !== void 0
          ? slots.loading()
          : (
              props.noSpinner === true
                ? void 0
                : [
                    h(QSpinner, {
                      color: props.spinnerColor,
                      size: props.spinnerSize
                    })
                  ]
            )
      ))
    }

    {
      if (__QUASAR_SSR_CLIENT__) {
        onMounted(() => {
          addImage(getCurrentSrc());
        });
      }
      else {
        addImage(getCurrentSrc());
      }

      onBeforeUnmount(() => {
        isDestroyed = true;

        if (loadTimer !== null) {
          clearTimeout(loadTimer);
          loadTimer = null;
        }
      });
    }

    return () => {
      const content = [];

      if (ratioStyle.value !== null) {
        content.push(
          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(
        h(Transition, { name: 'q-transition--fade' }, getContent)
      );

      return 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 = ref(false);
    const isWorking = ref(true);
    const rootRef = ref(null);
    const loadingRef = ref(null);

    let index = props.initialIndex || 0;
    let localScrollTarget, poll;

    const classes = 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;
          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 && 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 = computed(() => props.disable !== true && isWorking.value === true);

    watch([ isFetching, renderLoadingSlot ], () => { updateSvgAnimations(); });

    watch(() => props.disable, val => {
      if (val === true) { stop(); }
      else { resume(); }
    });

    watch(() => props.reverse, () => {
      if (isFetching.value === false && isWorking.value === true) {
        immediatePoll();
      }
    });

    watch(() => props.scrollTarget, updateScrollTarget);
    watch(() => props.debounce, setDebounce);

    let scrollPos = false;

    onActivated(() => {
      if (scrollPos !== false && localScrollTarget) {
        setVerticalScrollPosition(localScrollTarget, scrollPos);
      }
    });

    onDeactivated(() => {
      scrollPos = localScrollTarget
        ? getVerticalScrollPosition(localScrollTarget)
        : false;
    });

    onBeforeUnmount(() => {
      if (isWorking.value === true) {
        localScrollTarget.removeEventListener('scroll', poll, passive$3);
      }
    });

    onMounted(() => {
      setDebounce(props.debounce);
      updateScrollTarget();

      isFetching.value === false && updateSvgAnimations();
    });

    // expose public methods
    const vm = 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' ](
          h('div', { ref: loadingRef, class: classes.value }, hSlot(slots.loading))
        );
      }

      return 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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const { transitionProps, transitionStyle } = useTransition(props);

    const classes = computed(() =>
      'q-inner-loading absolute-full column flex-center'
      + (isDark.value === true ? ' q-inner-loading--dark' : '')
    );

    const labelClass = computed(() =>
      'q-inner-loading__label'
      + (props.labelClass !== void 0 ? ` ${ props.labelClass }` : '')
    );

    function getInner () {
      const child = [
        h(QSpinner, {
          size: props.size,
          color: props.color
        })
      ];

      if (props.label !== void 0) {
        child.push(
          h('div', {
            class: labelClass.value,
            style: props.labelStyle
          }, [ props.label ])
        );
      }

      return child
    }

    function getContent () {
      return props.showing === true
        ? h(
          'div',
          { class: classes.value, style: transitionStyle.value },
          slots.default !== void 0
            ? slots.default()
            : getInner()
        )
        : null
    }

    return () => h(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 = ref(null);
  const innerValue = ref(getInitialMaskedValue());

  function getIsTypeText () {
    return props.autogrow === true
      || [ 'textarea', 'text', 'search', 'url', 'tel', 'password' ].includes(props.type)
  }

  watch(() => props.type + props.autogrow, updateMaskInternals);

  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);
    }
  });

  watch(() => props.fillMask + props.reverseFillMask, () => {
    hasMask.value === true && updateMaskValue(innerValue.value, true);
  });

  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 && 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 } = getCurrentInstance();
    const { $q } = proxy;

    const temp = {};
    let emitCachedValue = NaN, typedNumber, stopValueWatcher, emitTimer = null, emitValueFn;

    const inputRef = 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 = computed(() => fieldValueIsFilled(innerValue.value));

    const onComposition = useKeyComposition(onInput);

    const state = useFieldState();

    const isTextarea = computed(() =>
      props.type === 'textarea' || props.autogrow === true
    );

    const isTypeText = computed(() =>
      isTextarea.value === true
      || [ 'text', 'search', 'url', 'tel', 'password' ].includes(props.type)
    );

    const onEvents = 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 = 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)
    watch(() => props.type, () => {
      if (inputRef.value) {
        inputRef.value.value = props.modelValue;
      }
    });

    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 && nextTick(adjustHeight);
    });

    watch(() => props.autogrow, val => {
      // textarea only
      if (val === true) {
        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';
      }
    });

    watch(() => props.dense, () => {
      props.autogrow === true && 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) {
            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);

          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 : '')
    }

    onBeforeUnmount(() => {
      onFinishEditing();
    });

    onMounted(() => {
      // textarea only
      props.autogrow === true && adjustHeight();
    });

    Object.assign(state, {
      innerValue,

      fieldClass: computed(() =>
        `q-${ isTextarea.value === true ? 'textarea' : 'input' }`
        + (props.autogrow === true ? ' q-textarea--autogrow' : '')
      ),

      hasShadow: computed(() =>
        props.type !== 'file'
        && typeof props.shadowText === 'string'
        && props.shadowText.length > 0
      ),

      inputRef,

      emitValue,

      hasValue,

      floatingLabel: computed(() =>
        (
          hasValue.value === true
          && (props.type !== 'number' || isNaN(innerValue.value) === false)
        )
        || fieldValueIsFilled(props.displayValue)
      ),

      getControl: () => {
        return 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 h('div', {
          class: 'q-field__native q-field__shadow absolute-bottom no-pointer-events'
            + (isTextarea.value === true ? '' : ' text-no-wrap')
        }, [
          h('span', { class: 'invisible' }, getCurValue()),
          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 = ref(isRuntimeSsrPreHydration.value === true ? props.ssrPrerender : false);

    const intersectionProps = 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 = computed(() =>
      props.disable !== true
      && (isRuntimeSsrPreHydration.value !== true || props.once !== true || props.ssrPrerender !== true)
    );

    const directives = computed(() => {
      // if hasDirective.value === true
      return [ [
        Intersection,
        intersectionProps.value,
        void 0,
        { once: props.once }
      ] ]
    });

    const transitionStyle = 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 () {
      return showing.value === true
        ? [ h('div', { key: 'content', style: transitionStyle.value }, hSlot(slots.default)) ]
        : void 0
    }

    return () => {
      const child = props.transition
        ? [
            h(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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const classes = 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 () => 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 } = getCurrentInstance();
    const { $q } = proxy;

    const model = ref(props.modelValue);
    const dragging = ref(false);

    const innerMin = computed(() => (
      isNaN(props.innerMin) === true || props.innerMin < props.min
        ? props.min
        : props.innerMin
    ));
    const innerMax = 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);
    }

    watch(
      () => `${ props.modelValue }|${ innerMin.value }|${ innerMax.value }`,
      normalizeModel
    );

    normalizeModel();

    const editable = computed(() => props.disable === false && props.readonly === false);

    const classes = computed(() =>
      'q-knob non-selectable' + (
        editable.value === true
          ? ' q-knob--editable'
          : (props.disable === true ? ' disabled' : '')
      )
    );

    const decimals = computed(() => (String(props.step).trim().split('.')[ 1 ] || '').length);
    const step = computed(() => (props.step === 0 ? 1 : props.step));
    const instantFeedback = computed(() => props.instantFeedback === true || dragging.value === true);

    const onEvents = $q.platform.is.mobile === true
      ? computed(() => (editable.value === true ? { onClick } : {}))
      : computed(() => (
        editable.value === true
          ? {
              onMousedown,
              onClick,
              onKeydown,
              onKeyup
            }
          : {}
      ));

    const attrs = computed(() => (
      editable.value === true
        ? { tabindex: props.tabindex }
        : { [ `aria-${ props.disable === true ? 'disabled' : 'readonly' }` ]: 'true' }
    ));

    const circularProps = 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 = 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 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;

    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 } = getCurrentInstance();

    watch(() => proxy.$q.lang.rtl, emitEvent);

    onMounted(() => {
      parentEl = proxy.$el.parentNode;
      configureScrollTarget();
    });

    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 } } = getCurrentInstance();

    const rootRef = ref(null);

    // page related
    const height = ref($q.screen.height);
    const width = ref(props.container === true ? 0 : $q.screen.width);
    const scroll = ref({ position: 0, direction: 'down', inflectionPoint: 0 });

    // container only prop
    const containerHeight = ref(0);
    const scrollbarWidth = ref(isRuntimeSsrPreHydration.value === true ? 0 : getScrollbarWidth());

    const classes = computed(() =>
      'q-layout q-layout--'
      + (props.container === true ? 'containerized' : 'standard')
    );

    const style = computed(() => (
      props.container === false
        ? { minHeight: $q.screen.height + 'px' }
        : null
    ));

    // used by container only
    const targetStyle = computed(() => (
      scrollbarWidth.value !== 0
        ? { [ $q.lang.rtl === true ? 'left' : 'right' ]: `${ scrollbarWidth.value }px` }
        : null
    ));

    const targetChildStyle = computed(() => (
      scrollbarWidth.value !== 0
        ? {
            [ $q.lang.rtl === true ? 'right' : 'left' ]: 0,
            [ $q.lang.rtl === true ? 'left' : 'right' ]: `-${ scrollbarWidth.value }px`,
            width: `calc(100% + ${ scrollbarWidth.value }px)`
          }
        : null
    ));

    function onPageScroll (data) {
      if (props.container === true || document.qScrollPrevented !== true) {
        const info = {
          position: data.position.top,
          direction: data.direction,
          directionChanged: data.directionChanged,
          inflectionPoint: data.inflectionPoint.top,
          delta: data.delta.top
        };

        scroll.value = info;
        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: computed(() => props.view),
      isContainer: computed(() => props.container),

      rootRef,

      height,
      containerHeight,
      scrollbarWidth,
      totalWidth: computed(() => width.value + scrollbarWidth.value),

      rows: computed(() => {
        const rows = props.view.toLowerCase().split(' ');
        return {
          top: rows[ 0 ].split(''),
          middle: rows[ 1 ].split(''),
          bottom: rows[ 2 ].split('')
        }
      }),

      header: reactive({ size: 0, offset: 0, space: false }),
      right: reactive({ size: 300, offset: 0, space: false }),
      footer: reactive({ size: 0, offset: 0, space: false }),
      left: reactive({ size: 300, offset: 0, space: false }),

      scroll,

      animate () {
        if (animateTimer !== null) {
          clearTimeout(animateTimer);
        }
        else {
          document.body.classList.add('q-body--layout-animate');
        }

        animateTimer = setTimeout(() => {
          animateTimer = null;
          document.body.classList.remove('q-body--layout-animate');
        }, 155);
      },

      update (part, prop, val) {
        $layout[ part ][ prop ] = val;
      }
    };

    provide(layoutKey, $layout);

    // prevent scrollbar flicker while resizing window height
    // if no page scrollbar is already present
    if (getScrollbarWidth() > 0) {
      let timer = null;
      const el = document.body;

      function restoreScrollbar () {
        timer = null;
        el.classList.remove('hide-scrollbar');
      }

      function hideScrollbar () {
        if (timer === null) {
          // if it has no scrollbar then there's nothing to do

          if (el.scrollHeight > $q.screen.height) {
            return
          }

          el.classList.add('hide-scrollbar');
        }
        else {
          clearTimeout(timer);
        }

        timer = setTimeout(restoreScrollbar, 300);
      }

      function updateScrollEvent (action) {
        if (timer !== null && action === 'remove') {
          clearTimeout(timer);
          restoreScrollbar();
        }

        window[ `${ action }EventListener` ]('resize', hideScrollbar);
      }

      watch(
        () => (props.container !== true ? 'add' : 'remove'),
        updateScrollEvent
      );

      props.container !== true && updateScrollEvent('add');

      onUnmounted(() => {
        updateScrollEvent('remove');
      });
    }

    return () => {
      const content = hMergeSlot(slots.default, [
        h(QScrollObserver, { onScroll: onPageScroll }),
        h(QResizeObserver, { onResize: onPageResize })
      ]);

      const layout = h('div', {
        class: classes.value,
        style: style.value,
        ref: props.container === true ? void 0 : rootRef,
        tabindex: -1
      }, content);

      if (props.container === true) {
        return h('div', {
          class: 'q-layout-container overflow-hidden',
          ref: rootRef
        }, [
          h(QResizeObserver, { onResize: onContainerResize }),
          h('div', {
            class: 'absolute-full',
            style: targetStyle.value
          }, [
            h('div', {
              class: 'scroll',
              style: targetChildStyle.value
            }, [ layout ])
          ])
        ])
      }

      return layout
    }
  }
});

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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const classes = 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 () => h('div', {
      class: classes.value
    }, [
      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 ? 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
          ? h(props.tag, data, node)
          : node[ 0 ]
      }

      if (props.placeholder !== void 0) {
        return h(props.tag, data, props.placeholder)
      }
    }
  }
});

const svg$m = h('svg', {
  key: 'svg',
  class: 'q-radio__bg absolute non-selectable',
  viewBox: '0 0 24 24'
}, [
  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'
  }),

  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 } = getCurrentInstance();

    const isDark = useDark(props, proxy.$q);
    const sizeStyle = useSize(props, optionSizes);

    const rootRef = ref(null);
    const { refocusTargetEl, refocusTarget } = useRefocusTarget(props, rootRef);

    const isTrue = computed(() => toRaw(props.modelValue) === toRaw(props.val));

    const classes = 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 = 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 = computed(() =>
      (isTrue.value === true
        ? props.checkedIcon
        : props.uncheckedIcon
      ) || null
    );

    const tabindex = computed(() => (
      props.disable === true ? -1 : props.tabindex || 0
    ));

    const formAttrs = 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
        ? [
            h('div', {
              key: 'icon',
              class: 'q-radio__icon-container absolute-full flex flex-center no-wrap'
            }, [
              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 = [
        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(
        h('div', {
          class: 'q-radio__label q-anchor--skip'
        }, label)
      );

      return 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 = computed(() =>
        (isTrue.value === true
          ? props.checkedIcon
          : (isIndeterminate.value === true ? props.indeterminateIcon : props.uncheckedIcon)
        ) || props.icon
      );

      const color = computed(() => (isTrue.value === true ? props.iconColor : null));

      return () => [
        h('div', { class: 'q-toggle__track' }),

        h('div', {
          class: 'q-toggle__thumb absolute flex flex-center no-wrap'
        }, icon.value !== void 0
          ? [
              h(QIcon, {
                name: icon.value,
                color: color.value
              })
            ]
          : void 0
        )
      ]
    }

    return useCheckbox('toggle', getInner)
  }
});

const components = {
  radio: QRadio,
  checkbox: QCheckbox,
  toggle: QToggle
};

const typeValues = Object.keys(components);

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 } } = 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 = computed(() => components[ props.type ]);

    const classes = computed(() =>
      'q-option-group q-gutter-x-sm'
      + (props.inline === true ? ' q-option-group--inline' : '')
    );

    const attrs = 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 () => 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 h('div', [
        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 } } = getCurrentInstance();

    const $layout = inject(layoutKey, emptyRenderFn);
    if ($layout === emptyRenderFn) {
      console.error('QPage needs to be a deep child of QLayout');
      return emptyRenderFn
    }

    const $pageContainer = inject(pageContainerKey, emptyRenderFn);
    if ($pageContainer === emptyRenderFn) {
      console.error('QPage needs to be child of QPageContainer');
      return emptyRenderFn
    }

    const style = 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 = computed(() =>
      `q-page${ props.padding === true ? ' q-layout-padding' : '' }`
    );

    return () => h('main', {
      class: classes.value,
      style: style.value
    }, hSlot(slots.default))
  }
});

var QPageContainer = createComponent({
  name: 'QPageContainer',

  setup (_, { slots }) {
    const { proxy: { $q } } = getCurrentInstance();

    const $layout = inject(layoutKey, emptyRenderFn);
    if ($layout === emptyRenderFn) {
      console.error('QPageContainer needs to be child of QLayout');
      return emptyRenderFn
    }

    provide(pageContainerKey, true);

    const style = 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 () => 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 } } = getCurrentInstance();

  const $layout = inject(layoutKey, emptyRenderFn);
  if ($layout === emptyRenderFn) {
    console.error('QPageSticky needs to be child of QLayout');
    return emptyRenderFn
  }

  const attach = 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 = computed(() => $layout.header.offset);
  const right = computed(() => $layout.right.offset);
  const bottom = computed(() => $layout.footer.offset);
  const left = computed(() => $layout.left.offset);

  const style = 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 = 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 h('div', {
      class: classes.value,
      style: style.value
    },
    props.expand === true
      ? content
      : [ 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 } } = getCurrentInstance();
    const { $layout, getStickyContent } = usePageSticky();
    const rootRef = ref(null);

    let heightWatcher;

    const scrollHeight = 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 = 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 = watch(scrollHeight, updateVisibility);
        }
      }
      else {
        cleanup();
      }
    }

    watch($layout.scroll, updateVisibility);
    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
        ? h('div', {
          ref: rootRef,
          class: 'q-page-scroller',
          onClick
        }, getStickyContent(slots))
        : null
    }

    updateReverse();

    onBeforeUnmount(cleanup);

    return () => h(
      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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);

    const minProp = computed(() => parseInt(props.min, 10));
    const maxProp = computed(() => parseInt(props.max, 10));
    const maxPagesProp = computed(() => parseInt(props.maxPages, 10));

    const inputPlaceholder = computed(() => model.value + ' / ' + maxProp.value);
    const boundaryLinksProp = computed(() => getBool(props.boundaryLinks, props.input));
    const boundaryNumbersProp = computed(() => getBool(props.boundaryNumbers, !props.input));
    const directionLinksProp = computed(() => getBool(props.directionLinks, props.input));
    const ellipsesProp = computed(() => getBool(props.ellipses, !props.input));

    const newPage = ref(null);
    const model = 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);
        }
      }
    });

    watch(() => `${ minProp.value }|${ maxProp.value }`, () => {
      model.value = props.modelValue;
    });

    const classes = computed(() =>
      'q-pagination row no-wrap items-center'
      + (props.disable === true ? ' disabled' : '')
    );

    const gutterProp = computed(() => (
      props.gutter in btnPadding
        ? `${ btnPadding[ props.gutter ] }px`
        : props.gutter || null
    ));
    const gutterStyle = computed(() => (
      gutterProp.value !== null
        ? `--q-pagination-gutter-parent:-${ gutterProp.value };--q-pagination-gutter-child:${ gutterProp.value }`
        : null
    ));

    const icons = 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 = computed(() => ({
      'aria-disabled': props.disable === true ? 'true' : 'false',
      role: 'navigation'
    }));

    const btnDesignProp = computed(() => getBtnDesign(props, 'flat'));
    const btnProps = 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 = computed(() => {
      // we also reset non-active design
      const acc = { [ btnDesignProp.value ]: false };
      if (props.activeDesign !== '') {
        acc[ props.activeDesign ] = true;
      }
      return acc
    });
    const activeBtnProps = computed(() => ({
      ...btnActiveDesignProp.value,
      color: props.activeColor || props.color,
      textColor: props.activeTextColor || props.textColor
    }));

    const btnConfig = 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 = 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 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 h('div', {
        class: classes.value,
        ...attrs.value
      }, [
        h('div', {
          class: 'q-pagination__content row no-wrap items-center',
          style: gutterStyle.value
        }, [
          ...contentStart,

          props.input === true
            ? 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
            })
            : 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 = ref(0);
    const rootRef = ref(null);
    const mediaParentRef = ref(null);
    const mediaRef = ref(null);

    let isWorking, mediaEl, mediaHeight, resizeHandler, observer, localScrollTarget;

    watch(() => props.height, () => {
      isWorking === true && updatePos();
    });

    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();
      }
    }

    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();
      }
    });

    onBeforeUnmount(() => {
      stop();
      observer !== void 0 && observer.disconnect();
      mediaEl.onload = mediaEl.onloadstart = mediaEl.loadedmetadata = null;
    });

    return () => {
      return h('div', {
        ref: rootRef,
        class: 'q-parallax',
        style: { height: `${ props.height }px` }
      }, [
        h('div', {
          ref: mediaParentRef,
          class: 'q-parallax__media absolute-full'
        }, slots.media !== void 0 ? slots.media() : [
          h('img', {
            ref: mediaRef,
            src: props.src
          })
        ]),

        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 } = getCurrentInstance();
    const { $q } = proxy;

    const menuRef = ref(null);

    const initialValue = ref('');
    const currentModel = ref('');

    let validated = false;

    const scope = 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 () {
      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(
        h('div', { class: 'q-dialog__title q-mt-sm q-mb-sm' }, props.title)
      );

      props.buttons === true && child.push(
        h('div', { class: 'q-popup-edit__buttons row justify-center no-wrap' }, [
          h(QBtn, {
            flat: true,
            color: props.color,
            label: props.labelCancel || $q.lang.label.cancel,
            onClick: cancel
          }),
          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 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 } = getCurrentInstance();
    const { $q } = proxy;

    const showing = ref(false);
    const popupRef = ref(null);
    const breakpoint = 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 = ref(getType());

    const popupProps = computed(() => (
      type.value === 'menu' ? { maxHeight: '99vh' } : {})
    );

    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 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 } = getCurrentInstance();
    const isDark = useDark(props, proxy.$q);
    const sizeStyle = useSize(props, defaultSizes);

    const motion = computed(() => props.indeterminate === true || props.query === true);
    const widthReverse = computed(() => props.reverse !== props.query);
    const style = computed(() => ({
      ...(sizeStyle.value !== null ? sizeStyle.value : {}),
      '--q-linear-progress-speed': `${ props.animationSpeed }ms`
    }));

    const classes = 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 = computed(() => width(props.buffer !== void 0 ? props.buffer : 1, widthReverse.value, proxy.$q));
    const transitionSuffix = computed(() => `with${ props.instantFeedback === true ? 'out' : '' }-transition`);

    const trackClass = 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 = computed(() => width(motion.value === true ? 1 : props.value, widthReverse.value, proxy.$q));
    const modelClass = computed(() =>
      'q-linear-progress__model absolute-full'
      + ` q-linear-progress__model--${ transitionSuffix.value }`
      + ` q-linear-progress__model--${ motion.value === true ? 'in' : '' }determinate`
    );

    const stripeStyle = computed(() => ({ width: `${ props.value * 100 }%` }));
    const stripeClass = computed(() =>
      `q-linear-progress__stripe absolute-${ props.reverse === true ? 'right' : 'left' }`
      + ` q-linear-progress__stripe--${ transitionSuffix.value }`
    );

    return () => {
      const child = [
        h('div', {
          class: trackClass.value,
          style: trackStyle.value
        }),

        h('div', {
          class: modelClass.value,
          style: modelStyle.value
        })
      ];

      props.stripe === true && motion.value === false && child.push(
        h('div', {
          class: stripeClass.value,
          style: stripeStyle.value
        })
      );

      return 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 } = getCurrentInstance();
    const { $q } = proxy;

    const state = ref('pull');
    const pullRatio = ref(0);
    const pulling = ref(false);
    const pullPosition = ref(-PULLER_HEIGHT);
    const animating = ref(false);
    const positionCSS = ref({});

    const style = computed(() => ({
      opacity: pullRatio.value,
      transform: `translateY(${ pullPosition.value }px) rotate(${ pullRatio.value * 360 }deg)`
    }));

    const classes = 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 = computed(() => {
      // if props.disable === false
      const modifiers = { down: true };

      if (props.noMouse !== true) {
        modifiers.mouse = true;
      }

      return [ [
        TouchPan,
        pull,
        void 0,
        modifiers
      ] ]
    });

    const contentClass = 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);
    }

    watch(() => props.scrollTarget, updateScrollTarget);

    onMounted(() => {
      $el = proxy.$el;
      updateScrollTarget();
    });

    onBeforeUnmount(() => {
      timer !== null && clearTimeout(timer);
    });

    // expose public methods
    Object.assign(proxy, { trigger, updateScrollTarget });

    return () => {
      const child = [
        h('div', { class: contentClass.value }, hSlot(slots.default)),

        h('div', {
          class: 'q-pull-to-refresh__puller-container fixed row flex-center no-pointer-events z-top',
          style: positionCSS.value
        }, [
          h('div', {
            class: classes.value,
            style: style.value
          }, [
            state.value !== 'refreshing'
              ? h(QIcon, {
                name: props.icon || $q.iconSet.pullToRefresh.icon,
                color: props.color,
                size: '32px'
              })
              : 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 } } = getCurrentInstance();

    const { state, methods } = useSlider({
      updateValue, updatePosition, getDragging,
      formAttrs: computed(() => ({
        type: 'hidden',
        name: props.name,
        value: `${ props.modelValue.min }|${ props.modelValue.max }`
      }))
    });

    const rootRef = ref(null);
    const curMinRatio = ref(0);
    const curMaxRatio = ref(0);
    const model = 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);
    }

    watch(
      () => `${ props.modelValue.min }|${ props.modelValue.max }|${ state.innerMin.value }|${ state.innerMax.value }`,
      normalizeModel
    );

    normalizeModel();

    const modelMinRatio = computed(() => methods.convertModelToRatio(model.value.min));
    const modelMaxRatio = computed(() => methods.convertModelToRatio(model.value.max));

    const ratioMin = computed(() => (
      state.active.value === true ? curMinRatio.value : modelMinRatio.value
    ));
    const ratioMax = computed(() => (
      state.active.value === true ? curMaxRatio.value : modelMaxRatio.value
    ));

    const selectionBarStyle = 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 = 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 = computed(() => (props.dragOnlyRange !== true ? state.tabindex.value : null));
    const trackContainerTabindex = computed(() => (
      $q.platform.is.mobile !== true && (props.dragRange || props.dragOnlyRange === true)
        ? state.tabindex.value
        : null
    ));

    const minThumbRef = ref(null);
    const minEvents = computed(() => getEvents('min'));
    const getMinThumb = methods.getThumbRenderFn({
      focusValue: 'min',
      getNodeData: () => ({
        ref: minThumbRef,
        key: 'tmin',
        ...minEvents.value,
        tabindex: thumbTabindex.value
      }),
      ratio: ratioMin,
      label: computed(() => (
        props.leftLabelValue !== void 0
          ? props.leftLabelValue
          : model.value.min
      )),
      thumbColor: computed(() => props.leftThumbColor || props.thumbColor || props.color),
      labelColor: computed(() => props.leftLabelColor || props.labelColor),
      labelTextColor: computed(() => props.leftLabelTextColor || props.labelTextColor)
    });

    const maxEvents = computed(() => getEvents('max'));
    const getMaxThumb = methods.getThumbRenderFn({
      focusValue: 'max',
      getNodeData: () => ({
        ...maxEvents.value,
        key: 'tmax',
        tabindex: thumbTabindex.value
      }),
      ratio: ratioMax,
      label: computed(() => (
        props.rightLabelValue !== void 0
          ? props.rightLabelValue
          : model.value.max
      )),
      thumbColor: computed(() => props.rightThumbColor || props.thumbColor || props.color),
      labelColor: computed(() => props.rightLabelColor || props.labelColor),
      labelTextColor: 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 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 } } = getCurrentInstance();

    const sizeStyle = useSize(props);
    const formAttrs = useFormAttrs(props);
    const injectFormInput = useFormInject(formAttrs);

    const mouseModel = ref(0);

    let iconRefs = {};

    const editable = computed(() =>
      props.readonly !== true && props.disable !== true
    );

    const classes = 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 = 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 = 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 = 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 = 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;
    }

    onBeforeUpdate(() => {
      iconRefs = {};
    });

    return () => {
      const child = [];

      stars.value.forEach(({ iconClass, name, attrs }, index) => {
        const i = index + 1;

        child.push(
          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 }` ],
            [ h(QIcon, { class: iconClass, name }) ]
          ))
        );
      });

      if (props.name !== void 0 && props.disable !== true) {
        injectFormInput(child, 'push');
      }

      return 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 () => h('div', {
      class: 'q-responsive'
    }, [
      h('div', {
        class: 'q-responsive__filler overflow-hidden'
      }, [
        h('div', { style: ratioStyle.value })
      ]),

      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 = ref(false);
    const panning = ref(false);
    const hover = ref(false);

    // other...
    const container = {
      vertical: ref(0),
      horizontal: ref(0)
    };

    const scroll = {
      vertical: {
        ref: ref(null),
        position: ref(0),
        size: ref(0)
      },

      horizontal: {
        ref: ref(null),
        position: ref(0),
        size: ref(0)
      }
    };

    const { proxy } = getCurrentInstance();

    const isDark = useDark(props, proxy.$q);

    let timer = null, panRefPos;

    const targetRef = ref(null);

    const classes = computed(() =>
      'q-scrollarea'
      + (isDark.value === true ? ' q-scrollarea--dark' : '')
    );

    scroll.vertical.percentage = 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 = 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 = computed(() =>
      scroll.vertical.percentage.value * (container.vertical.value - scroll.vertical.thumbSize.value)
    );
    scroll.vertical.thumbSize = computed(() =>
      Math.round(
        between(
          container.vertical.value * container.vertical.value / scroll.vertical.size.value,
          getMinThumbSize(container.vertical.value),
          container.vertical.value
        )
      )
    );
    scroll.vertical.style = computed(() => {
      return {
        ...props.thumbStyle,
        ...props.verticalThumbStyle,
        top: `${ scroll.vertical.thumbStart.value }px`,
        height: `${ scroll.vertical.thumbSize.value }px`
      }
    });
    scroll.vertical.thumbClass = computed(() =>
      'q-scrollarea__thumb q-scrollarea__thumb--v absolute-right'
      + (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
    );
    scroll.vertical.barClass = computed(() =>
      'q-scrollarea__bar q-scrollarea__bar--v absolute-right'
      + (scroll.vertical.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
    );

    scroll.horizontal.percentage = 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 = 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 = computed(() =>
      scroll.horizontal.percentage.value * (container.horizontal.value - scroll.horizontal.thumbSize.value)
    );
    scroll.horizontal.thumbSize = computed(() =>
      Math.round(
        between(
          container.horizontal.value * container.horizontal.value / scroll.horizontal.size.value,
          getMinThumbSize(container.horizontal.value),
          container.horizontal.value
        )
      )
    );
    scroll.horizontal.style = 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 = computed(() =>
      'q-scrollarea__thumb q-scrollarea__thumb--h absolute-bottom'
      + (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__thumb--invisible' : '')
    );
    scroll.horizontal.barClass = computed(() =>
      'q-scrollarea__bar q-scrollarea__bar--h absolute-bottom'
      + (scroll.horizontal.thumbHidden.value === true ? ' q-scrollarea__bar--invisible' : '')
    );

    const mainStyle = 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;

    watch(() => proxy.$q.lang.rtl, rtl => {
      if (targetRef.value !== null) {
        setHorizontalScrollPosition(
          targetRef.value,
          Math.abs(scroll.horizontal.position.value) * (rtl === true ? -1 : 1)
        );
      }
    });

    onDeactivated(() => {
      scrollPosition = {
        top: scroll.vertical.position.value,
        left: scroll.horizontal.position.value
      };
    });

    onActivated(() => {
      if (scrollPosition === null) { return }

      const scrollTarget = targetRef.value;

      if (scrollTarget !== null) {
        setHorizontalScrollPosition(scrollTarget, scrollPosition.left);
        setVerticalScrollPosition(scrollTarget, scrollPosition.top);
      }
    });

    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 h('div', {
        class: classes.value,
        onMouseenter,
        onMouseleave
      }, [
        h('div', {
          ref: targetRef,
          class: 'q-scrollarea__container scroll relative-position fit hide-scrollbar',
          tabindex: props.tabindex !== void 0 ? props.tabindex : void 0
        }, [
          h('div', {
            class: 'q-scrollarea__content absolute',
            style: mainStyle.value
          }, hMergeSlot(slots.default, [
            h(QResizeObserver, {
              debounce: 0,
              onResize: updateScrollSize
            })
          ])),

          h(QScrollObserver, {
            axis: 'both',
            onScroll: updateScroll
          })
        ]),

        h(QResizeObserver, {
          debounce: 0,
          onResize: updateContainer
        }),

        h('div', {
          class: scroll.vertical.barClass.value,
          style: [ props.barStyle, props.verticalBarStyle ],
          'aria-hidden': 'true',
          onMousedown: onVerticalMousedown
        }),

        h('div', {
          class: scroll.horizontal.barClass.value,
          style: [ props.barStyle, props.horizontalBarStyle ],
          'aria-hidden': 'true',
          onMousedown: onHorizontalMousedown
        }),

        withDirectives(
          h('div', {
            ref: scroll.vertical.ref,
            class: scroll.vertical.thumbClass.value,
            style: scroll.vertical.style.value,
            'aria-hidden': 'true'
          }),
          thumbVertDir
        ),

        withDirectives(
          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 = __QUASAR_SSR__ || 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 = getCurrentInstance();

  const { props, emit, proxy } = vm;
  const { $q } = proxy;

  let prevScrollStart, prevToIndex, localScrollViewSize, virtualScrollSizesAgg = [], virtualScrollSizes;

  const virtualScrollPaddingBefore = ref(0);
  const virtualScrollPaddingAfter = ref(0);
  const virtualScrollSliceSizeComputed = ref({});

  const beforeRef = ref(null);
  const afterRef = ref(null);
  const contentRef = ref(null);

  const virtualScrollSliceRange = ref({ from: 0, to: 0 });

  const colspanAttr = computed(() => (props.tableColspan !== void 0 ? props.tableColspan : 100));

  if (virtualScrollItemSizeComputed === void 0) {
    virtualScrollItemSizeComputed = computed(() => props.virtualScrollItemSize);
  }

  const needsReset = computed(() => virtualScrollItemSizeComputed.value + ';' + props.virtualScrollHorizontal);

  const needsSliceRecalc = computed(() =>
    needsReset.value + ';' + props.virtualScrollSliceRatioBefore + ';' + props.virtualScrollSliceRatioAfter
  );

  watch(needsSliceRecalc, () => { setVirtualScrollSize(); });
  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);
      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'
        ? h(tag, {
          class: 'q-virtual-scroll__padding',
          key: 'before',
          ref: beforeRef
        }, [
          h('tr', [
            h('td', {
              style: { [ paddingSize ]: `${ virtualScrollPaddingBefore.value }px`, ...style },
              colspan: colspanAttr.value
            })
          ])
        ])
        : h(tag, {
          class: 'q-virtual-scroll__padding',
          key: 'before',
          ref: beforeRef,
          style: { [ paddingSize ]: `${ virtualScrollPaddingBefore.value }px`, ...style }
        }),

      h(tag, {
        class: 'q-virtual-scroll__content',
        key: 'content',
        ref: contentRef,
        tabindex: -1
      }, content.flat()),

      tag === 'tbody'
        ? h(tag, {
          class: 'q-virtual-scroll__padding',
          key: 'after',
          ref: afterRef
        }, [
          h('tr', [
            h('td', {
              style: { [ paddingSize ]: `${ virtualScrollPaddingAfter.value }px`, ...style },
              colspan: colspanAttr.value
            })
          ])
        ])
        : 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
  );

  onBeforeMount(() => {
    setVirtualScrollSize();
  });

  let shouldActivate = false;

  onDeactivated(() => {
    shouldActivate = true;
  });

  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);
    }
  });

  __QUASAR_SSR__ || 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 } = getCurrentInstance();
    const { $q } = proxy;

    const menu = ref(false);
    const dialog = ref(false);
    const optionIndex = ref(-1);
    const inputValue = ref('');
    const dialogFieldFocused = ref(false);
    const innerLoadingIndicator = ref(false);

    let inputTimer = null, innerValueCache,
      hasDialog, userInputValue, filterId = null, defaultInputValue,
      transitionShowComputed, searchBuffer, searchBufferExp;

    const inputRef = ref(null);
    const targetRef = ref(null);
    const menuRef = ref(null);
    const dialogRef = ref(null);
    const menuContentRef = ref(null);

    const nameProp = useFormInputNameAttr(props);

    const onComposition = useKeyComposition(onInput);

    const virtualScrollLength = computed(() => (
      Array.isArray(props.options)
        ? props.options.length
        : 0
    ));

    const virtualScrollItemSizeComputed = 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 = 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 = computed(() => {
      const acc = {};
      fieldPropsList.forEach(key => {
        const val = props[ key ];
        if (val !== void 0) {
          acc[ key ] = val;
        }
      });
      return acc
    });

    const isOptionsDark = computed(() => (
      props.optionsDark === null
        ? state.isDark.value
        : props.optionsDark
    ));

    const hasValue = computed(() => fieldValueIsFilled(innerValue.value));

    const computedInputClass = 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 = computed(() =>
      (props.virtualScrollHorizontal === true ? 'q-virtual-scroll--horizontal' : '')
      + (props.popupContentClass ? ' ' + props.popupContentClass : '')
    );

    const noOptions = computed(() => virtualScrollLength.value === 0);

    const selectedString = computed(() =>
      innerValue.value
        .map(opt => getOptionLabel.value(opt))
        .join(', ')
    );

    const ariaCurrentValue = computed(() => (props.displayValue !== void 0
      ? props.displayValue
      : selectedString.value
    ));

    const needsHtmlFn = computed(() => (
      props.optionsHtml === true
        ? () => true
        : opt => opt !== void 0 && opt !== null && opt.html === true
    ));

    const valueAsHtml = computed(() => (
      props.displayValueHtml === true || (
        props.displayValue === void 0 && (
          props.optionsHtml === true
          || innerValue.value.some(needsHtmlFn.value)
        )
      )
    ));

    const tabindex = computed(() => (state.focused.value === true ? props.tabindex : -1));

    const comboboxAttrs = 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 = computed(() => ({
      id: `${ state.targetUid.value }_lb`,
      role: 'listbox',
      'aria-multiselectable': props.multiple === true ? 'true' : 'false'
    }));

    const selectedScope = computed(() => {
      return innerValue.value.map((opt, i) => ({
        index: i,
        opt,
        html: needsHtmlFn.value(opt),
        selected: true,
        removeAtIndex: removeAtIndexAndFocus,
        toggleOption,
        tabindex: tabindex.value
      }))
    });

    const optionScope = 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 = computed(() => (
      props.dropdownIcon !== void 0
        ? props.dropdownIcon
        : $q.iconSet.arrow.dropdown
    ));

    const squaredMenu = computed(() =>
      props.optionsCover === false
      && props.outlined !== true
      && props.standout !== true
      && props.borderless !== true
      && props.rounded !== true
    );

    const computedOptionsSelectedClass = 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 = computed(() => getPropValueFn(props.optionValue, 'value'));

    // returns method to get label of an option;
    // takes into account 'option-label' prop
    const getOptionLabel = computed(() => getPropValueFn(props.optionLabel, 'label'));

    // returns method to tell if an option is disabled;
    // takes into account 'option-disable' prop
    const isOptionDisabled = computed(() => getPropValueFn(props.optionDisable, 'disable'));

    const innerOptionsValue = computed(() => innerValue.value.map(opt => getOptionValue.value(opt)));

    const inputControlEvents = 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
    });

    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 });

    watch(() => props.fillInput, resetInputValue);

    watch(menu, updateMenu);

    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) { 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) {
          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) => 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); }
        }, () => h('span', {
          class: 'ellipsis',
          [ scope.html === true ? 'innerHTML' : 'textContent' ]: getOptionLabel.value(scope.opt)
        })))
      }

      return [
        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 h(QItem, {
            key: scope.index,
            ...scope.itemProps
          }, () => {
            return h(
              QItemSection,
              () => h(
                QItemLabel,
                () => 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 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;

            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' && nextTick(() => { afterFn(proxy); });
              typeof afterUpdateFn === 'function' && 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 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);
      nextTick(() => {
        dialogFieldFocused.value = false;
      });
    }

    function getDialog () {
      const content = [
        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(
        h('div', {
          ref: menuContentRef,
          class: menuContentClass.value + ' scroll',
          style: props.popupContentStyle,
          ...listboxAttrs.value,
          onClick: prevent,
          onScrollPassive: onVirtualScrollEvt
        }, getAllOptions())
      );

      return 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
      }, () => 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;
        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);

        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;
    }

    onBeforeUpdate(updatePreState);
    onUpdated(updateMenuPosition);

    updatePreState();

    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: 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: 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(
            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(
              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 => h('option', { value, selected: true }));

          child.push(
            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 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
          ? [
              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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const style = 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 = 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 () => 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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);
    const { getCacheWithFn } = useCache();

    const contentRef = ref(null);

    let timer = null, pan = {}, dirRefs = {}, dirContentRefs = {};

    const langDir = computed(() => (
      $q.lang.rtl === true
        ? { left: 'right', right: 'left' }
        : { left: 'left', right: 'right' }
    ));

    const classes = 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);
    }

    onBeforeUpdate(() => {
      dirRefs = {};
      dirContentRefs = {};
    });

    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(
            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' ] }` : '')
            }, [
              h('div', { ref: el => { dirContentRefs[ dir ] = el; } }, slots[ dir ]())
            ])
          );
        }
      });

      const node = 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(
          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 h('div', { class: classes.value }, content)
    }
  }
});

const space = h('div', { class: 'q-space' });

var QSpace = createComponent({
  name: 'QSpace',

  setup () {
    return () => space
  }
});

const svg$l = [
  h('g', {
    transform: 'matrix(1 0 0 -1 0 80)'
  }, [
    h('rect', {
      width: '10',
      height: '20',
      rx: '3'
    }, [
      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'
      })
    ]),
    h('rect', {
      x: '15',
      width: '10',
      height: '80',
      rx: '3'
    }, [
      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'
      })
    ]),
    h('rect', {
      x: '30',
      width: '10',
      height: '50',
      rx: '3'
    }, [
      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'
      })
    ]),
    h('rect', {
      x: '45',
      width: '10',
      height: '30',
      rx: '3'
    }, [
      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 () => 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 = [
  h('g', {
    transform: 'translate(1 1)',
    'stroke-width': '2',
    fill: 'none',
    'fill-rule': 'evenodd'
  }, [
    h('circle', {
      cx: '5',
      cy: '50',
      r: '5'
    }, [
      h('animate', {
        attributeName: 'cy',
        begin: '0s',
        dur: '2.2s',
        values: '50;5;50;50',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'cx',
        begin: '0s',
        dur: '2.2s',
        values: '5;27;49;5',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      })
    ]),
    h('circle', {
      cx: '27',
      cy: '5',
      r: '5'
    }, [
      h('animate', {
        attributeName: 'cy',
        begin: '0s',
        dur: '2.2s',
        from: '5',
        to: '5',
        values: '5;50;50;5',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'cx',
        begin: '0s',
        dur: '2.2s',
        from: '27',
        to: '27',
        values: '27;49;5;27',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      })
    ]),
    h('circle', {
      cx: '49',
      cy: '50',
      r: '5'
    }, [
      h('animate', {
        attributeName: 'cy',
        begin: '0s',
        dur: '2.2s',
        values: '50;50;5;50',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      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 () => 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 = [
  h('rect', {
    y: '10',
    width: '15',
    height: '120',
    rx: '6'
  }, [
    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'
    }),
    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'
    })
  ]),
  h('rect', {
    x: '30',
    y: '10',
    width: '15',
    height: '120',
    rx: '6'
  }, [
    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'
    }),
    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'
    })
  ]),
  h('rect', {
    x: '60',
    width: '15',
    height: '140',
    rx: '6'
  }, [
    h('animate', {
      attributeName: 'height',
      begin: '0s',
      dur: '1s',
      values: '120;110;100;90;80;70;60;50;40;140;120',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    }),
    h('animate', {
      attributeName: 'y',
      begin: '0s',
      dur: '1s',
      values: '10;15;20;25;30;35;40;45;50;0;10',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('rect', {
    x: '90',
    y: '10',
    width: '15',
    height: '120',
    rx: '6'
  }, [
    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'
    }),
    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'
    })
  ]),
  h('rect', {
    x: '120',
    y: '10',
    width: '15',
    height: '120',
    rx: '6'
  }, [
    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'
    }),
    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 () => 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 = [
  h('rect', {
    x: '25',
    y: '25',
    width: '50',
    height: '50',
    fill: 'none',
    'stroke-width': '4',
    stroke: 'currentColor'
  }, [
    h('animateTransform', {
      id: 'spinnerBox',
      attributeName: 'transform',
      type: 'rotate',
      from: '0 50 50',
      to: '180 50 50',
      dur: '0.5s',
      begin: 'rectBox.end'
    })
  ]),
  h('rect', {
    x: '27',
    y: '27',
    width: '46',
    height: '50',
    fill: 'currentColor'
  }, [
    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 () => 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 = [
  h('circle', {
    cx: '50',
    cy: '50',
    r: '48',
    fill: 'none',
    'stroke-width': '4',
    'stroke-miterlimit': '10',
    stroke: 'currentColor'
  }),
  h('line', {
    'stroke-linecap': 'round',
    'stroke-width': '4',
    'stroke-miterlimit': '10',
    stroke: 'currentColor',
    x1: '50',
    y1: '50',
    x2: '85',
    y2: '50.5'
  }, [
    h('animateTransform', {
      attributeName: 'transform',
      type: 'rotate',
      from: '0 50 50',
      to: '360 50 50',
      dur: '2s',
      repeatCount: 'indefinite'
    })
  ]),
  h('line', {
    'stroke-linecap': 'round',
    'stroke-width': '4',
    'stroke-miterlimit': '10',
    stroke: 'currentColor',
    x1: '50',
    y1: '50',
    x2: '49.5',
    y2: '74'
  }, [
    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 () => 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 = [
  h('rect', {
    x: '0',
    y: '0',
    width: ' 100',
    height: '100',
    fill: 'none'
  }),
  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'
  }),
  h('circle', {
    cx: '30',
    cy: '47',
    r: '5',
    fill: '#fff'
  }, [
    h('animate', {
      attributeName: 'opacity',
      from: '0',
      to: '1',
      values: '0;1;1',
      keyTimes: '0;0.2;1',
      dur: '1s',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '50',
    cy: '47',
    r: '5',
    fill: '#fff'
  }, [
    h('animate', {
      attributeName: 'opacity',
      from: '0',
      to: '1',
      values: '0;0;1;1',
      keyTimes: '0;0.2;0.4;1',
      dur: '1s',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '70',
    cy: '47',
    r: '5',
    fill: '#fff'
  }, [
    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 () => 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 = [
  h('rect', {
    x: '0',
    y: '0',
    width: ' 100',
    height: '100',
    fill: 'none'
  }),
  h('g', {
    transform: 'translate(25 25)'
  }, [
    h('rect', {
      x: '-20',
      y: '-20',
      width: ' 40',
      height: '40',
      fill: 'currentColor',
      opacity: '0.9'
    }, [
      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'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(75 25)'
  }, [
    h('rect', {
      x: '-20',
      y: '-20',
      width: ' 40',
      height: '40',
      fill: 'currentColor',
      opacity: '0.8'
    }, [
      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'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(25 75)'
  }, [
    h('rect', {
      x: '-20',
      y: '-20',
      width: ' 40',
      height: '40',
      fill: 'currentColor',
      opacity: '0.7'
    }, [
      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'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(75 75)'
  }, [
    h('rect', {
      x: '-20',
      y: '-20',
      width: ' 40',
      height: '40',
      fill: 'currentColor',
      opacity: '0.6'
    }, [
      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 () => 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 = [
  h('circle', {
    cx: '15',
    cy: '15',
    r: '15'
  }, [
    h('animate', {
      attributeName: 'r',
      from: '15',
      to: '15',
      begin: '0s',
      dur: '0.8s',
      values: '15;9;15',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    }),
    h('animate', {
      attributeName: 'fill-opacity',
      from: '1',
      to: '1',
      begin: '0s',
      dur: '0.8s',
      values: '1;.5;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '60',
    cy: '15',
    r: '9',
    'fill-opacity': '.3'
  }, [
    h('animate', {
      attributeName: 'r',
      from: '9',
      to: '9',
      begin: '0s',
      dur: '0.8s',
      values: '9;15;9',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    }),
    h('animate', {
      attributeName: 'fill-opacity',
      from: '.5',
      to: '.5',
      begin: '0s',
      dur: '0.8s',
      values: '.5;1;.5',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '105',
    cy: '15',
    r: '15'
  }, [
    h('animate', {
      attributeName: 'r',
      from: '15',
      to: '15',
      begin: '0s',
      dur: '0.8s',
      values: '15;9;15',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    }),
    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 () => 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 = [
  h('g', {
    transform: 'translate(20 50)'
  }, [
    h('rect', {
      x: '-10',
      y: '-30',
      width: ' 20',
      height: '60',
      fill: 'currentColor',
      opacity: '0.6'
    }, [
      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'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(50 50)'
  }, [
    h('rect', {
      x: '-10',
      y: '-30',
      width: ' 20',
      height: '60',
      fill: 'currentColor',
      opacity: '0.8'
    }, [
      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'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(80 50)'
  }, [
    h('rect', {
      x: '-10',
      y: '-30',
      width: ' 20',
      height: '60',
      fill: 'currentColor',
      opacity: '0.9'
    }, [
      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 () => 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 = [
  h('g', {
    transform: 'translate(-20,-20)'
  }, [
    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'
    }, [
      h('animateTransform', {
        attributeName: 'transform',
        type: 'rotate',
        from: '90 50 50',
        to: '0 50 50',
        dur: '1s',
        repeatCount: 'indefinite'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(20,20) rotate(15 50 50)'
  }, [
    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'
    }, [
      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 () => 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 = [
  h('circle', {
    cx: '12.5',
    cy: '12.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '0s',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '12.5',
    cy: '52.5',
    r: '12.5',
    'fill-opacity': '.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '100ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '52.5',
    cy: '12.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '300ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '52.5',
    cy: '52.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '600ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '92.5',
    cy: '12.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '800ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '92.5',
    cy: '52.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '400ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '12.5',
    cy: '92.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '700ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '52.5',
    cy: '92.5',
    r: '12.5'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '500ms',
      dur: '1s',
      values: '1;.2;1',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  h('circle', {
    cx: '92.5',
    cy: '92.5',
    r: '12.5'
  }, [
    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 () => 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 = [
  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'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '0s',
      dur: '1.4s',
      values: '0.5;1;0.5',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  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'
  }, [
    h('animate', {
      attributeName: 'fill-opacity',
      begin: '0.7s',
      dur: '1.4s',
      values: '0.5;1;0.5',
      calcMode: 'linear',
      repeatCount: 'indefinite'
    })
  ]),
  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 () => 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 = [
  h('g', [
    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'
    }),
    h('clipPath', {
      id: 'uil-hourglass-clip1'
    }, [
      h('rect', {
        x: '15',
        y: '20',
        width: ' 70',
        height: '25'
      }, [
        h('animate', {
          attributeName: 'height',
          from: '25',
          to: '0',
          dur: '1s',
          repeatCount: 'indefinite',
          values: '25;0;0',
          keyTimes: '0;0.5;1'
        }),
        h('animate', {
          attributeName: 'y',
          from: '20',
          to: '45',
          dur: '1s',
          repeatCount: 'indefinite',
          values: '20;45;45',
          keyTimes: '0;0.5;1'
        })
      ])
    ]),
    h('clipPath', {
      id: 'uil-hourglass-clip2'
    }, [
      h('rect', {
        x: '15',
        y: '55',
        width: ' 70',
        height: '25'
      }, [
        h('animate', {
          attributeName: 'height',
          from: '0',
          to: '25',
          dur: '1s',
          repeatCount: 'indefinite',
          values: '0;25;25',
          keyTimes: '0;0.5;1'
        }),
        h('animate', {
          attributeName: 'y',
          from: '80',
          to: '55',
          dur: '1s',
          repeatCount: 'indefinite',
          values: '80;55;55',
          keyTimes: '0;0.5;1'
        })
      ])
    ]),
    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'
    }),
    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'
    }),
    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 () => 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 = [
  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'
  }, [
    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 () => h('svg', {
      class: classes.value,
      width: cSize.value,
      height: cSize.value,
      viewBox: '0 0 100 100',
      preserveAspectRatio: 'xMidYMid'
    }, svg$8)
  }
});

const svg$7 = [
  h('g', {
    'stroke-width': '4',
    'stroke-linecap': 'round'
  }, [
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(180)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '1;.85;.7;.65;.55;.45;.35;.25;.15;.1;0;1',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(210)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '0;1;.85;.7;.65;.55;.45;.35;.25;.15;.1;0',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(240)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.1;0;1;.85;.7;.65;.55;.45;.35;.25;.15;.1',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(270)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.15;.1;0;1;.85;.7;.65;.55;.45;.35;.25;.15',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(300)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.25;.15;.1;0;1;.85;.7;.65;.55;.45;.35;.25',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(330)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.35;.25;.15;.1;0;1;.85;.7;.65;.55;.45;.35',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(0)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.45;.35;.25;.15;.1;0;1;.85;.7;.65;.55;.45',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(30)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.55;.45;.35;.25;.15;.1;0;1;.85;.7;.65;.55',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(60)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.65;.55;.45;.35;.25;.15;.1;0;1;.85;.7;.65',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(90)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.7;.65;.55;.45;.35;.25;.15;.1;0;1;.85;.7',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(120)'
    }, [
      h('animate', {
        attributeName: 'stroke-opacity',
        dur: '750ms',
        values: '.85;.7;.65;.55;.45;.35;.25;.15;.1;0;1;.85',
        repeatCount: 'indefinite'
      })
    ]),
    h('line', {
      y1: '17',
      y2: '29',
      transform: 'translate(32,32) rotate(150)'
    }, [
      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 () => 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 = [
  h('circle', {
    cx: '50',
    cy: '50',
    r: '44',
    fill: 'none',
    'stroke-width': '4',
    'stroke-opacity': '.5',
    stroke: 'currentColor'
  }),
  h('circle', {
    cx: '8',
    cy: '54',
    r: '6',
    fill: 'currentColor',
    'stroke-width': '3',
    stroke: 'currentColor'
  }, [
    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 () => 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 = [
  h('g', {
    transform: 'translate(1 1)',
    'stroke-width': '2',
    fill: 'none',
    'fill-rule': 'evenodd'
  }, [
    h('circle', {
      'stroke-opacity': '.5',
      cx: '18',
      cy: '18',
      r: '18'
    }),
    h('path', {
      d: 'M36 18c0-9.94-8.06-18-18-18'
    }, [
      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 () => 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 = [
  h('path', {
    d: 'M0 50A50 50 0 0 1 50 0L50 50L0 50',
    fill: 'currentColor',
    opacity: '0.5'
  }, [
    h('animateTransform', {
      attributeName: 'transform',
      type: 'rotate',
      from: '0 50 50',
      to: '360 50 50',
      dur: '0.8s',
      repeatCount: 'indefinite'
    })
  ]),
  h('path', {
    d: 'M50 0A50 50 0 0 1 100 50L50 50L50 0',
    fill: 'currentColor',
    opacity: '0.5'
  }, [
    h('animateTransform', {
      attributeName: 'transform',
      type: 'rotate',
      from: '0 50 50',
      to: '360 50 50',
      dur: '1.6s',
      repeatCount: 'indefinite'
    })
  ]),
  h('path', {
    d: 'M100 50A50 50 0 0 1 50 100L50 50L100 50',
    fill: 'currentColor',
    opacity: '0.5'
  }, [
    h('animateTransform', {
      attributeName: 'transform',
      type: 'rotate',
      from: '0 50 50',
      to: '360 50 50',
      dur: '2.4s',
      repeatCount: 'indefinite'
    })
  ]),
  h('path', {
    d: 'M50 100A50 50 0 0 1 0 50L50 50L50 100',
    fill: 'currentColor',
    opacity: '0.5'
  }, [
    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 () => 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 = [
  h('g', {
    fill: 'none',
    'fill-rule': 'evenodd',
    'stroke-width': '2'
  }, [
    h('circle', {
      cx: '22',
      cy: '22',
      r: '1'
    }, [
      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'
      }),
      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'
      })
    ]),
    h('circle', {
      cx: '22',
      cy: '22',
      r: '1'
    }, [
      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'
      }),
      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 () => 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 = [
  h('g', {
    transform: 'scale(0.55)'
  }, [
    h('circle', {
      cx: '30',
      cy: '150',
      r: '30',
      fill: 'currentColor'
    }, [
      h('animate', {
        attributeName: 'opacity',
        from: '0',
        to: '1',
        dur: '1s',
        begin: '0',
        repeatCount: 'indefinite',
        keyTimes: '0;0.5;1',
        values: '0;1;1'
      })
    ]),
    h('path', {
      d: 'M90,150h30c0-49.7-40.3-90-90-90v30C63.1,90,90,116.9,90,150z',
      fill: 'currentColor'
    }, [
      h('animate', {
        attributeName: 'opacity',
        from: '0',
        to: '1',
        dur: '1s',
        begin: '0.1',
        repeatCount: 'indefinite',
        keyTimes: '0;0.5;1',
        values: '0;1;1'
      })
    ]),
    h('path', {
      d: 'M150,150h30C180,67.2,112.8,0,30,0v30C96.3,30,150,83.7,150,150z',
      fill: 'currentColor'
    }, [
      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 () => 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 = [
  h('g', {
    fill: 'none',
    'fill-rule': 'evenodd',
    transform: 'translate(1 1)',
    'stroke-width': '2'
  }, [
    h('circle', {
      cx: '22',
      cy: '22',
      r: '6'
    }, [
      h('animate', {
        attributeName: 'r',
        begin: '1.5s',
        dur: '3s',
        values: '6;22',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'stroke-opacity',
        begin: '1.5s',
        dur: '3s',
        values: '1;0',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'stroke-width',
        begin: '1.5s',
        dur: '3s',
        values: '2;0',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      })
    ]),
    h('circle', {
      cx: '22',
      cy: '22',
      r: '6'
    }, [
      h('animate', {
        attributeName: 'r',
        begin: '3s',
        dur: '3s',
        values: '6;22',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'stroke-opacity',
        begin: '3s',
        dur: '3s',
        values: '1;0',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      }),
      h('animate', {
        attributeName: 'stroke-width',
        begin: '3s',
        dur: '3s',
        values: '2;0',
        calcMode: 'linear',
        repeatCount: 'indefinite'
      })
    ]),
    h('circle', {
      cx: '22',
      cy: '22',
      r: '8'
    }, [
      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 () => 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 = [
  h('defs', [
    h('linearGradient', {
      x1: '8.042%',
      y1: '0%',
      x2: '65.682%',
      y2: '23.865%',
      id: 'a'
    }, [
      h('stop', {
        'stop-color': 'currentColor',
        'stop-opacity': '0',
        offset: '0%'
      }),
      h('stop', {
        'stop-color': 'currentColor',
        'stop-opacity': '.631',
        offset: '63.146%'
      }),
      h('stop', {
        'stop-color': 'currentColor',
        offset: '100%'
      })
    ])
  ]),
  h('g', {
    transform: 'translate(1 1)',
    fill: 'none',
    'fill-rule': 'evenodd'
  }, [
    h('path', {
      d: 'M36 18c0-9.94-8.06-18-18-18',
      stroke: 'url(#a)',
      'stroke-width': '2'
    }, [
      h('animateTransform', {
        attributeName: 'transform',
        type: 'rotate',
        from: '0 18 18',
        to: '360 18 18',
        dur: '0.9s',
        repeatCount: 'indefinite'
      })
    ]),
    h('circle', {
      fill: 'currentColor',
      cx: '36',
      cy: '18',
      r: '1'
    }, [
      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 () => 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 } } = getCurrentInstance();
    const isDark = useDark(props, $q);

    const rootRef = ref(null);
    const sideRefs = {
      before: ref(null),
      after: ref(null)
    };

    const classes = 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 = computed(() => (props.horizontal === true ? 'height' : 'width'));
    const side = computed(() => (props.reverse !== true ? 'before' : 'after'));

    const computedLimits = 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 = 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 = 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 ]);
      }
    }

    watch(() => props.modelValue, v => {
      normalize(v, computedLimits.value);
    });

    watch(() => props.limits, () => {
      nextTick(() => {
        normalize(props.modelValue, computedLimits.value);
      });
    });

    return () => {
      const child = [
        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)),

        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
          )
        ]),

        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 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 } } = getCurrentInstance();
    const blurRef = ref(null);

    const isActive = computed(() => props.stepper.modelValue === props.step.name);

    const isDisable = computed(() => {
      const opt = props.step.disable;
      return opt === true || opt === ''
    });

    const isError = computed(() => {
      const opt = props.step.error;
      return opt === true || opt === ''
    });

    const isDone = computed(() => {
      const opt = props.step.done;
      return isDisable.value === false && (opt === true || opt === '')
    });

    const headerNav = computed(() => {
      const
        opt = props.step.headerNav,
        nav = opt === true || opt === '' || opt === void 0;

      return isDisable.value === false
        && props.stepper.headerNav
        && nav
    });

    const hasPrefix = 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 = 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 = 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 = 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 = 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 = [
        h('div', { class: 'q-focus-helper', tabindex: -1, ref: blurRef }),

        h('div', { class: 'q-stepper__dot row flex-center q-stepper__line relative-position' }, [
          h('span', { class: 'row flex-center' }, [
            hasPrefix.value === true
              ? props.step.prefix
              : h(QIcon, { name: icon.value })
          ])
        ])
      ];

      if (props.step.title !== void 0 && props.step.title !== null) {
        const content = [
          h('div', { class: 'q-stepper__title' }, props.step.title)
        ];

        if (props.step.caption !== void 0 && props.step.caption !== null) {
          content.push(
            h('div', { class: 'q-stepper__caption' }, props.step.caption)
          );
        }

        child.push(
          h('div', {
            class: 'q-stepper__label q-stepper__line relative-position'
          }, content)
        );
      }

      return withDirectives(
        h('div', data, child),
        [ [ Ripple, ripple.value ] ]
      )
    }
  }
});

function getStepWrapper (slots) {
  return h('div', {
    class: 'q-stepper__step-content'
  }, [
    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 } } = getCurrentInstance();

    const $stepper = inject(stepperKey, emptyRenderFn);
    if ($stepper === emptyRenderFn) {
      console.error('QStep needs to be a child of QStepper');
      return emptyRenderFn
    }

    const { getCacheWithFn } = useCache();

    const rootRef = ref(null);

    const isActive = computed(() => $stepper.value.modelValue === props.name);

    const scrollEvent = 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 = 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 h(
          KeepAlive,
          $stepper.value.keepAliveProps.value,
          isActive.value === true
            ? [
                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 () => h(
      'div',
      { ref: rootRef, class: 'q-stepper__step', role: 'tabpanel', ...scrollEvent.value },
      $stepper.value.vertical === true
        ? [
            h(StepHeader, {
              stepper: $stepper.value,
              step: props,
              goToPanel: $stepper.value.goToPanel
            }),

            $stepper.value.animated === true
              ? 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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    const {
      updatePanelsList, isValidPanelName,
      updatePanelIndex, getPanelContent,
      getPanels, panelDirectives, goToPanel,
      keepAliveProps, needsUniqueKeepAliveWrapper
    } = usePanel();

    provide(stepperKey, computed(() => ({
      goToPanel,
      keepAliveProps,
      needsUniqueKeepAliveWrapper,
      ...props
    })));

    const classes = 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 = 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 = h('div', {
          class: 'q-stepper__content'
        }, hSlot(slots.default));

        return top === void 0
          ? [ content ]
          : top.concat(content)
      }

      return [
        h(
          'div',
          { class: headerClasses.value },
          getPanels().map(panel => {
            const step = camelizeProps(panel.props);

            return 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 h('div', {
        class: classes.value
      }, hMergeSlot(slots.navigation, getContent()))
    }
  }
});

var QStepperNavigation = createComponent({
  name: 'QStepperNavigation',

  setup (_, { slots }) {
    return () => 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 = getCurrentInstance();
    const { proxy: { $q } } = vm;

    const onClick = evt => { emit('click', evt); };

    return () => {
      if (props.props === void 0) {
        return 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 ](
          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); // eslint-disable-line
          onClick(evt);
        }
      };

      return h('th', data, child)
    }
  }
});

function getTableMiddle (props, content) {
  return h('div', props, [
    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 = ref(null);

    const virtualScrollLength = 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 = 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 = computed(() =>
      'q-virtual-scroll q-virtual-scroll' + (props.virtualScrollHorizontal === true ? '--horizontal' : '--vertical')
      + (props.scrollTarget !== void 0 ? '' : ' scroll')
    );

    const attributes = computed(() => (
      props.scrollTarget !== void 0 ? {} : { tabindex: 0 }
    ));

    watch(virtualScrollLength, () => {
      localResetVirtualScroll();
    });

    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)
    }

    onBeforeMount(() => {
      localResetVirtualScroll();
    });

    onMounted(() => {
      configureScrollTarget();
    });

    onActivated(() => {
      configureScrollTarget();
    });

    onDeactivated(() => {
      unconfigureScrollTarget();
    });

    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()
        )
        : 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 = computed(() => {
    const { sortBy } = computedPagination.value;

    return sortBy
      ? colList.value.find(def => def.name === sortBy) || null
      : null
  });

  const computedSortMethod = 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 = 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
            })
          )
        }
  ));

  watch(
    () => props.filter,
    () => {
      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 = ref(
    Object.assign({
      sortBy: null,
      descending: false,
      page: 1,
      rowsPerPage: props.rowsPerPageOptions.length > 0
        ? props.rowsPerPageOptions[ 0 ]
        : 5
    }, props.pagination)
  );

  const computedPagination = computed(() => {
    const pag = props[ 'onUpdate:pagination' ] !== void 0
      ? { ...innerPagination.value, ...props.pagination }
      : innerPagination.value;

    return fixPagination(pag)
  });

  const isServerSide = computed(() => computedPagination.value.rowsNumber !== void 0);

  function sendServerRequest (pagination) {
    requestServerInteraction({
      pagination,
      filter: props.filter
    });
  }

  function requestServerInteraction (prop = {}) {
    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 = computed(() => (
    isServerSide.value === true
      ? computedPagination.value.rowsNumber || 0
      : filteredSortedRowsNumber.value
  ));

  const firstRowIndex = computed(() => {
    const { page, rowsPerPage } = computedPagination.value;
    return (page - 1) * rowsPerPage
  });

  const lastRowIndex = computed(() => {
    const { page, rowsPerPage } = computedPagination.value;
    return page * rowsPerPage
  });

  const isFirstPage = computed(() => computedPagination.value.page === 1);

  const pagesNumber = computed(() => (
    computedPagination.value.rowsPerPage === 0
      ? 1
      : Math.max(
        1,
        Math.ceil(computedRowsNumber.value / computedPagination.value.rowsPerPage)
      )
  ));

  const isLastPage = computed(() => (
    lastRowIndex.value === 0
      ? true
      : computedPagination.value.page >= pagesNumber.value
  ));

  const computedRowsPerPageOptions = 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
    }))
  });

  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 = computed(() => {
    const keys = {};
    props.selected.map(getRowKey.value).forEach(key => {
      keys[ key ] = true;
    });
    return keys
  });

  const hasSelectionMode = computed(() => {
    return props.selection !== 'none'
  });

  const singleSelection = computed(() => {
    return props.selection === 'single'
  });

  const multipleSelection = computed(() => {
    return props.selection === 'multiple'
  });

  const allRowsSelected = computed(() =>
    computedRows.value.length > 0 && computedRows.value.every(
      row => selectedKeys.value[ getRowKey.value(row) ] === true
    )
  );

  const someRowsSelected = computed(() =>
    allRowsSelected.value !== true
    && computedRows.value.some(row => selectedKeys.value[ getRowKey.value(row) ] === true)
  );

  const rowsSelectedNumber = 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 = ref(getVal(props.expanded));

  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 = 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 = 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 = computed(() => {
    const names = {};
    computedCols.value.forEach(col => {
      names[ col.name ] = col;
    });
    return names
  });

  const computedColspan = 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 = getCurrentInstance();
    const { proxy: { $q } } = vm;

    const isDark = useDark(props, $q);
    const { inFullscreen, toggleFullscreen } = useFullscreen();

    const getRowKey = computed(() => (
      typeof props.rowKey === 'function'
        ? props.rowKey
        : row => row[ props.rowKey ]
    ));

    const rootRef = ref(null);
    const virtScrollRef = ref(null);
    const hasVirtScroll = computed(() => props.grid !== true && props.virtualScroll === true);

    const cardDefaultClass = 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 = 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 = computed(() =>
      __containerClass.value + (props.loading === true ? ' q-table--loading' : '')
    );

    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 = 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 = computed(() => filteredSortedRows.value.length);

    const computedRows = 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 = computed(() => computedRows.value.length === 0);

    const virtProps = 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 = 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 = () => h('tbody', bottomRow({ cols: computedCols.value }));
        }

        return 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 [
        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 }))
            : 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 }))
          : [
              h(QCheckbox, {
                modelValue: selected,
                color: props.color,
                dark: isDark.value,
                dense: props.dense,
                'onUpdate:modelValue': (adding, evt) => {
                  updateSelection([ key ], [ row ], adding, evt);
                }
              })
            ];

        child.unshift(
          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 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 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 = 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 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(
            h('div', { class: 'q-table__control' }, [
              topLeft(marginalsScope.value)
            ])
          );
        }
        else if (props.title) {
          child.push(
            h('div', { class: 'q-table__control' }, [
              h('div', {
                class: [ 'q-table__title', props.titleClass ]
              }, props.title)
            ])
          );
        }
      }

      if (topRight !== void 0) {
        child.push(
          h('div', { class: 'q-table__separator col' })
        );
        child.push(
          h('div', { class: 'q-table__control' }, [
            topRight(marginalsScope.value)
          ])
        );
      }

      if (child.length === 0) {
        return
      }

      return h('div', { class: topClass }, child)
    }

    const headerSelectedValue = computed(() => (
      someRowsSelected.value === true
        ? null
        : allRowsSelected.value
    ));

    function getTHead () {
      const child = getTHeadTR();

      if (props.loading === true && slots.loading === void 0) {
        child.push(
          h('tr', { class: 'q-table__progress' }, [
            h('th', {
              class: 'relative-position',
              colspan: computedColspan.value
            }, getProgress())
          ])
        );
      }

      return 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)
          : h(QTh, {
            key: col.name,
            props
          }, () => col.label)
      });

      if (singleSelection.value === true && props.grid !== true) {
        child.unshift(
          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({}))
          : [
              h(QCheckbox, {
                color: props.color,
                modelValue: headerSelectedValue.value,
                dark: isDark.value,
                dense: props.dense,
                'onUpdate:modelValue': onMultipleSelectionSet
              })
            ];

        child.unshift(
          h('th', { class: 'q-table--col-auto-width' }, content)
        );
      }

      return [
        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 = 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 }) ]
          : [
              h(QIcon, {
                class: 'q-table__bottom-nodata-icon',
                name: $q.iconSet.table.warning
              }),
              message
            ];

        return h('div', { class: bottomClass + ' q-table__bottom--nodata' }, children)
      }

      const bottom = slots.bottom;

      if (bottom !== void 0) {
        return h('div', { class: bottomClass }, [ bottom(marginalsScope.value) ])
      }

      const child = props.hideSelectedBanner !== true && hasSelectionMode.value === true && rowsSelectedNumber.value > 0
        ? [
            h('div', { class: 'q-table__control' }, [
              h('div', [
                (props.selectedRowsLabel || $q.lang.table.selectedRecords)(rowsSelectedNumber.value)
              ])
            ])
          ]
        : [];

      if (props.hidePagination !== true) {
        return h('div', {
          class: bottomClass + ' justify-end'
        }, getPaginationDiv(child))
      }

      if (child.length > 0) {
        return 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(
        h('div', { class: 'q-table__separator col' })
      );

      if (hasOpts === true) {
        child.push(
          h('div', { class: 'q-table__control' }, [
            h('span', { class: 'q-table__bottom-item' }, [
              props.rowsPerPageLabel || $q.lang.table.recordsPerPage
            ]),
            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 = [
          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(
            h(QBtn, {
              key: 'pgFirst',
              ...btnProps,
              icon: navIcon.value[ 0 ],
              disable: isFirstPage.value,
              onClick: firstPage
            })
          );

          control.push(
            h(QBtn, {
              key: 'pgPrev',
              ...btnProps,
              icon: navIcon.value[ 1 ],
              disable: isFirstPage.value,
              onClick: prevPage
            }),

            h(QBtn, {
              key: 'pgNext',
              ...btnProps,
              icon: navIcon.value[ 2 ],
              disable: isLastPage.value,
              onClick: nextPage
            })
          );

          pagesNumber.value > 2 && control.push(
            h(QBtn, {
              key: 'pgLast',
              ...btnProps,
              icon: navIcon.value[ 3 ],
              disable: isLastPage.value,
              onClick: lastPage
            })
          );
        }
      }

      child.push(
        h('div', { class: 'q-table__control' }, control)
      );

      return child
    }

    function getGridHeader () {
      const child = props.gridHeader === true
        ? [
            h('table', { class: 'q-table' }, [
              getTHead()
            ])
          ]
        : (
            props.loading === true && slots.loading === void 0
              ? getProgress()
              : void 0
          );

      return h('div', { class: 'q-table__middle' }, child)
    }

    function getGridBody () {
      const item = slots.item !== void 0
        ? slots.item
        : scope => {
          const child = scope.cols.map(
            col => h('div', { class: 'q-table__grid-item-row' }, [
              h('div', { class: 'q-table__grid-item-title' }, [ col.label ]),
              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)
              : [
                  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(
              h('div', { class: 'q-table__grid-item-row' }, content),
              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 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' : '')
          }, [
            h('div', data, child)
          ])
        };

      return 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 h('div', data, child)
    }
  }
});

var QTr = createComponent({
  name: 'QTr',

  props: {
    props: Object,
    noHover: Boolean
  },

  setup (props, { slots }) {
    const classes = computed(() =>
      'q-tr'
      + (props.props === void 0 || props.props.header === true ? '' : ' ' + props.props.__trClass)
      + (props.noHover === true ? ' q-tr--no-hover' : '')
    );

    return () => 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 = getCurrentInstance();
    const classes = 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 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 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: computed(() => props.exact),
        ...routeData
      }
    );

    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 = 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 = ref(null);

    const mask = computed(() => getMask());
    const locale = computed(() => getLocale());

    const defaultDateModel = computed(() => getDefaultDateModel());

    const model = __splitDate(
      props.modelValue,
      mask.value, // initial mask
      locale.value, // initial locale
      props.calendar,
      defaultDateModel.value
    );

    const view = ref(getViewByModel(model));
    const innerModel = ref(model);
    const isAM = ref(model.hour === null || model.hour < 12);

    const classes = 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 = 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 = computed(() => (
      props.format24h !== null
        ? props.format24h
        : $q.lang.date.format24h
    ));

    const pointerStyle = 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 = computed(() => innerModel.value.hour !== null);
    const secLink = computed(() => minLink.value === true && innerModel.value.minute !== null);

    const hourInSelection = computed(() => (
      props.hourOptions !== void 0
        ? val => props.hourOptions.includes(val)
        : (
            props.options !== void 0
              ? val => props.options(val, null, null)
              : null
          )
    ));

    const minuteInSelection = 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 = 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 = 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 = computed(() => (
      minuteInSelection.value !== null
        ? getValidValues(0, 59, minuteInSelection.value)
        : null
    ));

    const validSeconds = computed(() => (
      secondInSelection.value !== null
        ? getValidValues(0, 59, secondInSelection.value)
        : null
    ));

    const viewValidOptions = computed(() => {
      switch (view.value) {
        case 'hour':
          return validHours.value
        case 'minute':
          return validMinutes.value
        case 'second':
          return validSeconds.value
      }
    });

    const positions = 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 = computed(() => {
      return [ [
        TouchPan,
        onPan,
        void 0,
        {
          stop: true,
          prevent: true,
          mouse: true
        }
      ] ]
    });

    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;
        }
      }
    });

    watch([ mask, locale ], () => {
      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 = [
        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),

        h('div', ':'),

        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(
          h('div', ':'),

          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 = [
        h('div', {
          class: 'q-time__header-label row items-center no-wrap',
          dir: 'ltr'
        }, label)
      ];

      computedFormat24h.value === false && child.push(
        h('div', {
          class: 'q-time__header-ampm column items-between no-wrap'
        }, [
          h('div', {
            class: 'q-time__link '
              + (isAM.value === true ? 'q-time__link--active' : 'cursor-pointer'),
            tabindex: tabindex.value,
            onClick: setAm,
            onKeyup: setAmOnKey
          }, 'AM'),

          h('div', {
            class: 'q-time__link '
              + (isAM.value !== true ? 'q-time__link--active' : 'cursor-pointer'),
            tabindex: tabindex.value,
            onClick: setPm,
            onKeyup: setPmOnKey
          }, 'PM')
        ])
      );

      return h('div', {
        class: 'q-time__header flex flex-center no-wrap ' + headerClass.value
      }, child)
    }

    function getClock () {
      const current = innerModel.value[ view.value ];

      return h('div', {
        class: 'q-time__content col relative-position'
      }, [
        h(Transition, {
          name: 'q-transition--scale'
        }, () => h('div', {
          key: 'clock' + view.value,
          class: 'q-time__container-parent absolute-full'
        }, [
          h('div', {
            ref: clockRef,
            class: 'q-time__container-child fit overflow-hidden'
          }, [
            withDirectives(
              h('div', {
                class: 'q-time__clock cursor-pointer non-selectable',
                onClick,
                onMousedown
              }, [
                h('div', { class: 'q-time__clock-circle fit' }, [
                  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 => 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' : ''))
                  }, [ h('span', pos.label) ]))
                ])
              ]),
              clockDirectives.value
            )
          ])
        ])),

        props.nowBtn === true ? 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(
        h('div', { class: 'q-time__actions' }, def)
      );

      if (props.name !== void 0 && props.disable !== true) {
        injectFormInput(child, 'push');
      }

      return h('div', {
        class: classes.value,
        tabindex: -1
      }, [
        getHeader(),
        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 = getCurrentInstance();
    const isDark = useDark(props, vm.proxy.$q);

    provide(timelineKey, props);

    const classes = computed(() =>
      `q-timeline q-timeline--${ props.layout } q-timeline--${ props.layout }--${ props.side }`
      + (isDark.value === true ? ' q-timeline--dark' : '')
    );

    return () => 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 = inject(timelineKey, emptyRenderFn);
    if ($timeline === emptyRenderFn) {
      console.error('QTimelineEntry needs to be child of QTimeline');
      return emptyRenderFn
    }

    const classes = computed(() =>
      `q-timeline__entry q-timeline__entry--${ props.side }`
      + (props.icon !== void 0 || props.avatar !== void 0 ? ' q-timeline__entry--icon' : '')
    );

    const dotClass = computed(() =>
      `q-timeline__dot text-${ props.color || $timeline.color }`
    );

    const reverse = 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 = [
          h('div'),
          h('div'),
          h(
            props.tag,
            { class: 'q-timeline__heading-title' },
            child
          )
        ];

        return h('div', {
          class: 'q-timeline__heading'
        }, reverse.value === true ? content.reverse() : content)
      }

      let dot;

      if (props.icon !== void 0) {
        dot = [
          h(QIcon, {
            class: 'row items-center justify-center',
            name: props.icon
          })
        ];
      }
      else if (props.avatar !== void 0) {
        dot = [
          h('img', {
            class: 'q-timeline__dot-img',
            src: props.avatar
          })
        ];
      }

      const content = [
        h('div', { class: 'q-timeline__subtitle' }, [
          h('span', {}, hSlot(slots.subtitle, [ props.subtitle ]))
        ]),

        h('div', { class: dotClass.value }, dot),

        h('div', { class: 'q-timeline__content' }, [
          h('h6', { class: 'q-timeline__title' }, hSlot(slots.title, [ props.title ]))
        ].concat(child))
      ];

      return h('li', {
        class: classes.value
      }, reverse.value === true ? content.reverse() : content)
    }
  }
});

var QToolbar = createComponent({
  name: 'QToolbar',

  props: {
    inset: Boolean
  },

  setup (props, { slots }) {
    const classes = computed(() =>
      'q-toolbar row no-wrap items-center'
      + (props.inset === true ? ' q-toolbar--inset' : '')
    );

    return () => h('div', { class: classes.value, role: 'toolbar' }, hSlot(slots.default))
  }
});

var QToolbarTitle = createComponent({
  name: 'QToolbarTitle',

  props: {
    shrink: Boolean
  },

  setup (props, { slots }) {
    const classes = computed(() =>
      'q-toolbar__title ellipsis'
      + (props.shrink === true ? ' col-shrink' : '')
    );

    return () => 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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);

    const lazy = ref({});
    const innerTicked = ref(props.ticked || []);
    const innerExpanded = ref(props.expanded || []);

    let blurTargets = {};

    onBeforeUpdate(() => {
      blurTargets = {};
    });

    const classes = 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 = computed(() => props.selected !== void 0);

    const computedIcon = computed(() => props.icon || $q.iconSet.tree.icon);

    const computedControlColor = computed(() => props.controlColor || props.color);

    const textColorClass = computed(() => (
      props.textColor !== void 0
        ? ` text-${ props.textColor }`
        : ''
    ));

    const selectedColorClass = computed(() => {
      const color = props.selectedColor || props.color;
      return color ? ` text-${ color }` : ''
    });

    const computedFilterMethod = 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 = 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 ] && 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
    });

    watch(() => props.ticked, val => {
      innerTicked.value = val;
    });

    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 = innerExpanded.value,
        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 : [];
            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 h(QIcon, {
          class: 'q-tree__icon q-mr-sm',
          name: node.icon,
          color: node.iconColor
        })
      }
      const src = node.img || node.avatar;
      if (src) {
        return 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 = h('div', { class: 'q-tree__node-body relative-position' }, [
          h('div', { class: textColorClass.value }, [
            body(slotScope)
          ])
        ]);
      }

      return h('div', {
        key,
        class: 'q-tree__node relative-position'
          + ` q-tree__node--${ isParent === true ? 'parent' : 'child' }`
      }, [
        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); }
            }
          }
        }, [
          h('div', {
            class: 'q-focus-helper',
            tabindex: -1,
            ref: el => { blurTargets[ m.key ] = el; }
          }),

          m.lazy === 'loading'
            ? h(QSpinner, {
              class: 'q-tree__spinner',
              color: computedControlColor.value
            })
            : (
                isParent === true
                  ? 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
            ? 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,

          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),
                  h('div', node[ props.labelKey ])
                ]
          ])
        ]),

        isParent === true
          ? (
              props.noTransition === true
                ? h('div', {
                  class: 'q-tree__node-collapsible' + textColorClass.value,
                  key: `${ key }__q`
                }, [
                  body,
                  h('div', {
                    class: 'q-tree__children'
                      + (m.disabled === true ? ' q-tree__node--disabled' : '')
                  }, m.expanded ? children : null)
                ])

                : h(QSlideTransition, {
                  duration: props.duration,
                  onShow,
                  onHide
                }, () => withDirectives(
                  h('div', {
                    class: 'q-tree__node-collapsible' + textColorClass.value,
                    key: `${ key }__q`
                  }, [
                    body,
                    h('div', {
                      class: 'q-tree__children'
                        + (m.disabled === true ? ' q-tree__node--disabled' : '')
                    }, children)
                  ]),
                  [ [ 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 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) {
  const vm = 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 = computed(() => props.disable !== true && props.readonly !== true);
  const dnd = ref(false);

  const rootRef = ref(null);
  const inputRef = ref(null);

  const state = {
    files: ref([]),
    queuedFiles: ref([]),
    uploadedFiles: ref([]),
    uploadedSize: 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 }));

  if (state.isBusy === void 0) {
    state.isBusy = ref(false);
  }

  const uploadSize = ref(0);
  const uploadProgress = computed(() => (
    uploadSize.value === 0
      ? 0
      : state.uploadedSize.value / uploadSize.value
  ));
  const uploadProgressLabel = computed(() => getProgressLabel(uploadProgress.value));
  const uploadSizeLabel = computed(() => humanStorageSize(uploadSize.value));

  const canAddFiles = 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 = computed(() =>
    editable.value === true
    && state.isBusy.value !== true
    && state.isUploading.value !== true
    && state.queuedFiles.value.length > 0
  );

  provide(uploaderKey, renderInput);

  const classes = 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 = computed(() =>
    'q-uploader__header'
    + (props.color !== void 0 ? ` bg-${ props.color }` : '')
    + (props.textColor !== void 0 ? ` text-${ props.textColor }` : '')
  );

  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 h(QBtn, data, child)
    }
  }

  function renderInput () {
    return 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 [
      h('div', {
        class: 'q-uploader__header-content column'
      }, [
        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
            ? h(QSpinner, { class: 'q-uploader__spinner' })
            : null,

          h('div', { class: 'col column justify-center' }, [
            props.label !== void 0
              ? h('div', { class: 'q-uploader__title' }, [ props.label ])
              : null,

            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 => 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
    }, [
      h('div', {
        class: 'q-uploader__file-header row flex-center no-wrap'
      }, [
        file.__status === 'failed'
          ? h(QIcon, {
            class: 'q-uploader__file-status',
            name: $q.iconSet.type.negative,
            color: 'negative'
          })
          : null,

        h('div', { class: 'q-uploader__file-header-content col' }, [
          h('div', { class: 'q-uploader__title' }, [ file.name ]),
          h('div', {
            class: 'q-uploader__subtitle row items-center no-wrap'
          }, [
            file.__sizeLabel + ' / ' + file.__progressLabel
          ])
        ]),

        file.__status === 'uploading'
          ? h(QCircularProgress, {
            value: file.__progress,
            min: 0,
            max: 1,
            indeterminate: file.__progress === 0
          })
          : h(QBtn, {
            round: true,
            dense: true,
            flat: true,
            icon: $q.iconSet.uploader[ file.__status === 'uploaded' ? 'done' : 'clear' ],
            onClick: () => { removeFile(file); }
          })
      ])
    ]))
  }

  onBeforeUnmount(() => {
    state.isUploading.value === true && state.abort();
    state.files.value.length > 0 && revokeImgURLs();
  });

  const publicApi = {};

  for (const key in state) {
    if (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)
  Object.assign(proxy, publicApi);

  return () => {
    const children = [
      h('div', { class: colorClass.value }, getHeader()),
      h('div', { class: 'q-uploader__list scroll' }, getList()),
      getDndNode('uploader')
    ];

    state.isBusy.value === true && children.push(
      h('div', {
        class: 'q-uploader__overlay absolute-full flex flex-center'
      }, [ h(QSpinner) ])
    );

    const data = { ref: rootRef, class: classes.value };

    if (canAddFiles.value === true) {
      Object.assign(data, { onDragover, onDragleave });
    }

    return 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 () {
    return getRenderer(injectPlugin)
  }
});

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 = ref([]);
  const promises = ref([]);
  const workingThreads = ref(0);

  const xhrProps = 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 = computed(() => workingThreads.value > 0);
  const isBusy = 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 = 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 = computed(() =>
      'q-video'
      + (props.ratio !== void 0 ? ' q-video--responsive' : '')
    );

    return () => h('div', {
      class: classes.value,
      style: ratioStyle.value
    }, [
      h('iframe', {
        src: props.src,
        title: props.title,
        fetchpriority: props.fetchpriority,
        loading: props.loading,
        referrerpolicy: props.referrerpolicy,
        frameborder: '0',
        allowfullscreen: true
      })
    ])
  }
});

/*
 * 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;
        }
      }
    }
);

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 } = getCurrentInstance();
    const isDark = useDark(props, proxy.$q);

    const dialogRef = 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
          ? h(QSeparator, {
            class: 'col-all',
            dark: isDark.value
          })
          : 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); }
          }, [
            h('div', { class: 'q-focus-helper' }),

            action.icon
              ? h(QIcon, { name: action.icon, color: action.color })
              : (
                  img
                    ? h('img', {
                      class: action.avatar ? 'q-bottom-sheet__avatar' : '',
                      src: img
                    })
                    : h('div', { class: 'q-bottom-sheet__empty-icon' })
                ),

            h('div', action.label)
          ])
      })
    }

    function getList () {
      return props.actions.map(action => {
        const img = action.avatar || action.img;

        return action.label === void 0
          ? h(QSeparator, { spaced: true, dark: isDark.value })
          : h(QItem, {
            class: [ 'q-bottom-sheet__item', action.classes ],
            style: action.style,
            tabindex: 0,
            clickable: true,
            dark: isDark.value,
            onClick () { onOk(action); }
          }, () => [
            h(
              QItemSection,
              { avatar: true },
              () => (
                action.icon
                  ? h(QIcon, { name: action.icon, color: action.color })
                  : (
                      img
                        ? h('img', {
                          class: action.avatar ? 'q-bottom-sheet__avatar' : '',
                          src: img
                        })
                        : null
                    )
              )
            ),

            h(QItemSection, () => action.label)
          ])
      })
    }

    function getCardContent () {
      const child = [];

      props.title && child.push(
        h(QCardSection, {
          class: 'q-dialog__title'
        }, () => props.title)
      );

      props.message && child.push(
        h(QCardSection, {
          class: 'q-dialog__message'
        }, () => props.message)
      );

      child.push(
        props.grid === true
          ? h('div', {
            class: 'row items-stretch justify-start',
            role: 'list'
          }, getGrid())
          : h('div', {
            role: 'list'
          }, getList())
      );

      return child
    }

    function getContent () {
      return [
        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 () => 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 = 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: () => () => h(DialogComponent, {
        ...props,
        ref: dialogRef,
        onOk,
        onHide,
        onVnodeMounted (...args) {
          if (typeof props.onVnodeMounted === 'function') {
            props.onVnodeMounted(...args);
          }

          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;
  }
};

if (__QUASAR_SSR__) {
  Plugin$4.parseSSR = ssrContext => {
    if (ssrContext !== void 0) {
      return getObject(ssrContext)
    }
  };
}

{
  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 } = getCurrentInstance();
    const { $q } = proxy;

    const isDark = useDark(props, $q);

    const dialogRef = ref(null);

    const model = ref(
      props.prompt !== void 0
        ? props.prompt.model
        : (props.options !== void 0 ? props.options.model : void 0)
    );

    const classes = computed(() =>
      'q-dialog-plugin'
      + (isDark.value === true ? ' q-dialog-plugin--dark q-dark' : '')
      + (props.progress !== false ? ' q-dialog-plugin--progress' : '')
    );

    const vmColor = computed(() =>
      props.color || (isDark.value === true ? 'amber' : 'primary')
    );

    const spinner = 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 = computed(() =>
      props.prompt !== void 0 || props.options !== void 0
    );

    const formProps = computed(() => {
      if (hasForm.value !== true) {
        return {}
      }

      const { model, isValid, items, ...formProps } = props.prompt !== void 0
        ? props.prompt
        : props.options;

      return formProps
    });

    const okLabel = computed(() => (
      isObject(props.ok) === true
        ? $q.lang.label.ok
        : (
            props.ok === true
              ? $q.lang.label.ok
              : props.ok
          )
    ));

    const cancelLabel = computed(() => (
      isObject(props.cancel) === true
        ? $q.lang.label.cancel
        : (
            props.cancel === true
              ? $q.lang.label.cancel
              : props.cancel
          )
    ));

    const okDisabled = 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 = 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 = 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
    }));

    watch(() => props.prompt && props.prompt.model, onUpdateModel);
    watch(() => props.options && props.options.model, onUpdateModel);

    function show () {
      dialogRef.value.show();
    }

    function hide () {
      dialogRef.value.hide();
    }

    function onOk () {
      emit('ok', 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
        ? h(QCardSection, {
          class: classes,
          innerHTML: text
        })
        : h(QCardSection, { class: classes }, () => text)
    }

    function getPrompt () {
      return [
        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 [
        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(
        h(QBtn, cancelProps.value)
      );

      props.ok && child.push(
        h(QBtn, okProps.value)
      );

      return 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(
        h(
          QCardSection,
          { class: 'q-dialog__progress' },
          () => h(spinner.value.component, spinner.value.props)
        )
      );

      props.message && child.push(
        getSection('q-dialog__message', props.message)
      );

      if (props.prompt !== void 0) {
        child.push(
          h(
            QCardSection,
            { class: 'scroll q-dialog-plugin__form' },
            getPrompt
          )
        );
      }
      else if (props.options !== void 0) {
        child.push(
          h(QSeparator, { dark: isDark.value }),
          h(
            QCardSection,
            { class: 'scroll q-dialog-plugin__form' },
            getOptions
          ),
          h(QSeparator, { dark: isDark.value })
        );
      }

      if (props.ok || props.cancel) {
        child.push(getButtons());
      }

      return child
    }

    function getContent () {
      return [
        h(QCard, {
          class: [
            classes.value,
            props.cardClass
          ],
          style: props.cardStyle,
          dark: isDark.value
        }, getCardContent)
      ]
    }

    // expose public methods
    Object.assign(proxy, { show, hide });

    return () => 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 = 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 = 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: () => () => 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 () {
            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 = [
                h(props.spinner, {
                  class: 'q-loading__spinner',
                  color: props.spinnerColor,
                  size: props.spinnerSize
                })
              ];

              props.message && content.push(
                h('div', {
                  class: 'q-loading__message'
                    + (props.messageColor ? ` text-${ props.messageColor }` : ''),
                  [ props.html === true ? 'innerHTML' : 'textContent' ]: props.message
                })
              );

              return h('div', {
                class: 'q-loading fullscreen flex flex-center z-max ' + props.customClass.trim(),
                key: props.uid
              }, [
                h('div', {
                  class: 'q-loading__backdrop'
                    + (props.backgroundColor ? ` bg-${ props.backgroundColor }` : '')
                }),

                h('div', {
                  class: 'q-loading__box column items-center ' + props.boxClass
                }, content)
              ])
            }

            return () => h(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 = 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 () => h('div', { class: 'q-notifications' }, positionList.map(pos => {
        return h(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(
                h(notif.spinner, {
                  class: 'q-notification__spinner q-notification__spinner--' + meta.leftClass,
                  color: notif.spinnerColor,
                  size: notif.spinnerSize
                })
              );
            }
            else if (notif.icon) {
              mainChild.push(
                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(
                h(QAvatar, {
                  class: 'q-notification__avatar q-notification__avatar--' + meta.leftClass
                }, () => 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
                ? `<div>${ notif.message }</div><div class="q-notification__caption">${ notif.caption }</div>`
                : notif.message;
            }
            else {
              const msgNode = [ notif.message ];
              msgChild = notif.caption
                ? [
                    h('div', msgNode),
                    h('div', { class: 'q-notification__caption' }, [ notif.caption ])
                  ]
                : msgNode;
            }

            mainChild.push(
              h('div', msgData, msgChild)
            );
          }

          const child = [
            h('div', { class: meta.contentClass }, mainChild)
          ];

          notif.progress === true && child.push(
            h('div', {
              key: `${ meta.uid }|p|${ meta.badge }`,
              class: meta.progressClass,
              style: meta.progressStyle
            })
          );

          notif.actions !== void 0 && child.push(
            h('div', {
              class: meta.actionsClass
            }, notif.actions.map(props => h(QBtn, props)))
          );

          meta.badge > 1 && child.push(
            h('div', {
              key: `${ meta.uid }|${ meta.badge }`,
              class: notif.meta.badgeClass,
              style: notif.badgeStyle
            }, [ meta.badge ])
          );

          return h('div', {
            ref: el => { notifRefs[ '' + meta.uid ] = el; },
            key: meta.uid,
            class: meta.class,
            ...meta.attrs
          }, [
            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 ] = 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);

// To be used for the custom component
// used on a Dialog plugin

function useDialogPluginComponent () {
  const { emit, proxy } = getCurrentInstance();

  // we need a Vue reference to the QDialog
  // component so we can handle it;
  // <q-dialog ref="dialogRef" ...
  // make sure that the setup() in which this
  // function is called returns dialogRef variable
  const dialogRef = ref(null);

  function show () { dialogRef.value.show(); }
  function hide () { dialogRef.value.hide(); }

  function onDialogOK (payload) {
    emit('ok', payload);
    hide();
  }

  function onDialogHide () { emit('hide'); }

  // expose public methods required by Dialog plugin
  Object.assign(proxy, { show, hide });

  return {
    dialogRef,
    onDialogHide,
    onDialogOK,
    onDialogCancel: hide
  }
}

// Don't forget to update the types in "ui/types/composables.d.ts"
const emits = [ 'ok', 'hide' ];

useDialogPluginComponent.emits = emits;
useDialogPluginComponent.emitsObject = getEmitsObject(emits);

function useMeta (metaOptions) {
  {
    const meta = { active: true };

    if (typeof metaOptions === 'function') {
      const content = computed(metaOptions);
      meta.val = content.value;

      watch(content, val => {
        meta.val = val;
        meta.active === true && planClientUpdate();
      });
    }
    else {
      meta.val = metaOptions;
    }

    clientList.push(meta);
    planClientUpdate();

    onActivated(() => {
      meta.active = true;
      planClientUpdate();
    });

    onDeactivated(() => {
      meta.active = false;
      planClientUpdate();
    });

    onUnmounted(() => {
      clientList.splice(clientList.indexOf(meta), 1);
      planClientUpdate();
    });
  }
}

/**
 * Returns the $q instance.
 * Equivalent to `this.$q` inside templates.
 */
function useQuasar () {
  return inject(quasarKey)
}

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 ];
    const liveEvents = [];

    if (list !== void 0 && callback) {
      list.forEach(entry => {
        if (entry.fn !== callback && entry.fn.__callback !== callback) {
          liveEvents.push(entry);
        }
      });

      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<any>, ... ]
 *                          Object form: { [key: string]: (resultAggregator: object) => Promise<any>, ... }
 * @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<Array<Object> | 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)
}

/**
 * client entry-point used by @quasar/vite-plugin for DEV only
 */

const Quasar = {
  version: '2.11.9',
  install: installQuasar,
  lang: Plugin$8,
  iconSet: Plugin$7
};

export { AddressbarColor, Plugin$6 as AppFullscreen, Plugin$5 as AppVisibility, BottomSheet, ClosePopup, Plugin$4 as Cookies, Plugin$9 as Dark, Dialog, EventBus, Intersection, Plugin$2 as Loading, Plugin$3 as LoadingBar, Plugin$1 as LocalStorage, Meta, Morph, Mutation, Notify, Platform, QAjaxBar, QAvatar, QBadge, QBanner, QBar, QBreadcrumbs, QBreadcrumbsEl, QBtn, QBtnDropdown, QBtnGroup, QBtnToggle, QCard, QCardActions, QCardSection, QCarousel, QCarouselControl, QCarouselSlide, QChatMessage, QCheckbox, QChip, QCircularProgress, QColor, QDate, QDialog, QDrawer, QEditor, QExpansionItem, QFab, QFabAction, QField, QFile, QFooter, QForm, QFormChildMixin, QHeader, QIcon, QImg, QInfiniteScroll, QInnerLoading, QInput, QIntersection, QItem, QItemLabel, QItemSection, QKnob, QLayout, QLinearProgress, QList, QMarkupTable, QMenu, QNoSsr, QOptionGroup, QPage, QPageContainer, QPageScroller, QPageSticky, QPagination, QParallax, QPopupEdit, QPopupProxy, QPullToRefresh, QRadio, QRange, QRating, QResizeObserver, QResponsive, QRouteTab, QScrollArea, QScrollObserver, QSelect, QSeparator, QSkeleton, QSlideItem, QSlideTransition, QSlider, QSpace, QSpinner, QSpinnerAudio, QSpinnerBall, QSpinnerBars, QSpinnerBox, QSpinnerClock, QSpinnerComment, QSpinnerCube, QSpinnerDots, QSpinnerFacebook, QSpinnerGears, QSpinnerGrid, QSpinnerHearts, QSpinnerHourglass, QSpinnerInfinity, QSpinnerIos, QSpinnerOrbit, QSpinnerOval, QSpinnerPie, QSpinnerPuff, QSpinnerRadio, QSpinnerRings, QSpinnerTail, QSplitter, QStep, QStepper, QStepperNavigation, QTab, QTabPanel, QTabPanels, QTable, QTabs, QTd, QTh, QTime, QTimeline, QTimelineEntry, QToggle, QToolbar, QToolbarTitle, QTooltip, QTr, QTree, QUploader, QUploaderAddTrigger, QVideo, QVirtualScroll, Quasar, Ripple, Screen, Scroll, ScrollFire, Plugin as SessionStorage, TouchHold, TouchPan, TouchRepeat, TouchSwipe, cloneDeep as clone, colors, copyToClipboard, createMetaMixin, createUploaderComponent, date, debounce, dom, event, exportFile, extend, format, frameDebounce, getCssVar, is, morph, noop, openUrl as openURL, patterns, runSequentialPromises, scroll, setCssVar, throttle, uid$3 as uid, useDialogPluginComponent, useFormChild, useMeta, useQuasar };