+2
readme
+2
readme
+55
-23
src/domlink.ts
+55
-23
src/domlink.ts
···
1
-
let _LCounter = 0; // is there a point to this? idk. might be unnecessary overhead.
1
+
let _LCounter = 0; // is there a point to this? IDK but it feels useful.
2
+
3
+
/** Core wrapper for {@link HTMLElement DOM elements}. */
2
4
export class Node {
3
5
wraps: HTMLElement;
4
6
children: Node[] = [];
5
-
id: string = "L" + _LCounter++;;
6
-
constructor(wrap: HTMLElement) {
7
-
this.wraps = wrap;
7
+
id: string = "L" + _LCounter++;
8
+
/**
9
+
* Creates a node, wrapping an HTML element.
10
+
* @example new Node(document.createElement("h1"));
11
+
*/
12
+
constructor(wraps: HTMLElement) {
13
+
this.wraps = wraps;
8
14
this.wraps.id = this.id;
9
15
}
10
-
// could i make add and with one function? yeah.
11
-
// will i do that? no.
12
-
// why not? because it feels nice to have them separate.
13
-
add(node: Node | string) {
16
+
/** Adds a child node. Should not be overridden. */
17
+
add(node: NodeEquivalent) {
14
18
if (node != undefined) {
15
19
let xnode: Node;
16
20
if (typeof node === "string") {
···
25
29
}
26
30
return this;
27
31
}
28
-
remove(node: Node) {
32
+
/** Works similarly to {@link add}, but takes multiple nodes and is the one you would want to override. */
33
+
with(...nodes: NodeEquivalent[]) {
34
+
nodes.forEach(x=>{this.add(x)});
35
+
return this;
36
+
}
37
+
/** Removes a child node in the same way {@link add} does. */
38
+
delete(node: Node) {
29
39
let idx = this.children.indexOf(node);
30
40
if (idx < 0) {
31
-
throw new Error("node not a child of this node");
41
+
throw new Error(`Node ${node.id} not found.`);
32
42
}
33
43
this.children.splice(idx, 1);
34
44
this.wraps.removeChild(node.wraps);
35
45
}
36
-
with(...nodes: (Node | string)[]) {
37
-
nodes.forEach(x=>{this.add(x)});
38
-
return this
39
-
}
40
-
style(fn: (style: CSSStyleDeclaration) => void) {
46
+
/** Removes one or more child nodes, like {@link with} does adding them. */
47
+
remove(...nodes: Node[]) {
48
+
nodes.forEach(x=>{this.delete(x)});
49
+
return this;
50
+
}
51
+
/** Conveniently chain-able callback thing for styling your nodes.
52
+
* @example new Label("This is a label!").style((s)=>{s.color="maroon";}) */
53
+
style(fn: (style: CSSStyleDeclaration) => void) {
41
54
fn(this.wraps.style);
42
55
return this;
43
56
}
57
+
/** Adds a CSS class to your node.
58
+
* @example new Container().class("my-class") */
44
59
class(cls: string) {
45
60
this.wraps.classList.add(cls);
46
61
return this;
···
52
67
NEW_TAB = "_blank",
53
68
OUT_FRAME = "_parent",
54
69
NOT_FRAME = "_top"
55
-
};
70
+
}
71
+
/** Wrapper for {@link HTMLAnchorElement `<a>`} */
56
72
export class Link extends Node {
57
73
constructor(around: NodeEquivalent | null) {
58
74
let a = document.createElement("a");
···
65
81
set destination(x: string) {
66
82
(this.wraps as HTMLAnchorElement).href = x;
67
83
}
68
-
get destination(): string {
84
+
// noinspection JSUnusedGlobalSymbols
85
+
get destination(): string {
69
86
return (this.wraps as HTMLAnchorElement).href;
70
87
}
71
88
set target(x: string) {
···
81
98
}
82
99
83
100
}
101
+
/** Wrapper for {@link HTMLDivElement `<div>`} */
84
102
export class Container extends Node {
85
103
constructor() {
86
104
let div = document.createElement("div");
···
88
106
super(div);
89
107
}
90
108
}
109
+
/** Vertical Flexbox container */
91
110
export class Column extends Container {
92
111
constructor() {
93
112
super();
94
113
this.wraps.classList.add("LColumn");
95
114
}
96
115
}
116
+
/** Horizontal Flexbox container */
97
117
export class Row extends Container {
98
118
constructor() {
99
119
super();
100
120
this.wraps.classList.add("LRow");
101
121
}
102
122
}
123
+
/** Wrapper for {@link HTMLTableElement `<table>`} */
103
124
export class Table extends Node {
104
125
constructor() {
105
126
let table = document.createElement("table");
···
107
128
super(table);
108
129
}
109
130
}
131
+
/** Wrapper for {@link HTMLTableRowElement `<tr>`} */
110
132
export class TableRow extends Node {
111
133
constructor() {
112
134
let tr = document.createElement("tr");
···
114
136
super(tr);
115
137
}
116
138
}
139
+
/** Wrapper for {@link HTMLTableCellElement `<td>`} */
117
140
export class TableCell extends Node {
118
141
constructor() {
119
142
let td = document.createElement("td");
···
121
144
super(td);
122
145
}
123
146
}
147
+
/** An "abstract" class of sorts for {@link HTMLElement}s that have a textContent member. */
124
148
export class Text extends Node {
125
149
private _text: string = "";
126
150
get text() {
···
131
155
this.wraps.textContent = x;
132
156
}
133
157
}
158
+
/** Wrapper for {@link HTMLSpanElement `<span>`} */
134
159
export class Label extends Text {
135
160
constructor(text: string = "") {
136
161
let el = document.createElement("span");
···
139
164
this.text = text;
140
165
}
141
166
}
167
+
/** Wrapper for {@link HTMLButtonElement `<button>`} */
142
168
export class Button extends Text {
143
169
constructor(label: string, action: EventListener) {
144
170
let btn = document.createElement("button");
···
149
175
this.text = label;
150
176
}
151
177
}
152
-
interface Updatable {
153
-
watch: (watcher: (newValue: string)=>void) => void;
178
+
/** Interface providing {@link Updatable.watch} */
179
+
interface Updatable<T> {
180
+
/** Takes a callback `watcher` that is called when something like a text input is updated. */
181
+
watch: (watcher: (newValue: T)=>void) => void;
154
182
}
155
-
export class Input extends Text implements Updatable {
183
+
export class Input extends Text implements Updatable<string> {
156
184
constructor(type: HTMLInputElement["type"] = "text") {
157
185
let el = document.createElement("input");
158
186
el.type = type;
···
172
200
173
201
174
202
175
-
// Implementation for Updatable
176
203
watching: ((newValue: string) => void)[] = [];
204
+
/** Implementation for {@link Updatable.watch} */
177
205
watch(watcher: (newValue: string)=>void) {
178
206
this.watching.push(watcher);
179
-
this.wraps.oninput = (e)=>{watcher(this.value)};
207
+
this.wraps.oninput = ()=>{watcher(this.value)};
180
208
watcher(this.value);
181
209
}
182
210
}
211
+
/** Wrapper for {@link HTMLImageElement `<img>`} */
183
212
export class Image extends Node {
184
213
constructor(src: string) {
185
214
let img = document.createElement("img");
···
188
217
super(img);
189
218
}
190
219
}
220
+
/** Wrapper for {@link HTMLDialogElement `<dialog>`} with methods to show/hide it as a modal. */
191
221
export class Modal extends Node {
192
222
constructor() {
193
223
let dlg: HTMLDialogElement = document.createElement("dialog");
194
224
dlg.classList.add("LModal");
195
225
super(dlg);
196
226
}
227
+
/** Shows the dialog as a modal. */
197
228
show() {
198
229
Body.add(this);
199
230
(this.wraps as HTMLDialogElement).showModal();
200
231
}
232
+
/** Hides the dialog. */
201
233
hide() {
202
234
(this.wraps as HTMLDialogElement).close();
203
235
Body.wraps.removeChild(this.wraps);
204
236
}
205
237
}
206
238
207
-
// user helper shit
239
+
/** Convenience wrapper for the {@link HTMLBodyElement body} of the document. */
208
240
export const Body = new Node(document.body);
+2
-2
src/issuesearch.ts
+2
-2
src/issuesearch.ts
···
146
146
(runButton.wraps as HTMLButtonElement).disabled = true;
147
147
if (last) {
148
148
try {
149
-
Body.remove(last);
149
+
Body.delete(last);
150
150
} catch {}
151
151
last = null;
152
152
}
···
171
171
searchInput.watch((search)=>{
172
172
if (allIssues.length > 0) {
173
173
if (last) {
174
-
Body.remove(last);
174
+
Body.delete(last);
175
175
}
176
176
search = search.toLowerCase();
177
177
last = renderIssues(allIssues.filter((item)=>{