Skip to content
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

fix slot data for cancelled transition #6314

Merged
merged 2 commits into from
Jul 26, 2021
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
42 changes: 28 additions & 14 deletions src/compiler/compile/render_dom/wrappers/Slot.ts
Original file line number Diff line number Diff line change
Expand Up @@ -107,7 +107,7 @@ export default class SlotWrapper extends Wrapper {
if (spread_dynamic_dependencies.size) {
get_slot_spread_changes_fn = renderer.component.get_unique_name(`get_${sanitize(slot_name)}_slot_spread_changes`);
renderer.blocks.push(b`
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))} > 0 ? -1 : 0;
const ${get_slot_spread_changes_fn} = #dirty => ${renderer.dirty(Array.from(spread_dynamic_dependencies))};
`);
}
} else {
Expand Down Expand Up @@ -168,27 +168,41 @@ export default class SlotWrapper extends Wrapper {
if (block.has_outros) {
condition = x`!#current || ${condition}`;
}
let dirty = x`#dirty`;
if (block.has_outros) {
dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${dirty}`;

// conditions to treat everything as dirty
const all_dirty_conditions = [
get_slot_spread_changes_fn ? x`${get_slot_spread_changes_fn}(#dirty)` : null,
block.has_outros ? x`!#current` : null
].filter(Boolean);
const all_dirty_condition = all_dirty_conditions.length ? all_dirty_conditions.reduce((condition1, condition2) => x`${condition1} || ${condition2}`): null;

let slot_update;
if (all_dirty_condition) {
const dirty = x`${all_dirty_condition} ? @get_all_dirty_from_scope(${renderer.reference('$$scope')}) : @get_slot_changes(${slot_definition}, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn})`;

slot_update = b`
if (${slot}.p && ${condition}) {
@update_slot_base(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_context_fn});
}
`;
} else {
slot_update = b`
if (${slot}.p && ${condition}) {
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, #dirty, ${get_slot_changes_fn}, ${get_slot_context_fn});
}
`;
}

const slot_update = get_slot_spread_changes_fn ? b`
if (${slot}.p && ${condition}) {
@update_slot_spread(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_spread_changes_fn}, ${get_slot_context_fn});
}
` : b`
if (${slot}.p && ${condition}) {
@update_slot(${slot}, ${slot_definition}, #ctx, ${renderer.reference('$$scope')}, ${dirty}, ${get_slot_changes_fn}, ${get_slot_context_fn});
}
`;
let fallback_condition = renderer.dirty(fallback_dynamic_dependencies);
let fallback_dirty = x`#dirty`;
if (block.has_outros) {
fallback_condition = x`!#current || ${fallback_condition}`;
fallback_dirty = x`!#current ? ${renderer.get_initial_dirty()} : ${fallback_dirty}`;
}

const fallback_update = has_fallback && fallback_dynamic_dependencies.length > 0 && b`
if (${slot_or_fallback} && ${slot_or_fallback}.p && ${fallback_condition}) {
${slot_or_fallback}.p(#ctx, ${dirty});
${slot_or_fallback}.p(#ctx, ${fallback_dirty});
}
`;

Expand Down
22 changes: 15 additions & 7 deletions src/runtime/internal/utils.ts
Original file line number Diff line number Diff line change
Expand Up @@ -109,20 +109,28 @@ export function get_slot_changes(definition, $$scope, dirty, fn) {
return $$scope.dirty;
}

export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
export function update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn) {
if (slot_changes) {
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
slot.p(slot_context, slot_changes);
}
}

export function update_slot_spread(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_spread_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_spread_changes_fn(dirty) | get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
if (slot_changes) {
const slot_context = get_slot_context(slot_definition, ctx, $$scope, get_slot_context_fn);
slot.p(slot_context, slot_changes);
export function update_slot(slot, slot_definition, ctx, $$scope, dirty, get_slot_changes_fn, get_slot_context_fn) {
const slot_changes = get_slot_changes(slot_definition, $$scope, dirty, get_slot_changes_fn);
update_slot_base(slot, slot_definition, ctx, $$scope, slot_changes, get_slot_context_fn);
}

export function get_all_dirty_from_scope($$scope) {
if ($$scope.ctx.length > 32) {
const dirty = [];
const length = $$scope.ctx.length / 32;
for (let i = 0; i < length; i++) {
dirty[i] = -1;
}
return dirty;
}
return -1;
}

export function exclude_internal_props(props) {
Expand Down
1 change: 1 addition & 0 deletions test/runtime/samples/transition-js-slot-2/_config.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
// cancelled the transition halfway
export default {
html: `
<div>Foo</div>
Expand Down
19 changes: 19 additions & 0 deletions test/runtime/samples/transition-js-slot-4-cancelled/Nested.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
export let visible;
export let slotProps;

function fade(node) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
</script>

{#if visible}
<div transition:fade>
<slot {slotProps}></slot>
</div>
{/if}
35 changes: 35 additions & 0 deletions test/runtime/samples/transition-js-slot-4-cancelled/_config.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
// updated props in the middle of transitions
// and cancelled the transition halfway
export default {
html: `
<div>outside Foo Foo Foo</div>
<div>inside Foo Foo Foo</div>
`,
props: {
props: 'Foo'
},

async test({ assert, component, target, window, raf }) {
await component.hide();
const [, div] = target.querySelectorAll('div');

raf.tick(50);
assert.equal(div.foo, 0.5);

component.props = 'Bar';
assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Foo Foo Foo</div>
`);

await component.show();

assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Bar Bar Bar</div>
`);

raf.tick(100);
assert.equal(div.foo, 1);
}
};
22 changes: 22 additions & 0 deletions test/runtime/samples/transition-js-slot-4-cancelled/main.svelte
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
<script>
import Nested from './Nested.svelte';

let visible = true;
let state = 'Foo';
let slotProps = 'Foo';
export let props;

export function show() {
visible = true;
}
export function hide() {
visible = false;
state = 'Bar';
slotProps = 'Bar';
}
</script>

<div>outside {state} {props} {slotProps}</div>
<Nested {visible} {slotProps} let:slotProps>
inside {state} {props} {slotProps}
</Nested>
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
export let visible;
export let slotProps;

function fade(node) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
</script>

{#if visible}
<div transition:fade>
<slot {slotProps}></slot>
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// updated props in the middle of transitions
// and cancelled the transition halfway
// + spreaded props + overflow context

export default {
html: `
<div>outside Foo Foo Foo</div>
<div>inside Foo Foo Foo</div>
0
`,
props: {
props: 'Foo'
},

async test({ assert, component, target, window, raf }) {
await component.hide();
const [, div] = target.querySelectorAll('div');

raf.tick(50);
assert.equal(div.foo, 0.5);

component.props = 'Bar';
assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Foo Foo Foo</div>
0
`);

await component.show();

assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Bar Bar Bar</div>
0
`);

raf.tick(100);
assert.equal(div.foo, 1);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
<script>
import Nested from './Nested.svelte';

export let a1=0,a2=0,a3=0,a4=0,a5=0,a6=0,a7=0,a8=0,a9=0,a10=0,a11=0, a12=0,a13=0,a14=0,a15=0,a16=0,a17=0,a18=0,a19=0,a20=0,a21=0, a22=0,a23=0,a24=0,a25=0,a26=0,a27=0,a28=0,a29=0,a30=0,a31=0,a32=0,a33=0;

let visible = true;
let state = 'Foo';
let slotProps = 'Foo';
export let props;

export function show() {
visible = true;
}
export function hide() {
visible = false;
state = 'Bar';
slotProps = 'Bar';
}
</script>

<div>outside {state} {props} {slotProps}</div>

<Nested {visible} {slotProps} let:slotProps>
inside {state} {props} {slotProps}
</Nested>

{a1+a2+a3+a4+a5+a6+a7+a8+a9+a10+a11+a12+a13+a14+a15+a16+a17+a18+a19+a20+a21+a22+a23+a24+a25+a26+a27+a28+a29+a30+a31+a32+a33}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
export let visible;
export let slotProps;

function fade(node) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
</script>

{#if visible}
<div transition:fade>
<slot {...slotProps}></slot>
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
<script>
export let visible;
let slotProps = { slotProps: 'XXX' };

function fade(node) {
return {
duration: 100,
tick: t => {
node.foo = t;
}
};
}
</script>

{#if visible}
<div transition:fade>
<slot {...slotProps}></slot>
</div>
{/if}
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
// updated props in the middle of transitions
// and cancelled the transition halfway
// with spreaded props

export default {
html: `
<div>outside Foo Foo Foo</div>
<div>inside Foo Foo Foo</div>
<div>inside Foo Foo XXX</div>
`,
props: {
props: 'Foo'
},

async test({ assert, component, target, window, raf }) {
await component.hide();
const [, div] = target.querySelectorAll('div');

raf.tick(50);
assert.equal(div.foo, 0.5);

component.props = 'Bar';
assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Foo Foo Foo</div>
<div>inside Foo Foo XXX</div>
`);

await component.show();

assert.htmlEqual(target.innerHTML, `
<div>outside Bar Bar Bar</div>
<div>inside Bar Bar Bar</div>
<div>inside Bar Bar XXX</div>
`);

raf.tick(100);
assert.equal(div.foo, 1);
}
};
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
<script>
import Nested from './Nested.svelte';
import Nested2 from './Nested2.svelte';

let visible = true;
let state = 'Foo';
let slotProps = { slotProps: 'Foo' };
export let props;

export function show() {
visible = true;
}
export function hide() {
visible = false;
state = 'Bar';
slotProps = { slotProps: 'Bar' };
}
</script>

<div>outside {state} {props} {slotProps.slotProps}</div>
<Nested {visible} {slotProps} let:slotProps>
inside {state} {props} {slotProps}
</Nested>
<Nested2 {visible} let:slotProps>
inside {state} {props} {slotProps}
</Nested2>
Loading