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.
1492 lines
50 KiB
1492 lines
50 KiB
'use strict';
|
|
|
|
Object.defineProperty(exports, '__esModule', { value: true });
|
|
|
|
var runtimeCore = require('@vue/runtime-core');
|
|
var shared = require('@vue/shared');
|
|
|
|
const svgNS = 'http://www.w3.org/2000/svg';
|
|
const doc = (typeof document !== 'undefined' ? document : null);
|
|
const staticTemplateCache = new Map();
|
|
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) {
|
|
// <parent> before | first ... last | anchor </parent>
|
|
const before = anchor ? anchor.previousSibling : parent.lastChild;
|
|
let template = staticTemplateCache.get(content);
|
|
if (!template) {
|
|
const t = doc.createElement('template');
|
|
t.innerHTML = isSVG ? `<svg>${content}</svg>` : content;
|
|
template = t.content;
|
|
if (isSVG) {
|
|
// remove outer svg wrapper
|
|
const wrapper = template.firstChild;
|
|
while (wrapper.firstChild) {
|
|
template.appendChild(wrapper.firstChild);
|
|
}
|
|
template.removeChild(wrapper);
|
|
}
|
|
staticTemplateCache.set(content, template);
|
|
}
|
|
parent.insertBefore(template.cloneNode(true), 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;
|
|
if (!next) {
|
|
el.removeAttribute('style');
|
|
}
|
|
else if (shared.isString(next)) {
|
|
if (prev !== next) {
|
|
const current = style.display;
|
|
style.cssText = next;
|
|
// 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 = current;
|
|
}
|
|
}
|
|
}
|
|
else {
|
|
for (const key in next) {
|
|
setStyle(style, key, next[key]);
|
|
}
|
|
if (prev && !shared.isString(prev)) {
|
|
for (const key in prev) {
|
|
if (next[key] == null) {
|
|
setStyle(style, key, '');
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
const importantRE = /\s*!important$/;
|
|
function setStyle(style, name, val) {
|
|
if (shared.isArray(val)) {
|
|
val.forEach(v => setStyle(style, name, v));
|
|
}
|
|
else {
|
|
if (name.startsWith('--')) {
|
|
// custom property definition
|
|
style.setProperty(name, val);
|
|
}
|
|
else {
|
|
const prefixed = autoPrefix(style, name);
|
|
if (importantRE.test(val)) {
|
|
// !important
|
|
style.setProperty(shared.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 = runtimeCore.camelize(rawName);
|
|
if (name !== 'filter' && name in style) {
|
|
return (prefixCache[rawName] = name);
|
|
}
|
|
name = shared.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 = shared.isSpecialBooleanAttr(key);
|
|
if (value == null || (isBoolean && !shared.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') {
|
|
// 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) {
|
|
el.value = newValue;
|
|
}
|
|
if (value == null) {
|
|
el.removeAttribute(key);
|
|
}
|
|
return;
|
|
}
|
|
if (value === '' || value == null) {
|
|
const type = typeof el[key];
|
|
if (type === 'boolean') {
|
|
// e.g. <select multiple> compiles to { multiple: '' }
|
|
el[key] = shared.includeBooleanAttr(value);
|
|
return;
|
|
}
|
|
else if (value == null && type === 'string') {
|
|
// e.g. <div :id="null">
|
|
el[key] = '';
|
|
el.removeAttribute(key);
|
|
return;
|
|
}
|
|
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
|
|
try {
|
|
el[key] = 0;
|
|
}
|
|
catch (_a) { }
|
|
el.removeAttribute(key);
|
|
return;
|
|
}
|
|
}
|
|
// some properties perform value validation and throw
|
|
try {
|
|
el[key] = value;
|
|
}
|
|
catch (e) {
|
|
}
|
|
}
|
|
|
|
// Async edge case fix requires storing an event listener's attach timestamp.
|
|
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 (_getNow() > 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();
|
|
}
|
|
// #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);
|
|
}
|
|
// 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 = 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 [shared.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) {
|
|
runtimeCore.callWithAsyncErrorHandling(patchStopImmediatePropagation(e, invoker.value), instance, 5 /* NATIVE_EVENT_HANDLER */, [e]);
|
|
}
|
|
};
|
|
invoker.value = initialValue;
|
|
invoker.attached = getNow();
|
|
return invoker;
|
|
}
|
|
function patchStopImmediatePropagation(e, value) {
|
|
if (shared.isArray(value)) {
|
|
const originalStop = e.stopImmediatePropagation;
|
|
e.stopImmediatePropagation = () => {
|
|
originalStop.call(e);
|
|
e._stopped = true;
|
|
};
|
|
return value.map(fn => (e) => !e._stopped && 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 (shared.isOn(key)) {
|
|
// ignore v-model listeners
|
|
if (!shared.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) && shared.isFunction(value)) {
|
|
return true;
|
|
}
|
|
return false;
|
|
}
|
|
// spellcheck and draggable are numerated 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') {
|
|
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) && shared.isString(value)) {
|
|
return false;
|
|
}
|
|
return key in el;
|
|
}
|
|
|
|
function defineCustomElement(options, hydate) {
|
|
const Comp = runtimeCore.defineComponent(options);
|
|
class VueCustomElement extends VueElement {
|
|
constructor(initialProps) {
|
|
super(Comp, initialProps, hydate);
|
|
}
|
|
}
|
|
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;
|
|
if (this.shadowRoot && hydrate) {
|
|
hydrate(this._createVNode(), this.shadowRoot);
|
|
}
|
|
else {
|
|
this.attachShadow({ mode: 'open' });
|
|
}
|
|
// set initial attrs
|
|
for (let i = 0; i < this.attributes.length; i++) {
|
|
this._setAttr(this.attributes[i].name);
|
|
}
|
|
// watch future attr changes
|
|
const observer = new MutationObserver(mutations => {
|
|
for (const m of mutations) {
|
|
this._setAttr(m.attributeName);
|
|
}
|
|
});
|
|
observer.observe(this, { attributes: true });
|
|
}
|
|
connectedCallback() {
|
|
this._connected = true;
|
|
if (!this._instance) {
|
|
this._resolveDef();
|
|
render(this._createVNode(), this.shadowRoot);
|
|
}
|
|
}
|
|
disconnectedCallback() {
|
|
this._connected = false;
|
|
runtimeCore.nextTick(() => {
|
|
if (!this._connected) {
|
|
render(null, this.shadowRoot);
|
|
this._instance = null;
|
|
}
|
|
});
|
|
}
|
|
/**
|
|
* resolve inner component definition (handle possible async component)
|
|
*/
|
|
_resolveDef() {
|
|
if (this._resolved) {
|
|
return;
|
|
}
|
|
const resolve = (def) => {
|
|
this._resolved = true;
|
|
// 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]);
|
|
}
|
|
}
|
|
const { props, styles } = def;
|
|
// defining getter/setters on prototype
|
|
const rawKeys = props ? (shared.isArray(props) ? props : Object.keys(props)) : [];
|
|
for (const key of rawKeys.map(shared.camelize)) {
|
|
Object.defineProperty(this, key, {
|
|
get() {
|
|
return this._getProp(key);
|
|
},
|
|
set(val) {
|
|
this._setProp(key, val);
|
|
}
|
|
});
|
|
}
|
|
this._applyStyles(styles);
|
|
};
|
|
const asyncDef = this._def.__asyncLoader;
|
|
if (asyncDef) {
|
|
asyncDef().then(resolve);
|
|
}
|
|
else {
|
|
resolve(this._def);
|
|
}
|
|
}
|
|
_setAttr(key) {
|
|
this._setProp(shared.camelize(key), shared.toNumber(this.getAttribute(key)), false);
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
_getProp(key) {
|
|
return this._props[key];
|
|
}
|
|
/**
|
|
* @internal
|
|
*/
|
|
_setProp(key, val, shouldReflect = true) {
|
|
if (val !== this._props[key]) {
|
|
this._props[key] = val;
|
|
if (this._instance) {
|
|
render(this._createVNode(), this.shadowRoot);
|
|
}
|
|
// reflect
|
|
if (shouldReflect) {
|
|
if (val === true) {
|
|
this.setAttribute(shared.hyphenate(key), '');
|
|
}
|
|
else if (typeof val === 'string' || typeof val === 'number') {
|
|
this.setAttribute(shared.hyphenate(key), val + '');
|
|
}
|
|
else if (!val) {
|
|
this.removeAttribute(shared.hyphenate(key));
|
|
}
|
|
}
|
|
}
|
|
}
|
|
_createVNode() {
|
|
const vnode = runtimeCore.createVNode(this._def, shared.extend({}, this._props));
|
|
if (!this._instance) {
|
|
vnode.ce = instance => {
|
|
this._instance = instance;
|
|
instance.isCE = true;
|
|
// 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);
|
|
});
|
|
}
|
|
}
|
|
}
|
|
|
|
function useCssModule(name = '$style') {
|
|
/* istanbul ignore else */
|
|
{
|
|
const instance = runtimeCore.getCurrentInstance();
|
|
if (!instance) {
|
|
return shared.EMPTY_OBJ;
|
|
}
|
|
const modules = instance.type.__cssModules;
|
|
if (!modules) {
|
|
return shared.EMPTY_OBJ;
|
|
}
|
|
const mod = modules[name];
|
|
if (!mod) {
|
|
return shared.EMPTY_OBJ;
|
|
}
|
|
return mod;
|
|
}
|
|
}
|
|
|
|
/**
|
|
* Runtime helper for SFC's CSS variable injection feature.
|
|
* @private
|
|
*/
|
|
function useCssVars(getter) {
|
|
return;
|
|
}
|
|
|
|
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 }) => runtimeCore.h(runtimeCore.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__*/ shared.extend({}, runtimeCore.BaseTransition.props, DOMTransitionPropsValidators));
|
|
/**
|
|
* #3227 Incoming hooks may be merged into arrays when wrapping Transition
|
|
* with custom HOCs.
|
|
*/
|
|
const callHook = (hook, args = []) => {
|
|
if (shared.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
|
|
? shared.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) => {
|
|
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 shared.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) {
|
|
const resolve = () => finishLeave(el, done);
|
|
addTransitionClass(el, leaveFromClass);
|
|
// force reflow so *-leave-from classes immediately take effect (#2593)
|
|
forceReflow();
|
|
addTransitionClass(el, leaveActiveClass);
|
|
nextFrame(() => {
|
|
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 (shared.isObject(duration)) {
|
|
return [NumberOf(duration.enter), NumberOf(duration.leave)];
|
|
}
|
|
else {
|
|
const n = NumberOf(duration);
|
|
return [n, n];
|
|
}
|
|
}
|
|
function NumberOf(val) {
|
|
const res = shared.toNumber(val);
|
|
return res;
|
|
}
|
|
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__*/ shared.extend({}, TransitionPropsValidators, {
|
|
tag: String,
|
|
moveClass: String
|
|
}),
|
|
setup(props, { slots }) {
|
|
const instance = runtimeCore.getCurrentInstance();
|
|
const state = runtimeCore.useTransitionState();
|
|
let prevChildren;
|
|
let children;
|
|
runtimeCore.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 = runtimeCore.toRaw(props);
|
|
const cssTransitionProps = resolveTransitionProps(rawProps);
|
|
let tag = rawProps.tag || runtimeCore.Fragment;
|
|
prevChildren = children;
|
|
children = slots.default ? runtimeCore.getTransitionRawChildren(slots.default()) : [];
|
|
for (let i = 0; i < children.length; i++) {
|
|
const child = children[i];
|
|
if (child.key != null) {
|
|
runtimeCore.setTransitionHooks(child, runtimeCore.resolveTransitionHooks(child, cssTransitionProps, state, instance));
|
|
}
|
|
}
|
|
if (prevChildren) {
|
|
for (let i = 0; i < prevChildren.length; i++) {
|
|
const child = prevChildren[i];
|
|
runtimeCore.setTransitionHooks(child, runtimeCore.resolveTransitionHooks(child, cssTransitionProps, state, instance));
|
|
positionMap.set(child, child.el.getBoundingClientRect());
|
|
}
|
|
}
|
|
return runtimeCore.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'];
|
|
return shared.isArray(fn) ? value => shared.invokeArrayFns(fn, value) : fn;
|
|
};
|
|
function onCompositionStart(e) {
|
|
e.target.composing = true;
|
|
}
|
|
function onCompositionEnd(e) {
|
|
const target = e.target;
|
|
if (target.composing) {
|
|
target.composing = false;
|
|
trigger(target, 'input');
|
|
}
|
|
}
|
|
function trigger(el, type) {
|
|
const e = document.createEvent('HTMLEvents');
|
|
e.initEvent(type, true, true);
|
|
el.dispatchEvent(e);
|
|
}
|
|
// 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();
|
|
}
|
|
else if (castToNumber) {
|
|
domValue = shared.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) {
|
|
if (lazy) {
|
|
return;
|
|
}
|
|
if (trim && el.value.trim() === value) {
|
|
return;
|
|
}
|
|
if ((number || el.type === 'number') && shared.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 (shared.isArray(modelValue)) {
|
|
const index = shared.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 (shared.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 (shared.isArray(value)) {
|
|
el.checked = shared.looseIndexOf(value, vnode.props.value) > -1;
|
|
}
|
|
else if (shared.isSet(value)) {
|
|
el.checked = value.has(vnode.props.value);
|
|
}
|
|
else if (value !== oldValue) {
|
|
el.checked = shared.looseEqual(value, getCheckboxValue(el, true));
|
|
}
|
|
}
|
|
const vModelRadio = {
|
|
created(el, { value }, vnode) {
|
|
el.checked = shared.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 = shared.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 = shared.isSet(value);
|
|
addEventListener(el, 'change', () => {
|
|
const selectedVal = Array.prototype.filter
|
|
.call(el.options, (o) => o.selected)
|
|
.map((o) => number ? shared.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 && !shared.isArray(value) && !shared.isSet(value)) {
|
|
return;
|
|
}
|
|
for (let i = 0, l = el.options.length; i < l; i++) {
|
|
const option = el.options[i];
|
|
const optionValue = getValue(option);
|
|
if (isMultiple) {
|
|
if (shared.isArray(value)) {
|
|
option.selected = shared.looseIndexOf(value, optionValue) > -1;
|
|
}
|
|
else {
|
|
option.selected = value.has(optionValue);
|
|
}
|
|
}
|
|
else {
|
|
if (shared.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 callModelHook(el, binding, vnode, prevVNode, hook) {
|
|
let modelToUse;
|
|
switch (el.tagName) {
|
|
case 'SELECT':
|
|
modelToUse = vModelSelect;
|
|
break;
|
|
case 'TEXTAREA':
|
|
modelToUse = vModelText;
|
|
break;
|
|
default:
|
|
switch (vnode.props && vnode.props.type) {
|
|
case 'checkbox':
|
|
modelToUse = vModelCheckbox;
|
|
break;
|
|
case 'radio':
|
|
modelToUse = vModelRadio;
|
|
break;
|
|
default:
|
|
modelToUse = vModelText;
|
|
}
|
|
}
|
|
const fn = modelToUse[hook];
|
|
fn && fn(el, binding, vnode, prevVNode);
|
|
}
|
|
// SSR vnode transforms
|
|
{
|
|
vModelText.getSSRProps = ({ value }) => ({ value });
|
|
vModelRadio.getSSRProps = ({ value }, vnode) => {
|
|
if (vnode.props && shared.looseEqual(vnode.props.value, value)) {
|
|
return { checked: true };
|
|
}
|
|
};
|
|
vModelCheckbox.getSSRProps = ({ value }, vnode) => {
|
|
if (shared.isArray(value)) {
|
|
if (vnode.props && shared.looseIndexOf(value, vnode.props.value) > -1) {
|
|
return { checked: true };
|
|
}
|
|
}
|
|
else if (shared.isSet(value)) {
|
|
if (vnode.props && value.has(vnode.props.value)) {
|
|
return { checked: true };
|
|
}
|
|
}
|
|
else if (value) {
|
|
return { checked: true };
|
|
}
|
|
};
|
|
}
|
|
|
|
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 = shared.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);
|
|
}
|
|
};
|
|
{
|
|
vShow.getSSRProps = ({ value }) => {
|
|
if (!value) {
|
|
return { style: { display: 'none' } };
|
|
}
|
|
};
|
|
}
|
|
function setDisplay(el, value) {
|
|
el.style.display = value ? el._vod : 'none';
|
|
}
|
|
|
|
const rendererOptions = shared.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 = runtimeCore.createRenderer(rendererOptions)));
|
|
}
|
|
function ensureHydrationRenderer() {
|
|
renderer = enabledHydration
|
|
? renderer
|
|
: runtimeCore.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);
|
|
const { mount } = app;
|
|
app.mount = (containerOrSelector) => {
|
|
const container = normalizeContainer(containerOrSelector);
|
|
if (!container)
|
|
return;
|
|
const component = app._component;
|
|
if (!shared.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);
|
|
const { mount } = app;
|
|
app.mount = (containerOrSelector) => {
|
|
const container = normalizeContainer(containerOrSelector);
|
|
if (container) {
|
|
return mount(container, true, container instanceof SVGElement);
|
|
}
|
|
};
|
|
return app;
|
|
});
|
|
function normalizeContainer(container) {
|
|
if (shared.isString(container)) {
|
|
const res = document.querySelector(container);
|
|
return res;
|
|
}
|
|
return container;
|
|
}
|
|
|
|
Object.keys(runtimeCore).forEach(function (k) {
|
|
if (k !== 'default') exports[k] = runtimeCore[k];
|
|
});
|
|
exports.Transition = Transition;
|
|
exports.TransitionGroup = TransitionGroup;
|
|
exports.VueElement = VueElement;
|
|
exports.createApp = createApp;
|
|
exports.createSSRApp = createSSRApp;
|
|
exports.defineCustomElement = defineCustomElement;
|
|
exports.defineSSRCustomElement = defineSSRCustomElement;
|
|
exports.hydrate = hydrate;
|
|
exports.render = render;
|
|
exports.useCssModule = useCssModule;
|
|
exports.useCssVars = useCssVars;
|
|
exports.vModelCheckbox = vModelCheckbox;
|
|
exports.vModelDynamic = vModelDynamic;
|
|
exports.vModelRadio = vModelRadio;
|
|
exports.vModelSelect = vModelSelect;
|
|
exports.vModelText = vModelText;
|
|
exports.vShow = vShow;
|
|
exports.withKeys = withKeys;
|
|
exports.withModifiers = withModifiers;
|