huytrongnguyen / rosie

MIT License
0 stars 0 forks source link

Add EditorField #8

Open huytrongnguyen opened 6 months ago

huytrongnguyen commented 6 months ago
import { HTMLAttributes, useEffect, useState } from 'react';
import { DataModel, Rosie } from 'rosie';

export interface EditorFieldProps extends HTMLAttributes<HTMLDivElement> {
  type?: string,
  rows?: number,
  record: DataModel<any>,
  field: string,
  label: string,
  labelSeparator?: string,
  labelAlign?: 'left' | 'top',
  labelCol?: number,
  labelCls?: string,
}

export function EditorField(props: EditorFieldProps) {
  const { type = 'text', rows = 3, record, field, label, labelSeparator = ':', labelAlign = 'left', labelCol = 6, labelCls, className, ...others } = props,
        [readOnly, setReadOnly] = useState(true),
        [fieldValue, setFieldValue] = useState<any>('');

  useEffect(() => {
    const record$ = props.record.subscribe(value => setFieldValue(value[field]));
    return () => { record$.unsubscribe(); }
  }, [])

  function updateValue() {
    record.set(field, fieldValue);
    setReadOnly(true);
  }

  if (!readOnly && type === 'textarea') {
    if (labelAlign === 'top') {
      return <div className={Rosie.classNames(className)} {...others}>
        <label className={Rosie.classNames('form-label fw-bold', labelCls)}>{label}</label>
        <textarea autoFocus rows={rows} className="form-control form-control-sm p-2" onBlur={() => { updateValue() }}
            value={fieldValue} onChange={e => setFieldValue(e.target.value)} />
      </div>
    } else {
      return <></>
    }
  }

  if (!readOnly && type === 'text') {
    if (labelAlign === 'top') {
      return <></>
    } else {
      return <div className={Rosie.classNames('row', className)} {...others}>
        <label className={Rosie.classNames(`col-${labelCol} col-form-label fw-bold`, labelCls)}>{label}{labelSeparator}</label>
        <div className={`col-${Rosie.GRID_COLUMNS - labelCol}`}>
          <input type="text" autoFocus className="form-control form-control-sm" onBlur={() => { updateValue() }}
              value={fieldValue} onChange={e => setFieldValue(e.target.value)} />
        </div>
      </div>
    }
  }

  if (labelAlign === 'top') {
    return <div className={Rosie.classNames(className)} {...others}>
      <label className={Rosie.classNames('form-label fw-bold', labelCls)}>{label}</label>
      <div className="input-group input-group-sm">
        <div className={Rosie.classNames('form-control form-control-sm p-2', {'bg-warning-subtle': record.isModified(field)})}>
          {fieldValue}
        </div>
        <span role="button" className="input-group-text" onClick={() => { setReadOnly(false) }}>
          <span className="fa fa-pencil" />
        </span>
      </div>
    </div>
  } else {
    return <div className={Rosie.classNames('row', className)} {...others}>
      <label className={Rosie.classNames(`col-${labelCol} col-form-label fw-bold`, labelCls)}>{label}{labelSeparator}</label>
      <div className={`col-${Rosie.GRID_COLUMNS - labelCol} py-1`}>
        <div className="input-group input-group-sm" style={{height:'28px'}}>
          <div className={Rosie.classNames('form-control', {'bg-warning-subtle': record.isModified(field)})}>
            {fieldValue}
          </div>
          <span role="button" className="input-group-text" onClick={() => { setReadOnly(false) }}>
            <span className="fa fa-pencil" />
          </span>
        </div>
      </div>
    </div>
  }
}