@vitessce/statistical-plots 2.0.3-beta.0 → 3.0.0

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.
Files changed (43) hide show
  1. package/dist/index.js +85871 -6
  2. package/dist-tsc/CellSetExpressionPlot.d.ts +33 -0
  3. package/dist-tsc/CellSetExpressionPlot.d.ts.map +1 -0
  4. package/{dist → dist-tsc}/CellSetExpressionPlot.js +3 -3
  5. package/dist-tsc/CellSetExpressionPlotOptions.d.ts +2 -0
  6. package/dist-tsc/CellSetExpressionPlotOptions.d.ts.map +1 -0
  7. package/{dist → dist-tsc}/CellSetExpressionPlotOptions.js +1 -3
  8. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts +16 -0
  9. package/dist-tsc/CellSetExpressionPlotSubscriber.d.ts.map +1 -0
  10. package/{dist → dist-tsc}/CellSetExpressionPlotSubscriber.js +7 -11
  11. package/dist-tsc/CellSetSizesPlot.d.ts +29 -0
  12. package/dist-tsc/CellSetSizesPlot.d.ts.map +1 -0
  13. package/dist-tsc/CellSetSizesPlot.js +149 -0
  14. package/dist-tsc/CellSetSizesPlotSubscriber.d.ts +18 -0
  15. package/dist-tsc/CellSetSizesPlotSubscriber.d.ts.map +1 -0
  16. package/dist-tsc/CellSetSizesPlotSubscriber.js +77 -0
  17. package/dist-tsc/ExpressionHistogram.d.ts +34 -0
  18. package/dist-tsc/ExpressionHistogram.d.ts.map +1 -0
  19. package/dist-tsc/ExpressionHistogram.js +93 -0
  20. package/dist-tsc/ExpressionHistogramSubscriber.d.ts +16 -0
  21. package/dist-tsc/ExpressionHistogramSubscriber.d.ts.map +1 -0
  22. package/{dist → dist-tsc}/ExpressionHistogramSubscriber.js +21 -14
  23. package/dist-tsc/index.d.ts +7 -0
  24. package/dist-tsc/index.d.ts.map +1 -0
  25. package/dist-tsc/index.js +6 -0
  26. package/dist-tsc/styles.d.ts +2 -0
  27. package/dist-tsc/styles.d.ts.map +1 -0
  28. package/{dist → dist-tsc}/styles.js +1 -1
  29. package/package.json +20 -11
  30. package/src/CellSetExpressionPlot.js +278 -0
  31. package/src/CellSetExpressionPlotOptions.js +73 -0
  32. package/src/CellSetExpressionPlotSubscriber.js +220 -0
  33. package/src/CellSetSizesPlot.js +173 -0
  34. package/src/CellSetSizesPlotSubscriber.js +151 -0
  35. package/src/ExpressionHistogram.js +120 -0
  36. package/src/ExpressionHistogramSubscriber.js +136 -0
  37. package/src/index.js +6 -0
  38. package/src/styles.js +9 -0
  39. package/dist/CellSetSizesPlot.js +0 -77
  40. package/dist/CellSetSizesPlotSubscriber.js +0 -44
  41. package/dist/DotPlot.js +0 -110
  42. package/dist/DotPlotSubscriber.js +0 -126
  43. package/dist/ExpressionHistogram.js +0 -49
@@ -1,10 +1,11 @@
1
1
  import { jsx as _jsx } from "react/jsx-runtime";
2
- import React, { useMemo } from 'react';
2
+ import React, { useMemo, useCallback, } from 'react';
3
3
  import { sum } from 'd3-array';
4
- import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useObsFeatureMatrixData, useFeatureSelection, registerPluginViewType, } from '@vitessce/vit-s';
4
+ import { TitleInfo, useCoordination, useLoaders, useUrls, useReady, useGridItemSize, useObsFeatureMatrixData, useFeatureSelection, } from '@vitessce/vit-s';
5
5
  import { ViewType, COMPONENT_COORDINATION_TYPES } from '@vitessce/constants-internal';
6
- import ExpressionHistogram from './ExpressionHistogram';
7
- import { useStyles } from './styles';
6
+ import { setObsSelection, getObsInfoFromDataWithinRange } from '@vitessce/sets-utils';
7
+ import ExpressionHistogram from './ExpressionHistogram.js';
8
+ import { useStyles } from './styles.js';
8
9
  /**
9
10
  * A subscriber component for `ExpressionHistogram`,
10
11
  * which listens for gene selection updates and
@@ -20,7 +21,7 @@ export function ExpressionHistogramSubscriber(props) {
20
21
  const classes = useStyles();
21
22
  const loaders = useLoaders();
22
23
  // Get "props" from the coordination space.
23
- const [{ dataset, obsType, featureType, featureValueType, featureSelection: geneSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.FEATURE_VALUE_HISTOGRAM], coordinationScopes);
24
+ const [{ dataset, obsType, featureType, featureValueType, featureSelection: geneSelection, additionalObsSets: additionalCellSets, obsSetColor: cellSetColor, }, { setAdditionalObsSets: setAdditionalCellSets, setObsSetColor: setCellSetColor, setObsColorEncoding: setCellColorEncoding, setObsSetSelection: setCellSetSelection, }] = useCoordination(COMPONENT_COORDINATION_TYPES[ViewType.FEATURE_VALUE_HISTOGRAM], coordinationScopes);
24
25
  const [width, height, containerRef] = useGridItemSize();
25
26
  const [urls, addUrl] = useUrls(loaders, dataset);
26
27
  // Get data from loaders using the data hooks.
@@ -38,11 +39,12 @@ export function ExpressionHistogramSubscriber(props) {
38
39
  // generate the array of data points for the histogram.
39
40
  const data = useMemo(() => {
40
41
  if (firstGeneSelected && obsFeatureMatrix && expressionData) {
41
- // Create new cellColors map based on the selected gene.
42
- return Array.from(expressionData[0]).map((_, index) => {
43
- const value = expressionData[0][index];
42
+ return obsIndex.map((cellId, cellIndex) => {
43
+ const value = expressionData[0][cellIndex];
44
+ // Create new cellColors map based on the selected gene.
44
45
  const normValue = value * 100 / 255;
45
- return { value: normValue, gene: firstGeneSelected };
46
+ const newItem = { value: normValue, gene: firstGeneSelected, cellId };
47
+ return newItem;
46
48
  });
47
49
  }
48
50
  if (obsFeatureMatrix) {
@@ -51,13 +53,18 @@ export function ExpressionHistogramSubscriber(props) {
51
53
  const values = obsFeatureMatrix.data
52
54
  .subarray(cellIndex * numGenes, (cellIndex + 1) * numGenes);
53
55
  const sumValue = sum(values) * 100 / 255;
54
- return { value: sumValue, gene: null };
56
+ const newItem = { value: sumValue, gene: null, cellId };
57
+ return newItem;
55
58
  });
56
59
  }
57
60
  return null;
58
61
  }, [obsIndex, featureIndex, obsFeatureMatrix, firstGeneSelected, expressionData]);
59
- return (_jsx(TitleInfo, { title: `Expression Histogram${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(ExpressionHistogram, { geneSelection: geneSelection, data: data, theme: theme, width: width, height: height }) }) }));
60
- }
61
- export function register() {
62
- registerPluginViewType(ViewType.FEATURE_VALUE_HISTOGRAM, ExpressionHistogramSubscriber, COMPONENT_COORDINATION_TYPES[ViewType.FEATURE_VALUE_HISTOGRAM]);
62
+ const onSelect = useCallback((value) => {
63
+ const geneName = firstGeneSelected ? [firstGeneSelected, 'values'].join(' ') : 'transcript count';
64
+ const selectedCellIds = getObsInfoFromDataWithinRange(value, data);
65
+ setObsSelection(selectedCellIds, additionalCellSets, cellSetColor, setCellSetSelection, setAdditionalCellSets, setCellSetColor, setCellColorEncoding, 'Selection ', `: based on ${geneName} in range [${value[0].toFixed(1)}, ${value[1].toFixed(1)}] `);
66
+ }, [additionalCellSets, cellSetColor, data, setAdditionalCellSets,
67
+ setCellColorEncoding, setCellSetColor, setCellSetSelection, firstGeneSelected,
68
+ ]);
69
+ return (_jsx(TitleInfo, { title: `Expression Histogram${(firstGeneSelected ? ` (${firstGeneSelected})` : '')}`, removeGridComponent: removeGridComponent, urls: urls, theme: theme, isReady: isReady, children: _jsx("div", { ref: containerRef, className: classes.vegaContainer, children: _jsx(ExpressionHistogram, { geneSelection: geneSelection, onSelect: onSelect, data: data, theme: theme, width: width, height: height }) }) }));
63
70
  }
@@ -0,0 +1,7 @@
1
+ export { CellSetExpressionPlotSubscriber } from "./CellSetExpressionPlotSubscriber.js";
2
+ export { CellSetSizesPlotSubscriber } from "./CellSetSizesPlotSubscriber.js";
3
+ export { ExpressionHistogramSubscriber } from "./ExpressionHistogramSubscriber.js";
4
+ export { default as CellSetSizesPlot } from "./CellSetSizesPlot.js";
5
+ export { default as CellSetExpressionPlot } from "./CellSetExpressionPlot.js";
6
+ export { default as ExpressionHistogram } from "./ExpressionHistogram.js";
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.js"],"names":[],"mappings":""}
@@ -0,0 +1,6 @@
1
+ export { CellSetExpressionPlotSubscriber } from './CellSetExpressionPlotSubscriber.js';
2
+ export { CellSetSizesPlotSubscriber } from './CellSetSizesPlotSubscriber.js';
3
+ export { ExpressionHistogramSubscriber } from './ExpressionHistogramSubscriber.js';
4
+ export { default as CellSetSizesPlot } from './CellSetSizesPlot.js';
5
+ export { default as CellSetExpressionPlot } from './CellSetExpressionPlot.js';
6
+ export { default as ExpressionHistogram } from './ExpressionHistogram.js';
@@ -0,0 +1,2 @@
1
+ export const useStyles: (props?: any) => import("@material-ui/core/styles/withStyles").ClassNameMap<"vegaContainer">;
2
+ //# sourceMappingURL=styles.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"styles.d.ts","sourceRoot":"","sources":["../src/styles.js"],"names":[],"mappings":"AAEA,qHAMI"}
@@ -1,4 +1,4 @@
1
- import { makeStyles } from '@material-ui/core/styles';
1
+ import { makeStyles } from '@material-ui/core';
2
2
  export const useStyles = makeStyles(() => ({
3
3
  vegaContainer: {
4
4
  display: 'flex',
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@vitessce/statistical-plots",
3
- "version": "2.0.3-beta.0",
3
+ "version": "3.0.0",
4
4
  "author": "Gehlenborg Lab",
5
5
  "homepage": "http://vitessce.io",
6
6
  "repository": {
@@ -8,31 +8,40 @@
8
8
  "url": "git+https://github.com/vitessce/vitessce.git"
9
9
  },
10
10
  "license": "MIT",
11
+ "type": "module",
11
12
  "main": "dist/index.js",
12
13
  "files": [
13
- "dist"
14
+ "src",
15
+ "dist",
16
+ "dist-tsc"
14
17
  ],
15
18
  "dependencies": {
16
19
  "@material-ui/core": "~4.12.3",
17
20
  "d3-array": "^2.4.0",
18
- "lodash": "^4.17.21",
19
- "@vitessce/utils": "2.0.3-beta.0",
20
- "@vitessce/sets-utils": "2.0.3-beta.0",
21
- "@vitessce/vit-s": "2.0.3-beta.0",
22
- "@vitessce/vega": "2.0.3-beta.0",
23
- "@vitessce/constants-internal": "2.0.3-beta.0"
21
+ "lodash-es": "^4.17.21",
22
+ "@vitessce/constants-internal": "3.0.0",
23
+ "@vitessce/sets-utils": "3.0.0",
24
+ "@vitessce/utils": "3.0.0",
25
+ "@vitessce/vega": "3.0.0",
26
+ "@vitessce/vit-s": "3.0.0"
24
27
  },
25
28
  "devDependencies": {
26
29
  "react": "^18.0.0",
27
- "vite": "^3.0.0",
30
+ "vite": "^4.3.0",
28
31
  "vitest": "^0.23.4"
29
32
  },
30
33
  "peerDependencies": {
31
34
  "react": "^16.8.0 || ^17.0.0 || ^18.0.0"
32
35
  },
33
36
  "scripts": {
34
- "start": "tsc --watch",
35
- "build": "tsc",
37
+ "bundle": "pnpm exec vite build -c ../../../scripts/vite.config.js",
36
38
  "test": "pnpm exec vitest --run -r ../../../ --dir ."
39
+ },
40
+ "module": "dist/index.js",
41
+ "exports": {
42
+ ".": {
43
+ "types": "./dist-tsc/index.d.ts",
44
+ "import": "./dist/index.js"
45
+ }
37
46
  }
38
47
  }
@@ -0,0 +1,278 @@
1
+ import React from 'react';
2
+ import { clamp } from 'lodash-es';
3
+ import { VegaPlot, VEGA_THEMES, DATASET_NAME } from '@vitessce/vega';
4
+ import { colorArrayToString } from '@vitessce/sets-utils';
5
+ import { capitalize } from '@vitessce/utils';
6
+
7
+ /**
8
+ * Gene expression histogram displayed as a bar chart,
9
+ * implemented with the VegaPlot component.
10
+ * @param {object} props
11
+ * @param {object[]} props.data The expression data, an array
12
+ * of objects with properties `value`, `gene`, and `set`.
13
+ * @param {number} props.domainMax The maximum gene expression value.
14
+ * @param {object[]} props.colors An object for each
15
+ * cell set, with properties `name` and `color`.
16
+ * @param {string} props.theme The name of the current Vitessce theme.
17
+ * @param {number} props.width The container width.
18
+ * @param {number} props.height The container height.
19
+ * @param {number} props.marginRight The size of the margin
20
+ * on the right side of the plot, to account for the vega menu button.
21
+ * By default, 90.
22
+ * @param {number} props.marginBottom The size of the margin
23
+ * on the bottom of the plot, to account for long x-axis labels.
24
+ * Default is allowing the component to automatically determine the margin.
25
+ * @param {string|null} props.featureValueTransformName A name
26
+ * for the feature value transformation function.
27
+ */
28
+ export default function CellSetExpressionPlot(props) {
29
+ const {
30
+ domainMax = 100,
31
+ colors,
32
+ data,
33
+ theme,
34
+ width,
35
+ height,
36
+ marginRight = 90,
37
+ marginBottom,
38
+ obsType,
39
+ featureValueType,
40
+ featureValueTransformName,
41
+ } = props;
42
+ // Get the max characters in an axis label for autsizing the bottom margin.
43
+ const maxCharactersForLabel = data.reduce((acc, val) => {
44
+ // eslint-disable-next-line no-param-reassign
45
+ acc = acc === undefined || val.set.length > acc ? val.set.length : acc;
46
+ return acc;
47
+ }, 0);
48
+ // Use a square-root term because the angle of the labels is 45 degrees (see below)
49
+ // so the perpendicular distance to the bottom of the labels is proportional to the
50
+ // square root of the length of the labels along the imaginary hypotenuse.
51
+ // 30 is an estimate of the pixel size of a given character and seems to work well.
52
+ const autoMarginBottom = marginBottom
53
+ || 30 + Math.sqrt(maxCharactersForLabel / 2) * 30;
54
+ // Manually set the color scale so that Vega-Lite does
55
+ // not choose the colors automatically.
56
+ const colorScale = {
57
+ domain: colors.map(d => d.name),
58
+ range: colors.map(d => colorArrayToString(d.color)),
59
+ };
60
+
61
+ const plotWidth = clamp(width - marginRight, 10, Infinity);
62
+ const plotHeight = clamp(height - autoMarginBottom, 10, Infinity);
63
+
64
+ const numBands = colors.length;
65
+ const bandWidth = plotWidth / numBands;
66
+
67
+ const rectColor = (theme === 'dark' ? 'white' : 'black');
68
+
69
+ const spec = {
70
+ $schema: 'https://vega.github.io/schema/vega/v5.json',
71
+ description: `A violin plot showing distributions of expression levels for selected ${obsType} sets.`,
72
+ width: plotWidth,
73
+ height: plotHeight,
74
+ config: {
75
+ ...VEGA_THEMES[theme],
76
+ axisBand: {
77
+ bandPosition: 1,
78
+ tickExtra: true,
79
+ tickOffset: 0,
80
+ },
81
+ },
82
+
83
+ signals: [
84
+ { name: 'bandWidth', value: bandWidth },
85
+ { name: 'width', value: plotWidth },
86
+ { name: 'height', value: plotHeight },
87
+ { name: 'trim', value: true },
88
+ ],
89
+
90
+ data: [
91
+ {
92
+ name: 'density',
93
+ source: DATASET_NAME,
94
+ transform: [
95
+ {
96
+ type: 'kde',
97
+ field: 'value',
98
+ groupby: ['set'],
99
+ bandwidth: 0,
100
+ extent: [0, domainMax],
101
+ },
102
+ ],
103
+ },
104
+ {
105
+ name: 'stats',
106
+ source: DATASET_NAME,
107
+ transform: [
108
+ {
109
+ type: 'aggregate',
110
+ groupby: ['set'],
111
+ fields: ['value', 'value', 'value'],
112
+ ops: ['q1', 'median', 'q3'],
113
+ as: ['q1', 'median', 'q3'],
114
+ },
115
+ ],
116
+ },
117
+ ],
118
+
119
+ scales: [
120
+ {
121
+ name: 'layout',
122
+ type: 'band',
123
+ range: 'width',
124
+ domain: { data: DATASET_NAME, field: 'set' },
125
+ },
126
+ {
127
+ name: 'yscale',
128
+ type: 'linear',
129
+ range: 'height',
130
+ domain: [0, domainMax],
131
+ },
132
+ {
133
+ name: 'wscale',
134
+ type: 'linear',
135
+ range: [0, { signal: 'bandWidth' }],
136
+ domain: { data: 'density', field: 'density' },
137
+ },
138
+ {
139
+ name: 'wscaleReversed',
140
+ type: 'linear',
141
+ reverse: true,
142
+ range: [0, { signal: 'bandWidth' }],
143
+ domain: { data: 'density', field: 'density' },
144
+ },
145
+ {
146
+ name: 'color',
147
+ type: 'ordinal',
148
+ ...colorScale,
149
+ },
150
+ ],
151
+
152
+ axes: [
153
+ {
154
+ orient: 'left',
155
+ scale: 'yscale',
156
+ zindex: 1,
157
+ title: (featureValueTransformName && featureValueTransformName !== 'None')
158
+ ? [`${featureValueTransformName}-Transformed`, `${capitalize(featureValueType)} Values`]
159
+ : `${capitalize(featureValueType)} Values`,
160
+ },
161
+ {
162
+ orient: 'bottom',
163
+ scale: 'layout',
164
+ tickCount: 5,
165
+ zindex: 1,
166
+ title: `${capitalize(obsType)} Set`,
167
+ labelAngle: -45,
168
+ labelAlign: 'right',
169
+ },
170
+ ],
171
+
172
+ marks: [
173
+ {
174
+ type: 'group',
175
+ from: {
176
+ facet: {
177
+ data: 'density',
178
+ name: 'violin',
179
+ groupby: 'set',
180
+ },
181
+ },
182
+
183
+ encode: {
184
+ enter: {
185
+ xc: { scale: 'layout', field: 'set', band: 0.5 },
186
+ width: { signal: 'bandWidth' },
187
+ height: { signal: 'height' },
188
+ },
189
+ },
190
+
191
+ data: [
192
+ {
193
+ name: 'summary',
194
+ source: 'stats',
195
+ transform: [
196
+ {
197
+ type: 'filter',
198
+ expr: 'datum.set === parent.set',
199
+ },
200
+ ],
201
+ },
202
+ ],
203
+
204
+ marks: [
205
+ {
206
+ type: 'area',
207
+ orient: 'vertical',
208
+ from: { data: 'violin' },
209
+ encode: {
210
+ enter: {
211
+ fill: { scale: 'color', field: { parent: 'set' } },
212
+ },
213
+ update: {
214
+ width: { scale: 'wscale', field: 'density' },
215
+ xc: { signal: 'bandWidth / 2' },
216
+ y2: { scale: 'yscale', field: 'value' },
217
+ y: { scale: 'yscale', value: 0 },
218
+ },
219
+ },
220
+ },
221
+ {
222
+ type: 'area',
223
+ orient: 'vertical',
224
+ from: { data: 'violin' },
225
+ encode: {
226
+ enter: {
227
+ fill: { scale: 'color', field: { parent: 'set' } },
228
+ },
229
+ update: {
230
+ width: { scale: 'wscaleReversed', field: 'density' },
231
+ xc: { signal: 'bandWidth' },
232
+ y2: { scale: 'yscale', field: 'value' },
233
+ y: { scale: 'yscale', value: 0 },
234
+ },
235
+ },
236
+ },
237
+ {
238
+ type: 'rect',
239
+ from: { data: 'summary' },
240
+ encode: {
241
+ enter: {
242
+ fill: { value: rectColor },
243
+ width: { value: 2 },
244
+ },
245
+ update: {
246
+ y: { scale: 'yscale', field: 'q1' },
247
+ y2: { scale: 'yscale', field: 'q3' },
248
+ xc: { signal: 'bandWidth / 2' },
249
+ },
250
+ },
251
+ },
252
+ {
253
+ type: 'rect',
254
+ from: { data: 'summary' },
255
+ encode: {
256
+ enter: {
257
+ fill: { value: rectColor },
258
+ height: { value: 2 },
259
+ width: { value: 8 },
260
+ },
261
+ update: {
262
+ y: { scale: 'yscale', field: 'median' },
263
+ xc: { signal: 'bandWidth / 2' },
264
+ },
265
+ },
266
+ },
267
+ ],
268
+ },
269
+ ],
270
+ };
271
+
272
+ return (
273
+ <VegaPlot
274
+ data={data}
275
+ spec={spec}
276
+ />
277
+ );
278
+ }
@@ -0,0 +1,73 @@
1
+ import React from 'react';
2
+ import { TableCell, TableRow, TextField } from '@material-ui/core';
3
+ import { usePlotOptionsStyles, OptionsContainer, OptionSelect } from '@vitessce/vit-s';
4
+
5
+ export default function CellSetExpressionPlotOptions(props) {
6
+ const {
7
+ featureValueTransform,
8
+ setFeatureValueTransform,
9
+ featureValueTransformCoefficient,
10
+ setFeatureValueTransformCoefficient,
11
+ transformOptions,
12
+ } = props;
13
+ const classes = usePlotOptionsStyles();
14
+
15
+ const handleTransformChange = (event) => {
16
+ setFeatureValueTransform(event.target.value === '' ? null : event.target.value);
17
+ };
18
+
19
+ // Feels a little hacky, but I think this is the best way to handle
20
+ // the limitations of the v4 material-ui number input.
21
+ const handleTransformCoefficientChange = (event) => {
22
+ const { value } = event.target;
23
+ if (!value) {
24
+ setFeatureValueTransformCoefficient(value);
25
+ } else {
26
+ const newCoefficient = Number(value);
27
+ if (!Number.isNaN(newCoefficient) && newCoefficient >= 0) {
28
+ setFeatureValueTransformCoefficient(value);
29
+ }
30
+ }
31
+ };
32
+
33
+ return (
34
+ <OptionsContainer>
35
+ <TableRow>
36
+ <TableCell className={classes.labelCell}>Transform</TableCell>
37
+ <TableCell className={classes.inputCell}>
38
+ <OptionSelect
39
+ key="gating-transform-select"
40
+ className={classes.select}
41
+ value={featureValueTransform === null ? '' : featureValueTransform}
42
+ onChange={handleTransformChange}
43
+ inputProps={{
44
+ id: 'scatterplot-transform-select',
45
+ }}
46
+ >
47
+ {transformOptions.map(opt => (
48
+ <option key={opt.name} value={opt.value === null ? '' : opt.value}>
49
+ {opt.name}
50
+ </option>
51
+ ))}
52
+ </OptionSelect>
53
+ </TableCell>
54
+ </TableRow>
55
+ <TableRow key="transform-coefficient-option-row">
56
+ <TableCell className={classes.labelCell}>
57
+ Transform Coefficient
58
+ </TableCell>
59
+ <TableCell className={classes.inputCell}>
60
+ <TextField
61
+ label="Number"
62
+ type="number"
63
+ onChange={handleTransformCoefficientChange}
64
+ value={featureValueTransformCoefficient}
65
+ InputLabelProps={{
66
+ shrink: true,
67
+ }}
68
+ />
69
+ </TableCell>
70
+ </TableRow>
71
+ </OptionsContainer>
72
+ );
73
+ }