diff --git a/CHANGELOG.md b/CHANGELOG.md index cc4aad2..e364bb8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Unreleased] +## [v1.0.6] - 2026-03-16 + +### Added +- Add `IotaElement` as base class for custom elements + ## [v1.0.5] - 2026-03-15 ### Fixed diff --git a/iota-element.js b/iota-element.js new file mode 100644 index 0000000..fd88043 --- /dev/null +++ b/iota-element.js @@ -0,0 +1,236 @@ +import { html } from '@aegisjsproject/core/parsers/html.js'; +import { css } from '@aegisjsproject/core/parsers/css.js'; +import { $render } from './watcher.js'; + +export class IotaElement extends HTMLElement { + static shadowRootMode = 'open'; + static shadowRootClonable = false; + static shadowRootDelegatesFocus = false; + static shadowRootSerializable = false; + static shadowRootSlotAssignment = 'named'; + static shadowRootReferenceTarget = null; + + #shadow = this.attachShadow({ + mode: this.constructor.shadowRootMode, + clonable: this.constructor.shadowRootClonable, + delegatesFocus: this.constructor.shadowRootDelegatesFocus, + referenceTarget: this.constructor.shadowRootReferenceTarget, // No default value + serializable: this.constructor.shadowRootSerializable, + slotAssignment: this.constructor.shadowRootSlotAssignment, + }); + + #internals = this.attachInternals(); + #stack = new DisposableStack(); + #controller = this.#stack.adopt(new AbortController(), c => c.abort(new DOMException('Element was disposed.', 'AbortError'))); + #attrChanges = new Map(); + #updating = false; + #initialized = false; + #updateRequests = new Map(); + + async connectedCallback() { + if (! this.#initialized) { + this.#initialized = true; + + if (this.styles instanceof CSSStyleSheet) { + this.#shadow.adoptedStyleSheets = [this.styles]; + } else if (Array.isArray(this.styles)) { + this.#shadow.adoptedStyleSheets = this.styles; + } else if (typeof this.styles === 'string') { + this.#shadow.adoptedStyleSheets = [css`${this.styles}`]; + } + + if (this.html instanceof Node) { + $render(this.html, this.#shadow); + } else if (typeof this.html === 'string') { + $render(html`${this.html}`, this.#shadow); + } + } + + if (this.#stack.disposed) { + this.#stack = new DisposableStack(); + } + + if (this.#controller.signal.aborted) { + this.#controller = this.#stack.adopt(new AbortController(), c => c.abort(new DOMException('Element disposed.', 'AbortError'))); + } + + // Always add this since parent class can handle events too + this.addEventListener('command', this, { signal: this.#controller.signal }); + this.#internals.states.delete('disposed'); + + await this.#runUpdate('connected', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + }); + + this.#internals.states.add('ready'); + } + + adoptedCallback() { + this.#runUpdate('adopted', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + }); + } + + async disconnectedCallback() { + await this.#runUpdate('disconnected', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + }); + + this[Symbol.dispose](); + } + + attributeChangedCallback(name, oldValue, newValue) { + this.#attrChanges.set(name, { newValue, oldValue }); + + if (! this.#updating) { + this.#updating = true; + + queueMicrotask(() => { + const attributes = Object.fromEntries(this.#attrChanges); + this.#attrChanges.clear(); + this.#updating = false; + + this.#runUpdate('attributeChanged', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + attributes, + }); + }); + } + } + + handleEvent(event) { + if (! (event instanceof Event)) { + this.#throw(new TypeError('Handle event did not receive an event.')); + } else if (! this.#stack.disposed) { + if (event.type === 'command' && event.command === '--dispose') { + this[Symbol.dispose](); + } else if (event.type === 'command' && event.command === '--request-dispose') { + const req = new Event('requestdispose', { cancelable: true, bubbles: false }); + this.dispatchEvent(req); + + if (! req.defaultPrevented) { + this[Symbol.dispose](); + } + } else { + this.#runUpdate('eventDispatched', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + event, + }); + } + } else { + this.removeEventListener(event.type, this); + } + } + + [Symbol.dispose]() { + this.#runUpdate('dispose', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + }); + + this.#attrChanges.clear(); + this.#stack.dispose(); + this.#updateRequests.clear(); + this.#internals.states.add('disposed'); + this.#internals.states.delete('ready'); + } + + adopt(what, onDispose) { + return this.#stack.adopt(what, onDispose); + } + + abort(reason) { + this.#controller.abort(reason); + } + + defer(onDispose) { + this.#stack.defer(onDispose); + } + + requestUpdate(type, context = {}) { + if (! this.#stack.disposed) { + // If size !== 0, already scheduled + if (this.#updateRequests.size === 0) { + this.#updateRequests.set(type, context); + queueMicrotask(() => { + const requests = Object.fromEntries(this.#updateRequests); + this.#updateRequests.clear(); + + this.#runUpdate('custom', { + signal: this.#controller.signal, + shadow: this.#shadow, + internals: this.#internals, + requests, + }); + }); + } else if (this.#updateRequests.has(type)) { + const oldContext = this.#updateRequests.get(type); + this.#updateRequests.set(type, { ...oldContext, ...context }); + } else { + this.#updateRequests.set(type, context); + } + } + } + + use(what) { + return this.#stack.use(what); + } + + #throw(err) { + if (Error.isError(err)) { + this.dispatchEvent(new ErrorEvent('error', { message: err.message, error: err })); + } else { + const type = typeof err === 'object' ? err?.constructor?.name ?? 'null' : typeof err; + const e = new TypeError(`#throw expects an Error but got a ${type}.`, { cause: err }); + this.dispatchEvent(new ErrorEvent('error', { message: e.message, error: e })); + } + } + + async #runUpdate(type, context = {}) { + if (typeof this.update === 'function' && ! this.#stack.disposed) { + const stack = new AsyncDisposableStack(); + context.stack = stack; + + await Promise.try(() => this.update(type, context)) + .catch(err => this.#throw(err)) + .finally(() => stack.disposeAsync()); + } + } + + get aborted() { + return this.#controller.signal.aborted; + } + + get disposed() { + return this.#stack.disposed; + } + + get signal() { + return this.#controller.signal; + } + + static register(tagName, { + registry = globalThis?.customElements, + ...options + } = {}) { + if (typeof tagName !== 'string' || ! tagName.includes('-')) { + throw new TypeError('Tag name must be a non-empty string with a "-".'); + } else if (! registry.get(tagName)) { + registry.define(tagName, this, options); + return true; + } else { + return registry.get(tagName) === this; + } + } +}; diff --git a/iota.js b/iota.js index 45355e6..5ff3fee 100644 --- a/iota.js +++ b/iota.js @@ -10,3 +10,4 @@ export { AttrComputed, AttrState, $attr, $checked, $classList, $disabled, $hidden, $inert, $muted, $open, $readOnly, $requried, $selected, $value, $data, $aria, } from './attr.js'; +export { IotaElement } from './iota-element.js'; diff --git a/iota.test.js b/iota.test.js index d7e31a7..2e490f4 100644 --- a/iota.test.js +++ b/iota.test.js @@ -2,16 +2,11 @@ import '@shgysk8zer0/polyfills'; import test from 'node:test'; import assert from 'node:assert/strict'; import { - getRef, - RegistryKey, - hasSignalRef, - getSignalFromRef, - unregisterSignal, - $signal, - $computed, - $watch, - $unwatch -} from '@aegisjsproject/iota'; + getRef, RegistryKey, +} from './refs.js'; +import { hasSignalRef, getSignalFromRef, unregisterSignal } from './registry.js'; +import { $signal, $computed } from './disposable.js'; +import { $watch, $unwatch } from './watcher.js'; test('RegistryKey generation and disposal', () => { const key = getRef('test'); diff --git a/package-lock.json b/package-lock.json index fbe6fa7..0ead91e 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,12 +1,12 @@ { "name": "@aegisjsproject/iota", - "version": "1.0.5", + "version": "1.0.6", "lockfileVersion": 2, "requires": true, "packages": { "": { "name": "@aegisjsproject/iota", - "version": "1.0.5", + "version": "1.0.6", "funding": [ { "type": "librepay", @@ -19,6 +19,7 @@ ], "license": "MIT", "dependencies": { + "@aegisjsproject/core": "^0.2.34", "@aegisjsproject/escape": "^1.0.4", "@shgysk8zer0/signals": "^0.0.3" }, @@ -36,6 +37,56 @@ "node": ">=24.10.0" } }, + "node_modules/@aegisjsproject/callback-registry": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@aegisjsproject/callback-registry/-/callback-registry-2.0.3.tgz", + "integrity": "sha512-iJP7iESlo62AxmPH4WvAfuRzddTUWKG7dvvQbFBojixmeM5tOimsjDzIbGhPcwGLw8RigFhJiw0Z79OVGIufsA==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "dependencies": { + "@shgysk8zer0/signals": "^0.0.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aegisjsproject/core": { + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@aegisjsproject/core/-/core-0.2.34.tgz", + "integrity": "sha512-RpNbwmmnjI0X2vPcwszawMyWWvLGU8w6CCVKwVlygcI+gq41KuC+kJlPcda1qjDoIKQy+IbzFgxNdDzHaHIDXg==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "dependencies": { + "@aegisjsproject/callback-registry": "^2.0.2", + "@aegisjsproject/escape": "^1.0.4", + "@aegisjsproject/parsers": "^0.1.6", + "@aegisjsproject/router": "^1.1.15", + "@aegisjsproject/sanitizer": "^0.2.4", + "@aegisjsproject/state": "^1.0.7", + "@aegisjsproject/url": "^1.0.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aegisjsproject/dev-server": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@aegisjsproject/dev-server/-/dev-server-1.0.6.tgz", @@ -98,11 +149,55 @@ "node": ">=24.10.0" } }, + "node_modules/@aegisjsproject/parsers": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@aegisjsproject/parsers/-/parsers-0.1.6.tgz", + "integrity": "sha512-5Fy5G6lY4m61TyWhERBIbsIiW97efY40q5ocFHkkHPN1Yr3dCZsRaA00kvafAp/Elo0HDjO3U9c7vjncThMTTg==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "dependencies": { + "@aegisjsproject/sanitizer": "^0.2.4", + "@aegisjsproject/url": "^1.0.3" + }, + "engines": { + "node": ">=18.0.0" + } + }, + "node_modules/@aegisjsproject/router": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@aegisjsproject/router/-/router-1.1.15.tgz", + "integrity": "sha512-12d971iOqh8HsetfY7nl6gCOFhhHD1h4YOQ8KC4fSk2IZgVGjt6Z9UA1nY1bxbMTGzJ9UzOdOGle4L9x2de5kg==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "dependencies": { + "@aegisjsproject/state": "^1.0.5" + }, + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aegisjsproject/sanitizer": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@aegisjsproject/sanitizer/-/sanitizer-0.2.4.tgz", "integrity": "sha512-9EMby+BWrn0Pa0YEO6th9ozB4jbelpbii5Or954rXlnQROETR3HcM6ZgNkgsT+yqeUSolwG7+iFF8Lnqe8ePAg==", - "dev": true, "funding": [ { "type": "librepay", @@ -117,6 +212,25 @@ "node": ">=18.0.0" } }, + "node_modules/@aegisjsproject/state": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@aegisjsproject/state/-/state-1.0.7.tgz", + "integrity": "sha512-RsMJbyEC4RtFsMzRFCvhUiZS61IwxO6SRev+Sm9NJ7EG2yFJsup6Md7NVsrfZB5pS+NI+wkDNznjjKwJw5ViNw==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@aegisjsproject/trusted-types": { "version": "1.0.2", "resolved": "https://registry.npmjs.org/@aegisjsproject/trusted-types/-/trusted-types-1.0.2.tgz", @@ -137,6 +251,25 @@ "node": ">=18.0.0" } }, + "node_modules/@aegisjsproject/url": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@aegisjsproject/url/-/url-1.0.3.tgz", + "integrity": "sha512-gbk9oRpNxOeYDM/BxfsEYoG0DpFu2++WW9agwlkoVivUGlJyLawmsUQAn9+rvw2fm+Pmr1EUXLbRaxi7drTOKg==", + "funding": [ + { + "type": "librepay", + "url": "https://liberapay.com/shgysk8zer0" + }, + { + "type": "github", + "url": "https://github.com/sponsors/shgysk8zer0" + } + ], + "license": "MIT", + "engines": { + "node": ">=18.0.0" + } + }, "node_modules/@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", @@ -1904,6 +2037,28 @@ } }, "dependencies": { + "@aegisjsproject/callback-registry": { + "version": "2.0.3", + "resolved": "https://registry.npmjs.org/@aegisjsproject/callback-registry/-/callback-registry-2.0.3.tgz", + "integrity": "sha512-iJP7iESlo62AxmPH4WvAfuRzddTUWKG7dvvQbFBojixmeM5tOimsjDzIbGhPcwGLw8RigFhJiw0Z79OVGIufsA==", + "requires": { + "@shgysk8zer0/signals": "^0.0.3" + } + }, + "@aegisjsproject/core": { + "version": "0.2.34", + "resolved": "https://registry.npmjs.org/@aegisjsproject/core/-/core-0.2.34.tgz", + "integrity": "sha512-RpNbwmmnjI0X2vPcwszawMyWWvLGU8w6CCVKwVlygcI+gq41KuC+kJlPcda1qjDoIKQy+IbzFgxNdDzHaHIDXg==", + "requires": { + "@aegisjsproject/callback-registry": "^2.0.2", + "@aegisjsproject/escape": "^1.0.4", + "@aegisjsproject/parsers": "^0.1.6", + "@aegisjsproject/router": "^1.1.15", + "@aegisjsproject/sanitizer": "^0.2.4", + "@aegisjsproject/state": "^1.0.7", + "@aegisjsproject/url": "^1.0.3" + } + }, "@aegisjsproject/dev-server": { "version": "1.0.6", "resolved": "https://registry.npmjs.org/@aegisjsproject/dev-server/-/dev-server-1.0.6.tgz", @@ -1925,11 +2080,32 @@ "integrity": "sha512-Du0zbfNo0+Ujn1xFN5D26CA+iWstg4+8jqm3uXCNGK7+esb3RPgbV4DmcJD+bJHgku4qDjBPTpgZW3nt9qHbQw==", "dev": true }, + "@aegisjsproject/parsers": { + "version": "0.1.6", + "resolved": "https://registry.npmjs.org/@aegisjsproject/parsers/-/parsers-0.1.6.tgz", + "integrity": "sha512-5Fy5G6lY4m61TyWhERBIbsIiW97efY40q5ocFHkkHPN1Yr3dCZsRaA00kvafAp/Elo0HDjO3U9c7vjncThMTTg==", + "requires": { + "@aegisjsproject/sanitizer": "^0.2.4", + "@aegisjsproject/url": "^1.0.3" + } + }, + "@aegisjsproject/router": { + "version": "1.1.15", + "resolved": "https://registry.npmjs.org/@aegisjsproject/router/-/router-1.1.15.tgz", + "integrity": "sha512-12d971iOqh8HsetfY7nl6gCOFhhHD1h4YOQ8KC4fSk2IZgVGjt6Z9UA1nY1bxbMTGzJ9UzOdOGle4L9x2de5kg==", + "requires": { + "@aegisjsproject/state": "^1.0.5" + } + }, "@aegisjsproject/sanitizer": { "version": "0.2.4", "resolved": "https://registry.npmjs.org/@aegisjsproject/sanitizer/-/sanitizer-0.2.4.tgz", - "integrity": "sha512-9EMby+BWrn0Pa0YEO6th9ozB4jbelpbii5Or954rXlnQROETR3HcM6ZgNkgsT+yqeUSolwG7+iFF8Lnqe8ePAg==", - "dev": true + "integrity": "sha512-9EMby+BWrn0Pa0YEO6th9ozB4jbelpbii5Or954rXlnQROETR3HcM6ZgNkgsT+yqeUSolwG7+iFF8Lnqe8ePAg==" + }, + "@aegisjsproject/state": { + "version": "1.0.7", + "resolved": "https://registry.npmjs.org/@aegisjsproject/state/-/state-1.0.7.tgz", + "integrity": "sha512-RsMJbyEC4RtFsMzRFCvhUiZS61IwxO6SRev+Sm9NJ7EG2yFJsup6Md7NVsrfZB5pS+NI+wkDNznjjKwJw5ViNw==" }, "@aegisjsproject/trusted-types": { "version": "1.0.2", @@ -1937,6 +2113,11 @@ "integrity": "sha512-nVca/8lS8gpWiRBpiRaIu0S+8qXcIodBIVfv5IL337Zj17UfZMf8L4j+2TPuQ6tr78Sq9/sASUKhTWAWHA69sw==", "dev": true }, + "@aegisjsproject/url": { + "version": "1.0.3", + "resolved": "https://registry.npmjs.org/@aegisjsproject/url/-/url-1.0.3.tgz", + "integrity": "sha512-gbk9oRpNxOeYDM/BxfsEYoG0DpFu2++WW9agwlkoVivUGlJyLawmsUQAn9+rvw2fm+Pmr1EUXLbRaxi7drTOKg==" + }, "@eslint-community/eslint-utils": { "version": "4.9.0", "resolved": "https://registry.npmjs.org/@eslint-community/eslint-utils/-/eslint-utils-4.9.0.tgz", diff --git a/package.json b/package.json index a3426e6..5507080 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@aegisjsproject/iota", - "version": "1.0.5", + "version": "1.0.6", "description": "A Signals-based reactivity library", "keywords": [ "signals", @@ -76,6 +76,7 @@ }, "homepage": "https://github.com/AegisJSProject/iota#readme", "dependencies": { + "@aegisjsproject/core": "^0.2.34", "@aegisjsproject/escape": "^1.0.4", "@shgysk8zer0/signals": "^0.0.3" }, diff --git a/reactive-element.js b/reactive-element.js deleted file mode 100644 index a7e198b..0000000 --- a/reactive-element.js +++ /dev/null @@ -1,208 +0,0 @@ -import { createHTMLParser } from '@aegisjsproject/core/parsers/html.js'; -import { css } from '@aegisjsproject/core/parsers/css.js'; -import { sanitizer as sanitizerConfig } from '@aegisjsproject/sanitizer/config/base.js'; -import { $render, $text } from '@aegisjsproject/iota'; - - -// Need to force-allow comments for text signals until change is made in lib -const html = createHTMLParser({ - ...sanitizerConfig, - comments: true, -}); - -export class ReactiveElement extends HTMLElement { - #shadow = this.attachShadow({ mode: 'open' }); - #internals = this.attachInternals(); - #stack = new DisposableStack(); - #controller = this.#stack.adopt(new AbortController(), controller => controller.abort()); - - constructor() { - super(); - - if (this.styles instanceof CSSStyleSheet) { - this.#shadow.adoptedStyleSheets = [this.styles]; - } else if (Array.isArray(this.styles)) { - this.#shadow.adoptedStyleSheets = this.styles; - } else if (typeof this.styles === 'string') { - this.#shadow.adoptedStyleSheets = [css`${this.styles}`]; - } - - if (typeof this.render === 'function') { - this.render('constructed', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - $render, html, css, - }); - } - } - - connectedCallback() { - if (this.#stack.disposed) { - this.#stack = new DisposableStack(); - } - - if (this.#controller.signal.aborted) { - this.#controller = this.#stack.adopt(new AbortController(), controller => controller.abort()); - } - - if (this.html instanceof Node) { - $render(this.html, this.#shadow); - } else if (typeof this.html === 'string') { - $render(html`${this.html}`, this.#shadow); - } - - if (typeof this.render === 'function') { - this.render('connected', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - $render, html, css, - }); - } - } - - adoptedCallback() { - if (typeof this.render === 'function') { - this.render('adopted', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - }); - } - } - - disconnectedCallback() { - if (typeof this.render === 'function') { - this.render('disconnected', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - }); - } - - this.#stack.dispose(); - } - - attributeChangedCallback(name, oldValue, newValue) { - if (typeof this.render === 'function') { - this.render('attributeChanged', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - attribute: { name, oldValue, newValue }, - }); - } - } - - handleEvent(event) { - if (typeof this.render === 'function') { - this.render('eventDispatched', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - event, - }); - } - } - - [Symbol.dispose]() { - if (typeof this.render === 'function') { - this.render('dispose', { - stack: this.#stack, - signal: this.#controller.signal, - shadow: this.#shadow, - internals: this.#internals, - }); - } - this.#stack.dispose(); - } - - adopt(what, onDispose) { - return this.#stack.adopt(what, onDispose); - } - - use(what) { - return this.#stack.use(what); - } - - get disposed() { - return this.#stack.disposed; - } - - get signal() { - return this.#controller.signal; - } - - static register(tagName) { - if (typeof tagName !== 'string' || ! tagName.includes('-')) { - throw new TypeError('Tag name must be a non-empty string with a "-".'); - } else if (! customElements.get(tagName)) { - customElements.define(tagName, this); - } - } -} - -export class FancyCounter extends ReactiveElement { - #count = $text(0); - - get count() { return Number(this.#count.get()); } - - set count(val) { - this.#count.set(val); - } - - increment(by = 1) { - this.count += by; - } - - get styles() { - return css` - :host { display: inline-block; padding: 1rem; border: 1px solid #ccc; border-radius: 8px; } - button { cursor: pointer; padding: 0.5rem 1rem; } - button:disabled { opacity: 0.5; cursor: not-allowed; } - :host([inert]) button { opacity: 0.5; cursor: not-allowed; } - `; - } - - get html() { - return html` -

Native Counter

- - - `; - } - - render(phase, { stack, shadow, signal, event }) { - if (phase === 'connected') { - stack.use(this.#count); - shadow.getElementById('inc-btn').commandForElement = this; - shadow.getElementById('del-btn').commandForElement = this; - this.addEventListener('command', this, { signal }); - } else if (phase === 'eventDispatched' && event.type === 'command') { - switch(event.command) { - case '--increment': - this.increment(); - break; - - case '--dispose': - this[Symbol.dispose](); - break; - } - } else if (phase === 'disposed') { - this.inert = true; - } - } -} - -FancyCounter.register('fancy-counter'); diff --git a/rollup.config.js b/rollup.config.js index 87010f5..efe3b38 100644 --- a/rollup.config.js +++ b/rollup.config.js @@ -1,10 +1,10 @@ import terser from '@rollup/plugin-terser'; import nodeResolve from '@rollup/plugin-node-resolve'; - +const external = ['@shgysk8zer0/signal', '@aegisjsproject/core/parsers/html.js', '@aegisjsproject/core/parsers/css.js']; export default [{ input: 'iota.js', plugins: [nodeResolve()], - external: ['@shgysk8zer0/signals'], + external, output: [{ file: 'iota.cjs', format: 'cjs',