diff --git a/packages/core/src/components/vector-range/index.jsx b/packages/core/src/components/vector-range/index.jsx new file mode 100644 index 0000000..c93c932 --- /dev/null +++ b/packages/core/src/components/vector-range/index.jsx @@ -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 ( +
+ { + onChange?.( + isSingle + ? value + : { + min: value, + max: range.max, + }, + ); + }} + vector={isSingle ? range : range.min} + /> + + {!isSingle && ( + { + onChange?.({ + min: range.min, + max: value, + }); + }} + vector={range.max} + /> + )} +
+ ); +}; + +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; diff --git a/packages/core/src/components/vector-range/index.scss b/packages/core/src/components/vector-range/index.scss new file mode 100644 index 0000000..a774dc9 --- /dev/null +++ b/packages/core/src/components/vector-range/index.scss @@ -0,0 +1,11 @@ +.vector-range { + display: flex; +} + +.vector-range__to-label { + align-self: center; +} + +.vector-range input[type="number"] { + margin-left: 1em; +} diff --git a/packages/core/src/components/vector/index.jsx b/packages/core/src/components/vector/index.jsx new file mode 100644 index 0000000..5452d51 --- /dev/null +++ b/packages/core/src/components/vector/index.jsx @@ -0,0 +1,74 @@ +import './index.scss'; + +import {PropTypes, React} from '@latus/react'; +import Number from '../number'; + +const Vector = ({ + integer, + labels, + onChange, + vector, +}) => ( +
+ + +
+); + +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; diff --git a/packages/core/src/components/vector/index.scss b/packages/core/src/components/vector/index.scss new file mode 100644 index 0000000..56084b0 --- /dev/null +++ b/packages/core/src/components/vector/index.scss @@ -0,0 +1,3 @@ +.vector { + display: flex; +} diff --git a/packages/core/src/index.js b/packages/core/src/index.js index 7d165d8..8f2a298 100644 --- a/packages/core/src/index.js +++ b/packages/core/src/index.js @@ -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';