@pie-lib/graphing 2.5.1 → 2.6.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,340 @@
1
+ import React from 'react';
2
+ import PropTypes from 'prop-types';
3
+ import { color } from '@pie-lib/render-ui';
4
+ import { withStyles } from '@material-ui/core/styles';
5
+ import ExpansionPanelSummary from '@material-ui/core/ExpansionPanelSummary';
6
+ import Typography from '@material-ui/core/Typography';
7
+ import ExpansionPanelDetails from '@material-ui/core/ExpansionPanelDetails';
8
+ import ExpansionPanel from '@material-ui/core/ExpansionPanel';
9
+ import ExpandMoreIcon from '@material-ui/icons/ExpandMore';
10
+ import TextField from '@material-ui/core/TextField';
11
+ import { NumberTextFieldCustom, Toggle } from '@pie-lib/config-ui';
12
+
13
+ const GridConfig = props => {
14
+ const { classes, disabled, labelValue, labelValues, gridValue, gridValues, onChange } = props;
15
+
16
+ return (
17
+ <div className={classes.columnView}>
18
+ <NumberTextFieldCustom
19
+ className={classes.textField}
20
+ label="Grid Interval"
21
+ value={gridValue}
22
+ customValues={gridValues}
23
+ variant="outlined"
24
+ disabled={disabled}
25
+ onChange={(e, v) => onChange('step', v)}
26
+ />
27
+ <NumberTextFieldCustom
28
+ className={classes.textField}
29
+ label="Label Interval"
30
+ value={labelValue}
31
+ customValues={labelValues}
32
+ variant="outlined"
33
+ disabled={disabled}
34
+ onChange={(e, v) => onChange('labelStep', v)}
35
+ />
36
+ </div>
37
+ );
38
+ };
39
+
40
+ const AxisConfig = props => {
41
+ const { classes, disabled, label, maxValue, minValue, onChange, type } = props;
42
+
43
+ return (
44
+ <div className={classes.columnView}>
45
+ <Typography variant="subtitle2">
46
+ <i>{type === 'domain' ? 'x' : 'y'}</i>
47
+ -axis
48
+ </Typography>
49
+ <NumberTextFieldCustom
50
+ className={classes.textField}
51
+ label="Min Value"
52
+ value={minValue}
53
+ variant="outlined"
54
+ disabled={disabled}
55
+ onChange={(e, v) => onChange('min', v)}
56
+ />
57
+ <NumberTextFieldCustom
58
+ className={classes.textField}
59
+ label="Max Value"
60
+ value={maxValue}
61
+ variant="outlined"
62
+ disabled={disabled}
63
+ onChange={(e, v) => onChange('max', v)}
64
+ />
65
+ <TextField
66
+ label="Label"
67
+ value={label}
68
+ inputProps={{
69
+ maxLength: 5,
70
+ style: { textAlign: 'center' }
71
+ }}
72
+ variant="outlined"
73
+ className={classes.textField}
74
+ onChange={e => onChange('axisLabel', e.target.value)}
75
+ />
76
+ </div>
77
+ );
78
+ };
79
+
80
+ const GridSetup = props => {
81
+ const {
82
+ classes,
83
+ sizeConstraints,
84
+ domain,
85
+ dimensionsEnabled,
86
+ gridValues,
87
+ includeAxes,
88
+ labelValues,
89
+ onChange,
90
+ range,
91
+ size,
92
+ standardGrid
93
+ } = props;
94
+ const gridProps = { min: 2, max: 41 };
95
+
96
+ const onIncludeAxes = includeAxes => {
97
+ const noAxesConfig = type => {
98
+ const axis = type === 'domain' ? domain : range;
99
+
100
+ return {
101
+ min: 1,
102
+ max: axis.max < gridProps.min || axis.max > gridProps.max ? 16 : axis.max,
103
+ step: 1,
104
+ labelStep: 0
105
+ };
106
+ };
107
+
108
+ const updatedRange = {
109
+ ...range,
110
+ ...(includeAxes ? { labelStep: 1 } : noAxesConfig('range'))
111
+ };
112
+ const updatedDomain = {
113
+ ...domain,
114
+ ...(includeAxes ? { labelStep: 1 } : noAxesConfig('domain'))
115
+ };
116
+
117
+ onChange({ includeAxes, range: updatedRange, domain: updatedDomain });
118
+ };
119
+
120
+ const onStandardGridChanged = value => {
121
+ onChange({
122
+ standardGrid: value,
123
+ range: {
124
+ ...domain,
125
+ axisLabel: range.axisLabel
126
+ },
127
+ graph: {
128
+ ...size,
129
+ height: size.width
130
+ }
131
+ });
132
+ };
133
+
134
+ const onSizeChanged = (key, value) => {
135
+ const graph = { ...size, [key]: value };
136
+
137
+ if (standardGrid) {
138
+ graph.height = value;
139
+ }
140
+
141
+ onChange({ graph });
142
+ };
143
+
144
+ const onDomainChanged = (key, value) => {
145
+ domain[key] = value;
146
+
147
+ if (standardGrid && key !== 'axisLabel') {
148
+ range[key] = value;
149
+ }
150
+
151
+ onChange({ domain, range });
152
+ };
153
+
154
+ const onRangeChanged = (key, value) => {
155
+ range[key] = value;
156
+
157
+ onChange({ range });
158
+ };
159
+
160
+ const axesConfig = (
161
+ <React.Fragment>
162
+ <div className={classes.rowView}>
163
+ <AxisConfig
164
+ classes={classes}
165
+ type="domain"
166
+ minValue={domain.min}
167
+ maxValue={domain.max}
168
+ label={domain.axisLabel}
169
+ includeAxes={includeAxes}
170
+ onChange={onDomainChanged}
171
+ />
172
+ <AxisConfig
173
+ classes={classes}
174
+ type="range"
175
+ minValue={range.min}
176
+ maxValue={range.max}
177
+ label={range.axisLabel}
178
+ disabled={standardGrid}
179
+ includeAxes={includeAxes}
180
+ onChange={onRangeChanged}
181
+ />
182
+ </div>
183
+ <Typography className={classes.text}>
184
+ If you want the axis to be visible, use a zero or negative Min Value, and a positive Max
185
+ Value
186
+ </Typography>
187
+ <div className={classes.rowView}>
188
+ <GridConfig
189
+ classes={classes}
190
+ gridValue={domain.step}
191
+ labelValue={domain.labelStep}
192
+ gridValues={gridValues}
193
+ labelValues={labelValues}
194
+ onChange={onDomainChanged}
195
+ />
196
+ <GridConfig
197
+ classes={classes}
198
+ disabled={standardGrid}
199
+ gridValue={range.step}
200
+ labelValue={range.labelStep}
201
+ onChange={onRangeChanged}
202
+ />
203
+ </div>
204
+ <Typography className={classes.text}>
205
+ For unnumbered gridlines, enter a label interval of 0
206
+ </Typography>
207
+ </React.Fragment>
208
+ );
209
+
210
+ const gridlinesConfig = (
211
+ <div className={classes.columnView}>
212
+ <NumberTextFieldCustom
213
+ className={classes.largeTextField}
214
+ label="Number of Horizontal Gridlines"
215
+ value={domain.max}
216
+ min={!includeAxes && gridProps.min}
217
+ max={!includeAxes && gridProps.max}
218
+ variant="outlined"
219
+ onChange={(e, v) => onDomainChanged('max', v)}
220
+ />
221
+ <NumberTextFieldCustom
222
+ className={classes.largeTextField}
223
+ label="Number of Vertical Gridlines"
224
+ value={range.max}
225
+ min={!includeAxes && gridProps.min}
226
+ max={!includeAxes && gridProps.max}
227
+ variant="outlined"
228
+ disabled={standardGrid}
229
+ onChange={(e, v) => onRangeChanged('max', v)}
230
+ />
231
+ </div>
232
+ );
233
+
234
+ return (
235
+ <div className={classes.wrapper}>
236
+ <ExpansionPanel>
237
+ <ExpansionPanelSummary expandIcon={<ExpandMoreIcon />}>
238
+ <Typography variant="subtitle1">Customize Grid Setup</Typography>
239
+ </ExpansionPanelSummary>
240
+ <ExpansionPanelDetails>
241
+ <div className={classes.content}>
242
+ <Toggle label="Include axes and labels?" toggle={onIncludeAxes} checked={includeAxes} />
243
+ <Toggle
244
+ label="Constrain to standard coordinate grid?"
245
+ toggle={onStandardGridChanged}
246
+ checked={standardGrid}
247
+ />
248
+ {includeAxes ? axesConfig : gridlinesConfig}
249
+ {dimensionsEnabled && (
250
+ <div className={classes.dimensions}>
251
+ <div>
252
+ <Typography>Dimensions(px)</Typography>
253
+ <Typography className={classes.disabled}>Min 150, Max 700</Typography>
254
+ </div>
255
+ <NumberTextFieldCustom
256
+ className={classes.textField}
257
+ label="Width"
258
+ value={size.width}
259
+ min={sizeConstraints.min}
260
+ max={sizeConstraints.max}
261
+ step={sizeConstraints.step}
262
+ variant="outlined"
263
+ onChange={(e, v) => onSizeChanged('width', v)}
264
+ />
265
+ <NumberTextFieldCustom
266
+ className={classes.textField}
267
+ label="Height"
268
+ value={size.height}
269
+ min={sizeConstraints.min}
270
+ max={sizeConstraints.max}
271
+ step={sizeConstraints.step}
272
+ variant="outlined"
273
+ disabled={standardGrid}
274
+ onChange={(e, v) => onSizeChanged('height', v)}
275
+ />
276
+ </div>
277
+ )}
278
+ </div>
279
+ </ExpansionPanelDetails>
280
+ </ExpansionPanel>
281
+ </div>
282
+ );
283
+ };
284
+
285
+ GridSetup.propTypes = {
286
+ classes: PropTypes.object,
287
+ domain: PropTypes.object,
288
+ dimensionsEnabled: PropTypes.object,
289
+ gridValues: PropTypes.object,
290
+ includeAxes: PropTypes.bool,
291
+ labelValues: PropTypes.object,
292
+ onChange: PropTypes.function,
293
+ range: PropTypes.object,
294
+ size: PropTypes.object,
295
+ sizeConstraints: PropTypes.object,
296
+ standardGrid: PropTypes.bool
297
+ };
298
+
299
+ const styles = theme => ({
300
+ wrapper: {
301
+ width: '450px'
302
+ },
303
+ content: {
304
+ display: 'flex',
305
+ flexDirection: 'column',
306
+ width: '100%'
307
+ },
308
+ columnView: {
309
+ display: 'flex',
310
+ flexDirection: 'column',
311
+ alignItems: 'center'
312
+ },
313
+ rowView: {
314
+ display: 'flex',
315
+ justifyContent: 'space-around',
316
+ alignItems: 'center'
317
+ },
318
+ textField: {
319
+ width: '130px',
320
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
321
+ },
322
+ largeTextField: {
323
+ width: '230px',
324
+ margin: `${theme.spacing.unit}px ${theme.spacing.unit / 2}px`
325
+ },
326
+ text: {
327
+ fontStyle: 'italic',
328
+ margin: `${theme.spacing.unit}px 0`
329
+ },
330
+ dimensions: {
331
+ display: 'flex',
332
+ justifyContent: 'space-between',
333
+ alignItems: 'center'
334
+ },
335
+ disabled: {
336
+ color: color.disabled()
337
+ }
338
+ });
339
+
340
+ export default withStyles(styles)(GridSetup);
package/src/grid.jsx CHANGED
@@ -2,12 +2,14 @@ import React from 'react';
2
2
  import PropTypes from 'prop-types';
3
3
  import * as vx from '@vx/grid';
4
4
  import { types, utils } from '@pie-lib/plot';
5
+ import { color } from '@pie-lib/render-ui';
5
6
  import { withStyles } from '@material-ui/core/styles';
6
7
  import { getTickValues } from './utils';
7
8
 
8
9
  export class Grid extends React.Component {
9
10
  static propTypes = {
10
11
  disabled: PropTypes.bool,
12
+ disabledAdditionalGrid: PropTypes.bool,
11
13
  classes: PropTypes.object.isRequired,
12
14
  graphProps: types.GraphPropsType.isRequired
13
15
  };
@@ -15,26 +17,124 @@ export class Grid extends React.Component {
15
17
  shouldComponentUpdate(nextProps) {
16
18
  const { graphProps } = this.props;
17
19
  const { graphProps: nextGraphProps } = nextProps;
20
+
18
21
  return !utils.isDomainRangeEqual(graphProps, nextGraphProps);
19
22
  }
20
23
 
24
+ getAdditionalGridProps = (rowTickValues, columnTickValues) => {
25
+ const {
26
+ graphProps: {
27
+ scale,
28
+ size: { width, height },
29
+ domain,
30
+ range
31
+ }
32
+ } = this.props;
33
+ const rowTickLabelValues = getTickValues({
34
+ ...range,
35
+ step: range.labelStep
36
+ }).filter(value => rowTickValues.includes(value));
37
+ const columnTickLabelValues = getTickValues({
38
+ ...domain,
39
+ step: domain.labelStep
40
+ }).filter(value => columnTickValues.includes(value));
41
+
42
+ const minValueLength =
43
+ (rowTickLabelValues.length &&
44
+ Math.min(...rowTickLabelValues)
45
+ .toString()
46
+ .replace(/[.-]/g, '').length) ||
47
+ 1;
48
+ const maxValueLength =
49
+ (rowTickLabelValues.length &&
50
+ Math.max(...rowTickLabelValues)
51
+ .toString()
52
+ .replace(/[.-]/g, '').length) ||
53
+ 1;
54
+
55
+ const rowLabelLength = Math.max(minValueLength, maxValueLength) * 9 + 22;
56
+ const horizontalDistanceToZero = scale.x(0);
57
+ const verticalDistanceToZero = scale.y(0);
58
+ const columnLabelLength = 28;
59
+ const rowStrokeDasharray = `${horizontalDistanceToZero -
60
+ rowLabelLength} ${rowLabelLength} ${width}`;
61
+ const columnStrokeDasharray = `${verticalDistanceToZero} ${columnLabelLength} ${height}`;
62
+
63
+ const displayAdditionalGrid =
64
+ domain.labelStep > 0 &&
65
+ range.labelStep > 0 &&
66
+ rowTickLabelValues &&
67
+ columnTickLabelValues &&
68
+ rowTickLabelValues.length > 1 &&
69
+ columnTickLabelValues.length > 1 &&
70
+ (rowTickLabelValues.length !== rowTickValues.length ||
71
+ columnTickLabelValues.length !== columnTickValues.length);
72
+
73
+ const filteredColumnValues = columnTickLabelValues.filter(
74
+ value => value >= 0 || horizontalDistanceToZero - scale.x(value) > rowLabelLength
75
+ );
76
+ const filteredRowValues = rowTickLabelValues.filter(
77
+ value => value >= 0 || scale.y(value) - verticalDistanceToZero > columnLabelLength
78
+ );
79
+
80
+ return {
81
+ rowTickLabelValues: filteredRowValues,
82
+ columnTickLabelValues: filteredColumnValues,
83
+ rowStrokeDasharray,
84
+ columnStrokeDasharray,
85
+ displayAdditionalGrid
86
+ };
87
+ };
88
+
21
89
  render() {
22
90
  const { classes, graphProps } = this.props;
23
- const { scale, size, domain, range } = graphProps;
91
+ const {
92
+ scale,
93
+ size: { height, width },
94
+ domain,
95
+ range
96
+ } = graphProps;
24
97
  const rowTickValues = getTickValues(range);
25
- const columnTicksValues = getTickValues(domain);
98
+ const columnTickValues = getTickValues(domain);
99
+ const {
100
+ rowTickLabelValues,
101
+ columnTickLabelValues,
102
+ rowStrokeDasharray,
103
+ columnStrokeDasharray,
104
+ displayAdditionalGrid
105
+ } = this.getAdditionalGridProps(rowTickValues, columnTickValues);
26
106
 
27
107
  return (
28
- <vx.Grid
29
- innerRef={r => (this.grid = r)}
30
- xScale={scale.x}
31
- yScale={scale.y}
32
- width={size.width}
33
- height={size.height}
34
- className={classes.grid}
35
- rowTickValues={rowTickValues}
36
- columnTickValues={columnTicksValues}
37
- />
108
+ <>
109
+ <vx.Grid
110
+ innerRef={r => (this.grid = r)}
111
+ xScale={scale.x}
112
+ yScale={scale.y}
113
+ width={width}
114
+ height={height}
115
+ className={classes.grid}
116
+ rowTickValues={rowTickValues}
117
+ columnTickValues={columnTickValues}
118
+ />
119
+ {displayAdditionalGrid && (
120
+ <>
121
+ <vx.GridRows
122
+ scale={scale.y}
123
+ width={width}
124
+ tickValues={rowTickLabelValues}
125
+ stroke={color.primary()}
126
+ strokeDasharray={rowStrokeDasharray}
127
+ />
128
+ <vx.GridColumns
129
+ scale={scale.x}
130
+ height={height}
131
+ tickValues={columnTickLabelValues}
132
+ stroke={color.primary()}
133
+ strokeDasharray={columnStrokeDasharray}
134
+ />
135
+ </>
136
+ )}
137
+ </>
38
138
  );
39
139
  }
40
140
  }
package/src/index.js CHANGED
@@ -1,6 +1,7 @@
1
1
  import Graph from './graph';
2
2
  import GraphContainer from './container';
3
+ import GridSetup from './grid-setup';
3
4
  import * as tools from './tools';
4
5
  import ToolMenu from './tool-menu';
5
6
 
6
- export { Graph, GraphContainer, ToolMenu, tools };
7
+ export { Graph, GraphContainer, GridSetup, ToolMenu, tools };
package/src/labels.jsx CHANGED
@@ -51,11 +51,12 @@ class RawLabel extends React.Component {
51
51
 
52
52
  render() {
53
53
  const { text, side, graphProps, classes } = this.props;
54
+ const { size, domain, range } = graphProps;
55
+ const totalHeight = (size.height || 500) + (range.padding || 0) * 2;
56
+ const totalWidth = (size.width || 500) + (domain.padding || 0) * 2;
54
57
 
55
- const { size } = graphProps;
56
-
57
- const transform = getTransform(side, size.width, size.height);
58
- const width = side === 'left' || side === 'right' ? size.height : size.width;
58
+ const transform = getTransform(side, totalWidth, totalHeight);
59
+ const width = side === 'left' || side === 'right' ? totalHeight : totalWidth;
59
60
  const height = 36;
60
61
  const y = getY(side, height);
61
62
 
@@ -102,8 +103,10 @@ export class Labels extends React.Component {
102
103
  };
103
104
 
104
105
  static defaultProps = {};
106
+
105
107
  render() {
106
108
  const { value, graphProps } = this.props;
109
+
107
110
  return (
108
111
  <React.Fragment>
109
112
  {value && value.left && (