copy Daniel's conversion of html.js to TypeScript from microui

This commit is contained in:
Bruno Windels 2021-09-16 10:45:06 +02:00
parent cce8207870
commit 69672dd99c
10 changed files with 33 additions and 27 deletions

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {tag, text, classNames, setAttribute} from "./general/html.js"; import {tag, text, classNames, setAttribute} from "./general/html";
/** /**
* @param {Object} vm view model with {avatarUrl, avatarColorNumber, avatarTitle, avatarLetter} * @param {Object} vm view model with {avatarUrl, avatarColorNumber, avatarTitle, avatarLetter}
* @param {Number} size * @param {Number} size

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {el} from "./html.js"; import {el} from "./html";
import {mountView} from "./utils"; import {mountView} from "./utils";
import {ListView} from "./ListView"; import {ListView} from "./ListView";
import {insertAt} from "./utils"; import {insertAt} from "./utils";

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {el} from "./html.js"; import {el} from "./html";
import {mountView, insertAt} from "./utils"; import {mountView, insertAt} from "./utils";
import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList.js"; import {BaseObservableList as ObservableList} from "../../../../observable/list/BaseObservableList.js";
import {UIView, IMountArgs} from "./types"; import {UIView, IMountArgs} from "./types";
@ -79,7 +79,7 @@ export class ListView<T, V extends UIView> implements UIView {
if (this._className) { if (this._className) {
attr.className = this._className; attr.className = this._className;
} }
this._root = el(this._tagName, attr); this._root = el(this._tagName, attr) as HTMLElement;
this.loadList(); this.loadList();
if (this._onItemClick) { if (this._onItemClick) {
this._root!.addEventListener("click", this); this._root!.addEventListener("click", this);

View file

@ -15,7 +15,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {tag} from "../general/html.js"; import {tag} from "../general/html";
export class StaticView { export class StaticView {
constructor(value, render = undefined) { constructor(value, render = undefined) {

View file

@ -14,7 +14,7 @@ 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.js"; import { setAttribute, text, isChildren, classNames, TAG_NAMES, HTML_NS } from "./html";
import {mountView} from "./utils"; import {mountView} from "./utils";
import {BaseUpdateView} from "./BaseUpdateView.js"; import {BaseUpdateView} from "./BaseUpdateView.js";

View file

@ -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.
@ -16,12 +17,16 @@ limitations under the License.
// DOM helper functions // DOM helper functions
export function isChildren(children) { export type ClassNames<T> = { [className: string]: boolean | ((value?: T) => boolean) }
export type BasicAttributes<T> = { [attribute: string]: ClassNames<T> | boolean | string }
export type Child = string | Text | Element
export function isChildren(children: object | Child | Child[]): children is Child | Child[] {
// children should be an not-object (that's the attributes), or a domnode, or an array // children should be an not-object (that's the attributes), or a domnode, or an array
return typeof children !== "object" || !!children.nodeType || Array.isArray(children); return typeof children !== "object" || "nodeType" in children || Array.isArray(children);
} }
export function classNames(obj, value) { export function classNames<T>(obj: ClassNames<T>, value?: T): string {
return Object.entries(obj).reduce((cn, [name, enabled]) => { return Object.entries(obj).reduce((cn, [name, enabled]) => {
if (typeof enabled === "function") { if (typeof enabled === "function") {
enabled = enabled(value); enabled = enabled(value);
@ -34,7 +39,7 @@ export function classNames(obj, value) {
}, ""); }, "");
} }
export function setAttribute(el, name, value) { export function setAttribute(el: Element, name: string, value: string | boolean): void {
if (name === "className") { if (name === "className") {
name = "class"; name = "class";
} }
@ -48,22 +53,24 @@ export function setAttribute(el, name, value) {
} }
} }
export function el(elementName, attributes, children) { export function el(elementName: string, attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]): Element {
return elNS(HTML_NS, elementName, attributes, children); return elNS(HTML_NS, elementName, attributes, children);
} }
export function elNS(ns, elementName, attributes, children) { export function elNS(ns: string, elementName: string, attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]): Element {
if (attributes && isChildren(attributes)) { if (attributes && isChildren(attributes)) {
children = attributes; children = attributes;
attributes = null; attributes = undefined;
} }
const e = document.createElementNS(ns, elementName); const e = document.createElementNS(ns, elementName);
if (attributes) { if (attributes) {
for (let [name, value] of Object.entries(attributes)) { for (let [name, value] of Object.entries(attributes)) {
if (name === "className" && typeof value === "object" && value !== null) { if (typeof value === "object") {
value = classNames(value); // Only className should ever be an object; be careful
// here anyway and ignore object-valued non-className attributes.
value = (value !== null && name === "className") ? classNames(value) : false;
} }
setAttribute(e, name, value); setAttribute(e, name, value);
} }
@ -74,7 +81,7 @@ export function elNS(ns, elementName, attributes, children) {
children = [children]; children = [children];
} }
for (let c of children) { for (let c of children) {
if (!c.nodeType) { if (typeof c === "string") {
c = text(c); c = text(c);
} }
e.appendChild(c); e.appendChild(c);
@ -83,12 +90,12 @@ export function elNS(ns, elementName, attributes, children) {
return e; return e;
} }
export function text(str) { export function text(str: string): Text {
return document.createTextNode(str); return document.createTextNode(str);
} }
export const HTML_NS = "http://www.w3.org/1999/xhtml"; export const HTML_NS: string = "http://www.w3.org/1999/xhtml";
export const SVG_NS = "http://www.w3.org/2000/svg"; export const SVG_NS: string = "http://www.w3.org/2000/svg";
export const TAG_NAMES = { export const TAG_NAMES = {
[HTML_NS]: [ [HTML_NS]: [
@ -97,10 +104,9 @@ export const TAG_NAMES = {
"table", "thead", "tbody", "tr", "th", "td", "hr", "table", "thead", "tbody", "tr", "th", "td", "hr",
"pre", "code", "button", "time", "input", "textarea", "label", "form", "progress", "output", "video"], "pre", "code", "button", "time", "input", "textarea", "label", "form", "progress", "output", "video"],
[SVG_NS]: ["svg", "circle"] [SVG_NS]: ["svg", "circle"]
}; } as const;
export const tag = {};
export const tag: { [tagName in typeof TAG_NAMES[string][number]]: (attributes?: BasicAttributes<never> | Child | Child[], children?: Child | Child[]) => Element } = {} as any;
for (const [ns, tags] of Object.entries(TAG_NAMES)) { for (const [ns, tags] of Object.entries(TAG_NAMES)) {
for (const tagName of tags) { for (const tagName of tags) {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {UIView, IMountArgs} from "./types"; import {UIView, IMountArgs} from "./types";
import {tag} from "./html.js"; import {tag} from "./html";
export function mountView(view: UIView, mountArgs: IMountArgs): HTMLElement { export function mountView(view: UIView, mountArgs: IMountArgs): HTMLElement {
let node; let node;
@ -38,7 +38,7 @@ export function errorToDOM(error: Error): HTMLElement {
tag.h3(error.message), tag.h3(error.message),
tag.p(`This occurred while running ${callee}.`), tag.p(`This occurred while running ${callee}.`),
tag.pre(error.stack), tag.pre(error.stack),
]); ]) as HTMLElement;
} }
export function insertAt(parentNode: HTMLElement, idx: number, childNode: HTMLElement): void { export function insertAt(parentNode: HTMLElement, idx: number, childNode: HTMLElement): void {

View file

@ -15,7 +15,7 @@ limitations under the License.
*/ */
import {TemplateView} from "../../general/TemplateView.js"; import {TemplateView} from "../../general/TemplateView.js";
import {classNames, tag} from "../../general/html.js"; import {classNames, tag} from "../../general/html";
import {AvatarView} from "../../AvatarView.js"; import {AvatarView} from "../../AvatarView.js";
export class RoomDetailsView extends TemplateView { export class RoomDetailsView extends TemplateView {

View file

@ -16,7 +16,7 @@ limitations under the License.
*/ */
import {renderStaticAvatar} from "../../../avatar.js"; import {renderStaticAvatar} from "../../../avatar.js";
import {tag} from "../../../general/html.js"; 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.js";
import {Popup} from "../../../general/Popup.js"; import {Popup} from "../../../general/Popup.js";

View file

@ -14,7 +14,7 @@ See the License for the specific language governing permissions and
limitations under the License. limitations under the License.
*/ */
import {tag, text} from "../../../general/html.js"; import {tag, text} from "../../../general/html";
import {BaseMessageView} from "./BaseMessageView.js"; import {BaseMessageView} from "./BaseMessageView.js";
export class TextMessageView extends BaseMessageView { export class TextMessageView extends BaseMessageView {