diff --git a/doc/reference/slots.md b/doc/reference/slots.md
index 69be4c14..7e12e450 100644
--- a/doc/reference/slots.md
+++ b/doc/reference/slots.md
@@ -87,8 +87,8 @@ grandparent of the slot content).
## Default Slot
-The first element inside the component which is not a named slot will
-be considered the `default` slot. For example:
+All elements inside the component which are not a named slot will be treated as
+part of the content of the `default` slot. For example:
```xml
@@ -110,6 +110,7 @@ One can mix default slot and named slots:
default content
content for footer slot here
+
```
@@ -211,28 +212,37 @@ use this `Notebook` component:
## Slot scopes
-For other kind of advanced use cases, the content of a slot may depends on some
-specific information specific to the generic component. This is the opposite
-of the slot params.
+For other kinds of advanced use cases, the content of a slot may depends on some
+information specific to the generic component. This is the opposite of the slot
+params.
To solve this kind of problems, one can use the `t-slot-scope` directive along
with the `t-set-slot`. This defines the name of a variable that can access
everything given by the child component:
```xml
-
+
content
-
+
```
And the child component that includes the slot can provide values like this:
```xml
-
-
-
+
+```
+
+In the case of the default slot, you may declare the slot scope directly on the
+component itself:
+
+```xml
+
+ content
+
+
+
```
diff --git a/src/compiler/parser.ts b/src/compiler/parser.ts
index 121d4380..2f99c76a 100644
--- a/src/compiler/parser.ts
+++ b/src/compiler/parser.ts
@@ -677,6 +677,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
const dynamicProps = node.getAttribute("t-props");
node.removeAttribute("t-props");
+ const defaultSlotScope = node.getAttribute("t-slot-scope");
+ node.removeAttribute("t-slot-scope");
+
const props: ASTComponent["props"] = {};
for (let name of node.getAttributeNames()) {
const value = node.getAttribute(name)!;
@@ -742,6 +745,9 @@ function parseComponent(node: Element, ctx: ParsingContext): AST | null {
const defaultContent = parseChildNodes(clone, ctx);
if (defaultContent) {
slots.default = { content: defaultContent };
+ if (defaultSlotScope) {
+ slots.default.scope = defaultSlotScope;
+ }
}
}
return { type: ASTType.TComponent, name, isDynamic, dynamicProps, props, slots };
diff --git a/tests/components/__snapshots__/slots.test.ts.snap b/tests/components/__snapshots__/slots.test.ts.snap
index 279efed5..de520935 100644
--- a/tests/components/__snapshots__/slots.test.ts.snap
+++ b/tests/components/__snapshots__/slots.test.ts.snap
@@ -424,6 +424,42 @@ exports[`slots default slot next to named slot, with default content 2`] = `
}"
`;
+exports[`slots default slot with slot scope: shorthand syntax 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+
+ function slot1(ctx, node, key = \\"\\") {
+ let b2,b3;
+ if (ctx['slotScope'].bool) {
+ b2 = text(\`some text\`);
+ } else {
+ b3 = text(\`other text\`);
+ }
+ return multi([b2, b3]);
+ }
+
+ return function template(ctx, node, key = \\"\\") {
+ return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
+ }
+}"
+`;
+
+exports[`slots default slot with slot scope: shorthand syntax 2`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+ let { callSlot } = helpers;
+
+ let block1 = createBlock(\`\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
+ return block1([], [b2]);
+ }
+}"
+`;
+
exports[`slots default slot work with text nodes (variation) 1`] = `
"function anonymous(bdom, helpers
) {
@@ -1137,44 +1173,6 @@ exports[`slots simple default slot 2`] = `
exports[`slots simple default slot with params 1`] = `
"function anonymous(bdom, helpers
-) {
- let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
- let { capture } = helpers;
-
- function slot1(ctx, node, key = \\"\\") {
- let b2,b3;
- if (ctx['slotScope'].bool) {
- b2 = text(\`some text\`);
- } else {
- b3 = text(\`other text\`);
- }
- return multi([b2, b3]);
- }
-
- return function template(ctx, node, key = \\"\\") {
- const ctx1 = capture(ctx);
- return component(\`Child\`, {slots: {'default': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
- }
-}"
-`;
-
-exports[`slots simple default slot with params 2`] = `
-"function anonymous(bdom, helpers
-) {
- let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
- let { callSlot } = helpers;
-
- let block1 = createBlock(\`\`);
-
- return function template(ctx, node, key = \\"\\") {
- let b2 = callSlot(ctx, node, key, 'default', false, {bool: ctx['state'].bool});
- return block1([], [b2]);
- }
-}"
-`;
-
-exports[`slots simple default slot with params 3`] = `
-"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
@@ -1194,7 +1192,7 @@ exports[`slots simple default slot with params 3`] = `
}"
`;
-exports[`slots simple default slot with params 4`] = `
+exports[`slots simple default slot with params 2`] = `
"function anonymous(bdom, helpers
) {
let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
@@ -1236,6 +1234,44 @@ exports[`slots simple default slot, variation 2`] = `
}"
`;
+exports[`slots simple slot with slot scope 1`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+ let { capture } = helpers;
+
+ function slot1(ctx, node, key = \\"\\") {
+ let b2,b3;
+ if (ctx['slotScope'].bool) {
+ b2 = text(\`some text\`);
+ } else {
+ b3 = text(\`other text\`);
+ }
+ return multi([b2, b3]);
+ }
+
+ return function template(ctx, node, key = \\"\\") {
+ const ctx1 = capture(ctx);
+ return component(\`Child\`, {slots: {'slotName': {__render: slot1, __ctx: ctx1, __scope: \\"slotScope\\"}}}, key + \`__1\`, node, ctx);
+ }
+}"
+`;
+
+exports[`slots simple slot with slot scope 2`] = `
+"function anonymous(bdom, helpers
+) {
+ let { text, createBlock, list, multi, html, toggler, component, comment } = bdom;
+ let { callSlot } = helpers;
+
+ let block1 = createBlock(\`\`);
+
+ return function template(ctx, node, key = \\"\\") {
+ let b2 = callSlot(ctx, node, key, 'slotName', false, {bool: ctx['state'].bool});
+ return block1([], [b2]);
+ }
+}"
+`;
+
exports[`slots slot and (inline) t-call 1`] = `
"function anonymous(bdom, helpers
) {
diff --git a/tests/components/slots.test.ts b/tests/components/slots.test.ts
index dd151ad5..c5324d72 100644
--- a/tests/components/slots.test.ts
+++ b/tests/components/slots.test.ts
@@ -45,7 +45,36 @@ describe("slots", () => {
expect(fixture.innerHTML).toBe("some text");
});
- test("simple default slot with params", async () => {
+ test("simple slot with slot scope", async () => {
+ let child: any;
+ class Child extends Component {
+ static template = xml``;
+ state = useState({ bool: true });
+ setup() {
+ child = this;
+ }
+ }
+
+ class Parent extends Component {
+ static template = xml`
+
+
+ some text
+ other text
+
+ `;
+ static components = { Child };
+ }
+
+ await mount(Parent, fixture);
+ expect(fixture.innerHTML).toBe("some text");
+
+ child.state.bool = false;
+ await nextTick();
+ expect(fixture.innerHTML).toBe("other text");
+ });
+
+ test("default slot with slot scope: shorthand syntax", async () => {
let child: any;
class Child extends Component {
static template = xml``;
@@ -57,11 +86,9 @@ describe("slots", () => {
class Parent extends Component {
static template = xml`
-
-
- some text
- other text
-
+
+ some text
+ other text
`;
static components = { Child };
}