refactor: adjust event options handling to be JSX friendly

This commit is contained in:
Evan You 2020-07-14 13:20:59 -04:00
parent e3d30ba26f
commit 00ab9e2e85
6 changed files with 33 additions and 37 deletions

View File

@ -77,13 +77,13 @@ describe('compiler-dom: transform v-on', () => {
it('should support multiple modifiers and event options w/ prefixIdentifiers: true', () => { it('should support multiple modifiers and event options w/ prefixIdentifiers: true', () => {
const { const {
props: [prop] props: [prop]
} = parseWithVOn(`<div @click.stop.capture.passive="test"/>`, { } = parseWithVOn(`<div @click.stop.capture.once="test"/>`, {
prefixIdentifiers: true prefixIdentifiers: true
}) })
expect(prop).toMatchObject({ expect(prop).toMatchObject({
type: NodeTypes.JS_PROPERTY, type: NodeTypes.JS_PROPERTY,
key: { key: {
content: `onClick.capture.passive` content: `onClickCaptureOnce`
}, },
value: { value: {
callee: V_ON_WITH_MODIFIERS, callee: V_ON_WITH_MODIFIERS,
@ -101,7 +101,7 @@ describe('compiler-dom: transform v-on', () => {
expect(prop).toMatchObject({ expect(prop).toMatchObject({
type: NodeTypes.JS_PROPERTY, type: NodeTypes.JS_PROPERTY,
key: { key: {
content: `onKeydown.capture` content: `onKeydownCapture`
}, },
value: { value: {
callee: V_ON_WITH_KEYS, callee: V_ON_WITH_KEYS,
@ -274,7 +274,7 @@ describe('compiler-dom: transform v-on', () => {
) )
expect(prop).toMatchObject({ expect(prop).toMatchObject({
key: { key: {
content: `onKeyup.capture` content: `onKeyupCapture`
}, },
value: { value: {
type: NodeTypes.JS_CACHE_EXPRESSION, type: NodeTypes.JS_CACHE_EXPRESSION,

View File

@ -11,7 +11,7 @@ import {
isStaticExp isStaticExp
} from '@vue/compiler-core' } from '@vue/compiler-core'
import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers' import { V_ON_WITH_MODIFIERS, V_ON_WITH_KEYS } from '../runtimeHelpers'
import { makeMap } from '@vue/shared' import { makeMap, capitalize } from '@vue/shared'
const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`) const isEventOptionModifier = /*#__PURE__*/ makeMap(`passive,once,capture`)
const isNonKeyModifier = /*#__PURE__*/ makeMap( const isNonKeyModifier = /*#__PURE__*/ makeMap(
@ -38,7 +38,8 @@ const resolveModifiers = (key: ExpressionNode, modifiers: string[]) => {
const modifier = modifiers[i] const modifier = modifiers[i]
if (isEventOptionModifier(modifier)) { if (isEventOptionModifier(modifier)) {
// eventOptionModifiers: modifiers for addEventListener() options, e.g. .passive & .capture // eventOptionModifiers: modifiers for addEventListener() options,
// e.g. .passive & .capture
eventOptionModifiers.push(modifier) eventOptionModifiers.push(modifier)
} else { } else {
// runtimeModifiers: modifiers that needs runtime guards // runtimeModifiers: modifiers that needs runtime guards
@ -125,16 +126,10 @@ export const transformOn: DirectiveTransform = (dir, node, context) => {
} }
if (eventOptionModifiers.length) { if (eventOptionModifiers.length) {
const modifierPostfix = eventOptionModifiers.map(capitalize).join('')
key = isStaticExp(key) key = isStaticExp(key)
? createSimpleExpression( ? createSimpleExpression(`${key.content}${modifierPostfix}`, true)
`${key.content}.${eventOptionModifiers.join(`.`)}`, : createCompoundExpression([`(`, key, `) + "${modifierPostfix}"`])
true
)
: createCompoundExpression([
`(`,
key,
`) + ".${eventOptionModifiers.join(`.`)}"`
])
} }
return { return {

View File

@ -174,7 +174,7 @@ describe('component: emit', () => {
const fn = jest.fn() const fn = jest.fn()
render( render(
h(Foo, { h(Foo, {
'onFoo.once': fn onFooOnce: fn
}), }),
nodeOps.createElement('div') nodeOps.createElement('div')
) )
@ -213,8 +213,8 @@ describe('component: emit', () => {
test('.once listeners', () => { test('.once listeners', () => {
const def2 = { emits: { click: null } } const def2 = { emits: { click: null } }
expect(isEmitListener(def2, 'onClick.once')).toBe(true) expect(isEmitListener(def2, 'onClickOnce')).toBe(true)
expect(isEmitListener(def2, 'onclick.once')).toBe(false) expect(isEmitListener(def2, 'onclickOnce')).toBe(false)
}) })
}) })
}) })

View File

@ -76,7 +76,7 @@ export function emit(
handler = props[handlerName] handler = props[handlerName]
} }
if (!handler) { if (!handler) {
handler = props[handlerName + `.once`] handler = props[handlerName + `Once`]
if (!instance.emitted) { if (!instance.emitted) {
;(instance.emitted = {} as Record<string, boolean>)[handlerName] = true ;(instance.emitted = {} as Record<string, boolean>)[handlerName] = true
} else if (instance.emitted[handlerName]) { } else if (instance.emitted[handlerName]) {
@ -136,7 +136,7 @@ export function isEmitListener(comp: Component, key: string): boolean {
if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) { if (!isOn(key) || !(emits = normalizeEmitsOptions(comp))) {
return false return false
} }
key = key.replace(/\.once$/, '') key = key.replace(/Once$/, '')
return ( return (
hasOwn(emits, key[2].toLowerCase() + key.slice(3)) || hasOwn(emits, key[2].toLowerCase() + key.slice(3)) ||
hasOwn(emits, key.slice(2)) hasOwn(emits, key.slice(2))

View File

@ -61,7 +61,7 @@ describe(`runtime-dom: events patching`, () => {
const el = document.createElement('div') const el = document.createElement('div')
const event = new Event('click') const event = new Event('click')
const fn = jest.fn() const fn = jest.fn()
patchProp(el, 'onClick.once.capture', null, fn) patchProp(el, 'onClickOnceCapture', null, fn)
el.dispatchEvent(event) el.dispatchEvent(event)
await timeout() await timeout()
el.dispatchEvent(event) el.dispatchEvent(event)
@ -73,13 +73,17 @@ describe(`runtime-dom: events patching`, () => {
const el = document.createElement('div') const el = document.createElement('div')
const event = new Event('click') const event = new Event('click')
const fn = jest.fn() const fn = jest.fn()
patchProp(el, 'onClick.capture', null, fn) patchProp(el, 'onClickCapture', null, fn)
patchProp(el, 'onClick.capture', fn, null) el.dispatchEvent(event)
await timeout()
expect(fn).toHaveBeenCalledTimes(1)
patchProp(el, 'onClickCapture', fn, null)
el.dispatchEvent(event) el.dispatchEvent(event)
await timeout() await timeout()
el.dispatchEvent(event) el.dispatchEvent(event)
await timeout() await timeout()
expect(fn).not.toHaveBeenCalled() expect(fn).toHaveBeenCalledTimes(1)
}) })
it('should support native onclick', async () => { it('should support native onclick', async () => {

View File

@ -82,23 +82,20 @@ export function patchEvent(
} }
} }
const optionsModifierRE = /\.(once|passive|capture)\b/g const optionsModifierRE = /(?:Once|Passive|Capture)$/
function parseName(name: string): [string, EventListenerOptions | undefined] { function parseName(name: string): [string, EventListenerOptions | undefined] {
name = name.slice(2).toLowerCase() let options: EventListenerOptions | undefined
if (optionsModifierRE.test(name)) { if (optionsModifierRE.test(name)) {
const options: EventListenerOptions = {} options = {}
name = name.replace( let m
optionsModifierRE, while ((m = name.match(optionsModifierRE))) {
(_, key: keyof EventListenerOptions) => { name = name.slice(0, name.length - m[0].length)
options[key] = true ;(options as any)[m[0].toLowerCase()] = true
return '' options
} }
)
return [name, options]
} else {
return [name, undefined]
} }
return [name.slice(2).toLowerCase(), options]
} }
function createInvoker( function createInvoker(