Skip to content

Commit f6fcc6e

Browse files
[ci] release (next) (#391)
Co-authored-by: github-actions[bot] <github-actions[bot]@users.noreply.github.com>
1 parent 6a632a6 commit f6fcc6e

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

44 files changed

+1025
-3
lines changed

.changeset/pre.json

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,7 @@
44
"initialVersions": {
55
"haunted": "5.0.0"
66
},
7-
"changesets": []
7+
"changesets": [
8+
"chilly-ants-swim"
9+
]
810
}

CHANGELOG.md

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
# haunted
2+
3+
## 6.0.0-next.0
4+
5+
### Major Changes
6+
7+
- f861a4b: Deprecates haunted.js and web.js bundles

component.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { GenericRenderer, RenderFunction } from './core';
2+
interface Renderer<P extends object> extends GenericRenderer<HTMLElement, P> {
3+
(this: Component<P>, host: Component<P>): unknown | void;
4+
observedAttributes?: (keyof P)[];
5+
}
6+
declare type Component<P extends object> = HTMLElement & P;
7+
declare type Constructor<P extends object> = new (...args: unknown[]) => Component<P>;
8+
interface Creator {
9+
<P extends object>(renderer: Renderer<P>): Constructor<P>;
10+
<P extends object>(renderer: Renderer<P>, options: Options<P>): Constructor<P>;
11+
<P extends object>(renderer: Renderer<P>, baseElement: Constructor<{}>, options: Omit<Options<P>, 'baseElement'>): Constructor<P>;
12+
}
13+
interface Options<P> {
14+
baseElement?: Constructor<{}>;
15+
observedAttributes?: (keyof P)[];
16+
useShadowDOM?: boolean;
17+
shadowRootInit?: ShadowRootInit;
18+
}
19+
declare function makeComponent(render: RenderFunction): Creator;
20+
export { makeComponent, Component, Constructor as ComponentConstructor, Creator as ComponentCreator };

component.js

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
1+
import { BaseScheduler } from './scheduler';
2+
const toCamelCase = (val = '') => val.replace(/-+([a-z])?/g, (_, char) => char ? char.toUpperCase() : '');
3+
function makeComponent(render) {
4+
class Scheduler extends BaseScheduler {
5+
frag;
6+
constructor(renderer, frag, host) {
7+
super(renderer, (host || frag));
8+
this.frag = frag;
9+
}
10+
commit(result) {
11+
render(result, this.frag);
12+
}
13+
}
14+
function component(renderer, baseElementOrOptions, options) {
15+
const BaseElement = (options || baseElementOrOptions || {}).baseElement || HTMLElement;
16+
const { observedAttributes = [], useShadowDOM = true, shadowRootInit = {} } = options || baseElementOrOptions || {};
17+
class Element extends BaseElement {
18+
_scheduler;
19+
static get observedAttributes() {
20+
return renderer.observedAttributes || observedAttributes || [];
21+
}
22+
constructor() {
23+
super();
24+
if (useShadowDOM === false) {
25+
this._scheduler = new Scheduler(renderer, this);
26+
}
27+
else {
28+
this.attachShadow({ mode: 'open', ...shadowRootInit });
29+
this._scheduler = new Scheduler(renderer, this.shadowRoot, this);
30+
}
31+
}
32+
connectedCallback() {
33+
this._scheduler.update();
34+
}
35+
disconnectedCallback() {
36+
this._scheduler.teardown();
37+
}
38+
attributeChangedCallback(name, oldValue, newValue) {
39+
if (oldValue === newValue) {
40+
return;
41+
}
42+
let val = newValue === '' ? true : newValue;
43+
Reflect.set(this, toCamelCase(name), val);
44+
}
45+
}
46+
;
47+
function reflectiveProp(initialValue) {
48+
let value = initialValue;
49+
let isSetup = false;
50+
return Object.freeze({
51+
enumerable: true,
52+
configurable: true,
53+
get() {
54+
return value;
55+
},
56+
set(newValue) {
57+
// Avoid scheduling update when prop value hasn't changed
58+
if (isSetup && value === newValue)
59+
return;
60+
isSetup = true;
61+
value = newValue;
62+
if (this._scheduler) {
63+
this._scheduler.update();
64+
}
65+
}
66+
});
67+
}
68+
const proto = new Proxy(BaseElement.prototype, {
69+
getPrototypeOf(target) {
70+
return target;
71+
},
72+
set(target, key, value, receiver) {
73+
let desc;
74+
if (key in target) {
75+
desc = Object.getOwnPropertyDescriptor(target, key);
76+
if (desc && desc.set) {
77+
desc.set.call(receiver, value);
78+
return true;
79+
}
80+
Reflect.set(target, key, value, receiver);
81+
return true;
82+
}
83+
if (typeof key === 'symbol' || key[0] === '_') {
84+
desc = {
85+
enumerable: true,
86+
configurable: true,
87+
writable: true,
88+
value
89+
};
90+
}
91+
else {
92+
desc = reflectiveProp(value);
93+
}
94+
Object.defineProperty(receiver, key, desc);
95+
if (desc.set) {
96+
desc.set.call(receiver, value);
97+
}
98+
return true;
99+
}
100+
});
101+
Object.setPrototypeOf(Element.prototype, proto);
102+
return Element;
103+
}
104+
return component;
105+
}
106+
export { makeComponent };

core.d.ts

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import { ComponentCreator } from './component';
2+
import { ContextCreator } from './create-context';
3+
import { ChildPart } from 'lit/html';
4+
declare type Component<P> = HTMLElement & P;
5+
declare type ComponentOrVirtualComponent<T extends HTMLElement | ChildPart, P extends object> = T extends HTMLElement ? Component<P> : ChildPart;
6+
declare type GenericRenderer<T extends HTMLElement | ChildPart, P extends object = {}> = (this: ComponentOrVirtualComponent<T, P>, ...args: any[]) => unknown | void;
7+
declare type RenderFunction = (result: unknown, container: DocumentFragment | HTMLElement) => void;
8+
interface Options {
9+
render: RenderFunction;
10+
}
11+
declare function haunted({ render }: Options): {
12+
component: ComponentCreator;
13+
createContext: ContextCreator;
14+
};
15+
export { haunted as default, Options, GenericRenderer, RenderFunction, ComponentOrVirtualComponent };
16+
export { useCallback } from './use-callback';
17+
export { useController } from './use-controller';
18+
export { useEffect } from './use-effect';
19+
export { useLayoutEffect } from './use-layout-effect';
20+
export { useState } from './use-state';
21+
export { useReducer } from './use-reducer';
22+
export { useMemo } from './use-memo';
23+
export { useContext } from './use-context';
24+
export { useRef } from './use-ref';
25+
export { hook, Hook } from './hook';
26+
export { BaseScheduler } from './scheduler';
27+
export { State } from './state';

core.js

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { makeComponent } from './component';
2+
import { makeContext } from './create-context';
3+
function haunted({ render }) {
4+
const component = makeComponent(render);
5+
const createContext = makeContext(component);
6+
return { component, createContext };
7+
}
8+
export { haunted as default };
9+
export { useCallback } from './use-callback';
10+
export { useController } from './use-controller';
11+
export { useEffect } from './use-effect';
12+
export { useLayoutEffect } from './use-layout-effect';
13+
export { useState } from './use-state';
14+
export { useReducer } from './use-reducer';
15+
export { useMemo } from './use-memo';
16+
export { useContext } from './use-context';
17+
export { useRef } from './use-ref';
18+
export { hook, Hook } from './hook';
19+
export { BaseScheduler } from './scheduler';
20+
export { State } from './state';

create-context.d.ts

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
import { ComponentConstructor, ComponentCreator } from './component';
2+
interface ConsumerProps<T> {
3+
render: (value: T) => unknown;
4+
}
5+
interface Creator {
6+
<T>(defaultValue: T): Context<T>;
7+
}
8+
interface Context<T> {
9+
Provider: ComponentConstructor<{}>;
10+
Consumer: ComponentConstructor<ConsumerProps<T>>;
11+
defaultValue: T;
12+
}
13+
interface ContextDetail<T> {
14+
Context: Context<T>;
15+
callback: (value: T) => void;
16+
value: T;
17+
unsubscribe?: (this: Context<T>) => void;
18+
}
19+
declare function makeContext(component: ComponentCreator): Creator;
20+
export { makeContext, Creator as ContextCreator, Context, ContextDetail };

create-context.js

Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
import { contextEvent } from './symbols';
2+
import { useContext } from './use-context';
3+
function makeContext(component) {
4+
return (defaultValue) => {
5+
const Context = {
6+
Provider: class extends HTMLElement {
7+
listeners;
8+
_value;
9+
constructor() {
10+
super();
11+
this.listeners = new Set();
12+
this.addEventListener(contextEvent, this);
13+
}
14+
disconnectedCallback() {
15+
this.removeEventListener(contextEvent, this);
16+
}
17+
handleEvent(event) {
18+
const { detail } = event;
19+
if (detail.Context === Context) {
20+
detail.value = this.value;
21+
detail.unsubscribe = this.unsubscribe.bind(this, detail.callback);
22+
this.listeners.add(detail.callback);
23+
event.stopPropagation();
24+
}
25+
}
26+
unsubscribe(callback) {
27+
this.listeners.delete(callback);
28+
}
29+
set value(value) {
30+
this._value = value;
31+
for (let callback of this.listeners) {
32+
callback(value);
33+
}
34+
}
35+
get value() {
36+
return this._value;
37+
}
38+
},
39+
Consumer: component(function ({ render }) {
40+
const context = useContext(Context);
41+
return render(context);
42+
}),
43+
defaultValue,
44+
};
45+
return Context;
46+
};
47+
}
48+
export { makeContext };

create-effect.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { State, Callable } from './state';
2+
declare type Effect = (this: State) => void | VoidFunction | Promise<void>;
3+
declare function createEffect(setEffects: (state: State, cb: Callable) => void): (callback: Effect, values?: unknown[] | undefined) => void;
4+
export { createEffect };

create-effect.js

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
import { Hook, hook } from './hook';
2+
function createEffect(setEffects) {
3+
return hook(class extends Hook {
4+
callback;
5+
lastValues;
6+
values;
7+
_teardown;
8+
constructor(id, state, ignored1, ignored2) {
9+
super(id, state);
10+
setEffects(state, this);
11+
}
12+
update(callback, values) {
13+
this.callback = callback;
14+
this.values = values;
15+
}
16+
call() {
17+
if (!this.values || this.hasChanged()) {
18+
this.run();
19+
}
20+
this.lastValues = this.values;
21+
}
22+
run() {
23+
this.teardown();
24+
this._teardown = this.callback.call(this.state);
25+
}
26+
teardown() {
27+
if (typeof this._teardown === 'function') {
28+
this._teardown();
29+
}
30+
}
31+
hasChanged() {
32+
return !this.lastValues || this.values.some((value, i) => this.lastValues[i] !== value);
33+
}
34+
});
35+
}
36+
export { createEffect };

haunted.d.ts

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
export { html, render, component, createContext, virtual } from './lit-haunted';
2+
export * from './core';
3+
export { default } from './core';

hook.d.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
import { State } from './state';
2+
declare abstract class Hook<P extends unknown[] = unknown[], R = unknown, H = unknown> {
3+
id: number;
4+
state: State<H>;
5+
constructor(id: number, state: State<H>);
6+
abstract update(...args: P): R;
7+
teardown?(): void;
8+
}
9+
interface CustomHook<P extends unknown[] = unknown[], R = unknown, H = unknown> {
10+
new (id: number, state: State<H>, ...args: P): Hook<P, R, H>;
11+
}
12+
declare function hook<P extends unknown[], R, H = unknown>(Hook: CustomHook<P, R, H>): (...args: P) => R;
13+
export { hook, Hook };

hook.js

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
import { current, notify } from './interface';
2+
import { hookSymbol } from './symbols';
3+
class Hook {
4+
id;
5+
state;
6+
constructor(id, state) {
7+
this.id = id;
8+
this.state = state;
9+
}
10+
}
11+
function use(Hook, ...args) {
12+
let id = notify();
13+
let hooks = current[hookSymbol];
14+
let hook = hooks.get(id);
15+
if (!hook) {
16+
hook = new Hook(id, current, ...args);
17+
hooks.set(id, hook);
18+
}
19+
return hook.update(...args);
20+
}
21+
function hook(Hook) {
22+
return use.bind(null, Hook);
23+
}
24+
export { hook, Hook };

interface.d.ts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { State } from './state';
2+
declare let current: State | null;
3+
declare function setCurrent(state: State): void;
4+
declare function clear(): void;
5+
declare function notify(): number;
6+
export { clear, current, setCurrent, notify };

interface.js

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
let current;
2+
let currentId = 0;
3+
function setCurrent(state) {
4+
current = state;
5+
}
6+
function clear() {
7+
current = null;
8+
currentId = 0;
9+
}
10+
function notify() {
11+
return currentId++;
12+
}
13+
export { clear, current, setCurrent, notify };

lit-haunted.d.ts

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
import { html, render } from 'lit';
2+
declare const component: import("./component").ComponentCreator, createContext: import("./create-context").ContextCreator;
3+
declare const virtual: any;
4+
export { component, createContext, virtual, html, render };

lit-haunted.js

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,6 @@
1+
import { html, render } from 'lit';
2+
import haunted from './core';
3+
import { makeVirtual } from './virtual';
4+
const { component, createContext } = haunted({ render });
5+
const virtual = makeVirtual();
6+
export { component, createContext, virtual, html, render };

0 commit comments

Comments
 (0)