@chalabi/svelte-sheets 2.0.2 → 2.0.4

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/Readme.md CHANGED
@@ -2,7 +2,7 @@
2
2
 
3
3
  Ultra fast excel sheets in the browser. Hugely inspired by JExcel, built on XLSX shoulders.
4
4
 
5
- => Find a live example [Here](https://ticruz38.github.io/svelte-sheets/)
5
+ => Find a live example [Here](https://chalabi2.github.io/svelte-sheets/)
6
6
 
7
7
  ### Motivation
8
8
 
package/dist/Sheet.svelte CHANGED
@@ -84,8 +84,8 @@ let extendRaf = null;
84
84
  // implement virtual list
85
85
  export let startY = 0;
86
86
  export let startX = 0;
87
- export let endY = 0;
88
- export let endX = 0;
87
+ export let endY = 50; // Initial render batch
88
+ export let endX = 20; // Initial render batch
89
89
  // virtual list state
90
90
  let height_map = [];
91
91
  let width_map = [];
@@ -180,45 +180,58 @@ export function onInputChange(value, row, column) {
180
180
  }
181
181
  function refresh(data, viewport_height, viewport_width) {
182
182
  return __awaiter(this, void 0, void 0, function* () {
183
+ if (!viewport)
184
+ return;
183
185
  const { scrollTop, scrollLeft } = viewport;
184
186
  yield tick(); // wait until the DOM is up to date
187
+ const defaultHeight = 24;
188
+ const defaultWidth = config.defaultColWidth || 50;
189
+ // Safety limits to prevent infinite loops
190
+ const maxRowsToRender = Math.max(data.length, Math.ceil((viewport_height + bottom_buffer * 2) / defaultHeight) + 10, 100);
191
+ const maxColsToRender = Math.max(columns.length, Math.ceil((viewport_width + right_buffer * 2) / defaultWidth) + 10, 50);
185
192
  let content_height = top - scrollTop - bottom_buffer;
186
193
  let content_width = left - scrollLeft - left_buffer;
187
194
  // vertical
188
195
  let y = startY;
189
- while (content_height < viewport_height) {
196
+ while (content_height < viewport_height && y < maxRowsToRender) {
190
197
  let row = rowElements[y - startY];
191
198
  if (!row) {
192
199
  endY = y + 1;
193
200
  yield tick(); // render the newly visible row
194
201
  row = rowElements[y - startY];
202
+ // If still no row after tick, break to prevent infinite loop
203
+ if (!row)
204
+ break;
195
205
  }
196
206
  const row_height = (height_map[y] = getRowHeight(y));
197
207
  content_height += row_height;
198
208
  y += 1;
199
209
  }
200
- endY = y;
210
+ endY = Math.max(y, 1);
201
211
  let remaining = data.length - endY;
202
- average_height = (top + content_height) / endY;
203
- bottom = remaining * average_height;
212
+ average_height = endY > 0 ? (top + content_height) / endY : defaultHeight;
213
+ bottom = remaining * (average_height || defaultHeight);
204
214
  height_map.length = data.length;
205
215
  // horizontal
206
216
  let x = startX;
207
- while (content_width < viewport_width) {
217
+ while (content_width < viewport_width && x < maxColsToRender) {
208
218
  let col = colElements[x - startX];
209
219
  if (!col) {
210
220
  endX = x + 1;
211
221
  yield tick(); // render the newly visible col
212
222
  col = colElements[x - startX];
223
+ // If still no col after tick, break to prevent infinite loop
224
+ if (!col)
225
+ break;
213
226
  }
214
227
  const col_width = (width_map[x] = getColumnsWidth(x));
215
228
  content_width += col_width;
216
229
  x += 1;
217
230
  }
218
- endX = x;
231
+ endX = Math.max(x, 1);
219
232
  let remains = columns.length - endX;
220
- average_width = (left + content_width) / endX;
221
- right = remains * average_width;
233
+ average_width = endX > 0 ? (left + content_width) / endX : defaultWidth;
234
+ right = remains * (average_width || defaultWidth);
222
235
  width_map.length = columns.length;
223
236
  });
224
237
  }
@@ -227,8 +240,11 @@ function refresh(data, viewport_height, viewport_width) {
227
240
  let scrollLeft;
228
241
  let scrollTop;
229
242
  $: (function scrollX() {
230
- if (!scrollLeft || !colElements)
243
+ if (scrollLeft === undefined || !colElements)
231
244
  return;
245
+ // Ensure we have valid dimensions to work with
246
+ const totalCols = Math.max(columns.length, endX, 1);
247
+ const defaultWidth = config.defaultColWidth || 50;
232
248
  // if (!scrollLeft) ;
233
249
  // horizontal scrolling
234
250
  for (let v = 0; v < colElements.length; v += 1) {
@@ -236,8 +252,8 @@ $: (function scrollX() {
236
252
  }
237
253
  let c = 0;
238
254
  let x = 0;
239
- while (true) {
240
- const col_width = width_map[c] || average_width;
255
+ while (c < totalCols) {
256
+ const col_width = width_map[c] || average_width || defaultWidth;
241
257
  if (x + col_width > scrollLeft - left_buffer) {
242
258
  startX = c;
243
259
  left = x;
@@ -246,31 +262,40 @@ $: (function scrollX() {
246
262
  x += col_width;
247
263
  c += 1;
248
264
  }
249
- while (true) {
250
- x += width_map[c] || average_width;
265
+ // Safety: ensure we found a valid startX
266
+ if (c >= totalCols) {
267
+ startX = Math.max(0, totalCols - 1);
268
+ left = x;
269
+ }
270
+ while (c < totalCols + Math.ceil((viewport_width + right_buffer) / defaultWidth)) {
271
+ const w = width_map[c] || average_width || defaultWidth;
272
+ x += w;
251
273
  c += 1;
252
274
  if (x > scrollLeft + viewport_width + right_buffer)
253
275
  break;
254
276
  }
255
- endX = c;
277
+ endX = Math.max(c, 1);
256
278
  const remaining = endX > columns.length
257
- ? (viewport_width + right_buffer) / 24
279
+ ? (viewport_width + right_buffer) / defaultWidth
258
280
  : columns.length - endX;
259
- average_width = x / endX;
281
+ average_width = endX > 0 ? x / endX : defaultWidth;
260
282
  // while (c < columns.length) width_map[c++] = average_width;
261
- right = remaining * average_width;
283
+ right = remaining * (average_width || defaultWidth);
262
284
  })();
263
285
  $: (function scrollY() {
264
- if (!scrollTop || !rowElements)
286
+ if (scrollTop === undefined || !rowElements)
265
287
  return;
288
+ // Ensure we have valid dimensions to work with
289
+ const totalRows = Math.max(data.length, endY, 1);
290
+ const defaultHeight = 24;
266
291
  // vertical scrolling
267
292
  for (let v = 0; v < rowElements.length; v += 1) {
268
293
  height_map[startY + v] = getRowHeight(startY + v);
269
294
  }
270
295
  let r = 0;
271
296
  let y = 0;
272
- while (true) {
273
- const row_height = height_map[r] || average_height;
297
+ while (r < totalRows) {
298
+ const row_height = height_map[r] || average_height || defaultHeight;
274
299
  if (y + row_height > scrollTop - top_buffer) {
275
300
  startY = r;
276
301
  top = y;
@@ -279,19 +304,25 @@ $: (function scrollY() {
279
304
  y += row_height;
280
305
  r += 1;
281
306
  }
282
- while (true) {
283
- y += height_map[r] || average_height;
307
+ // Safety: ensure we found a valid startY
308
+ if (r >= totalRows) {
309
+ startY = Math.max(0, totalRows - 1);
310
+ top = y;
311
+ }
312
+ while (r < totalRows + Math.ceil((viewport_height + bottom_buffer) / defaultHeight)) {
313
+ const h = height_map[r] || average_height || defaultHeight;
314
+ y += h;
284
315
  r += 1;
285
316
  if (y > scrollTop + viewport_height + bottom_buffer)
286
317
  break;
287
318
  }
288
- endY = r;
319
+ endY = Math.max(r, 1);
289
320
  const remaining = endY > data.length
290
- ? (viewport_height + bottom_buffer) / 24
321
+ ? (viewport_height + bottom_buffer) / defaultHeight
291
322
  : data.length - endY;
292
- average_height = y / endY;
323
+ average_height = endY > 0 ? y / endY : defaultHeight;
293
324
  // while (r < data.length) height_map[r++] = average_height;
294
- bottom = remaining * average_height;
325
+ bottom = remaining * (average_height || defaultHeight);
295
326
  })();
296
327
  function handle_scroll(e) {
297
328
  scrollTop = viewport.scrollTop;
@@ -315,6 +346,53 @@ onMount(() => {
315
346
  // document.addEventListener("touchmove", jexcel.touchEndControls);
316
347
  document === null || document === void 0 ? void 0 : document.addEventListener("keydown", onKeyDown);
317
348
  document === null || document === void 0 ? void 0 : document.addEventListener("keyup", onKeyUp);
349
+ // Initialize hotkeys in browser only
350
+ hotkeys("ctrl+z, command+z", function (e) {
351
+ if (isReadOnly)
352
+ return;
353
+ e.preventDefault();
354
+ cmdz = true;
355
+ if (historyIndex == 0)
356
+ return;
357
+ historyIndex -= 1;
358
+ const res = JSON.parse(history[historyIndex]);
359
+ data = res.data;
360
+ columns = res.columns;
361
+ rows = res.rows;
362
+ style = res.style;
363
+ setTimeout((_) => (cmdz = false), 10);
364
+ });
365
+ hotkeys("ctrl+shift+z, command+shift+z", function (e) {
366
+ if (isReadOnly)
367
+ return;
368
+ console.log("redo");
369
+ e.preventDefault();
370
+ cmdz = true;
371
+ if (history.length - 1 == historyIndex)
372
+ return;
373
+ historyIndex = historyIndex + 1;
374
+ const res = JSON.parse(history[historyIndex]);
375
+ data = res.data;
376
+ columns = res.columns;
377
+ rows = res.rows;
378
+ style = res.style;
379
+ setTimeout((_) => (cmdz = false), 10);
380
+ });
381
+ hotkeys("ctrl+c, command+c, ctrl+x, command+x", function (e, handler) {
382
+ var _a;
383
+ if (isReadOnly && ((_a = handler === null || handler === void 0 ? void 0 : handler.key) === null || _a === void 0 ? void 0 : _a.includes("x")))
384
+ return;
385
+ e.preventDefault();
386
+ clipboard = JSON.stringify(selected);
387
+ });
388
+ hotkeys("ctrl+v, command+v", function (e) {
389
+ if (isReadOnly)
390
+ return;
391
+ e.preventDefault();
392
+ if (!clipboard)
393
+ return;
394
+ data = pasteSelection(data, JSON.parse(clipboard), selected);
395
+ });
318
396
  }
319
397
  });
320
398
  function onMouseDown(e) {
@@ -521,52 +599,6 @@ function onKeyUp(e) {
521
599
  // on keyup just reinitialize everything
522
600
  keypressed[e.keyCode] = false;
523
601
  }
524
- hotkeys("ctrl+z, command+z", function (e) {
525
- if (isReadOnly)
526
- return;
527
- e.preventDefault();
528
- cmdz = true;
529
- if (historyIndex == 0)
530
- return;
531
- historyIndex -= 1;
532
- const res = JSON.parse(history[historyIndex]);
533
- data = res.data;
534
- columns = res.columns;
535
- rows = res.rows;
536
- style = res.style;
537
- setTimeout((_) => (cmdz = false), 10);
538
- });
539
- hotkeys("ctrl+shift+z, command+shift+z", function (e) {
540
- if (isReadOnly)
541
- return;
542
- console.log("redo");
543
- e.preventDefault();
544
- cmdz = true;
545
- if (history.length - 1 == historyIndex)
546
- return;
547
- historyIndex = historyIndex + 1;
548
- const res = JSON.parse(history[historyIndex]);
549
- data = res.data;
550
- columns = res.columns;
551
- rows = res.rows;
552
- style = res.style;
553
- setTimeout((_) => (cmdz = false), 10);
554
- });
555
- hotkeys("ctrl+c, command+c, ctrl+x, command+x", function (e, handler) {
556
- var _a;
557
- if (isReadOnly && ((_a = handler === null || handler === void 0 ? void 0 : handler.key) === null || _a === void 0 ? void 0 : _a.includes("x")))
558
- return;
559
- e.preventDefault();
560
- clipboard = JSON.stringify(selected);
561
- });
562
- hotkeys("ctrl+v, command+v", function (e) {
563
- if (isReadOnly)
564
- return;
565
- e.preventDefault();
566
- if (!clipboard)
567
- return;
568
- data = pasteSelection(data, JSON.parse(clipboard), selected);
569
- });
570
602
  function onKeyDown(e) {
571
603
  keypressed[e.keyCode] = true;
572
604
  if (!!edition) {
@@ -634,6 +666,9 @@ $: (() => {
634
666
  }
635
667
  data = d;
636
668
  }
669
+ else if (!columns.length) {
670
+ columns = Array.from({ length: data[0].length }, () => ({}));
671
+ }
637
672
  }
638
673
  // Adjust minimal dimensions
639
674
  var j = 0;
@@ -669,8 +704,8 @@ let rowLine;
669
704
  let square;
670
705
  let squareX;
671
706
  let squareY;
672
- let topLeft;
673
- let bottomRight;
707
+ let topLeft = { c: 0, r: 0 };
708
+ let bottomRight = { c: 0, r: 0 };
674
709
  $: if (mounted) {
675
710
  if (extendRaf)
676
711
  cancelAnimationFrame(extendRaf);
@@ -864,7 +899,7 @@ function historyPush(data, rows, columns, style) {
864
899
  {/each}
865
900
  </tr>
866
901
  </thead>
867
- <tbody class="draggable" bind:this={viewport} on:scroll={handle_scroll}>
902
+ <tbody class="draggable" on:scroll={handle_scroll}>
868
903
  {#each visibleY as r}
869
904
  <tr
870
905
  class="virtual-row"
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@chalabi/svelte-sheets",
3
- "version": "2.0.2",
3
+ "version": "2.0.4",
4
4
  "description": "Run your excel sheet in the browser!",
5
5
  "main": "./dist/index.js",
6
6
  "types": "./dist/index.d.ts",