Releases: dgp1130/HydroActive
Release list
releases/0.1.7
BREAKING CHANGES: The defineSignalComponent and defineBaseComponent functions have been renamed to component and baseComponent respectively. The new versions do not immediately call customElements.define. Users must either manually define the component (preferably via MyComponent.define()) immediately after calling component / baseComponent or rely on a consumer of the component calling MyComponent.define() prior to using it (which Dehydrated.prototype.access and Dehydrated.prototype.hydrate will both do automatically). See the below changelog for more details.
-
769332b - Applies component definition returned from
defineSignalComponent/defineBaseComponent. Returned values are applied to the component instance based on their property names and create a public API for the component.export const MyComponent = defineSignalComponent('my-component', (host) => { return { log(): void { console.log('Hello, World!'); }, }; }); const instance = document.querySelector('my-component')!; instance.log(); // Logs 'Hello, World!'
-
a772872 -
hydrateandDehydrated.prototype.hydratenow accept hydration "parameters" which are applied to the component object prior to triggering hydration. This allows a parent component to required data to a child component during hydration. Types are defined on the globalHTMLElementHydrationParamsMapinterface (typically via thePropertiestype) whichhydrateuses to type check parameters correctly.import { Properties, defineSignalComponent } from 'hydroactive'; export class MyElement extends HTMLElement { declare tagName: 'MY-ELEMENT'; // Necessary. public foo!: string; public bar?: number; public baz?: boolean; } customElements.define('my-element', MyElement); declare global { interface HTMLElementTagNameMap { 'my-element': MyElement; } interface HTMLElementHydrationParamsMap { 'my-element': Properties<MyElement, { required: 'foo', optional: 'bar' | 'baz', }>; } } export const MyComponent = defineSignalComponent('my-component', (host) => { host.query('my-element').hydrate(MyElement, { foo: 'test', // Required bar: 1234, // Optional baz: true, // Optional }); });
Currently it is not straightforward to define and access component parameters in a the functional authoring format of HydroActive components (hence why the above example uses a native custom element), this will be added later.
-
8b93477 - Adds
untracked. This allows reading signal values without adding them as a dependency of any currently active consumer.const foo = signal('foo'); const bar = signal('bar'); // Re-runs when `foo` changes, but not when `bar` changes because it is untracked. host.effect(() => { const f = foo(); const b = untracked(() => bar()); console.log(`${f} - ${b}`); });
-
ab672f4 -
ReactiveRoot.prototype.effectnow supports a custom scheduler. -
e80c0c9 - Implements the initial draft of the On-Demand Definitions community protocol. Components now automatically create a static
definemethod which callscustomElements.definerather than doing so immediately. This allows components to be tree shaken when unused.Dehydrated.prototype.accessandDehydrated.prototype.hydrateboth now call the staticdefineproperty of the input class if it exists. This allows a HydroActive component to automatically define any custom elements it depends upon.BREAKING CHANGE: Note that this change implicitly means that
defineSignalComponentanddefineBaseComponentno longer actually define their custom elements immediately (name change in a follow up commit). Components must be manually defined after class creation. This can either be done in the top-level scope, preferrable with the newdefinehelper, or implicitly when those components are used in.access/.hydrate.For example, given the pre-rendered HTML:
<my-other-component> <my-component defer-hydration></my-component> </my-other-component>
We can implement the two components like so:
import { component } from 'hydroactive'; export const MyComponent = component('my-component', () => { return { doSomething(): void { /* ... */ } }; }); // No need to call `MyComponent.define()` here as it is implicitly defined below when used. export const MyOtherComponent = component('my-other-component', (host) => { // `.access` Implicitly calls `MyComponent.define()`. host.query('my-component').access(MyComponent).element.doSomething(); }); MyOtherComponent.define(); // Should define in top-level scope so the pre-rendered HTML upgrades.
Regarding the question of whether or not to call
.definein the top-level scope, the general recommendation is that any custom element should either:- Be rendered with
defer-hydration-defer-hydrationimplies that whatever code hydrates that element is responsible for calling.defineon the appropriate custom element class (which automatically happens when calling.hydrate). - Be defined in the top-level scope - An element without
defer-hydrationis expected to become active on page load, therefore it needs to define itself in the top-level scope and cannot rely on any consumers defining it automatically.
- Be rendered with
-
9d9219f - Renames
defineSignalComponentanddefineBaseComponentto justcomponentandbaseComponent. This reflects the change in e80c0c9 whereby these functions no longer actually define the component they are creating as they no longer callcustomElements.define. -
283fee9 - Fix: Signals no longer notify consumers which are added during a consumer's notification from a signal change.
-
ef6033f - Fix: Exports
HydroActiveComponenttype.
0.1.6
https://www.npmjs.com/package/hydroactive/v/0.1.5
Updates the README.md to include a more accurate example.
0.1.5
https://www.npmjs.com/package/hydroactive/v/0.1.5
This release lands a number of changes focused on providing stronger abstractions. Notably, ComponentRef and ElementRef have been removed and host has been upgraded to a SignalComponentAccessor which supports .connected and .effect.
- 07e5ffc - Added
ElementAccessor.prototype.writeas a corollary toElementAccessor.prototype.read. Useful for non-reactive / one-time writes to the DOM. - 55f3344 - Added
AttrAccessor.prototype.writeas a corollary toAttrAccess.prototype.read. Useful for non-reactive / one-time writes to the DOM. - 027fccd - Deletes
ElementRef. PreferElementAccessororComponentAccessor. - e26e4d8 - Adds
Connectable. This is an interface which declaresConnectable.prototype.connected, used byComponentAccessorto bind to the lifecycle of a component. - d96979b - Adds
ReactiveRoot. This is an interface which declaresReactiveRoot.prototype.effect, used bySignalComponentAccessorto bind effects to a component's lifecycle. - 3f9a622 - Adds
SignalComponentAccessor. This is a subclass ofComponentAccessorbut with knowledge of signals, meaning it implementsReactiveRootand provides.effect. - 194b010 - Updates
defineComponentto provide aSignalComponentAccessor. This means you can callhost.effect(() => { /* ... */ })inside a component. - 709874c - Deletes
ComponentRef. In general you can usehost/SignalComponentAccessorto fill the same need. However if you only need.connectedyou should accept aConnectableand if you only need.effectyou should accept aReactiveRoot. - 0050cfe - Renames
defineComponenttodefineSignalComponentto make clear the dependency on signals. - 8d5012b - Adds
defineBaseComponentas a corollary todefineSignalComponentbut which provides aComponentAccessor(as opposed to aSignalComponentAccessor) which does not implementReactiveRoot(no.effect) and therefore has no dependency on signals. If a component is simple enough to not require signals, preferdefineBaseComponentfor an even smaller bundle size. - 5114b34 - Sets
sideEffects: falseinpackage.json. This should improve bundle sizes as HydroActive does not rely on top-level side effects and bundlers can take advantage of that to more aggressively eliminate unused code.
0.1.4
https://www.npmjs.com/package/hydroactive/v/0.1.4
This release adds support for shadow roots. Now on any Queryable you can call .shadow to get another Queryable scoped to that element's shadow root. This works like so:
defineComponent('hello-world', (comp, host) => {
host.query('span'); // Queries light DOM.
host.shadow.query('span'); // Queries shadow DOM.
});This works with open shadow roots across the board. It was implemented via a new QueryRoot class which serves as as base implementation of the Queryable interface. To support shadow roots, QueryRoot as well as Dehydrated and ElementAccessor accept a getClosedShadowRoot function.
import { QueryRoot } from 'hydroactive';
const el: Element = /* ... */;
const root = QueryRoot.from(el, /* getClosedShadowRoot */ () => shadowRootOfEl);
root.shadow.query('...'); // Queries `shadowRootOfEl`.This parameter does not need to be provided for open shadow roots (which are easily accessible by QueryRoot without being explicitly given as an input), but is needed for closed shadow roots as they are otherwise inaccessible.
The host parameter of the defineComponent callback has been upgraded to ComponentAccessor (a new subclass of ElementAccessor specific to HydroActiveComponent elements) which automatically provides this function, so host.shadow naturally supports closed shadow roots with no additional effort necessary. This implementation also does not leak the closed shadow root or ElementInternals object on the HydroActiveComponent or anywhere else accessible outside the component and its ComponentAccessor (which is naturally private to the component), so its shadow root is truly closed.
0.1.3
https://www.npmjs.com/package/hydroactive/v/0.1.3
Fixed publish script as the entire JS code has not been published since v0.1.0.
0.1.2
https://www.npmjs.com/package/hydroactive/v/0.1.2
Adds README to the published output so it displays on the NPM package page.
0.1.1
https://www.npmjs.com/package/hydroactive/v/0.1.1
This release adds improved hydration support and reworks the core DOM manipulation APIs to decouple utilities like comp.live into smaller, more composable pieces. ComponentRef is unchanged, but defineComponent now provides a second host parameter (an ElementAccessor of the host element) which serves as an entry point. See the README for a basic introduction into how live works under the new system.
There are a few new concepts which make this work:
Queryable- An interface which providesqueryandqueryAllmethods.Dehydrated- A class which wraps a potentially dehydrated element and only lets you unwrap it by asserting that the element is hydrated or actually hydrating it yourself.ElementAccessor- Wraps an element and provides convenient functions for reading and interacting with it.AttrAccessor- Wraps an element and a specific attribute and provides convenient functions for reading and interacting with it.liveandbind- Work basically the same as before, but have been moved into standalone functions which accept anElementAccessoras a parameter, rather than being methods onComponentRef. Part of the goal here is to make signals tree shakable when not used.
The old APIs under ComponentRef are likely to be removed and heavily reduced. comp.live is basically obsoleted by the new live standalone function for instance. Some form of ComponentRef will likely still exist as a means of accessing ComponentRef.prototype.connected, but what form that takes long term is still TBD.
Check out this YouTube video discussing the changes in this release and the design decisions which went into hydration support.
0.1.0
https://www.npmjs.com/package/hydroactive/v/0.1.0
BREAKING CHANGES: Basically everything. This is effectively starting a rewrite of HydroActive. Most of the core developer experience is pretty similar, there's no major radical changes here. However this does not yet have feature parity with 0.0.5, so a number of APIs just don't exist yet.
See this new YouTube video for a breakdown of the project and an introduction to 0.1.0 features. The video does not contain a comprehensive list of all 0.1.0 HydroActive features, but future videos and content should continue to expand on it going forward. Release notes will also be more specific about individual changes in each incremental release.
0.0.5
https://www.npmjs.com/package/hydroactive/v/0.0.5
Changelog:
- e79f9cf - Adds support for binding boolean values to attributes. If the boolean is true, the attribute is set to empty string, and if the boolean is false the attribute is removed.
- a8d1bc5 -
$.liveand$.bindnow acceptElementreferences in addition to string queries. - cee36e7 - Adds
$.scope. This allows chaining additional queries limited to descendents of the scoped selector. - b7d91dd - Disallows extra properties not supported by the component to be passed to
hydrate.
0.0.4
https://www.npmjs.com/package/hydroactive/v/0.0.4
Changelog:
- 519e458 - Deletes
factory()and templating implementation, movinghydrate()out of/testing.jsexport as a replacement. Instead of usingfactory()to create a new instance of an element based on a template with a special attribute, prerender the component inside a template, clone that template, and thenhydrate()it directly. See examples in 519e458. - 6244237 - Defines custom elements directly in
component('my-tag-name', () => { /* ... */ }). A separatecustomElements.define()call is no longer necessary. This makes it impossible to hydrate a HydroActive component without defining its custom element first, which removes a foot-gun that leaked dehydrated elements into user-space. - 144dd0f - Renames
$.hydrate()to$.read(). - c5b6d7c - Adds new implementation of
$.hydrate()(originally named$.hydrateElement()but subsequently renamed to$.hydrate()in 6842fb7). This is like$.read()and$.query(), except that it requires the target to be dehydrated, and triggers hydration immediately with the given props. This allows fine-grained orchestration of hydration timing. - 6cbaa08 - Updates
$.query()and$.queryAll()to throw when they find a custom element. Use$.read()or$.hydrate()instead. - 6c85a54 - Removes type check restriction on
$.query()and$.queryAll()'s selector. This was originally intended to catch accidental dehydrated queries of custom elements, but this is now asserted more thoroughly in 6cbaa08 and is obsoleted.