dbl-components
Version:
Framework based on bootstrap 5
220 lines (195 loc) • 6.23 kB
JavaScript
import React from "react";
import moment from "moment";
import { resolveRefs, eventHandler } from "dbl-utils";
import JsonRender from "../../json-render";
import Field from "./field";
const schemaInput = {
control: {
name: ['$definitions/name', 'wrapControl'],
tag: 'div',
classes: 'input-group',
style: {
flexWrap: 'nowrap'
},
content: {
from: {
name: ['$definitions/name', 'fromControl'],
wrapper: false,
component: 'NoWrapField',
controlClasses: 'border-end-0',
type: '$definitions/type',
style: {
minWidth: 50
}
},
divisor: {
name: ['$definitions/name', 'divisor'],
tag: 'span',
classes: 'input-group-text border-start-0 border-end-0',
content: '$definitions/divisor'
},
to: {
name: ['$definitions/name', 'toControl'],
wrapper: false,
component: 'NoWrapField',
controlClasses: 'border-start-0',
type: '$definitions/type',
style: {
minWidth: 50
}
}
}
},
definitions: {
}
};
export default class RangeField extends Field {
static jsClass = 'RangeField';
static defaultProps = {
...Field.defaultProps,
type: 'number',
default: [],
from: {},
to: {},
divisor: '-',
controlClasses: []
}
events = [];
constructor(props) {
super(props);
this.mutations = this.mutations.bind(this);
this.jsonRender = new JsonRender(props, this.mutations);
this.schemaInput = resolveRefs(schemaInput.control, {
definitions: {
...schemaInput.definitions,
name: props.name,
type: props.type,
divisor: props.divisor
}
});
this.events.push(
[`${this.props.name}-fromControl`, this.onValuesChange.bind(this)],
[`${this.props.name}-toControl`, this.onValuesChange.bind(this)],
[`invalid.${this.props.name}-fromControl`, this.onAnyInvalid.bind(this)],
[`invalid.${this.props.name}-toControl`, this.onAnyInvalid.bind(this)],
[`focus.${this.props.name}-fromControl`, this.onAnyFocus.bind(this)],
[`focus.${this.props.name}-toControl`, this.onAnyFocus.bind(this)],
);
}
componentDidMount() {
super.componentDidMount();
this.events.forEach(evt => eventHandler.subscribe(...evt));
}
componentWillUnmount() {
super.componentWillUnmount();
this.events.forEach(([evtName]) => eventHandler.unsubscribe(evtName));
}
onUpdate(...args) {
super.onUpdate(...args);
const [{ value }] = args;
if (value === undefined) return;
const [from, to] = value === null || value === '' ? [null, null] : value;
this.setState({
min: from,
max: to
});
eventHandler.dispatch(`update.${this.props.name}-fromControl`, { value: from });
eventHandler.dispatch(`update.${this.props.name}-toControl`, { value: to });
}
get inputNode() {
return this.jsonRender.buildContent(this.schemaInput, 0);
}
onValuesChange(data) {
const newState = {};
if (data[`${this.props.name}-fromControl`]) {
let min = data[`${this.props.name}-fromControl`];
if ((this.props.type || this.type) === 'number') min = Number(min);
newState.min = min;
}
if (data[`${this.props.name}-toControl`]) {
let max = data[`${this.props.name}-toControl`];
if ((this.props.type || this.type) === 'number') max = Number(max);
newState.max = max;
}
this.setState(newState,
() => this.onChange({
target: {
value: [this.state.min, this.state.max]
}
})
);
}
isInvalid(value) {
const [min, max] = value;
let isInvalid = true;
switch (this.props.type) {
case 'date':
case 'time':
isInvalid = !(max && min) || moment(min).isAfter(max);
break;
default:
isInvalid = !(typeof max === 'number' && typeof min === 'number') || (min > max);
break;
}
return isInvalid;
}
onAnyInvalid() {
this.onInvalid();
}
onAnyFocus() {
this.onFocus();
}
mutations(name, section) {
switch (name) {
case `${this.props.name}-fromControl`:
case `${this.props.name}-toControl`: {
const {
autoComplete, list, pattern,
required, type, disabled,
min, max, step, noValidate,
readOnly, dir, accept,
multiple, maxLength, minLength
} = this.props;
const { value: v } = this.state;
const ival = name.endsWith('fromControl') ? 0 : 1;
const minMax = name.endsWith('fromControl') ? 'max' : 'min';
const controlClasses = new Set([this.props.controlClasses].flat().map(c => v.split(' ')).flat());
let addDelete;
if (readOnly) addDelete = 'add';
else addDelete = 'delete';
controlClasses[addDelete]('form-control-plaintext');
return Object.assign(
{
autoComplete, list, pattern,
required, type, disabled,
min, max, step, noValidate,
readOnly, dir, accept,
multiple, maxLength, minLength,
value: v[ival]
},
name.endsWith('fromControl') ? this.props.from : this.props.to,
{
type: this.props.type,
[minMax]: this.state[minMax],
controlClasses: Array.from(controlClasses).flat().join(' ')
});
}
case `${this.props.name}-divisor`: {
const classes = new Set(section.classes.split(' '));
let addDelete;
if (this.props.readOnly) addDelete = 'add';
else addDelete = 'delete';
classes[addDelete]('border-0');
return {
classes: Array.from(classes).flat().join(' '),
style: {
backgroundColor: this.props.disabled ? 'var(--bs-secondary-bg)' : 'var(--bs-transparent-bg)'
},
content: this.props.divisor,
}
}
default:
break;
}
}
}