|
168 | 168 | let startRow = 0; |
169 | 169 | let startColumn; |
170 | 170 | for (const row of rawValues) { |
171 | | - startColumn = row.findIndex(cell => cell?.effectiveValue); |
| 171 | + startColumn = row?.findIndex(cell => cell?.effectiveValue); |
172 | 172 |
|
173 | | - if (startColumn !== -1) { |
| 173 | + if (startColumn !== undefined && startColumn !== -1) { |
174 | 174 | break; |
175 | 175 | } |
176 | 176 |
|
177 | 177 | startRow += 1; |
178 | 178 | } |
179 | 179 |
|
180 | | - if (startRow >= rawValues.length || startColumn === -1) { |
| 180 | + if (startRow >= rawValues.length || startColumn === undefined || startColumn === -1) { |
181 | 181 | // No data to work with |
182 | 182 | return null |
183 | 183 | } |
184 | 184 |
|
185 | | - // Search for the end of the first range of data. |
186 | | - let endRow; |
187 | | - for (let row = startRow; row < rawValues.length; row++) { |
188 | | - if (!rawValues[row]?.[startColumn] || !rawValues[row]?.[startColumn]?.effectiveValue) { |
189 | | - endRow = row - 1; |
190 | | - break; |
191 | | - } |
192 | | - } |
193 | | - endRow = endRow ?? rawValues.length - 1; |
194 | | - |
195 | | - let endColumn = rawValues[startRow].findIndex((cell, index) => index > startColumn && !cell?.effectiveValue); |
196 | | - endColumn = endColumn === -1 ? rawValues[startRow].length - 1 : endColumn - 1; |
197 | | - |
198 | 185 | // Save data offset inside raw values to be able to store data back in the same range as the source data. |
199 | 186 | this.rowOffset = startRow; |
200 | 187 | this.columnOffset = startColumn; |
201 | 188 |
|
| 189 | + // Search for the end of the first range of data. |
| 190 | + let endColumn, endRow; |
| 191 | + if (this.dataInColumns) { |
| 192 | + // Search for the first row with an empty cell at startColumn column. |
| 193 | + // Why? Because the first column with data contains headings (future property names). |
| 194 | + // And we can't build property names out of empty strings. |
| 195 | + for (let row = startRow; row < rawValues.length; row++) { |
| 196 | + if (!rawValues[row]?.[startColumn]?.effectiveValue) { |
| 197 | + endRow = row - 1; |
| 198 | + break; |
| 199 | + } |
| 200 | + } |
| 201 | + endRow = endRow ?? rawValues.length - 1; |
| 202 | + |
| 203 | + // Search for the first fully empty column. |
| 204 | + let column = startColumn + 1; |
| 205 | + let isEmpty; |
| 206 | + while (true) { |
| 207 | + isEmpty = true; |
| 208 | + for (let row = startRow; row <= endRow; row++) { |
| 209 | + if (rawValues[row]?.[column]?.effectiveValue) { |
| 210 | + column += 1; |
| 211 | + isEmpty = false; |
| 212 | + break; |
| 213 | + } |
| 214 | + } |
| 215 | + |
| 216 | + if (isEmpty) { |
| 217 | + endColumn = column - 1; |
| 218 | + break; |
| 219 | + } |
| 220 | + } |
| 221 | + } else { |
| 222 | + // Search for the first empty cell at startRow row. |
| 223 | + // Why? Because the first row with data contains headings (future property names). |
| 224 | + // And we can't build property names out of empty strings. |
| 225 | + endColumn = rawValues[startRow].findIndex((cell, index) => index > startColumn && !cell?.effectiveValue); |
| 226 | + endColumn = endColumn === -1 ? rawValues[startRow].length - 1 : endColumn - 1; |
| 227 | + |
| 228 | + // Search for the first fully empty row. |
| 229 | + for (let row = startRow; row < rawValues.length; row++) { |
| 230 | + const r = rawValues[row]; |
| 231 | + const isEmpty = r?.every((cell, index) => index <= endColumn && !cell?.effectiveValue); |
| 232 | + |
| 233 | + if (!r || isEmpty) { |
| 234 | + endRow = row - 1; |
| 235 | + break; |
| 236 | + } |
| 237 | + } |
| 238 | + endRow = endRow ?? rawValues.length - 1; |
| 239 | + } |
| 240 | + |
202 | 241 | let values = []; |
203 | 242 | for (let rowIndex = startRow; rowIndex <= endRow; rowIndex++) { |
204 | 243 | const row = rawValues[rowIndex]; |
|
0 commit comments