From 8be74e833f83e274f71caa4635c906cd04030c5d Mon Sep 17 00:00:00 2001 From: "github-actions[bot]" Date: Tue, 27 Aug 2024 14:15:19 +0000 Subject: [PATCH] Publish packages --- .changeset/slimy-lizards-tickle.md | 157 ------------------------ .changeset/strong-sheep-sing.md | 9 -- .changeset/tiny-horses-cheat.md | 5 - examples/custom-element/CHANGELOG.md | 7 ++ examples/custom-element/package.json | 2 +- examples/getting-started/CHANGELOG.md | 7 ++ examples/getting-started/package.json | 2 +- examples/kitchen-sink/CHANGELOG.md | 10 ++ examples/kitchen-sink/package.json | 2 +- packages/core/CHANGELOG.md | 159 +++++++++++++++++++++++++ packages/core/package.json | 4 +- packages/polyfill/CHANGELOG.md | 164 ++++++++++++++++++++++++++ packages/polyfill/package.json | 2 +- packages/preact/CHANGELOG.md | 160 +++++++++++++++++++++++++ packages/preact/package.json | 6 +- packages/react/CHANGELOG.md | 159 +++++++++++++++++++++++++ packages/react/package.json | 4 +- packages/signals/CHANGELOG.md | 159 +++++++++++++++++++++++++ packages/signals/package.json | 6 +- pnpm-lock.yaml | 10 +- 20 files changed, 844 insertions(+), 190 deletions(-) delete mode 100644 .changeset/slimy-lizards-tickle.md delete mode 100644 .changeset/strong-sheep-sing.md delete mode 100644 .changeset/tiny-horses-cheat.md diff --git a/.changeset/slimy-lizards-tickle.md b/.changeset/slimy-lizards-tickle.md deleted file mode 100644 index e4835189..00000000 --- a/.changeset/slimy-lizards-tickle.md +++ /dev/null @@ -1,157 +0,0 @@ ---- -'@remote-dom/polyfill': minor -'@remote-dom/signals': minor -'@remote-dom/preact': minor -'@remote-dom/react': minor -'@remote-dom/core': minor ---- - -## Added native support for synchronizing attributes and event listeners - -Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). - -```ts -import {RemoteElement} from '@remote-dom/core/elements'; - -class MyElement extends RemoteElement { - static get remoteProperties() { - return ['label']; - } -} - -customElements.define('my-element', MyElement); - -const myElement = document.createElement('my-element'); -myElement.label = 'Hello, World!'; -``` - -The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: - -```ts -const myElement = document.createElement('my-element'); -myElement.setAttribute('label', 'Hello, World!'); - -// myElement.label === 'Hello, World!', and this value is synchronized -// with the host environment as a “remote property” -``` - -Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: - -```ts -import {RemoteElement} from '@remote-dom/core/elements'; - -class MyElement extends RemoteElement { - static get remoteProperties() { - return { - onChange: { - event: true, - }, - }; - } -} - -customElements.define('my-element', MyElement); - -const myElement = document.createElement('my-element'); - -// This adds a callback property that is synchronized with the host environment -myElement.onChange = () => console.log('Changed!'); - -// And so does this, but using the `addEventListener` method instead -myElement.addEventListener('change', () => console.log('Changed!')); -``` - -These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: - -```ts -import {RemoteElement} from '@remote-dom/core/elements'; - -class MyElement extends RemoteElement { - static get remoteAttributes() { - return ['label']; - } - - // If you want to add instance properties, you can do it with getters and - // setters that manipulate the attribute value: - // - // get label() { - // return this.getAttribute('label'); - // } - // - // set label(value) { - // this.setAttribute('label', value); - // } -} - -customElements.define('my-element', MyElement); - -const myElement = document.createElement('my-element'); -myElement.setAttribute('label', 'Hello, World!'); -``` - -Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: - -```ts -import {RemoteElement} from '@remote-dom/core/elements'; - -class MyElement extends RemoteElement { - static get remoteEvents() { - return ['change']; - } -} - -customElements.define('my-element', MyElement); - -const myElement = document.createElement('my-element'); - -// And so does this, but using the `addEventListener` method instead -myElement.addEventListener('change', () => console.log('Changed!')); - -// No `myElement.onChange` property is created -``` - -The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. - -This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. - -All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: - -```ts -// Remote environment: - -class MyElement extends RemoteElement { - static get remoteEvents() { - return ['change']; - } -} - -customElements.define('my-element', MyElement); - -const myElement = document.createElement('my-element'); - -myElement.addEventListener('change', (event) => { - console.log('Changed! New value: ', event.detail); -}); - -// Host environment: - -class MyElement extends HTMLElement { - connectedCallback() { - // Emit a change event on this element, with detail that will be passed - // to the remote environment - this.addEventListener('change', (event) => { - event.stopImmediatePropagation(); - - this.dispatchEvent( - new CustomEvent('change', { - detail: this.value, - }), - ); - }); - } - - // Additional implementation details of the host custom element... -} - -customElements.define('my-element', MyElement); -``` diff --git a/.changeset/strong-sheep-sing.md b/.changeset/strong-sheep-sing.md deleted file mode 100644 index 729fd015..00000000 --- a/.changeset/strong-sheep-sing.md +++ /dev/null @@ -1,9 +0,0 @@ ---- -'@remote-dom/polyfill': patch ---- - -Bug fixes to event dispatching - -- Listeners on the target are now called during both the capture and bubble phases. -- `stopPropagation` now respected. -- `stopImmediatePropagation` now also stops regular propagation. diff --git a/.changeset/tiny-horses-cheat.md b/.changeset/tiny-horses-cheat.md deleted file mode 100644 index 19a476f1..00000000 --- a/.changeset/tiny-horses-cheat.md +++ /dev/null @@ -1,5 +0,0 @@ ---- -'@remote-dom/polyfill': patch ---- - -Fix `Event.bubbles` and `Event.composedPath()` implementations diff --git a/examples/custom-element/CHANGELOG.md b/examples/custom-element/CHANGELOG.md index 021a2870..7df100de 100644 --- a/examples/custom-element/CHANGELOG.md +++ b/examples/custom-element/CHANGELOG.md @@ -1,5 +1,12 @@ # example-custom-element +## 0.0.13 + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/core@1.5.0 + ## 0.0.12 ### Patch Changes diff --git a/examples/custom-element/package.json b/examples/custom-element/package.json index 3b5dbe33..183db8a6 100644 --- a/examples/custom-element/package.json +++ b/examples/custom-element/package.json @@ -1,6 +1,6 @@ { "name": "example-custom-element", - "version": "0.0.12", + "version": "0.0.13", "type": "module", "private": true, "scripts": { diff --git a/examples/getting-started/CHANGELOG.md b/examples/getting-started/CHANGELOG.md index e15939e1..dac20b83 100644 --- a/examples/getting-started/CHANGELOG.md +++ b/examples/getting-started/CHANGELOG.md @@ -1,5 +1,12 @@ # example-getting-started +## 0.0.13 + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/core@1.5.0 + ## 0.0.12 ### Patch Changes diff --git a/examples/getting-started/package.json b/examples/getting-started/package.json index 471171da..c315540e 100644 --- a/examples/getting-started/package.json +++ b/examples/getting-started/package.json @@ -1,6 +1,6 @@ { "name": "example-getting-started", - "version": "0.0.12", + "version": "0.0.13", "type": "module", "private": true, "scripts": { diff --git a/examples/kitchen-sink/CHANGELOG.md b/examples/kitchen-sink/CHANGELOG.md index 4e0e9fb6..19782851 100644 --- a/examples/kitchen-sink/CHANGELOG.md +++ b/examples/kitchen-sink/CHANGELOG.md @@ -1,5 +1,15 @@ # example-kitchen-sink +## 0.0.16 + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/signals@2.0.0 + - @remote-dom/preact@1.2.0 + - @remote-dom/react@1.2.0 + - @remote-dom/core@1.5.0 + ## 0.0.15 ### Patch Changes diff --git a/examples/kitchen-sink/package.json b/examples/kitchen-sink/package.json index 49bb128a..0974a2cd 100644 --- a/examples/kitchen-sink/package.json +++ b/examples/kitchen-sink/package.json @@ -2,7 +2,7 @@ "name": "example-kitchen-sink", "type": "module", "private": true, - "version": "0.0.15", + "version": "0.0.16", "scripts": { "start": "vite" }, diff --git a/packages/core/CHANGELOG.md b/packages/core/CHANGELOG.md index 21a2f92f..95b0a131 100644 --- a/packages/core/CHANGELOG.md +++ b/packages/core/CHANGELOG.md @@ -1,5 +1,164 @@ # @remote-dom/core +## 1.5.0 + +### Minor Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - ## Added native support for synchronizing attributes and event listeners + + Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return ['label']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.label = 'Hello, World!'; + ``` + + The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: + + ```ts + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + + // myElement.label === 'Hello, World!', and this value is synchronized + // with the host environment as a “remote property” + ``` + + Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return { + onChange: { + event: true, + }, + }; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // This adds a callback property that is synchronized with the host environment + myElement.onChange = () => console.log('Changed!'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + ``` + + These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteAttributes() { + return ['label']; + } + + // If you want to add instance properties, you can do it with getters and + // setters that manipulate the attribute value: + // + // get label() { + // return this.getAttribute('label'); + // } + // + // set label(value) { + // this.setAttribute('label', value); + // } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + ``` + + Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + + // No `myElement.onChange` property is created + ``` + + The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. + + This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. + + All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: + + ```ts + // Remote environment: + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + myElement.addEventListener('change', (event) => { + console.log('Changed! New value: ', event.detail); + }); + + // Host environment: + + class MyElement extends HTMLElement { + connectedCallback() { + // Emit a change event on this element, with detail that will be passed + // to the remote environment + this.addEventListener('change', (event) => { + event.stopImmediatePropagation(); + + this.dispatchEvent( + new CustomEvent('change', { + detail: this.value, + }), + ); + }); + } + + // Additional implementation details of the host custom element... + } + + customElements.define('my-element', MyElement); + ``` + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403), [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403), [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/polyfill@1.3.0 + ## 1.4.1 ### Patch Changes diff --git a/packages/core/package.json b/packages/core/package.json index 359183e7..ad965e8a 100644 --- a/packages/core/package.json +++ b/packages/core/package.json @@ -7,7 +7,7 @@ "access": "public", "@remote-dom/registry": "https://registry.npmjs.org" }, - "version": "1.4.1", + "version": "1.5.0", "engines": { "node": ">=14.0.0" }, @@ -80,7 +80,7 @@ "build": "rollup --config ./rollup.config.js" }, "dependencies": { - "@remote-dom/polyfill": "workspace:^1.2.0", + "@remote-dom/polyfill": "workspace:^1.3.0", "htm": "^3.1.1" }, "peerDependencies": { diff --git a/packages/polyfill/CHANGELOG.md b/packages/polyfill/CHANGELOG.md index df0d166b..8a4aa589 100644 --- a/packages/polyfill/CHANGELOG.md +++ b/packages/polyfill/CHANGELOG.md @@ -1,5 +1,169 @@ # @remote-dom/polyfill +## 1.3.0 + +### Minor Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - ## Added native support for synchronizing attributes and event listeners + + Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return ['label']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.label = 'Hello, World!'; + ``` + + The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: + + ```ts + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + + // myElement.label === 'Hello, World!', and this value is synchronized + // with the host environment as a “remote property” + ``` + + Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return { + onChange: { + event: true, + }, + }; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // This adds a callback property that is synchronized with the host environment + myElement.onChange = () => console.log('Changed!'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + ``` + + These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteAttributes() { + return ['label']; + } + + // If you want to add instance properties, you can do it with getters and + // setters that manipulate the attribute value: + // + // get label() { + // return this.getAttribute('label'); + // } + // + // set label(value) { + // this.setAttribute('label', value); + // } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + ``` + + Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + + // No `myElement.onChange` property is created + ``` + + The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. + + This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. + + All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: + + ```ts + // Remote environment: + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + myElement.addEventListener('change', (event) => { + console.log('Changed! New value: ', event.detail); + }); + + // Host environment: + + class MyElement extends HTMLElement { + connectedCallback() { + // Emit a change event on this element, with detail that will be passed + // to the remote environment + this.addEventListener('change', (event) => { + event.stopImmediatePropagation(); + + this.dispatchEvent( + new CustomEvent('change', { + detail: this.value, + }), + ); + }); + } + + // Additional implementation details of the host custom element... + } + + customElements.define('my-element', MyElement); + ``` + +### Patch Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - Bug fixes to event dispatching + + - Listeners on the target are now called during both the capture and bubble phases. + - `stopPropagation` now respected. + - `stopImmediatePropagation` now also stops regular propagation. + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - Fix `Event.bubbles` and `Event.composedPath()` implementations + ## 1.2.1 ### Patch Changes diff --git a/packages/polyfill/package.json b/packages/polyfill/package.json index e9a75fba..5c05b314 100644 --- a/packages/polyfill/package.json +++ b/packages/polyfill/package.json @@ -7,7 +7,7 @@ "access": "public", "@remote-dom/registry": "https://registry.npmjs.org" }, - "version": "1.2.1", + "version": "1.3.0", "engines": { "node": ">=14.0.0" }, diff --git a/packages/preact/CHANGELOG.md b/packages/preact/CHANGELOG.md index c7e29a41..9251fbdc 100644 --- a/packages/preact/CHANGELOG.md +++ b/packages/preact/CHANGELOG.md @@ -1,5 +1,165 @@ # @remote-dom/preact +## 1.2.0 + +### Minor Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - ## Added native support for synchronizing attributes and event listeners + + Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return ['label']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.label = 'Hello, World!'; + ``` + + The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: + + ```ts + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + + // myElement.label === 'Hello, World!', and this value is synchronized + // with the host environment as a “remote property” + ``` + + Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return { + onChange: { + event: true, + }, + }; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // This adds a callback property that is synchronized with the host environment + myElement.onChange = () => console.log('Changed!'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + ``` + + These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteAttributes() { + return ['label']; + } + + // If you want to add instance properties, you can do it with getters and + // setters that manipulate the attribute value: + // + // get label() { + // return this.getAttribute('label'); + // } + // + // set label(value) { + // this.setAttribute('label', value); + // } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + ``` + + Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + + // No `myElement.onChange` property is created + ``` + + The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. + + This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. + + All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: + + ```ts + // Remote environment: + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + myElement.addEventListener('change', (event) => { + console.log('Changed! New value: ', event.detail); + }); + + // Host environment: + + class MyElement extends HTMLElement { + connectedCallback() { + // Emit a change event on this element, with detail that will be passed + // to the remote environment + this.addEventListener('change', (event) => { + event.stopImmediatePropagation(); + + this.dispatchEvent( + new CustomEvent('change', { + detail: this.value, + }), + ); + }); + } + + // Additional implementation details of the host custom element... + } + + customElements.define('my-element', MyElement); + ``` + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/signals@2.0.0 + - @remote-dom/core@1.5.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/preact/package.json b/packages/preact/package.json index 01c2462d..07133311 100644 --- a/packages/preact/package.json +++ b/packages/preact/package.json @@ -7,7 +7,7 @@ "access": "public", "@remote-dom/registry": "https://registry.npmjs.org" }, - "version": "1.1.0", + "version": "1.2.0", "engines": { "node": ">=14.0.0" }, @@ -55,8 +55,8 @@ "build": "rollup --config rollup.config.js" }, "dependencies": { - "@remote-dom/core": "workspace:^1.4.0", - "@remote-dom/signals": "workspace:^1.1.0", + "@remote-dom/core": "workspace:^1.5.0", + "@remote-dom/signals": "workspace:^2.0.0", "htm": "^3.1.1" }, "peerDependencies": { diff --git a/packages/react/CHANGELOG.md b/packages/react/CHANGELOG.md index c812f0c5..cac8701f 100644 --- a/packages/react/CHANGELOG.md +++ b/packages/react/CHANGELOG.md @@ -1,5 +1,164 @@ # @remote-dom/react +## 1.2.0 + +### Minor Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - ## Added native support for synchronizing attributes and event listeners + + Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return ['label']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.label = 'Hello, World!'; + ``` + + The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: + + ```ts + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + + // myElement.label === 'Hello, World!', and this value is synchronized + // with the host environment as a “remote property” + ``` + + Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return { + onChange: { + event: true, + }, + }; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // This adds a callback property that is synchronized with the host environment + myElement.onChange = () => console.log('Changed!'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + ``` + + These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteAttributes() { + return ['label']; + } + + // If you want to add instance properties, you can do it with getters and + // setters that manipulate the attribute value: + // + // get label() { + // return this.getAttribute('label'); + // } + // + // set label(value) { + // this.setAttribute('label', value); + // } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + ``` + + Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + + // No `myElement.onChange` property is created + ``` + + The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. + + This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. + + All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: + + ```ts + // Remote environment: + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + myElement.addEventListener('change', (event) => { + console.log('Changed! New value: ', event.detail); + }); + + // Host environment: + + class MyElement extends HTMLElement { + connectedCallback() { + // Emit a change event on this element, with detail that will be passed + // to the remote environment + this.addEventListener('change', (event) => { + event.stopImmediatePropagation(); + + this.dispatchEvent( + new CustomEvent('change', { + detail: this.value, + }), + ); + }); + } + + // Additional implementation details of the host custom element... + } + + customElements.define('my-element', MyElement); + ``` + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/core@1.5.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/react/package.json b/packages/react/package.json index ecc16586..b4dd8d98 100644 --- a/packages/react/package.json +++ b/packages/react/package.json @@ -6,7 +6,7 @@ "access": "public", "@remote-dom/registry": "https://registry.npmjs.org" }, - "version": "1.1.0", + "version": "1.2.0", "engines": { "node": ">=18.0.0" }, @@ -68,7 +68,7 @@ "build": "rollup --config rollup.config.js" }, "dependencies": { - "@remote-dom/core": "workspace:^1.4.0", + "@remote-dom/core": "workspace:^1.5.0", "@types/react": "^18.0.0", "htm": "^3.1.1" }, diff --git a/packages/signals/CHANGELOG.md b/packages/signals/CHANGELOG.md index 9346c4e4..d3a0b54c 100644 --- a/packages/signals/CHANGELOG.md +++ b/packages/signals/CHANGELOG.md @@ -1,5 +1,164 @@ # @remote-dom/signals +## 2.0.0 + +### Minor Changes + +- [#389](https://github.com/Shopify/remote-dom/pull/389) [`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403) Thanks [@lemonmade](https://github.com/lemonmade)! - ## Added native support for synchronizing attributes and event listeners + + Previously, Remote DOM only offered “remote properties” as a way to synchronize element state between the host and remote environments. These remote properties effectively synchronize a subset of a custom element’s instance properties. The `RemoteElement` class offers [a declarative way to define the properties that should be synchronized](/packages/core/README.md#remote-properties). + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return ['label']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.label = 'Hello, World!'; + ``` + + The same `remoteProperties` configuration can create special handling for attributes and event listeners. By default, a remote property is automatically updated when setting an [attribute](https://developer.mozilla.org/en-US/docs/Glossary/Attribute) of the same name: + + ```ts + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + + // myElement.label === 'Hello, World!', and this value is synchronized + // with the host environment as a “remote property” + ``` + + Similarly, a remote property can be automatically updated when adding an event listener based on a conventional `on` property naming prefix: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteProperties() { + return { + onChange: { + event: true, + }, + }; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // This adds a callback property that is synchronized with the host environment + myElement.onChange = () => console.log('Changed!'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + ``` + + These utilities are handy, but they don’t align with patterns in native DOM elements, particularly when it comes to events. Now, both of these can be represented in a fashion that is more conventional in HTML. The `remoteAttributes` configuration allows you to define a set of element attributes that will be synchronized directly the host environment, instead of being treated as instance properties: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteAttributes() { + return ['label']; + } + + // If you want to add instance properties, you can do it with getters and + // setters that manipulate the attribute value: + // + // get label() { + // return this.getAttribute('label'); + // } + // + // set label(value) { + // this.setAttribute('label', value); + // } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + myElement.setAttribute('label', 'Hello, World!'); + ``` + + Similarly, the `remoteEvents` configuration allows you to define a set of event listeners that will be synchronized directly with the host environment: + + ```ts + import {RemoteElement} from '@remote-dom/core/elements'; + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + // And so does this, but using the `addEventListener` method instead + myElement.addEventListener('change', () => console.log('Changed!')); + + // No `myElement.onChange` property is created + ``` + + The `remoteProperties` configuration will continue to be supported for cases where you want to synchronize instance properties. Because instance properties can be any JavaScript type, properties are the highest-fidelity field that can be synchronized between the remote and host environments. However, adding event listeners using the `remoteProperties.event` configuration is **deprecated and will be removed in the next major version**. You should use the `remoteEvents` configuration instead. If you were previously defining remote properties which only accepted strings, consider using the `remoteAttributes` configuration instead, which stores the value entirely in an HTML attribute instead. + + This change is being released in a backwards-compatible way, so you can continue to use the existing `remoteProperties` configuration on host and/or remote environments without any code changes. + + All host utilities have been updated to support the new `attributes` and `eventListeners` property that are synchronized with the remote environment. This includes updates to the [React](/packages/react/README.md#event-listener-props) and [Preact hosts to map events to conventional callback props](/packages/preact/README.md#event-listener-props), and updates to the [`DOMRemoteReceiver` class](/packages/core/README.md#domremotereceiver), which now applies fields to the host element exactly as they were applied in the remote environment: + + ```ts + // Remote environment: + + class MyElement extends RemoteElement { + static get remoteEvents() { + return ['change']; + } + } + + customElements.define('my-element', MyElement); + + const myElement = document.createElement('my-element'); + + myElement.addEventListener('change', (event) => { + console.log('Changed! New value: ', event.detail); + }); + + // Host environment: + + class MyElement extends HTMLElement { + connectedCallback() { + // Emit a change event on this element, with detail that will be passed + // to the remote environment + this.addEventListener('change', (event) => { + event.stopImmediatePropagation(); + + this.dispatchEvent( + new CustomEvent('change', { + detail: this.value, + }), + ); + }); + } + + // Additional implementation details of the host custom element... + } + + customElements.define('my-element', MyElement); + ``` + +### Patch Changes + +- Updated dependencies [[`2479b21`](https://github.com/Shopify/remote-dom/commit/2479b21406f6149063bfc095dbb6c3a019386403)]: + - @remote-dom/core@1.5.0 + ## 1.1.0 ### Minor Changes diff --git a/packages/signals/package.json b/packages/signals/package.json index 8a3ad3b5..06bb4c63 100644 --- a/packages/signals/package.json +++ b/packages/signals/package.json @@ -6,7 +6,7 @@ "access": "public", "@remote-dom/registry": "https://registry.npmjs.org" }, - "version": "1.1.0", + "version": "2.0.0", "engines": { "node": ">=14.0.0" }, @@ -30,7 +30,7 @@ }, "peerDependencies": { "@preact/signals-core": "^1.3.0", - "@remote-dom/core": "workspace:^1.4.0" + "@remote-dom/core": "workspace:^1.5.0" }, "peerDependenciesMeta": { "@preact/signals-core": { @@ -42,7 +42,7 @@ }, "devDependencies": { "@preact/signals-core": "^1.7.0", - "@remote-dom/core": "workspace:^1.4.0" + "@remote-dom/core": "workspace:^1.5.0" }, "browserslist": [ "defaults and not dead" diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 8225f99b..ceaf5825 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -124,7 +124,7 @@ importers: packages/core: dependencies: '@remote-dom/polyfill': - specifier: workspace:^1.2.0 + specifier: workspace:^1.3.0 version: link:../polyfill htm: specifier: ^3.1.1 @@ -142,10 +142,10 @@ importers: packages/preact: dependencies: '@remote-dom/core': - specifier: workspace:^1.4.0 + specifier: workspace:^1.5.0 version: link:../core '@remote-dom/signals': - specifier: workspace:^1.1.0 + specifier: workspace:^2.0.0 version: link:../signals htm: specifier: ^3.1.1 @@ -167,7 +167,7 @@ importers: packages/react: dependencies: '@remote-dom/core': - specifier: workspace:^1.4.0 + specifier: workspace:^1.5.0 version: link:../core '@types/react': specifier: ^18.0.0 @@ -195,7 +195,7 @@ importers: specifier: ^1.7.0 version: 1.7.0 '@remote-dom/core': - specifier: workspace:^1.4.0 + specifier: workspace:^1.5.0 version: link:../core packages: