diff --git a/CHANGELOG.md b/CHANGELOG.md index f24bc543b7..7bcc280213 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -4,6 +4,9 @@ * Handle `width`/`height` attributes when spreading ([#6752](https://github.com/sveltejs/svelte/issues/6752)) * Add support for resize observer bindings (`
`) ([#8022](https://github.com/sveltejs/svelte/pull/8022)) +* Update interpolated style directive properly when using spread ([#8438](https://github.com/sveltejs/svelte/issues/8438)) +* Remove style directive property when value is `undefined` ([#8462](https://github.com/sveltejs/svelte/issues/8462)) +* Ensure version is typed as `string` instead of the literal `__VERSION__` ([#8498](https://github.com/sveltejs/svelte/issues/8498)) ## 3.58.0 diff --git a/site/content/docs/02-template-syntax/01-svelte-components.md b/site/content/docs/02-template-syntax/01-svelte-components.md index 460562fed8..d1f353629b 100644 --- a/site/content/docs/02-template-syntax/01-svelte-components.md +++ b/site/content/docs/02-template-syntax/01-svelte-components.md @@ -36,7 +36,9 @@ Svelte uses the `export` keyword to mark a variable declaration as a _property_ ``` -You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that whenever a prop is removed by the consumer, its value is set to `undefined` rather than the initial value. +--- + +You can specify a default initial value for a prop. It will be used if the component's consumer doesn't specify the prop on the component (or if its initial value is `undefined`) when instantiating the component. Note that if the values of props are subsequently updated, then any prop whose value is not specified will be set to `undefined` (rather than its initial value). In development mode (see the [compiler options](/docs/svelte-compiler#svelte-compile)), a warning will be printed if no default initial value is provided and the consumer does not specify a value. To squelch this warning, ensure that a default initial value is specified, even if it is `undefined`. diff --git a/src/compiler/compile/render_dom/wrappers/Element/index.ts b/src/compiler/compile/render_dom/wrappers/Element/index.ts index cefebfcff3..e2c0ece733 100644 --- a/src/compiler/compile/render_dom/wrappers/Element/index.ts +++ b/src/compiler/compile/render_dom/wrappers/Element/index.ts @@ -1240,19 +1240,27 @@ export default class ElementWrapper extends Wrapper { block.chunks.hydrate.push(updater); + const self_deps = expression.dynamic_dependencies(); + const all_deps = new Set([ + ...self_deps, + ...this.dynamic_style_dependencies + ]); + + let condition = block.renderer.dirty([...all_deps]); + // Assume that style has changed through the spread attribute if (has_spread) { + if (should_cache && all_deps.size) { + // Update the cached value + block.chunks.update.push(b` + if (${condition}) { + ${cached_snippet} = ${snippet}; + }` + ); + } block.chunks.update.push(updater); } else { - const self_deps = expression.dynamic_dependencies(); - const all_deps = new Set([ - ...self_deps, - ...this.dynamic_style_dependencies - ]); - - if (all_deps.size === 0) return; - - let condition = block.renderer.dirty([...all_deps]); + if (all_deps.size === 0) return; if (should_cache) { condition = x`${condition} && ${cached_snippet} !== (${cached_snippet} = ${snippet})`; diff --git a/src/compiler/index.ts b/src/compiler/index.ts index 76eb45d37f..57277ac756 100644 --- a/src/compiler/index.ts +++ b/src/compiler/index.ts @@ -3,5 +3,5 @@ export { default as parse } from './parse/index'; export { default as preprocess } from './preprocess/index'; export { walk } from 'estree-walker'; -export const VERSION = '__VERSION__'; +export const VERSION: string = '__VERSION__'; // additional exports added through generate-type-definitions.js diff --git a/src/runtime/internal/dom.ts b/src/runtime/internal/dom.ts index c025313c15..066fa6eb1c 100644 --- a/src/runtime/internal/dom.ts +++ b/src/runtime/internal/dom.ts @@ -307,7 +307,7 @@ export function attr(node: Element, attribute: string, value?: string) { else if (node.getAttribute(attribute) !== value) node.setAttribute(attribute, value); } -/** +/** * List of attributes that should always be set through the attr method, * because updating them through the property setter doesn't work reliably. * In the example of `width`/`height`, the problem is that the setter only @@ -641,7 +641,7 @@ export function set_input_type(input, type) { } export function set_style(node, key, value, important) { - if (value === null) { + if (value == null) { node.style.removeProperty(key); } else { node.style.setProperty(key, value, important ? 'important' : ''); diff --git a/src/runtime/store/index.ts b/src/runtime/store/index.ts index 67c2bcb15c..e5184eb25b 100644 --- a/src/runtime/store/index.ts +++ b/src/runtime/store/index.ts @@ -12,8 +12,15 @@ export type Updater = (value: T) => T; /** Cleanup logic callback. */ type Invalidator = (value?: T) => void; -/** Start and stop notification callbacks. */ -export type StartStopNotifier = (set: Subscriber) => Unsubscriber | void; +/** + * Start and stop notification callbacks. + * This function is called when the first subscriber subscribes. + * + * @param {(value: T) => void} set Function that sets the value of the store. + * @returns {void | (() => void)} Optionally, a cleanup function that is called when the last remaining + * subscriber unsubscribes. + */ +export type StartStopNotifier = (set: (value: T) => void) => void | (() => void); /** Readable interface for subscribing. */ export interface Readable { @@ -48,7 +55,7 @@ const subscriber_queue = []; /** * Creates a `Readable` store that allows reading by subscription. * @param value initial value - * @param {StartStopNotifier}start start and stop notifications for subscriptions + * @param {StartStopNotifier} [start] */ export function readable(value?: T, start?: StartStopNotifier): Readable { return { @@ -59,7 +66,7 @@ export function readable(value?: T, start?: StartStopNotifier): Readable(value?: T, start: StartStopNotifier = noop): Writable { let stop: Unsubscriber; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js new file mode 100644 index 0000000000..af1030c25a --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/_config.js @@ -0,0 +1,18 @@ +export default { + html: ` +
+ `, + + test({ assert, target, window, component }) { + const div = target.querySelector('div'); + const styles = window.getComputedStyle(div); + assert.equal(styles.backgroundColor, 'rgb(255, 0, 0)'); + + { + component.backgroundColor = 128; + const div = target.querySelector('div'); + const styles = window.getComputedStyle(div); + assert.equal(styles.backgroundColor, 'rgb(128, 0, 0)'); + } + } +}; diff --git a/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte new file mode 100644 index 0000000000..4f44ae2e67 --- /dev/null +++ b/test/runtime-puppeteer/samples/inline-style-directive-update-with-spread/main.svelte @@ -0,0 +1,5 @@ + + +
diff --git a/test/runtime/samples/inline-style-become-undefined/_config.js b/test/runtime/samples/inline-style-become-undefined/_config.js new file mode 100644 index 0000000000..a2a0727efa --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/_config.js @@ -0,0 +1,11 @@ +export default { + async test({ assert, target, window }) { + const div = target.querySelector('div'); + const click = new window.MouseEvent('click'); + + assert.htmlEqual(target.innerHTML, '
'); + await div.dispatchEvent(click); + await Promise.resolve(); + assert.htmlEqual(target.innerHTML, '
'); + } +}; diff --git a/test/runtime/samples/inline-style-become-undefined/main.svelte b/test/runtime/samples/inline-style-become-undefined/main.svelte new file mode 100644 index 0000000000..ee38934fc7 --- /dev/null +++ b/test/runtime/samples/inline-style-become-undefined/main.svelte @@ -0,0 +1,9 @@ + + +