You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1698 lines
61 KiB
1698 lines
61 KiB
import { camelize, warn, callWithAsyncErrorHandling, defineComponent, nextTick, createVNode, getCurrentInstance, watchPostEffect, onMounted, onUnmounted, Fragment, Static, h, BaseTransition, useTransitionState, onUpdated, toRaw, getTransitionRawChildren, setTransitionHooks, resolveTransitionHooks, createRenderer, isRuntimeOnly, createHydrationRenderer } from '@vue/runtime-core'; |
|
export * from '@vue/runtime-core'; |
|
import { isString, isArray, hyphenate, capitalize, isSpecialBooleanAttr, includeBooleanAttr, isOn, isModelListener, isFunction, toNumber, camelize as camelize$1, extend, EMPTY_OBJ, isObject, invokeArrayFns, looseIndexOf, isSet, looseEqual, isHTMLTag, isSVGTag } from '@vue/shared'; |
|
|
|
const svgNS = 'http://www.w3.org/2000/svg'; |
|
const doc = (typeof document !== 'undefined' ? document : null); |
|
const templateContainer = doc && /*#__PURE__*/ doc.createElement('template'); |
|
const nodeOps = { |
|
insert: (child, parent, anchor) => { |
|
parent.insertBefore(child, anchor || null); |
|
}, |
|
remove: child => { |
|
const parent = child.parentNode; |
|
if (parent) { |
|
parent.removeChild(child); |
|
} |
|
}, |
|
createElement: (tag, isSVG, is, props) => { |
|
const el = isSVG |
|
? doc.createElementNS(svgNS, tag) |
|
: doc.createElement(tag, is ? { is } : undefined); |
|
if (tag === 'select' && props && props.multiple != null) { |
|
el.setAttribute('multiple', props.multiple); |
|
} |
|
return el; |
|
}, |
|
createText: text => doc.createTextNode(text), |
|
createComment: text => doc.createComment(text), |
|
setText: (node, text) => { |
|
node.nodeValue = text; |
|
}, |
|
setElementText: (el, text) => { |
|
el.textContent = text; |
|
}, |
|
parentNode: node => node.parentNode, |
|
nextSibling: node => node.nextSibling, |
|
querySelector: selector => doc.querySelector(selector), |
|
setScopeId(el, id) { |
|
el.setAttribute(id, ''); |
|
}, |
|
cloneNode(el) { |
|
const cloned = el.cloneNode(true); |
|
// #3072 |
|
// - in `patchDOMProp`, we store the actual value in the `el._value` property. |
|
// - normally, elements using `:value` bindings will not be hoisted, but if |
|
// the bound value is a constant, e.g. `:value="true"` - they do get |
|
// hoisted. |
|
// - in production, hoisted nodes are cloned when subsequent inserts, but |
|
// cloneNode() does not copy the custom property we attached. |
|
// - This may need to account for other custom DOM properties we attach to |
|
// elements in addition to `_value` in the future. |
|
if (`_value` in el) { |
|
cloned._value = el._value; |
|
} |
|
return cloned; |
|
}, |
|
// __UNSAFE__ |
|
// Reason: innerHTML. |
|
// Static content here can only come from compiled templates. |
|
// As long as the user only uses trusted templates, this is safe. |
|
insertStaticContent(content, parent, anchor, isSVG, start, end) { |
|
// <parent> before | first ... last | anchor </parent> |
|
const before = anchor ? anchor.previousSibling : parent.lastChild; |
|
// #5308 can only take cached path if: |
|
// - has a single root node |
|
// - nextSibling info is still available |
|
if (start && (start === end || start.nextSibling)) { |
|
// cached |
|
while (true) { |
|
parent.insertBefore(start.cloneNode(true), anchor); |
|
if (start === end || !(start = start.nextSibling)) |
|
break; |
|
} |
|
} |
|
else { |
|
// fresh insert |
|
templateContainer.innerHTML = isSVG ? `<svg>${content}</svg>` : content; |
|
const template = templateContainer.content; |
|
if (isSVG) { |
|
// remove outer svg wrapper |
|
const wrapper = template.firstChild; |
|
while (wrapper.firstChild) { |
|
template.appendChild(wrapper.firstChild); |
|
} |
|
template.removeChild(wrapper); |
|
} |
|
parent.insertBefore(template, anchor); |
|
} |
|
return [ |
|
// first |
|
before ? before.nextSibling : parent.firstChild, |
|
// last |
|
anchor ? anchor.previousSibling : parent.lastChild |
|
]; |
|
} |
|
}; |
|
|
|
// compiler should normalize class + :class bindings on the same element |
|
// into a single binding ['staticClass', dynamic] |
|
function patchClass(el, value, isSVG) { |
|
// directly setting className should be faster than setAttribute in theory |
|
// if this is an element during a transition, take the temporary transition |
|
// classes into account. |
|
const transitionClasses = el._vtc; |
|
if (transitionClasses) { |
|
value = (value ? [value, ...transitionClasses] : [...transitionClasses]).join(' '); |
|
} |
|
if (value == null) { |
|
el.removeAttribute('class'); |
|
} |
|
else if (isSVG) { |
|
el.setAttribute('class', value); |
|
} |
|
else { |
|
el.className = value; |
|
} |
|
} |
|
|
|
function patchStyle(el, prev, next) { |
|
const style = el.style; |
|
const isCssString = isString(next); |
|
if (next && !isCssString) { |
|
for (const key in next) { |
|
setStyle(style, key, next[key]); |
|
} |
|
if (prev && !isString(prev)) { |
|
for (const key in prev) { |
|
if (next[key] == null) { |
|
setStyle(style, key, ''); |
|
} |
|
} |
|
} |
|
} |
|
else { |
|
const currentDisplay = style.display; |
|
if (isCssString) { |
|
if (prev !== next) { |
|
style.cssText = next; |
|
} |
|
} |
|
else if (prev) { |
|
el.removeAttribute('style'); |
|
} |
|
// indicates that the `display` of the element is controlled by `v-show`, |
|
// so we always keep the current `display` value regardless of the `style` |
|
// value, thus handing over control to `v-show`. |
|
if ('_vod' in el) { |
|
style.display = currentDisplay; |
|
} |
|
} |
|
} |
|
const importantRE = /\s*!important$/; |
|
function setStyle(style, name, val) { |
|
if (isArray(val)) { |
|
val.forEach(v => setStyle(style, name, v)); |
|
} |
|
else { |
|
if (val == null) |
|
val = ''; |
|
if (name.startsWith('--')) { |
|
// custom property definition |
|
style.setProperty(name, val); |
|
} |
|
else { |
|
const prefixed = autoPrefix(style, name); |
|
if (importantRE.test(val)) { |
|
// !important |
|
style.setProperty(hyphenate(prefixed), val.replace(importantRE, ''), 'important'); |
|
} |
|
else { |
|
style[prefixed] = val; |
|
} |
|
} |
|
} |
|
} |
|
const prefixes = ['Webkit', 'Moz', 'ms']; |
|
const prefixCache = {}; |
|
function autoPrefix(style, rawName) { |
|
const cached = prefixCache[rawName]; |
|
if (cached) { |
|
return cached; |
|
} |
|
let name = camelize(rawName); |
|
if (name !== 'filter' && name in style) { |
|
return (prefixCache[rawName] = name); |
|
} |
|
name = capitalize(name); |
|
for (let i = 0; i < prefixes.length; i++) { |
|
const prefixed = prefixes[i] + name; |
|
if (prefixed in style) { |
|
return (prefixCache[rawName] = prefixed); |
|
} |
|
} |
|
return rawName; |
|
} |
|
|
|
const xlinkNS = 'http://www.w3.org/1999/xlink'; |
|
function patchAttr(el, key, value, isSVG, instance) { |
|
if (isSVG && key.startsWith('xlink:')) { |
|
if (value == null) { |
|
el.removeAttributeNS(xlinkNS, key.slice(6, key.length)); |
|
} |
|
else { |
|
el.setAttributeNS(xlinkNS, key, value); |
|
} |
|
} |
|
else { |
|
// note we are only checking boolean attributes that don't have a |
|
// corresponding dom prop of the same name here. |
|
const isBoolean = isSpecialBooleanAttr(key); |
|
if (value == null || (isBoolean && !includeBooleanAttr(value))) { |
|
el.removeAttribute(key); |
|
} |
|
else { |
|
el.setAttribute(key, isBoolean ? '' : value); |
|
} |
|
} |
|
} |
|
|
|
// __UNSAFE__ |
|
// functions. The user is responsible for using them with only trusted content. |
|
function patchDOMProp(el, key, value, |
|
// the following args are passed only due to potential innerHTML/textContent |
|
// overriding existing VNodes, in which case the old tree must be properly |
|
// unmounted. |
|
prevChildren, parentComponent, parentSuspense, unmountChildren) { |
|
if (key === 'innerHTML' || key === 'textContent') { |
|
if (prevChildren) { |
|
unmountChildren(prevChildren, parentComponent, parentSuspense); |
|
} |
|
el[key] = value == null ? '' : value; |
|
return; |
|
} |
|
if (key === 'value' && |
|
el.tagName !== 'PROGRESS' && |
|
// custom elements may use _value internally |
|
!el.tagName.includes('-')) { |
|
// store value as _value as well since |
|
// non-string values will be stringified. |
|
el._value = value; |
|
const newValue = value == null ? '' : value; |
|
if (el.value !== newValue || |
|
// #4956: always set for OPTION elements because its value falls back to |
|
// textContent if no value attribute is present. And setting .value for |
|
// OPTION has no side effect |
|
el.tagName === 'OPTION') { |
|
el.value = newValue; |
|
} |
|
if (value == null) { |
|
el.removeAttribute(key); |
|
} |
|
return; |
|
} |
|
let needRemove = false; |
|
if (value === '' || value == null) { |
|
const type = typeof el[key]; |
|
if (type === 'boolean') { |
|
// e.g. <select multiple> compiles to { multiple: '' } |
|
value = includeBooleanAttr(value); |
|
} |
|
else if (value == null && type === 'string') { |
|
// e.g. <div :id="null"> |
|
value = ''; |
|
needRemove = true; |
|
} |
|
else if (type === 'number') { |
|
// e.g. <img :width="null"> |
|
// the value of some IDL attr must be greater than 0, e.g. input.size = 0 -> error |
|
value = 0; |
|
needRemove = true; |
|
} |
|
} |
|
// some properties perform value validation and throw, |
|
// some properties has getter, no setter, will error in 'use strict' |
|
// eg. <select :type="null"></select> <select :willValidate="null"></select> |
|
try { |
|
el[key] = value; |
|
} |
|
catch (e) { |
|
if ((process.env.NODE_ENV !== 'production')) { |
|
warn(`Failed setting prop "${key}" on <${el.tagName.toLowerCase()}>: ` + |
|
`value ${value} is invalid.`, e); |
|
} |
|
} |
|
needRemove && el.removeAttribute(key); |
|
} |
|
|
|
// Async edge case fix requires storing an event listener's attach timestamp. |
|
const [_getNow, skipTimestampCheck] = /*#__PURE__*/ (() => { |
|
let _getNow = Date.now; |
|
let skipTimestampCheck = false; |
|
if (typeof window !== 'undefined') { |
|
// Determine what event timestamp the browser is using. Annoyingly, the |
|
// timestamp can either be hi-res (relative to page load) or low-res |
|
// (relative to UNIX epoch), so in order to compare time we have to use the |
|
// same timestamp type when saving the flush timestamp. |
|
if (Date.now() > document.createEvent('Event').timeStamp) { |
|
// if the low-res timestamp which is bigger than the event timestamp |
|
// (which is evaluated AFTER) it means the event is using a hi-res timestamp, |
|
// and we need to use the hi-res version for event listeners as well. |
|
_getNow = performance.now.bind(performance); |
|
} |
|
// #3485: Firefox <= 53 has incorrect Event.timeStamp implementation |
|
// and does not fire microtasks in between event propagation, so safe to exclude. |
|
const ffMatch = navigator.userAgent.match(/firefox\/(\d+)/i); |
|
skipTimestampCheck = !!(ffMatch && Number(ffMatch[1]) <= 53); |
|
} |
|
return [_getNow, skipTimestampCheck]; |
|
})(); |
|
// To avoid the overhead of repeatedly calling performance.now(), we cache |
|
// and use the same timestamp for all event listeners attached in the same tick. |
|
let cachedNow = 0; |
|
const p = /*#__PURE__*/ Promise.resolve(); |
|
const reset = () => { |
|
cachedNow = 0; |
|
}; |
|
const getNow = () => cachedNow || (p.then(reset), (cachedNow = _getNow())); |
|
function addEventListener(el, event, handler, options) { |
|
el.addEventListener(event, handler, options); |
|
} |
|
function removeEventListener(el, event, handler, options) { |
|
el.removeEventListener(event, handler, options); |
|
} |
|
function patchEvent(el, rawName, prevValue, nextValue, instance = null) { |
|
// vei = vue event invokers |
|
const invokers = el._vei || (el._vei = {}); |
|
const existingInvoker = invokers[rawName]; |
|
if (nextValue && existingInvoker) { |
|
// patch |
|
existingInvoker.value = nextValue; |
|
} |
|
else { |
|
const [name, options] = parseName(rawName); |
|
if (nextValue) { |
|
// add |
|
const invoker = (invokers[rawName] = createInvoker(nextValue, instance)); |
|
addEventListener(el, name, invoker, options); |
|
} |
|
else if (existingInvoker) { |
|
// remove |
|
removeEventListener(el, name, existingInvoker, options); |
|
invokers[rawName] = undefined; |
|
} |
|
} |
|
} |
|
const optionsModifierRE = /(?:Once|Passive|Capture)$/; |
|
function parseName(name) { |
|
let options; |
|
if (optionsModifierRE.test(name)) { |
|
options = {}; |
|
let m; |
|
while ((m = name.match(optionsModifierRE))) { |
|
name = name.slice(0, name.length - m[0].length); |
|
options[m[0].toLowerCase()] = true; |
|
} |
|
} |
|
return [hyphenate(name.slice(2)), options]; |
|
} |
|
function createInvoker(initialValue, instance) { |
|
const invoker = (e) => { |
|
// async edge case #6566: inner click event triggers patch, event handler |
|
// attached to outer element during patch, and triggered again. This |
|
// happens because browsers fire microtask ticks between event propagation. |
|
// the solution is simple: we save the timestamp when a handler is attached, |
|
// and the handler would only fire if the event passed to it was fired |
|
// AFTER it was attached. |
|
const timeStamp = e.timeStamp || _getNow(); |
|
if (skipTimestampCheck || timeStamp >= invoker.attached - 1) { |
|
callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */, [e]); |
|
} |
|
}; |
|
invoker.value = initialValue; |
|
invoker.attached = getNow(); |
|
return invoker; |
|
} |
|
function patchStopImmediatePropagation(e, value) { |
|
if (isArray(value)) { |
|
const originalStop = e.stopImmediatePropagation; |
|
e.stopImmediatePropagation = () => { |
|
originalStop.call(e); |
|
e._stopped = true; |
|
}; |
|
return value.map(fn => (e) => !e._stopped && fn && fn(e)); |
|
} |
|
else { |
|
return value; |
|
} |
|
} |
|
|
|
const nativeOnRE = /^on[a-z]/; |
|
const patchProp = (el, key, prevValue, nextValue, isSVG = false, prevChildren, parentComponent, parentSuspense, unmountChildren) => { |
|
if (key === 'class') { |
|
patchClass(el, nextValue, isSVG); |
|
} |
|
else if (key === 'style') { |
|
patchStyle(el, prevValue, nextValue); |
|
} |
|
else if (isOn(key)) { |
|
// ignore v-model listeners |
|
if (!isModelListener(key)) { |
|
patchEvent(el, key, prevValue, nextValue, parentComponent); |
|
} |
|
} |
|
else if (key[0] === '.' |
|
? ((key = key.slice(1)), true) |
|
: key[0] === '^' |
|
? ((key = key.slice(1)), false) |
|
: shouldSetAsProp(el, key, nextValue, isSVG)) { |
|
patchDOMProp(el, key, nextValue, prevChildren, parentComponent, parentSuspense, unmountChildren); |
|
} |
|
else { |
|
// special case for <input v-model type="checkbox"> with |
|
// :true-value & :false-value |
|
// store value as dom properties since non-string values will be |
|
// stringified. |
|
if (key === 'true-value') { |
|
el._trueValue = nextValue; |
|
} |
|
else if (key === 'false-value') { |
|
el._falseValue = nextValue; |
|
} |
|
patchAttr(el, key, nextValue, isSVG); |
|
} |
|
}; |
|
function shouldSetAsProp(el, key, value, isSVG) { |
|
if (isSVG) { |
|
// most keys must be set as attribute on svg elements to work |
|
// ...except innerHTML & textContent |
|
if (key === 'innerHTML' || key === 'textContent') { |
|
return true; |
|
} |
|
// or native onclick with function values |
|
if (key in el && nativeOnRE.test(key) && isFunction(value)) { |
|
return true; |
|
} |
|
return false; |
|
} |
|
// these are enumerated attrs, however their corresponding DOM properties |
|
// are actually booleans - this leads to setting it with a string "false" |
|
// value leading it to be coerced to `true`, so we need to always treat |
|
// them as attributes. |
|
// Note that `contentEditable` doesn't have this problem: its DOM |
|
// property is also enumerated string values. |
|
if (key === 'spellcheck' || key === 'draggable' || key === 'translate') { |
|
return false; |
|
} |
|
// #1787, #2840 form property on form elements is readonly and must be set as |
|
// attribute. |
|
if (key === 'form') { |
|
return false; |
|
} |
|
// #1526 <input list> must be set as attribute |
|
if (key === 'list' && el.tagName === 'INPUT') { |
|
return false; |
|
} |
|
// #2766 <textarea type> must be set as attribute |
|
if (key === 'type' && el.tagName === 'TEXTAREA') { |
|
return false; |
|
} |
|
// native onclick with string value, must be set as attribute |
|
if (nativeOnRE.test(key) && isString(value)) { |
|
return false; |
|
} |
|
return key in el; |
|
} |
|
|
|
function defineCustomElement(options, hydrate) { |
|
const Comp = defineComponent(options); |
|
class VueCustomElement extends VueElement { |
|
constructor(initialProps) { |
|
super(Comp, initialProps, hydrate); |
|
} |
|
} |
|
VueCustomElement.def = Comp; |
|
return VueCustomElement; |
|
} |
|
const defineSSRCustomElement = ((options) => { |
|
// @ts-ignore |
|
return defineCustomElement(options, hydrate); |
|
}); |
|
const BaseClass = (typeof HTMLElement !== 'undefined' ? HTMLElement : class { |
|
}); |
|
class VueElement extends BaseClass { |
|
constructor(_def, _props = {}, hydrate) { |
|
super(); |
|
this._def = _def; |
|
this._props = _props; |
|
/** |
|
* @internal |
|
*/ |
|
this._instance = null; |
|
this._connected = false; |
|
this._resolved = false; |
|
this._numberProps = null; |
|
if (this.shadowRoot && hydrate) { |
|
hydrate(this._createVNode(), this.shadowRoot); |
|
} |
|
else { |
|
if ((process.env.NODE_ENV !== 'production') && this.shadowRoot) { |
|
warn(`Custom element has pre-rendered declarative shadow root but is not ` + |
|
`defined as hydratable. Use \`defineSSRCustomElement\`.`); |
|
} |
|
this.attachShadow({ mode: 'open' }); |
|
} |
|
} |
|
connectedCallback() { |
|
this._connected = true; |
|
if (!this._instance) { |
|
this._resolveDef(); |
|
} |
|
} |
|
disconnectedCallback() { |
|
this._connected = false; |
|
nextTick(() => { |
|
if (!this._connected) { |
|
render(null, this.shadowRoot); |
|
this._instance = null; |
|
} |
|
}); |
|
} |
|
/** |
|
* resolve inner component definition (handle possible async component) |
|
*/ |
|
_resolveDef() { |
|
if (this._resolved) { |
|
return; |
|
} |
|
this._resolved = true; |
|
// set initial attrs |
|
for (let i = 0; i < this.attributes.length; i++) { |
|
this._setAttr(this.attributes[i].name); |
|
} |
|
// watch future attr changes |
|
new MutationObserver(mutations => { |
|
for (const m of mutations) { |
|
this._setAttr(m.attributeName); |
|
} |
|
}).observe(this, { attributes: true }); |
|
const resolve = (def) => { |
|
const { props, styles } = def; |
|
const hasOptions = !isArray(props); |
|
const rawKeys = props ? (hasOptions ? Object.keys(props) : props) : []; |
|
// cast Number-type props set before resolve |
|
let numberProps; |
|
if (hasOptions) { |
|
for (const key in this._props) { |
|
const opt = props[key]; |
|
if (opt === Number || (opt && opt.type === Number)) { |
|
this._props[key] = toNumber(this._props[key]); |
|
(numberProps || (numberProps = Object.create(null)))[key] = true; |
|
} |
|
} |
|
} |
|
this._numberProps = numberProps; |
|
// check if there are props set pre-upgrade or connect |
|
for (const key of Object.keys(this)) { |
|
if (key[0] !== '_') { |
|
this._setProp(key, this[key], true, false); |
|
} |
|
} |
|
// defining getter/setters on prototype |
|
for (const key of rawKeys.map(camelize$1)) { |
|
Object.defineProperty(this, key, { |
|
get() { |
|
return this._getProp(key); |
|
}, |
|
set(val) { |
|
this._setProp(key, val); |
|
} |
|
}); |
|
} |
|
// apply CSS |
|
this._applyStyles(styles); |
|
// initial render |
|
this._update(); |
|
}; |
|
const asyncDef = this._def.__asyncLoader; |
|
if (asyncDef) { |
|
asyncDef().then(resolve); |
|
} |
|
else { |
|
resolve(this._def); |
|
} |
|
} |
|
_setAttr(key) { |
|
let value = this.getAttribute(key); |
|
if (this._numberProps && this._numberProps[key]) { |
|
value = toNumber(value); |
|
} |
|
this._setProp(camelize$1(key), value, false); |
|
} |
|
/** |
|
* @internal |
|
*/ |
|
_getProp(key) { |
|
return this._props[key]; |
|
} |
|
/** |
|
* @internal |
|
*/ |
|
_setProp(key, val, shouldReflect = true, shouldUpdate = true) { |
|
if (val !== this._props[key]) { |
|
this._props[key] = val; |
|
if (shouldUpdate && this._instance) { |
|
this._update(); |
|
} |
|
// reflect |
|
if (shouldReflect) { |
|
if (val === true) { |
|
this.setAttribute(hyphenate(key), ''); |
|
} |
|
else if (typeof val === 'string' || typeof val === 'number') { |
|
this.setAttribute(hyphenate(key), val + ''); |
|
} |
|
else if (!val) { |
|
this.removeAttribute(hyphenate(key)); |
|
} |
|
} |
|
} |
|
} |
|
_update() { |
|
render(this._createVNode(), this.shadowRoot); |
|
} |
|
_createVNode() { |
|
const vnode = createVNode(this._def, extend({}, this._props)); |
|
if (!this._instance) { |
|
vnode.ce = instance => { |
|
this._instance = instance; |
|
instance.isCE = true; |
|
// HMR |
|
if ((process.env.NODE_ENV !== 'production')) { |
|
instance.ceReload = newStyles => { |
|
// always reset styles |
|
if (this._styles) { |
|
this._styles.forEach(s => this.shadowRoot.removeChild(s)); |
|
this._styles.length = 0; |
|
} |
|
this._applyStyles(newStyles); |
|
// if this is an async component, ceReload is called from the inner |
|
// component so no need to reload the async wrapper |
|
if (!this._def.__asyncLoader) { |
|
// reload |
|
this._instance = null; |
|
this._update(); |
|
} |
|
}; |
|
} |
|
// intercept emit |
|
instance.emit = (event, ...args) => { |
|
this.dispatchEvent(new CustomEvent(event, { |
|
detail: args |
|
})); |
|
}; |
|
// locate nearest Vue custom element parent for provide/inject |
|
let parent = this; |
|
while ((parent = |
|
parent && (parent.parentNode || parent.host))) { |
|
if (parent instanceof VueElement) { |
|
instance.parent = parent._instance; |
|
break; |
|
} |
|
} |
|
}; |
|
} |
|
return vnode; |
|
} |
|
_applyStyles(styles) { |
|
if (styles) { |
|
styles.forEach(css => { |
|
const s = document.createElement('style'); |
|
s.textContent = css; |
|
this.shadowRoot.appendChild(s); |
|
// record for HMR |
|
if ((process.env.NODE_ENV !== 'production')) { |
|
(this._styles || (this._styles = [])).push(s); |
|
} |
|
}); |
|
} |
|
} |
|
} |
|
|
|
function useCssModule(name = '$style') { |
|
/* istanbul ignore else */ |
|
{ |
|
const instance = getCurrentInstance(); |
|
if (!instance) { |
|
(process.env.NODE_ENV !== 'production') && warn(`useCssModule must be called inside setup()`); |
|
return EMPTY_OBJ; |
|
} |
|
const modules = instance.type.__cssModules; |
|
if (!modules) { |
|
(process.env.NODE_ENV !== 'production') && warn(`Current instance does not have CSS modules injected.`); |
|
return EMPTY_OBJ; |
|
} |
|
const mod = modules[name]; |
|
if (!mod) { |
|
(process.env.NODE_ENV !== 'production') && |
|
warn(`Current instance does not have CSS module named "${name}".`); |
|
return EMPTY_OBJ; |
|
} |
|
return mod; |
|
} |
|
} |
|
|
|
/** |
|
* Runtime helper for SFC's CSS variable injection feature. |
|
* @private |
|
*/ |
|
function useCssVars(getter) { |
|
const instance = getCurrentInstance(); |
|
/* istanbul ignore next */ |
|
if (!instance) { |
|
(process.env.NODE_ENV !== 'production') && |
|
warn(`useCssVars is called without current active component instance.`); |
|
return; |
|
} |
|
const setVars = () => setVarsOnVNode(instance.subTree, getter(instance.proxy)); |
|
watchPostEffect(setVars); |
|
onMounted(() => { |
|
const ob = new MutationObserver(setVars); |
|
ob.observe(instance.subTree.el.parentNode, { childList: true }); |
|
onUnmounted(() => ob.disconnect()); |
|
}); |
|
} |
|
function setVarsOnVNode(vnode, vars) { |
|
if (vnode.shapeFlag & 128 /* SUSPENSE */) { |
|
const suspense = vnode.suspense; |
|
vnode = suspense.activeBranch; |
|
if (suspense.pendingBranch && !suspense.isHydrating) { |
|
suspense.effects.push(() => { |
|
setVarsOnVNode(suspense.activeBranch, vars); |
|
}); |
|
} |
|
} |
|
// drill down HOCs until it's a non-component vnode |
|
while (vnode.component) { |
|
vnode = vnode.component.subTree; |
|
} |
|
if (vnode.shapeFlag & 1 /* ELEMENT */ && vnode.el) { |
|
setVarsOnNode(vnode.el, vars); |
|
} |
|
else if (vnode.type === Fragment) { |
|
vnode.children.forEach(c => setVarsOnVNode(c, vars)); |
|
} |
|
else if (vnode.type === Static) { |
|
let { el, anchor } = vnode; |
|
while (el) { |
|
setVarsOnNode(el, vars); |
|
if (el === anchor) |
|
break; |
|
el = el.nextSibling; |
|
} |
|
} |
|
} |
|
function setVarsOnNode(el, vars) { |
|
if (el.nodeType === 1) { |
|
const style = el.style; |
|
for (const key in vars) { |
|
style.setProperty(`--${key}`, vars[key]); |
|
} |
|
} |
|
} |
|
|
|
const TRANSITION = 'transition'; |
|
const ANIMATION = 'animation'; |
|
// DOM Transition is a higher-order-component based on the platform-agnostic |
|
// base Transition component, with DOM-specific logic. |
|
const Transition = (props, { slots }) => h(BaseTransition, resolveTransitionProps(props), slots); |
|
Transition.displayName = 'Transition'; |
|
const DOMTransitionPropsValidators = { |
|
name: String, |
|
type: String, |
|
css: { |
|
type: Boolean, |
|
default: true |
|
}, |
|
duration: [String, Number, Object], |
|
enterFromClass: String, |
|
enterActiveClass: String, |
|
enterToClass: String, |
|
appearFromClass: String, |
|
appearActiveClass: String, |
|
appearToClass: String, |
|
leaveFromClass: String, |
|
leaveActiveClass: String, |
|
leaveToClass: String |
|
}; |
|
const TransitionPropsValidators = (Transition.props = |
|
/*#__PURE__*/ extend({}, BaseTransition.props, DOMTransitionPropsValidators)); |
|
/** |
|
* #3227 Incoming hooks may be merged into arrays when wrapping Transition |
|
* with custom HOCs. |
|
*/ |
|
const callHook = (hook, args = []) => { |
|
if (isArray(hook)) { |
|
hook.forEach(h => h(...args)); |
|
} |
|
else if (hook) { |
|
hook(...args); |
|
} |
|
}; |
|
/** |
|
* Check if a hook expects a callback (2nd arg), which means the user |
|
* intends to explicitly control the end of the transition. |
|
*/ |
|
const hasExplicitCallback = (hook) => { |
|
return hook |
|
? isArray(hook) |
|
? hook.some(h => h.length > 1) |
|
: hook.length > 1 |
|
: false; |
|
}; |
|
function resolveTransitionProps(rawProps) { |
|
const baseProps = {}; |
|
for (const key in rawProps) { |
|
if (!(key in DOMTransitionPropsValidators)) { |
|
baseProps[key] = rawProps[key]; |
|
} |
|
} |
|
if (rawProps.css === false) { |
|
return baseProps; |
|
} |
|
const { name = 'v', type, duration, enterFromClass = `${name}-enter-from`, enterActiveClass = `${name}-enter-active`, enterToClass = `${name}-enter-to`, appearFromClass = enterFromClass, appearActiveClass = enterActiveClass, appearToClass = enterToClass, leaveFromClass = `${name}-leave-from`, leaveActiveClass = `${name}-leave-active`, leaveToClass = `${name}-leave-to` } = rawProps; |
|
const durations = normalizeDuration(duration); |
|
const enterDuration = durations && durations[0]; |
|
const leaveDuration = durations && durations[1]; |
|
const { onBeforeEnter, onEnter, onEnterCancelled, onLeave, onLeaveCancelled, onBeforeAppear = onBeforeEnter, onAppear = onEnter, onAppearCancelled = onEnterCancelled } = baseProps; |
|
const finishEnter = (el, isAppear, done) => { |
|
removeTransitionClass(el, isAppear ? appearToClass : enterToClass); |
|
removeTransitionClass(el, isAppear ? appearActiveClass : enterActiveClass); |
|
done && done(); |
|
}; |
|
const finishLeave = (el, done) => { |
|
el._isLeaving = false; |
|
removeTransitionClass(el, leaveFromClass); |
|
removeTransitionClass(el, leaveToClass); |
|
removeTransitionClass(el, leaveActiveClass); |
|
done && done(); |
|
}; |
|
const makeEnterHook = (isAppear) => { |
|
return (el, done) => { |
|
const hook = isAppear ? onAppear : onEnter; |
|
const resolve = () => finishEnter(el, isAppear, done); |
|
callHook(hook, [el, resolve]); |
|
nextFrame(() => { |
|
removeTransitionClass(el, isAppear ? appearFromClass : enterFromClass); |
|
addTransitionClass(el, isAppear ? appearToClass : enterToClass); |
|
if (!hasExplicitCallback(hook)) { |
|
whenTransitionEnds(el, type, enterDuration, resolve); |
|
} |
|
}); |
|
}; |
|
}; |
|
return extend(baseProps, { |
|
onBeforeEnter(el) { |
|
callHook(onBeforeEnter, [el]); |
|
addTransitionClass(el, enterFromClass); |
|
addTransitionClass(el, enterActiveClass); |
|
}, |
|
onBeforeAppear(el) { |
|
callHook(onBeforeAppear, [el]); |
|
addTransitionClass(el, appearFromClass); |
|
addTransitionClass(el, appearActiveClass); |
|
}, |
|
onEnter: makeEnterHook(false), |
|
onAppear: makeEnterHook(true), |
|
onLeave(el, done) { |
|
el._isLeaving = true; |
|
const resolve = () => finishLeave(el, done); |
|
addTransitionClass(el, leaveFromClass); |
|
// force reflow so *-leave-from classes immediately take effect (#2593) |
|
forceReflow(); |
|
addTransitionClass(el, leaveActiveClass); |
|
nextFrame(() => { |
|
if (!el._isLeaving) { |
|
// cancelled |
|
return; |
|
} |
|
removeTransitionClass(el, leaveFromClass); |
|
addTransitionClass(el, leaveToClass); |
|
if (!hasExplicitCallback(onLeave)) { |
|
whenTransitionEnds(el, type, leaveDuration, resolve); |
|
} |
|
}); |
|
callHook(onLeave, [el, resolve]); |
|
}, |
|
onEnterCancelled(el) { |
|
finishEnter(el, false); |
|
callHook(onEnterCancelled, [el]); |
|
}, |
|
onAppearCancelled(el) { |
|
finishEnter(el, true); |
|
callHook(onAppearCancelled, [el]); |
|
}, |
|
onLeaveCancelled(el) { |
|
finishLeave(el); |
|
callHook(onLeaveCancelled, [el]); |
|
} |
|
}); |
|
} |
|
function normalizeDuration(duration) { |
|
if (duration == null) { |
|
return null; |
|
} |
|
else if (isObject(duration)) { |
|
return [NumberOf(duration.enter), NumberOf(duration.leave)]; |
|
} |
|
else { |
|
const n = NumberOf(duration); |
|
return [n, n]; |
|
} |
|
} |
|
function NumberOf(val) { |
|
const res = toNumber(val); |
|
if ((process.env.NODE_ENV !== 'production')) |
|
validateDuration(res); |
|
return res; |
|
} |
|
function validateDuration(val) { |
|
if (typeof val !== 'number') { |
|
warn(`<transition> explicit duration is not a valid number - ` + |
|
`got ${JSON.stringify(val)}.`); |
|
} |
|
else if (isNaN(val)) { |
|
warn(`<transition> explicit duration is NaN - ` + |
|
'the duration expression might be incorrect.'); |
|
} |
|
} |
|
function addTransitionClass(el, cls) { |
|
cls.split(/\s+/).forEach(c => c && el.classList.add(c)); |
|
(el._vtc || |
|
(el._vtc = new Set())).add(cls); |
|
} |
|
function removeTransitionClass(el, cls) { |
|
cls.split(/\s+/).forEach(c => c && el.classList.remove(c)); |
|
const { _vtc } = el; |
|
if (_vtc) { |
|
_vtc.delete(cls); |
|
if (!_vtc.size) { |
|
el._vtc = undefined; |
|
} |
|
} |
|
} |
|
function nextFrame(cb) { |
|
requestAnimationFrame(() => { |
|
requestAnimationFrame(cb); |
|
}); |
|
} |
|
let endId = 0; |
|
function whenTransitionEnds(el, expectedType, explicitTimeout, resolve) { |
|
const id = (el._endId = ++endId); |
|
const resolveIfNotStale = () => { |
|
if (id === el._endId) { |
|
resolve(); |
|
} |
|
}; |
|
if (explicitTimeout) { |
|
return setTimeout(resolveIfNotStale, explicitTimeout); |
|
} |
|
const { type, timeout, propCount } = getTransitionInfo(el, expectedType); |
|
if (!type) { |
|
return resolve(); |
|
} |
|
const endEvent = type + 'end'; |
|
let ended = 0; |
|
const end = () => { |
|
el.removeEventListener(endEvent, onEnd); |
|
resolveIfNotStale(); |
|
}; |
|
const onEnd = (e) => { |
|
if (e.target === el && ++ended >= propCount) { |
|
end(); |
|
} |
|
}; |
|
setTimeout(() => { |
|
if (ended < propCount) { |
|
end(); |
|
} |
|
}, timeout + 1); |
|
el.addEventListener(endEvent, onEnd); |
|
} |
|
function getTransitionInfo(el, expectedType) { |
|
const styles = window.getComputedStyle(el); |
|
// JSDOM may return undefined for transition properties |
|
const getStyleProperties = (key) => (styles[key] || '').split(', '); |
|
const transitionDelays = getStyleProperties(TRANSITION + 'Delay'); |
|
const transitionDurations = getStyleProperties(TRANSITION + 'Duration'); |
|
const transitionTimeout = getTimeout(transitionDelays, transitionDurations); |
|
const animationDelays = getStyleProperties(ANIMATION + 'Delay'); |
|
const animationDurations = getStyleProperties(ANIMATION + 'Duration'); |
|
const animationTimeout = getTimeout(animationDelays, animationDurations); |
|
let type = null; |
|
let timeout = 0; |
|
let propCount = 0; |
|
/* istanbul ignore if */ |
|
if (expectedType === TRANSITION) { |
|
if (transitionTimeout > 0) { |
|
type = TRANSITION; |
|
timeout = transitionTimeout; |
|
propCount = transitionDurations.length; |
|
} |
|
} |
|
else if (expectedType === ANIMATION) { |
|
if (animationTimeout > 0) { |
|
type = ANIMATION; |
|
timeout = animationTimeout; |
|
propCount = animationDurations.length; |
|
} |
|
} |
|
else { |
|
timeout = Math.max(transitionTimeout, animationTimeout); |
|
type = |
|
timeout > 0 |
|
? transitionTimeout > animationTimeout |
|
? TRANSITION |
|
: ANIMATION |
|
: null; |
|
propCount = type |
|
? type === TRANSITION |
|
? transitionDurations.length |
|
: animationDurations.length |
|
: 0; |
|
} |
|
const hasTransform = type === TRANSITION && |
|
/\b(transform|all)(,|$)/.test(styles[TRANSITION + 'Property']); |
|
return { |
|
type, |
|
timeout, |
|
propCount, |
|
hasTransform |
|
}; |
|
} |
|
function getTimeout(delays, durations) { |
|
while (delays.length < durations.length) { |
|
delays = delays.concat(delays); |
|
} |
|
return Math.max(...durations.map((d, i) => toMs(d) + toMs(delays[i]))); |
|
} |
|
// Old versions of Chromium (below 61.0.3163.100) formats floating pointer |
|
// numbers in a locale-dependent way, using a comma instead of a dot. |
|
// If comma is not replaced with a dot, the input will be rounded down |
|
// (i.e. acting as a floor function) causing unexpected behaviors |
|
function toMs(s) { |
|
return Number(s.slice(0, -1).replace(',', '.')) * 1000; |
|
} |
|
// synchronously force layout to put elements into a certain state |
|
function forceReflow() { |
|
return document.body.offsetHeight; |
|
} |
|
|
|
const positionMap = new WeakMap(); |
|
const newPositionMap = new WeakMap(); |
|
const TransitionGroupImpl = { |
|
name: 'TransitionGroup', |
|
props: /*#__PURE__*/ extend({}, TransitionPropsValidators, { |
|
tag: String, |
|
moveClass: String |
|
}), |
|
setup(props, { slots }) { |
|
const instance = getCurrentInstance(); |
|
const state = useTransitionState(); |
|
let prevChildren; |
|
let children; |
|
onUpdated(() => { |
|
// children is guaranteed to exist after initial render |
|
if (!prevChildren.length) { |
|
return; |
|
} |
|
const moveClass = props.moveClass || `${props.name || 'v'}-move`; |
|
if (!hasCSSTransform(prevChildren[0].el, instance.vnode.el, moveClass)) { |
|
return; |
|
} |
|
// we divide the work into three loops to avoid mixing DOM reads and writes |
|
// in each iteration - which helps prevent layout thrashing. |
|
prevChildren.forEach(callPendingCbs); |
|
prevChildren.forEach(recordPosition); |
|
const movedChildren = prevChildren.filter(applyTranslation); |
|
// force reflow to put everything in position |
|
forceReflow(); |
|
movedChildren.forEach(c => { |
|
const el = c.el; |
|
const style = el.style; |
|
addTransitionClass(el, moveClass); |
|
style.transform = style.webkitTransform = style.transitionDuration = ''; |
|
const cb = (el._moveCb = (e) => { |
|
if (e && e.target !== el) { |
|
return; |
|
} |
|
if (!e || /transform$/.test(e.propertyName)) { |
|
el.removeEventListener('transitionend', cb); |
|
el._moveCb = null; |
|
removeTransitionClass(el, moveClass); |
|
} |
|
}); |
|
el.addEventListener('transitionend', cb); |
|
}); |
|
}); |
|
return () => { |
|
const rawProps = toRaw(props); |
|
const cssTransitionProps = resolveTransitionProps(rawProps); |
|
let tag = rawProps.tag || Fragment; |
|
prevChildren = children; |
|
children = slots.default ? getTransitionRawChildren(slots.default()) : []; |
|
for (let i = 0; i < children.length; i++) { |
|
const child = children[i]; |
|
if (child.key != null) { |
|
setTransitionHooks(child, resolveTransitionHooks(child, cssTransitionProps, state, instance)); |
|
} |
|
else if ((process.env.NODE_ENV !== 'production')) { |
|
warn(`<TransitionGroup> children must be keyed.`); |
|
} |
|
} |
|
if (prevChildren) { |
|
for (let i = 0; i < prevChildren.length; i++) { |
|
const child = prevChildren[i]; |
|
setTransitionHooks(child, resolveTransitionHooks(child, cssTransitionProps, state, instance)); |
|
positionMap.set(child, child.el.getBoundingClientRect()); |
|
} |
|
} |
|
return createVNode(tag, null, children); |
|
}; |
|
} |
|
}; |
|
const TransitionGroup = TransitionGroupImpl; |
|
function callPendingCbs(c) { |
|
const el = c.el; |
|
if (el._moveCb) { |
|
el._moveCb(); |
|
} |
|
if (el._enterCb) { |
|
el._enterCb(); |
|
} |
|
} |
|
function recordPosition(c) { |
|
newPositionMap.set(c, c.el.getBoundingClientRect()); |
|
} |
|
function applyTranslation(c) { |
|
const oldPos = positionMap.get(c); |
|
const newPos = newPositionMap.get(c); |
|
const dx = oldPos.left - newPos.left; |
|
const dy = oldPos.top - newPos.top; |
|
if (dx || dy) { |
|
const s = c.el.style; |
|
s.transform = s.webkitTransform = `translate(${dx}px,${dy}px)`; |
|
s.transitionDuration = '0s'; |
|
return c; |
|
} |
|
} |
|
function hasCSSTransform(el, root, moveClass) { |
|
// Detect whether an element with the move class applied has |
|
// CSS transitions. Since the element may be inside an entering |
|
// transition at this very moment, we make a clone of it and remove |
|
// all other transition classes applied to ensure only the move class |
|
// is applied. |
|
const clone = el.cloneNode(); |
|
if (el._vtc) { |
|
el._vtc.forEach(cls => { |
|
cls.split(/\s+/).forEach(c => c && clone.classList.remove(c)); |
|
}); |
|
} |
|
moveClass.split(/\s+/).forEach(c => c && clone.classList.add(c)); |
|
clone.style.display = 'none'; |
|
const container = (root.nodeType === 1 ? root : root.parentNode); |
|
container.appendChild(clone); |
|
const { hasTransform } = getTransitionInfo(clone); |
|
container.removeChild(clone); |
|
return hasTransform; |
|
} |
|
|
|
const getModelAssigner = (vnode) => { |
|
const fn = vnode.props['onUpdate:modelValue'] || |
|
(false ); |
|
return isArray(fn) ? value => invokeArrayFns(fn, value) : fn; |
|
}; |
|
function onCompositionStart(e) { |
|
e.target.composing = true; |
|
} |
|
function onCompositionEnd(e) { |
|
const target = e.target; |
|
if (target.composing) { |
|
target.composing = false; |
|
target.dispatchEvent(new Event('input')); |
|
} |
|
} |
|
// We are exporting the v-model runtime directly as vnode hooks so that it can |
|
// be tree-shaken in case v-model is never used. |
|
const vModelText = { |
|
created(el, { modifiers: { lazy, trim, number } }, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
const castToNumber = number || (vnode.props && vnode.props.type === 'number'); |
|
addEventListener(el, lazy ? 'change' : 'input', e => { |
|
if (e.target.composing) |
|
return; |
|
let domValue = el.value; |
|
if (trim) { |
|
domValue = domValue.trim(); |
|
} |
|
if (castToNumber) { |
|
domValue = toNumber(domValue); |
|
} |
|
el._assign(domValue); |
|
}); |
|
if (trim) { |
|
addEventListener(el, 'change', () => { |
|
el.value = el.value.trim(); |
|
}); |
|
} |
|
if (!lazy) { |
|
addEventListener(el, 'compositionstart', onCompositionStart); |
|
addEventListener(el, 'compositionend', onCompositionEnd); |
|
// 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. |
|
addEventListener(el, 'change', onCompositionEnd); |
|
} |
|
}, |
|
// set value on mounted so it's after min/max for type="range" |
|
mounted(el, { value }) { |
|
el.value = value == null ? '' : value; |
|
}, |
|
beforeUpdate(el, { value, modifiers: { lazy, trim, number } }, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
// avoid clearing unresolved text. #2302 |
|
if (el.composing) |
|
return; |
|
if (document.activeElement === el && el.type !== 'range') { |
|
if (lazy) { |
|
return; |
|
} |
|
if (trim && el.value.trim() === value) { |
|
return; |
|
} |
|
if ((number || el.type === 'number') && toNumber(el.value) === value) { |
|
return; |
|
} |
|
} |
|
const newValue = value == null ? '' : value; |
|
if (el.value !== newValue) { |
|
el.value = newValue; |
|
} |
|
} |
|
}; |
|
const vModelCheckbox = { |
|
// #4096 array checkboxes need to be deep traversed |
|
deep: true, |
|
created(el, _, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
addEventListener(el, 'change', () => { |
|
const modelValue = el._modelValue; |
|
const elementValue = getValue(el); |
|
const checked = el.checked; |
|
const assign = el._assign; |
|
if (isArray(modelValue)) { |
|
const index = looseIndexOf(modelValue, elementValue); |
|
const found = index !== -1; |
|
if (checked && !found) { |
|
assign(modelValue.concat(elementValue)); |
|
} |
|
else if (!checked && found) { |
|
const filtered = [...modelValue]; |
|
filtered.splice(index, 1); |
|
assign(filtered); |
|
} |
|
} |
|
else if (isSet(modelValue)) { |
|
const cloned = new Set(modelValue); |
|
if (checked) { |
|
cloned.add(elementValue); |
|
} |
|
else { |
|
cloned.delete(elementValue); |
|
} |
|
assign(cloned); |
|
} |
|
else { |
|
assign(getCheckboxValue(el, checked)); |
|
} |
|
}); |
|
}, |
|
// set initial checked on mount to wait for true-value/false-value |
|
mounted: setChecked, |
|
beforeUpdate(el, binding, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
setChecked(el, binding, vnode); |
|
} |
|
}; |
|
function setChecked(el, { value, oldValue }, vnode) { |
|
el._modelValue = value; |
|
if (isArray(value)) { |
|
el.checked = looseIndexOf(value, vnode.props.value) > -1; |
|
} |
|
else if (isSet(value)) { |
|
el.checked = value.has(vnode.props.value); |
|
} |
|
else if (value !== oldValue) { |
|
el.checked = looseEqual(value, getCheckboxValue(el, true)); |
|
} |
|
} |
|
const vModelRadio = { |
|
created(el, { value }, vnode) { |
|
el.checked = looseEqual(value, vnode.props.value); |
|
el._assign = getModelAssigner(vnode); |
|
addEventListener(el, 'change', () => { |
|
el._assign(getValue(el)); |
|
}); |
|
}, |
|
beforeUpdate(el, { value, oldValue }, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
if (value !== oldValue) { |
|
el.checked = looseEqual(value, vnode.props.value); |
|
} |
|
} |
|
}; |
|
const vModelSelect = { |
|
// <select multiple> value need to be deep traversed |
|
deep: true, |
|
created(el, { value, modifiers: { number } }, vnode) { |
|
const isSetModel = isSet(value); |
|
addEventListener(el, 'change', () => { |
|
const selectedVal = Array.prototype.filter |
|
.call(el.options, (o) => o.selected) |
|
.map((o) => number ? toNumber(getValue(o)) : getValue(o)); |
|
el._assign(el.multiple |
|
? isSetModel |
|
? new Set(selectedVal) |
|
: selectedVal |
|
: selectedVal[0]); |
|
}); |
|
el._assign = getModelAssigner(vnode); |
|
}, |
|
// set value in mounted & updated because <select> relies on its children |
|
// <option>s. |
|
mounted(el, { value }) { |
|
setSelected(el, value); |
|
}, |
|
beforeUpdate(el, _binding, vnode) { |
|
el._assign = getModelAssigner(vnode); |
|
}, |
|
updated(el, { value }) { |
|
setSelected(el, value); |
|
} |
|
}; |
|
function setSelected(el, value) { |
|
const isMultiple = el.multiple; |
|
if (isMultiple && !isArray(value) && !isSet(value)) { |
|
(process.env.NODE_ENV !== 'production') && |
|
warn(`<select multiple v-model> expects an Array or Set value for its binding, ` + |
|
`but got ${Object.prototype.toString.call(value).slice(8, -1)}.`); |
|
return; |
|
} |
|
for (let i = 0, l = el.options.length; i < l; i++) { |
|
const option = el.options[i]; |
|
const optionValue = getValue(option); |
|
if (isMultiple) { |
|
if (isArray(value)) { |
|
option.selected = looseIndexOf(value, optionValue) > -1; |
|
} |
|
else { |
|
option.selected = value.has(optionValue); |
|
} |
|
} |
|
else { |
|
if (looseEqual(getValue(option), value)) { |
|
if (el.selectedIndex !== i) |
|
el.selectedIndex = i; |
|
return; |
|
} |
|
} |
|
} |
|
if (!isMultiple && el.selectedIndex !== -1) { |
|
el.selectedIndex = -1; |
|
} |
|
} |
|
// retrieve raw value set via :value bindings |
|
function getValue(el) { |
|
return '_value' in el ? el._value : el.value; |
|
} |
|
// retrieve raw value for true-value and false-value set via :true-value or :false-value bindings |
|
function getCheckboxValue(el, checked) { |
|
const key = checked ? '_trueValue' : '_falseValue'; |
|
return key in el ? el[key] : checked; |
|
} |
|
const vModelDynamic = { |
|
created(el, binding, vnode) { |
|
callModelHook(el, binding, vnode, null, 'created'); |
|
}, |
|
mounted(el, binding, vnode) { |
|
callModelHook(el, binding, vnode, null, 'mounted'); |
|
}, |
|
beforeUpdate(el, binding, vnode, prevVNode) { |
|
callModelHook(el, binding, vnode, prevVNode, 'beforeUpdate'); |
|
}, |
|
updated(el, binding, vnode, prevVNode) { |
|
callModelHook(el, binding, vnode, prevVNode, 'updated'); |
|
} |
|
}; |
|
function resolveDynamicModel(tagName, type) { |
|
switch (tagName) { |
|
case 'SELECT': |
|
return vModelSelect; |
|
case 'TEXTAREA': |
|
return vModelText; |
|
default: |
|
switch (type) { |
|
case 'checkbox': |
|
return vModelCheckbox; |
|
case 'radio': |
|
return vModelRadio; |
|
default: |
|
return vModelText; |
|
} |
|
} |
|
} |
|
function callModelHook(el, binding, vnode, prevVNode, hook) { |
|
const modelToUse = resolveDynamicModel(el.tagName, vnode.props && vnode.props.type); |
|
const fn = modelToUse[hook]; |
|
fn && fn(el, binding, vnode, prevVNode); |
|
} |
|
// SSR vnode transforms, only used when user includes client-oriented render |
|
// function in SSR |
|
function initVModelForSSR() { |
|
vModelText.getSSRProps = ({ value }) => ({ value }); |
|
vModelRadio.getSSRProps = ({ value }, vnode) => { |
|
if (vnode.props && looseEqual(vnode.props.value, value)) { |
|
return { checked: true }; |
|
} |
|
}; |
|
vModelCheckbox.getSSRProps = ({ value }, vnode) => { |
|
if (isArray(value)) { |
|
if (vnode.props && looseIndexOf(value, vnode.props.value) > -1) { |
|
return { checked: true }; |
|
} |
|
} |
|
else if (isSet(value)) { |
|
if (vnode.props && value.has(vnode.props.value)) { |
|
return { checked: true }; |
|
} |
|
} |
|
else if (value) { |
|
return { checked: true }; |
|
} |
|
}; |
|
vModelDynamic.getSSRProps = (binding, vnode) => { |
|
if (typeof vnode.type !== 'string') { |
|
return; |
|
} |
|
const modelToUse = resolveDynamicModel( |
|
// resolveDynamicModel expects an uppercase tag name, but vnode.type is lowercase |
|
vnode.type.toUpperCase(), vnode.props && vnode.props.type); |
|
if (modelToUse.getSSRProps) { |
|
return modelToUse.getSSRProps(binding, vnode); |
|
} |
|
}; |
|
} |
|
|
|
const systemModifiers = ['ctrl', 'shift', 'alt', 'meta']; |
|
const modifierGuards = { |
|
stop: e => e.stopPropagation(), |
|
prevent: e => e.preventDefault(), |
|
self: e => e.target !== e.currentTarget, |
|
ctrl: e => !e.ctrlKey, |
|
shift: e => !e.shiftKey, |
|
alt: e => !e.altKey, |
|
meta: e => !e.metaKey, |
|
left: e => 'button' in e && e.button !== 0, |
|
middle: e => 'button' in e && e.button !== 1, |
|
right: e => 'button' in e && e.button !== 2, |
|
exact: (e, modifiers) => systemModifiers.some(m => e[`${m}Key`] && !modifiers.includes(m)) |
|
}; |
|
/** |
|
* @private |
|
*/ |
|
const withModifiers = (fn, modifiers) => { |
|
return (event, ...args) => { |
|
for (let i = 0; i < modifiers.length; i++) { |
|
const guard = modifierGuards[modifiers[i]]; |
|
if (guard && guard(event, modifiers)) |
|
return; |
|
} |
|
return fn(event, ...args); |
|
}; |
|
}; |
|
// Kept for 2.x compat. |
|
// Note: IE11 compat for `spacebar` and `del` is removed for now. |
|
const keyNames = { |
|
esc: 'escape', |
|
space: ' ', |
|
up: 'arrow-up', |
|
left: 'arrow-left', |
|
right: 'arrow-right', |
|
down: 'arrow-down', |
|
delete: 'backspace' |
|
}; |
|
/** |
|
* @private |
|
*/ |
|
const withKeys = (fn, modifiers) => { |
|
return (event) => { |
|
if (!('key' in event)) { |
|
return; |
|
} |
|
const eventKey = hyphenate(event.key); |
|
if (modifiers.some(k => k === eventKey || keyNames[k] === eventKey)) { |
|
return fn(event); |
|
} |
|
}; |
|
}; |
|
|
|
const vShow = { |
|
beforeMount(el, { value }, { transition }) { |
|
el._vod = el.style.display === 'none' ? '' : el.style.display; |
|
if (transition && value) { |
|
transition.beforeEnter(el); |
|
} |
|
else { |
|
setDisplay(el, value); |
|
} |
|
}, |
|
mounted(el, { value }, { transition }) { |
|
if (transition && value) { |
|
transition.enter(el); |
|
} |
|
}, |
|
updated(el, { value, oldValue }, { transition }) { |
|
if (!value === !oldValue) |
|
return; |
|
if (transition) { |
|
if (value) { |
|
transition.beforeEnter(el); |
|
setDisplay(el, true); |
|
transition.enter(el); |
|
} |
|
else { |
|
transition.leave(el, () => { |
|
setDisplay(el, false); |
|
}); |
|
} |
|
} |
|
else { |
|
setDisplay(el, value); |
|
} |
|
}, |
|
beforeUnmount(el, { value }) { |
|
setDisplay(el, value); |
|
} |
|
}; |
|
function setDisplay(el, value) { |
|
el.style.display = value ? el._vod : 'none'; |
|
} |
|
// SSR vnode transforms, only used when user includes client-oriented render |
|
// function in SSR |
|
function initVShowForSSR() { |
|
vShow.getSSRProps = ({ value }) => { |
|
if (!value) { |
|
return { style: { display: 'none' } }; |
|
} |
|
}; |
|
} |
|
|
|
const rendererOptions = /*#__PURE__*/ extend({ patchProp }, nodeOps); |
|
// lazy create the renderer - this makes core renderer logic tree-shakable |
|
// in case the user only imports reactivity utilities from Vue. |
|
let renderer; |
|
let enabledHydration = false; |
|
function ensureRenderer() { |
|
return (renderer || |
|
(renderer = createRenderer(rendererOptions))); |
|
} |
|
function ensureHydrationRenderer() { |
|
renderer = enabledHydration |
|
? renderer |
|
: createHydrationRenderer(rendererOptions); |
|
enabledHydration = true; |
|
return renderer; |
|
} |
|
// use explicit type casts here to avoid import() calls in rolled-up d.ts |
|
const render = ((...args) => { |
|
ensureRenderer().render(...args); |
|
}); |
|
const hydrate = ((...args) => { |
|
ensureHydrationRenderer().hydrate(...args); |
|
}); |
|
const createApp = ((...args) => { |
|
const app = ensureRenderer().createApp(...args); |
|
if ((process.env.NODE_ENV !== 'production')) { |
|
injectNativeTagCheck(app); |
|
injectCompilerOptionsCheck(app); |
|
} |
|
const { mount } = app; |
|
app.mount = (containerOrSelector) => { |
|
const container = normalizeContainer(containerOrSelector); |
|
if (!container) |
|
return; |
|
const component = app._component; |
|
if (!isFunction(component) && !component.render && !component.template) { |
|
// __UNSAFE__ |
|
// Reason: potential execution of JS expressions in in-DOM template. |
|
// The user must make sure the in-DOM template is trusted. If it's |
|
// rendered by the server, the template should not contain any user data. |
|
component.template = container.innerHTML; |
|
} |
|
// clear content before mounting |
|
container.innerHTML = ''; |
|
const proxy = mount(container, false, container instanceof SVGElement); |
|
if (container instanceof Element) { |
|
container.removeAttribute('v-cloak'); |
|
container.setAttribute('data-v-app', ''); |
|
} |
|
return proxy; |
|
}; |
|
return app; |
|
}); |
|
const createSSRApp = ((...args) => { |
|
const app = ensureHydrationRenderer().createApp(...args); |
|
if ((process.env.NODE_ENV !== 'production')) { |
|
injectNativeTagCheck(app); |
|
injectCompilerOptionsCheck(app); |
|
} |
|
const { mount } = app; |
|
app.mount = (containerOrSelector) => { |
|
const container = normalizeContainer(containerOrSelector); |
|
if (container) { |
|
return mount(container, true, container instanceof SVGElement); |
|
} |
|
}; |
|
return app; |
|
}); |
|
function injectNativeTagCheck(app) { |
|
// Inject `isNativeTag` |
|
// this is used for component name validation (dev only) |
|
Object.defineProperty(app.config, 'isNativeTag', { |
|
value: (tag) => isHTMLTag(tag) || isSVGTag(tag), |
|
writable: false |
|
}); |
|
} |
|
// dev only |
|
function injectCompilerOptionsCheck(app) { |
|
if (isRuntimeOnly()) { |
|
const isCustomElement = app.config.isCustomElement; |
|
Object.defineProperty(app.config, 'isCustomElement', { |
|
get() { |
|
return isCustomElement; |
|
}, |
|
set() { |
|
warn(`The \`isCustomElement\` config option is deprecated. Use ` + |
|
`\`compilerOptions.isCustomElement\` instead.`); |
|
} |
|
}); |
|
const compilerOptions = app.config.compilerOptions; |
|
const msg = `The \`compilerOptions\` config option is only respected when using ` + |
|
`a build of Vue.js that includes the runtime compiler (aka "full build"). ` + |
|
`Since you are using the runtime-only build, \`compilerOptions\` ` + |
|
`must be passed to \`@vue/compiler-dom\` in the build setup instead.\n` + |
|
`- For vue-loader: pass it via vue-loader's \`compilerOptions\` loader option.\n` + |
|
`- For vue-cli: see https://cli.vuejs.org/guide/webpack.html#modifying-options-of-a-loader\n` + |
|
`- For vite: pass it via @vitejs/plugin-vue options. See https://github.com/vitejs/vite/tree/main/packages/plugin-vue#example-for-passing-options-to-vuecompiler-dom`; |
|
Object.defineProperty(app.config, 'compilerOptions', { |
|
get() { |
|
warn(msg); |
|
return compilerOptions; |
|
}, |
|
set() { |
|
warn(msg); |
|
} |
|
}); |
|
} |
|
} |
|
function normalizeContainer(container) { |
|
if (isString(container)) { |
|
const res = document.querySelector(container); |
|
if ((process.env.NODE_ENV !== 'production') && !res) { |
|
warn(`Failed to mount app: mount target selector "${container}" returned null.`); |
|
} |
|
return res; |
|
} |
|
if ((process.env.NODE_ENV !== 'production') && |
|
window.ShadowRoot && |
|
container instanceof window.ShadowRoot && |
|
container.mode === 'closed') { |
|
warn(`mounting on a ShadowRoot with \`{mode: "closed"}\` may lead to unpredictable bugs`); |
|
} |
|
return container; |
|
} |
|
let ssrDirectiveInitialized = false; |
|
/** |
|
* @internal |
|
*/ |
|
const initDirectivesForSSR = () => { |
|
if (!ssrDirectiveInitialized) { |
|
ssrDirectiveInitialized = true; |
|
initVModelForSSR(); |
|
initVShowForSSR(); |
|
} |
|
} |
|
; |
|
|
|
export { Transition, TransitionGroup, VueElement, createApp, createSSRApp, defineCustomElement, defineSSRCustomElement, hydrate, initDirectivesForSSR, render, useCssModule, useCssVars, vModelCheckbox, vModelDynamic, vModelRadio, vModelSelect, vModelText, vShow, withKeys, withModifiers };
|
|
|