dce-reactkit 1.0.20 → 2.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.
- package/.eslintrc.js +95 -0
- package/lib/components/LoadingSpinner.d.ts +7 -0
- package/lib/components/LoadingSpinner.js +39 -0
- package/lib/components/LoadingSpinner.js.map +1 -0
- package/lib/components/Modal.d.ts +69 -0
- package/lib/components/Modal.js +311 -0
- package/lib/components/Modal.js.map +1 -0
- package/lib/helpers/abbreviate.d.ts +9 -0
- package/lib/helpers/abbreviate.js +21 -0
- package/lib/helpers/abbreviate.js.map +1 -0
- package/lib/helpers/avg.d.ts +8 -0
- package/lib/helpers/avg.js +24 -0
- package/lib/helpers/avg.js.map +1 -0
- package/lib/helpers/ceilToNumDecimals.d.ts +9 -0
- package/lib/helpers/ceilToNumDecimals.js +15 -0
- package/lib/helpers/ceilToNumDecimals.js.map +1 -0
- package/lib/helpers/floorToNumDecimals.d.ts +9 -0
- package/lib/helpers/floorToNumDecimals.js +15 -0
- package/lib/helpers/floorToNumDecimals.js.map +1 -0
- package/lib/helpers/forceNumIntoBounds.d.ts +10 -0
- package/lib/helpers/forceNumIntoBounds.js +15 -0
- package/lib/helpers/forceNumIntoBounds.js.map +1 -0
- package/lib/helpers/padDecimalZeros.d.ts +10 -0
- package/lib/helpers/padDecimalZeros.js +30 -0
- package/lib/helpers/padDecimalZeros.js.map +1 -0
- package/lib/helpers/padZerosLeft.d.ts +9 -0
- package/lib/helpers/padZerosLeft.js +21 -0
- package/lib/helpers/padZerosLeft.js.map +1 -0
- package/lib/helpers/roundToNumDecimals.d.ts +9 -0
- package/lib/helpers/roundToNumDecimals.js +15 -0
- package/lib/helpers/roundToNumDecimals.js.map +1 -0
- package/lib/helpers/sum.d.ts +8 -0
- package/lib/helpers/sum.js +15 -0
- package/lib/helpers/sum.js.map +1 -0
- package/lib/helpers/waitMs.d.ts +7 -0
- package/lib/helpers/waitMs.js +55 -0
- package/lib/helpers/waitMs.js.map +1 -0
- package/lib/types/Variant.d.ts +15 -0
- package/lib/types/Variant.js +19 -0
- package/lib/types/Variant.js.map +1 -0
- package/package.json +22 -40
- package/src/components/LoadingSpinner.scss +50 -0
- package/src/components/LoadingSpinner.tsx +50 -0
- package/src/components/Modal.scss +48 -0
- package/src/components/Modal.tsx +391 -0
- package/src/helpers/abbreviate.tsx +23 -0
- package/src/helpers/avg.tsx +22 -0
- package/src/helpers/ceilToNumDecimals.tsx +13 -0
- package/src/helpers/floorToNumDecimals.tsx +13 -0
- package/src/helpers/forceNumIntoBounds.tsx +19 -0
- package/src/helpers/padDecimalZeros.tsx +32 -0
- package/src/helpers/padZerosLeft.tsx +21 -0
- package/src/helpers/roundToNumDecimals.tsx +13 -0
- package/src/helpers/sum.tsx +16 -0
- package/src/helpers/waitMs.tsx +12 -0
- package/src/types/Variant.tsx +16 -0
- package/tsconfig.json +24 -0
- package/index.js +0 -1
- package/lib/Bar.js +0 -169
- package/lib/Donut.js +0 -145
- package/lib/DownloadButton.js +0 -81
- package/lib/Histogram.js +0 -193
- package/lib/Scatter.js +0 -162
- package/lib/StackedBar.js +0 -243
- package/lib/common/chart/Holder/index.js +0 -182
- package/lib/common/chart/Holder/style.css +0 -41
- package/lib/common/chart/colors.js +0 -12
- package/lib/common/csvToString.js +0 -23
- package/lib/common/isFloat.js +0 -14
- package/lib/common/isInteger.js +0 -24
- package/lib/common/math.js +0 -21
- package/lib/common/validators.js +0 -18
- package/lib/glyph/Adjust.js +0 -13
- package/lib/glyph/Angle.js +0 -44
- package/lib/glyph/Archive.js +0 -13
- package/lib/glyph/Arrow.js +0 -49
- package/lib/glyph/ArrowsHorizontal.js +0 -13
- package/lib/glyph/ArrowsVertical.js +0 -13
- package/lib/glyph/Award.js +0 -13
- package/lib/glyph/Ban.js +0 -13
- package/lib/glyph/Barcode.js +0 -13
- package/lib/glyph/Bed.js +0 -13
- package/lib/glyph/Bell.js +0 -35
- package/lib/glyph/Bolt.js +0 -13
- package/lib/glyph/Bookmark.js +0 -37
- package/lib/glyph/Bug.js +0 -13
- package/lib/glyph/Calendar.js +0 -13
- package/lib/glyph/Check.js +0 -36
- package/lib/glyph/Checkbox.js +0 -40
- package/lib/glyph/Chevron.js +0 -63
- package/lib/glyph/Circle.js +0 -15
- package/lib/glyph/Cog.js +0 -13
- package/lib/glyph/DoubleAngle.js +0 -45
- package/lib/glyph/Download.js +0 -13
- package/lib/glyph/Ellipsis.js +0 -36
- package/lib/glyph/Envelope.js +0 -13
- package/lib/glyph/File.js +0 -37
- package/lib/glyph/IdCard.js +0 -13
- package/lib/glyph/Menu.js +0 -13
- package/lib/glyph/Minus.js +0 -13
- package/lib/glyph/Move.js +0 -13
- package/lib/glyph/Plus.js +0 -13
- package/lib/glyph/Square.js +0 -47
- package/lib/glyph/Teacher.js +0 -13
- package/lib/glyph/TextAlign.js +0 -53
- package/lib/glyph/Times.js +0 -36
- package/lib/glyph/WarningTriangle.js +0 -13
- package/lib/index.js +0 -1
- package/src-old/common/csvToString.js +0 -5
- package/src-old/common/isFloat.js +0 -3
- package/src-old/common/isInteger.js +0 -14
- package/src-old/common/math.js +0 -16
- package/src-old/common/validators.js +0 -14
- package/src-old/components/Alert.js +0 -79
- package/src-old/components/Button.js +0 -64
- package/src-old/components/CSV/autoMatch.js +0 -174
- package/src-old/components/CSV/getNumCols.js +0 -15
- package/src-old/components/CSV/index.js +0 -743
- package/src-old/components/Card.js +0 -64
- package/src-old/components/Chart/Bar.js +0 -169
- package/src-old/components/Chart/Donut.js +0 -160
- package/src-old/components/Chart/Histogram.js +0 -189
- package/src-old/components/Chart/Scatter.js +0 -162
- package/src-old/components/Chart/colors.js +0 -20
- package/src-old/components/Checkbox/index.js +0 -106
- package/src-old/components/Checkbox/style.css +0 -11
- package/src-old/components/DatePicker.js +0 -59
- package/src-old/components/DownloadButton.js +0 -72
- package/src-old/components/Dropdown.js +0 -120
- package/src-old/components/Layout/LeftAddon.js +0 -63
- package/src-old/components/Layout/RightAddon.js +0 -64
- package/src-old/components/Layout/TwoCol.js +0 -67
- package/src-old/components/Modal.js +0 -161
- package/src-old/components/PhotoRoster.js +0 -152
- package/src-old/components/Pills.js +0 -117
- package/src-old/components/Radio.js +0 -94
- package/src-old/components/Splash/Error.js +0 -62
- package/src-old/components/Splash/Load/index.js +0 -32
- package/src-old/components/Splash/Load/style.css +0 -51
- package/src-old/components/Stat/General.js +0 -252
- package/src-old/components/Stat/helpers/calcAverage.js +0 -20
- package/src-old/components/Stat/helpers/calcExtremes.js +0 -27
- package/src-old/components/Stat/helpers/calcMedian.js +0 -32
- package/src-old/components/Stat/helpers/calcStdDev.js +0 -30
- package/src-old/components/Stat/helpers/calcSum.js +0 -8
- package/src-old/components/UserProfile/index.js +0 -151
- package/src-old/components/UserProfile/style.css +0 -33
- package/src-old/components/Well.js +0 -28
- package/src-old/components/Wizard/Question/CSV.js +0 -307
- package/src-old/components/Wizard/Question/Chooser/Assignment.js +0 -108
- package/src-old/components/Wizard/Question/Chooser/Course.js +0 -214
- package/src-old/components/Wizard/Question/Chooser/helpers/divideIntoSections.js +0 -16
- package/src-old/components/Wizard/Question/Chooser/helpers/genGetItemsPromise.js +0 -20
- package/src-old/components/Wizard/Question/Chooser/helpers/preprocessSections.js +0 -36
- package/src-old/components/Wizard/Question/Chooser/index.js +0 -306
- package/src-old/components/Wizard/Question/Form/Field/CheckableItem.js +0 -155
- package/src-old/components/Wizard/Question/Form/Field/Input.js +0 -149
- package/src-old/components/Wizard/Question/Form/Field/NumberItem.js +0 -374
- package/src-old/components/Wizard/Question/Form/Field/index.js +0 -34
- package/src-old/components/Wizard/Question/Form/Field/readme.md +0 -7
- package/src-old/components/Wizard/Question/Form/index.js +0 -175
- package/src-old/components/Wizard/Question/Form/style.css +0 -3
- package/src-old/components/Wizard/Question/OneButton.js +0 -80
- package/src-old/components/Wizard/Question/index.js +0 -46
- package/src-old/components/Wizard/Question/readme.md +0 -10
- package/src-old/components/Wizard/QuestionGroup/AnswerAll.js +0 -126
- package/src-old/components/Wizard/QuestionGroup/AnswerAny.js +0 -60
- package/src-old/components/Wizard/index.js +0 -233
- package/src-old/components/Wizard/style.css +0 -21
- package/src-old/glyphs/Adjust.js +0 -13
- package/src-old/glyphs/Angle.js +0 -57
- package/src-old/glyphs/Archive.js +0 -13
- package/src-old/glyphs/Arrow.js +0 -85
- package/src-old/glyphs/ArrowsHorizontal.js +0 -13
- package/src-old/glyphs/ArrowsVertical.js +0 -13
- package/src-old/glyphs/Award.js +0 -13
- package/src-old/glyphs/Ban.js +0 -13
- package/src-old/glyphs/Barcode.js +0 -13
- package/src-old/glyphs/Bed.js +0 -13
- package/src-old/glyphs/Bell.js +0 -34
- package/src-old/glyphs/Bolt.js +0 -13
- package/src-old/glyphs/Bookmark.js +0 -36
- package/src-old/glyphs/Bug.js +0 -13
- package/src-old/glyphs/Calendar.js +0 -13
- package/src-old/glyphs/Check.js +0 -34
- package/src-old/glyphs/Checkbox.js +0 -42
- package/src-old/glyphs/Chevron.js +0 -88
- package/src-old/glyphs/Circle.js +0 -16
- package/src-old/glyphs/Cog.js +0 -13
- package/src-old/glyphs/DoubleAngle.js +0 -58
- package/src-old/glyphs/Download.js +0 -13
- package/src-old/glyphs/Ellipsis.js +0 -35
- package/src-old/glyphs/Envelope.js +0 -13
- package/src-old/glyphs/File.js +0 -40
- package/src-old/glyphs/IdCard.js +0 -13
- package/src-old/glyphs/Menu.js +0 -13
- package/src-old/glyphs/Minus.js +0 -13
- package/src-old/glyphs/Move.js +0 -13
- package/src-old/glyphs/Plus.js +0 -13
- package/src-old/glyphs/Square.js +0 -48
- package/src-old/glyphs/Teacher.js +0 -13
- package/src-old/glyphs/TextAlign.js +0 -58
- package/src-old/glyphs/Times.js +0 -34
- package/src-old/glyphs/WarningTriangle.js +0 -13
- package/src-old/index.js +0 -1
|
@@ -1,743 +0,0 @@
|
|
|
1
|
-
import React, { Component } from 'react';
|
|
2
|
-
import PropTypes from 'prop-types';
|
|
3
|
-
import genUUID from 'uuid/v1';
|
|
4
|
-
|
|
5
|
-
// Import bootstrap componenets
|
|
6
|
-
import { Table } from 'reactstrap';
|
|
7
|
-
// Import bootstrap stylesheet
|
|
8
|
-
import 'bootstrap/dist/css/bootstrap.min.css';
|
|
9
|
-
|
|
10
|
-
// Import other components
|
|
11
|
-
import Alert from '../Alert';
|
|
12
|
-
import LeftAddon from '../Layout/LeftAddon';
|
|
13
|
-
import RightAddon from '../Layout/RightAddon';
|
|
14
|
-
import Button from '../Button';
|
|
15
|
-
import Dropdown from '../Dropdown';
|
|
16
|
-
import DownloadButton from '../DownloadButton';
|
|
17
|
-
|
|
18
|
-
// Import glyphs
|
|
19
|
-
import Ellipsis from '../../glyphs/Ellipsis';
|
|
20
|
-
import Times from '../../glyphs/Times';
|
|
21
|
-
import Chevron from '../../glyphs/Chevron';
|
|
22
|
-
import Checkbox from '../../glyphs/Checkbox';
|
|
23
|
-
import Check from '../../glyphs/Check';
|
|
24
|
-
|
|
25
|
-
// Import helper functions
|
|
26
|
-
import autoMatch from './autoMatch';
|
|
27
|
-
import getNumCols from './getNumCols';
|
|
28
|
-
// Import script for stringifying csv for download
|
|
29
|
-
import csvToString from '../../common/csvToString';
|
|
30
|
-
|
|
31
|
-
class CSV extends Component {
|
|
32
|
-
constructor(props) {
|
|
33
|
-
super(props);
|
|
34
|
-
|
|
35
|
-
// Pre-process CSV to determine which columns can be matched (use validator)
|
|
36
|
-
const {
|
|
37
|
-
headersToMatch,
|
|
38
|
-
csv,
|
|
39
|
-
} = this.props;
|
|
40
|
-
|
|
41
|
-
// Skip processing if we have no work to do
|
|
42
|
-
const noCSV = (
|
|
43
|
-
!csv
|
|
44
|
-
|| !csv.data
|
|
45
|
-
|| csv.data.length === 0
|
|
46
|
-
);
|
|
47
|
-
if (!headersToMatch || noCSV) {
|
|
48
|
-
this.state = {
|
|
49
|
-
noCSV,
|
|
50
|
-
};
|
|
51
|
-
return;
|
|
52
|
-
}
|
|
53
|
-
|
|
54
|
-
// Perform auto-matching
|
|
55
|
-
const {
|
|
56
|
-
allowedHeaders,
|
|
57
|
-
columnMapping,
|
|
58
|
-
} = autoMatch(csv, headersToMatch);
|
|
59
|
-
this.allowedHeaders = allowedHeaders;
|
|
60
|
-
|
|
61
|
-
// Save headerKey map for easier lookup
|
|
62
|
-
this.headerKeyToHeader = {};
|
|
63
|
-
headersToMatch.forEach((header) => {
|
|
64
|
-
this.headerKeyToHeader[header.key] = header;
|
|
65
|
-
});
|
|
66
|
-
|
|
67
|
-
// Save boolean (auto matching occurred)
|
|
68
|
-
this.autoMatchingOccurred = Object.keys(columnMapping).length > 0;
|
|
69
|
-
|
|
70
|
-
// Save state
|
|
71
|
-
this.state = {
|
|
72
|
-
columnMapping,
|
|
73
|
-
};
|
|
74
|
-
|
|
75
|
-
// Bind functions
|
|
76
|
-
this.matchSelected = this.matchSelected.bind(this);
|
|
77
|
-
this.onDoneMatchingClicked = this.onDoneMatchingClicked.bind(this);
|
|
78
|
-
this.unmatch = this.unmatch.bind(this);
|
|
79
|
-
}
|
|
80
|
-
|
|
81
|
-
onDoneMatchingClicked() {
|
|
82
|
-
// Deconstruct props
|
|
83
|
-
const {
|
|
84
|
-
csv,
|
|
85
|
-
onFinished,
|
|
86
|
-
} = this.props;
|
|
87
|
-
|
|
88
|
-
// Skip if no listener
|
|
89
|
-
if (!onFinished) {
|
|
90
|
-
return;
|
|
91
|
-
}
|
|
92
|
-
|
|
93
|
-
// Deconstruct state
|
|
94
|
-
const { columnMapping } = this.state;
|
|
95
|
-
|
|
96
|
-
// Post-process csv
|
|
97
|
-
const headerKeyToColumnIndex = {};
|
|
98
|
-
Object.keys(columnMapping).forEach((cIndex) => {
|
|
99
|
-
headerKeyToColumnIndex[columnMapping[cIndex]] = cIndex;
|
|
100
|
-
});
|
|
101
|
-
const matchedHeaderKeys = Object.values(columnMapping);
|
|
102
|
-
// Process each row, turning it into an object (with headerKeys as props)
|
|
103
|
-
const rowObjects = csv.data.map((datum) => {
|
|
104
|
-
const rowObject = {};
|
|
105
|
-
matchedHeaderKeys.forEach((headerKey) => {
|
|
106
|
-
const cIndex = headerKeyToColumnIndex[headerKey];
|
|
107
|
-
const datumKey = (
|
|
108
|
-
csv.headers
|
|
109
|
-
? csv.headers[cIndex]
|
|
110
|
-
: cIndex
|
|
111
|
-
);
|
|
112
|
-
rowObject[headerKey] = datum[datumKey];
|
|
113
|
-
});
|
|
114
|
-
return rowObject;
|
|
115
|
-
});
|
|
116
|
-
const newCSV = {
|
|
117
|
-
headers: matchedHeaderKeys,
|
|
118
|
-
data: rowObjects,
|
|
119
|
-
};
|
|
120
|
-
|
|
121
|
-
onFinished(newCSV);
|
|
122
|
-
}
|
|
123
|
-
|
|
124
|
-
unmatch(cIndex) {
|
|
125
|
-
// Get previous column mapping
|
|
126
|
-
const { columnMapping } = this.state;
|
|
127
|
-
|
|
128
|
-
// Update column mapping
|
|
129
|
-
delete columnMapping[cIndex];
|
|
130
|
-
|
|
131
|
-
// Save state
|
|
132
|
-
this.setState({
|
|
133
|
-
columnMapping,
|
|
134
|
-
});
|
|
135
|
-
}
|
|
136
|
-
|
|
137
|
-
matchSelected(cIndex, headerKey) {
|
|
138
|
-
// Get previous column mapping
|
|
139
|
-
const { columnMapping } = this.state;
|
|
140
|
-
|
|
141
|
-
// Unmatch other column that are matched with this headerKey (if applicable)
|
|
142
|
-
const cIndices = Object.keys(columnMapping);
|
|
143
|
-
for (let i = 0; i < cIndices.length; i++) {
|
|
144
|
-
if (columnMapping[cIndices[i]] === headerKey) {
|
|
145
|
-
// Unmatch this column
|
|
146
|
-
delete columnMapping[cIndices[i]];
|
|
147
|
-
break;
|
|
148
|
-
}
|
|
149
|
-
}
|
|
150
|
-
|
|
151
|
-
// Save new mapping
|
|
152
|
-
columnMapping[cIndex] = headerKey;
|
|
153
|
-
this.setState({
|
|
154
|
-
columnMapping,
|
|
155
|
-
changesMade: true,
|
|
156
|
-
});
|
|
157
|
-
}
|
|
158
|
-
|
|
159
|
-
render() {
|
|
160
|
-
// Detect "no csv" error
|
|
161
|
-
const { noCSV } = this.state;
|
|
162
|
-
if (noCSV) {
|
|
163
|
-
// No data
|
|
164
|
-
return (
|
|
165
|
-
<Alert
|
|
166
|
-
color="warning"
|
|
167
|
-
>
|
|
168
|
-
<h3>This CSV file is empty</h3>
|
|
169
|
-
Unfortunately, we can't load this CSV file because it was empty.
|
|
170
|
-
</Alert>
|
|
171
|
-
);
|
|
172
|
-
}
|
|
173
|
-
|
|
174
|
-
// Deconstruct properties
|
|
175
|
-
const {
|
|
176
|
-
csv,
|
|
177
|
-
noHeader,
|
|
178
|
-
numRowsToPreview,
|
|
179
|
-
onReupload,
|
|
180
|
-
headersToMatch,
|
|
181
|
-
downloadFilename,
|
|
182
|
-
} = this.props;
|
|
183
|
-
|
|
184
|
-
// Get current header mapping
|
|
185
|
-
const {
|
|
186
|
-
columnMapping,
|
|
187
|
-
impossible,
|
|
188
|
-
changesMade,
|
|
189
|
-
} = this.state;
|
|
190
|
-
|
|
191
|
-
// Display error if matching is impossible
|
|
192
|
-
if (impossible) {
|
|
193
|
-
const requiredColumns = headersToMatch.map((header) => {
|
|
194
|
-
return (
|
|
195
|
-
<li key={genUUID()}>
|
|
196
|
-
<strong>{header.title}</strong>
|
|
197
|
-
{header.description && (
|
|
198
|
-
<span>
|
|
199
|
-
–
|
|
200
|
-
<em>{header.description}</em>
|
|
201
|
-
</span>
|
|
202
|
-
)}
|
|
203
|
-
</li>
|
|
204
|
-
);
|
|
205
|
-
});
|
|
206
|
-
return (
|
|
207
|
-
<Alert
|
|
208
|
-
color="danger"
|
|
209
|
-
className="text-center"
|
|
210
|
-
>
|
|
211
|
-
<h3>
|
|
212
|
-
This file doesn't match our requirements
|
|
213
|
-
</h3>
|
|
214
|
-
You need a column for each of the following types of info:
|
|
215
|
-
<div>
|
|
216
|
-
<div className="text-left d-inline-block">
|
|
217
|
-
<ul>
|
|
218
|
-
{requiredColumns}
|
|
219
|
-
</ul>
|
|
220
|
-
</div>
|
|
221
|
-
</div>
|
|
222
|
-
<Button
|
|
223
|
-
text={(
|
|
224
|
-
<span>
|
|
225
|
-
<Chevron direction="left" className="mr-2" />
|
|
226
|
-
Back
|
|
227
|
-
</span>
|
|
228
|
-
)}
|
|
229
|
-
onClick={onReupload}
|
|
230
|
-
color="secondary"
|
|
231
|
-
size="lg"
|
|
232
|
-
/>
|
|
233
|
-
</Alert>
|
|
234
|
-
);
|
|
235
|
-
}
|
|
236
|
-
|
|
237
|
-
// Determine the number of columns in the csv
|
|
238
|
-
const numCols = getNumCols(csv);
|
|
239
|
-
|
|
240
|
-
// Check if CSV has too few cols
|
|
241
|
-
if (numCols === 0) {
|
|
242
|
-
// No data
|
|
243
|
-
return (
|
|
244
|
-
<Alert
|
|
245
|
-
color="warning"
|
|
246
|
-
>
|
|
247
|
-
<h2>This CSV file is empty (no columns).</h2>
|
|
248
|
-
</Alert>
|
|
249
|
-
);
|
|
250
|
-
}
|
|
251
|
-
|
|
252
|
-
// Create table header
|
|
253
|
-
let tableHeaderContent = [];
|
|
254
|
-
|
|
255
|
-
// > Add header matching selectors
|
|
256
|
-
if (headersToMatch) {
|
|
257
|
-
// Loop through each csv column and create a selector cell
|
|
258
|
-
const selectors = [];
|
|
259
|
-
for (let i = 0; i < numCols; i++) {
|
|
260
|
-
const currentHeaderKey = columnMapping[i];
|
|
261
|
-
const currentHeader = (
|
|
262
|
-
currentHeaderKey
|
|
263
|
-
? this.headerKeyToHeader[currentHeaderKey]
|
|
264
|
-
: null
|
|
265
|
-
);
|
|
266
|
-
// Create dropdown items
|
|
267
|
-
const items = (
|
|
268
|
-
this.allowedHeaders[i].map((header) => {
|
|
269
|
-
// Skip current header
|
|
270
|
-
if (header === currentHeader) {
|
|
271
|
-
return null;
|
|
272
|
-
}
|
|
273
|
-
return {
|
|
274
|
-
text: header.title,
|
|
275
|
-
onClick: () => {
|
|
276
|
-
this.matchSelected(i, header.key);
|
|
277
|
-
},
|
|
278
|
-
};
|
|
279
|
-
}).filter((x) => { return x; })
|
|
280
|
-
);
|
|
281
|
-
if (currentHeader) {
|
|
282
|
-
// Add "ignore" optiona
|
|
283
|
-
items.splice(0, 0, {
|
|
284
|
-
text: (
|
|
285
|
-
<span>
|
|
286
|
-
<Times className="mr-2" />
|
|
287
|
-
Ignore this Column
|
|
288
|
-
</span>
|
|
289
|
-
),
|
|
290
|
-
onClick: () => {
|
|
291
|
-
this.unmatch(i);
|
|
292
|
-
},
|
|
293
|
-
});
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
selectors.push((
|
|
297
|
-
<th
|
|
298
|
-
key={genUUID()}
|
|
299
|
-
>
|
|
300
|
-
<Dropdown
|
|
301
|
-
title={(
|
|
302
|
-
currentHeader
|
|
303
|
-
? currentHeader.title
|
|
304
|
-
: 'Ignored'
|
|
305
|
-
)}
|
|
306
|
-
items={items}
|
|
307
|
-
color={(
|
|
308
|
-
currentHeader
|
|
309
|
-
? 'info'
|
|
310
|
-
: 'secondary'
|
|
311
|
-
)}
|
|
312
|
-
block
|
|
313
|
-
/>
|
|
314
|
-
</th>
|
|
315
|
-
));
|
|
316
|
-
}
|
|
317
|
-
// Add selectors as another row
|
|
318
|
-
tableHeaderContent.push((
|
|
319
|
-
<tr
|
|
320
|
-
key={genUUID()}
|
|
321
|
-
>
|
|
322
|
-
{selectors}
|
|
323
|
-
</tr>
|
|
324
|
-
));
|
|
325
|
-
}
|
|
326
|
-
|
|
327
|
-
// > Add csv headers
|
|
328
|
-
if (!noHeader && csv.headers) {
|
|
329
|
-
const headerElements = csv.headers.map((title) => {
|
|
330
|
-
return (
|
|
331
|
-
<th
|
|
332
|
-
key={title}
|
|
333
|
-
>
|
|
334
|
-
{title}
|
|
335
|
-
</th>
|
|
336
|
-
);
|
|
337
|
-
});
|
|
338
|
-
tableHeaderContent.push((
|
|
339
|
-
<tr
|
|
340
|
-
key={genUUID()}
|
|
341
|
-
>
|
|
342
|
-
{headerElements}
|
|
343
|
-
</tr>
|
|
344
|
-
));
|
|
345
|
-
}
|
|
346
|
-
|
|
347
|
-
// Create table rows
|
|
348
|
-
let tableRowContent = [];
|
|
349
|
-
for (let i = 0; i < (numRowsToPreview || csv.data.length); i++) {
|
|
350
|
-
// Create cells
|
|
351
|
-
let cells;
|
|
352
|
-
if (csv.headers) {
|
|
353
|
-
// Assume data is in object format
|
|
354
|
-
cells = csv.headers.map((header) => {
|
|
355
|
-
return (
|
|
356
|
-
<td
|
|
357
|
-
key={genUUID()}
|
|
358
|
-
>
|
|
359
|
-
{csv.data[i][header]}
|
|
360
|
-
</td>
|
|
361
|
-
);
|
|
362
|
-
});
|
|
363
|
-
} else {
|
|
364
|
-
// Assume data is in array format
|
|
365
|
-
cells = csv.data[i].map((cellText) => {
|
|
366
|
-
return (
|
|
367
|
-
<td
|
|
368
|
-
key={genUUID()}
|
|
369
|
-
>
|
|
370
|
-
{cellText}
|
|
371
|
-
</td>
|
|
372
|
-
);
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
|
|
376
|
-
tableRowContent.push((
|
|
377
|
-
<tr
|
|
378
|
-
key={genUUID()}
|
|
379
|
-
>
|
|
380
|
-
{cells}
|
|
381
|
-
</tr>
|
|
382
|
-
));
|
|
383
|
-
}
|
|
384
|
-
|
|
385
|
-
// Add ellipses if preview cut off some rows
|
|
386
|
-
if (numRowsToPreview && numRowsToPreview < csv.data.length) {
|
|
387
|
-
const cells = [];
|
|
388
|
-
for (let i = 0; i < numCols; i++) {
|
|
389
|
-
cells.push((
|
|
390
|
-
<td
|
|
391
|
-
key={genUUID()}
|
|
392
|
-
className="text-center"
|
|
393
|
-
>
|
|
394
|
-
<Ellipsis vertical />
|
|
395
|
-
</td>
|
|
396
|
-
));
|
|
397
|
-
}
|
|
398
|
-
tableRowContent.push((
|
|
399
|
-
<tr
|
|
400
|
-
key={genUUID()}
|
|
401
|
-
>
|
|
402
|
-
{cells}
|
|
403
|
-
</tr>
|
|
404
|
-
));
|
|
405
|
-
}
|
|
406
|
-
|
|
407
|
-
// Wrap header and row content objects
|
|
408
|
-
tableRowContent = (
|
|
409
|
-
tableRowContent.length > 0
|
|
410
|
-
? (
|
|
411
|
-
<tbody>
|
|
412
|
-
{tableRowContent}
|
|
413
|
-
</tbody>
|
|
414
|
-
)
|
|
415
|
-
: null
|
|
416
|
-
);
|
|
417
|
-
tableHeaderContent = (
|
|
418
|
-
tableHeaderContent.length > 0
|
|
419
|
-
? (
|
|
420
|
-
<thead className="text-nowrap">
|
|
421
|
-
{tableHeaderContent}
|
|
422
|
-
</thead>
|
|
423
|
-
)
|
|
424
|
-
: null
|
|
425
|
-
);
|
|
426
|
-
|
|
427
|
-
// If performing header matching, create match message
|
|
428
|
-
let matchMessage;
|
|
429
|
-
if (headersToMatch) {
|
|
430
|
-
// Create checklist
|
|
431
|
-
const requiredElements = [];
|
|
432
|
-
const optionalElements = [];
|
|
433
|
-
let requirementsMet = true;
|
|
434
|
-
let optionalColumnsMet = true;
|
|
435
|
-
headersToMatch
|
|
436
|
-
.forEach((header) => {
|
|
437
|
-
const isMatched = (
|
|
438
|
-
Object.values(columnMapping).indexOf(header.key) >= 0
|
|
439
|
-
);
|
|
440
|
-
const element = (
|
|
441
|
-
<LeftAddon
|
|
442
|
-
key={genUUID()}
|
|
443
|
-
>
|
|
444
|
-
<Checkbox
|
|
445
|
-
checked={isMatched}
|
|
446
|
-
className="mr-2"
|
|
447
|
-
/>
|
|
448
|
-
<div
|
|
449
|
-
className={(
|
|
450
|
-
isMatched
|
|
451
|
-
? 'text-success'
|
|
452
|
-
: 'text-danger'
|
|
453
|
-
)}
|
|
454
|
-
>
|
|
455
|
-
{header.title}
|
|
456
|
-
{header.description && (
|
|
457
|
-
<span>
|
|
458
|
-
–
|
|
459
|
-
<em>{header.description}</em>
|
|
460
|
-
</span>
|
|
461
|
-
)}
|
|
462
|
-
</div>
|
|
463
|
-
</LeftAddon>
|
|
464
|
-
);
|
|
465
|
-
if (header.required) {
|
|
466
|
-
requiredElements.push(element);
|
|
467
|
-
if (!isMatched) {
|
|
468
|
-
requirementsMet = false;
|
|
469
|
-
}
|
|
470
|
-
} else {
|
|
471
|
-
optionalElements.push(element);
|
|
472
|
-
if (!isMatched) {
|
|
473
|
-
optionalColumnsMet = false;
|
|
474
|
-
}
|
|
475
|
-
}
|
|
476
|
-
});
|
|
477
|
-
|
|
478
|
-
// Create back button
|
|
479
|
-
const backButton = (
|
|
480
|
-
<Button
|
|
481
|
-
text={(
|
|
482
|
-
<span>
|
|
483
|
-
<Chevron
|
|
484
|
-
direction="left"
|
|
485
|
-
className="mr-2"
|
|
486
|
-
/>
|
|
487
|
-
Choose New File
|
|
488
|
-
</span>
|
|
489
|
-
)}
|
|
490
|
-
color="secondary"
|
|
491
|
-
size="lg"
|
|
492
|
-
className="mt-2 mr-2"
|
|
493
|
-
onClick={onReupload}
|
|
494
|
-
/>
|
|
495
|
-
);
|
|
496
|
-
|
|
497
|
-
// Display "please verify" if all columns are matched and auto-matching
|
|
498
|
-
// occurred (an user hasn't made changes)
|
|
499
|
-
if (
|
|
500
|
-
requirementsMet
|
|
501
|
-
&& optionalColumnsMet
|
|
502
|
-
&& !changesMade
|
|
503
|
-
&& this.autoMatchingOccurred
|
|
504
|
-
) {
|
|
505
|
-
const dropdownMessage = 'We auto-detected the columns of your CSV file. Verify that each dropdown matches the type of info in the column below it.';
|
|
506
|
-
matchMessage = (
|
|
507
|
-
<Alert
|
|
508
|
-
color="warning"
|
|
509
|
-
className="text-center pb-2"
|
|
510
|
-
>
|
|
511
|
-
<h4>
|
|
512
|
-
Verify Our Matchings
|
|
513
|
-
</h4>
|
|
514
|
-
<div>
|
|
515
|
-
{dropdownMessage}
|
|
516
|
-
</div>
|
|
517
|
-
{backButton}
|
|
518
|
-
<Button
|
|
519
|
-
text={(
|
|
520
|
-
<span>
|
|
521
|
-
<Check
|
|
522
|
-
className="mr-2"
|
|
523
|
-
/>
|
|
524
|
-
Looks Good
|
|
525
|
-
</span>
|
|
526
|
-
)}
|
|
527
|
-
color="success"
|
|
528
|
-
size="lg"
|
|
529
|
-
className="mt-2"
|
|
530
|
-
onClick={this.onDoneMatchingClicked}
|
|
531
|
-
/>
|
|
532
|
-
</Alert>
|
|
533
|
-
);
|
|
534
|
-
} else {
|
|
535
|
-
// Divide elements into required and optional
|
|
536
|
-
const requiredChecklist = (
|
|
537
|
-
requiredElements.length > 0
|
|
538
|
-
? (
|
|
539
|
-
<div>
|
|
540
|
-
<div className="text-dark">
|
|
541
|
-
<strong>Required columns:</strong>
|
|
542
|
-
</div>
|
|
543
|
-
{requiredElements}
|
|
544
|
-
</div>
|
|
545
|
-
)
|
|
546
|
-
: null
|
|
547
|
-
);
|
|
548
|
-
const optionalChecklist = (
|
|
549
|
-
optionalElements.length > 0
|
|
550
|
-
? (
|
|
551
|
-
<div>
|
|
552
|
-
<div className="text-dark">
|
|
553
|
-
<strong>Optional columns:</strong>
|
|
554
|
-
</div>
|
|
555
|
-
{optionalElements}
|
|
556
|
-
</div>
|
|
557
|
-
)
|
|
558
|
-
: null
|
|
559
|
-
);
|
|
560
|
-
|
|
561
|
-
// Create done button
|
|
562
|
-
const doneButton = (
|
|
563
|
-
<Button
|
|
564
|
-
text={(
|
|
565
|
-
<span>
|
|
566
|
-
<Check
|
|
567
|
-
className="mr-2"
|
|
568
|
-
/>
|
|
569
|
-
Done Matching
|
|
570
|
-
</span>
|
|
571
|
-
)}
|
|
572
|
-
color="success"
|
|
573
|
-
size="lg"
|
|
574
|
-
className="mt-2"
|
|
575
|
-
onClick={this.onDoneMatchingClicked}
|
|
576
|
-
disabled={!requirementsMet}
|
|
577
|
-
/>
|
|
578
|
-
);
|
|
579
|
-
matchMessage = (
|
|
580
|
-
<Alert
|
|
581
|
-
color="warning"
|
|
582
|
-
className="text-center pb-2"
|
|
583
|
-
>
|
|
584
|
-
<h4>
|
|
585
|
-
Match Columns
|
|
586
|
-
</h4>
|
|
587
|
-
Use the dropdowns to tell us where the following columns are:
|
|
588
|
-
{(this.autoMatchingOccurred && !changesMade) && (
|
|
589
|
-
<div>
|
|
590
|
-
(we tried to
|
|
591
|
-
<em> auto-match </em>
|
|
592
|
-
some of the columns for you)
|
|
593
|
-
</div>
|
|
594
|
-
)}
|
|
595
|
-
<Alert
|
|
596
|
-
color="light"
|
|
597
|
-
className="mb-0 text-left"
|
|
598
|
-
>
|
|
599
|
-
{requiredChecklist}
|
|
600
|
-
{optionalChecklist}
|
|
601
|
-
</Alert>
|
|
602
|
-
{backButton}
|
|
603
|
-
{doneButton}
|
|
604
|
-
</Alert>
|
|
605
|
-
);
|
|
606
|
-
}
|
|
607
|
-
}
|
|
608
|
-
|
|
609
|
-
// Create the csv preview
|
|
610
|
-
const csvPreview = (
|
|
611
|
-
<div
|
|
612
|
-
style={{
|
|
613
|
-
overflowX: 'scroll',
|
|
614
|
-
backgroundColor: 'white',
|
|
615
|
-
}}
|
|
616
|
-
>
|
|
617
|
-
<Table
|
|
618
|
-
striped
|
|
619
|
-
bordered
|
|
620
|
-
className="mb-0"
|
|
621
|
-
>
|
|
622
|
-
{tableHeaderContent}
|
|
623
|
-
{tableRowContent}
|
|
624
|
-
</Table>
|
|
625
|
-
</div>
|
|
626
|
-
);
|
|
627
|
-
|
|
628
|
-
// Download button
|
|
629
|
-
let downloadButton;
|
|
630
|
-
if (downloadFilename) {
|
|
631
|
-
const filename = (
|
|
632
|
-
downloadFilename.endsWith('.csv')
|
|
633
|
-
? downloadFilename
|
|
634
|
-
: `${downloadFilename}.csv`
|
|
635
|
-
);
|
|
636
|
-
const fileContents = csvToString(csv);
|
|
637
|
-
const downloadMessage = (
|
|
638
|
-
<RightAddon centerVertically>
|
|
639
|
-
<h3 className="mb-0">{filename}</h3>
|
|
640
|
-
<DownloadButton
|
|
641
|
-
filename={filename}
|
|
642
|
-
contents={fileContents}
|
|
643
|
-
large
|
|
644
|
-
/>
|
|
645
|
-
</RightAddon>
|
|
646
|
-
);
|
|
647
|
-
if (matchMessage) {
|
|
648
|
-
// Performing matching. Add an unobtrusive download button
|
|
649
|
-
downloadButton = (
|
|
650
|
-
<Alert
|
|
651
|
-
color="dark"
|
|
652
|
-
className="mb-0"
|
|
653
|
-
>
|
|
654
|
-
{downloadMessage}
|
|
655
|
-
</Alert>
|
|
656
|
-
);
|
|
657
|
-
} else {
|
|
658
|
-
// Not performing matching. Show the preview inside the download alert
|
|
659
|
-
return (
|
|
660
|
-
<Alert
|
|
661
|
-
color="dark"
|
|
662
|
-
className="mb-0"
|
|
663
|
-
>
|
|
664
|
-
{downloadMessage}
|
|
665
|
-
<div className="mt-2">
|
|
666
|
-
{csvPreview}
|
|
667
|
-
</div>
|
|
668
|
-
</Alert>
|
|
669
|
-
);
|
|
670
|
-
}
|
|
671
|
-
}
|
|
672
|
-
|
|
673
|
-
return (
|
|
674
|
-
<div>
|
|
675
|
-
{downloadButton}
|
|
676
|
-
{matchMessage}
|
|
677
|
-
{csvPreview}
|
|
678
|
-
</div>
|
|
679
|
-
);
|
|
680
|
-
}
|
|
681
|
-
}
|
|
682
|
-
|
|
683
|
-
CSV.propTypes = {
|
|
684
|
-
/* The CSV to show. Note: csv.headers and csv.data have different forms,
|
|
685
|
-
* depending upon the value of noHeader */
|
|
686
|
-
csv: PropTypes.shape({
|
|
687
|
-
headers: PropTypes.arrayOf(PropTypes.string),
|
|
688
|
-
data: PropTypes.arrayOf(PropTypes.object),
|
|
689
|
-
}).isRequired,
|
|
690
|
-
// Headers to match (matching is performed if headers are included)
|
|
691
|
-
headersToMatch: PropTypes.arrayOf(
|
|
692
|
-
PropTypes.shape({
|
|
693
|
-
// A json object key so we can create json objects out of each row
|
|
694
|
-
key: PropTypes.string,
|
|
695
|
-
// A human-readable title of what the column must contain
|
|
696
|
-
title: PropTypes.string,
|
|
697
|
-
// A human-readable description of what the column must contain
|
|
698
|
-
description: PropTypes.string,
|
|
699
|
-
// If true, this header must be matched to continue
|
|
700
|
-
required: PropTypes.bool,
|
|
701
|
-
/* A function that returns true if a cell in the column is valid, or a
|
|
702
|
-
* string matching our built-in validators (see top of component) */
|
|
703
|
-
validator: PropTypes.func,
|
|
704
|
-
/* A list of guesses for the csv column header. If one of these matches
|
|
705
|
-
* (and the column is valid), this will be automatically matched */
|
|
706
|
-
titleGuesses: PropTypes.arrayOf(PropTypes.string),
|
|
707
|
-
// If true, cells in this column may be empty
|
|
708
|
-
allowEmpty: PropTypes.bool,
|
|
709
|
-
})
|
|
710
|
-
),
|
|
711
|
-
/* If true, the csv is not expected to have any header (and csv.data is
|
|
712
|
-
* expected to be an array of row arrays)
|
|
713
|
-
* example:
|
|
714
|
-
* - csv.headers: null
|
|
715
|
-
* - csv.data: [['Gabe', 'gabe@example.com'], ...]
|
|
716
|
-
* otherwise, csv.headers is read as the list of keys to use in extracting
|
|
717
|
-
* cell data from csv.data
|
|
718
|
-
* example:
|
|
719
|
-
* - csv.headers = ['Full Name', 'email']
|
|
720
|
-
* - csv.data = [{'Full Name': 'Gabe', email: 'gabe@example.com'}, ...])
|
|
721
|
-
*/
|
|
722
|
-
noHeader: PropTypes.bool,
|
|
723
|
-
// The number of rows to show in the csv preview (null to show all)
|
|
724
|
-
numRowsToPreview: PropTypes.number,
|
|
725
|
-
// A handler to call when the "choose new file" button is clicked
|
|
726
|
-
onReupload: PropTypes.func,
|
|
727
|
-
// A handler to call when matching is finished
|
|
728
|
-
onFinished: PropTypes.func,
|
|
729
|
-
/* If defined, a download button is shown and the downloaded file has this
|
|
730
|
-
* name */
|
|
731
|
-
downloadFilename: PropTypes.string,
|
|
732
|
-
};
|
|
733
|
-
|
|
734
|
-
CSV.defaultProps = {
|
|
735
|
-
headersToMatch: null,
|
|
736
|
-
noHeader: false,
|
|
737
|
-
numRowsToPreview: null,
|
|
738
|
-
onReupload: null,
|
|
739
|
-
onFinished: null,
|
|
740
|
-
downloadFilename: null,
|
|
741
|
-
};
|
|
742
|
-
|
|
743
|
-
export default CSV;
|