diff --git a/js/src/common/Component.ts b/js/src/common/Component.ts
index 765abc60e..7b450f3c9 100644
--- a/js/src/common/Component.ts
+++ b/js/src/common/Component.ts
@@ -8,30 +8,67 @@ export type ComponentAttrs = {
/**
* The `Component` class defines a user interface 'building block'. A component
- * can generate a virtual DOM to be rendered on each redraw.
+ * 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.component({foo: 'bar'));
+ * return m('div', Hello World
);
+ *
+ * @example
+ * return m('div', MyComponent.component({foo: 'bar'), m('p', 'Hello World!'));
*
* @see https://mithril.js.org/components.html
*/
export default abstract class Component implements Mithril.ClassComponent {
- element!: Element;
+ /**
+ * The root DOM element for the component.
+ */
+ protected element!: Element;
- attrs: T;
+ /**
+ * The attributes passed into the component.
+ *
+ * @see https://mithril.js.org/hyperscript.html#dom-attributes
+ */
+ protected attrs: T;
+ /**
+ * @inheritdoc
+ */
abstract view(vnode: Mithril.Vnode): Mithril.Children;
- oninit(vnode: Mithril.Vnode) {
+ /**
+ * @inheritdoc
+ */
+ protected oninit(vnode: Mithril.Vnode) {
this.setAttrs(vnode.attrs);
}
- oncreate(vnode: Mithril.VnodeDOM) {
+ /**
+ * @inheritdoc
+ */
+ protected oncreate(vnode: Mithril.VnodeDOM) {
this.element = vnode.dom;
}
- onbeforeupdate(vnode: Mithril.VnodeDOM) {
+ /**
+ * @inheritdoc
+ */
+ protected onbeforeupdate(vnode: Mithril.VnodeDOM) {
this.setAttrs(vnode.attrs);
}
@@ -48,7 +85,7 @@ export default abstract class Component implemen
* @returns {jQuery} the jQuery object for the DOM node
* @final
*/
- $(selector) {
+ protected $(selector) {
const $element = $(this.element);
return selector ? $element.find(selector) : $element;
@@ -56,14 +93,21 @@ export default abstract class Component implemen
/**
* 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(attrs = {}, children = null) {
+ static component(attrs = {}, children = null): Mithril.Vnode {
const componentProps = Object.assign({}, attrs);
return m(this as any, componentProps, children);
}
- private setAttrs(attrs: T = {} as T) {
+ /**
+ * Saves a reference to the vnode attrs after running them through initAttrs,
+ * and checking for common issues.
+ */
+ private setAttrs(attrs: T = {} as T): void {
this.constructor.initAttrs(attrs);
if (attrs) {
@@ -83,7 +127,8 @@ export default abstract class Component implemen
this.attrs = attrs;
}
- protected static initAttrs(attrs: T): T {
- return attrs;
- }
+ /**
+ * Initialize the component's attrs.
+ */
+ protected static initAttrs(attrs: T): void {}
}
diff --git a/js/src/common/Fragment.ts b/js/src/common/Fragment.ts
index 7eab011ea..a929ce066 100644
--- a/js/src/common/Fragment.ts
+++ b/js/src/common/Fragment.ts
@@ -1,32 +1,54 @@
+import * as Mithril from 'mithril';
+
/**
- * Base class enabling jquery for mithril components attached with m.render().
+ * The `Fragment` class provides a wrapper class for Mithril components to be used with m.render().
+ * This is very similar to the `Component` wrapper class, but is used for more fine-grained control over
+ * the rendering and display of some significant chunks of the DOM.
+ *
+ * The main benefit of using this wrapper class as opposed to Mithril components directly
+ * is that it stores the vnode DOM, and provides a `$()` method allowing manipulation of said DOM.
+ *
+ * This should only be used when necessary, and only with `m.render`. If you are unsure whether you need
+ * this or `Component, you probably need `Component`.
*/
-export default abstract class Fragment {
- element!: Element;
+export default abstract class Fragment implements Mithril.ClassComponent {
+ /**
+ * The root DOM element for the fragment.
+ */
+ protected element!: Element;
/**
- * Returns a jQuery object for this component's element. If you pass in a
+ * Returns a jQuery object for this fragment'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
+ * For example, calling `fragment.$('li')` will return a jQuery object
* containing all of the `li` elements inside the DOM element of this
- * component.
+ * fragment.
*
* @param {String} [selector] a jQuery-compatible selector string
* @returns {jQuery} the jQuery object for the DOM node
* @final
*/
- $(selector) {
+ public $(selector) {
const $element = $(this.element);
return selector ? $element.find(selector) : $element;
}
/**
+ * Get the renderable virtual DOM that represents the fragment's view.
*
+ * This should NOT be overridden by subclasses. Subclasses wishing to define
+ * their virtual DOM should override Fragment#view instead.
+ *
+ * @example
+ * const fragment = new MyFragment();
+ * m.render(document.body, fragment.render());
+ *
+ * @final
*/
- render() {
+ public render(): Mithril.Vnode {
const vdom = this.view();
vdom.attrs = vdom.attrs || {};
@@ -42,7 +64,8 @@ export default abstract class Fragment {
return vdom;
}
- oncreate: () => {};
-
+ /**
+ * @inheritdoc
+ */
abstract view();
}