Skip to content

Commit

Permalink
feat: new scoped slot syntax implementation update per rfc
Browse files Browse the repository at this point in the history
  • Loading branch information
yyx990803 committed Jan 15, 2019
1 parent aef5b4e commit c5c354d
Show file tree
Hide file tree
Showing 3 changed files with 118 additions and 12 deletions.
14 changes: 14 additions & 0 deletions src/compiler/helpers.js
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,20 @@ export function getAndRemoveAttr (
return val
}

export function getAndRemoveAttrByRegex (
el: ASTElement,
name: RegExp
) {
const list = el.attrsList
for (let i = 0, l = list.length; i < l; i++) {
const attr = list[i]
if (name.test(attr.name)) {
list.splice(i, 1)
return attr
}
}
}

function rangeSetItem (
item: any,
range?: { start?: number, end?: number }
Expand Down
56 changes: 44 additions & 12 deletions src/compiler/parser/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,8 @@ import {
getBindingAttr,
getAndRemoveAttr,
getRawBindingAttr,
pluckModuleFunction
pluckModuleFunction,
getAndRemoveAttrByRegex
} from '../helpers'

export const onRE = /^@|^v-on:/
Expand All @@ -31,6 +32,8 @@ export const bindRE = /^:|^\.|^v-bind:/
const propBindRE = /^\./
const modifierRE = /\.[^.]+/g

const scopedSlotShorthandRE = /^:?\(.*\)$/

const lineBreakRE = /[\r\n]/
const whitespaceRE = /\s+/g

Expand Down Expand Up @@ -568,9 +571,20 @@ function processSlotContent (el) {
getAndRemoveAttr(el, 'slot-scope') ||
// new in 2.6: slot-props and its shorthand works the same as slot-scope
// when used on <template> containers
getAndRemoveAttr(el, 'slot-props') ||
getAndRemoveAttr(el, '()')
getAndRemoveAttr(el, 'slot-props')
)
// 2.6 shorthand syntax
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
if (shorthand) {
if (process.env.NODE_ENV !== 'production' && el.slotScope) {
warn(
`Unexpected mixed usage of different slot syntaxes.`,
el
)
}
el.slotTarget = getScopedSlotShorthandName(shorthand)
el.slotScope = shorthand.value
}
} else if ((slotScope = getAndRemoveAttr(el, 'slot-scope'))) {
/* istanbul ignore if */
if (process.env.NODE_ENV !== 'production' && el.attrsMap['v-for']) {
Expand All @@ -585,19 +599,29 @@ function processSlotContent (el) {
el.slotScope = slotScope
} else {
// 2.6: slot-props on component, denotes default slot
slotScope = getAndRemoveAttr(el, 'slot-props') || getAndRemoveAttr(el, '()')
if (slotScope) {
if (process.env.NODE_ENV !== 'production' && !maybeComponent(el)) {
warn(
`slot-props cannot be used on non-component elements.`,
el.rawAttrsMap['slot-props'] || el.rawAttrsMap['()']
)
slotScope = getAndRemoveAttr(el, 'slot-props')
const shorthand = getAndRemoveAttrByRegex(el, scopedSlotShorthandRE)
if (slotScope || shorthand) {
if (process.env.NODE_ENV !== 'production') {
if (!maybeComponent(el)) {
warn(
`slot-props cannot be used on non-component elements.`,
el.rawAttrsMap['slot-props'] || el.rawAttrsMap['()']
)
}
if (slotScope && shorthand) {
warn(
`Unexpected mixed usage of different slot syntaxes.`,
el
)
}
}
// add the component's children to its default slot
const slots = el.scopedSlots || (el.scopedSlots = {})
const slotContainer = slots[`"default"`] = createASTElement('template', [], el)
const target = shorthand ? getScopedSlotShorthandName(shorthand) : `"default"`
const slotContainer = slots[target] = createASTElement('template', [], el)
slotContainer.children = el.children
slotContainer.slotScope = slotScope
slotContainer.slotScope = shorthand ? shorthand.value : slotScope
// remove children as they are returned from scopedSlots now
el.children = []
// mark el non-plain so data gets generated
Expand All @@ -617,6 +641,14 @@ function processSlotContent (el) {
}
}

function getScopedSlotShorthandName ({ name }) {
return name.charAt(0) === ':'
// dynamic :(name)
? name.slice(2, -1) || `"default"`
// static (name)
: `"${name.slice(1, -1) || `default`}"`
}

// handle <slot/> outlets
function processSlotOutlet (el) {
if (el.tag === 'slot') {
Expand Down
60 changes: 60 additions & 0 deletions test/unit/features/component/component-scoped-slot.spec.js
Original file line number Diff line number Diff line change
Expand Up @@ -731,5 +731,65 @@ describe('Component scoped slot', () => {
// run tests for both full syntax and shorthand
runSuite('slot-props')
runSuite('()')

it('shorthand named slots', () => {
const vm = new Vue({
template: `
<foo ()="foo">
{{ foo }}
<template (one)="one">
{{ one }}
</template>
<template (two)="two">
{{ two }}
</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo default from foo one from foo two`)
})

it('shorthand without scope variable', () => {
const vm = new Vue({
template: `
<foo>
<template (one)>one</template>
<template (two)>two</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`onetwo`)
})

it('shorthand named slots on root', () => {
const vm = new Vue({
template: `
<foo (one)="one">
{{ one }}
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one`)
})

it('dynamic shorthand', () => {
const vm = new Vue({
data: {
a: 'one',
b: 'two'
},
template: `
<foo>
<template :(a)="one">{{ one }} </template>
<template :(b)="two">{{ two }}</template>
</foo>
`,
components: { Foo }
}).$mount()
expect(vm.$el.innerHTML.replace(/\s+/g, ' ')).toMatch(`from foo one from foo two`)
})
})
})

0 comments on commit c5c354d

Please sign in to comment.