typescript(monadic): feat: basics of outputs
Signed-off-by: prescientmoon <git@moonythm.dev>
This commit is contained in:
parent
9e6c150e5f
commit
9e3da82f0d
|
@ -9,7 +9,7 @@ type TodoState = {
|
||||||
type TodoAction = 'complete';
|
type TodoAction = 'complete';
|
||||||
|
|
||||||
const todo = makeComponent(
|
const todo = makeComponent(
|
||||||
({ name, done }: TodoState, dispatch: Dispatcher<TodoAction>) => {
|
({ name, done }: TodoState, { dispatch }) => {
|
||||||
return html`
|
return html`
|
||||||
<div>
|
<div>
|
||||||
Name: ${name} Completed: ${done}
|
Name: ${name} Completed: ${done}
|
||||||
|
|
|
@ -40,6 +40,7 @@
|
||||||
"typescript": "^3.8.3"
|
"typescript": "^3.8.3"
|
||||||
},
|
},
|
||||||
"dependencies": {
|
"dependencies": {
|
||||||
|
"fp-ts": "^2.6.0",
|
||||||
"helpers": "^0.0.6"
|
"helpers": "^0.0.6"
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -1,25 +1,36 @@
|
||||||
import { mapRecord } from './Record';
|
import { mapRecord } from './Record';
|
||||||
import { values } from './helpers';
|
import { values } from './helpers';
|
||||||
|
import O from 'fp-ts/es6/Option';
|
||||||
|
import { constant } from 'fp-ts/es6/function';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Helper type for dispatcher functions
|
* Helper type for dispatcher functions
|
||||||
*/
|
*/
|
||||||
export type Dispatcher<A> = (action: A) => void;
|
export type Dispatcher<A> = (action: A) => void;
|
||||||
|
|
||||||
|
export type Communicate<
|
||||||
|
T,
|
||||||
|
A,
|
||||||
|
O,
|
||||||
|
N extends string,
|
||||||
|
C extends ChildrenConfigs<N>
|
||||||
|
> = {
|
||||||
|
dispatch: Dispatcher<A>;
|
||||||
|
child: <K extends N>(key: K, name: string, input: C[K]['input']) => T;
|
||||||
|
raise: (event: O) => void;
|
||||||
|
};
|
||||||
|
|
||||||
export type ComponentConfig<
|
export type ComponentConfig<
|
||||||
T,
|
T,
|
||||||
S,
|
S,
|
||||||
A,
|
A,
|
||||||
|
O,
|
||||||
N extends string,
|
N extends string,
|
||||||
C extends ChildrenConfigs<N>
|
C extends ChildrenConfigs<N>
|
||||||
> = {
|
> = {
|
||||||
render: (
|
render: (state: S, communicate: Communicate<T, A, O, N, C>) => T;
|
||||||
state: S,
|
|
||||||
dispatch: Dispatcher<A>,
|
|
||||||
child: <K extends N>(key: K, name: string, input: C[K]['input']) => T
|
|
||||||
) => T;
|
|
||||||
handleAction: (action: A, state: S) => S;
|
handleAction: (action: A, state: S) => S;
|
||||||
children: ChildrenTemplates<T, S, N, C>;
|
children: ChildrenTemplates<T, S, A, O, N, C>;
|
||||||
};
|
};
|
||||||
|
|
||||||
type GenericLens<T, U> = {
|
type GenericLens<T, U> = {
|
||||||
|
@ -27,17 +38,26 @@ type GenericLens<T, U> = {
|
||||||
set: (v: T, n: U) => T;
|
set: (v: T, n: U) => T;
|
||||||
};
|
};
|
||||||
|
|
||||||
type Child<I = unknown, S = unknown> = {
|
type Child<I = unknown, S = unknown, O = unknown> = {
|
||||||
input: I;
|
input: I;
|
||||||
state: S;
|
state: S;
|
||||||
|
output: O;
|
||||||
};
|
};
|
||||||
|
|
||||||
type ChildrenConfigs<N extends string> = Record<N, Child>;
|
type ChildrenConfigs<N extends string> = Record<N, Child>;
|
||||||
|
|
||||||
type ChildrenTemplates<T, S, N extends string, C extends ChildrenConfigs<N>> = {
|
type ChildrenTemplates<
|
||||||
|
T,
|
||||||
|
S,
|
||||||
|
A,
|
||||||
|
O,
|
||||||
|
N extends string,
|
||||||
|
C extends ChildrenConfigs<N>
|
||||||
|
> = {
|
||||||
[K in N]: {
|
[K in N]: {
|
||||||
lens: (input: C[K]['input']) => GenericLens<S, C[K]['state']>;
|
lens: (input: C[K]['input']) => GenericLens<S, C[K]['state']>;
|
||||||
component: ComponentConfig<T, C[K]['state'], unknown, string, {}>;
|
component: ComponentConfig<T, C[K]['state'], O, unknown, string, {}>;
|
||||||
|
handleOutput: (input: C[K]['input'], output: C[K]['output']) => O.Option<A>;
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -45,7 +65,14 @@ type Children<T, S, N extends string, C extends ChildrenConfigs<N>> = {
|
||||||
[K in N]: Record<
|
[K in N]: Record<
|
||||||
string,
|
string,
|
||||||
{
|
{
|
||||||
component: Component<T, C[K]['state'], unknown, string, {}>;
|
component: Component<
|
||||||
|
T,
|
||||||
|
C[K]['state'],
|
||||||
|
unknown,
|
||||||
|
C[K]['output'],
|
||||||
|
string,
|
||||||
|
{}
|
||||||
|
>;
|
||||||
lens: GenericLens<S, C[K]['state']>;
|
lens: GenericLens<S, C[K]['state']>;
|
||||||
}
|
}
|
||||||
>;
|
>;
|
||||||
|
@ -55,16 +82,17 @@ export class Component<
|
||||||
T,
|
T,
|
||||||
S,
|
S,
|
||||||
A,
|
A,
|
||||||
|
O,
|
||||||
N extends string,
|
N extends string,
|
||||||
C extends ChildrenConfigs<N>
|
C extends ChildrenConfigs<N>
|
||||||
> {
|
> {
|
||||||
private childrenMap: Children<T, S, N, C>;
|
public childrenMap: Children<T, S, N, C>;
|
||||||
public constructor(
|
public constructor(
|
||||||
protected state: S,
|
protected state: S,
|
||||||
private config: ComponentConfig<T, S, A, N, C>,
|
private config: ComponentConfig<T, S, A, O, N, C>,
|
||||||
private pushDownwards: (state: S) => void
|
private pushDownwards: (state: S) => void
|
||||||
) {
|
) {
|
||||||
this.childrenMap = mapRecord(this.config.children, () => ({}));
|
this.childrenMap = mapRecord(this.config.children, constant({}));
|
||||||
}
|
}
|
||||||
|
|
||||||
protected pushStateUpwards(state: S) {
|
protected pushStateUpwards(state: S) {
|
||||||
|
@ -76,10 +104,10 @@ export class Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
public getTemplate(): T {
|
public getTemplate(): T {
|
||||||
return this.config.render(
|
return this.config.render(this.state, {
|
||||||
this.state,
|
dispatch: value => this.dispatch(value),
|
||||||
value => this.dispatch(value),
|
raise: _ => undefined,
|
||||||
(key, name, input) => {
|
child: (key, name, input) => {
|
||||||
const hasName = Reflect.has(this.childrenMap[key], name);
|
const hasName = Reflect.has(this.childrenMap[key], name);
|
||||||
|
|
||||||
if (!hasName) {
|
if (!hasName) {
|
||||||
|
@ -97,8 +125,8 @@ export class Component<
|
||||||
}
|
}
|
||||||
|
|
||||||
return this.childrenMap[key][name].component.getTemplate();
|
return this.childrenMap[key][name].component.getTemplate();
|
||||||
}
|
},
|
||||||
);
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
public dispatch(action: A) {
|
public dispatch(action: A) {
|
||||||
|
@ -111,10 +139,8 @@ export class Component<
|
||||||
/**
|
/**
|
||||||
* Get a list of all the children of the component
|
* Get a list of all the children of the component
|
||||||
*/
|
*/
|
||||||
private children() {
|
private children(): this['childrenMap'][N][string][] {
|
||||||
return values(this.childrenMap).flatMap(record =>
|
return values(this.childrenMap).flatMap(record => values(record)) as any;
|
||||||
values(record)
|
|
||||||
) as Children<T, S, N, C>[N][string][];
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -129,15 +155,17 @@ export const makeComponent = <
|
||||||
T,
|
T,
|
||||||
S,
|
S,
|
||||||
A,
|
A,
|
||||||
|
O,
|
||||||
N extends string,
|
N extends string,
|
||||||
C extends ChildrenConfigs<N>
|
C extends ChildrenConfigs<N>
|
||||||
>(
|
>(
|
||||||
render: ComponentConfig<T, S, A, N, C>['render'],
|
render: ComponentConfig<T, S, A, O, N, C>['render'],
|
||||||
handleAction: ComponentConfig<T, S, A, N, C>['handleAction'],
|
handleAction: ComponentConfig<T, S, O, A, N, C>['handleAction'],
|
||||||
children: ComponentConfig<T, S, A, N, C>['children']
|
children: ComponentConfig<T, S, A, O, N, C>['children']
|
||||||
) => ({ render, handleAction, children });
|
) => ({ render, handleAction, children });
|
||||||
|
|
||||||
export const mkChild = <T, PS, I, S>(
|
export const mkChild = <T, PS, A, I, S, O>(
|
||||||
lens: (input: I) => GenericLens<PS, S>,
|
lens: (input: I) => GenericLens<PS, S>,
|
||||||
component: ComponentConfig<T, S, unknown, string, {}>
|
component: ComponentConfig<T, S, A, O, string, {}>,
|
||||||
) => ({ lens, component });
|
handleOutput: (input: I, output: O) => O.Option<A> = constant(O.none)
|
||||||
|
) => ({ lens, component, handleOutput });
|
||||||
|
|
|
@ -1,13 +1,13 @@
|
||||||
import { ComponentConfig, Component } from './Component';
|
import { ComponentConfig, Component } from './Component';
|
||||||
|
|
||||||
export type EnvConfig<T, S, A> = {
|
export type EnvConfig<T, S, A, O> = {
|
||||||
render: (template: T, parent: HTMLElement) => void;
|
render: (template: T, parent: HTMLElement) => void;
|
||||||
parent: HTMLElement;
|
parent: HTMLElement;
|
||||||
component: ComponentConfig<T, S, A, string, {}>;
|
component: ComponentConfig<T, S, A, O, string, {}>;
|
||||||
initialState: S;
|
initialState: S;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const runUi = <T, S, A>(config: EnvConfig<T, S, A>) => {
|
export const runUi = <T, S, A, O>(config: EnvConfig<T, S, A, O>) => {
|
||||||
const reRender = () => config.render(component.getTemplate(), config.parent);
|
const reRender = () => config.render(component.getTemplate(), config.parent);
|
||||||
|
|
||||||
const component = new Component(config.initialState, config.component, _ => {
|
const component = new Component(config.initialState, config.component, _ => {
|
||||||
|
|
|
@ -3448,6 +3448,11 @@ form-data@~2.3.2:
|
||||||
combined-stream "^1.0.6"
|
combined-stream "^1.0.6"
|
||||||
mime-types "^2.1.12"
|
mime-types "^2.1.12"
|
||||||
|
|
||||||
|
fp-ts@^2.6.0:
|
||||||
|
version "2.6.0"
|
||||||
|
resolved "https://registry.yarnpkg.com/fp-ts/-/fp-ts-2.6.0.tgz#55dc9e82612a44a7bfed3484b18c535514d63e51"
|
||||||
|
integrity sha512-4EKVa3EOP3NoDwXCLgSCT2t+E2wPCWDXCuj3wEcQ1ovkLfdCMxKxCuElpXptIPBmtA+KbjnUl5Ta/1U3jsCmkg==
|
||||||
|
|
||||||
fragment-cache@^0.2.1:
|
fragment-cache@^0.2.1:
|
||||||
version "0.2.1"
|
version "0.2.1"
|
||||||
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
resolved "https://registry.yarnpkg.com/fragment-cache/-/fragment-cache-0.2.1.tgz#4290fad27f13e89be7f33799c6bc5a0abfff0d19"
|
||||||
|
|
Loading…
Reference in a new issue