github.com/resonatecoop/id@v1.1.0-43/frontend/src/components/forms/generic.js (about)

     1  const html = require('choo/html')
     2  const Component = require('choo/component')
     3  const icon = require('@resonate/icon-element')
     4  const input = require('@resonate/input-element')
     5  
     6  class Form extends Component {
     7    constructor (id, state, emit) {
     8      super(id)
     9  
    10      this.emit = emit
    11      this.state = state
    12  
    13      this.local = state.components[id] = {}
    14      this.local.submitted = false
    15    }
    16  
    17    createElement (props) {
    18      this.form = props.form
    19      this.validate = props.validate
    20      this.submit = props.submit
    21  
    22      this.local.fields = props.fields || []
    23      this.local.altButton = props.altButton
    24      this.local.buttonText = props.buttonText || ''
    25      this.local.id = props.id
    26      this.local.action = props.action
    27      this.local.method = props.method || 'POST'
    28  
    29      const pristine = this.form.pristine
    30      const errors = this.form.errors
    31      const values = this.form.values
    32  
    33      const inputs = this.local.fields.map(fieldProps => {
    34        if (fieldProps.component) return fieldProps.component
    35  
    36        const { name = fieldProps.type, help, component } = fieldProps
    37  
    38        fieldProps.onInput = typeof fieldProps.onInput === 'function'
    39          ? fieldProps.onInput.bind(this)
    40          : null
    41  
    42        const element = component || input(Object.assign({}, fieldProps, {
    43          onchange: (e) => {
    44            this.validate({
    45              name: e.target.name,
    46              value: e.target.value
    47            })
    48            this.rerender()
    49          },
    50          value: values[name]
    51        }))
    52  
    53        return html`
    54          <div class="mb3">
    55            ${fieldProps.label
    56              ? html`
    57                  <label for=${fieldProps.id || name} class="f5 db mb1 dark-gray">
    58                    ${fieldProps.label}
    59                  </label>
    60                `
    61              : ''
    62            }
    63            <div class="relative">
    64              ${element}
    65              ${errors[name] && !pristine[name]
    66                ? html`
    67                  <div class="absolute left-0 ph1 flex items-center" style="top:50%;transform: translate(-100%, -50%);">
    68                    ${icon('info', { class: 'fill-red', size: 'sm' })}
    69                  </div>
    70                `
    71                : ''
    72              }
    73            </div>
    74            ${typeof help === 'function' ? help(values[name]) : help}
    75            ${errors[name] && !pristine[name]
    76              ? html`<span class="message f5 pb2">${errors[name].message}</span>`
    77              : ''
    78            }
    79          </div>
    80        `
    81      })
    82  
    83      // form attributes
    84      const attrs = {
    85        novalidate: 'novalidate',
    86        class: 'flex flex-column flex-auto',
    87        id: this.local.id,
    88        action: this.local.action,
    89        method: this.local.method,
    90        onsubmit: (e) => {
    91          e.preventDefault()
    92  
    93          for (const field of e.target.elements) {
    94            const isRequired = field.required
    95            const name = field.name || ''
    96            const value = field.value || ''
    97            if (isRequired) this.validate({ name, value })
    98          }
    99  
   100          if (this.form.valid) {
   101            this.submit(e.target)
   102          }
   103  
   104          this.rerender()
   105        }
   106      }
   107  
   108      const submitButton = (props = {}) => {
   109        const attrs = Object.assign({
   110          disabled: false,
   111          class: `bg-white dib bn pv3 ph5 flex-shrink-0 f5 ${props.disabled ? 'o-50' : 'grow'}`,
   112          style: 'outline:solid 1px var(--near-black);outline-offset:-1px',
   113          type: 'submit'
   114        }, props)
   115  
   116        return html`<button ${attrs}>${this.local.buttonText}</button>`
   117      }
   118  
   119      return html`
   120        <div class="flex flex-column flex-auto">
   121          <form ${attrs}>
   122            ${inputs}
   123            ${this.local.altButton
   124              ? html`
   125                <div class="flex mt3">
   126                  <div class="flex mr3">
   127                    ${this.local.altButton}
   128                  </div>
   129                  <div class="flex flex-auto justify-end">
   130                    ${submitButton({ disabled: this.local.submitted })}
   131                  </div>
   132                </div>`
   133              : html`
   134                <div class="flex flex-auto">
   135                  ${submitButton({ disabled: this.local.submitted })}
   136                </div>`
   137              }
   138          </form>
   139        </div>
   140      `
   141    }
   142  
   143    update () {
   144      return false
   145    }
   146  }
   147  
   148  module.exports = Form