Skip to content

Commit

Permalink
feat: dynamic remove keys
Browse files Browse the repository at this point in the history
  • Loading branch information
LittleSound committed Jun 7, 2024
1 parent cbd9a1b commit 6957448
Show file tree
Hide file tree
Showing 2 changed files with 128 additions and 24 deletions.
104 changes: 100 additions & 4 deletions packages/runtime-vapor/__tests__/componentSlots.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -348,15 +348,69 @@ describe('component: slots', () => {
expect(host.innerHTML).toBe('<div><h1>header</h1></div>')
})

test('dynamic slot props', async () => {
let props: any

const bindObj = ref<Record<string, any>>({ foo: 1, baz: 'qux' })
const Comp = defineComponent(() =>
createSlot('default', [() => bindObj.value]),
)
define(() =>
createComponent(
Comp,
{},
{ default: _props => ((props = _props), []) },
),
).render()

expect(props).toEqual({ foo: 1, baz: 'qux' })

bindObj.value.foo = 2
await nextTick()
expect(props).toEqual({ foo: 2, baz: 'qux' })

delete bindObj.value.baz
await nextTick()
expect(props).toEqual({ foo: 2 })
})

test('dynamic slot props with static slot props', async () => {
let props: any

const foo = ref(0)
const bindObj = ref<Record<string, any>>({ foo: 100, baz: 'qux' })
const Comp = defineComponent(() =>
createSlot('default', [{ foo: () => foo.value }, () => bindObj.value]),
)
define(() =>
createComponent(
Comp,
{},
{ default: _props => ((props = _props), []) },
),
).render()

expect(props).toEqual({ foo: 100, baz: 'qux' })

foo.value = 2
await nextTick()
expect(props).toEqual({ foo: 100, baz: 'qux' })

delete bindObj.value.foo
await nextTick()
expect(props).toEqual({ foo: 2, baz: 'qux' })
})

test('slot class binding should be merged', async () => {
let props: any

const enable = ref(true)
const className = ref('foo')
const classObj = ref({ bar: true })
const Comp = defineComponent(() =>
createSlot('default', [
{ class: () => 'foo' },
{ class: () => className.value },
() => ({ class: ['baz', 'qux'] }),
{ class: () => ({ bar: enable.value }) },
{ class: () => classObj.value },
]),
)
define(() =>
Expand All @@ -368,9 +422,51 @@ describe('component: slots', () => {
).render()

expect(props).toEqual({ class: 'foo baz qux bar' })
enable.value = false

classObj.value.bar = false
await nextTick()
expect(props).toEqual({ class: 'foo baz qux' })

className.value = ''
await nextTick()
expect(props).toEqual({ class: 'baz qux' })
})

test('slot style binding should be merged', async () => {
let props: any

const style = ref<any>({ fontSize: '12px' })
const Comp = defineComponent(() =>
createSlot('default', [
{ style: () => style.value },
() => ({ style: { width: '100px', color: 'blue' } }),
{ style: () => 'color: red' },
]),
)
define(() =>
createComponent(
Comp,
{},
{ default: _props => ((props = _props), []) },
),
).render()

expect(props).toEqual({
style: {
fontSize: '12px',
width: '100px',
color: 'red',
},
})

style.value = null
await nextTick()
expect(props).toEqual({
style: {
width: '100px',
color: 'red',
},
})
})

test('dynamic slot should be render correctly with binds', async () => {
Expand Down
48 changes: 28 additions & 20 deletions packages/runtime-vapor/src/componentSlots.ts
Original file line number Diff line number Diff line change
Expand Up @@ -159,53 +159,61 @@ export function createSlot(

return fragment

function withProps(fn: Slot | undefined): Slot | undefined {
function withProps<T extends (p: any) => any>(fn?: T) {
if (fn)
return (binds?: NormalizedRawProps) =>
return (binds?: NormalizedRawProps): ReturnType<T> =>
fn(binds && normalizeSlotProps(binds))
}
}

function normalizeSlotProps(rawPropsList: NormalizedRawProps) {
const ret = shallowReactive<Data>({})
const { length } = rawPropsList
const isNeedToMerge = length > 1
const dataCache = isNeedToMerge ? shallowReactive<Data[]>([]) : undefined
const mergings = length > 1 ? shallowReactive<Data[]>([]) : undefined
const result = shallowReactive<Data>({})

for (let i = 0; i < length; i++) {
const rawProps = rawPropsList[i]
if (isFunction(rawProps)) {
// dynamic props
renderEffect(() => {
const props = rawProps()
if (isNeedToMerge) {
dataCache![i] = props
if (mergings) {
mergings[i] = props
} else {
for (const key in props) {
ret[key] = props[key]
}
setDynamicProps(props)
}
})
} else {
const itemRet = isNeedToMerge
? (dataCache![i] = shallowReactive<Data>({}))
: ret
// static props
const props = mergings
? (mergings[i] = shallowReactive<Data>({}))
: result
for (const key in rawProps) {
const valueSource = rawProps[key]
renderEffect(() => {
itemRet[key] = valueSource()
props[key] = valueSource()
})
}
}
}

if (isNeedToMerge) {
if (mergings) {
renderEffect(() => {
const props = mergeProps(...dataCache!)
for (const key in props) {
ret[key] = props[key]
}
setDynamicProps(mergeProps(...mergings))
})
}

return ret
return result

function setDynamicProps(props: Data) {
const otherExistingKeys = new Set(Object.keys(result))
for (const key in props) {
result[key] = props[key]
otherExistingKeys.delete(key)
}
// delete other stale props
for (const key of otherExistingKeys) {
delete result[key]
}
}
}

0 comments on commit 6957448

Please sign in to comment.