-
Notifications
You must be signed in to change notification settings - Fork 258
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
feat: array of slots #599
feat: array of slots #599
Changes from 2 commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -18,7 +18,9 @@ import { | |
MethodOptions, | ||
AllowedComponentProps, | ||
ComponentCustomProps, | ||
ExtractDefaultPropTypes | ||
ExtractDefaultPropTypes, | ||
Component, | ||
VNode | ||
} from 'vue' | ||
|
||
import { config } from './config' | ||
|
@@ -27,7 +29,8 @@ import { | |
isFunctionalComponent, | ||
isHTML, | ||
isObjectComponent, | ||
mergeGlobalProperties | ||
mergeGlobalProperties, | ||
isObject | ||
} from './utils' | ||
import { processSlot } from './utils/compileSlots' | ||
import { createWrapper, VueWrapper } from './vueWrapper' | ||
|
@@ -261,6 +264,33 @@ export function mount( | |
to.appendChild(el) | ||
} | ||
|
||
function slotToFunction(slot: Slot): Function { | ||
if (typeof slot === 'object' && 'render' in slot && slot.render) { | ||
return slot.render | ||
} | ||
|
||
if (typeof slot === 'function') { | ||
return slot | ||
} | ||
|
||
if (typeof slot === 'object') { | ||
return () => slot | ||
} | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. nit: I would write a single |
||
|
||
if (typeof slot === 'string') { | ||
// if it is HTML we process and render it using h | ||
if (isHTML(slot)) { | ||
return (props: VNodeProps) => h(processSlot(slot), props) | ||
} | ||
// otherwise it is just a string so we just return it as-is | ||
else { | ||
return () => slot | ||
} | ||
} | ||
|
||
throw Error(`Invalid slot received.`) | ||
} | ||
|
||
// handle any slots passed via mounting options | ||
const slots = | ||
options?.slots && | ||
|
@@ -269,33 +299,24 @@ export function mount( | |
acc: { [key: string]: Function }, | ||
[name, slot]: [string, Slot] | ||
): { [key: string]: Function } => { | ||
// case of an SFC getting passed | ||
if (typeof slot === 'object' && 'render' in slot && slot.render) { | ||
acc[name] = slot.render | ||
return acc | ||
} | ||
if (Array.isArray(slot)) { | ||
const normalized = slot.reduce<Array<Function | VNode>>( | ||
(acc, curr) => { | ||
const toF = slotToFunction(curr) | ||
if (isObject(curr) && 'render' in curr) { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I have a hard time understanding the code. Maybe some comments would help. It feels like this check is somewhat redundant with what is done in There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Naming definitely can be improved. I also have a hard time understanding this, or why it works - the amount of ways you can create a Vue component (and as such, a lot) is a bit overwhelming. I can think of at least 6 different ways:
Throw in scoped slots, and you end up with this. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. DOne |
||
const rendered = h(toF as any) | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not entirely clear why I need to pass There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is indeed weird. I don't know enough about slot internal behavior to help sadly. |
||
return acc.concat(rendered) | ||
} | ||
return acc.concat(toF()) | ||
}, | ||
[] | ||
) | ||
acc[name] = () => normalized | ||
|
||
if (typeof slot === 'function') { | ||
acc[name] = slot | ||
return acc | ||
} | ||
|
||
if (typeof slot === 'object') { | ||
acc[name] = () => slot | ||
return acc | ||
} | ||
|
||
if (typeof slot === 'string') { | ||
// if it is HTML we process and render it using h | ||
if (isHTML(slot)) { | ||
acc[name] = (props: VNodeProps) => h(processSlot(slot), props) | ||
} | ||
// otherwise it is just a string so we just return it as-is | ||
else { | ||
acc[name] = () => slot | ||
} | ||
return acc | ||
} | ||
acc[name] = slotToFunction(slot) | ||
|
||
return acc | ||
}, | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,5 +1,5 @@ | ||
<template> | ||
<p> | ||
<p id="with-props"> | ||
{{ msg }} | ||
</p> | ||
</template> | ||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
nit: Function is rarely used as a type. We usually prefer
() => string
for example.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ok, I will change this. I suppose it'll be like
type SlotLike = () => string | (() => string) | { render: () => string }
or something to that meaning.There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The type here is very complicated, it wants me to specify some very large union of public component instance, functional components... I just removed the entire return type and will let TS figure it out.