Skip to content

Commit

Permalink
Adds data property type to element()
Browse files Browse the repository at this point in the history
  • Loading branch information
stephband committed Jul 26, 2024
1 parent d8c6c41 commit 02fd105
Show file tree
Hide file tree
Showing 4 changed files with 56 additions and 14 deletions.
13 changes: 13 additions & 0 deletions element/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,19 @@ definitions.
- `"tokens"` - defines a tokens attribute (think `class`) and a string setter / TokenList getter property
- `"src"` - defines a URL attribute that links to a data property (TODO)
- `"module"` - defines a URL attribute that ... (TODO)
- `"data"` - defines a property that exposes Literal's `data` object. This is
useful if you are building a closed system where literal custom elements are
authored inside literal templates, as data can be passed efficiently from
template to custom element.

```html
<template is="literal-html">
<p>The light is <toggle-button data="${ data }"></toggle-button></p>
</template>
```

It is probably less useful for publishing custom elements intended for general
consumption.

Changes to properties defined in this way are signalled to Literal's renderer.
Literal updates the shadow DOM (not the whole thing, just the parts that need
Expand Down
2 changes: 1 addition & 1 deletion element/element.js
Original file line number Diff line number Diff line change
Expand Up @@ -113,7 +113,7 @@ export default function LiteralElement(tag, lifecycle = {}, properties = {}, par
// Create templates. This is a crude way to do it, and we should probably
// isolate templates in the shadow from those outside with a separate
// template cache (based around shadow.getElementById()?)... but... it'll
// do for now
// do for now... ?
if (window.DEBUG) document.head.appendChild(create('comment', ' Templates for ' + name));
document.head.append.apply(document.head, templates);

Expand Down
5 changes: 5 additions & 0 deletions element/property.js
Original file line number Diff line number Diff line change
Expand Up @@ -110,6 +110,11 @@ const types = {
.then((object) => this[symbol].value = object)
.catch((error) => console.error(error));
}
}),

data: (name, symbol) => ({
get: function() { return getInternals(this).renderer.data; },
set: function(data) { getInternals(this).renderer.push(data); }
})
};

Expand Down
50 changes: 37 additions & 13 deletions modules/template.js
Original file line number Diff line number Diff line change
Expand Up @@ -92,7 +92,7 @@ export class LiteralDOM {
const children = content.childNodes;

// The first node may change. The last node is always the last node.
this.#data = Signal.of(Data.of(data));
this.#data = Signal.of(Data.objectOf(data));
this.#first = children[0];
this.#last = children[children.length - 1];

Expand Down Expand Up @@ -131,8 +131,8 @@ export class LiteralDOM {
}

/*
template.firstNode
template.lastNode
.firstNode
.lastNode
*/

get firstNode() {
Expand All @@ -148,24 +148,40 @@ export class LiteralDOM {
return this.#last;
}

/*
.push()
*/
/**
.data
Read-only property exposing (literal's `data` proxy of) the currently
rendered object. This is the same object available as `data` inside a
literal template. Setting properties on this object causes the DOM to update.
**/

get data() {
const data = this.#data.value;
return Data.of(data) || data;
}

/**
.push(object)
Rerenders and binds the DOM to (literal's `data` proxy of) `object`. This is
the same object available as `data` inside the template.
**/

push(object) {
if (this.status === 'done') throw new Error('Renderer is done, cannot .push() data');

// Make sure we have the raw object
object = Data.objectOf(object);

// Dedup
if (this.#data === object) return;
if (this.#data.value === object) return;

// If we are coming out of sleep put content back in the DOM
if (this.#data === null && object !== null) {
if (this.#data.value === null && object !== null) {
this.lastNode.before(this.content);
}

// Causes renderers to .invalidate() because they are dependent on
// this.#data signal
this.#data.value = Data.of(object);
// Causes renderers dependent on this signal to .invalidate()
this.#data.value = object;

// If object is null put template to sleep: remove all but the last node
// to the content fragment and blank out the last text node, which we
Expand Down Expand Up @@ -246,8 +262,16 @@ export default class LiteralRenderer extends LiteralDOM {
}

static of(html) {
const template = create('template', html);
return new LiteralRenderer(template);
return LiteralRenderer.from(create('template', html));
}

static from(template, parent) {
const id = identify(template, 'literal-');
const fragment = template.content;
const compiled = cache[id]
|| (cache[id] = LiteralRenderer.compile(fragment, options, '#' + id));

return new LiteralDOM(compiled, fragment.cloneNode(true), parent = template.parentElement);
}

static compile(fragment, options, src) {
Expand Down

0 comments on commit 02fd105

Please sign in to comment.