export function render(_ctx) {
const n0 = t0()
- _renderEffect(() => _setAttr(n0, "class", _ctx.cls))
+ _renderEffect(() => _setAttr(n0, "class", _ctx.cls, true))
return n0
}"
`;
}"
`;
+exports[`compiler v-bind > v-bind w/ svg elements 1`] = `
+"import { setDynamicProps as _setDynamicProps, renderEffect as _renderEffect, template as _template } from 'vue';
+const t0 = _template("<svg></svg>", true, 1)
+
+export function render(_ctx) {
+ const n0 = t0()
+ _renderEffect(() => _setDynamicProps(n0, [_ctx.obj], true, true))
+ return n0
+}"
+`;
+
exports[`compiler v-bind > with constant value 1`] = `
"import { setProp as _setProp, template as _template } from 'vue';
const t0 = _template("<div f=\\"foo1\\" h=\\"1\\"></div>", true)
<svg :class="cls"/>
`)
expect(code).matchSnapshot()
- expect(code).contains('_setAttr(n0, "class", _ctx.cls))')
+ expect(code).contains('_setAttr(n0, "class", _ctx.cls, true))')
+ })
+
+ test('v-bind w/ svg elements', () => {
+ const { code } = compileWithVBind(`
+ <svg v-bind="obj"/>
+ `)
+ expect(code).matchSnapshot()
+ expect(code).contains('_setDynamicProps(n0, [_ctx.obj], true, true))')
})
test('number value', () => {
import {
canSetValueDirectly,
capitalize,
+ extend,
isSVGTag,
shouldSetAsAttr,
toHandlerKey,
export type HelperConfig = {
name: VaporHelper
needKey?: boolean
+ isSVG?: boolean
acceptRoot?: boolean
}
setAttr: { name: 'setAttr', needKey: true },
setProp: { name: 'setProp', needKey: true },
setDOMProp: { name: 'setDOMProp', needKey: true },
- setDynamicProps: { name: 'setDynamicProps' },
} as const satisfies Partial<Record<VaporHelper, HelperConfig>>
// only the static key prop will reach here
`n${oper.element}`,
resolvedHelper.needKey ? genExpression(key, context) : false,
propValue,
+ resolvedHelper.isSVG && 'true',
),
]
}
context: CodegenContext,
): CodeFragment[] {
const { helper } = context
+ const isSVG = isSVGTag(oper.tag)
const values = oper.props.map(props =>
Array.isArray(props)
? genLiteralObjectProps(props, context) // static and dynamic arg props
`n${oper.element}`,
genMulti(DELIMITERS_ARRAY, ...values),
oper.root && 'true',
+ isSVG && 'true',
),
]
}
// 1. SVG: always attribute
if (isSVG) {
- // TODO pass svg flag
- return helpers.setAttr
+ return extend({ isSVG: true }, helpers.setAttr)
}
if (modifier) {
element: number
props: IRProps[]
root: boolean
+ tag: string
}
export interface SetDynamicEventsIRNode extends BaseIRNode {
element: context.reference(),
props: dynamicArgs,
root: singleRoot,
+ tag,
},
getEffectIndex,
)
expect(el.getAttributeNS(xlinkNS, 'href')).toBe(null)
})
- test('textContent attributes /w svg', () => {
+ test('textContent attributes w/ svg', () => {
const el = document.createElementNS('http://www.w3.org/2000/svg', 'use')
patchProp(el, 'textContent', null, 'foo', 'svg')
expect(el.attributes.length).toBe(0)
* @internal
*/
export { svgNS, mathmlNS } from './nodeOps'
+/**
+ * @internal
+ */
+export { xlinkNS } from './modules/attrs'
} from '../../src/dom/prop'
import { setStyle } from '../../src/dom/prop'
import { VaporComponentInstance } from '../../src/component'
-import { ref, setCurrentInstance } from '@vue/runtime-dom'
+import { ref, setCurrentInstance, svgNS, xlinkNS } from '@vue/runtime-dom'
let removeComponentInstance = NOOP
beforeEach(() => {
key: string,
value: any,
el = element.cloneNode(true) as HTMLElement,
+ isSVG: boolean = false,
) {
- _setDynamicProp(el, key, value)
+ _setDynamicProp(el, key, value, isSVG)
return el
}
expect(res.textContent).toBe('foo')
})
- test.todo('should be able to set something on SVG')
+ test('set class w/ SVG', () => {
+ const el = document.createElementNS(svgNS, 'svg') as any
+ setDynamicProp('class', 'foo', el, true)
+ expect(el.getAttribute('class')).toBe('foo')
+ })
+
+ test('set class incremental w/ SVG', () => {
+ const el = document.createElementNS(svgNS, 'svg') as any
+ el.setAttribute('class', 'bar')
+ el.$root = true
+ setDynamicProp('class', 'foo', el, true)
+ expect(el.getAttribute('class')).toBe('bar foo')
+ })
+
+ test('set xlink attributes w/ SVG', () => {
+ const el = document.createElementNS(
+ 'http://www.w3.org/2000/svg',
+ 'use',
+ ) as any
+ setDynamicProp('xlink:href', 'a', el, true)
+ expect(el.getAttributeNS(xlinkNS, 'href')).toBe('a')
+ setDynamicProp('xlink:href', null, el, true)
+ expect(el.getAttributeNS(xlinkNS, 'href')).toBe(null)
+ })
+
+ test('set textContent attributes w/ SVG', () => {
+ const el = document.createElementNS(
+ 'http://www.w3.org/2000/svg',
+ 'use',
+ ) as any
+ setDynamicProp('textContent', 'foo', el, true)
+ expect(el.attributes.length).toBe(0)
+ expect(el.innerHTML).toBe('foo')
+ })
})
describe('setDynamicProps', () => {
patchStyle,
shouldSetAsProp,
warn,
+ xlinkNS,
} from '@vue/runtime-dom'
import {
type VaporComponentInstance,
$html?: string
$cls?: string
$sty?: NormalizedStyle | string | undefined
+ $svg?: boolean
value?: string
_value?: any
}
}
}
-export function setAttr(el: any, key: string, value: any): void {
+export function setAttr(
+ el: any,
+ key: string,
+ value: any,
+ isSVG: boolean = false,
+): void {
if (!isApplyingFallthroughProps && el.$root && hasFallthroughKey(key)) {
return
}
+ if (isSVG && key.startsWith('xlink:')) {
+ if (value == null) {
+ el.removeAttributeNS(xlinkNS, key.slice(6, key.length))
+ } else {
+ el.setAttributeNS(xlinkNS, key, value)
+ }
+ return
+ }
// special case for <input v-model type="checkbox"> with
// :true-value & :false-value
needRemove && el.removeAttribute(key)
}
-export function setClass(el: TargetElement, value: any): void {
+export function setClass(
+ el: TargetElement,
+ value: any,
+ isSVG: boolean = false,
+): void {
if (el.$root) {
setClassIncremental(el, value)
} else if ((value = normalizeClass(value)) !== el.$cls) {
- el.className = el.$cls = value
+ if (isSVG) {
+ el.setAttribute('class', (el.$cls = value))
+ } else {
+ el.className = el.$cls = value
+ }
}
}
}
}
-export function setDynamicProps(el: any, args: any[]): void {
+export function setDynamicProps(
+ el: any,
+ args: any[],
+ root?: boolean,
+ isSVG?: boolean,
+): void {
const props = args.length > 1 ? mergeProps(...args) : args[0]
const cacheKey = `$dprops${isApplyingFallthroughProps ? '$' : ''}`
const prevKeys = el[cacheKey] as string[]
+ if (root) el.$root = root
if (prevKeys) {
for (const key of prevKeys) {
if (!(key in props)) {
- setDynamicProp(el, key, null)
+ setDynamicProp(el, key, null, isSVG)
}
}
}
for (const key of (el[cacheKey] = Object.keys(props))) {
- setDynamicProp(el, key, props[key])
+ setDynamicProp(el, key, props[key], isSVG)
}
}
el: TargetElement,
key: string,
value: any,
+ isSVG: boolean = false,
): void {
- // TODO
- const isSVG = false
if (key === 'class') {
- setClass(el, value)
+ setClass(el, value, isSVG)
} else if (key === 'style') {
setStyle(el, value)
} else if (isOn(key)) {
setDOMProp(el, key, value)
}
} else {
- // TODO special case for <input v-model type="checkbox">
- setAttr(el, key, value)
+ setAttr(el, key, value, isSVG)
}
return value
}