diff --git a/src/index.js b/src/index.js index b273e735..d5940de9 100644 --- a/src/index.js +++ b/src/index.js @@ -25,6 +25,7 @@ import TextFieldComponent from './text-field' import WidgetStatsComponent from './widget-stats' // export { default as WidgetNumber } from './widget-number' // deprecated by WidgetStats // export { default as WidgetTrend } from './widget-trend' // deprecated by WidgetStats +import SelectionGroupComponent from './selection-group' const wrapTheme = (WrappedComponent, theme = defaultTheme) => { class Wrapper extends React.PureComponent { @@ -57,6 +58,7 @@ const StyledSwitch = wrapTheme(StyledSwitchComponent) const TabPanels = wrapTheme(TabPanelsComponent) const TextField = wrapTheme(TextFieldComponent) const WidgetStats = wrapTheme(WidgetStatsComponent) +const SelectionGroup = wrapTheme(SelectionGroupComponent) export { Alert, @@ -77,4 +79,5 @@ export { TabPanels, TextField, WidgetStats, + SelectionGroup, } diff --git a/src/selection-group/checkbox-group.js b/src/selection-group/checkbox-group.js new file mode 100644 index 00000000..ef5eaf95 --- /dev/null +++ b/src/selection-group/checkbox-group.js @@ -0,0 +1,41 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import FormGroup from '@material-ui/core/FormGroup' +import FormControlLabel from '@material-ui/core/FormControlLabel' +import Grid from '@material-ui/core/Grid' + +import StyledCheckbox from '../styled-checkbox' + + +const CheckboxGroup = ({ filterVals, checkboxOnChange, direction }) => ( + + + {filterVals.map((val) => { + const [optionName, optionState] = Object.entries(val)[0] + return ( +
+ + )} + label={optionName} + /> +
+ ) + })} +
+
+) + +CheckboxGroup.propTypes = { + filterVals: PropTypes.array.isRequired, + checkboxOnChange: PropTypes.func.isRequired, + direction: PropTypes.string.isRequired, +} + +export default CheckboxGroup diff --git a/src/selection-group/index.js b/src/selection-group/index.js new file mode 100644 index 00000000..9841cc4d --- /dev/null +++ b/src/selection-group/index.js @@ -0,0 +1,128 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' + +import { makeStyles } from '@material-ui/core/styles' +import Grid from '@material-ui/core/Grid' +import FormControl from '@material-ui/core/FormControl' +import Paper from '@material-ui/core/Paper' +import Typography from '@material-ui/core/Typography' + +import DynamicButton from '../dynamic-button' +import CheckboxGroup from './checkbox-group' +import RadioGroup from './radio-group' +import SwitchGroup from './switch-group' +import SliderGroup from './slider-group' + + +const useStyles = makeStyles(() => ({ + paper: { paddingBottom: '10px' }, + formControl: { margin: '15px 15px 5px 15px' }, + select: { paddingLeft: '15px' }, +})) + +const SelectionGroup = ({ + type, + options, + optionsLabel, + onChange, + direction, + hasSelectAll, + sliderStep, +}) => { + const classes = useStyles() + const [filterVals, setFilterVals] = useState(options) + const [selectAll, setSelectAll] = useState(true) + + const checkboxOnChange = (e) => { + const newFilterVals = filterVals.map((v) => { + const [ filterName ] = Object.keys(v) + if (filterName === e.target.name) { + return { [e.target.name]: e.target.checked } + } + return v + }) + const optionsState = newFilterVals.filter((v) => !Object.values(v)[0]) + if (optionsState.length === 0) setSelectAll(!selectAll) + if (optionsState.length > 0 && !selectAll) setSelectAll(!selectAll) + setFilterVals(newFilterVals) + + const selectedVals = newFilterVals.reduce((arr, val) => { + const [ name, selected ] = Object.entries(val)[0] + if (selected) arr.push(name) + return arr + }, []) + onChange(newFilterVals, selectedVals) + } + + const selectAllOnClick = () => { + const newFilterVals = filterVals.reduce((arr, opt) => { + const [ name ] = Object.keys(opt) + arr.push({ [name]: selectAll }) + return arr + }, []) + setFilterVals(newFilterVals) + setSelectAll(!selectAll) + + const selectedVals = newFilterVals.reduce((arr, val) => { + const [ name, selected ] = Object.entries(val)[0] + if (selected) arr.push(name) + return arr + }, []) + onChange(newFilterVals, selectedVals) + } + + return ( + + + + {optionsLabel} + {type === 'checkbox' && ( + + )} + {type === 'radio' && ()} + {type === 'switch' && ( + + )} + {type === 'slider' && ( + + )} + + {hasSelectAll && + type !== 'radio' && + type !== 'slider' && +
+ + {selectAll ? 'Select All' : 'Reset'} + +
} +
+
+ ) +} + +SelectionGroup.propTypes = { + options: PropTypes.array.isRequired, + type: PropTypes.string.isRequired, + optionsLabel: PropTypes.string, + hasSelectAll: PropTypes.bool, + onChange: PropTypes.func, + direction: PropTypes.string, + sliderStep: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), +} + +SelectionGroup.defaultProps = { + hasSelectAll: false, + onChange: () => {}, + direction: 'column', + optionsLabel: '', + sliderStep: 1, +} + +export default SelectionGroup diff --git a/src/selection-group/radio-group.js b/src/selection-group/radio-group.js new file mode 100644 index 00000000..6154cbe0 --- /dev/null +++ b/src/selection-group/radio-group.js @@ -0,0 +1,42 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' + +import MUIRadioGroup from '@material-ui/core/RadioGroup' +import FormControlLabel from '@material-ui/core/FormControlLabel' +import Grid from '@material-ui/core/Grid' + +import StyledRadio from '../styled-radio' + + +const RadioGroup = ({ options, onChange, direction }) => { + const [val, setVal] = useState(options[0]) + + const radioOnChange = (e) => { + setVal(e.target.value) + onChange(e.target.value) + } + + return ( + + + {options.map((o, i) => ( +
+ )} + label={o} + /> +
+ ))} +
+
+ ) +} + +RadioGroup.propTypes = { + options: PropTypes.array.isRequired, + onChange: PropTypes.func.isRequired, + direction: PropTypes.string.isRequired, +} + +export default RadioGroup diff --git a/src/selection-group/slider-group.js b/src/selection-group/slider-group.js new file mode 100644 index 00000000..655f60e4 --- /dev/null +++ b/src/selection-group/slider-group.js @@ -0,0 +1,106 @@ +import React, { useState } from 'react' +import PropTypes from 'prop-types' + +import { makeStyles } from '@material-ui/core/styles' +import Grid from '@material-ui/core/Grid' +import Slider from '@material-ui/core/Slider' +import FormControl from '@material-ui/core/FormControl' +import Input from '@material-ui/core/Input' + + +const useStyles = makeStyles(() => ({ + sliderInput: { + boxSizing: 'border-box', + marginRight: '4px', + padding: '4px', + border: '1px solid', + borderRadius: '4px', + }, + verHeight: { height: '300px' }, +})) +const SliderGroup = ({ sliderStep, options, direction, onChange }) => { + const classes = useStyles() + const orientation = direction === 'column' ? 'vertical' : 'horizontal' + const gridContainerProps = direction === 'column' + ? { container: true, direction: 'row-reverse', justify: 'center', alignItems:'center' } + : {} + const gridSliderProps = direction === 'column' ? { item: true, xs: 6 } : {} + const gridInputProps = direction === 'column' + ? { + item: true, + container: true, + direction: 'column-reverse', + alignItems: 'center', + justify: 'space-between', + } + : {} + + let [min, max, initialMin = '', initialMax = ''] = options + if (!initialMin && initialMin !== 0) initialMin = min + if (!initialMax) initialMax = max + + const [val, setVal] = useState([initialMin, initialMax]) + const [currentMin, currentMax] = val + + const sliderOnChange = (_, val) => { + setVal(val) + onChange(val) + } + + const minOnChange = (e) => setVal([e.target.value, currentMax]) + const maxOnChange = (e) => setVal([currentMin, e.target.value]) + + return ( + + + + + + + + + + + + + + + + ) +} + +SliderGroup.propTypes = { + onChange: PropTypes.func.isRequired, + options: PropTypes.array.isRequired, + sliderStep: PropTypes.oneOfType([ + PropTypes.number, + PropTypes.string, + ]), + direction: PropTypes.string, +} + +SliderGroup.defaultProps = { + direction: 'row', +} + +export default SliderGroup diff --git a/src/selection-group/switch-group.js b/src/selection-group/switch-group.js new file mode 100644 index 00000000..4de0c32e --- /dev/null +++ b/src/selection-group/switch-group.js @@ -0,0 +1,41 @@ +import React from 'react' +import PropTypes from 'prop-types' + +import FormGroup from '@material-ui/core/FormGroup' +import FormControlLabel from '@material-ui/core/FormControlLabel' +import Grid from '@material-ui/core/Grid' +import Switch from '@material-ui/core/Switch' + + +const SwitchGroup = ({ filterVals, switchOnChange, direction }) => ( + + + {filterVals.map((val) => { + const [optionName, optionState] = Object.entries(val)[0] + return ( +
+ + )} + label={optionName} + /> +
+ ) + })} +
+
+) + +SwitchGroup.propTypes = { + filterVals: PropTypes.array.isRequired, + switchOnChange: PropTypes.func.isRequired, + direction: PropTypes.string.isRequired, +} + +export default SwitchGroup diff --git a/stories/selection-group.stories.js b/stories/selection-group.stories.js new file mode 100644 index 00000000..bcde24fe --- /dev/null +++ b/stories/selection-group.stories.js @@ -0,0 +1,110 @@ +import React from 'react' +import { SelectionGroup } from '../src' + + +export default { + component: SelectionGroup, + title: 'SelectionGroup', +} + +export const Radio = () => { + const options = ['hello', 'there', 'hi', 'bye'] + const radioOnChange = (newVal) => console.log('newVal: ', newVal) + + return ( + <> + + + + ) +} + +export const Checkbox = () => { + const options = [ { hello: false }, { there: false }, { hi: false }, { bye: false } ] + const checkboxOnChange = (newFilterVals, selectedVals) => { + console.log('newFilters: ', newFilterVals) + console.log('selectedVals: ', selectedVals) + } + return ( + <> + + + + ) +} + +export const Switch = () => { + const options = [ { hello: false }, { there: false }, { hi: false }, { bye: false } ] + const switchOnChange = (newFilterVals, selectedVals) => { + console.log('newFilters: ', newFilterVals) + console.log('selectedVals: ', selectedVals) + } + return ( + <> + + + + ) +} + +export const Slider = () => { + // options format = [min, max, initialMin, initialMax] --> last 2 is optional + const options = [0, 1000, 500, 1000] + const sliderOnChange = (val) => console.log('val: ', val) + + return ( + <> + + + + ) +}