feat: Vector & VectorRange

This commit is contained in:
cha0s 2021-01-26 12:31:57 -06:00
parent a7a74a0775
commit b4a879d573
5 changed files with 194 additions and 0 deletions

View File

@ -0,0 +1,104 @@
import './index.scss';
import {
PropTypes,
React,
useState,
} from '@latus/react';
import Vector from '../vector';
const VectorRange = ({
integer,
onChange,
range,
}) => {
const [isSingle, setIsSingle] = useState(Array.isArray(range));
return (
<div className="vector-range">
<Vector
integer={integer}
onChange={(value) => {
onChange?.(
isSingle
? value
: {
min: value,
max: range.max,
},
);
}}
vector={isSingle ? range : range.min}
/>
<label className="vector-range__to-label">
to
{isSingle && '?'}
<input
type="checkbox"
checked={!isSingle}
onChange={(event) => {
setIsSingle(!event.target.checked);
onChange?.(
!event.target.checked
? range.min
: {
min: range,
max: range,
},
);
}}
/>
</label>
{!isSingle && (
<Vector
integer={integer}
onChange={(value) => {
onChange?.({
min: range.min,
max: value,
});
}}
vector={range.max}
/>
)}
</div>
);
};
const createVectorRangePropType = (isRequired) => (props, propName, componentName) => {
const fail = (why) => new Error(
`Invalid prop ${propName} suppied to ${componentName}. ${why}.`,
);
const range = props[propName];
if (!range && isRequired) {
return fail('Is required but missing');
}
if (
(
'undefined' === typeof range.min
|| 'undefined' === typeof range.max
)
&& (
!Array.isArray(range)
|| 2 !== range.length
)
) {
return fail('Expected {min, max} or a Vector');
}
return undefined;
};
export const vectorRangePropType = createVectorRangePropType(false);
vectorRangePropType.isRequired = createVectorRangePropType(true);
VectorRange.defaultProps = {
integer: false,
onChange: null,
};
VectorRange.propTypes = {
integer: PropTypes.bool,
onChange: PropTypes.func,
range: vectorRangePropType.isRequired,
};
export default VectorRange;

View File

@ -0,0 +1,11 @@
.vector-range {
display: flex;
}
.vector-range__to-label {
align-self: center;
}
.vector-range input[type="number"] {
margin-left: 1em;
}

View File

@ -0,0 +1,74 @@
import './index.scss';
import {PropTypes, React} from '@latus/react';
import Number from '../number';
const Vector = ({
integer,
labels,
onChange,
vector,
}) => (
<div className="vector">
<label>
{labels[0]}
<Number
integer={integer}
onChange={(value) => {
onChange?.([
value,
vector[1],
]);
}}
value={vector[0]}
/>
</label>
<label>
{labels[1]}
<Number
integer={integer}
onChange={(value) => {
onChange?.([
vector[0],
value,
]);
}}
value={vector[1]}
/>
</label>
</div>
);
const createVectorPropType = (isRequired) => (props, propName, componentName) => {
const fail = (why) => new Error(
`Invalid prop ${propName} suppied to ${componentName}. ${why}.`,
);
const vector = props[propName];
if (!vector && isRequired) {
return fail('Is required but missing');
}
if (
!Array.isArray(vector)
|| 2 !== vector.length
) {
return fail('Expected 2-length array vector');
}
return undefined;
};
export const vectorPropType = createVectorPropType(false);
vectorPropType.isRequired = createVectorPropType(true);
Vector.defaultProps = {
integer: false,
onChange: null,
labels: ['X', 'Y'],
};
Vector.propTypes = {
integer: PropTypes.bool,
onChange: PropTypes.func,
labels: PropTypes.arrayOf(PropTypes.string),
vector: vectorPropType.isRequired,
};
export default Vector;

View File

@ -0,0 +1,3 @@
.vector {
display: flex;
}

View File

@ -2,6 +2,8 @@ import {projects, user} from './state';
export {default as Number} from './components/number';
export {default as Range, rangePropType} from './components/range';
export {default as Vector, vectorPropType} from './components/vector';
export {default as VectorRange, vectorRangePropType} from './components/vector-range';
export {default as ProjectContext} from './context/project';
export {default as UriContext} from './context/uri';
export {default as useProject} from './hooks/use-project';