forked from mystiq/hydrogen-web
copy Daniel's conversion of TemplateView to TypeScript from microui
with some minor type adjustments
This commit is contained in:
parent
ea4d833a43
commit
00aa40ea7b
33 changed files with 122 additions and 92 deletions
|
@ -18,7 +18,7 @@ import {SessionView} from "./session/SessionView.js";
|
||||||
import {LoginView} from "./login/LoginView.js";
|
import {LoginView} from "./login/LoginView.js";
|
||||||
import {SessionLoadView} from "./login/SessionLoadView.js";
|
import {SessionLoadView} from "./login/SessionLoadView.js";
|
||||||
import {SessionPickerView} from "./login/SessionPickerView.js";
|
import {SessionPickerView} from "./login/SessionPickerView.js";
|
||||||
import {TemplateView} from "./general/TemplateView.js";
|
import {TemplateView} from "./general/TemplateView";
|
||||||
import {StaticView} from "./general/StaticView.js";
|
import {StaticView} from "./general/StaticView.js";
|
||||||
|
|
||||||
export class RootView extends TemplateView {
|
export class RootView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "./TemplateView.js";
|
import {TemplateView} from "./TemplateView";
|
||||||
import {spinner} from "../common.js";
|
import {spinner} from "../common.js";
|
||||||
|
|
||||||
export class LoadingView extends TemplateView {
|
export class LoadingView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "./TemplateView.js";
|
import {TemplateView} from "./TemplateView";
|
||||||
|
|
||||||
export class Menu extends TemplateView {
|
export class Menu extends TemplateView {
|
||||||
static option(label, callback) {
|
static option(label, callback) {
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
/*
|
/*
|
||||||
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
Copyright 2020 Bruno Windels <bruno@windels.cloud>
|
||||||
|
Copyright 2021 Daniel Fedorin <danila.fedorin@gmail.com>
|
||||||
|
|
||||||
Licensed under the Apache License, Version 2.0 (the "License");
|
Licensed under the Apache License, Version 2.0 (the "License");
|
||||||
you may not use this file except in compliance with the License.
|
you may not use this file except in compliance with the License.
|
||||||
|
@ -14,11 +15,12 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS } from "./html";
|
import { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS, ClassNames, Child} from "./html";
|
||||||
import {mountView} from "./utils";
|
import {mountView} from "./utils";
|
||||||
import {BaseUpdateView} from "./BaseUpdateView";
|
import {BaseUpdateView, IObservableValue} from "./BaseUpdateView";
|
||||||
|
import {IMountArgs, ViewNode, UIView} from "./types";
|
||||||
|
|
||||||
function objHasFns(obj) {
|
function objHasFns(obj: ClassNames<unknown>): obj is { [className: string]: boolean } {
|
||||||
for(const value of Object.values(obj)) {
|
for(const value of Object.values(obj)) {
|
||||||
if (typeof value === "function") {
|
if (typeof value === "function") {
|
||||||
return true;
|
return true;
|
||||||
|
@ -26,6 +28,17 @@ function objHasFns(obj) {
|
||||||
}
|
}
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
export type RenderFn<T> = (t: Builder<T>, vm: T) => ViewNode;
|
||||||
|
type EventHandler = ((event: Event) => void);
|
||||||
|
type AttributeStaticValue = string | boolean;
|
||||||
|
type AttributeBinding<T> = (value: T) => AttributeStaticValue;
|
||||||
|
export type AttrValue<T> = AttributeStaticValue | AttributeBinding<T> | EventHandler | ClassNames<T>;
|
||||||
|
export type Attributes<T> = { [attribute: string]: AttrValue<T> };
|
||||||
|
type ElementFn<T> = (attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]) => Element;
|
||||||
|
export type Builder<T> = TemplateBuilder<T> & { [tagName in typeof TAG_NAMES[string][number]]: ElementFn<T> };
|
||||||
|
|
||||||
/**
|
/**
|
||||||
Bindable template. Renders once, and allows bindings for given nodes. If you need
|
Bindable template. Renders once, and allows bindings for given nodes. If you need
|
||||||
to change the structure on a condition, use a subtemplate (if)
|
to change the structure on a condition, use a subtemplate (if)
|
||||||
|
@ -39,18 +52,21 @@ function objHasFns(obj) {
|
||||||
- add subviews inside the template
|
- add subviews inside the template
|
||||||
*/
|
*/
|
||||||
// TODO: should we rename this to BoundView or something? As opposed to StaticView ...
|
// TODO: should we rename this to BoundView or something? As opposed to StaticView ...
|
||||||
export class TemplateView extends BaseUpdateView {
|
export class TemplateView<T extends IObservableValue> extends BaseUpdateView<T> {
|
||||||
constructor(value, render = undefined) {
|
private _render?: RenderFn<T>;
|
||||||
|
private _eventListeners?: { node: Element, name: string, fn: EventHandler, useCapture: boolean }[] = undefined;
|
||||||
|
private _bindings?: (() => void)[] = undefined;
|
||||||
|
private _root?: ViewNode = undefined;
|
||||||
|
// public because used by TemplateBuilder
|
||||||
|
_subViews?: UIView[] = undefined;
|
||||||
|
|
||||||
|
constructor(value: T, render?: RenderFn<T>) {
|
||||||
super(value);
|
super(value);
|
||||||
// TODO: can avoid this if we have a separate class for inline templates vs class template views
|
// TODO: can avoid this if we have a separate class for inline templates vs class template views
|
||||||
this._render = render;
|
this._render = render;
|
||||||
this._eventListeners = null;
|
|
||||||
this._bindings = null;
|
|
||||||
this._subViews = null;
|
|
||||||
this._root = null;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_attach() {
|
_attach(): void {
|
||||||
if (this._eventListeners) {
|
if (this._eventListeners) {
|
||||||
for (let {node, name, fn, useCapture} of this._eventListeners) {
|
for (let {node, name, fn, useCapture} of this._eventListeners) {
|
||||||
node.addEventListener(name, fn, useCapture);
|
node.addEventListener(name, fn, useCapture);
|
||||||
|
@ -58,7 +74,7 @@ export class TemplateView extends BaseUpdateView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_detach() {
|
_detach(): void {
|
||||||
if (this._eventListeners) {
|
if (this._eventListeners) {
|
||||||
for (let {node, name, fn, useCapture} of this._eventListeners) {
|
for (let {node, name, fn, useCapture} of this._eventListeners) {
|
||||||
node.removeEventListener(name, fn, useCapture);
|
node.removeEventListener(name, fn, useCapture);
|
||||||
|
@ -66,13 +82,13 @@ export class TemplateView extends BaseUpdateView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
mount(options) {
|
mount(options?: IMountArgs): ViewNode {
|
||||||
const builder = new TemplateBuilder(this);
|
const builder = new TemplateBuilder(this) as Builder<T>;
|
||||||
try {
|
try {
|
||||||
if (this._render) {
|
if (this._render) {
|
||||||
this._root = this._render(builder, this._value);
|
this._root = this._render(builder, this._value);
|
||||||
} else if (this.render) { // overriden in subclass
|
} else if (this["render"]) { // overriden in subclass
|
||||||
this._root = this.render(builder, this._value);
|
this._root = this["render"](builder, this._value);
|
||||||
} else {
|
} else {
|
||||||
throw new Error("no render function passed in, or overriden in subclass");
|
throw new Error("no render function passed in, or overriden in subclass");
|
||||||
}
|
}
|
||||||
|
@ -82,10 +98,10 @@ export class TemplateView extends BaseUpdateView {
|
||||||
// takes care of update being called when needed
|
// takes care of update being called when needed
|
||||||
this.subscribeOnMount(options);
|
this.subscribeOnMount(options);
|
||||||
this._attach();
|
this._attach();
|
||||||
return this._root;
|
return this._root!;
|
||||||
}
|
}
|
||||||
|
|
||||||
unmount() {
|
unmount(): void {
|
||||||
this._detach();
|
this._detach();
|
||||||
super.unmount();
|
super.unmount();
|
||||||
if (this._subViews) {
|
if (this._subViews) {
|
||||||
|
@ -95,11 +111,11 @@ export class TemplateView extends BaseUpdateView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
root() {
|
root(): ViewNode | undefined {
|
||||||
return this._root;
|
return this._root;
|
||||||
}
|
}
|
||||||
|
|
||||||
update(value) {
|
update(value: T, props?: string[]): void {
|
||||||
this._value = value;
|
this._value = value;
|
||||||
if (this._bindings) {
|
if (this._bindings) {
|
||||||
for (const binding of this._bindings) {
|
for (const binding of this._bindings) {
|
||||||
|
@ -108,35 +124,36 @@ export class TemplateView extends BaseUpdateView {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addEventListener(node, name, fn, useCapture = false) {
|
_addEventListener(node: Element, name: string, fn: (event: Event) => void, useCapture: boolean = false): void {
|
||||||
if (!this._eventListeners) {
|
if (!this._eventListeners) {
|
||||||
this._eventListeners = [];
|
this._eventListeners = [];
|
||||||
}
|
}
|
||||||
this._eventListeners.push({node, name, fn, useCapture});
|
this._eventListeners.push({node, name, fn, useCapture});
|
||||||
}
|
}
|
||||||
|
|
||||||
_addBinding(bindingFn) {
|
_addBinding(bindingFn: () => void): void {
|
||||||
if (!this._bindings) {
|
if (!this._bindings) {
|
||||||
this._bindings = [];
|
this._bindings = [];
|
||||||
}
|
}
|
||||||
this._bindings.push(bindingFn);
|
this._bindings.push(bindingFn);
|
||||||
}
|
}
|
||||||
|
|
||||||
addSubView(view) {
|
addSubView(view: UIView): void {
|
||||||
if (!this._subViews) {
|
if (!this._subViews) {
|
||||||
this._subViews = [];
|
this._subViews = [];
|
||||||
}
|
}
|
||||||
this._subViews.push(view);
|
this._subViews.push(view);
|
||||||
}
|
}
|
||||||
|
|
||||||
removeSubView(view) {
|
removeSubView(view: UIView): void {
|
||||||
|
if (!this._subViews) { return; }
|
||||||
const idx = this._subViews.indexOf(view);
|
const idx = this._subViews.indexOf(view);
|
||||||
if (idx !== -1) {
|
if (idx !== -1) {
|
||||||
this._subViews.splice(idx, 1);
|
this._subViews.splice(idx, 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
updateSubViews(value, props) {
|
updateSubViews(value: IObservableValue, props: string[]) {
|
||||||
if (this._subViews) {
|
if (this._subViews) {
|
||||||
for (const v of this._subViews) {
|
for (const v of this._subViews) {
|
||||||
v.update(value, props);
|
v.update(value, props);
|
||||||
|
@ -146,33 +163,35 @@ export class TemplateView extends BaseUpdateView {
|
||||||
}
|
}
|
||||||
|
|
||||||
// what is passed to render
|
// what is passed to render
|
||||||
class TemplateBuilder {
|
export class TemplateBuilder<T extends IObservableValue> {
|
||||||
constructor(templateView) {
|
private _templateView: TemplateView<T>;
|
||||||
|
private _closed: boolean = false;
|
||||||
|
|
||||||
|
constructor(templateView: TemplateView<T>) {
|
||||||
this._templateView = templateView;
|
this._templateView = templateView;
|
||||||
this._closed = false;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
close() {
|
close(): void {
|
||||||
this._closed = true;
|
this._closed = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
_addBinding(fn) {
|
_addBinding(fn: () => void): void {
|
||||||
if (this._closed) {
|
if (this._closed) {
|
||||||
console.trace("Adding a binding after render will likely cause memory leaks");
|
console.trace("Adding a binding after render will likely cause memory leaks");
|
||||||
}
|
}
|
||||||
this._templateView._addBinding(fn);
|
this._templateView._addBinding(fn);
|
||||||
}
|
}
|
||||||
|
|
||||||
get _value() {
|
get _value(): T {
|
||||||
return this._templateView._value;
|
return this._templateView.value;
|
||||||
}
|
}
|
||||||
|
|
||||||
addEventListener(node, name, fn, useCapture = false) {
|
addEventListener(node: Element, name: string, fn: (event: Event) => void, useCapture: boolean = false): void {
|
||||||
this._templateView._addEventListener(node, name, fn, useCapture);
|
this._templateView._addEventListener(node, name, fn, useCapture);
|
||||||
}
|
}
|
||||||
|
|
||||||
_addAttributeBinding(node, name, fn) {
|
_addAttributeBinding(node: Element, name: string, fn: (value: T) => boolean | string): void {
|
||||||
let prevValue = undefined;
|
let prevValue: string | boolean | undefined = undefined;
|
||||||
const binding = () => {
|
const binding = () => {
|
||||||
const newValue = fn(this._value);
|
const newValue = fn(this._value);
|
||||||
if (prevValue !== newValue) {
|
if (prevValue !== newValue) {
|
||||||
|
@ -184,11 +203,11 @@ class TemplateBuilder {
|
||||||
binding();
|
binding();
|
||||||
}
|
}
|
||||||
|
|
||||||
_addClassNamesBinding(node, obj) {
|
_addClassNamesBinding(node: Element, obj: ClassNames<T>): void {
|
||||||
this._addAttributeBinding(node, "className", value => classNames(obj, value));
|
this._addAttributeBinding(node, "className", value => classNames(obj, value));
|
||||||
}
|
}
|
||||||
|
|
||||||
_addTextBinding(fn) {
|
_addTextBinding(fn: (value: T) => string): Text {
|
||||||
const initialValue = fn(this._value);
|
const initialValue = fn(this._value);
|
||||||
const node = text(initialValue);
|
const node = text(initialValue);
|
||||||
let prevValue = initialValue;
|
let prevValue = initialValue;
|
||||||
|
@ -204,21 +223,30 @@ class TemplateBuilder {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
_setNodeAttributes(node, attributes) {
|
_isEventHandler(key: string, value: AttrValue<T>): value is (event: Event) => void {
|
||||||
|
// This isn't actually safe, but it's incorrect to feed event handlers to
|
||||||
|
// non-on* attributes.
|
||||||
|
return key.startsWith("on") && key.length > 2 && typeof value === "function";
|
||||||
|
}
|
||||||
|
|
||||||
|
_setNodeAttributes(node: Element, attributes: Attributes<T>): void {
|
||||||
for(let [key, value] of Object.entries(attributes)) {
|
for(let [key, value] of Object.entries(attributes)) {
|
||||||
const isFn = typeof value === "function";
|
|
||||||
// binding for className as object of className => enabled
|
// binding for className as object of className => enabled
|
||||||
if (key === "className" && typeof value === "object" && value !== null) {
|
if (typeof value === "object") {
|
||||||
|
if (key !== "className" || value === null) {
|
||||||
|
// Ignore non-className objects.
|
||||||
|
continue;
|
||||||
|
}
|
||||||
if (objHasFns(value)) {
|
if (objHasFns(value)) {
|
||||||
this._addClassNamesBinding(node, value);
|
this._addClassNamesBinding(node, value);
|
||||||
} else {
|
} else {
|
||||||
setAttribute(node, key, classNames(value));
|
setAttribute(node, key, classNames(value, this._value));
|
||||||
}
|
}
|
||||||
} else if (key.startsWith("on") && key.length > 2 && isFn) {
|
} else if (this._isEventHandler(key, value)) {
|
||||||
const eventName = key.substr(2, 1).toLowerCase() + key.substr(3);
|
const eventName = key.substr(2, 1).toLowerCase() + key.substr(3);
|
||||||
const handler = value;
|
const handler = value;
|
||||||
this._templateView._addEventListener(node, eventName, handler);
|
this._templateView._addEventListener(node, eventName, handler);
|
||||||
} else if (isFn) {
|
} else if (typeof value === "function") {
|
||||||
this._addAttributeBinding(node, key, value);
|
this._addAttributeBinding(node, key, value);
|
||||||
} else {
|
} else {
|
||||||
setAttribute(node, key, value);
|
setAttribute(node, key, value);
|
||||||
|
@ -226,14 +254,14 @@ class TemplateBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_setNodeChildren(node, children) {
|
_setNodeChildren(node: Element, children: Child | Child[]): void{
|
||||||
if (!Array.isArray(children)) {
|
if (!Array.isArray(children)) {
|
||||||
children = [children];
|
children = [children];
|
||||||
}
|
}
|
||||||
for (let child of children) {
|
for (let child of children) {
|
||||||
if (typeof child === "function") {
|
if (typeof child === "function") {
|
||||||
child = this._addTextBinding(child);
|
child = this._addTextBinding(child);
|
||||||
} else if (!child.nodeType) {
|
} else if (typeof child === "string") {
|
||||||
// not a DOM node, turn into text
|
// not a DOM node, turn into text
|
||||||
child = text(child);
|
child = text(child);
|
||||||
}
|
}
|
||||||
|
@ -241,7 +269,7 @@ class TemplateBuilder {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
_addReplaceNodeBinding(fn, renderNode) {
|
_addReplaceNodeBinding<R>(fn: (value: T) => R, renderNode: (old: ViewNode | null) => ViewNode): ViewNode {
|
||||||
let prevValue = fn(this._value);
|
let prevValue = fn(this._value);
|
||||||
let node = renderNode(null);
|
let node = renderNode(null);
|
||||||
|
|
||||||
|
@ -260,14 +288,14 @@ class TemplateBuilder {
|
||||||
return node;
|
return node;
|
||||||
}
|
}
|
||||||
|
|
||||||
el(name, attributes, children) {
|
el(name: string, attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]): ViewNode {
|
||||||
return this.elNS(HTML_NS, name, attributes, children);
|
return this.elNS(HTML_NS, name, attributes, children);
|
||||||
}
|
}
|
||||||
|
|
||||||
elNS(ns, name, attributes, children) {
|
elNS(ns: string, name: string, attributes?: Attributes<T> | Child | Child[], children?: Child | Child[]): ViewNode {
|
||||||
if (attributes && isChildren(attributes)) {
|
if (attributes !== undefined && isChildren(attributes)) {
|
||||||
children = attributes;
|
children = attributes;
|
||||||
attributes = null;
|
attributes = undefined;
|
||||||
}
|
}
|
||||||
|
|
||||||
const node = document.createElementNS(ns, name);
|
const node = document.createElementNS(ns, name);
|
||||||
|
@ -284,20 +312,22 @@ class TemplateBuilder {
|
||||||
|
|
||||||
// this inserts a view, and is not a view factory for `if`, so returns the root element to insert in the template
|
// this inserts a view, and is not a view factory for `if`, so returns the root element to insert in the template
|
||||||
// you should not call t.view() and not use the result (e.g. attach the result to the template DOM tree).
|
// you should not call t.view() and not use the result (e.g. attach the result to the template DOM tree).
|
||||||
view(view, mountOptions = undefined) {
|
view(view: UIView, mountOptions?: IMountArgs): ViewNode {
|
||||||
this._templateView.addSubView(view);
|
this._templateView.addSubView(view);
|
||||||
return mountView(view, mountOptions);
|
return mountView(view, mountOptions);
|
||||||
}
|
}
|
||||||
|
|
||||||
// map a value to a view, every time the value changes
|
// map a value to a view, every time the value changes
|
||||||
mapView(mapFn, viewCreator) {
|
mapView<R>(mapFn: (value: T) => R, viewCreator: (mapped: R) => UIView | null): ViewNode {
|
||||||
return this._addReplaceNodeBinding(mapFn, (prevNode) => {
|
return this._addReplaceNodeBinding(mapFn, (prevNode) => {
|
||||||
if (prevNode && prevNode.nodeType !== Node.COMMENT_NODE) {
|
if (prevNode && prevNode.nodeType !== Node.COMMENT_NODE) {
|
||||||
const subViews = this._templateView._subViews;
|
const subViews = this._templateView._subViews;
|
||||||
const viewIdx = subViews.findIndex(v => v.root() === prevNode);
|
if (subViews) {
|
||||||
if (viewIdx !== -1) {
|
const viewIdx = subViews.findIndex(v => v.root() === prevNode);
|
||||||
const [view] = subViews.splice(viewIdx, 1);
|
if (viewIdx !== -1) {
|
||||||
view.unmount();
|
const [view] = subViews.splice(viewIdx, 1);
|
||||||
|
view.unmount();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
const view = viewCreator(mapFn(this._value));
|
const view = viewCreator(mapFn(this._value));
|
||||||
|
@ -312,7 +342,7 @@ class TemplateBuilder {
|
||||||
// Special case of mapView for a TemplateView.
|
// Special case of mapView for a TemplateView.
|
||||||
// Always creates a TemplateView, if this is optional depending
|
// Always creates a TemplateView, if this is optional depending
|
||||||
// on mappedValue, use `if` or `mapView`
|
// on mappedValue, use `if` or `mapView`
|
||||||
map(mapFn, renderFn) {
|
map<R>(mapFn: (value: T) => R, renderFn: (mapped: R, t: Builder<T>, vm: T) => ViewNode): ViewNode {
|
||||||
return this.mapView(mapFn, mappedValue => {
|
return this.mapView(mapFn, mappedValue => {
|
||||||
return new TemplateView(this._value, (t, vm) => {
|
return new TemplateView(this._value, (t, vm) => {
|
||||||
const rootNode = renderFn(mappedValue, t, vm);
|
const rootNode = renderFn(mappedValue, t, vm);
|
||||||
|
@ -326,7 +356,7 @@ class TemplateBuilder {
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
ifView(predicate, viewCreator) {
|
ifView(predicate: (value: T) => boolean, viewCreator: (value: T) => UIView): ViewNode {
|
||||||
return this.mapView(
|
return this.mapView(
|
||||||
value => !!predicate(value),
|
value => !!predicate(value),
|
||||||
enabled => enabled ? viewCreator(this._value) : null
|
enabled => enabled ? viewCreator(this._value) : null
|
||||||
|
@ -335,7 +365,7 @@ class TemplateBuilder {
|
||||||
|
|
||||||
// creates a conditional subtemplate
|
// creates a conditional subtemplate
|
||||||
// use mapView if you need to map to a different view class
|
// use mapView if you need to map to a different view class
|
||||||
if(predicate, renderFn) {
|
if(predicate: (value: T) => boolean, renderFn: (t: Builder<T>, vm: T) => ViewNode) {
|
||||||
return this.ifView(predicate, vm => new TemplateView(vm, renderFn));
|
return this.ifView(predicate, vm => new TemplateView(vm, renderFn));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -345,8 +375,8 @@ class TemplateBuilder {
|
||||||
This should only be used if the side-effect won't add any bindings,
|
This should only be used if the side-effect won't add any bindings,
|
||||||
event handlers, ...
|
event handlers, ...
|
||||||
You should not call the TemplateBuilder (e.g. `t.xxx()`) at all from the side effect,
|
You should not call the TemplateBuilder (e.g. `t.xxx()`) at all from the side effect,
|
||||||
instead use tags from html.js to help you construct any DOM you need. */
|
instead use tags from html.ts to help you construct any DOM you need. */
|
||||||
mapSideEffect(mapFn, sideEffect) {
|
mapSideEffect<R>(mapFn: (value: T) => R, sideEffect: (newV: R, oldV: R | undefined) => void) {
|
||||||
let prevValue = mapFn(this._value);
|
let prevValue = mapFn(this._value);
|
||||||
const binding = () => {
|
const binding = () => {
|
||||||
const newValue = mapFn(this._value);
|
const newValue = mapFn(this._value);
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
||||||
|
|
||||||
export class CompleteSSOView extends TemplateView {
|
export class CompleteSSOView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {hydrogenGithubLink} from "./common.js";
|
import {hydrogenGithubLink} from "./common.js";
|
||||||
import {PasswordLoginView} from "./PasswordLoginView.js";
|
import {PasswordLoginView} from "./PasswordLoginView.js";
|
||||||
import {CompleteSSOView} from "./CompleteSSOView.js";
|
import {CompleteSSOView} from "./CompleteSSOView.js";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
|
|
||||||
export class PasswordLoginView extends TemplateView {
|
export class PasswordLoginView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {spinner} from "../common.js";
|
import {spinner} from "../common.js";
|
||||||
|
|
||||||
/** a view used both in the login view and the loading screen
|
/** a view used both in the login view and the loading screen
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
||||||
|
|
||||||
export class SessionLoadView extends TemplateView {
|
export class SessionLoadView extends TemplateView {
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListView} from "../general/ListView";
|
import {ListView} from "../general/ListView";
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {hydrogenGithubLink} from "./common.js";
|
import {hydrogenGithubLink} from "./common.js";
|
||||||
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
import {SessionLoadStatusView} from "./SessionLoadStatusView.js";
|
||||||
|
|
||||||
|
|
|
@ -16,7 +16,7 @@ limitations under the License.
|
||||||
|
|
||||||
import {RoomView} from "./room/RoomView.js";
|
import {RoomView} from "./room/RoomView.js";
|
||||||
import {InviteView} from "./room/InviteView.js";
|
import {InviteView} from "./room/InviteView.js";
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {StaticView} from "../general/StaticView.js";
|
import {StaticView} from "../general/StaticView.js";
|
||||||
|
|
||||||
export class RoomGridView extends TemplateView {
|
export class RoomGridView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {spinner} from "../common.js";
|
import {spinner} from "../common.js";
|
||||||
|
|
||||||
export class SessionStatusView extends TemplateView {
|
export class SessionStatusView extends TemplateView {
|
||||||
|
|
|
@ -20,7 +20,7 @@ import {RoomView} from "./room/RoomView.js";
|
||||||
import {UnknownRoomView} from "./room/UnknownRoomView.js";
|
import {UnknownRoomView} from "./room/UnknownRoomView.js";
|
||||||
import {InviteView} from "./room/InviteView.js";
|
import {InviteView} from "./room/InviteView.js";
|
||||||
import {LightboxView} from "./room/LightboxView.js";
|
import {LightboxView} from "./room/LightboxView.js";
|
||||||
import {TemplateView} from "../general/TemplateView.js";
|
import {TemplateView} from "../general/TemplateView";
|
||||||
import {StaticView} from "../general/StaticView.js";
|
import {StaticView} from "../general/StaticView.js";
|
||||||
import {SessionStatusView} from "./SessionStatusView.js";
|
import {SessionStatusView} from "./SessionStatusView.js";
|
||||||
import {RoomGridView} from "./RoomGridView.js";
|
import {RoomGridView} from "./RoomGridView.js";
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {renderStaticAvatar} from "../../avatar.js";
|
import {renderStaticAvatar} from "../../avatar.js";
|
||||||
import {spinner} from "../../common.js";
|
import {spinner} from "../../common.js";
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListView} from "../../general/ListView";
|
import {ListView} from "../../general/ListView";
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {RoomTileView} from "./RoomTileView.js";
|
import {RoomTileView} from "./RoomTileView.js";
|
||||||
import {InviteTileView} from "./InviteTileView.js";
|
import {InviteTileView} from "./InviteTileView.js";
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {AvatarView} from "../../AvatarView.js";
|
import {AvatarView} from "../../AvatarView.js";
|
||||||
|
|
||||||
export class RoomTileView extends TemplateView {
|
export class RoomTileView extends TemplateView {
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {AvatarView} from "../../AvatarView.js";
|
import {AvatarView} from "../../AvatarView.js";
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
|
|
||||||
export class MemberDetailsView extends TemplateView {
|
export class MemberDetailsView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {AvatarView} from "../../AvatarView.js";
|
import {AvatarView} from "../../AvatarView.js";
|
||||||
|
|
||||||
export class MemberTileView extends TemplateView {
|
export class MemberTileView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {RoomDetailsView} from "./RoomDetailsView.js";
|
import {RoomDetailsView} from "./RoomDetailsView.js";
|
||||||
import {MemberListView} from "./MemberListView.js";
|
import {MemberListView} from "./MemberListView.js";
|
||||||
import {LoadingView} from "../../general/LoadingView.js";
|
import {LoadingView} from "../../general/LoadingView.js";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {classNames, tag} from "../../general/html";
|
import {classNames, tag} from "../../general/html";
|
||||||
import {AvatarView} from "../../AvatarView.js";
|
import {AvatarView} from "../../AvatarView.js";
|
||||||
|
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {renderStaticAvatar} from "../../avatar.js";
|
import {renderStaticAvatar} from "../../avatar.js";
|
||||||
|
|
||||||
export class InviteView extends TemplateView {
|
export class InviteView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {spinner} from "../../common.js";
|
import {spinner} from "../../common.js";
|
||||||
|
|
||||||
export class LightboxView extends TemplateView {
|
export class LightboxView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {Popup} from "../../general/Popup.js";
|
import {Popup} from "../../general/Popup.js";
|
||||||
import {Menu} from "../../general/Menu.js";
|
import {Menu} from "../../general/Menu.js";
|
||||||
import {viewClassForEntry} from "./TimelineView"
|
import {viewClassForEntry} from "./TimelineView"
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
|
|
||||||
export class RoomArchivedView extends TemplateView {
|
export class RoomArchivedView extends TemplateView {
|
||||||
render(t) {
|
render(t) {
|
||||||
|
|
|
@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {Popup} from "../../general/Popup.js";
|
import {Popup} from "../../general/Popup.js";
|
||||||
import {Menu} from "../../general/Menu.js";
|
import {Menu} from "../../general/Menu.js";
|
||||||
import {TimelineView} from "./TimelineView";
|
import {TimelineView} from "./TimelineView";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {spinner} from "../../common.js";
|
import {spinner} from "../../common.js";
|
||||||
|
|
||||||
export class TimelineLoadingView extends TemplateView {
|
export class TimelineLoadingView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
|
|
||||||
export class UnknownRoomView extends TemplateView {
|
export class UnknownRoomView extends TemplateView {
|
||||||
render(t, vm) {
|
render(t, vm) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../../general/TemplateView.js";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
|
|
||||||
export class AnnouncementView extends TemplateView {
|
export class AnnouncementView extends TemplateView {
|
||||||
render(t) {
|
render(t) {
|
||||||
|
|
|
@ -18,7 +18,7 @@ limitations under the License.
|
||||||
import {renderStaticAvatar} from "../../../avatar.js";
|
import {renderStaticAvatar} from "../../../avatar.js";
|
||||||
import {tag} from "../../../general/html";
|
import {tag} from "../../../general/html";
|
||||||
import {mountView} from "../../../general/utils";
|
import {mountView} from "../../../general/utils";
|
||||||
import {TemplateView} from "../../../general/TemplateView.js";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
import {Popup} from "../../../general/Popup.js";
|
import {Popup} from "../../../general/Popup.js";
|
||||||
import {Menu} from "../../../general/Menu.js";
|
import {Menu} from "../../../general/Menu.js";
|
||||||
import {ReactionsView} from "./ReactionsView.js";
|
import {ReactionsView} from "./ReactionsView.js";
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../../general/TemplateView.js";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
import {spinner} from "../../../common.js";
|
import {spinner} from "../../../common.js";
|
||||||
|
|
||||||
export class GapView extends TemplateView {
|
export class GapView extends TemplateView {
|
||||||
|
|
|
@ -15,7 +15,7 @@ limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {ListView} from "../../../general/ListView";
|
import {ListView} from "../../../general/ListView";
|
||||||
import {TemplateView} from "../../../general/TemplateView.js";
|
import {TemplateView} from "../../../general/TemplateView";
|
||||||
|
|
||||||
export class ReactionsView extends ListView {
|
export class ReactionsView extends ListView {
|
||||||
constructor(reactionsViewModel) {
|
constructor(reactionsViewModel) {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {StaticView} from "../../general/StaticView.js";
|
import {StaticView} from "../../general/StaticView.js";
|
||||||
|
|
||||||
export class SessionBackupSettingsView extends TemplateView {
|
export class SessionBackupSettingsView extends TemplateView {
|
||||||
|
|
|
@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
|
||||||
limitations under the License.
|
limitations under the License.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import {TemplateView} from "../../general/TemplateView.js";
|
import {TemplateView} from "../../general/TemplateView";
|
||||||
import {SessionBackupSettingsView} from "./SessionBackupSettingsView.js"
|
import {SessionBackupSettingsView} from "./SessionBackupSettingsView.js"
|
||||||
|
|
||||||
export class SettingsView extends TemplateView {
|
export class SettingsView extends TemplateView {
|
||||||
|
|
Loading…
Reference in a new issue