Why Gemfury? Push, build, and install  RubyGems npm packages Python packages Maven artifacts PHP packages Go Modules Debian packages RPM packages NuGet packages

Repository URL to install this package:

Details    
@skava/ui / src / forms / input / InputState.tsx
Size: Mime:
import { FocusEvent } from 'react'
import { Serializable } from '@skava/typings'
import { toBoolean, isArray } from 'exotic'
import { extendObservable, action, observable, computed } from 'xmobx/mobx'
import { isValid, errorMessage } from '../deps'
import { FormStateType } from '../form/typings'
import { shouldRemap, unknown, hasType } from './deps'
import {
  InputStateType,
  InputStateMethodsType,
  InputValue,
  InputStringType,
} from './typings'
import { toggleTypes, validTypes, types } from './fixture'

/**
 * @todo use mobx actions
 * @todo - onValueChange, keyboard navigation, plugins...
 */
class InputState<Props = any> extends InputStateType<Props> {
  // @observable className = undefined
  // @observable value: InputValue = ''
  // @observable isEnabled = true
  // @observable isFocused = false
  // @observable isSelected = false
  // @observable type = 'text'
  // @observable elementList = []
  // @observable error: undefined | Error = undefined
  // @observable isValidInput = true
  @observable
  isVisible = true

  formState: FormStateType

  static init(data?: Object) {
    return new InputState(data)
  }
  static types = types
  isInputState: boolean = true

  constructor(stateData: Object = {}) {
    // shouldn't need this super call...
    super()

    /**
     * @todo !!!!!!!!!!!!!!! REMOVE - THIS MAKES EVERYTHING OBSERVABLE @@PERF
     */
    console.warn(
      '!!!!!!!!!!!!!!! REMOVE - THIS MAKES EVERYTHING OBSERVABLE @@PERF'
    )
    extendObservable(this, { ...types })

    // @note - this loops through to dynamically take all props
    // this is bad because it takes tooo many properties
    this.from(stateData)
  }

  /**
   * loop through keys, set props
   * @see chain.from
   */
  @action
  from(obj: Object = {}) {
    // we can replace a whole object
    // that replacement can be observable
    const dynamic = {}

    const set = (key: string, value: any) => {
      const should = shouldRemap(key)

      if (should === false) {
        unknown(key, value)
      }
      if (key === 'identifier') {
        this._identifier = value
        // this.identity = this.identity || value
        this.identity = value
        return
      }

      // regardless
      dynamic[key] = value
      // === this will not update ===
      this[key] = value
    }

    const onKey = key => {
      const value = obj[key]
      if (hasType(key) === true) {
        this[key] = value
      } else {
        set(key, value)
      }
    }

    Object.keys(obj).forEach(onKey)

    // update
    this.dynamicState = dynamic
    return this
  }

  /**
   * @todo @name setIsFocused
   */
  @action.bound
  updateFocused(event: FocusEvent<any>, instance) {
    if (!event || event.target.value === '') {
      this.isFocused = !this.isFocused
    }
  }

  /**
   * @note @todo @fixme - this is why we do actions to set the data
   */
  @action.bound
  validateInput() {
    this.setIsValidInput(isValid(this.value, this.validationType))
    this.errorMessage = errorMessage(this.errorMessageFor as string)
  }

  @action.bound
  setValue(value: InputValue) {
    console.log('setting_value', { selfValue: this.value, value, self: this })
    if (toggleTypes.includes(this.type as InputStringType)) {
      this.isSelected = toBoolean(value)
      return
    }
    this.value = value || ''
  }

  /**
   * @todo should be computed eh...
   */
  getValue = (): InputValue => {
    // @todo and radio, it should return value?
    if (toggleTypes.includes(this.type as InputStringType)) {
      return this.isSelected as boolean
    }
    return this.value
  }

  @action
  setValidationType(validationType: string) {
    this.validationType = validationType || ''
  }
  @action
  setType(type: string) {
    this.type = type
  }

  @action
  setIsValidInput(value: boolean) {
    this.isValidInput = value
  }
  // setEnabled
  disable = () => {
    this.isEnabled = false
  }
  enable = () => {
    this.isEnabled = true
  }
  // setValidity
  invalid = () => {
    this.isValid = false
  }
  // setValidity
  valid = () => {
    this.isValid = true
  }
  get identifier() {
    return this._identifier || this.identity || this.name || '@@empty'
  }
  select = () => {
    this.isSelected = true
  }
  unselect = () => {
    this.isSelected = false
  }

  @action.bound
  setIsSelected = (isSelected: boolean) => {
    this.isSelected = isSelected
  }

  @action
  setIsVisible(isVisible: boolean): void {
    this.isVisible = isVisible
  }

  /**
   * @description should not really be used - if ever used, as a last resort
   * @type {IAction}
   * @modifies this.input
   */
  setInputReference = (dom: HTMLInputElement) => {
    this.input = dom
  }

  /**
   * @description to put props into container
   */
  @action.bound
  setProps(props: Props) {
    console.log('input state setprops', props)
    this.props = props
  }

  // ========= protected read =========

  /**
   * @variation radio [name] for when it's a radio, because there is only one with the same name
   * @variation toggle/checkbox .isSelected
   * @variation text/textarea/other .value
   */
  toJSON(): Serializable {
    const key = this.name || this.identity
    const serialized = {
      [key]: this.value || this.isSelected,
    }

    /**
     * @todo @fixme bad serialization - fixed in ui-component-library
     */
    if (serialized['0'] === key) {
      delete serialized['0']
    }

    /**
     * @todo @@perf put in in env, remove, use new form flow
     */
    if (isArray(this.elementList) && this.elementList.length > 0) {
      const serializedChildren =
        isArray(this.elementList) &&
        this.elementList.length > 0 &&
        JSON.stringify(this.elementList)
      console.warn('can serialize children !!! ', serializedChildren)
    }

    return serialized
  }

  toString() {
    return JSON.stringify(this.toJSON(), undefined, 2)
  }
}

export { InputState }
export default InputState