From 7ea6a2994a2a35e96e47d843250cc61c1314ec39 Mon Sep 17 00:00:00 2001 From: Ben McCann <322311+benmccann@users.noreply.github.com> Date: Mon, 26 Oct 2020 10:25:03 -0700 Subject: [PATCH 01/13] Curly braces linting fixes (#5585) --- src/compiler/compile/Component.ts | 21 ++++++++++++------- src/compiler/compile/nodes/Binding.ts | 20 +++++++++++------- .../render_dom/wrappers/Element/Attribute.ts | 3 ++- src/compiler/parse/index.ts | 3 ++- src/compiler/parse/read/script.ts | 10 +++++---- src/compiler/parse/state/mustache.ts | 10 +++++---- src/compiler/utils/fuzzymatch.ts | 3 ++- src/runtime/motion/spring.ts | 6 ++++-- test/hydration/index.ts | 8 ++++--- 9 files changed, 52 insertions(+), 32 deletions(-) diff --git a/src/compiler/compile/Component.ts b/src/compiler/compile/Component.ts index c16862165c..d2542c9830 100644 --- a/src/compiler/compile/Component.ts +++ b/src/compiler/compile/Component.ts @@ -521,8 +521,7 @@ export default class Component { if (this.hoistable_nodes.has(node)) return false; if (this.reactive_declaration_nodes.has(node)) return false; if (node.type === 'ImportDeclaration') return false; - if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) - return false; + if (node.type === 'ExportDeclaration' && node.specifiers.length > 0) return false; return true; }); } @@ -1038,8 +1037,9 @@ export default class Component { this.vars.find( variable => variable.name === name && variable.module ) - ) + ) { return false; + } return true; }); @@ -1288,8 +1288,9 @@ export default class Component { declaration.dependencies.forEach(name => { if (declaration.assignees.has(name)) return; const earlier_declarations = lookup.get(name); - if (earlier_declarations) + if (earlier_declarations) { earlier_declarations.forEach(add_declaration); + } }); this.reactive_declarations.push(declaration); @@ -1319,8 +1320,9 @@ export default class Component { if (globals.has(name) && node.type !== 'InlineComponent') return; let message = `'${name}' is not defined`; - if (!this.ast.instance) + if (!this.ast.instance) { message += `. Consider adding a + +

name: {name}

+

$$props: {JSON.stringify($$props)}

+

$$restProps: {JSON.stringify($$restProps)}

+ diff --git a/test/custom-elements/samples/$$props/test.js b/test/custom-elements/samples/$$props/test.js new file mode 100644 index 0000000000..94cad86577 --- /dev/null +++ b/test/custom-elements/samples/$$props/test.js @@ -0,0 +1,13 @@ +import * as assert from 'assert'; +import './main.svelte'; + +export default function (target) { + target.innerHTML = ''; + const el = target.querySelector('custom-element'); + + assert.htmlEqual(el.shadowRoot.innerHTML, ` +

name: world

+

$$props: {"name":"world","answer":"42","test":"svelte"}

+

$$restProps: {"answer":"42","test":"svelte"}

+ `); +} diff --git a/test/js/samples/css-shadow-dom-keyframes/expected.js b/test/js/samples/css-shadow-dom-keyframes/expected.js index a0a0ebe021..82a39e5924 100644 --- a/test/js/samples/css-shadow-dom-keyframes/expected.js +++ b/test/js/samples/css-shadow-dom-keyframes/expected.js @@ -1,6 +1,7 @@ /* generated by Svelte vX.Y.Z */ import { SvelteElement, + attribute_to_object, detach, element, init, @@ -34,7 +35,18 @@ class Component extends SvelteElement { constructor(options) { super(); this.shadowRoot.innerHTML = ``; - init(this, { target: this.shadowRoot }, null, create_fragment, safe_not_equal, {}); + + init( + this, + { + target: this.shadowRoot, + props: attribute_to_object(this.attributes) + }, + null, + create_fragment, + safe_not_equal, + {} + ); if (options) { if (options.target) { From 5d7ffdb8a7231a772da6b259a8f2c78a4007c53b Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 30 Oct 2020 03:15:54 +0800 Subject: [PATCH 07/13] fix function slot props based on context (#5607) --- CHANGELOG.md | 1 + .../compile/nodes/shared/Expression.ts | 65 ++++++++++++++----- .../compile/render_dom/wrappers/EachBlock.ts | 27 ++++---- .../Nested.svelte | 14 ++++ .../_config.js | 36 ++++++++++ .../main.svelte | 8 +++ .../Nested.svelte | 11 ++++ .../_config.js | 19 ++++++ .../main.svelte | 8 +++ .../Inner.svelte | 9 +++ .../Nested.svelte | 8 +++ .../_config.js | 19 ++++++ .../main.svelte | 8 +++ 13 files changed, 202 insertions(+), 31 deletions(-) create mode 100644 test/runtime/samples/component-slot-context-props-each-nested/Nested.svelte create mode 100644 test/runtime/samples/component-slot-context-props-each-nested/_config.js create mode 100644 test/runtime/samples/component-slot-context-props-each-nested/main.svelte create mode 100644 test/runtime/samples/component-slot-context-props-each/Nested.svelte create mode 100644 test/runtime/samples/component-slot-context-props-each/_config.js create mode 100644 test/runtime/samples/component-slot-context-props-each/main.svelte create mode 100644 test/runtime/samples/component-slot-context-props-let/Inner.svelte create mode 100644 test/runtime/samples/component-slot-context-props-let/Nested.svelte create mode 100644 test/runtime/samples/component-slot-context-props-let/_config.js create mode 100644 test/runtime/samples/component-slot-context-props-let/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index cfff198368..4afd7f20e0 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,7 @@ ## Unreleased * Fix `$$props` and `$$restProps` when compiling to a custom element ([#5482](https://github.com/sveltejs/svelte/issues/5482)) +* Fix function calls in `` props that use contextual values ([#5565](https://github.com/sveltejs/svelte/issues/5565)) * Add `Element` and `Node` to known globals ([#5586](https://github.com/sveltejs/svelte/issues/5586)) ## 3.29.4 diff --git a/src/compiler/compile/nodes/shared/Expression.ts b/src/compiler/compile/nodes/shared/Expression.ts index 027e962e29..ef134f4359 100644 --- a/src/compiler/compile/nodes/shared/Expression.ts +++ b/src/compiler/compile/nodes/shared/Expression.ts @@ -4,7 +4,6 @@ import is_reference from 'is-reference'; import flatten_reference from '../../utils/flatten_reference'; import { create_scopes, Scope, extract_names } from '../../utils/scope'; import { sanitize } from '../../../utils/names'; -import Wrapper from '../../render_dom/wrappers/shared/Wrapper'; import TemplateScope from './TemplateScope'; import get_object from '../../utils/get_object'; import Block from '../../render_dom/Block'; @@ -12,12 +11,12 @@ import is_dynamic from '../../render_dom/wrappers/shared/is_dynamic'; import { b } from 'code-red'; import { invalidate } from '../../render_dom/invalidate'; import { Node, FunctionExpression, Identifier } from 'estree'; -import { TemplateNode } from '../../../interfaces'; +import { INode } from '../interfaces'; import { is_reserved_keyword } from '../../utils/reserved_keywords'; import replace_object from '../../utils/replace_object'; import EachBlock from '../EachBlock'; -type Owner = Wrapper | TemplateNode; +type Owner = INode; export default class Expression { type: 'Expression' = 'Expression'; @@ -37,7 +36,6 @@ export default class Expression { manipulated: Node; - // todo: owner type constructor(component: Component, owner: Owner, template_scope: TemplateScope, info, lazy?: boolean) { // TODO revert to direct property access in prod? Object.defineProperties(this, { @@ -276,10 +274,12 @@ export default class Expression { else { // we need a combo block/init recipe const deps = Array.from(contextual_dependencies); + const function_expression = node as FunctionExpression; - (node as FunctionExpression).params = [ + const has_args = function_expression.params.length > 0; + function_expression.params = [ ...deps.map(name => ({ type: 'Identifier', name } as Identifier)), - ...(node as FunctionExpression).params + ...function_expression.params ]; const context_args = deps.map(name => block.renderer.reference(name)); @@ -291,18 +291,49 @@ export default class Expression { this.replace(id as any); - if ((node as FunctionExpression).params.length > 0) { - declarations.push(b` - function ${id}(...args) { - return ${callee}(${context_args}, ...args); - } - `); + const func_declaration = has_args + ? b`function ${id}(...args) { + return ${callee}(${context_args}, ...args); + }` + : b`function ${id}() { + return ${callee}(${context_args}); + }`; + + if (owner.type === 'Attribute' && owner.parent.name === 'slot') { + const dep_scopes = new Set(deps.map(name => template_scope.get_owner(name))); + // find the nearest scopes + let node: INode = owner.parent; + while (node && !dep_scopes.has(node)) { + node = node.parent; + } + + const func_expression = func_declaration[0]; + + if (node.type === 'InlineComponent') { + // + this.replace(func_expression); + } else { + // {#each}, {#await} + const func_id = component.get_unique_name(id.name + '_func'); + block.renderer.add_to_context(func_id.name, true); + // rename #ctx -> child_ctx; + walk(func_expression, { + enter(node) { + if (node.type === 'Identifier' && node.name === '#ctx') { + node.name = 'child_ctx'; + } + } + }); + // add to get_xxx_context + // child_ctx[x] = function () { ... } + (template_scope.get_owner(deps[0]) as EachBlock).contexts.push({ + key: func_id, + modifier: () => func_expression + }); + this.replace(block.renderer.reference(func_id)); + } } else { - declarations.push(b` - function ${id}() { - return ${callee}(${context_args}); - } - `); + declarations.push(func_declaration); } } diff --git a/src/compiler/compile/render_dom/wrappers/EachBlock.ts b/src/compiler/compile/render_dom/wrappers/EachBlock.ts index b31d2bcba7..a2c20e0074 100644 --- a/src/compiler/compile/render_dom/wrappers/EachBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/EachBlock.ts @@ -196,11 +196,6 @@ export default class EachBlockWrapper extends Wrapper { ? !this.next.is_dom_node() : !parent_node || !this.parent.is_dom_node(); - this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`); - - if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`); - if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`); - const snippet = this.node.expression.manipulate(block); block.chunks.init.push(b`let ${this.vars.each_block_value} = ${snippet};`); @@ -208,15 +203,6 @@ export default class EachBlockWrapper extends Wrapper { block.chunks.init.push(b`@validate_each_argument(${this.vars.each_block_value});`); } - // TODO which is better — Object.create(array) or array.slice()? - renderer.blocks.push(b` - function ${this.vars.get_each_context}(#ctx, list, i) { - const child_ctx = #ctx.slice(); - ${this.context_props} - return child_ctx; - } - `); - const initial_anchor_node: Identifier = { type: 'Identifier', name: parent_node ? 'null' : '#anchor' }; const initial_mount_node: Identifier = parent_node || { type: 'Identifier', name: '#target' }; const update_anchor_node = needs_anchor @@ -360,6 +346,19 @@ export default class EachBlockWrapper extends Wrapper { if (this.else) { this.else.fragment.render(this.else.block, null, x`#nodes` as Identifier); } + + this.context_props = this.node.contexts.map(prop => b`child_ctx[${renderer.context_lookup.get(prop.key.name).index}] = ${prop.modifier(x`list[i]`)};`); + + if (this.node.has_binding) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.vars.each_block_value.name).index}] = list;`); + if (this.node.has_binding || this.node.has_index_binding || this.node.index) this.context_props.push(b`child_ctx[${renderer.context_lookup.get(this.index_name.name).index}] = i;`); + // TODO which is better — Object.create(array) or array.slice()? + renderer.blocks.push(b` + function ${this.vars.get_each_context}(#ctx, list, i) { + const child_ctx = #ctx.slice(); + ${this.context_props} + return child_ctx; + } + `); } render_keyed({ diff --git a/test/runtime/samples/component-slot-context-props-each-nested/Nested.svelte b/test/runtime/samples/component-slot-context-props-each-nested/Nested.svelte new file mode 100644 index 0000000000..fab3ae1890 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each-nested/Nested.svelte @@ -0,0 +1,14 @@ + + +{#each items as item (item)} + {#each keys as key (key)} + setKey(key, value, item)} /> + {/each} +{/each} \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-each-nested/_config.js b/test/runtime/samples/component-slot-context-props-each-nested/_config.js new file mode 100644 index 0000000000..869cc555b1 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each-nested/_config.js @@ -0,0 +1,36 @@ +export default { + html: ` + + + + + `, + async test({ assert, target, window, component }) { + const [btn1, btn2, btn3, btn4] = target.querySelectorAll('button'); + const click = new window.MouseEvent('click'); + + await btn1.dispatchEvent(click); + assert.deepEqual(component.log, ['setKey(a, value-a-c, c)']); + + await btn2.dispatchEvent(click); + assert.deepEqual(component.log, [ + 'setKey(a, value-a-c, c)', + 'setKey(b, value-b-c, c)' + ]); + + await btn3.dispatchEvent(click); + assert.deepEqual(component.log, [ + 'setKey(a, value-a-c, c)', + 'setKey(b, value-b-c, c)', + 'setKey(a, value-a-d, d)' + ]); + + await btn4.dispatchEvent(click); + assert.deepEqual(component.log, [ + 'setKey(a, value-a-c, c)', + 'setKey(b, value-b-c, c)', + 'setKey(a, value-a-d, d)', + 'setKey(b, value-b-d, d)' + ]); + } +}; diff --git a/test/runtime/samples/component-slot-context-props-each-nested/main.svelte b/test/runtime/samples/component-slot-context-props-each-nested/main.svelte new file mode 100644 index 0000000000..7feb55aaf6 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each-nested/main.svelte @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-each/Nested.svelte b/test/runtime/samples/component-slot-context-props-each/Nested.svelte new file mode 100644 index 0000000000..a29ffe0376 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each/Nested.svelte @@ -0,0 +1,11 @@ + + +{#each keys as key (key)} + setKey(key, value)} /> +{/each} \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-each/_config.js b/test/runtime/samples/component-slot-context-props-each/_config.js new file mode 100644 index 0000000000..f374e7f827 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each/_config.js @@ -0,0 +1,19 @@ +export default { + html: ` + + + `, + async test({ assert, target, window, component }) { + const [btn1, btn2] = target.querySelectorAll('button'); + const click = new window.MouseEvent('click'); + + await btn1.dispatchEvent(click); + assert.deepEqual(component.log, ['setKey(a, value-a)']); + + await btn2.dispatchEvent(click); + assert.deepEqual(component.log, [ + 'setKey(a, value-a)', + 'setKey(b, value-b)' + ]); + } +}; diff --git a/test/runtime/samples/component-slot-context-props-each/main.svelte b/test/runtime/samples/component-slot-context-props-each/main.svelte new file mode 100644 index 0000000000..7dc17b4c95 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-each/main.svelte @@ -0,0 +1,8 @@ + + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-let/Inner.svelte b/test/runtime/samples/component-slot-context-props-let/Inner.svelte new file mode 100644 index 0000000000..6beaabbb68 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-let/Inner.svelte @@ -0,0 +1,9 @@ + + + + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-let/Nested.svelte b/test/runtime/samples/component-slot-context-props-let/Nested.svelte new file mode 100644 index 0000000000..97c44c5145 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-let/Nested.svelte @@ -0,0 +1,8 @@ + + + + set(key, value)} /> + \ No newline at end of file diff --git a/test/runtime/samples/component-slot-context-props-let/_config.js b/test/runtime/samples/component-slot-context-props-let/_config.js new file mode 100644 index 0000000000..f374e7f827 --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-let/_config.js @@ -0,0 +1,19 @@ +export default { + html: ` + + + `, + async test({ assert, target, window, component }) { + const [btn1, btn2] = target.querySelectorAll('button'); + const click = new window.MouseEvent('click'); + + await btn1.dispatchEvent(click); + assert.deepEqual(component.log, ['setKey(a, value-a)']); + + await btn2.dispatchEvent(click); + assert.deepEqual(component.log, [ + 'setKey(a, value-a)', + 'setKey(b, value-b)' + ]); + } +}; diff --git a/test/runtime/samples/component-slot-context-props-let/main.svelte b/test/runtime/samples/component-slot-context-props-let/main.svelte new file mode 100644 index 0000000000..a061f73b3a --- /dev/null +++ b/test/runtime/samples/component-slot-context-props-let/main.svelte @@ -0,0 +1,8 @@ + + + + + From 169fd8497c6dc97f989ccf5b564f69769f5bf335 Mon Sep 17 00:00:00 2001 From: Cameron Messinides Date: Thu, 29 Oct 2020 16:24:39 -0400 Subject: [PATCH 08/13] docs: add documentation for $$slots (#5277) --- site/content/docs/02-template-syntax.md | 25 ++++++++ .../04-optional-slots/app-a/App.svelte | 57 +++++++++++++++++ .../04-optional-slots/app-a/Comment.svelte | 56 ++++++++++++++++ .../04-optional-slots/app-a/Project.svelte | 62 ++++++++++++++++++ .../04-optional-slots/app-b/App.svelte | 57 +++++++++++++++++ .../04-optional-slots/app-b/Comment.svelte | 56 ++++++++++++++++ .../04-optional-slots/app-b/Project.svelte | 64 +++++++++++++++++++ .../14-composition/04-optional-slots/text.md | 28 ++++++++ .../app-a/App.svelte | 0 .../app-a/Hoverable.svelte | 0 .../app-b/App.svelte | 0 .../app-b/Hoverable.svelte | 0 .../{04-slot-props => 05-slot-props}/text.md | 0 13 files changed, 405 insertions(+) create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-a/App.svelte create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-a/Comment.svelte create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-a/Project.svelte create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-b/App.svelte create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-b/Comment.svelte create mode 100755 site/content/tutorial/14-composition/04-optional-slots/app-b/Project.svelte create mode 100644 site/content/tutorial/14-composition/04-optional-slots/text.md rename site/content/tutorial/14-composition/{04-slot-props => 05-slot-props}/app-a/App.svelte (100%) rename site/content/tutorial/14-composition/{04-slot-props => 05-slot-props}/app-a/Hoverable.svelte (100%) rename site/content/tutorial/14-composition/{04-slot-props => 05-slot-props}/app-b/App.svelte (100%) rename site/content/tutorial/14-composition/{04-slot-props => 05-slot-props}/app-b/Hoverable.svelte (100%) rename site/content/tutorial/14-composition/{04-slot-props => 05-slot-props}/text.md (100%) diff --git a/site/content/docs/02-template-syntax.md b/site/content/docs/02-template-syntax.md index d955f650e2..119488aacc 100644 --- a/site/content/docs/02-template-syntax.md +++ b/site/content/docs/02-template-syntax.md @@ -1317,6 +1317,31 @@ Named slots allow consumers to target specific areas. They can also have fallbac ``` +#### [`$$slots`](slots_object) + +--- + +`$$slots` is an object whose keys are the names of the slots passed into the component by the parent. If the parent does not pass in a slot with a particular name, that name will not be a present in `$$slots`. This allows components to render a slot (and other elements, like wrappers for styling) only if the parent provides it. + +Note that explicitly passing in an empty named slot will add that slot's name to `$$slots`. For example, if a parent passes `
` to a child component, `$$slots.title` will be truthy within the child. + +```sv + + +

Blog Post Title

+
+ + +
+ + {#if $$slots.description} + +
+ + {/if} +
+``` + #### [``](slot_let) --- diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-a/App.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-a/App.svelte new file mode 100755 index 0000000000..80a4df59f8 --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-a/App.svelte @@ -0,0 +1,57 @@ + + + + +

+ Projects +

+ +
    +
  • + +
    + +

    Those interface tests are now passing.

    +
    +
    +
    +
  • +
  • + +
  • +
diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-a/Comment.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-a/Comment.svelte new file mode 100755 index 0000000000..8d15ffa882 --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-a/Comment.svelte @@ -0,0 +1,56 @@ + + + + +
+
+ +
+

{name}

+ +
+
+
+ +
+
diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-a/Project.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-a/Project.svelte new file mode 100755 index 0000000000..38d19fc638 --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-a/Project.svelte @@ -0,0 +1,62 @@ + + + + +
+
+

{title}

+

{tasksCompleted}/{totalTasks} tasks completed

+
+
+

Comments

+ +
+
diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-b/App.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-b/App.svelte new file mode 100755 index 0000000000..80a4df59f8 --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-b/App.svelte @@ -0,0 +1,57 @@ + + + + +

+ Projects +

+ +
    +
  • + +
    + +

    Those interface tests are now passing.

    +
    +
    +
    +
  • +
  • + +
  • +
diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-b/Comment.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-b/Comment.svelte new file mode 100755 index 0000000000..8d15ffa882 --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-b/Comment.svelte @@ -0,0 +1,56 @@ + + + + +
+
+ +
+

{name}

+ +
+
+
+ +
+
diff --git a/site/content/tutorial/14-composition/04-optional-slots/app-b/Project.svelte b/site/content/tutorial/14-composition/04-optional-slots/app-b/Project.svelte new file mode 100755 index 0000000000..87a1d0c3ac --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/app-b/Project.svelte @@ -0,0 +1,64 @@ + + + + +
+
+

{title}

+

{tasksCompleted}/{totalTasks} tasks completed

+
+ {#if $$slots.comments} +
+

Comments

+ +
+ {/if} +
diff --git a/site/content/tutorial/14-composition/04-optional-slots/text.md b/site/content/tutorial/14-composition/04-optional-slots/text.md new file mode 100644 index 0000000000..b875f4c87d --- /dev/null +++ b/site/content/tutorial/14-composition/04-optional-slots/text.md @@ -0,0 +1,28 @@ +--- +title: Checking for slot content +--- + +In some cases, you may want to control parts of your component based on whether the parent passes in content for a certain slot. Perhaps you have a wrapper around that slot, and you don't want to render it if the slot is empty. Or perhaps you'd like to apply a class only if the slot is present. You can do this by checking the properties of the special `$$slots` variable. + +`$$slots` is an object whose keys are the names of the slots passed in by the parent component. If the parent leaves a slot empty, then `$$slots` will not have an entry for that slot. + +Notice that both instances of `` in this example render a container for comments and a notification dot, even though only one has comments. We want to use `$$slots` to make sure we only render these elements when the parent `` passes in content for the `comments` slot. + +In `Project.svelte`, update the `class:has-discussion` directive on the `
`: + +```html +
+``` + +Next, wrap the `comments` slot and its wrapping `
` in an `if` block that checks `$$slots`: + +```html +{#if $$slots.comments} +
+

Comments

+ +
+{/if} +``` + +Now the comments container and the notification dot won't render when `` leaves the `comments` slot empty. diff --git a/site/content/tutorial/14-composition/04-slot-props/app-a/App.svelte b/site/content/tutorial/14-composition/05-slot-props/app-a/App.svelte similarity index 100% rename from site/content/tutorial/14-composition/04-slot-props/app-a/App.svelte rename to site/content/tutorial/14-composition/05-slot-props/app-a/App.svelte diff --git a/site/content/tutorial/14-composition/04-slot-props/app-a/Hoverable.svelte b/site/content/tutorial/14-composition/05-slot-props/app-a/Hoverable.svelte similarity index 100% rename from site/content/tutorial/14-composition/04-slot-props/app-a/Hoverable.svelte rename to site/content/tutorial/14-composition/05-slot-props/app-a/Hoverable.svelte diff --git a/site/content/tutorial/14-composition/04-slot-props/app-b/App.svelte b/site/content/tutorial/14-composition/05-slot-props/app-b/App.svelte similarity index 100% rename from site/content/tutorial/14-composition/04-slot-props/app-b/App.svelte rename to site/content/tutorial/14-composition/05-slot-props/app-b/App.svelte diff --git a/site/content/tutorial/14-composition/04-slot-props/app-b/Hoverable.svelte b/site/content/tutorial/14-composition/05-slot-props/app-b/Hoverable.svelte similarity index 100% rename from site/content/tutorial/14-composition/04-slot-props/app-b/Hoverable.svelte rename to site/content/tutorial/14-composition/05-slot-props/app-b/Hoverable.svelte diff --git a/site/content/tutorial/14-composition/04-slot-props/text.md b/site/content/tutorial/14-composition/05-slot-props/text.md similarity index 100% rename from site/content/tutorial/14-composition/04-slot-props/text.md rename to site/content/tutorial/14-composition/05-slot-props/text.md From 148b6105ed6862953ac2466e5bf5c8f9c792c468 Mon Sep 17 00:00:00 2001 From: Tan Li Hau Date: Fri, 30 Oct 2020 04:32:12 +0800 Subject: [PATCH 09/13] fix else block transition update (#5591) --- CHANGELOG.md | 1 + .../compile/render_dom/wrappers/IfBlock.ts | 2 ++ .../samples/transition-abort/_config.js | 31 +++++++++++++++++++ .../samples/transition-abort/main.svelte | 21 +++++++++++++ 4 files changed, 55 insertions(+) create mode 100644 test/runtime/samples/transition-abort/_config.js create mode 100644 test/runtime/samples/transition-abort/main.svelte diff --git a/CHANGELOG.md b/CHANGELOG.md index 4afd7f20e0..a3fb361e5f 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,7 @@ * Fix `$$props` and `$$restProps` when compiling to a custom element ([#5482](https://github.com/sveltejs/svelte/issues/5482)) * Fix function calls in `` props that use contextual values ([#5565](https://github.com/sveltejs/svelte/issues/5565)) +* Fix handling aborted transitions in `{:else}` blocks ([#5573](https://github.com/sveltejs/svelte/issues/5573)) * Add `Element` and `Node` to known globals ([#5586](https://github.com/sveltejs/svelte/issues/5586)) ## 3.29.4 diff --git a/src/compiler/compile/render_dom/wrappers/IfBlock.ts b/src/compiler/compile/render_dom/wrappers/IfBlock.ts index 0cb31036e6..a95f64f4d2 100644 --- a/src/compiler/compile/render_dom/wrappers/IfBlock.ts +++ b/src/compiler/compile/render_dom/wrappers/IfBlock.ts @@ -447,6 +447,8 @@ export default class IfBlockWrapper extends Wrapper { if (!${name}) { ${name} = ${if_blocks}[${current_block_type_index}] = ${if_block_creators}[${current_block_type_index}](#ctx); ${name}.c(); + } else { + ${name}.p(#ctx, #dirty); } ${has_transitions && b`@transition_in(${name}, 1);`} ${name}.m(${update_mount_node}, ${anchor}); diff --git a/test/runtime/samples/transition-abort/_config.js b/test/runtime/samples/transition-abort/_config.js new file mode 100644 index 0000000000..4f31c44a4d --- /dev/null +++ b/test/runtime/samples/transition-abort/_config.js @@ -0,0 +1,31 @@ +// expect aborting halfway through outro transition +// to behave the same in `{#if}` block as in `{:else}` block +export default { + html: ` +
a
+ +
a
+ `, + + async test({ assert, component, target, window, raf }) { + component.visible = false; + + // abort halfway through the outro transition + raf.tick(50); + + await component.$set({ + visible: true, + array: ['a', 'b', 'c'] + }); + + assert.htmlEqual(target.innerHTML, ` +
a
+
b
+
c
+ +
a
+
b
+
c
+ `); + } +}; diff --git a/test/runtime/samples/transition-abort/main.svelte b/test/runtime/samples/transition-abort/main.svelte new file mode 100644 index 0000000000..b574229712 --- /dev/null +++ b/test/runtime/samples/transition-abort/main.svelte @@ -0,0 +1,21 @@ + + +{#if visible} + {#each array as item} +
{item}
+ {/each} +{/if} + +{#if !visible} +{:else} + {#each array as item} +
{item}
+ {/each} +{/if} \ No newline at end of file From 33d1fc741ad0c36f43c6b27ebedc20218b413594 Mon Sep 17 00:00:00 2001 From: Daniel Sandoval Date: Sun, 1 Nov 2020 01:13:10 -0800 Subject: [PATCH 10/13] "What's new in Svelte" November newsletter (#5554) * outline and showcase so far * respond to feedback, fill out features * Update site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> * Update site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> * Update number of speakers Co-authored-by: Simon H <5968653+dummdidumm@users.noreply.github.com> Co-authored-by: Ben McCann <322311+benmccann@users.noreply.github.com> --- ...11-01-whats-new-in-svelte-november-2020.md | 46 +++++++++++++++++++ 1 file changed, 46 insertions(+) create mode 100644 site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md diff --git a/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md new file mode 100644 index 0000000000..15813dfa69 --- /dev/null +++ b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md @@ -0,0 +1,46 @@ +--- +title: What's new in Svelte: November 2020 +description: Slot forwarding fixes, SvelteKit for faster local development, and more from Svelte Summit +author: Daniel Sandoval +authorURL: https://desandoval.net +--- + +Welcome back to the "What's new in Svelte" series! This month, we're covering new features & bug fixes, last month's Svelte Summit and some stand-out sites and libraries... + +## New features & impactful bug fixes + +1. Destructuring Promises now works as expected by using the `{#await}` syntax + (**3.29.3**, [Example](https://svelte.dev/repl/3fd4e2cecfa14d629961478f1dac2445?version=3.29.3)) +2. Slot forwarding (released in 3.29.0) should no longer hang during compilation (**3.29.3**, [Example](https://svelte.dev/repl/29959e70103f4868a6525c0734934936?version=3.29.3)) +3. Better typings for the `get` function in `svelte/store` and on lifecycle hooks (**3.29.1**) + +**What's going on in Sapper?** + +Sapper got some new types in its `preload` function, which will make typing easier if you are using TypeScript. See the [Sapper docs](https://sapper.svelte.dev/docs#Typing_the_function) on how to use them. There also were fixes to `preload` links in exported sites. Route layouts got a few fixes too - including ensuring CSS is applied to nested route layouts. You can also better organize your files now that extensions with multiple dots are supported. (**0.28.10**) + + +For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github.com/sveltejs/svelte/blob/master/CHANGELOG.md) and [Sapper](https://github.com/sveltejs/sapper/blob/master/CHANGELOG.md). + + +## [Svelte Summit](https://sveltesummit.com/) was Svelte-tacular! +- Rich Harris demoed the possible future of Svelte development in a talk titled "Futuristic Web Development". The not-yet-public project is called SvelteKit (name may change) and will bring a first-class developer experience and more flexibility for build outputs. If you want to get the full sneak-peek, [check out the video](https://www.youtube.com/watch?v=qSfdtmcZ4d0). +- 17 speakers made the best of the conference's virtual format... From floating heads to seamless demos, Svelte developers from every skill level will find something of interest in this year's [YouTube playlist](https://www.youtube.com/playlist?list=PL8bMgX1kyZThM1sbYCoWdTcpiYysJsSeu) + +--- + +## Community Showcase +- [Svelte Lab](https://sveltelab.app/) showcases a variety of components, visualizations and interactions that can be acheived in Svelte. You can click into any component to see its source or edit it, using the site's built-in REPL +- [svelte-electron-boilerplate](https://github.com/hjalmar/svelte-electron-boilerplate) is a fast way to get up and running with a Svelte app built in the desktop javascript framework, Electron +- [React Hooks in Svelte](https://github.com/benflap/tabler-icons-svelte) showcases examples of common React Hooks ported to Svelte. +- [gurlic](https://gurlic.com/) is a social network and internet expirement that is super snappy thanks to Svelte +- [Interference 2020](https://interference2020.org/) visualizes reported foreign interference in the 2020 U.S. elections. You can learn more about how it was built in [YYY's talk at Svelte Summit]() +- [jitsi-svelte](https://github.com/relm-us/jitsi-svelte) lets you to easily create your own custom Jitsi client by providing out-of-the-box components built with Svelte +- [Ellx](https://ellx.io/) is part spreadsheet, part notebook and part IDE. It's super smooth thanks to Svelte 😎 +- [This New Zealand news site](https://www.nzherald.co.nz/nz/election-2020-latest-results-party-vote-electorate-vote-and-full-data/5CFVO4ENKNQDE3SICRRNPU5GZM/) breaks down the results of the 2020 Parliamentary elections using Svelte +- [Budibase](https://github.com/Budibase/budibase) is a no-code app builder, powered by Svelte +- [Svelt-yjs](https://github.com/relm-us/svelt-yjs) combines the collaborative, local-first technology of Yjs with the power of Svelte to enable multiple users across the internet to stay in sync. +- [tabler-icons-svelte](https://github.com/benflap/tabler-icons-svelte) is a Svelte wrapper for over 850 free MIT-licensed high-quality SVG icons for you to use in your web projects. + +## See you next month! + +Got an idea for something to add to the Showcase? Want to get involved more with Svelte? We're always looking for maintainers, contributors and fanatics... Check out the [Svelte Society](https://sveltesociety.dev/), [Reddit](https://www.reddit.com/r/sveltejs/) and [Discord](https://discord.com/invite/yy75DKs) to get involved! From 2db9cc2bc9dc2c3e482b7e61ad2726960a64bbb6 Mon Sep 17 00:00:00 2001 From: hmt Date: Mon, 2 Nov 2020 00:51:03 +0100 Subject: [PATCH 11/13] Some typos [ci-skip] (#5632) --- .../blog/2020-11-01-whats-new-in-svelte-november-2020.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md index 15813dfa69..4e14c0aec8 100644 --- a/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md +++ b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md @@ -29,12 +29,12 @@ For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github --- ## Community Showcase -- [Svelte Lab](https://sveltelab.app/) showcases a variety of components, visualizations and interactions that can be acheived in Svelte. You can click into any component to see its source or edit it, using the site's built-in REPL +- [Svelte Lab](https://sveltelab.app/) showcases a variety of components, visualizations and interactions that can be achieved in Svelte. You can click into any component to see its source or edit it, using the site's built-in REPL - [svelte-electron-boilerplate](https://github.com/hjalmar/svelte-electron-boilerplate) is a fast way to get up and running with a Svelte app built in the desktop javascript framework, Electron - [React Hooks in Svelte](https://github.com/benflap/tabler-icons-svelte) showcases examples of common React Hooks ported to Svelte. -- [gurlic](https://gurlic.com/) is a social network and internet expirement that is super snappy thanks to Svelte +- [gurlic](https://gurlic.com/) is a social network and internet experiment that is super snappy thanks to Svelte - [Interference 2020](https://interference2020.org/) visualizes reported foreign interference in the 2020 U.S. elections. You can learn more about how it was built in [YYY's talk at Svelte Summit]() -- [jitsi-svelte](https://github.com/relm-us/jitsi-svelte) lets you to easily create your own custom Jitsi client by providing out-of-the-box components built with Svelte +- [jitsi-svelte](https://github.com/relm-us/jitsi-svelte) lets you easily create your own custom Jitsi client by providing out-of-the-box components built with Svelte - [Ellx](https://ellx.io/) is part spreadsheet, part notebook and part IDE. It's super smooth thanks to Svelte 😎 - [This New Zealand news site](https://www.nzherald.co.nz/nz/election-2020-latest-results-party-vote-electorate-vote-and-full-data/5CFVO4ENKNQDE3SICRRNPU5GZM/) breaks down the results of the 2020 Parliamentary elections using Svelte - [Budibase](https://github.com/Budibase/budibase) is a no-code app builder, powered by Svelte From fb8ef740fa5396050b473c865b91fad42db64cf7 Mon Sep 17 00:00:00 2001 From: Simon H <5968653+dummdidumm@users.noreply.github.com> Date: Mon, 2 Nov 2020 08:41:30 +0100 Subject: [PATCH 12/13] Blog post: Fix link --- .../blog/2020-11-01-whats-new-in-svelte-november-2020.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md index 4e14c0aec8..433bafaa29 100644 --- a/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md +++ b/site/content/blog/2020-11-01-whats-new-in-svelte-november-2020.md @@ -31,7 +31,7 @@ For all the features and bugfixes see the CHANGELOGs for [Svelte](https://github ## Community Showcase - [Svelte Lab](https://sveltelab.app/) showcases a variety of components, visualizations and interactions that can be achieved in Svelte. You can click into any component to see its source or edit it, using the site's built-in REPL - [svelte-electron-boilerplate](https://github.com/hjalmar/svelte-electron-boilerplate) is a fast way to get up and running with a Svelte app built in the desktop javascript framework, Electron -- [React Hooks in Svelte](https://github.com/benflap/tabler-icons-svelte) showcases examples of common React Hooks ported to Svelte. +- [React Hooks in Svelte](https://github.com/joshnuss/react-hooks-in-svelte) showcases examples of common React Hooks ported to Svelte. - [gurlic](https://gurlic.com/) is a social network and internet experiment that is super snappy thanks to Svelte - [Interference 2020](https://interference2020.org/) visualizes reported foreign interference in the 2020 U.S. elections. You can learn more about how it was built in [YYY's talk at Svelte Summit]() - [jitsi-svelte](https://github.com/relm-us/jitsi-svelte) lets you easily create your own custom Jitsi client by providing out-of-the-box components built with Svelte From c8334c8b2839a7acea26ef2e6d6fe7c2cfa8cbbd Mon Sep 17 00:00:00 2001 From: Shriji Date: Tue, 3 Nov 2020 10:51:35 +0530 Subject: [PATCH 13/13] Update README.md (#5620) adds number of people online on discord. --- README.md | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/README.md b/README.md index 9fbada7aa3..d1befaf1ce 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ license - Chat + Chat