Skip to content

Instantly share code, notes, and snippets.

@nordfjord
Last active December 2, 2018 16:53
Show Gist options
  • Save nordfjord/151d8fb6ff8a837f28b73eab3ac6cbe5 to your computer and use it in GitHub Desktop.
Save nordfjord/151d8fb6ff8a837f28b73eab3ac6cbe5 to your computer and use it in GitHub Desktop.
Mithril Components
export const FactoryComponent = <TAttrs>(
factory: (attrs: TAttrs, children?: Mithril.Child) => Component<TAttrs>
) =>
({
controller(attrs: TAttrs, children?: Mithril.Child) {
return factory(attrs, children);
},
view(component: Component<TAttrs>, attrs: TAttrs, children) {
return component.view(attrs, children);
},
} as Mithril.Component<TAttrs, Component<TAttrs>>);
interface Component<T> {
view(attrs: T, children?: Mithril.Child): Mithril.VirtualElement;
}
import { stream, scan } from 'flyd';
import m from 'mithril';
interface ReducerComponentOptions<TState, TAction, Attrs> {
reducer: (state: TState, action: TAction) => TState;
initialState: (attrs: Attrs) => TState;
view: (state: TState, dispatch: flyd.Stream<TAction>) => Mithril.VirtualElement;
didMount?: (state: TState, dispatch: flyd.Stream<TAction>) => void;
}
interface ReducerComponentState<TState, TAction> {
state$: flyd.Stream<TState>;
action$: flyd.Stream<TAction>;
}
export const ReducerComponent = <TState, TAction, Attrs = any>({
reducer,
initialState,
view,
didMount,
}: ReducerComponentOptions<TState, TAction, Attrs>): Mithril.Component<
Attrs,
ReducerComponentState<TState, TAction>
> => ({
controller(attrs: Attrs) {
const action$ = stream<TAction>();
const state$ = action$.pipe(scan(reducer, initialState(attrs)));
state$.map(() => m.redraw());
if (didMount) didMount(state$(), action$);
return {
action$,
state$,
onunload() {
action$.end(true);
},
};
},
view({ action$, state$ }) {
return view(state$.val, action$);
},
});
import { Maybe } from 'util/maybe';
import m from 'mithril';
const isStream = (stream: any): stream is vNodeStream => {
if (typeof stream === 'function' && 'hasVal' in stream) {
return true;
}
return false;
};
export interface Ctrl {
onunload?: () => void;
toolbar?: () => Mithril.VirtualElement;
header?: () => Mithril.VirtualElement;
title?: () => Mithril.VirtualElement;
}
export interface vNodeStream extends Ctrl, flyd.Stream<Mithril.VirtualElement> {}
interface StreamComponentCtrl extends Ctrl {
view$(): Mithril.VirtualElement;
}
const noop = function() {
return;
};
export const StreamComponent = <TAttrs>(
f: (attrs: TAttrs) => vNodeStream | (() => Mithril.VirtualElement)
): Mithril.Component<TAttrs, StreamComponentCtrl> => ({
controller(attrs) {
const view$ = f(attrs);
const onunload = (isStream(view$) && view$.onunload) || noop;
const ctrl: StreamComponentCtrl = { view$, onunload };
if (isStream(view$)) {
view$.map(() => m.debounced_redraw());
ctrl.toolbar = view$.toolbar;
ctrl.header = view$.header;
ctrl.title = view$.title;
}
return ctrl;
},
view: ({ view$ }) => Maybe(view$()).getOrElse(m('')),
});
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment