UNPKG

vuetify

Version:

Vue Material Component Framework

1,652 lines (1,567 loc) 587 kB
/*! * Vuetify v3.1.7 * Forged by John Leider * Released under the MIT License. */ import { ref, onBeforeUnmount, watch, readonly, reactive, computed, watchEffect, toRefs, capitalize, getCurrentInstance as getCurrentInstance$1, unref, provide, inject as inject$1, onScopeDispose, effectScope, shallowRef, defineComponent as defineComponent$1, camelize, h, onDeactivated, onActivated, onMounted, toRaw, createVNode, TransitionGroup, Transition, mergeProps, onBeforeMount, nextTick, withDirectives, Fragment, resolveDirective, vShow, isRef, toRef, Text, resolveDynamicComponent, cloneVNode, warn, toHandlers, Teleport, createTextVNode, onBeforeUpdate, vModelText, onUpdated } from 'vue'; const IN_BROWSER = typeof window !== 'undefined'; const SUPPORTS_INTERSECTION = IN_BROWSER && 'IntersectionObserver' in window; const SUPPORTS_TOUCH = IN_BROWSER && ('ontouchstart' in window || window.navigator.maxTouchPoints > 0); const SUPPORTS_FOCUS_VISIBLE = IN_BROWSER && typeof CSS !== 'undefined' && CSS.supports('selector(:focus-visible)'); // Utilities function useResizeObserver(callback) { const resizeRef = ref(); const contentRect = ref(); if (IN_BROWSER) { const observer = new ResizeObserver(entries => { callback?.(entries, observer); if (!entries.length) return; contentRect.value = entries[0].contentRect; }); onBeforeUnmount(() => { observer.disconnect(); }); watch(resizeRef, (newValue, oldValue) => { if (oldValue) { observer.unobserve(oldValue); contentRect.value = undefined; } if (newValue) observer.observe(newValue); }, { flush: 'post' }); } return { resizeRef, contentRect: readonly(contentRect) }; } function _classPrivateFieldInitSpec(obj, privateMap, value) { _checkPrivateRedeclaration(obj, privateMap); privateMap.set(obj, value); } function _checkPrivateRedeclaration(obj, privateCollection) { if (privateCollection.has(obj)) { throw new TypeError("Cannot initialize the same private elements twice on an object"); } } function _classPrivateFieldSet(receiver, privateMap, value) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "set"); _classApplyDescriptorSet(receiver, descriptor, value); return value; } function _classApplyDescriptorSet(receiver, descriptor, value) { if (descriptor.set) { descriptor.set.call(receiver, value); } else { if (!descriptor.writable) { throw new TypeError("attempted to set read only private field"); } descriptor.value = value; } } function _classPrivateFieldGet(receiver, privateMap) { var descriptor = _classExtractFieldDescriptor(receiver, privateMap, "get"); return _classApplyDescriptorGet(receiver, descriptor); } function _classExtractFieldDescriptor(receiver, privateMap, action) { if (!privateMap.has(receiver)) { throw new TypeError("attempted to " + action + " private field on non-instance"); } return privateMap.get(receiver); } function _classApplyDescriptorGet(receiver, descriptor) { if (descriptor.get) { return descriptor.get.call(receiver); } return descriptor.value; } // Types function getNestedValue(obj, path, fallback) { const last = path.length - 1; if (last < 0) return obj === undefined ? fallback : obj; for (let i = 0; i < last; i++) { if (obj == null) { return fallback; } obj = obj[path[i]]; } if (obj == null) return fallback; return obj[path[last]] === undefined ? fallback : obj[path[last]]; } function deepEqual(a, b) { if (a === b) return true; if (a instanceof Date && b instanceof Date && a.getTime() !== b.getTime()) { // If the values are Date, compare them as timestamps return false; } if (a !== Object(a) || b !== Object(b)) { // If the values aren't objects, they were already checked for equality return false; } const props = Object.keys(a); if (props.length !== Object.keys(b).length) { // Different number of props, don't bother to check return false; } return props.every(p => deepEqual(a[p], b[p])); } function getObjectValueByPath(obj, path, fallback) { // credit: http://stackoverflow.com/questions/6491463/accessing-nested-javascript-objects-with-string-key#comment55278413_6491621 if (obj == null || !path || typeof path !== 'string') return fallback; if (obj[path] !== undefined) return obj[path]; path = path.replace(/\[(\w+)\]/g, '.$1'); // convert indexes to properties path = path.replace(/^\./, ''); // strip a leading dot return getNestedValue(obj, path.split('.'), fallback); } function getPropertyFromItem(item, property, fallback) { if (property == null) return item === undefined ? fallback : item; if (item !== Object(item)) { if (typeof property !== 'function') return fallback; const value = property(item, fallback); return typeof value === 'undefined' ? fallback : value; } if (typeof property === 'string') return getObjectValueByPath(item, property, fallback); if (Array.isArray(property)) return getNestedValue(item, property, fallback); if (typeof property !== 'function') return fallback; const value = property(item, fallback); return typeof value === 'undefined' ? fallback : value; } function createRange(length) { let start = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; return Array.from({ length }, (v, k) => start + k); } function convertToUnit(str) { let unit = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'px'; if (str == null || str === '') { return undefined; } else if (isNaN(+str)) { return String(str); } else if (!isFinite(+str)) { return undefined; } else { return `${Number(str)}${unit}`; } } function isObject(obj) { return obj !== null && typeof obj === 'object' && !Array.isArray(obj); } function isComponentInstance(obj) { return obj?.$el; } // KeyboardEvent.keyCode aliases const keyCodes = Object.freeze({ enter: 13, tab: 9, delete: 46, esc: 27, space: 32, up: 38, down: 40, left: 37, right: 39, end: 35, home: 36, del: 46, backspace: 8, insert: 45, pageup: 33, pagedown: 34, shift: 16 }); const keyValues = Object.freeze({ enter: 'Enter', tab: 'Tab', delete: 'Delete', esc: 'Escape', space: 'Space', up: 'ArrowUp', down: 'ArrowDown', left: 'ArrowLeft', right: 'ArrowRight', end: 'End', home: 'Home', del: 'Delete', backspace: 'Backspace', insert: 'Insert', pageup: 'PageUp', pagedown: 'PageDown', shift: 'Shift' }); function keys(o) { return Object.keys(o); } function pick(obj, paths) { const found = Object.create(null); const rest = Object.create(null); for (const key in obj) { if (paths.some(path => path instanceof RegExp ? path.test(key) : path === key)) { found[key] = obj[key]; } else { rest[key] = obj[key]; } } return [found, rest]; } function omit(obj, exclude) { const clone = { ...obj }; exclude.forEach(prop => delete clone[prop]); return clone; } /** * Filter attributes that should be applied to * the root element of a an input component. Remaining * attributes should be passed to the <input> element inside. */ function filterInputAttrs(attrs) { return pick(attrs, ['class', 'style', 'id', /^data-/]); } function wrapInArray(v) { return v == null ? [] : Array.isArray(v) ? v : [v]; } function clamp(value) { let min = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 0; let max = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : 1; return Math.max(min, Math.min(max, value)); } function padEnd(str, length) { let char = arguments.length > 2 && arguments[2] !== undefined ? arguments[2] : '0'; return str + char.repeat(Math.max(0, length - str.length)); } function chunk(str) { let size = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1; const chunked = []; let index = 0; while (index < str.length) { chunked.push(str.substr(index, size)); index += size; } return chunked; } function humanReadableFileSize(bytes) { let base = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 1000; if (bytes < base) { return `${bytes} B`; } const prefix = base === 1024 ? ['Ki', 'Mi', 'Gi'] : ['k', 'M', 'G']; let unit = -1; while (Math.abs(bytes) >= base && unit < prefix.length - 1) { bytes /= base; ++unit; } return `${bytes.toFixed(1)} ${prefix[unit]}B`; } function mergeDeep() { let source = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : {}; let target = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : {}; let arrayFn = arguments.length > 2 ? arguments[2] : undefined; const out = {}; for (const key in source) { out[key] = source[key]; } for (const key in target) { const sourceProperty = source[key]; const targetProperty = target[key]; // Only continue deep merging if // both properties are objects if (isObject(sourceProperty) && isObject(targetProperty)) { out[key] = mergeDeep(sourceProperty, targetProperty, arrayFn); continue; } if (Array.isArray(sourceProperty) && Array.isArray(targetProperty) && arrayFn) { out[key] = arrayFn(sourceProperty, targetProperty); continue; } out[key] = targetProperty; } return out; } function toKebabCase() { let str = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : ''; if (toKebabCase.cache.has(str)) return toKebabCase.cache.get(str); const kebab = str.replace(/[^a-z]/gi, '-').replace(/\B([A-Z])/g, '-$1').toLowerCase(); toKebabCase.cache.set(str, kebab); return kebab; } toKebabCase.cache = new Map(); function findChildrenWithProvide(key, vnode) { if (!vnode || typeof vnode !== 'object') return []; if (Array.isArray(vnode)) { return vnode.map(child => findChildrenWithProvide(key, child)).flat(1); } else if (Array.isArray(vnode.children)) { return vnode.children.map(child => findChildrenWithProvide(key, child)).flat(1); } else if (vnode.component) { if (Object.getOwnPropertySymbols(vnode.component.provides).includes(key)) { return [vnode.component]; } else if (vnode.component.subTree) { return findChildrenWithProvide(key, vnode.component.subTree).flat(1); } } return []; } var _arr = /*#__PURE__*/new WeakMap(); var _pointer = /*#__PURE__*/new WeakMap(); class CircularBuffer { constructor(size) { _classPrivateFieldInitSpec(this, _arr, { writable: true, value: [] }); _classPrivateFieldInitSpec(this, _pointer, { writable: true, value: 0 }); this.size = size; } push(val) { _classPrivateFieldGet(this, _arr)[_classPrivateFieldGet(this, _pointer)] = val; _classPrivateFieldSet(this, _pointer, (_classPrivateFieldGet(this, _pointer) + 1) % this.size); } values() { return _classPrivateFieldGet(this, _arr).slice(_classPrivateFieldGet(this, _pointer)).concat(_classPrivateFieldGet(this, _arr).slice(0, _classPrivateFieldGet(this, _pointer))); } } function getEventCoordinates(e) { if ('touches' in e) { return { clientX: e.touches[0].clientX, clientY: e.touches[0].clientY }; } return { clientX: e.clientX, clientY: e.clientY }; } // Only allow a single return type function destructComputed(getter) { const refs = reactive({}); const base = computed(getter); watchEffect(() => { for (const key in base.value) { refs[key] = base.value[key]; } }, { flush: 'sync' }); return toRefs(refs); } /** Array.includes but value can be any type */ function includes(arr, val) { return arr.includes(val); } const onRE = /^on[^a-z]/; const isOn = key => onRE.test(key); const EventProp = [Function, Array]; function hasEvent(props, name) { name = 'on' + capitalize(name); return !!(props[name] || props[`${name}Once`] || props[`${name}Capture`] || props[`${name}OnceCapture`] || props[`${name}CaptureOnce`]); } function callEvent(handler) { for (var _len2 = arguments.length, args = new Array(_len2 > 1 ? _len2 - 1 : 0), _key2 = 1; _key2 < _len2; _key2++) { args[_key2 - 1] = arguments[_key2]; } if (Array.isArray(handler)) { for (const h of handler) { h(...args); } } else if (typeof handler === 'function') { handler(...args); } } const block = ['top', 'bottom']; const inline = ['start', 'end', 'left', 'right']; /** Parse a raw anchor string into an object */ function parseAnchor(anchor, isRtl) { let [side, align] = anchor.split(' '); if (!align) { align = includes(block, side) ? 'start' : includes(inline, side) ? 'top' : 'center'; } return { side: toPhysical(side, isRtl), align: toPhysical(align, isRtl) }; } function toPhysical(str, isRtl) { if (str === 'start') return isRtl ? 'right' : 'left'; if (str === 'end') return isRtl ? 'left' : 'right'; return str; } function flipSide(anchor) { return { side: { center: 'center', top: 'bottom', bottom: 'top', left: 'right', right: 'left' }[anchor.side], align: anchor.align }; } function flipAlign(anchor) { return { side: anchor.side, align: { center: 'center', top: 'bottom', bottom: 'top', left: 'right', right: 'left' }[anchor.align] }; } function flipCorner(anchor) { return { side: anchor.align, align: anchor.side }; } function getAxis(anchor) { return includes(block, anchor.side) ? 'y' : 'x'; } class Box { constructor(_ref) { let { x, y, width, height } = _ref; this.x = x; this.y = y; this.width = width; this.height = height; } get top() { return this.y; } get bottom() { return this.y + this.height; } get left() { return this.x; } get right() { return this.x + this.width; } } function getOverflow(a, b) { return { x: { before: Math.max(0, b.left - a.left), after: Math.max(0, a.right - b.right) }, y: { before: Math.max(0, b.top - a.top), after: Math.max(0, a.bottom - b.bottom) } }; } /** @see https://stackoverflow.com/a/57876601/2074736 */ function nullifyTransforms(el) { const rect = el.getBoundingClientRect(); const style = getComputedStyle(el); const tx = style.transform; if (tx) { let ta, sx, sy, dx, dy; if (tx.startsWith('matrix3d(')) { ta = tx.slice(9, -1).split(/, /); sx = +ta[0]; sy = +ta[5]; dx = +ta[12]; dy = +ta[13]; } else if (tx.startsWith('matrix(')) { ta = tx.slice(7, -1).split(/, /); sx = +ta[0]; sy = +ta[3]; dx = +ta[4]; dy = +ta[5]; } else { return new Box(rect); } const to = style.transformOrigin; const x = rect.x - dx - (1 - sx) * parseFloat(to); const y = rect.y - dy - (1 - sy) * parseFloat(to.slice(to.indexOf(' ') + 1)); const w = sx ? rect.width / sx : el.offsetWidth + 1; const h = sy ? rect.height / sy : el.offsetHeight + 1; return new Box({ x, y, width: w, height: h }); } else { return new Box(rect); } } function animate(el, keyframes, options) { if (typeof el.animate === 'undefined') return { finished: Promise.resolve() }; const animation = el.animate(keyframes, options); if (typeof animation.finished === 'undefined') { animation.finished = new Promise(resolve => { animation.onfinish = () => { resolve(animation); }; }); } return animation; } /* eslint-disable no-console */ // import Vuetify from '../framework' function createMessage(message, vm, parent) { // if (Vuetify.config.silent) return if (parent) { vm = { __isVue: true, $parent: parent, $options: vm }; } if (vm) { // Only show each message once per instance vm.$_alreadyWarned = vm.$_alreadyWarned || []; if (vm.$_alreadyWarned.includes(message)) return; vm.$_alreadyWarned.push(message); } return `[Vuetify] ${message}` + (vm ? generateComponentTrace(vm) : ''); } function consoleWarn(message, vm, parent) { const newMessage = createMessage(message, vm, parent); newMessage != null && console.warn(newMessage); } function consoleError(message, vm, parent) { const newMessage = createMessage(message, vm, parent); newMessage != null && console.error(newMessage); } /** * Shamelessly stolen from vuejs/vue/blob/dev/src/core/util/debug.js */ const classifyRE = /(?:^|[-_])(\w)/g; const classify = str => str.replace(classifyRE, c => c.toUpperCase()).replace(/[-_]/g, ''); function formatComponentName(vm, includeFile) { if (vm.$root === vm) { return '<Root>'; } const options = typeof vm === 'function' && vm.cid != null ? vm.options : vm.__isVue ? vm.$options || vm.constructor.options : vm || {}; let name = options.name || options._componentTag; const file = options.__file; if (!name && file) { const match = file.match(/([^/\\]+)\.vue$/); name = match?.[1]; } return (name ? `<${classify(name)}>` : `<Anonymous>`) + (file && includeFile !== false ? ` at ${file}` : ''); } function generateComponentTrace(vm) { if (vm.__isVue && vm.$parent) { const tree = []; let currentRecursiveSequence = 0; while (vm) { if (tree.length > 0) { const last = tree[tree.length - 1]; if (last.constructor === vm.constructor) { currentRecursiveSequence++; vm = vm.$parent; continue; } else if (currentRecursiveSequence > 0) { tree[tree.length - 1] = [last, currentRecursiveSequence]; currentRecursiveSequence = 0; } } tree.push(vm); vm = vm.$parent; } return '\n\nfound in\n\n' + tree.map((vm, i) => `${i === 0 ? '---> ' : ' '.repeat(5 + i * 2)}${Array.isArray(vm) // eslint-disable-next-line sonarjs/no-nested-template-literals ? `${formatComponentName(vm[0])}... (${vm[1]} recursive calls)` : formatComponentName(vm)}`).join('\n'); } else { return `\n\n(found in ${formatComponentName(vm)})`; } } // For converting XYZ to sRGB const srgbForwardMatrix = [[3.2406, -1.5372, -0.4986], [-0.9689, 1.8758, 0.0415], [0.0557, -0.2040, 1.0570]]; // Forward gamma adjust const srgbForwardTransform = C => C <= 0.0031308 ? C * 12.92 : 1.055 * C ** (1 / 2.4) - 0.055; // For converting sRGB to XYZ const srgbReverseMatrix = [[0.4124, 0.3576, 0.1805], [0.2126, 0.7152, 0.0722], [0.0193, 0.1192, 0.9505]]; // Reverse gamma adjust const srgbReverseTransform = C => C <= 0.04045 ? C / 12.92 : ((C + 0.055) / 1.055) ** 2.4; function fromXYZ$1(xyz) { const rgb = Array(3); const transform = srgbForwardTransform; const matrix = srgbForwardMatrix; // Matrix transform, then gamma adjustment for (let i = 0; i < 3; ++i) { // Rescale back to [0, 255] rgb[i] = Math.round(clamp(transform(matrix[i][0] * xyz[0] + matrix[i][1] * xyz[1] + matrix[i][2] * xyz[2])) * 255); } return { r: rgb[0], g: rgb[1], b: rgb[2] }; } function toXYZ$1(_ref) { let { r, g, b } = _ref; const xyz = [0, 0, 0]; const transform = srgbReverseTransform; const matrix = srgbReverseMatrix; // Rescale from [0, 255] to [0, 1] then adjust sRGB gamma to linear RGB r = transform(r / 255); g = transform(g / 255); b = transform(b / 255); // Matrix color space transform for (let i = 0; i < 3; ++i) { xyz[i] = matrix[i][0] * r + matrix[i][1] * g + matrix[i][2] * b; } return xyz; } const delta = 0.20689655172413793; // 6÷29 const cielabForwardTransform = t => t > delta ** 3 ? Math.cbrt(t) : t / (3 * delta ** 2) + 4 / 29; const cielabReverseTransform = t => t > delta ? t ** 3 : 3 * delta ** 2 * (t - 4 / 29); function fromXYZ(xyz) { const transform = cielabForwardTransform; const transformedY = transform(xyz[1]); return [116 * transformedY - 16, 500 * (transform(xyz[0] / 0.95047) - transformedY), 200 * (transformedY - transform(xyz[2] / 1.08883))]; } function toXYZ(lab) { const transform = cielabReverseTransform; const Ln = (lab[0] + 16) / 116; return [transform(Ln + lab[1] / 500) * 0.95047, transform(Ln), transform(Ln - lab[2] / 200) * 1.08883]; } // Utilities // Types function isCssColor(color) { return !!color && /^(#|var\(--|(rgb|hsl)a?\()/.test(color); } function parseColor$1(color) { if (typeof color === 'number') { if (isNaN(color) || color < 0 || color > 0xFFFFFF) { // int can't have opacity consoleWarn(`'${color}' is not a valid hex color`); } return { r: (color & 0xFF0000) >> 16, g: (color & 0xFF00) >> 8, b: color & 0xFF }; } else if (typeof color === 'string') { let hex = color.startsWith('#') ? color.slice(1) : color; if ([3, 4].includes(hex.length)) { hex = hex.split('').map(char => char + char).join(''); } else if (![6, 8].includes(hex.length)) { consoleWarn(`'${color}' is not a valid hex(a) color`); } const int = parseInt(hex, 16); if (isNaN(int) || int < 0 || int > 0xFFFFFFFF) { consoleWarn(`'${color}' is not a valid hex(a) color`); } return HexToRGB(hex); } else { throw new TypeError(`Colors can only be numbers or strings, recieved ${color == null ? color : color.constructor.name} instead`); } } /** Converts HSVA to RGBA. Based on formula from https://en.wikipedia.org/wiki/HSL_and_HSV */ function HSVtoRGB(hsva) { const { h, s, v, a } = hsva; const f = n => { const k = (n + h / 60) % 6; return v - v * s * Math.max(Math.min(k, 4 - k, 1), 0); }; const rgb = [f(5), f(3), f(1)].map(v => Math.round(v * 255)); return { r: rgb[0], g: rgb[1], b: rgb[2], a }; } /** Converts RGBA to HSVA. Based on formula from https://en.wikipedia.org/wiki/HSL_and_HSV */ function RGBtoHSV(rgba) { if (!rgba) return { h: 0, s: 1, v: 1, a: 1 }; const r = rgba.r / 255; const g = rgba.g / 255; const b = rgba.b / 255; const max = Math.max(r, g, b); const min = Math.min(r, g, b); let h = 0; if (max !== min) { if (max === r) { h = 60 * (0 + (g - b) / (max - min)); } else if (max === g) { h = 60 * (2 + (b - r) / (max - min)); } else if (max === b) { h = 60 * (4 + (r - g) / (max - min)); } } if (h < 0) h = h + 360; const s = max === 0 ? 0 : (max - min) / max; const hsv = [h, s, max]; return { h: hsv[0], s: hsv[1], v: hsv[2], a: rgba.a }; } function HSVtoHSL(hsva) { const { h, s, v, a } = hsva; const l = v - v * s / 2; const sprime = l === 1 || l === 0 ? 0 : (v - l) / Math.min(l, 1 - l); return { h, s: sprime, l, a }; } function HSLtoHSV(hsl) { const { h, s, l, a } = hsl; const v = l + s * Math.min(l, 1 - l); const sprime = v === 0 ? 0 : 2 - 2 * l / v; return { h, s: sprime, v, a }; } function RGBtoCSS(_ref) { let { r, g, b, a } = _ref; return a === undefined ? `rgb(${r}, ${g}, ${b})` : `rgba(${r}, ${g}, ${b}, ${a})`; } function HSVtoCSS(hsva) { return RGBtoCSS(HSVtoRGB(hsva)); } function toHex(v) { const h = Math.round(v).toString(16); return ('00'.substr(0, 2 - h.length) + h).toUpperCase(); } function RGBtoHex(_ref2) { let { r, g, b, a } = _ref2; return `#${[toHex(r), toHex(g), toHex(b), a !== undefined ? toHex(Math.round(a * 255)) : ''].join('')}`; } function HexToRGB(hex) { hex = parseHex(hex); let [r, g, b, a] = chunk(hex, 2).map(c => parseInt(c, 16)); a = a === undefined ? a : a / 255; return { r, g, b, a }; } function HexToHSV(hex) { const rgb = HexToRGB(hex); return RGBtoHSV(rgb); } function HSVtoHex(hsva) { return RGBtoHex(HSVtoRGB(hsva)); } function parseHex(hex) { if (hex.startsWith('#')) { hex = hex.slice(1); } hex = hex.replace(/([^0-9a-f])/gi, 'F'); if (hex.length === 3 || hex.length === 4) { hex = hex.split('').map(x => x + x).join(''); } if (hex.length !== 6) { hex = padEnd(padEnd(hex, 6), 8, 'F'); } return hex; } function lighten(value, amount) { const lab = fromXYZ(toXYZ$1(value)); lab[0] = lab[0] + amount * 10; return fromXYZ$1(toXYZ(lab)); } function darken(value, amount) { const lab = fromXYZ(toXYZ$1(value)); lab[0] = lab[0] - amount * 10; return fromXYZ$1(toXYZ(lab)); } /** * Calculate the relative luminance of a given color * @see https://www.w3.org/TR/WCAG20/#relativeluminancedef */ function getLuma(color) { const rgb = parseColor$1(color); return toXYZ$1(rgb)[1]; } /** * Returns the contrast ratio (1-21) between two colors. * @see https://www.w3.org/TR/WCAG20/#contrast-ratiodef */ function getContrast(first, second) { const l1 = getLuma(first); const l2 = getLuma(second); const light = Math.max(l1, l2); const dark = Math.min(l1, l2); return (light + 0.05) / (dark + 0.05); } // Utilities function getCurrentInstance(name, message) { const vm = getCurrentInstance$1(); if (!vm) { throw new Error(`[Vuetify] ${name} ${message || 'must be called from inside a setup function'}`); } return vm; } function getCurrentInstanceName() { let name = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : 'composables'; const vm = getCurrentInstance(name).type; return toKebabCase(vm?.aliasName || vm?.name); } let _uid = 0; let _map = new WeakMap(); function getUid() { const vm = getCurrentInstance('getUid'); if (_map.has(vm)) return _map.get(vm);else { const uid = _uid++; _map.set(vm, uid); return uid; } } getUid.reset = () => { _uid = 0; _map = new WeakMap(); }; function injectSelf(key) { const { provides } = getCurrentInstance('injectSelf'); if (provides && key in provides) { // TS doesn't allow symbol as index type return provides[key]; } } /** * Creates a factory function for props definitions. * This is used to define props in a composable then override * default values in an implementing component. * * @example Simplified signature * (props: Props) => (defaults?: Record<keyof props, any>) => Props * * @example Usage * const makeProps = propsFactory({ * foo: String, * }) * * defineComponent({ * props: { * ...makeProps({ * foo: 'a', * }), * }, * setup (props) { * // would be "string | undefined", now "string" because a default has been provided * props.foo * }, * } */ function propsFactory(props, source) { return defaults => { return Object.keys(props).reduce((obj, prop) => { const isObjectDefinition = typeof props[prop] === 'object' && props[prop] != null && !Array.isArray(props[prop]); const definition = isObjectDefinition ? props[prop] : { type: props[prop] }; if (defaults && prop in defaults) { obj[prop] = { ...definition, default: defaults[prop] }; } else { obj[prop] = definition; } if (source && !obj[prop].source) { obj[prop].source = source; } return obj; }, {}); }; } // Utilities // Types const DefaultsSymbol = Symbol.for('vuetify:defaults'); function createDefaults(options) { return ref(options); } function useDefaults() { const defaults = inject$1(DefaultsSymbol); if (!defaults) throw new Error('[Vuetify] Could not find defaults instance'); return defaults; } function provideDefaults(defaults, options) { const injectedDefaults = useDefaults(); const providedDefaults = ref(defaults); const newDefaults = computed(() => { const scoped = unref(options?.scoped); const reset = unref(options?.reset); const root = unref(options?.root); let properties = mergeDeep(providedDefaults.value, { prev: injectedDefaults.value }); if (scoped) return properties; if (reset || root) { const len = Number(reset || Infinity); for (let i = 0; i <= len; i++) { if (!properties || !('prev' in properties)) { break; } properties = properties.prev; } return properties; } return mergeDeep(properties.prev, properties); }); provide(DefaultsSymbol, newDefaults); return newDefaults; } function useToggleScope(source, fn) { let scope; function start() { scope = effectScope(); scope.run(() => fn.length ? fn(() => { scope?.stop(); start(); }) : fn()); } watch(source, active => { if (active && !scope) { start(); } else if (!active) { scope?.stop(); scope = undefined; } }, { immediate: true }); onScopeDispose(() => { scope?.stop(); }); } // Utils // Types function propIsDefined(vnode, prop) { return typeof vnode.props?.[prop] !== 'undefined' || typeof vnode.props?.[toKebabCase(prop)] !== 'undefined'; } const defineComponent = function defineComponent(options) { options._setup = options._setup ?? options.setup; if (!options.name) { consoleWarn('The component is missing an explicit name, unable to generate default prop value'); return options; } if (options._setup) { options.props = options.props ?? {}; options.props = propsFactory(options.props, toKebabCase(options.name))(); options.props._as = String; options.setup = function setup(props, ctx) { const defaults = useDefaults(); // Skip props proxy if defaults are not provided if (!defaults.value) return options._setup(props, ctx); const vm = getCurrentInstance$1(); const componentDefaults = computed(() => defaults.value[props._as ?? options.name]); const _props = new Proxy(props, { get(target, prop) { if (typeof prop === 'string' && !propIsDefined(vm.vnode, prop)) { return componentDefaults.value?.[prop] ?? defaults.value.global?.[prop] ?? target[prop]; } return Reflect.get(target, prop); } }); const _subcomponentDefaults = shallowRef(); watchEffect(() => { if (componentDefaults.value) { const subComponents = Object.entries(componentDefaults.value).filter(_ref => { let [key] = _ref; return key.startsWith(key[0].toUpperCase()); }); if (subComponents.length) _subcomponentDefaults.value = Object.fromEntries(subComponents); } }); const setupBindings = options._setup(_props, ctx); // If subcomponent defaults are provided, override any // subcomponents provided by the component's setup function. // This uses injectSelf so must be done after the original setup to work. useToggleScope(_subcomponentDefaults, () => { provideDefaults(mergeDeep(injectSelf(DefaultsSymbol)?.value ?? {}, _subcomponentDefaults.value)); }); return setupBindings; }; } return options; }; // Implementation function genericComponent() { let exposeDefaults = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : true; return options => (exposeDefaults ? defineComponent : defineComponent$1)(options); } function defineFunctionalComponent(props, render) { render.props = props; return render; } function createSimpleFunctional(klass) { let tag = arguments.length > 1 && arguments[1] !== undefined ? arguments[1] : 'div'; let name = arguments.length > 2 ? arguments[2] : undefined; return genericComponent()({ name: name ?? capitalize(camelize(klass.replace(/__/g, '-'))), props: { tag: { type: String, default: tag } }, setup(props, _ref) { let { slots } = _ref; return () => h(props.tag, { class: klass }, slots.default?.()); } }); } /** * Returns: * - 'null' if the node is not attached to the DOM * - the root node (HTMLDocument | ShadowRoot) otherwise */ function attachedRoot(node) { /* istanbul ignore next */ if (typeof node.getRootNode !== 'function') { // Shadow DOM not supported (IE11), lets find the root of this node while (node.parentNode) node = node.parentNode; // The root parent is the document if the node is attached to the DOM if (node !== document) return null; return document; } const root = node.getRootNode(); // The composed root node is the document if the node is attached to the DOM if (root !== document && root.getRootNode({ composed: true }) !== document) return null; return root; } const standardEasing = 'cubic-bezier(0.4, 0, 0.2, 1)'; const deceleratedEasing = 'cubic-bezier(0.0, 0, 0.2, 1)'; // Entering const acceleratedEasing = 'cubic-bezier(0.4, 0, 1, 1)'; // Leaving function getScrollParent(el) { while (el) { if (hasScrollbar(el)) return el; el = el.parentElement; } return document.scrollingElement; } function getScrollParents(el, stopAt) { const elements = []; if (stopAt && el && !stopAt.contains(el)) return elements; while (el) { if (hasScrollbar(el)) elements.push(el); if (el === stopAt) break; el = el.parentElement; } return elements; } function hasScrollbar(el) { if (!el || el.nodeType !== Node.ELEMENT_NODE) return false; const style = window.getComputedStyle(el); return style.overflowY === 'scroll' || style.overflowY === 'auto' && el.scrollHeight > el.clientHeight; } function isFixedPosition(el) { while (el) { if (window.getComputedStyle(el).position === 'fixed') { return true; } el = el.offsetParent; } return false; } // Utilities // Types function useRender(render) { const vm = getCurrentInstance('useRender'); vm.render = render; } // Composables // Types const VuetifyLayoutKey = Symbol.for('vuetify:layout'); const VuetifyLayoutItemKey = Symbol.for('vuetify:layout-item'); const ROOT_ZINDEX = 1000; const makeLayoutProps = propsFactory({ overlaps: { type: Array, default: () => [] }, fullHeight: Boolean }, 'layout'); // Composables const makeLayoutItemProps = propsFactory({ name: { type: String }, order: { type: [Number, String], default: 0 }, absolute: Boolean }, 'layout-item'); function useLayout() { const layout = inject$1(VuetifyLayoutKey); if (!layout) throw new Error('[Vuetify] Could not find injected layout'); return { getLayoutItem: layout.getLayoutItem, mainRect: layout.mainRect, mainStyles: layout.mainStyles }; } function useLayoutItem(options) { const layout = inject$1(VuetifyLayoutKey); if (!layout) throw new Error('[Vuetify] Could not find injected layout'); const id = options.id ?? `layout-item-${getUid()}`; const vm = getCurrentInstance('useLayoutItem'); provide(VuetifyLayoutItemKey, { id }); const isKeptAlive = ref(false); onDeactivated(() => isKeptAlive.value = true); onActivated(() => isKeptAlive.value = false); const { layoutItemStyles, layoutItemScrimStyles } = layout.register(vm, { ...options, active: computed(() => isKeptAlive.value ? false : options.active.value), id }); onBeforeUnmount(() => layout.unregister(id)); return { layoutItemStyles, layoutRect: layout.layoutRect, layoutItemScrimStyles }; } const generateLayers = (layout, positions, layoutSizes, activeItems) => { let previousLayer = { top: 0, left: 0, right: 0, bottom: 0 }; const layers = [{ id: '', layer: { ...previousLayer } }]; for (const id of layout) { const position = positions.get(id); const amount = layoutSizes.get(id); const active = activeItems.get(id); if (!position || !amount || !active) continue; const layer = { ...previousLayer, [position.value]: parseInt(previousLayer[position.value], 10) + (active.value ? parseInt(amount.value, 10) : 0) }; layers.push({ id, layer }); previousLayer = layer; } return layers; }; function createLayout(props) { const parentLayout = inject$1(VuetifyLayoutKey, null); const rootZIndex = computed(() => parentLayout ? parentLayout.rootZIndex.value - 100 : ROOT_ZINDEX); const registered = ref([]); const positions = reactive(new Map()); const layoutSizes = reactive(new Map()); const priorities = reactive(new Map()); const activeItems = reactive(new Map()); const disabledTransitions = reactive(new Map()); const { resizeRef, contentRect: layoutRect } = useResizeObserver(); const computedOverlaps = computed(() => { const map = new Map(); const overlaps = props.overlaps ?? []; for (const overlap of overlaps.filter(item => item.includes(':'))) { const [top, bottom] = overlap.split(':'); if (!registered.value.includes(top) || !registered.value.includes(bottom)) continue; const topPosition = positions.get(top); const bottomPosition = positions.get(bottom); const topAmount = layoutSizes.get(top); const bottomAmount = layoutSizes.get(bottom); if (!topPosition || !bottomPosition || !topAmount || !bottomAmount) continue; map.set(bottom, { position: topPosition.value, amount: parseInt(topAmount.value, 10) }); map.set(top, { position: bottomPosition.value, amount: -parseInt(bottomAmount.value, 10) }); } return map; }); const layers = computed(() => { const uniquePriorities = [...new Set([...priorities.values()].map(p => p.value))].sort((a, b) => a - b); const layout = []; for (const p of uniquePriorities) { const items = registered.value.filter(id => priorities.get(id)?.value === p); layout.push(...items); } return generateLayers(layout, positions, layoutSizes, activeItems); }); const transitionsEnabled = computed(() => { return !Array.from(disabledTransitions.values()).some(ref => ref.value); }); const mainRect = computed(() => { return layers.value[layers.value.length - 1].layer; }); const mainStyles = computed(() => { return { '--v-layout-left': convertToUnit(mainRect.value.left), '--v-layout-right': convertToUnit(mainRect.value.right), '--v-layout-top': convertToUnit(mainRect.value.top), '--v-layout-bottom': convertToUnit(mainRect.value.bottom), ...(transitionsEnabled.value ? undefined : { transition: 'none' }) }; }); const items = computed(() => { return layers.value.slice(1).map((_ref, index) => { let { id } = _ref; const { layer } = layers.value[index]; const size = layoutSizes.get(id); const position = positions.get(id); return { id, ...layer, size: Number(size.value), position: position.value }; }); }); const getLayoutItem = id => { return items.value.find(item => item.id === id); }; const rootVm = getCurrentInstance('createLayout'); const isMounted = ref(false); onMounted(() => { isMounted.value = true; }); provide(VuetifyLayoutKey, { register: (vm, _ref2) => { let { id, order, position, layoutSize, elementSize, active, disableTransitions, absolute } = _ref2; priorities.set(id, order); positions.set(id, position); layoutSizes.set(id, layoutSize); activeItems.set(id, active); disableTransitions && disabledTransitions.set(id, disableTransitions); const instances = findChildrenWithProvide(VuetifyLayoutItemKey, rootVm?.vnode); const instanceIndex = instances.indexOf(vm); if (instanceIndex > -1) registered.value.splice(instanceIndex, 0, id);else registered.value.push(id); const index = computed(() => items.value.findIndex(i => i.id === id)); const zIndex = computed(() => rootZIndex.value + layers.value.length * 2 - index.value * 2); const layoutItemStyles = computed(() => { const isHorizontal = position.value === 'left' || position.value === 'right'; const isOppositeHorizontal = position.value === 'right'; const isOppositeVertical = position.value === 'bottom'; const styles = { [position.value]: 0, zIndex: zIndex.value, transform: `translate${isHorizontal ? 'X' : 'Y'}(${(active.value ? 0 : -110) * (isOppositeHorizontal || isOppositeVertical ? -1 : 1)}%)`, position: absolute.value || rootZIndex.value !== ROOT_ZINDEX ? 'absolute' : 'fixed', ...(transitionsEnabled.value ? undefined : { transition: 'none' }) }; if (!isMounted.value) return styles; const item = items.value[index.value]; if (!item) throw new Error(`[Vuetify] Could not find layout item "${id}"`); const overlap = computedOverlaps.value.get(id); if (overlap) { item[overlap.position] += overlap.amount; } return { ...styles, height: isHorizontal ? `calc(100% - ${item.top}px - ${item.bottom}px)` : elementSize.value ? `${elementSize.value}px` : undefined, left: isOppositeHorizontal ? undefined : `${item.left}px`, right: isOppositeHorizontal ? `${item.right}px` : undefined, top: position.value !== 'bottom' ? `${item.top}px` : undefined, bottom: position.value !== 'top' ? `${item.bottom}px` : undefined, width: !isHorizontal ? `calc(100% - ${item.left}px - ${item.right}px)` : elementSize.value ? `${elementSize.value}px` : undefined }; }); const layoutItemScrimStyles = computed(() => ({ zIndex: zIndex.value - 1 })); return { layoutItemStyles, layoutItemScrimStyles, zIndex }; }, unregister: id => { priorities.delete(id); positions.delete(id); layoutSizes.delete(id); activeItems.delete(id); disabledTransitions.delete(id); registered.value = registered.value.filter(v => v !== id); }, mainRect, mainStyles, getLayoutItem, items, layoutRect, rootZIndex }); const layoutClasses = computed(() => ['v-layout', { 'v-layout--full-height': props.fullHeight }]); const layoutStyles = computed(() => ({ zIndex: rootZIndex.value, position: parentLayout ? 'relative' : undefined, overflow: parentLayout ? 'hidden' : undefined })); return { layoutClasses, layoutStyles, getLayoutItem, items, layoutRect, layoutRef: resizeRef }; } /** * WCAG 3.0 APCA perceptual contrast algorithm from https://github.com/Myndex/SAPC-APCA * @licence https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document * @see https://www.w3.org/WAI/GL/task-forces/silver/wiki/Visual_Contrast_of_Text_Subgroup */ // MAGICAL NUMBERS // sRGB Conversion to Relative Luminance (Y) // Transfer Curve (aka "Gamma") for sRGB linearization // Simple power curve vs piecewise described in docs // Essentially, 2.4 best models actual display // characteristics in combination with the total method const mainTRC = 2.4; const Rco = 0.2126729; // sRGB Red Coefficient (from matrix) const Gco = 0.7151522; // sRGB Green Coefficient (from matrix) const Bco = 0.0721750; // sRGB Blue Coefficient (from matrix) // For Finding Raw SAPC Contrast from Relative Luminance (Y) // Constants for SAPC Power Curve Exponents // One pair for normal text, and one for reverse // These are the "beating heart" of SAPC const normBG = 0.55; const normTXT = 0.58; const revTXT = 0.57; const revBG = 0.62; // For Clamping and Scaling Values const blkThrs = 0.03; // Level that triggers the soft black clamp const blkClmp = 1.45; // Exponent for the soft black clamp curve const deltaYmin = 0.0005; // Lint trap const scaleBoW = 1.25; // Scaling for dark text on light const scaleWoB = 1.25; // Scaling for light text on dark const loConThresh = 0.078; // Threshold for new simple offset scale const loConFactor = 12.82051282051282; // = 1/0.078, const loConOffset = 0.06; // The simple offset const loClip = 0.001; // Output clip (lint trap #2) function APCAcontrast(text, background) { // Linearize sRGB const Rtxt = (text.r / 255) ** mainTRC; const Gtxt = (text.g / 255) ** mainTRC; const Btxt = (text.b / 255) ** mainTRC; const Rbg = (background.r / 255) ** mainTRC; const Gbg = (background.g / 255) ** mainTRC; const Bbg = (background.b / 255) ** mainTRC; // Apply the standard coefficients and sum to Y let Ytxt = Rtxt * Rco + Gtxt * Gco + Btxt * Bco; let Ybg = Rbg * Rco + Gbg * Gco + Bbg * Bco; // Soft clamp Y when near black. // Now clamping all colors to prevent crossover errors if (Ytxt <= blkThrs) Ytxt += (blkThrs - Ytxt) ** blkClmp; if (Ybg <= blkThrs) Ybg += (blkThrs - Ybg) ** blkClmp; // Return 0 Early for extremely low ∆Y (lint trap #1) if (Math.abs(Ybg - Ytxt) < deltaYmin) return 0.0; // SAPC CONTRAST let outputContrast; // For weighted final values if (Ybg > Ytxt) { // For normal polarity, black text on white // Calculate the SAPC contrast value and scale const SAPC = (Ybg ** normBG - Ytxt ** normTXT) * scaleBoW; // NEW! SAPC SmoothScale™ // Low Contrast Smooth Scale Rollout to prevent polarity reversal // and also a low clip for very low contrasts (lint trap #2) // much of this is for very low contrasts, less than 10 // therefore for most reversing needs, only loConOffset is important outputContrast = SAPC < loClip ? 0.0 : SAPC < loConThresh ? SAPC - SAPC * loConFactor * loConOffset : SAPC - loConOffset; } else { // For reverse polarity, light text on dark // WoB should always return negative value. const SAPC = (Ybg ** revBG - Ytxt ** revTXT) * scaleWoB; outputContrast = SAPC > -loClip ? 0.0 : SAPC > -loConThresh ? SAPC - SAPC * loConFactor * loConOffset : SAPC + loConOffset; } return outputContrast * 100; } // Utilities // Types const ThemeSymbol = Symbol.for('vuetify:theme'); const makeThemeProps = propsFactory({ theme: String }, 'theme'); const defaultThemeOptions = { defaultTheme: 'light', variations: { colors: [], lighten: 0, darken: 0 }, themes: { light: { dark: false, colors: { background: '#FFFFFF', surface: '#FFFFFF', 'surface-variant': '#424242', 'on-surface-variant': '#EEEEEE', primary: '#6200EE', 'primary-darken-1': '#3700B3', secondary: '#03DAC6', 'secondary-darken-1': '#018786', error: '#B00020', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00' }, variables: { 'border-color': '#000000', 'border-opacity': 0.12, 'high-emphasis-opacity': 0.87, 'medium-emphasis-opacity': 0.60, 'disabled-opacity': 0.38, 'idle-opacity': 0.04, 'hover-opacity': 0.04, 'focus-opacity': 0.12, 'selected-opacity': 0.08, 'activated-opacity': 0.12, 'pressed-opacity': 0.12, 'dragged-opacity': 0.08, 'theme-kbd': '#212529', 'theme-on-kbd': '#FFFFFF', 'theme-code': '#F5F5F5', 'theme-on-code': '#000000' } }, dark: { dark: true, colors: { background: '#121212', surface: '#212121', 'surface-variant': '#BDBDBD', 'on-surface-variant': '#424242', primary: '#BB86FC', 'primary-darken-1': '#3700B3', secondary: '#03DAC5', 'secondary-darken-1': '#03DAC5', error: '#CF6679', info: '#2196F3', success: '#4CAF50', warning: '#FB8C00' }, variables: { 'border-color': '#FFFFFF', 'border-opacity': 0.12, 'high-emphasis-opacity': 0.87, 'medium-emphasis-opacity': 0.60, 'disabled-opacity': 0.38, 'idle-opacity': 0.10, 'hover-opacity': 0.04, 'focus-opacity': 0.12, 'selected-opacity': 0.08, 'activated-opacity': 0.12, 'pressed-opacity': 0.16, 'dragged-opacity': 0.08, 'theme-kbd': '#212529', 'theme-on-kbd': '#FFFFFF', 'theme-code': '#343434', 'theme-on-code': '#CCCCCC' } } } }; function parseThemeOptions() { let options = arguments.length > 0 && arguments[0] !== undefined ? arguments[0] : defaultThemeOptions; if (!options) return { ...defaultThemeOptions, isDisabled: true }; const themes = {}; for (const [key, theme] of Object.entries(options.themes ?? {})) { const defaultTheme = theme.dark || key === 'dark' ? defaultThemeOptions.themes?.dark : defaultThemeOptions.themes?.light; themes[key] = mergeDeep(defaultTheme, theme); } return mergeDeep(defaultThemeOptions, { ...options, themes }); } // Composables function createTheme(options) { const parsedOptions = reactive(parseThemeOptions(options)); const name = ref(parsedOptions.defaultTheme); const themes = ref(parsedOptions.themes); const computedThemes = computed(() => { const acc = {}; for (const [name, original] of Object.entries(themes.value)) { const theme = acc[name] = { ...original, colors: { ...original.colors } }; if (parsedOptions.variations) { for (const name of parsedOptions.variations.colors) { const color = theme.colors[name]; if (!color) continue; for (const variation of ['lighten', 'darken']) { const fn = variation === 'lighten' ? lighten : darken; for (const amount of createRange(parsedOptions.variations[variation], 1)) { theme.colors[`${name}-${variation}-${amount}`] = RGBtoHex(fn(parseColor$1(color), amount)); } } } } for (const color of Object.keys(theme.colors)) { if (/^on-[a-z]/.test(color) || theme.colors[`on-${color}`]) continue; const onColor = `on-${color}`; const colorVal = parseColor$1(theme.colors[color]); const blackContrast = Math.abs(APCAcontrast(parseColor$1(0), colorVal)); const whiteContrast = Math.abs(APCAcontrast(parseColor$1(0xffffff), colorVal));