| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149 |
- import { patchClass } from './modules/class'
- import { patchStyle } from './modules/style'
- import { patchAttr } from './modules/attrs'
- import { patchDOMProp } from './modules/props'
- import { patchEvent } from './modules/events'
- import {
- camelize,
- isFunction,
- isModelListener,
- isOn,
- isString,
- } from '@vue/shared'
- import type { RendererOptions } from '@vue/runtime-core'
- import type { VueElement } from './apiCustomElement'
- const isNativeOn = (key: string) =>
- key.charCodeAt(0) === 111 /* o */ &&
- key.charCodeAt(1) === 110 /* n */ &&
- // lowercase letter
- key.charCodeAt(2) > 96 &&
- key.charCodeAt(2) < 123
- type DOMRendererOptions = RendererOptions<Node, Element>
- export const patchProp: DOMRendererOptions['patchProp'] = (
- el,
- key,
- prevValue,
- nextValue,
- namespace,
- parentComponent,
- ) => {
- const isSVG = namespace === 'svg'
- 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, parentComponent)
- // #6007 also set form state as attributes so they work with
- // <input type="reset"> or libs / extensions that expect attributes
- // #11163 custom elements may use value as an prop and set it as object
- if (
- !el.tagName.includes('-') &&
- (key === 'value' || key === 'checked' || key === 'selected')
- ) {
- patchAttr(el, key, nextValue, isSVG, parentComponent, key !== 'value')
- }
- } else if (
- // #11081 force set props for possible async custom element
- (el as VueElement)._isVueCE &&
- (/[A-Z]/.test(key) || !isString(nextValue))
- ) {
- patchDOMProp(el, camelize(key), nextValue, parentComponent, key)
- } 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 as any)._trueValue = nextValue
- } else if (key === 'false-value') {
- ;(el as any)._falseValue = nextValue
- }
- patchAttr(el, key, nextValue, isSVG, parentComponent)
- }
- }
- function shouldSetAsProp(
- el: Element,
- key: string,
- value: unknown,
- isSVG: boolean,
- ) {
- 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 && isNativeOn(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' ||
- key === 'autocorrect'
- ) {
- 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
- }
- // #8780 the width or height of embedded tags must be set as attribute
- if (key === 'width' || key === 'height') {
- const tag = el.tagName
- if (
- tag === 'IMG' ||
- tag === 'VIDEO' ||
- tag === 'CANVAS' ||
- tag === 'SOURCE'
- ) {
- return false
- }
- }
- // native onclick with string value, must be set as attribute
- if (isNativeOn(key) && isString(value)) {
- return false
- }
- return key in el
- }
|