Pages

npm links

Compilation With Depack

The module was compiled with Depack: the website bundler that runs Google Closure Compiler. The command for creating the bundle is:

{
  "depack": "depack example/App.jsx -o splendid/js/main.js -I 2018 -a -H -p -w"
}
where the advanced compilation is set, as well as Preact's h pragma added to the beginning of each file. -p is for pretty printing, and -w for no warnings. Depack then performs static-analysis on the entered files, and creates a temp directory where it transpiles JSX files. Depack Preact Form Compilation

Back To Top

Development Server

The development server is setup with the @idio/FrontEnd1.4.0 middleware that serves the source files as modules with JSX transpiled using @a-la/JSX1.4.0. This allows to change source code files and serve them as native modules that the modern browser understands.
import core from '@idio/core'
import render from '@depack/render'

(async () => {
  const { url } = await core({
    frontend: { directory: ['example', 'src'] },
    async api(ctx, next) {
      if (ctx.path == '/form') {
        ctx.body = { data: 'ok', error: null }
      } else await next()
    },
    serve(ctx) {
      ctx.body = '<!doctype html>' + render(<html>
        <head>
          <link rel="stylesheet" href="https://stackpath.bootstrapcdn.com/bootstrap/4.2.1/css/bootstrap.min.css" integrity="sha384-GJzZqFGwb1QTTN6wy59ffF1BuGJpLSa9DkKMp0DgiMDm4iYMj70gZWKYbI706tWS" crossOrigin="anonymous"/>
          <meta charset="utf-8"/>
          <meta name="viewport" content="width=device-width, initial-scale=1, shrink-to-fit=no"/>

          <title>Form Example</title>
          <style>
            {`body {
              background: #dfffdf;
            }`}
          </style>
        </head>
        <body id="preact">
          <script type="module" src="/example/App.jsx"/>
        </body>
      </html>)
    },
  }, { port: null })
  console.log('%s', url)
})()
The server code itself is written in JSX thanks to the ÀLaMode's require hook. The JSX's VNode tree is then transformed into HTML with preact-render-to-string.
require('alamode')()
require(`../${process.argv[2]}`)
The example can be started with yarn example/ command:
MacBook:form zavr$ yarn example/
yarn run v1.13.0
$ yarn e example/example
$ node example example/example
http://localhost:5000

Back To Top

Main And Module

Because the FrontEnd middleware serves data from the module directory, and Depack compiles files from there as well, the package exports the module field in its package.json file that points to the source code built with ÀLaMode to convert <jsx></jsx> into Preact's pragma executions: h('jsx', {}). The module containts import and export statements, therefore it needs to be used by the bundler and development middleware that can serve it. Depack passes all modules to the Google Closure Compiler and it is the preferred way to build for the modern web, since the require call should become obsolete. The following example shows what code is exported in the module field:
import { h } from 'preact'
import { Component } from 'preact'

export default class Form extends Component {
  constructor() {
    super()
    this.state = {
      values: {},
    }
    /**
     * @type {FormProps}
     */
    this.props = this.props
  }
  getChildContext() {
    return {
      values: this.state.values,
      onChange: this.onChange.bind(this),
    }
  }
  onChange(name, value) {
    this.setState({
      values: {
        ...this.state.values,
        [name]: value,
      },
    })
    if (this.props.onChange)
      this.props.onChange(this.state.values)
  }
  /**
   * @param {FormProps} props Options for the Form component.
 * @param {function} [props.onChange] The callback to call when a change is made to any of the inputs inside of the form.
 * @param {function} [props.formRef] The function to call with the reference to the form HTML.
 * @param {function} [props.onSubmit] The function to call on form submit.
   */
  render({ children, formRef, onSubmit, onChange, ...props }) {
    return    h('form',{...props,'ref':formRef, 'onSubmit':onSubmit},
      children,
    )
  }
}

/**
 * The div with `form-group` class to hold the label, input, help and validation message.
 */
export class FormGroup extends Component {
  constructor() {
    super()
    this.id = `i${Math.floor(Math.random() * 100000)}`
    this.hid = `h${this.id}`
    /**
     * @type {FormGroupProps}
     */
    this.props = this.props
  }
  getChildContext() {
    return {
      id: this.id,
      hid: this.hid,
    }
  }
  render() {
    const { children, label, help } = this.props
    return h('div',{'className':"form-group"},
      label && h('label',{'htmlFor':this.id},label),
      children,
      help && h('small',{'id':this.hid,'dangerouslySetInnerHTML':{ __html: help },'className':"form-text text-muted"}),
    )
  }
}

export { default as Select } from './Select'
export { default as TextArea } from './TextArea'
export { default as Input } from './Input'
export { default as SubmitForm } from './SubmitForm'

/**
 * The button with `type="submit"` which can be loading with a spinner indicator.
 * @param {SubmitButtonProps} props Options for the SubmitButton component.
 * @param {boolean} [props.loading=false] Whether the button should display as loading. Default `false`.
 * @param {string} [props.loadingText] The text to show during the loading progress.
 * @param {string} props.confirmText The text for the normal state.
 * @param {string} [props.className] The class name, such as `btn-lg`.
 * @param {('primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark')} [props.type="primary"] The type of the button to add to the class as `btn-{type}`. Default `primary`.
 * @param {boolean} [props.outline=false] Display the outline style of the button via setting the `btn-outline-{type}` class. Default `false`.
 */
export const SubmitButton = (props) => {
  const { loading, confirmText, loadingText = confirmText, className, type = 'primary', outline = false } = props
  const classes = ['btn', `btn-${outline ? 'outline-' : ''}${type}`, className].filter(Boolean)
  return (h('button',{ 'className':classes.join(' '),'type':"submit", 'disabled':loading},
    loading && h('span',{'className':`spinner-border spinner-border-sm${loadingText ? ' mr-2' : ''}`,'role':"status",'aria-hidden':"true"}),
    loading ? loadingText : confirmText,
  ))
}

/* documentary types/index.xml */
/**
 * @typedef {Object} FormProps Options for the Form component.
 * @prop {function} [onChange] The callback to call when a change is made to any of the inputs inside of the form.
 * @prop {function} [formRef] The function to call with the reference to the form HTML.
 * @prop {function} [onSubmit] The function to call on form submit.
 *
 * @typedef {Object} FormGroupProps Options for the FormGroup component.
 * @prop {string} [label] The label to display for the group.
 * @prop {string} [help] The help text to show in `<small className="form-text text-muted">{help}</small>`
 *
 * @typedef {Object} SubmitButtonProps Options for the SubmitButton component.
 * @prop {boolean} [loading=false] Whether the button should display as loading. Default `false`.
 * @prop {string} [loadingText] The text to show during the loading progress.
 * @prop {string} confirmText The text for the normal state.
 * @prop {string} [className] The class name, such as `btn-lg`.
 * @prop {('primary' | 'secondary' | 'success' | 'danger' | 'warning' | 'info' | 'light' | 'dark')} [type="primary"] The type of the button to add to the class as `btn-{type}`. Default `primary`.
 * @prop {boolean} [outline=false] Display the outline style of the button via setting the `btn-outline-{type}` class. Default `false`.
 */
The program as modules can be viewed on the following page. Modules Demo. The aim was to discover the use case for building JSX files into JS for serving as modules on CDNs like GitHub pages where the addition of .jsx extension is required for serving <script type="module"></script>.