-
Notifications
You must be signed in to change notification settings - Fork 4.4k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Migrate Choropleth visualization to React (#4313)
* Migrate Choropleth to React: skeleton * Migrate Choropleth to React: Editor - skeleton * Choropleth Editor: Bounds tab * Choropleth Editor: Colors tab * Choropleth Editor: Format tab * Choropleth Editor: General tab * Some refinements * Migrate Choropleth to React: Renderer * Refine code * CR1
- Loading branch information
1 parent
ef56e4e
commit 1a95904
Showing
21 changed files
with
986 additions
and
714 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,11 @@ | ||
import { isEqual } from 'lodash'; | ||
import { useMemo, useRef } from 'react'; | ||
|
||
export default function useMemoWithDeepCompare(create, inputs) { | ||
const valueRef = useRef(); | ||
const value = useMemo(create, inputs); | ||
if (!isEqual(value, valueRef.current)) { | ||
valueRef.current = value; | ||
} | ||
return valueRef.current; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
import { extend } from 'lodash'; | ||
import ColorPalette from '@/visualizations/ColorPalette'; | ||
|
||
export default extend({ | ||
White: '#ffffff', | ||
Black: '#000000', | ||
'Light Gray': '#dddddd', | ||
}, ColorPalette); |
80 changes: 80 additions & 0 deletions
80
client/app/visualizations/choropleth/Editor/BoundsSettings.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,80 @@ | ||
import { isFinite, cloneDeep } from 'lodash'; | ||
import React, { useState, useEffect, useCallback } from 'react'; | ||
import { useDebouncedCallback } from 'use-debounce'; | ||
import InputNumber from 'antd/lib/input-number'; | ||
import * as Grid from 'antd/lib/grid'; | ||
import { EditorPropTypes } from '@/visualizations'; | ||
|
||
export default function BoundsSettings({ options, onOptionsChange }) { | ||
// Bounds may be changed in editor or on preview (by drag/zoom map). | ||
// Changes from preview does not come frequently (only when user release mouse button), | ||
// but changes from editor should be debounced. | ||
// Therefore this component has intermediate state to hold immediate user input, | ||
// which is updated from `options.bounds` and by inputs immediately on user input, | ||
// but `onOptionsChange` event is debounced and uses last value from internal state. | ||
|
||
const [bounds, setBounds] = useState(options.bounds); | ||
const [onOptionsChangeDebounced] = useDebouncedCallback(onOptionsChange, 200); | ||
|
||
useEffect(() => { | ||
setBounds(options.bounds); | ||
}, [options.bounds]); | ||
|
||
const updateBounds = useCallback((i, j, v) => { | ||
v = parseFloat(v); // InputNumber may emit `null` and empty strings instead of numbers | ||
if (isFinite(v)) { | ||
const newBounds = cloneDeep(bounds); | ||
newBounds[i][j] = v; | ||
setBounds(newBounds); | ||
onOptionsChangeDebounced({ bounds: newBounds }); | ||
} | ||
}, [bounds]); | ||
|
||
return ( | ||
<React.Fragment> | ||
<div className="m-b-15"> | ||
<label htmlFor="choropleth-editor-bounds-ne">North-East latitude and longitude</label> | ||
<Grid.Row gutter={15}> | ||
<Grid.Col span={12}> | ||
<InputNumber | ||
id="choropleth-editor-bounds-ne" | ||
className="w-100" | ||
value={bounds[1][0]} | ||
onChange={value => updateBounds(1, 0, value)} | ||
/> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<InputNumber | ||
className="w-100" | ||
value={bounds[1][1]} | ||
onChange={value => updateBounds(1, 1, value)} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
</div> | ||
|
||
<div className="m-b-15"> | ||
<label htmlFor="choropleth-editor-bounds-sw">South-West latitude and longitude</label> | ||
<Grid.Row gutter={15}> | ||
<Grid.Col span={12}> | ||
<InputNumber | ||
id="choropleth-editor-bounds-sw" | ||
className="w-100" | ||
value={bounds[0][0]} | ||
onChange={value => updateBounds(0, 0, value)} | ||
/> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<InputNumber | ||
className="w-100" | ||
value={bounds[0][1]} | ||
onChange={value => updateBounds(0, 1, value)} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
</div> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
BoundsSettings.propTypes = EditorPropTypes; |
132 changes: 132 additions & 0 deletions
132
client/app/visualizations/choropleth/Editor/ColorsSettings.jsx
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,132 @@ | ||
import React from 'react'; | ||
import { useDebouncedCallback } from 'use-debounce'; | ||
import Select from 'antd/lib/select'; | ||
import InputNumber from 'antd/lib/input-number'; | ||
import * as Grid from 'antd/lib/grid'; | ||
import ColorPicker from '@/components/ColorPicker'; | ||
import { EditorPropTypes } from '@/visualizations'; | ||
import ColorPalette from '../ColorPalette'; | ||
|
||
export default function ColorsSettings({ options, onOptionsChange }) { | ||
const [onOptionsChangeDebounced] = useDebouncedCallback(onOptionsChange, 200); | ||
|
||
return ( | ||
<React.Fragment> | ||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-clustering-mode">Clustering mode</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<Select | ||
id="choropleth-editor-clustering-mode" | ||
className="w-100" | ||
defaultValue={options.clusteringMode} | ||
onChange={clusteringMode => onOptionsChange({ clusteringMode })} | ||
> | ||
<Select.Option value="q">quantile</Select.Option> | ||
<Select.Option value="e">equidistant</Select.Option> | ||
<Select.Option value="k">k-means</Select.Option> | ||
</Select> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-steps">Steps</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<InputNumber | ||
id="choropleth-editor-color-steps" | ||
className="w-100" | ||
min={3} | ||
max={11} | ||
defaultValue={options.steps} | ||
onChange={steps => onOptionsChangeDebounced({ steps })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-min">Min Color</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<ColorPicker | ||
id="choropleth-editor-color-min" | ||
interactive | ||
presetColors={ColorPalette} | ||
placement="topRight" | ||
color={options.colors.min} | ||
onChange={min => onOptionsChange({ colors: { min } })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-max">Max Color</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<ColorPicker | ||
id="choropleth-editor-color-max" | ||
interactive | ||
presetColors={ColorPalette} | ||
placement="topRight" | ||
color={options.colors.max} | ||
onChange={max => onOptionsChange({ colors: { max } })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-no-value">No value color</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<ColorPicker | ||
id="choropleth-editor-color-no-value" | ||
interactive | ||
presetColors={ColorPalette} | ||
placement="topRight" | ||
color={options.colors.noValue} | ||
onChange={noValue => onOptionsChange({ colors: { noValue } })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-background">Background color</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<ColorPicker | ||
id="choropleth-editor-color-background" | ||
interactive | ||
presetColors={ColorPalette} | ||
placement="topRight" | ||
color={options.colors.background} | ||
onChange={background => onOptionsChange({ colors: { background } })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
|
||
<Grid.Row type="flex" align="middle" className="m-b-15"> | ||
<Grid.Col span={12}> | ||
<label htmlFor="choropleth-editor-color-borders">Borders color</label> | ||
</Grid.Col> | ||
<Grid.Col span={12}> | ||
<ColorPicker | ||
id="choropleth-editor-color-borders" | ||
interactive | ||
presetColors={ColorPalette} | ||
placement="topRight" | ||
color={options.colors.borders} | ||
onChange={borders => onOptionsChange({ colors: { borders } })} | ||
/> | ||
</Grid.Col> | ||
</Grid.Row> | ||
</React.Fragment> | ||
); | ||
} | ||
|
||
ColorsSettings.propTypes = EditorPropTypes; |
Oops, something went wrong.