Skip to content

Releases: dgp1130/HydroActive

releases/0.1.7

releases/0.1.7 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 15 Dec 00:52

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 - hydrate and Dehydrated.prototype.hydrate now 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 global HTMLElementHydrationParamsMap interface (typically via the Properties type) which hydrate uses 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.effect now supports a custom scheduler.

  • e80c0c9 - Implements the initial draft of the On-Demand Definitions community protocol. Components now automatically create a static define method which calls customElements.define rather than doing so immediately. This allows components to be tree shaken when unused. Dehydrated.prototype.access and Dehydrated.prototype.hydrate both now call the static define property 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 defineSignalComponent and defineBaseComponent no 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 new define helper, 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 .define in the top-level scope, the general recommendation is that any custom element should either:

    1. Be rendered with defer-hydration - defer-hydration implies that whatever code hydrates that element is responsible for calling .define on the appropriate custom element class (which automatically happens when calling .hydrate).
    2. Be defined in the top-level scope - An element without defer-hydration is 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.
  • 9d9219f - Renames defineSignalComponent and defineBaseComponent to just component and baseComponent. This reflects the change in e80c0c9 whereby these functions no longer actually define the component they are creating as they no longer call customElements.define.

  • 283fee9 - Fix: Signals no longer notify consumers which are added during a consumer's notification from a signal change.

  • ef6033f - Fix: Exports HydroActiveComponent type.

0.1.6

0.1.6 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 11 Sep 03:54

https://www.npmjs.com/package/hydroactive/v/0.1.5

Updates the README.md to include a more accurate example.

0.1.5

0.1.5 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 11 Sep 03:49

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.write as a corollary to ElementAccessor.prototype.read. Useful for non-reactive / one-time writes to the DOM.
  • 55f3344 - Added AttrAccessor.prototype.write as a corollary to AttrAccess.prototype.read. Useful for non-reactive / one-time writes to the DOM.
  • 027fccd - Deletes ElementRef. Prefer ElementAccessor or ComponentAccessor.
  • e26e4d8 - Adds Connectable. This is an interface which declares Connectable.prototype.connected, used by ComponentAccessor to bind to the lifecycle of a component.
  • d96979b - Adds ReactiveRoot. This is an interface which declares ReactiveRoot.prototype.effect, used by SignalComponentAccessor to bind effects to a component's lifecycle.
  • 3f9a622 - Adds SignalComponentAccessor. This is a subclass of ComponentAccessor but with knowledge of signals, meaning it implements ReactiveRoot and provides .effect.
  • 194b010 - Updates defineComponent to provide a SignalComponentAccessor. This means you can call host.effect(() => { /* ... */ }) inside a component.
  • 709874c - Deletes ComponentRef. In general you can use host / SignalComponentAccessor to fill the same need. However if you only need .connected you should accept a Connectable and if you only need .effect you should accept a ReactiveRoot.
  • 0050cfe - Renames defineComponent to defineSignalComponent to make clear the dependency on signals.
  • 8d5012b - Adds defineBaseComponent as a corollary to defineSignalComponent but which provides a ComponentAccessor (as opposed to a SignalComponentAccessor) which does not implement ReactiveRoot (no .effect) and therefore has no dependency on signals. If a component is simple enough to not require signals, prefer defineBaseComponent for an even smaller bundle size.
  • 5114b34 - Sets sideEffects: false in package.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

0.1.4 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 10 Aug 06:28

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

0.1.3 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 07 Aug 02:02

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

0.1.2 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 13 May 06:04

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

0.1.1 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 03 Mar 07:51

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 provides query and queryAll methods.
  • 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.
  • live and bind - Work basically the same as before, but have been moved into standalone functions which accept an ElementAccessor as a parameter, rather than being methods on ComponentRef. 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

0.1.0 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 18 Feb 00:15

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

0.0.5 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 02 Dec 23:26

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 - $.live and $.bind now accept Element references 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

0.0.4 Pre-release
Pre-release

Choose a tag to compare

@dgp1130 dgp1130 released this 07 Jan 07:23

https://www.npmjs.com/package/hydroactive/v/0.0.4

Changelog:

  • 519e458 - Deletes factory() and templating implementation, moving hydrate() out of /testing.js export as a replacement. Instead of using factory() 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 then hydrate() it directly. See examples in 519e458.
  • 6244237 - Defines custom elements directly in component('my-tag-name', () => { /* ... */ }). A separate customElements.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.