networknt / react-schema-form

react form based on json schema for form generation and validation
MIT License
358 stars 106 forks source link

Is it possible to add custom format? #178

Open lukaszrys opened 4 years ago

lukaszrys commented 4 years ago

The original plugin (http://schemaform.io/examples/bootstrap-example.html) has the Color pallette in "Kitchen Sink - More stuff").

Unfortunately, I do not see this one here. Is it possible to create it?

stevehu commented 4 years ago

It looks pretty cool and I think it is very easy to implement it. The look and feel might be a little different since we are using the material-ui instead of bootstrap. There might be other material-ui components available thought. Would you like to try it out and submit a PR?

https://material-ui.com/customization/color/

naeemba commented 4 years ago

@lukaszrys I think you can add any component to your list of available components, the SchemaForm component that you can import it using import { SchemaForm } from 'react-schema-form' accepts a property named mapper, the mapper is an object which can be like this

const mapper = {
    'my-own-color-picker': YourColorPicker,
    .... other components that you want to use in your forms
}

In your component you can debug the probs, they are straight forward, also it may help to use our color picker: if you are implementing your color picker, consider the ComposedComponent which may be required, also utils can be totally helpful during the implementation.

// @flow
import React from 'react';
import { CirclePicker, ChromePicker } from 'react-color';
import Icon from '@material-ui/core/Icon';
import Dialog from 'packages/common/web/dialog';
import Typography from '@material-ui/core/Typography';
import { withStyles } from '@material-ui/core/styles';
import Collapse from '@material-ui/core/Collapse';
import List from '@material-ui/core/List';
import ListItem from '@material-ui/core/ListItem';
import ListItemIcon from '@material-ui/core/ListItemIcon';
import ListItemText from '@material-ui/core/ListItemText';
import IconButton from '@material-ui/core/IconButton';
import ColorizeIcon from '@material-ui/icons/Colorize';
import ExpandLess from '@material-ui/icons/ExpandLess';
import ExpandMore from '@material-ui/icons/ExpandMore';
import type { SchemaFormLocalization } from 'packages/common/shared/types';
import { ComposedComponent, utils } from 'react-schema-form';
import { colorDialogStyles as styles } from './styles';

type Props = {
  form: {
    className: string,
    key: string,
    readonly: boolean,
    title: string,
    required: boolean,
    style: any,
  },
  onChange: (key: string, value: string) => void,
  model: any,
  classes: any,
  localization: SchemaFormLocalization,
};

type State = {
  open: boolean,
  color: string,
  selectedColor: string,
  disabled: boolean,
  colorPickerOpen: boolean,
};

class ColorSelect extends React.Component<Props, State> {
  state = {
    open: false,
    color: '',
    disabled: true,
    selectedColor: '',
    colorPickerOpen: false,
  };

  static getDerivedStateFromProps(nextProps: Props, prevState: State) {
    if (prevState.color == null || prevState.color === '') {
      return {
        color: '',
      };
    }

    return null;
  }

  componentDidMount() {
    this.setColor();
  }

  setColor = () => {
    const { form, model } = this.props;
    const color = utils.selectOrSet(form.key, model);
    this.setState({
      color: color || '',
      selectedColor: color || '',
    });
  };

  open = () => {
    this.setState({ open: true });
  };

  handleClose = () => {
    this.setState({ open: false });
  };

  onChangeColor = color => {
    this.setState({ selectedColor: color.hex, disabled: false });
  };

  submit = () => {
    const { selectedColor } = this.state;
    const {
      form: { key },
      onChange,
    } = this.props;
    onChange(key, selectedColor);
    this.setState({ color: selectedColor, open: false, disabled: true });
  };

  colorPickerOpenHandle = () => {
    this.setState(state => ({ colorPickerOpen: !state.colorPickerOpen }));
  };

  render() {
    const {
      classes,
      form: { title, required, style },
      localization: { getLocalizedString },
    } = this.props;

    const label = getLocalizedString(title) + (required ? ' *' : '');

    const {
      disabled,
      selectedColor,
      colorPickerOpen,
      color,
      open,
    } = this.state;

    const colors = [
      '#D50000',
      '#F44336',
      '#e81d62',
      '#9C27B0',
      '#7341f2',
      '#3F51B5',
      '#2196F3',
      '#03A9F4',
      '#00BCD4',
      '#009688',
      '#4CAF50',
      '#8ac249',
      '#CDDC39',
      '#FFEB3B',
      '#FFC107',
      '#fe9700',
      '#fe5621',
      '#795548',
      '#9E9E9E',
      '#607D8B',
    ];

    return (
      <div style={style}>
        <div className={classes.div}>
          <IconButton onClick={this.open}>
            <Icon style={{ color }}>
              {selectedColor ? 'lens' : 'panorama_fish_eye'}
            </Icon>
          </IconButton>
          <Typography component="span" className={classes.title}>
            {label}
          </Typography>
        </div>
        <Dialog
          title={getLocalizedString('form.select.color')}
          open={open}
          onClose={this.handleClose}
          classes={{
            paper: classes.colorDialog,
            content: classes.colorDialogContent,
          }}
          actions={[
            {
              label: getLocalizedString('clear'),
              onClick: () => {
                this.setState(
                  {
                    selectedColor: '',
                  },
                  this.submit
                );
                this.handleClose();
              },
            },
            {
              label: getLocalizedString('submit'),
              type: 'primary',
              onClick: this.submit,
              disabled,
            },
          ]}
        >
          <List>
            <ListItem>
              <CirclePicker
                width="185px"
                circleSize={23}
                color={selectedColor}
                colors={colors}
                onChange={this.onChangeColor}
              />
            </ListItem>
            <ListItem button onClick={this.colorPickerOpenHandle}>
              <ListItemIcon className={classes.colorizeIcon}>
                <ColorizeIcon />
              </ListItemIcon>
              <ListItemText primary="Color Picker" />
              {colorPickerOpen ? <ExpandLess /> : <ExpandMore />}
            </ListItem>
            <Collapse in={colorPickerOpen} timeout="auto" unmountOnExit>
              <ChromePicker
                color={selectedColor}
                onChange={this.onChangeColor}
              />
            </Collapse>
          </List>
        </Dialog>
      </div>
    );
  }
}

export default withStyles(styles)(ComposedComponent(ColorSelect));

At the end you can use this with your form object which you should pass to SchemaForm like this:

{ key: 'color', type: 'my-own-color-picker'}