Let Plot components size themselves
parent
049e8f8c90
commit
0d774cecb0
|
@ -1,132 +1,34 @@
|
|||
import React, {useReducer, useRef, useMemo, SFC, CSSProperties} from 'react'
|
||||
import React, {SFC} from 'react'
|
||||
import {AutoSizer} from 'react-virtualized'
|
||||
|
||||
import {Table, PlotEnv} from 'src/minard'
|
||||
import {Axes} from 'src/minard/components/Axes'
|
||||
import {useMousePos} from 'src/minard/utils/useMousePos'
|
||||
import {useMountedEffect} from 'src/minard/utils/useMountedEffect'
|
||||
import {
|
||||
setDimensions,
|
||||
setTable,
|
||||
setControlledXDomain,
|
||||
setControlledYDomain,
|
||||
setXAxisLabel,
|
||||
setYAxisLabel,
|
||||
} from 'src/minard/utils/plotEnvActions'
|
||||
import {plotEnvReducer, INITIAL_PLOT_ENV} from 'src/minard/utils/plotEnvReducer'
|
||||
SizedPlot,
|
||||
Props as SizedPlotProps,
|
||||
} from 'src/minard/components/SizedPlot'
|
||||
|
||||
export interface Props {
|
||||
//
|
||||
// Required props
|
||||
// ==============
|
||||
//
|
||||
table: Table
|
||||
width: number
|
||||
height: number
|
||||
children: (env: PlotEnv) => JSX.Element
|
||||
type Props = Pick<
|
||||
SizedPlotProps,
|
||||
Exclude<keyof SizedPlotProps, 'width' | 'height'>
|
||||
> & {width?: number; height?: number}
|
||||
|
||||
//
|
||||
// Miscellaneous options
|
||||
// =====================
|
||||
//
|
||||
axesStroke?: string
|
||||
tickFont?: string
|
||||
tickFill?: string
|
||||
xAxisLabel?: string
|
||||
yAxisLabel?: string
|
||||
|
||||
// The x domain of the plot can be explicitly set. If this prop is passed,
|
||||
// then the component is operating in a "controlled" mode, where it always
|
||||
// uses the passed x domain. Any interaction with the plot that should change
|
||||
// the x domain (clicking, brushing, etc.) will call the `onSetXDomain` prop
|
||||
// when the component is in controlled mode. If the `xDomain` prop is not
|
||||
// passed, then the component is "uncontrolled". It will compute and set the
|
||||
// `xDomain` automatically.
|
||||
xDomain?: [number, number]
|
||||
onSetXDomain?: (xDomain: [number, number]) => void
|
||||
|
||||
// See the `xDomain` and `onSetXDomain` props
|
||||
yDomain?: [number, number]
|
||||
onSetYDomain?: (yDomain: [number, number]) => void
|
||||
}
|
||||
|
||||
export const Plot: SFC<Props> = ({
|
||||
width,
|
||||
height,
|
||||
table,
|
||||
children,
|
||||
axesStroke = '#31313d',
|
||||
tickFont = 'bold 10px Roboto',
|
||||
tickFill = '#8e91a1',
|
||||
xAxisLabel = '',
|
||||
yAxisLabel = '',
|
||||
xDomain = null,
|
||||
yDomain = null,
|
||||
}) => {
|
||||
const [env, dispatch] = useReducer(plotEnvReducer, {
|
||||
...INITIAL_PLOT_ENV,
|
||||
width,
|
||||
height,
|
||||
xDomain,
|
||||
yDomain,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
baseLayer: {...INITIAL_PLOT_ENV.baseLayer, table},
|
||||
})
|
||||
|
||||
useMountedEffect(() => dispatch(setTable(table)), [table])
|
||||
useMountedEffect(() => dispatch(setControlledXDomain(xDomain)), [xDomain])
|
||||
useMountedEffect(() => dispatch(setControlledYDomain(yDomain)), [yDomain])
|
||||
useMountedEffect(() => dispatch(setXAxisLabel(xAxisLabel)), [xAxisLabel])
|
||||
useMountedEffect(() => dispatch(setYAxisLabel(yAxisLabel)), [yAxisLabel])
|
||||
useMountedEffect(() => dispatch(setDimensions(width, height)), [
|
||||
width,
|
||||
height,
|
||||
])
|
||||
|
||||
const mouseRegion = useRef<HTMLDivElement>(null)
|
||||
const {x: hoverX, y: hoverY} = useMousePos(mouseRegion.current)
|
||||
|
||||
const childProps = useMemo(
|
||||
() => ({
|
||||
...env,
|
||||
hoverX,
|
||||
hoverY,
|
||||
dispatch,
|
||||
}),
|
||||
[env, hoverX, hoverY, dispatch]
|
||||
)
|
||||
|
||||
const plotStyle: CSSProperties = {
|
||||
position: 'relative',
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
}
|
||||
|
||||
const layersStyle: CSSProperties = {
|
||||
position: 'absolute',
|
||||
top: `${env.margins.top}px`,
|
||||
right: `${env.margins.right}px`,
|
||||
bottom: `${env.margins.bottom}px`,
|
||||
left: `${env.margins.left}px`,
|
||||
/*
|
||||
Works just like a `SizedPlot`, except it will measure the width and height of
|
||||
the containing element if no `width` and `height` props are passed.
|
||||
*/
|
||||
export const Plot: SFC<Props> = props => {
|
||||
if (props.width && props.height) {
|
||||
return <SizedPlot {...props} width={props.width} height={props.height} />
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="minard-plot" style={plotStyle}>
|
||||
<Axes
|
||||
env={env}
|
||||
axesStroke={axesStroke}
|
||||
tickFont={tickFont}
|
||||
tickFill={tickFill}
|
||||
>
|
||||
<div className="minard-layers" style={layersStyle}>
|
||||
{children(childProps)}
|
||||
</div>
|
||||
<div
|
||||
className="minard-interaction-region"
|
||||
style={layersStyle}
|
||||
ref={mouseRegion}
|
||||
/>
|
||||
</Axes>
|
||||
</div>
|
||||
<AutoSizer>
|
||||
{({width, height}) => {
|
||||
if (width === 0 || height === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return <SizedPlot {...props} width={width} height={height} />
|
||||
}}
|
||||
</AutoSizer>
|
||||
)
|
||||
}
|
||||
|
|
|
@ -0,0 +1,132 @@
|
|||
import React, {useReducer, useRef, useMemo, SFC, CSSProperties} from 'react'
|
||||
|
||||
import {Table, PlotEnv} from 'src/minard'
|
||||
import {Axes} from 'src/minard/components/Axes'
|
||||
import {useMousePos} from 'src/minard/utils/useMousePos'
|
||||
import {useMountedEffect} from 'src/minard/utils/useMountedEffect'
|
||||
import {
|
||||
setDimensions,
|
||||
setTable,
|
||||
setControlledXDomain,
|
||||
setControlledYDomain,
|
||||
setXAxisLabel,
|
||||
setYAxisLabel,
|
||||
} from 'src/minard/utils/plotEnvActions'
|
||||
import {plotEnvReducer, INITIAL_PLOT_ENV} from 'src/minard/utils/plotEnvReducer'
|
||||
|
||||
export interface Props {
|
||||
//
|
||||
// Required props
|
||||
// ==============
|
||||
//
|
||||
table: Table
|
||||
width: number
|
||||
height: number
|
||||
children: (env: PlotEnv) => JSX.Element
|
||||
|
||||
//
|
||||
// Miscellaneous options
|
||||
// =====================
|
||||
//
|
||||
axesStroke?: string
|
||||
tickFont?: string
|
||||
tickFill?: string
|
||||
xAxisLabel?: string
|
||||
yAxisLabel?: string
|
||||
|
||||
// The x domain of the plot can be explicitly set. If this prop is passed,
|
||||
// then the component is operating in a "controlled" mode, where it always
|
||||
// uses the passed x domain. Any interaction with the plot that should change
|
||||
// the x domain (clicking, brushing, etc.) will call the `onSetXDomain` prop
|
||||
// when the component is in controlled mode. If the `xDomain` prop is not
|
||||
// passed, then the component is "uncontrolled". It will compute and set the
|
||||
// `xDomain` automatically.
|
||||
xDomain?: [number, number]
|
||||
onSetXDomain?: (xDomain: [number, number]) => void
|
||||
|
||||
// See the `xDomain` and `onSetXDomain` props
|
||||
yDomain?: [number, number]
|
||||
onSetYDomain?: (yDomain: [number, number]) => void
|
||||
}
|
||||
|
||||
export const SizedPlot: SFC<Props> = ({
|
||||
width,
|
||||
height,
|
||||
table,
|
||||
children,
|
||||
axesStroke = '#31313d',
|
||||
tickFont = 'bold 10px Roboto',
|
||||
tickFill = '#8e91a1',
|
||||
xAxisLabel = '',
|
||||
yAxisLabel = '',
|
||||
xDomain = null,
|
||||
yDomain = null,
|
||||
}) => {
|
||||
const [env, dispatch] = useReducer(plotEnvReducer, {
|
||||
...INITIAL_PLOT_ENV,
|
||||
width,
|
||||
height,
|
||||
xDomain,
|
||||
yDomain,
|
||||
xAxisLabel,
|
||||
yAxisLabel,
|
||||
baseLayer: {...INITIAL_PLOT_ENV.baseLayer, table},
|
||||
})
|
||||
|
||||
useMountedEffect(() => dispatch(setTable(table)), [table])
|
||||
useMountedEffect(() => dispatch(setControlledXDomain(xDomain)), [xDomain])
|
||||
useMountedEffect(() => dispatch(setControlledYDomain(yDomain)), [yDomain])
|
||||
useMountedEffect(() => dispatch(setXAxisLabel(xAxisLabel)), [xAxisLabel])
|
||||
useMountedEffect(() => dispatch(setYAxisLabel(yAxisLabel)), [yAxisLabel])
|
||||
useMountedEffect(() => dispatch(setDimensions(width, height)), [
|
||||
width,
|
||||
height,
|
||||
])
|
||||
|
||||
const mouseRegion = useRef<HTMLDivElement>(null)
|
||||
const {x: hoverX, y: hoverY} = useMousePos(mouseRegion.current)
|
||||
|
||||
const childProps = useMemo(
|
||||
() => ({
|
||||
...env,
|
||||
hoverX,
|
||||
hoverY,
|
||||
dispatch,
|
||||
}),
|
||||
[env, hoverX, hoverY, dispatch]
|
||||
)
|
||||
|
||||
const plotStyle: CSSProperties = {
|
||||
position: 'relative',
|
||||
width: `${width}px`,
|
||||
height: `${height}px`,
|
||||
}
|
||||
|
||||
const layersStyle: CSSProperties = {
|
||||
position: 'absolute',
|
||||
top: `${env.margins.top}px`,
|
||||
right: `${env.margins.right}px`,
|
||||
bottom: `${env.margins.bottom}px`,
|
||||
left: `${env.margins.left}px`,
|
||||
}
|
||||
|
||||
return (
|
||||
<div className="minard-plot" style={plotStyle}>
|
||||
<Axes
|
||||
env={env}
|
||||
axesStroke={axesStroke}
|
||||
tickFont={tickFont}
|
||||
tickFill={tickFill}
|
||||
>
|
||||
<div className="minard-layers" style={layersStyle}>
|
||||
{children(childProps)}
|
||||
</div>
|
||||
<div
|
||||
className="minard-interaction-region"
|
||||
style={layersStyle}
|
||||
ref={mouseRegion}
|
||||
/>
|
||||
</Axes>
|
||||
</div>
|
||||
)
|
||||
}
|
|
@ -1,7 +1,6 @@
|
|||
// Libraries
|
||||
import React, {useMemo, useEffect, SFC} from 'react'
|
||||
import {connect} from 'react-redux'
|
||||
import {AutoSizer} from 'react-virtualized'
|
||||
import {
|
||||
Plot as MinardPlot,
|
||||
Histogram as MinardHistogram,
|
||||
|
@ -99,36 +98,24 @@ const Histogram: SFC<Props> = ({
|
|||
}
|
||||
|
||||
return (
|
||||
<AutoSizer>
|
||||
{({width, height}) => {
|
||||
if (width === 0 || height === 0) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<MinardPlot
|
||||
table={table}
|
||||
width={width}
|
||||
height={height}
|
||||
xAxisLabel={xAxisLabel}
|
||||
xDomain={xDomain}
|
||||
onSetXDomain={setXDomain}
|
||||
>
|
||||
{env => (
|
||||
<MinardHistogram
|
||||
env={env}
|
||||
x={mappings.x}
|
||||
fill={fill}
|
||||
binCount={binCount}
|
||||
position={position}
|
||||
tooltip={HistogramTooltip}
|
||||
colors={colorHexes}
|
||||
/>
|
||||
)}
|
||||
</MinardPlot>
|
||||
)
|
||||
}}
|
||||
</AutoSizer>
|
||||
<MinardPlot
|
||||
table={table}
|
||||
xAxisLabel={xAxisLabel}
|
||||
xDomain={xDomain}
|
||||
onSetXDomain={setXDomain}
|
||||
>
|
||||
{env => (
|
||||
<MinardHistogram
|
||||
env={env}
|
||||
x={mappings.x}
|
||||
fill={fill}
|
||||
binCount={binCount}
|
||||
position={position}
|
||||
tooltip={HistogramTooltip}
|
||||
colors={colorHexes}
|
||||
/>
|
||||
)}
|
||||
</MinardPlot>
|
||||
)
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in New Issue