1
0
mirror of https://github.com/flarum/core.git synced 2025-08-07 00:47:00 +02:00

Significantly increase documentation for Component and Fragment

This commit is contained in:
Alexander Skvortsov
2020-09-04 17:35:21 -04:00
committed by Franz Liedke
parent e7f6e37799
commit 60dbd3f26c
2 changed files with 91 additions and 23 deletions

View File

@@ -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', <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<T extends ComponentAttrs = any> implements Mithril.ClassComponent<T> {
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<T, this>): Mithril.Children;
oninit(vnode: Mithril.Vnode<T, this>) {
/**
* @inheritdoc
*/
protected oninit(vnode: Mithril.Vnode<T, this>) {
this.setAttrs(vnode.attrs);
}
oncreate(vnode: Mithril.VnodeDOM<T, this>) {
/**
* @inheritdoc
*/
protected oncreate(vnode: Mithril.VnodeDOM<T, this>) {
this.element = vnode.dom;
}
onbeforeupdate(vnode: Mithril.VnodeDOM<T, this>) {
/**
* @inheritdoc
*/
protected onbeforeupdate(vnode: Mithril.VnodeDOM<T, this>) {
this.setAttrs(vnode.attrs);
}
@@ -48,7 +85,7 @@ export default abstract class Component<T extends ComponentAttrs = any> 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<T extends ComponentAttrs = any> 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<T extends ComponentAttrs = any> implemen
this.attrs = attrs;
}
protected static initAttrs<T>(attrs: T): T {
return attrs;
}
/**
* Initialize the component's attrs.
*/
protected static initAttrs<T>(attrs: T): void {}
}

View File

@@ -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();
}