-
-
Notifications
You must be signed in to change notification settings - Fork 9
/
Component.ts
160 lines (140 loc) · 4.82 KB
/
Component.ts
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
import type Mithril from 'mithril';
export interface ComponentAttrs extends Mithril.Attributes {}
/**
* The `Component` class defines a user interface 'building block'. A component
* generates a virtual DOM to be rendered on each redraw.
*
* Essentially, this is a wrapper for Mithril's components that adds several useful features:
*
* - In the `oninit` and `onbeforeupdate` lifecycle hooks, we store vnode attrs in `this.attrs.
* This allows us to use attrs across components without having to pass the vnode to every single
* method.
* - The static `initAttrs` method allows a convenient way to provide defaults (or to otherwise modify)
* the attrs that have been passed into a component.
* - When the component is created in the DOM, we store its DOM element under `this.element`; this lets
* us use jQuery to modify child DOM state from internal methods via the `this.$()` method.
* - A convenience `component` method, which serves as an alternative to hyperscript and JSX.
*
* As with other Mithril components, components extending Component can be initialized
* and nested using JSX, hyperscript, or a combination of both. The `component` method can also
* be used.
*
* @example
* return m('div', <MyComponent foo="bar"><p>Hello World</p></MyComponent>);
*
* @example
* return m('div', MyComponent.component({foo: 'bar'), m('p', 'Hello World!'));
*
* @see https://mithril.js.org/components.html
*/
export default abstract class Component<Attrs extends ComponentAttrs = ComponentAttrs, State = undefined> implements Mithril.ClassComponent<Attrs> {
/**
* The root DOM element for the component.
*/
element!: Element;
/**
* The attributes passed into the component.
*
* @see https://mithril.js.org/components.html#passing-data-to-components
*/
attrs!: Attrs;
/**
* Class component state that is persisted between redraws.
*
* Updating this will **not** automatically trigger a redraw, unlike
* other frameworks.
*
* This is different to Vnode state, which is always an instance of your
* class component.
*
* This is `undefined` by default.
*/
state!: State;
/**
* @inheritdoc
*/
abstract view(vnode: Mithril.Vnode<Attrs, this>): Mithril.Children;
/**
* @inheritdoc
*/
oninit(vnode: Mithril.Vnode<Attrs, this>) {
this.setAttrs(vnode.attrs);
}
/**
* @inheritdoc
*/
oncreate(vnode: Mithril.VnodeDOM<Attrs, this>) {
this.element = vnode.dom;
}
/**
* @inheritdoc
*/
onbeforeupdate(vnode: Mithril.VnodeDOM<Attrs, this>) {
this.setAttrs(vnode.attrs);
}
/**
* @inheritdoc
*/
onupdate(vnode: Mithril.VnodeDOM<Attrs, this>) {}
/**
* @inheritdoc
*/
onbeforeremove(vnode: Mithril.VnodeDOM<Attrs, this>) {}
/**
* @inheritdoc
*/
onremove(vnode: Mithril.VnodeDOM<Attrs, this>) {}
/**
* Returns a jQuery object for this component's element. If you pass in a
* selector string, this method will return a jQuery object, using the current
* element as its buffer.
*
* For example, calling `component.$('li')` will return a jQuery object
* containing all of the `li` elements inside the DOM element of this
* component.
*
* @param [selector] a jQuery-compatible selector string
* @returns the jQuery object for the DOM node
* @final
*/
$(selector?: string): JQuery {
const $element = $(this.element) as JQuery<HTMLElement>;
return selector ? $element.find(selector) : $element;
}
/**
* Convenience method to attach a component without JSX.
* Has the same effect as calling `m(THIS_CLASS, attrs, children)`.
*
* @see https://mithril.js.org/hyperscript.html#mselector,-attributes,-children
*/
static component<SAttrs extends ComponentAttrs = ComponentAttrs>(attrs: SAttrs = {} as SAttrs, children: Mithril.Children = null): Mithril.Vnode {
const componentAttrs = { ...attrs };
return m(this as any, componentAttrs, children);
}
/**
* Saves a reference to the vnode attrs after running them through initAttrs,
* and checking for common issues.
*/
private setAttrs(attrs: Attrs = {} as Attrs): void {
(this.constructor as typeof Component).initAttrs(attrs);
if (attrs) {
if ('children' in attrs) {
throw new Error(
`[${
(this.constructor as typeof Component).name
}] The "children" attribute of attrs should never be used. Either pass children in as the vnode children or rename the attribute`
);
}
if ('tag' in attrs) {
throw new Error(`[${(this.constructor as typeof Component).name}] You cannot use the "tag" attribute name with Mithril 2.`);
}
}
this.attrs = attrs;
}
/**
* Initialize the component's attrs.
*
* This can be used to assign default values for missing, optional attrs.
*/
static initAttrs(attrs: unknown): void {}
}