dce-reactkit 1.0.20 → 2.0.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.
Files changed (215) hide show
  1. package/.eslintrc.js +95 -0
  2. package/lib/components/LoadingSpinner.d.ts +6 -0
  3. package/lib/components/LoadingSpinner.js +41 -0
  4. package/lib/components/LoadingSpinner.js.map +1 -0
  5. package/lib/components/Modal.d.ts +69 -0
  6. package/lib/components/Modal.js +315 -0
  7. package/lib/components/Modal.js.map +1 -0
  8. package/lib/errors/ErrorWithCode.d.ts +9 -0
  9. package/lib/errors/ErrorWithCode.js +33 -0
  10. package/lib/errors/ErrorWithCode.js.map +1 -0
  11. package/lib/helpers/abbreviate.d.ts +9 -0
  12. package/lib/helpers/abbreviate.js +21 -0
  13. package/lib/helpers/abbreviate.js.map +1 -0
  14. package/lib/helpers/avg.d.ts +8 -0
  15. package/lib/helpers/avg.js +24 -0
  16. package/lib/helpers/avg.js.map +1 -0
  17. package/lib/helpers/ceilToNumDecimals.d.ts +9 -0
  18. package/lib/helpers/ceilToNumDecimals.js +15 -0
  19. package/lib/helpers/ceilToNumDecimals.js.map +1 -0
  20. package/lib/helpers/floorToNumDecimals.d.ts +9 -0
  21. package/lib/helpers/floorToNumDecimals.js +15 -0
  22. package/lib/helpers/floorToNumDecimals.js.map +1 -0
  23. package/lib/helpers/forceNumIntoBounds.d.ts +10 -0
  24. package/lib/helpers/forceNumIntoBounds.js +15 -0
  25. package/lib/helpers/forceNumIntoBounds.js.map +1 -0
  26. package/lib/helpers/padDecimalZeros.d.ts +10 -0
  27. package/lib/helpers/padDecimalZeros.js +30 -0
  28. package/lib/helpers/padDecimalZeros.js.map +1 -0
  29. package/lib/helpers/padZerosLeft.d.ts +9 -0
  30. package/lib/helpers/padZerosLeft.js +21 -0
  31. package/lib/helpers/padZerosLeft.js.map +1 -0
  32. package/lib/helpers/roundToNumDecimals.d.ts +9 -0
  33. package/lib/helpers/roundToNumDecimals.js +15 -0
  34. package/lib/helpers/roundToNumDecimals.js.map +1 -0
  35. package/lib/helpers/sum.d.ts +8 -0
  36. package/lib/helpers/sum.js +15 -0
  37. package/lib/helpers/sum.js.map +1 -0
  38. package/lib/helpers/visitServerEndpoint.d.ts +17 -0
  39. package/lib/helpers/visitServerEndpoint.js +84 -0
  40. package/lib/helpers/visitServerEndpoint.js.map +1 -0
  41. package/lib/helpers/waitMs.d.ts +7 -0
  42. package/lib/helpers/waitMs.js +55 -0
  43. package/lib/helpers/waitMs.js.map +1 -0
  44. package/lib/types/ReactKitErrorCode.d.ts +9 -0
  45. package/lib/types/ReactKitErrorCode.js +14 -0
  46. package/lib/types/ReactKitErrorCode.js.map +1 -0
  47. package/lib/types/Variant.d.ts +15 -0
  48. package/lib/types/Variant.js +19 -0
  49. package/lib/types/Variant.js.map +1 -0
  50. package/package.json +22 -40
  51. package/src/components/LoadingSpinner.tsx +105 -0
  52. package/src/components/Modal.tsx +447 -0
  53. package/src/errors/ErrorWithCode.tsx +15 -0
  54. package/src/helpers/abbreviate.tsx +23 -0
  55. package/src/helpers/avg.tsx +22 -0
  56. package/src/helpers/ceilToNumDecimals.tsx +13 -0
  57. package/src/helpers/floorToNumDecimals.tsx +13 -0
  58. package/src/helpers/forceNumIntoBounds.tsx +19 -0
  59. package/src/helpers/padDecimalZeros.tsx +32 -0
  60. package/src/helpers/padZerosLeft.tsx +21 -0
  61. package/src/helpers/roundToNumDecimals.tsx +13 -0
  62. package/src/helpers/sum.tsx +16 -0
  63. package/src/helpers/visitServerEndpoint.tsx +59 -0
  64. package/src/helpers/waitMs.tsx +12 -0
  65. package/src/types/ReactKitErrorCode.tsx +12 -0
  66. package/src/types/Variant.tsx +16 -0
  67. package/tsconfig.json +24 -0
  68. package/index.js +0 -1
  69. package/lib/Bar.js +0 -169
  70. package/lib/Donut.js +0 -145
  71. package/lib/DownloadButton.js +0 -81
  72. package/lib/Histogram.js +0 -193
  73. package/lib/Scatter.js +0 -162
  74. package/lib/StackedBar.js +0 -243
  75. package/lib/common/chart/Holder/index.js +0 -182
  76. package/lib/common/chart/Holder/style.css +0 -41
  77. package/lib/common/chart/colors.js +0 -12
  78. package/lib/common/csvToString.js +0 -23
  79. package/lib/common/isFloat.js +0 -14
  80. package/lib/common/isInteger.js +0 -24
  81. package/lib/common/math.js +0 -21
  82. package/lib/common/validators.js +0 -18
  83. package/lib/glyph/Adjust.js +0 -13
  84. package/lib/glyph/Angle.js +0 -44
  85. package/lib/glyph/Archive.js +0 -13
  86. package/lib/glyph/Arrow.js +0 -49
  87. package/lib/glyph/ArrowsHorizontal.js +0 -13
  88. package/lib/glyph/ArrowsVertical.js +0 -13
  89. package/lib/glyph/Award.js +0 -13
  90. package/lib/glyph/Ban.js +0 -13
  91. package/lib/glyph/Barcode.js +0 -13
  92. package/lib/glyph/Bed.js +0 -13
  93. package/lib/glyph/Bell.js +0 -35
  94. package/lib/glyph/Bolt.js +0 -13
  95. package/lib/glyph/Bookmark.js +0 -37
  96. package/lib/glyph/Bug.js +0 -13
  97. package/lib/glyph/Calendar.js +0 -13
  98. package/lib/glyph/Check.js +0 -36
  99. package/lib/glyph/Checkbox.js +0 -40
  100. package/lib/glyph/Chevron.js +0 -63
  101. package/lib/glyph/Circle.js +0 -15
  102. package/lib/glyph/Cog.js +0 -13
  103. package/lib/glyph/DoubleAngle.js +0 -45
  104. package/lib/glyph/Download.js +0 -13
  105. package/lib/glyph/Ellipsis.js +0 -36
  106. package/lib/glyph/Envelope.js +0 -13
  107. package/lib/glyph/File.js +0 -37
  108. package/lib/glyph/IdCard.js +0 -13
  109. package/lib/glyph/Menu.js +0 -13
  110. package/lib/glyph/Minus.js +0 -13
  111. package/lib/glyph/Move.js +0 -13
  112. package/lib/glyph/Plus.js +0 -13
  113. package/lib/glyph/Square.js +0 -47
  114. package/lib/glyph/Teacher.js +0 -13
  115. package/lib/glyph/TextAlign.js +0 -53
  116. package/lib/glyph/Times.js +0 -36
  117. package/lib/glyph/WarningTriangle.js +0 -13
  118. package/lib/index.js +0 -1
  119. package/src-old/common/csvToString.js +0 -5
  120. package/src-old/common/isFloat.js +0 -3
  121. package/src-old/common/isInteger.js +0 -14
  122. package/src-old/common/math.js +0 -16
  123. package/src-old/common/validators.js +0 -14
  124. package/src-old/components/Alert.js +0 -79
  125. package/src-old/components/Button.js +0 -64
  126. package/src-old/components/CSV/autoMatch.js +0 -174
  127. package/src-old/components/CSV/getNumCols.js +0 -15
  128. package/src-old/components/CSV/index.js +0 -743
  129. package/src-old/components/Card.js +0 -64
  130. package/src-old/components/Chart/Bar.js +0 -169
  131. package/src-old/components/Chart/Donut.js +0 -160
  132. package/src-old/components/Chart/Histogram.js +0 -189
  133. package/src-old/components/Chart/Scatter.js +0 -162
  134. package/src-old/components/Chart/colors.js +0 -20
  135. package/src-old/components/Checkbox/index.js +0 -106
  136. package/src-old/components/Checkbox/style.css +0 -11
  137. package/src-old/components/DatePicker.js +0 -59
  138. package/src-old/components/DownloadButton.js +0 -72
  139. package/src-old/components/Dropdown.js +0 -120
  140. package/src-old/components/Layout/LeftAddon.js +0 -63
  141. package/src-old/components/Layout/RightAddon.js +0 -64
  142. package/src-old/components/Layout/TwoCol.js +0 -67
  143. package/src-old/components/Modal.js +0 -161
  144. package/src-old/components/PhotoRoster.js +0 -152
  145. package/src-old/components/Pills.js +0 -117
  146. package/src-old/components/Radio.js +0 -94
  147. package/src-old/components/Splash/Error.js +0 -62
  148. package/src-old/components/Splash/Load/index.js +0 -32
  149. package/src-old/components/Splash/Load/style.css +0 -51
  150. package/src-old/components/Stat/General.js +0 -252
  151. package/src-old/components/Stat/helpers/calcAverage.js +0 -20
  152. package/src-old/components/Stat/helpers/calcExtremes.js +0 -27
  153. package/src-old/components/Stat/helpers/calcMedian.js +0 -32
  154. package/src-old/components/Stat/helpers/calcStdDev.js +0 -30
  155. package/src-old/components/Stat/helpers/calcSum.js +0 -8
  156. package/src-old/components/UserProfile/index.js +0 -151
  157. package/src-old/components/UserProfile/style.css +0 -33
  158. package/src-old/components/Well.js +0 -28
  159. package/src-old/components/Wizard/Question/CSV.js +0 -307
  160. package/src-old/components/Wizard/Question/Chooser/Assignment.js +0 -108
  161. package/src-old/components/Wizard/Question/Chooser/Course.js +0 -214
  162. package/src-old/components/Wizard/Question/Chooser/helpers/divideIntoSections.js +0 -16
  163. package/src-old/components/Wizard/Question/Chooser/helpers/genGetItemsPromise.js +0 -20
  164. package/src-old/components/Wizard/Question/Chooser/helpers/preprocessSections.js +0 -36
  165. package/src-old/components/Wizard/Question/Chooser/index.js +0 -306
  166. package/src-old/components/Wizard/Question/Form/Field/CheckableItem.js +0 -155
  167. package/src-old/components/Wizard/Question/Form/Field/Input.js +0 -149
  168. package/src-old/components/Wizard/Question/Form/Field/NumberItem.js +0 -374
  169. package/src-old/components/Wizard/Question/Form/Field/index.js +0 -34
  170. package/src-old/components/Wizard/Question/Form/Field/readme.md +0 -7
  171. package/src-old/components/Wizard/Question/Form/index.js +0 -175
  172. package/src-old/components/Wizard/Question/Form/style.css +0 -3
  173. package/src-old/components/Wizard/Question/OneButton.js +0 -80
  174. package/src-old/components/Wizard/Question/index.js +0 -46
  175. package/src-old/components/Wizard/Question/readme.md +0 -10
  176. package/src-old/components/Wizard/QuestionGroup/AnswerAll.js +0 -126
  177. package/src-old/components/Wizard/QuestionGroup/AnswerAny.js +0 -60
  178. package/src-old/components/Wizard/index.js +0 -233
  179. package/src-old/components/Wizard/style.css +0 -21
  180. package/src-old/glyphs/Adjust.js +0 -13
  181. package/src-old/glyphs/Angle.js +0 -57
  182. package/src-old/glyphs/Archive.js +0 -13
  183. package/src-old/glyphs/Arrow.js +0 -85
  184. package/src-old/glyphs/ArrowsHorizontal.js +0 -13
  185. package/src-old/glyphs/ArrowsVertical.js +0 -13
  186. package/src-old/glyphs/Award.js +0 -13
  187. package/src-old/glyphs/Ban.js +0 -13
  188. package/src-old/glyphs/Barcode.js +0 -13
  189. package/src-old/glyphs/Bed.js +0 -13
  190. package/src-old/glyphs/Bell.js +0 -34
  191. package/src-old/glyphs/Bolt.js +0 -13
  192. package/src-old/glyphs/Bookmark.js +0 -36
  193. package/src-old/glyphs/Bug.js +0 -13
  194. package/src-old/glyphs/Calendar.js +0 -13
  195. package/src-old/glyphs/Check.js +0 -34
  196. package/src-old/glyphs/Checkbox.js +0 -42
  197. package/src-old/glyphs/Chevron.js +0 -88
  198. package/src-old/glyphs/Circle.js +0 -16
  199. package/src-old/glyphs/Cog.js +0 -13
  200. package/src-old/glyphs/DoubleAngle.js +0 -58
  201. package/src-old/glyphs/Download.js +0 -13
  202. package/src-old/glyphs/Ellipsis.js +0 -35
  203. package/src-old/glyphs/Envelope.js +0 -13
  204. package/src-old/glyphs/File.js +0 -40
  205. package/src-old/glyphs/IdCard.js +0 -13
  206. package/src-old/glyphs/Menu.js +0 -13
  207. package/src-old/glyphs/Minus.js +0 -13
  208. package/src-old/glyphs/Move.js +0 -13
  209. package/src-old/glyphs/Plus.js +0 -13
  210. package/src-old/glyphs/Square.js +0 -48
  211. package/src-old/glyphs/Teacher.js +0 -13
  212. package/src-old/glyphs/TextAlign.js +0 -58
  213. package/src-old/glyphs/Times.js +0 -34
  214. package/src-old/glyphs/WarningTriangle.js +0 -13
  215. package/src-old/index.js +0 -1
@@ -1,79 +0,0 @@
1
- import React, { Component } from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- // Import bootstrap components
5
- import { Alert as BootstrapAlert } from 'reactstrap';
6
- // Import bootstrap stylesheet
7
- import 'bootstrap/dist/css/bootstrap.min.css';
8
-
9
- class Alert extends Component {
10
- constructor(props) {
11
- super(props);
12
-
13
- this.state = {
14
- visible: true,
15
- };
16
- this.closeClicked = this.closeClicked.bind(this);
17
- }
18
-
19
- closeClicked() {
20
- this.setState({
21
- visible: false,
22
- });
23
-
24
- // Call parent's onDismiss function
25
- const { onDismiss } = this.props;
26
- if (onDismiss) {
27
- onDismiss();
28
- }
29
- }
30
-
31
- render() {
32
- // Extract properties
33
- const {
34
- dismissible,
35
- onDismiss,
36
- centered,
37
- children,
38
- ...props
39
- } = this.props;
40
-
41
- // Deconstruct state
42
- const { visible } = this.state;
43
-
44
- return (
45
- <BootstrapAlert
46
- isOpen={visible}
47
- toggle={dismissible ? this.closeClicked : undefined}
48
- style={{
49
- textAlign: (centered ? 'center' : 'left'),
50
- }}
51
- {...props}
52
- >
53
- {children}
54
- </BootstrapAlert>
55
- );
56
- }
57
- }
58
-
59
- Alert.propTypes = {
60
- /* If true, the alert is dismissible */
61
- dismissible: PropTypes.bool,
62
- /* Handler to call when the alert is dismissed (only valid if dismissible) */
63
- onDismiss: PropTypes.func,
64
- /* If true, text is centered in the alert */
65
- centered: PropTypes.bool,
66
- /* Children (body) of the alert */
67
- children: PropTypes.oneOfType([
68
- PropTypes.arrayOf(PropTypes.node),
69
- PropTypes.node,
70
- ]).isRequired,
71
- };
72
-
73
- Alert.defaultProps = {
74
- dismissible: false,
75
- onDismiss: null,
76
- centered: false,
77
- };
78
-
79
- export default Alert;
@@ -1,64 +0,0 @@
1
- import React, { Component } from 'react';
2
- import PropTypes from 'prop-types';
3
-
4
- // Import bootstrap components
5
- import { Button as BootstrapButton } from 'reactstrap';
6
- // Import bootstrap stylesheet
7
- import 'bootstrap/dist/css/bootstrap.min.css';
8
-
9
- /* Allowed colors:
10
- * - primary
11
- * - secondary
12
- * - success
13
- * - info
14
- * - warning
15
- * - danger
16
- * - link
17
- *
18
- * To disable, include "disabled" tag
19
- * To use outline-style of button, include "outline" tag
20
- * To use block-style of button, include "block" tag
21
- *
22
- * Sizes:
23
- * - lg
24
- * - sm
25
- * - normal (used by default)
26
- */
27
-
28
- class Button extends Component {
29
- render() {
30
- // Extract properties
31
- const {
32
- text,
33
- children,
34
- ...props
35
- } = this.props;
36
-
37
- // Set up button style
38
- return (
39
- <BootstrapButton {...props}>
40
- {text || children || 'Choose'}
41
- </BootstrapButton>
42
- );
43
- }
44
- }
45
-
46
- Button.propTypes = {
47
- /* Label text of the button */
48
- text: PropTypes.string,
49
- /* Body of the button (only valid if text is not included) */
50
- children: PropTypes.oneOfType([
51
- PropTypes.arrayOf(PropTypes.node),
52
- PropTypes.node,
53
- ]),
54
- /* Also, all Bootstrap button props may be included:
55
- * https://reactstrap.github.io/components/buttons/
56
- */
57
- };
58
-
59
- Button.defaultProps = {
60
- text: null,
61
- children: null,
62
- };
63
-
64
- export default Button;
@@ -1,174 +0,0 @@
1
- import validators from '../../common/validators';
2
- import getNumCols from './getNumCols';
3
-
4
- /**
5
- * Performs auto csv header matching and determines the allowed manual matchings
6
- * that a user can do (via dropowns)
7
- * @param {object} csv - the csv object to analyze
8
- * @param {object} headersToMatch - a list of headers to match against the csv
9
- * headers
10
- * @param {string} headersToMatch[].key - the property name to use for the
11
- * parsed version of the csv (for each row object, the value in this column
12
- * will be found in row[key]). Example: 'name'
13
- * @param {string} headersToMatch[].title - a human-readable title of the column
14
- * that needs to be matched. Example: 'Student Name'
15
- * @param {string} headersToMatch[].description - a human-readable description
16
- * of the column that needs to be matched. Example: 'the student's full name'
17
- * @param {boolean} headersToMatch[].required - if true, the header must be
18
- * matched before continuing. Otherwise, the header is allowed to remain
19
- * unmatched
20
- * @param {string|function} headersToMatch[].validator - a validator function
21
- * that must return true on all cells in a column for the column to be deemed
22
- * valid for matching. If validator is a string, it must be a listed validator
23
- * in /common/validators
24
- * @param {boolean} [allowEmpty] - if true, empty cells are not validated (they
25
- * are deemed valid)
26
- * @param {array.<String>} headersToMatch[].titleGuesses - additional guesses
27
- * for what the csv might be titling this column. Example:
28
- * ['Full Name', 'Given Name'].
29
- * We will always use headersToMatch[].title as one of the titleGuesses
30
- */
31
- export default (csv, headersToMatch) => {
32
- // > Determine which headers can be matched to each column (use validator)
33
- const allowedHeaders = {};
34
- // ^ column index => array of headers that we can match this column with
35
- const headerToAllowedColumns = [];
36
- // ^ headerKey => array of column indices that we can match the header with
37
- const numCols = getNumCols(csv);
38
- for (let cIndex = 0; cIndex < numCols; cIndex++) {
39
- // The key to look up the column in csv.data[i]
40
- const columnHeader = (
41
- csv.headers
42
- ? csv.headers[cIndex]
43
- : cIndex
44
- );
45
-
46
- // Loop through all headers and determine if it's allowed to be matched
47
- for (let hIndex = 0; hIndex < headersToMatch.length; hIndex++) {
48
- const headerToMatch = headersToMatch[hIndex];
49
-
50
- // Check for a validator
51
- let validationFailed;
52
- let { validator } = headerToMatch;
53
- if (typeof validator === 'string') {
54
- validator = validators[validator];
55
- }
56
- if (validator) {
57
- // Check that all cells in the column are valid
58
- for (let rIndex = 0; rIndex < csv.data.length; rIndex++) {
59
- const row = csv.data[rIndex];
60
- const cell = row[columnHeader];
61
-
62
- // Check for fail due to emptiness
63
- if (
64
- !headerToMatch.allowEmpty
65
- && (!cell || cell.trim().length === 0)
66
- ) {
67
- validationFailed = true;
68
- break;
69
- }
70
-
71
- // Check for fail due to validator
72
- if (!validator(cell)) {
73
- validationFailed = true;
74
- break;
75
- }
76
- }
77
- }
78
-
79
- // Add to list if validation succeeded
80
- if (!validationFailed) {
81
- if (!allowedHeaders[cIndex]) {
82
- allowedHeaders[cIndex] = [];
83
- }
84
- allowedHeaders[cIndex].push(headerToMatch);
85
-
86
- if (!headerToAllowedColumns[headerToMatch.key]) {
87
- headerToAllowedColumns[headerToMatch.key] = [];
88
- }
89
- headerToAllowedColumns[headerToMatch.key].push(cIndex);
90
- }
91
- }
92
- }
93
- console.log(allowedHeaders);
94
-
95
- // Check that each header can be matched to at least one column
96
- for (let i = 0; i < headersToMatch.length; i++) {
97
- const allowedColumns = headerToAllowedColumns[headersToMatch[i].key];
98
- if (!allowedColumns && headersToMatch[i].required) {
99
- this.state = {
100
- impossible: true,
101
- };
102
- return;
103
- }
104
- }
105
-
106
- // Try to automatically match up columns
107
- const columnMapping = {}; // column index => headerKey
108
- const headerIsMatched = {}; // headerKey => true if already matched
109
- const columnIsMatched = {}; // cIndex => true if already matched
110
- // > If a column can only be assigned to one column, automatically assign it
111
- Object.keys(headerToAllowedColumns).forEach((headerKey) => {
112
- const allowedColumns = headerToAllowedColumns[headerKey];
113
- if (allowedColumns.length === 1) {
114
- // Save header mapping
115
- columnMapping[allowedColumns[0]] = headerKey;
116
- headerIsMatched[headerKey] = true;
117
- columnIsMatched[allowedColumns[0]] = true;
118
- }
119
- });
120
-
121
- // Match remaining columns by title guesses (if possible)
122
- if (csv.headers) {
123
- // We have header names to attempt to match
124
- const headerToColumnsThatMatchGuesses = {};
125
- // ^ headerKey => list of columns with title guesses that match
126
- headersToMatch.forEach((header) => {
127
- // Skip header if already matched
128
- if (headerIsMatched[header.key]) {
129
- return;
130
- }
131
-
132
- // Compile all title guesses (and add on actual title as a guess)
133
- const titleGuesses = [header.title].concat(header.titleGuesses || []);
134
-
135
- // Check all allowed columns
136
- headerToAllowedColumns[header.key].forEach((cIndex) => {
137
- // Skip column if already matched
138
- if (columnIsMatched[cIndex]) {
139
- return;
140
- }
141
-
142
- // Keep track of guesses that match
143
- titleGuesses.forEach((titleGuess) => {
144
- const fixedTitleGuess = titleGuess.trim().toLowerCase();
145
- const fixedHeaderTitle = csv.headers[cIndex].trim().toLowerCase();
146
- if (fixedTitleGuess === fixedHeaderTitle) {
147
- // Found a match
148
- if (!headerToColumnsThatMatchGuesses[header.key]) {
149
- headerToColumnsThatMatchGuesses[header.key] = [];
150
- }
151
- headerToColumnsThatMatchGuesses[header.key].push(cIndex);
152
- }
153
- });
154
- });
155
- });
156
-
157
- // If title guesses provide one option for matching, use it!
158
- Object.keys(headerToColumnsThatMatchGuesses).forEach((headerKey) => {
159
- const columnsThatMatch = headerToColumnsThatMatchGuesses[headerKey];
160
- if (columnsThatMatch.length === 1) {
161
- // Found one match!
162
- // Save header mapping
163
- columnMapping[columnsThatMatch[0]] = headerKey;
164
- headerIsMatched[headerKey] = true;
165
- columnIsMatched[columnsThatMatch[0]] = true;
166
- }
167
- });
168
- }
169
-
170
- return {
171
- allowedHeaders,
172
- columnMapping,
173
- };
174
- };
@@ -1,15 +0,0 @@
1
- export default (csv) => {
2
- let numCols;
3
- if (csv.headers) {
4
- numCols = csv.headers.length;
5
- } else {
6
- // No headers (assume rows are arrays)
7
- // Number of columns equals the length of the longest row
8
- csv.data.forEach((row) => {
9
- if (!numCols || row.length > numCols) {
10
- numCols = row.length;
11
- }
12
- });
13
- }
14
- return numCols;
15
- };