Fixes to the CategoricalDeckGLContainer and filter box (#6038)

* WIP

* WIP

* Fix time grain

* Fix autoformatting

* Fix more autoformatting

* Fix even more autoformatting

* Fix manual move of play slider

* Fix state management

* Revert change

* Fix screengrid

* Remove autozoom on render

* Remove unneeded code

* Zoom when mounted

* Fix lint
This commit is contained in:
Beto Dealmeida 2018-10-16 12:08:37 -07:00 committed by Maxime Beauchemin
parent 91792a564a
commit b4a1983303
5 changed files with 1975 additions and 64 deletions

View File

@ -181,9 +181,12 @@ class FilterBox extends React.Component {
.filter(key => !selectedValues.hasOwnProperty(key)
|| !(key in filtersChoices))
.forEach((key) => {
const choices = filtersChoices[key];
const choices = filtersChoices[key] || [];
const choiceIds = new Set(choices.map(f => f.id));
selectedValues[key]
const selectedValuesForKey = Array.isArray(selectedValues[key])
? selectedValues[key]
: [selectedValues[key]];
selectedValuesForKey
.filter(value => !choiceIds.has(value))
.forEach((value) => {
choices.unshift({

View File

@ -14,34 +14,37 @@ const propTypes = {
disabled: PropTypes.bool,
viewport: PropTypes.object.isRequired,
children: PropTypes.node,
onViewportChange: PropTypes.func,
onValuesChange: PropTypes.func,
};
const defaultProps = {
aggregation: false,
disabled: false,
onViewportChange: () => {},
onValuesChange: () => {},
};
export default class AnimatableDeckGLContainer extends React.Component {
constructor(props) {
super(props);
const { getLayers, start, end, getStep, values, disabled, viewport, ...other } = props;
this.state = { values, viewport };
this.other = other;
this.onChange = this.onChange.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState({ values: nextProps.values, viewport: nextProps.viewport });
}
onChange(newValues) {
this.setState({
values: Array.isArray(newValues)
? newValues
: [newValues, this.props.getStep(newValues)],
});
}
render() {
const { start, end, getStep, disabled, aggregation, children, getLayers } = this.props;
const { values, viewport } = this.state;
const {
start,
end,
getStep,
disabled,
aggregation,
children,
getLayers,
values,
viewport,
onViewportChange,
onValuesChange,
} = this.props;
const layers = getLayers(values);
return (
<div>
@ -49,7 +52,7 @@ export default class AnimatableDeckGLContainer extends React.Component {
{...this.other}
viewport={viewport}
layers={layers}
onViewportChange={newViewport => this.setState({ viewport: newViewport })}
onViewportChange={onViewportChange}
/>
{!disabled &&
<PlaySlider
@ -58,7 +61,7 @@ export default class AnimatableDeckGLContainer extends React.Component {
step={getStep(start)}
values={values}
range={!aggregation}
onChange={this.onChange}
onChange={onValuesChange}
/>
}
{children}

View File

@ -48,28 +48,31 @@ export default class CategoricalDeckGLContainer extends React.PureComponent {
* The container will have an interactive legend, populated from the
* categories present in the data.
*/
/* eslint-disable-next-line react/sort-comp */
static getDerivedStateFromProps(nextProps) {
const fd = nextProps.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
const timestamps = nextProps.payload.data.features.map(f => f.__timestamp);
const { start, end, getStep, values, disabled } = getPlaySliderParams(timestamps, timeGrain);
const categories = getCategories(fd, nextProps.payload.data.features);
return { start, end, getStep, values, disabled, categories };
}
constructor(props) {
super(props);
this.state = CategoricalDeckGLContainer.getDerivedStateFromProps(props);
const fd = props.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
const timestamps = props.payload.data.features.map(f => f.__timestamp);
const { start, end, getStep, values, disabled } = getPlaySliderParams(timestamps, timeGrain);
const categories = getCategories(fd, props.payload.data.features);
this.state = { start, end, getStep, values, disabled, categories, viewport: props.viewport };
this.getLayers = this.getLayers.bind(this);
this.onValuesChange = this.onValuesChange.bind(this);
this.onViewportChange = this.onViewportChange.bind(this);
this.toggleCategory = this.toggleCategory.bind(this);
this.showSingleCategory = this.showSingleCategory.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState(CategoricalDeckGLContainer.getDerivedStateFromProps(nextProps, this.state));
onValuesChange(values) {
this.setState({
values: Array.isArray(values)
? values
: [values, values + this.state.getStep(values)],
});
}
onViewportChange(viewport) {
this.setState({ viewport });
}
getLayers(values) {
const {
@ -79,31 +82,35 @@ export default class CategoricalDeckGLContainer extends React.PureComponent {
onAddFilter,
setTooltip,
} = this.props;
let data = [...payload.data.features];
let features = [...payload.data.features];
// Add colors from categories or fixed color
data = this.addColor(data, fd);
features = this.addColor(features, fd);
// Apply user defined data mutator if defined
if (fd.js_data_mutator) {
const jsFnMutator = sandboxedEval(fd.js_data_mutator);
data = jsFnMutator(data);
features = jsFnMutator(features);
}
// Filter by time
if (values[0] === values[1] || values[1] === this.end) {
data = data.filter(d => d.__timestamp >= values[0] && d.__timestamp <= values[1]);
features = features.filter(d => d.__timestamp >= values[0] && d.__timestamp <= values[1]);
} else {
data = data.filter(d => d.__timestamp >= values[0] && d.__timestamp < values[1]);
features = features.filter(d => d.__timestamp >= values[0] && d.__timestamp < values[1]);
}
// Show only categories selected in the legend
if (fd.dimension) {
data = data.filter(d => this.state.categories[d.cat_color].enabled);
features = features.filter(d => this.state.categories[d.cat_color].enabled);
}
payload.data.features = data;
return [getLayer(fd, payload, onAddFilter, setTooltip)];
const filteredPayload = {
...payload,
data: { ...payload.data, features },
};
return [getLayer(fd, filteredPayload, onAddFilter, setTooltip)];
}
addColor(data, fd) {
const c = fd.color_picker || { r: 0, g: 0, b: 0, a: 1 };
@ -146,8 +153,10 @@ export default class CategoricalDeckGLContainer extends React.PureComponent {
end={this.state.end}
getStep={this.state.getStep}
values={this.state.values}
onValuesChange={this.onValuesChange}
disabled={this.state.disabled}
viewport={this.props.viewport}
viewport={this.state.viewport}
onViewportChange={this.onViewportChange}
mapboxApiAccessToken={this.props.mapboxApiKey}
mapStyle={this.props.formData.mapbox_style}
setControlValue={this.props.setControlValue}

View File

@ -62,24 +62,31 @@ const defaultProps = {
};
class DeckGLScreenGrid extends React.PureComponent {
/* eslint-disable-next-line react/sort-comp */
static getDerivedStateFromProps(nextProps) {
const fd = nextProps.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
const timestamps = nextProps.payload.data.features.map(f => f.__timestamp);
const { start, end, getStep, values, disabled } = getPlaySliderParams(timestamps, timeGrain);
return { start, end, getStep, values, disabled };
}
constructor(props) {
super(props);
this.state = DeckGLScreenGrid.getDerivedStateFromProps(props);
const fd = props.formData;
const timeGrain = fd.time_grain_sqla || fd.granularity || 'PT1M';
const timestamps = props.payload.data.features.map(f => f.__timestamp);
const { start, end, getStep, values, disabled } = getPlaySliderParams(timestamps, timeGrain);
const viewport = fd.autozoom
? fitViewport(props.viewport, getPoints(props.payload.data.features))
: props.viewport;
this.state = { start, end, getStep, values, disabled, viewport };
this.getLayers = this.getLayers.bind(this);
this.onValuesChange = this.onValuesChange.bind(this);
this.onViewportChange = this.onViewportChange.bind(this);
}
componentWillReceiveProps(nextProps) {
this.setState(DeckGLScreenGrid.getDerivedStateFromProps(nextProps, this.state));
onValuesChange(values) {
this.setState({
values: Array.isArray(values)
? values
: [values, values + this.state.getStep(values)],
});
}
onViewportChange(viewport) {
this.setState({ viewport });
}
getLayers(values) {
const filters = [];
@ -102,11 +109,7 @@ class DeckGLScreenGrid extends React.PureComponent {
}
render() {
const { formData, payload } = this.props;
const viewport = formData.autozoom
? fitViewport(this.props.viewport, getPoints(payload.data.features))
: this.props.viewport;
const { formData, payload, setControlValue } = this.props;
return (
<div>
<AnimatableDeckGLContainer
@ -115,11 +118,13 @@ class DeckGLScreenGrid extends React.PureComponent {
end={this.state.end}
getStep={this.state.getStep}
values={this.state.values}
onValuesChange={this.onValuesChange}
disabled={this.state.disabled}
viewport={viewport}
mapboxApiAccessToken={this.props.payload.data.mapboxApiKey}
mapStyle={this.props.formData.mapbox_style}
setControlValue={this.props.setControlValue}
viewport={this.state.viewport}
onViewportChange={this.onViewportChange}
mapboxApiAccessToken={payload.data.mapboxApiKey}
mapStyle={formData.mapbox_style}
setControlValue={setControlValue}
aggregation
/>
</div>

File diff suppressed because it is too large Load Diff