Socket
Socket
Sign inDemoInstall

haunted

Package Overview
Dependencies
Maintainers
1
Versions
41
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

haunted - npm Package Compare versions

Comparing version 4.4.0 to 4.5.0

core.js

629

haunted.js

@@ -1,2 +0,2 @@

import { render, directive } from 'https://unpkg.com/lit-html@^1.0.0/lit-html.js';
import { directive, render } from 'https://unpkg.com/lit-html@^1.0.0/lit-html.js';
export { html, render } from 'https://unpkg.com/lit-html@^1.0.0/lit-html.js';

@@ -34,2 +34,4 @@

//import { render, html } from './lit.js';
const defer = Promise.resolve().then.bind(Promise.resolve());

@@ -58,76 +60,80 @@

const read = scheduler();
const write = scheduler();
function makeContainer(render$$1) {
const read = scheduler();
const write = scheduler();
class Container {
constructor(renderer, frag, host) {
this.renderer = renderer;
this.frag = frag;
this.host = host || frag;
this[hookSymbol] = new Map();
this[phaseSymbol] = null;
this._updateQueued = false;
}
class Container {
constructor(renderer, frag, host) {
this.renderer = renderer;
this.frag = frag;
this.host = host || frag;
this[hookSymbol] = new Map();
this[phaseSymbol] = null;
this._updateQueued = false;
}
update() {
if(this._updateQueued) return;
read(() => {
let result = this.handlePhase(updateSymbol);
write(() => {
this.handlePhase(commitSymbol, result);
update() {
if(this._updateQueued) return;
read(() => {
let result = this.handlePhase(updateSymbol);
write(() => {
this.handlePhase(commitSymbol, result);
if(this[effectsSymbol]) {
write(() => {
this.handlePhase(effectsSymbol);
});
}
if(this[effectsSymbol]) {
write(() => {
this.handlePhase(effectsSymbol);
});
}
});
this._updateQueued = false;
});
this._updateQueued = false;
});
this._updateQueued = true;
}
this._updateQueued = true;
}
handlePhase(phase, arg) {
this[phaseSymbol] = phase;
switch(phase) {
case commitSymbol: return this.commit(arg);
case updateSymbol: return this.render();
case effectsSymbol: return this.runEffects(effectsSymbol);
handlePhase(phase, arg) {
this[phaseSymbol] = phase;
switch(phase) {
case commitSymbol: return this.commit(arg);
case updateSymbol: return this.render();
case effectsSymbol: return this.runEffects(effectsSymbol);
}
this[phaseSymbol] = null;
}
this[phaseSymbol] = null;
}
commit(result) {
render(result, this.frag);
this.runEffects(commitSymbol);
}
commit(result) {
render$$1(result, this.frag);
this.runEffects(commitSymbol);
}
render() {
setCurrent(this);
let result = this.args ?
this.renderer.apply(this.host, this.args) :
this.renderer.call(this.host, this.host);
clear();
return result;
}
runEffects(symbol) {
let effects = this[symbol];
if(effects) {
render() {
setCurrent(this);
for(let effect of effects) {
effect.call(this);
}
let result = this.args ?
this.renderer.apply(this.host, this.args) :
this.renderer.call(this.host, this.host);
clear();
return result;
}
}
teardown() {
let hooks = this[hookSymbol];
hooks.forEach((hook) => {
if (typeof hook.teardown === 'function') {
hook.teardown();
runEffects(symbol) {
let effects = this[symbol];
if(effects) {
setCurrent(this);
for(let effect of effects) {
effect.call(this);
}
clear();
}
});
}
teardown() {
let hooks = this[hookSymbol];
hooks.forEach((hook) => {
if (typeof hook.teardown === 'function') {
hook.teardown();
}
});
}
}
return Container;
}

@@ -141,76 +147,80 @@

function component(renderer, BaseElement = HTMLElement, {useShadowDOM = true, shadowRootInit = {}} = {}) {
class Element extends BaseElement {
static get observedAttributes() {
return renderer.observedAttributes || [];
}
function makeComponent(Container) {
function component(renderer, BaseElement = HTMLElement, {useShadowDOM = true, shadowRootInit = {}} = {}) {
class Element extends BaseElement {
static get observedAttributes() {
return renderer.observedAttributes || [];
}
constructor() {
super();
if (useShadowDOM === false) {
this._container = new Container(renderer, this);
} else {
this.attachShadow({ mode: "open", ...shadowRootInit});
this._container = new Container(renderer, this.shadowRoot, this);
constructor() {
super();
if (useShadowDOM === false) {
this._container = new Container(renderer, this);
} else {
this.attachShadow({ mode: "open", ...shadowRootInit});
this._container = new Container(renderer, this.shadowRoot, this);
}
}
}
connectedCallback() {
this._container.update();
}
disconnectedCallback() {
this._container.teardown();
}
attributeChangedCallback(name, _, newValue) {
let val = newValue === '' ? true : newValue;
Reflect.set(this, toCamelCase(name), val);
}
}
function reflectiveProp(initialValue) {
let value = initialValue;
return Object.freeze({
enumerable: true,
configurable: true,
get() {
return value;
},
set(newValue) {
value = newValue;
connectedCallback() {
this._container.update();
}
})
}
const proto = new Proxy(BaseElement.prototype, {
set(target, key, value, receiver) {
if(key in target) {
Reflect.set(target, key, value);
disconnectedCallback() {
this._container.teardown();
}
let desc;
if(typeof key === 'symbol' || key[0] === '_') {
desc = {
enumerable: true,
configurable: true,
writable: true,
value
};
} else {
desc = reflectiveProp(value);
attributeChangedCallback(name, _, newValue) {
let val = newValue === '' ? true : newValue;
Reflect.set(this, toCamelCase(name), val);
}
Object.defineProperty(receiver, key, desc);
}
function reflectiveProp(initialValue) {
let value = initialValue;
return Object.freeze({
enumerable: true,
configurable: true,
get() {
return value;
},
set(newValue) {
value = newValue;
this._container.update();
}
})
}
if(desc.set) {
desc.set.call(receiver, value);
const proto = new Proxy(BaseElement.prototype, {
set(target, key, value, receiver) {
if(key in target) {
Reflect.set(target, key, value);
}
let desc;
if(typeof key === 'symbol' || key[0] === '_') {
desc = {
enumerable: true,
configurable: true,
writable: true,
value
};
} else {
desc = reflectiveProp(value);
}
Object.defineProperty(receiver, key, desc);
if(desc.set) {
desc.set.call(receiver, value);
}
return true;
}
});
return true;
}
});
Object.setPrototypeOf(Element.prototype, proto);
Object.setPrototypeOf(Element.prototype, proto);
return Element;
}
return Element;
return component;
}

@@ -242,24 +252,2 @@

const useMemo = hook(class extends Hook {
constructor(id, el, fn, values) {
super(id, el);
this.value = fn();
this.values = values;
}
update(fn, values) {
if(this.hasChanged(values)) {
this.values = values;
this.value = fn();
}
return this.value;
}
hasChanged(values) {
return values.some((value, i) => this.values[i] !== value);
}
});
const useCallback = (fn, inputs) => useMemo(() => fn, inputs);
function setEffects(el, cb) {

@@ -311,2 +299,151 @@ if(!(effectsSymbol in el)) {

function setContexts(el, consumer) {
if(!(contextSymbol in el)) {
el[contextSymbol] = [];
}
el[contextSymbol].push(consumer);
}
const useContext = hook(class extends Hook {
constructor(id, el) {
super(id, el);
setContexts(el, this);
this._updater = this._updater.bind(this);
this._ranEffect = false;
this._unsubscribe = null;
setEffects(el, this);
}
update(Context) {
if (this.el.virtual) {
throw new Error('can\'t be used with virtual components');
}
if (this.Context !== Context) {
this._subscribe(Context);
this.Context = Context;
}
return this.value;
}
call() {
if(!this._ranEffect) {
this._ranEffect = true;
if(this._unsubscribe) this._unsubscribe();
this._subscribe(this.Context);
this.el.update();
}
}
_updater(value) {
this.value = value;
this.el.update();
}
_subscribe(Context) {
const detail = { Context, callback: this._updater };
this.el.host.dispatchEvent(new CustomEvent(contextEvent, {
detail, // carrier
bubbles: true, // to bubble up in tree
cancelable: true, // to be able to cancel
composed: true, // to pass ShadowDOM boundaries
}));
const { unsubscribe, value } = detail;
this.value = unsubscribe ? value : Context.defaultValue;
this._unsubscribe = unsubscribe;
}
teardown() {
if (this._unsubscribe) {
this._unsubscribe();
}
}
});
function makeContext(component) {
return (defaultValue) => {
const Context = {
Provider: class extends HTMLElement {
constructor() {
super();
this.listeners = new Set();
this.addEventListener(contextEvent, this);
}
disconnectedCallback() {
this.removeEventListener(contextEvent, this);
}
handleEvent(event) {
const { detail } = event;
if (detail.Context === Context) {
detail.value = this.value;
detail.unsubscribe = this.unsubscribe.bind(this, detail.callback);
this.listeners.add(detail.callback);
event.stopPropagation();
}
}
unsubscribe(callback) {
if(this.listeners.has(callback)) {
this.listeners.delete(callback);
}
}
set value(value) {
this._value = value;
for(let callback of this.listeners) {
callback(value);
}
}
get value() {
return this._value;
}
},
Consumer: component(function ({ render: render$$1 }) {
const context = useContext(Context);
return render$$1(context);
}),
defaultValue
};
return Context;
};
}
const useMemo = hook(class extends Hook {
constructor(id, el, fn, values) {
super(id, el);
this.value = fn();
this.values = values;
}
update(fn, values) {
if(this.hasChanged(values)) {
this.values = values;
this.value = fn();
}
return this.value;
}
hasChanged(values) {
return values.some((value, i) => this.values[i] !== value);
}
});
const useCallback = (fn, inputs) => useMemo(() => fn, inputs);
const useState = hook(class extends Hook {

@@ -316,2 +453,7 @@ constructor(id, el, initialValue) {

this.updater = this.updater.bind(this);
if(typeof initialValue === 'function') {
initialValue = initialValue();
}
this.makeArgs(initialValue);

@@ -358,45 +500,63 @@ }

const partToContainer = new WeakMap();
const containerToPart = new WeakMap();
const useRef = (initialValue) => {
return useMemo(() => {
return {
current: initialValue
};
}, []);
};
class DirectiveContainer extends Container {
constructor(renderer, part) {
super(renderer, part);
this.virtual = true;
}
function haunted({ render: render$$1 }) {
const Container = makeContainer(render$$1);
const component = makeComponent(Container);
const createContext = makeContext(component);
commit(result) {
this.host.setValue(result);
this.host.commit();
}
teardown() {
super.teardown();
let part = containerToPart.get(this);
partToContainer.delete(part);
}
return { Container, component, createContext };
}
const includes = Array.prototype.includes;
function withHooks(renderer) {
function factory(...args) {
return part => {
let cont = partToContainer.get(part);
if(!cont) {
cont = new DirectiveContainer(renderer, part);
partToContainer.set(part, cont);
containerToPart.set(cont, part);
teardownOnRemove(cont, part);
}
cont.args = args;
cont.update();
};
function makeVirtual(Container) {
const partToContainer = new WeakMap();
const containerToPart = new WeakMap();
class DirectiveContainer extends Container {
constructor(renderer, part) {
super(renderer, part);
this.virtual = true;
}
commit(result) {
this.host.setValue(result);
this.host.commit();
}
teardown() {
super.teardown();
let part = containerToPart.get(this);
partToContainer.delete(part);
}
}
function virtual(renderer) {
function factory(...args) {
return part => {
let cont = partToContainer.get(part);
if(!cont) {
cont = new DirectiveContainer(renderer, part);
partToContainer.set(part, cont);
containerToPart.set(cont, part);
teardownOnRemove(cont, part);
}
cont.args = args;
cont.update();
};
}
return directive(factory);
}
return directive(factory);
return virtual;
}
const includes = Array.prototype.includes;
function teardownOnRemove(cont, part, node = part.startNode) {

@@ -425,114 +585,11 @@ let frag = node.parentNode;

function setContexts(el, consumer) {
if(!(contextSymbol in el)) {
el[contextSymbol] = [];
const { Container, component, createContext } = haunted({
render(what, where) {
render(what, where);
}
el[contextSymbol].push(consumer);
}
const useContext = hook(class extends Hook {
constructor(id, el) {
super(id, el);
setContexts(el, this);
this._updater = this._updater.bind(this);
}
update(Context) {
if (this.el.virtual) {
throw new Error('can\'t be used with virtual components');
}
if (this.Context !== Context) {
this._subscribe(Context);
this.Context = Context;
}
return this.value;
}
_updater(value) {
this.value = value;
this.el.update();
}
_subscribe(Context) {
const detail = { Context, callback: this._updater };
this.el.host.dispatchEvent(new CustomEvent(contextEvent, {
detail, // carrier
bubbles: true, // to bubble up in tree
cancelable: true, // to be able to cancel
composed: true, // to pass ShadowDOM boundaries
}));
const { unsubscribe, value } = detail;
this.value = unsubscribe ? value : Context.defaultValue;
this._unsubscribe = unsubscribe;
}
teardown() {
if (this._unsubscribe) {
this._unsubscribe();
}
}
});
const createContext = (defaultValue) => {
const Context = {};
Context.Provider = class extends HTMLElement {
constructor() {
super();
this.listeners = [];
this.eventHandler = (event) => {
const { detail } = event;
if (detail.Context === Context) {
detail.value = this.value;
detail.unsubscribe = () => {
const index = this.listeners.indexOf(detail.callback);
const virtual = makeVirtual(Container);
if (index > -1) {
this.listeners.splice(index, 1);
}
};
this.listeners.push(detail.callback);
event.stopPropagation();
}
};
this.addEventListener(contextEvent, this.eventHandler);
}
disconnectedCallback() {
this.removeEventListener(contextEvent, this.eventHandler);
}
set value(value) {
this._value = value;
this.listeners.forEach(callback => callback(value));
}
get value() {
return this._value;
}
};
Context.Consumer = component(function ({ render: render$$1 }) {
const context = useContext(Context);
return render$$1(context);
});
Context.defaultValue = defaultValue;
return Context;
};
export { component, useCallback, useEffect, useState, useReducer, useMemo, withHooks, withHooks as virtual, useContext, createContext, hook, Hook };
export default haunted;
export { component, createContext, virtual, useCallback, useEffect, useState, useReducer, useMemo, useContext, useRef, hook, Hook };
{
"name": "haunted",
"version": "4.4.0",
"description": "",
"main": "index.js",
"module": "index.js",
"version": "4.5.0",
"description": "Hooks for web components",
"main": "lib/haunted.js",
"module": "lib/haunted.js",
"type": "module",

@@ -16,5 +16,5 @@ "scripts": {

"haunted.js",
"index.js",
"web.js",
"index.d.ts"
"core.js",
"lib"
],

@@ -21,0 +21,0 @@ "repository": {

# Haunted 🦇 🎃
React's Hooks API but for standard web components and [hyperHTML](https://codepen.io/WebReflection/pen/pxXrdy?editors=0010) or [lit-html](https://lit-html.polymer-project.org/).
React's Hooks API but for standard web components and [lit-html](https://lit-html.polymer-project.org/) or [hyperHTML](https://codepen.io/WebReflection/pen/pxXrdy?editors=0010).

@@ -37,23 +37,66 @@ ```html

```
For Internet Explorer 11, you'll need to use a proxy polyfill, in addition to the usual webcomponentsjs polyfills.
eg.
```html
<script type="text/javascript" src="https://cdn.jsdelivr.net/npm/proxy-polyfill@0.3.0/proxy.min.js"></script>
<script src="https://cdn.jsdelivr.net/npm/proxy-polyfill@0.3.0/proxy.min.js"></script>
```
For a full example see - https://github.com/crisward/haunted-ie11
For a full example with Internet Explorer 11, see - https://github.com/crisward/haunted-ie11
You can also use Custom Elements without Shadow DOM if you wish.
eg.
```js
component(() => html`...`, HTMLElement, { useShadowDOM: false }));
```
component(() => html`...`, HTMLElement, {useShadowDOM: false}))
### Importing
__Haunted__ can be imported just like any other library when using a bundler of your choice:
```js
import { component, html, useState } from 'haunted';
```
### Builds
The main entry point is intended for [lit-html](https://github.com/Polymer/lit-html) users.
Haunted comes in a few builds. Pick one based on your chosen environment:
#### lighterhtml, etc
* __index.js__ is available for bundlers such as Webpack and Rollup. Use with: `import { useState } from 'haunted';`;
* __web.js__ is avaible for use with the web's native module support. Use with: `import { useState } from '../node_modules/haunted/web.js';`.
* __haunted.js__ is available via the CDN [unpkg](https://unpkg.com). This is great for small apps or for local development without having to install anything. Use with `import { useState } from 'https://unpkg.com/haunted/haunted.js';`
If you are using [lighterhtml](https://github.com/WebReflection/lighterhtml) or [hyperHTML](https://github.com/WebReflection/hyperHTML) then instead import `haunted/core`. This export gives you a function that creates Hooks that work with any template library.
```js
import haunted, { useState } from 'haunted/core';
import { html, render } from 'lighterhtml';
const { component } = haunted({
render(what, where) {
render(where, () => what);
}
});
const App = component(() => {
const [count, setCount] = useState(0);
return html`Using lighterhtml! Count: ${count}`;
});
```
#### Web modules
__Haunted__ can work directly in the browser without using any build tools. Simply import the `haunted.js` bundle. You can use the [unpkg] or [pika](https://www.pika.dev/cdn) CDNs. This works great for demo pages and small apps. Here's an example with unpkg:
```js
import { html } from 'https://unpkg.com/lit-html/lit-html.js';
import { component, useState, useEffect } from 'https://unpkg.com/haunted/haunted.js';
```
If using pika then use the `html` export from Haunted, as pika bundles everything together:
```js
import { useState, component, html } from 'https://cdn.pika.dev/haunted';
```
If you install Haunted __locally__ this build is located at `node_modules/haunted/haunted.js`.
## API

@@ -77,5 +120,5 @@

const App = component(({ name }) => {
const App = ({ name }) => {
return html`Hello ${name}!`;
});
};

@@ -169,2 +212,10 @@ customElements.define('my-app', component(App));

Additionally you can provide a function as the argument to useState, in which case the function is called to initialize the first state, but never called again.
```js
const [count, setCount] = useState(() => {
return expensiveFunction();
});
```
#### useEffect

@@ -318,2 +369,27 @@

#### useRef
Create and returns an object with one property "current" which can be assigned any value and is unaffected by multiple renders.
```html
<!doctype html>
<my-app></my-app>
<script type="module">
import { html } from 'https://unpkg.com/lit-html/lit-html.js';
import { component, useRef } from 'https://unpkg.com/haunted/haunted.js';
function App() {
const myRef = useRef(0);
return html`
${myRef.current}
`;
}
customElements.define('my-app', component(App));
</script>
```
#### useContext

@@ -320,0 +396,0 @@

@@ -1,2 +0,2 @@

import { render, directive } from '../lit-html/lit-html.js';
import { directive, render } from '../lit-html/lit-html.js';
export { html, render } from '../lit-html/lit-html.js';

@@ -34,2 +34,4 @@

//import { render, html } from './lit.js';
const defer = Promise.resolve().then.bind(Promise.resolve());

@@ -58,76 +60,80 @@

const read = scheduler();
const write = scheduler();
function makeContainer(render$$1) {
const read = scheduler();
const write = scheduler();
class Container {
constructor(renderer, frag, host) {
this.renderer = renderer;
this.frag = frag;
this.host = host || frag;
this[hookSymbol] = new Map();
this[phaseSymbol] = null;
this._updateQueued = false;
}
class Container {
constructor(renderer, frag, host) {
this.renderer = renderer;
this.frag = frag;
this.host = host || frag;
this[hookSymbol] = new Map();
this[phaseSymbol] = null;
this._updateQueued = false;
}
update() {
if(this._updateQueued) return;
read(() => {
let result = this.handlePhase(updateSymbol);
write(() => {
this.handlePhase(commitSymbol, result);
update() {
if(this._updateQueued) return;
read(() => {
let result = this.handlePhase(updateSymbol);
write(() => {
this.handlePhase(commitSymbol, result);
if(this[effectsSymbol]) {
write(() => {
this.handlePhase(effectsSymbol);
});
}
if(this[effectsSymbol]) {
write(() => {
this.handlePhase(effectsSymbol);
});
}
});
this._updateQueued = false;
});
this._updateQueued = false;
});
this._updateQueued = true;
}
this._updateQueued = true;
}
handlePhase(phase, arg) {
this[phaseSymbol] = phase;
switch(phase) {
case commitSymbol: return this.commit(arg);
case updateSymbol: return this.render();
case effectsSymbol: return this.runEffects(effectsSymbol);
handlePhase(phase, arg) {
this[phaseSymbol] = phase;
switch(phase) {
case commitSymbol: return this.commit(arg);
case updateSymbol: return this.render();
case effectsSymbol: return this.runEffects(effectsSymbol);
}
this[phaseSymbol] = null;
}
this[phaseSymbol] = null;
}
commit(result) {
render(result, this.frag);
this.runEffects(commitSymbol);
}
commit(result) {
render$$1(result, this.frag);
this.runEffects(commitSymbol);
}
render() {
setCurrent(this);
let result = this.args ?
this.renderer.apply(this.host, this.args) :
this.renderer.call(this.host, this.host);
clear();
return result;
}
runEffects(symbol) {
let effects = this[symbol];
if(effects) {
render() {
setCurrent(this);
for(let effect of effects) {
effect.call(this);
}
let result = this.args ?
this.renderer.apply(this.host, this.args) :
this.renderer.call(this.host, this.host);
clear();
return result;
}
}
teardown() {
let hooks = this[hookSymbol];
hooks.forEach((hook) => {
if (typeof hook.teardown === 'function') {
hook.teardown();
runEffects(symbol) {
let effects = this[symbol];
if(effects) {
setCurrent(this);
for(let effect of effects) {
effect.call(this);
}
clear();
}
});
}
teardown() {
let hooks = this[hookSymbol];
hooks.forEach((hook) => {
if (typeof hook.teardown === 'function') {
hook.teardown();
}
});
}
}
return Container;
}

@@ -141,76 +147,80 @@

function component(renderer, BaseElement = HTMLElement, {useShadowDOM = true, shadowRootInit = {}} = {}) {
class Element extends BaseElement {
static get observedAttributes() {
return renderer.observedAttributes || [];
}
function makeComponent(Container) {
function component(renderer, BaseElement = HTMLElement, {useShadowDOM = true, shadowRootInit = {}} = {}) {
class Element extends BaseElement {
static get observedAttributes() {
return renderer.observedAttributes || [];
}
constructor() {
super();
if (useShadowDOM === false) {
this._container = new Container(renderer, this);
} else {
this.attachShadow({ mode: "open", ...shadowRootInit});
this._container = new Container(renderer, this.shadowRoot, this);
constructor() {
super();
if (useShadowDOM === false) {
this._container = new Container(renderer, this);
} else {
this.attachShadow({ mode: "open", ...shadowRootInit});
this._container = new Container(renderer, this.shadowRoot, this);
}
}
}
connectedCallback() {
this._container.update();
}
disconnectedCallback() {
this._container.teardown();
}
attributeChangedCallback(name, _, newValue) {
let val = newValue === '' ? true : newValue;
Reflect.set(this, toCamelCase(name), val);
}
}
function reflectiveProp(initialValue) {
let value = initialValue;
return Object.freeze({
enumerable: true,
configurable: true,
get() {
return value;
},
set(newValue) {
value = newValue;
connectedCallback() {
this._container.update();
}
})
}
const proto = new Proxy(BaseElement.prototype, {
set(target, key, value, receiver) {
if(key in target) {
Reflect.set(target, key, value);
disconnectedCallback() {
this._container.teardown();
}
let desc;
if(typeof key === 'symbol' || key[0] === '_') {
desc = {
enumerable: true,
configurable: true,
writable: true,
value
};
} else {
desc = reflectiveProp(value);
attributeChangedCallback(name, _, newValue) {
let val = newValue === '' ? true : newValue;
Reflect.set(this, toCamelCase(name), val);
}
Object.defineProperty(receiver, key, desc);
}
function reflectiveProp(initialValue) {
let value = initialValue;
return Object.freeze({
enumerable: true,
configurable: true,
get() {
return value;
},
set(newValue) {
value = newValue;
this._container.update();
}
})
}
if(desc.set) {
desc.set.call(receiver, value);
const proto = new Proxy(BaseElement.prototype, {
set(target, key, value, receiver) {
if(key in target) {
Reflect.set(target, key, value);
}
let desc;
if(typeof key === 'symbol' || key[0] === '_') {
desc = {
enumerable: true,
configurable: true,
writable: true,
value
};
} else {
desc = reflectiveProp(value);
}
Object.defineProperty(receiver, key, desc);
if(desc.set) {
desc.set.call(receiver, value);
}
return true;
}
});
return true;
}
});
Object.setPrototypeOf(Element.prototype, proto);
Object.setPrototypeOf(Element.prototype, proto);
return Element;
}
return Element;
return component;
}

@@ -242,24 +252,2 @@

const useMemo = hook(class extends Hook {
constructor(id, el, fn, values) {
super(id, el);
this.value = fn();
this.values = values;
}
update(fn, values) {
if(this.hasChanged(values)) {
this.values = values;
this.value = fn();
}
return this.value;
}
hasChanged(values) {
return values.some((value, i) => this.values[i] !== value);
}
});
const useCallback = (fn, inputs) => useMemo(() => fn, inputs);
function setEffects(el, cb) {

@@ -311,2 +299,151 @@ if(!(effectsSymbol in el)) {

function setContexts(el, consumer) {
if(!(contextSymbol in el)) {
el[contextSymbol] = [];
}
el[contextSymbol].push(consumer);
}
const useContext = hook(class extends Hook {
constructor(id, el) {
super(id, el);
setContexts(el, this);
this._updater = this._updater.bind(this);
this._ranEffect = false;
this._unsubscribe = null;
setEffects(el, this);
}
update(Context) {
if (this.el.virtual) {
throw new Error('can\'t be used with virtual components');
}
if (this.Context !== Context) {
this._subscribe(Context);
this.Context = Context;
}
return this.value;
}
call() {
if(!this._ranEffect) {
this._ranEffect = true;
if(this._unsubscribe) this._unsubscribe();
this._subscribe(this.Context);
this.el.update();
}
}
_updater(value) {
this.value = value;
this.el.update();
}
_subscribe(Context) {
const detail = { Context, callback: this._updater };
this.el.host.dispatchEvent(new CustomEvent(contextEvent, {
detail, // carrier
bubbles: true, // to bubble up in tree
cancelable: true, // to be able to cancel
composed: true, // to pass ShadowDOM boundaries
}));
const { unsubscribe, value } = detail;
this.value = unsubscribe ? value : Context.defaultValue;
this._unsubscribe = unsubscribe;
}
teardown() {
if (this._unsubscribe) {
this._unsubscribe();
}
}
});
function makeContext(component) {
return (defaultValue) => {
const Context = {
Provider: class extends HTMLElement {
constructor() {
super();
this.listeners = new Set();
this.addEventListener(contextEvent, this);
}
disconnectedCallback() {
this.removeEventListener(contextEvent, this);
}
handleEvent(event) {
const { detail } = event;
if (detail.Context === Context) {
detail.value = this.value;
detail.unsubscribe = this.unsubscribe.bind(this, detail.callback);
this.listeners.add(detail.callback);
event.stopPropagation();
}
}
unsubscribe(callback) {
if(this.listeners.has(callback)) {
this.listeners.delete(callback);
}
}
set value(value) {
this._value = value;
for(let callback of this.listeners) {
callback(value);
}
}
get value() {
return this._value;
}
},
Consumer: component(function ({ render: render$$1 }) {
const context = useContext(Context);
return render$$1(context);
}),
defaultValue
};
return Context;
};
}
const useMemo = hook(class extends Hook {
constructor(id, el, fn, values) {
super(id, el);
this.value = fn();
this.values = values;
}
update(fn, values) {
if(this.hasChanged(values)) {
this.values = values;
this.value = fn();
}
return this.value;
}
hasChanged(values) {
return values.some((value, i) => this.values[i] !== value);
}
});
const useCallback = (fn, inputs) => useMemo(() => fn, inputs);
const useState = hook(class extends Hook {

@@ -316,2 +453,7 @@ constructor(id, el, initialValue) {

this.updater = this.updater.bind(this);
if(typeof initialValue === 'function') {
initialValue = initialValue();
}
this.makeArgs(initialValue);

@@ -358,45 +500,63 @@ }

const partToContainer = new WeakMap();
const containerToPart = new WeakMap();
const useRef = (initialValue) => {
return useMemo(() => {
return {
current: initialValue
};
}, []);
};
class DirectiveContainer extends Container {
constructor(renderer, part) {
super(renderer, part);
this.virtual = true;
}
function haunted({ render: render$$1 }) {
const Container = makeContainer(render$$1);
const component = makeComponent(Container);
const createContext = makeContext(component);
commit(result) {
this.host.setValue(result);
this.host.commit();
}
teardown() {
super.teardown();
let part = containerToPart.get(this);
partToContainer.delete(part);
}
return { Container, component, createContext };
}
const includes = Array.prototype.includes;
function withHooks(renderer) {
function factory(...args) {
return part => {
let cont = partToContainer.get(part);
if(!cont) {
cont = new DirectiveContainer(renderer, part);
partToContainer.set(part, cont);
containerToPart.set(cont, part);
teardownOnRemove(cont, part);
}
cont.args = args;
cont.update();
};
function makeVirtual(Container) {
const partToContainer = new WeakMap();
const containerToPart = new WeakMap();
class DirectiveContainer extends Container {
constructor(renderer, part) {
super(renderer, part);
this.virtual = true;
}
commit(result) {
this.host.setValue(result);
this.host.commit();
}
teardown() {
super.teardown();
let part = containerToPart.get(this);
partToContainer.delete(part);
}
}
function virtual(renderer) {
function factory(...args) {
return part => {
let cont = partToContainer.get(part);
if(!cont) {
cont = new DirectiveContainer(renderer, part);
partToContainer.set(part, cont);
containerToPart.set(cont, part);
teardownOnRemove(cont, part);
}
cont.args = args;
cont.update();
};
}
return directive(factory);
}
return directive(factory);
return virtual;
}
const includes = Array.prototype.includes;
function teardownOnRemove(cont, part, node = part.startNode) {

@@ -425,114 +585,11 @@ let frag = node.parentNode;

function setContexts(el, consumer) {
if(!(contextSymbol in el)) {
el[contextSymbol] = [];
const { Container, component, createContext } = haunted({
render(what, where) {
render(what, where);
}
el[contextSymbol].push(consumer);
}
const useContext = hook(class extends Hook {
constructor(id, el) {
super(id, el);
setContexts(el, this);
this._updater = this._updater.bind(this);
}
update(Context) {
if (this.el.virtual) {
throw new Error('can\'t be used with virtual components');
}
if (this.Context !== Context) {
this._subscribe(Context);
this.Context = Context;
}
return this.value;
}
_updater(value) {
this.value = value;
this.el.update();
}
_subscribe(Context) {
const detail = { Context, callback: this._updater };
this.el.host.dispatchEvent(new CustomEvent(contextEvent, {
detail, // carrier
bubbles: true, // to bubble up in tree
cancelable: true, // to be able to cancel
composed: true, // to pass ShadowDOM boundaries
}));
const { unsubscribe, value } = detail;
this.value = unsubscribe ? value : Context.defaultValue;
this._unsubscribe = unsubscribe;
}
teardown() {
if (this._unsubscribe) {
this._unsubscribe();
}
}
});
const createContext = (defaultValue) => {
const Context = {};
Context.Provider = class extends HTMLElement {
constructor() {
super();
this.listeners = [];
this.eventHandler = (event) => {
const { detail } = event;
if (detail.Context === Context) {
detail.value = this.value;
detail.unsubscribe = () => {
const index = this.listeners.indexOf(detail.callback);
const virtual = makeVirtual(Container);
if (index > -1) {
this.listeners.splice(index, 1);
}
};
this.listeners.push(detail.callback);
event.stopPropagation();
}
};
this.addEventListener(contextEvent, this.eventHandler);
}
disconnectedCallback() {
this.removeEventListener(contextEvent, this.eventHandler);
}
set value(value) {
this._value = value;
this.listeners.forEach(callback => callback(value));
}
get value() {
return this._value;
}
};
Context.Consumer = component(function ({ render: render$$1 }) {
const context = useContext(Context);
return render$$1(context);
});
Context.defaultValue = defaultValue;
return Context;
};
export { component, useCallback, useEffect, useState, useReducer, useMemo, withHooks, withHooks as virtual, useContext, createContext, hook, Hook };
export default haunted;
export { component, createContext, virtual, useCallback, useEffect, useState, useReducer, useMemo, useContext, useRef, hook, Hook };
SocketSocket SOC 2 Logo

Product

  • Package Alerts
  • Integrations
  • Docs
  • Pricing
  • FAQ
  • Roadmap
  • Changelog

Packages

npm

Stay in touch

Get open source security insights delivered straight into your inbox.


  • Terms
  • Privacy
  • Security

Made with ⚡️ by Socket Inc