juxscript 1.1.233 → 1.1.234

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.
@@ -1 +1 @@
1
- {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
@@ -4,7 +4,6 @@ import { FileUpload } from './fileupload.js';
4
4
  import { Table } from './table.js';
5
5
  import { Tabs } from './tabs.js';
6
6
  import { Button } from './button.js';
7
- import { renderIcon } from './icons.js';
8
7
  import { DataFrameSource, LoadResult, dataFrameSource } from './dataframe/DataFrameSource.js';
9
8
  import { ImportSettingsModal } from './dataframe/ImportSettingsModal.js';
10
9
 
@@ -1,5 +1,6 @@
1
1
  import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
2
  import { FileStorage } from '../storage/FileStorage.js';
3
+ import { DataFrame } from '../storage/DataFrame.js';
3
4
  export interface FileUploadOptions {
4
5
  label?: string;
5
6
  accept?: string;
@@ -33,6 +34,15 @@ export declare class FileUpload extends BaseComponent<FileUploadState> {
33
34
  private _fileListElement;
34
35
  private _storage;
35
36
  private _metadata;
37
+ /** IndexedDB caching */
38
+ private _cacheEnabled;
39
+ private _cacheDriver;
40
+ private _cacheDbName;
41
+ private _cacheStoreName;
42
+ private _cacheKeyPrefix;
43
+ private _storageKeys;
44
+ private _lastStorageKey;
45
+ private _cachedDataFrames;
36
46
  constructor(id: string, options?: FileUploadOptions);
37
47
  protected getTriggerEvents(): readonly string[];
38
48
  protected getCallbackEvents(): readonly string[];
@@ -41,6 +51,35 @@ export declare class FileUpload extends BaseComponent<FileUploadState> {
41
51
  icon(value: string): this;
42
52
  variant(value: 'outline' | 'primary' | 'ghost' | 'secondary'): this;
43
53
  storage(store: FileStorage, metadata?: Record<string, any>): this;
54
+ /**
55
+ * Enable IndexedDB caching of uploaded files.
56
+ * After upload, the file is parsed and stored as a DataFrame.
57
+ * The storage key is available via .storageKey and a 'stored' event fires.
58
+ *
59
+ * Usage:
60
+ * jux.fileUpload('my-upload', { accept: '.csv' })
61
+ * .cache() // enable with defaults
62
+ * .cache({ prefix: 'raw/' }) // key prefix
63
+ * .cache({ dbName: 'my-db' }) // custom DB
64
+ *
65
+ * upload.bind('stored', (key, df) => {
66
+ * await data().fromStorage(key).into(myTable);
67
+ * });
68
+ */
69
+ cache(options?: {
70
+ enabled?: boolean;
71
+ dbName?: string;
72
+ storeName?: string;
73
+ prefix?: string;
74
+ }): this;
75
+ /** The IndexedDB key for the most recently cached upload, or null. */
76
+ get storageKey(): string | null;
77
+ /** Map of filename → storage key for all cached files this session. */
78
+ get storageKeys(): ReadonlyMap<string, string>;
79
+ /** Get the storage key for a specific filename. */
80
+ storageKeyFor(filename: string): string | null;
81
+ /** The cached DataFrame for the most recent upload (after 'stored' fires). */
82
+ get cachedDf(): DataFrame | null;
44
83
  clear(): this;
45
84
  clearFiles(): this;
46
85
  getValue(): File[];
@@ -50,6 +89,7 @@ export declare class FileUpload extends BaseComponent<FileUploadState> {
50
89
  protected _validateValue(files: File[]): boolean | string;
51
90
  private _updateFileList;
52
91
  private _formatFileSize;
92
+ private _cacheFile;
53
93
  update(prop: string, value: any): void;
54
94
  render(targetId?: string | HTMLElement | BaseComponent<any>): this;
55
95
  }
@@ -1 +1 @@
1
- {"version":3,"file":"fileupload.d.ts","sourceRoot":"","sources":["fileupload.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"fileupload.d.ts","sourceRoot":"","sources":["fileupload.ts"],"names":[],"mappings":"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"}
@@ -1,6 +1,7 @@
1
1
  import { BaseComponent } from './base/BaseComponent.js';
2
2
  import { renderIcon } from './icons.js';
3
3
  import { formatIdAsLabel } from '../utils/formatId.js';
4
+ import { TabularDriver } from '../storage/TabularDriver.js';
4
5
  // Event definitions
5
6
  const TRIGGER_EVENTS = [];
6
7
  const CALLBACK_EVENTS = ['change', 'filesSelected', 'clear', 'stored'];
@@ -28,6 +29,15 @@ export class FileUpload extends BaseComponent {
28
29
  });
29
30
  this._fileListElement = null;
30
31
  this._storage = null;
32
+ /** IndexedDB caching */
33
+ this._cacheEnabled = false;
34
+ this._cacheDriver = null;
35
+ this._cacheDbName = 'jux-dataframes';
36
+ this._cacheStoreName = 'frames';
37
+ this._cacheKeyPrefix = '';
38
+ this._storageKeys = new Map();
39
+ this._lastStorageKey = null;
40
+ this._cachedDataFrames = new Map();
31
41
  if (options.onValidate) {
32
42
  this._onValidate = options.onValidate;
33
43
  }
@@ -64,6 +74,55 @@ export class FileUpload extends BaseComponent {
64
74
  this._metadata = metadata;
65
75
  return this;
66
76
  }
77
+ /**
78
+ * Enable IndexedDB caching of uploaded files.
79
+ * After upload, the file is parsed and stored as a DataFrame.
80
+ * The storage key is available via .storageKey and a 'stored' event fires.
81
+ *
82
+ * Usage:
83
+ * jux.fileUpload('my-upload', { accept: '.csv' })
84
+ * .cache() // enable with defaults
85
+ * .cache({ prefix: 'raw/' }) // key prefix
86
+ * .cache({ dbName: 'my-db' }) // custom DB
87
+ *
88
+ * upload.bind('stored', (key, df) => {
89
+ * await data().fromStorage(key).into(myTable);
90
+ * });
91
+ */
92
+ cache(options = {}) {
93
+ this._cacheEnabled = options.enabled ?? true;
94
+ if (options.dbName)
95
+ this._cacheDbName = options.dbName;
96
+ if (options.storeName)
97
+ this._cacheStoreName = options.storeName;
98
+ if (options.prefix)
99
+ this._cacheKeyPrefix = options.prefix;
100
+ if (this._cacheEnabled && !this._cacheDriver) {
101
+ this._cacheDriver = new TabularDriver(this._cacheDbName, this._cacheStoreName);
102
+ }
103
+ return this;
104
+ }
105
+ /* ═════════════════════════════════════════════════════════════════
106
+ * STORAGE KEY ACCESSORS
107
+ * ═════════════════════════════════════════════════════════════════ */
108
+ /** The IndexedDB key for the most recently cached upload, or null. */
109
+ get storageKey() {
110
+ return this._lastStorageKey;
111
+ }
112
+ /** Map of filename → storage key for all cached files this session. */
113
+ get storageKeys() {
114
+ return this._storageKeys;
115
+ }
116
+ /** Get the storage key for a specific filename. */
117
+ storageKeyFor(filename) {
118
+ return this._storageKeys.get(filename) ?? null;
119
+ }
120
+ /** The cached DataFrame for the most recent upload (after 'stored' fires). */
121
+ get cachedDf() {
122
+ if (!this._lastStorageKey)
123
+ return null;
124
+ return this._cachedDataFrames.get(this._lastStorageKey) ?? null;
125
+ }
67
126
  clear() {
68
127
  this.state.files = [];
69
128
  if (this._inputElement) {
@@ -72,6 +131,7 @@ export class FileUpload extends BaseComponent {
72
131
  if (this._fileListElement) {
73
132
  this._fileListElement.innerHTML = '';
74
133
  }
134
+ this._lastStorageKey = null;
75
135
  this._triggerCallback('clear', null, null, this);
76
136
  return this;
77
137
  }
@@ -154,6 +214,46 @@ export class FileUpload extends BaseComponent {
154
214
  const i = Math.floor(Math.log(bytes) / Math.log(k));
155
215
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
156
216
  }
217
+ /* ═════════════════════════════════════════════════════════════════
218
+ * INTERNAL — CACHE FILE TO INDEXEDDB
219
+ * ═════════════════════════════════════════════════════════════════ */
220
+ async _cacheFile(file) {
221
+ if (!this._cacheEnabled || !this._cacheDriver)
222
+ return;
223
+ try {
224
+ const key = this._cacheKeyPrefix + file.name;
225
+ let df;
226
+ const isExcel = /\.(xlsx?|xls)$/i.test(file.name);
227
+ if (isExcel) {
228
+ const sheets = await this._cacheDriver.streamFileMultiSheet(file, {});
229
+ const sheetNames = Object.keys(sheets);
230
+ if (sheetNames.length === 0)
231
+ return;
232
+ df = sheets[sheetNames[0]];
233
+ }
234
+ else {
235
+ const text = await file.text();
236
+ df = this._cacheDriver.parseCSV(text, {
237
+ autoDetectDelimiter: true,
238
+ hasHeader: true
239
+ });
240
+ }
241
+ await this._cacheDriver.store(key, df, {
242
+ source: file.name,
243
+ size: file.size,
244
+ type: file.type,
245
+ cachedAt: new Date().toISOString(),
246
+ ...this._metadata
247
+ });
248
+ this._lastStorageKey = key;
249
+ this._storageKeys.set(file.name, key);
250
+ this._cachedDataFrames.set(key, df);
251
+ this._triggerCallback('stored', key, null, this);
252
+ }
253
+ catch (err) {
254
+ console.warn(`[FileUpload] Failed to cache ${file.name}:`, err.message);
255
+ }
256
+ }
157
257
  /* ═════════════════════════════════════════════════════════════════
158
258
  * RENDER
159
259
  * ═════════════════════════════════════════════════════════════════ */
@@ -170,7 +270,6 @@ export class FileUpload extends BaseComponent {
170
270
  wrapper.className += ` ${className}`;
171
271
  if (style)
172
272
  wrapper.setAttribute('style', style);
173
- // Hidden file input
174
273
  const fileInput = document.createElement('input');
175
274
  fileInput.type = 'file';
176
275
  fileInput.accept = accept;
@@ -178,7 +277,6 @@ export class FileUpload extends BaseComponent {
178
277
  fileInput.style.display = 'none';
179
278
  fileInput.id = `${this._id}-input`;
180
279
  this._inputElement = fileInput;
181
- // Button
182
280
  const btn = document.createElement('button');
183
281
  btn.className = `jux-fileupload-button jux-button-${variant}`;
184
282
  btn.type = 'button';
@@ -191,25 +289,21 @@ export class FileUpload extends BaseComponent {
191
289
  const labelSpan = document.createElement('span');
192
290
  labelSpan.textContent = label;
193
291
  btn.appendChild(labelSpan);
194
- // Button container
195
292
  const buttonContainer = document.createElement('div');
196
293
  buttonContainer.className = 'jux-fileupload-button-container';
197
294
  buttonContainer.appendChild(fileInput);
198
295
  buttonContainer.appendChild(btn);
199
296
  wrapper.appendChild(buttonContainer);
200
- // File list
201
297
  if (showFileList) {
202
298
  const fileList = document.createElement('div');
203
299
  fileList.className = 'jux-fileupload-filelist';
204
300
  this._fileListElement = fileList;
205
301
  wrapper.appendChild(fileList);
206
302
  }
207
- // Error element
208
303
  wrapper.appendChild(this._renderError());
209
- // Wire up button click to trigger file input
210
304
  btn.addEventListener('click', () => fileInput.click());
211
- // Wire up file input change
212
- fileInput.addEventListener('change', (e) => {
305
+ // Wire up file input change — with caching
306
+ fileInput.addEventListener('change', async (e) => {
213
307
  const files = Array.from(fileInput.files || []);
214
308
  this.state.files = files;
215
309
  this._updateFileList();
@@ -223,15 +317,19 @@ export class FileUpload extends BaseComponent {
223
317
  console.error('[FileUpload] Storage error:', err);
224
318
  });
225
319
  }
320
+ // Cache to IndexedDB as parsed DataFrames
321
+ if (this._cacheEnabled && files.length > 0) {
322
+ for (const file of files) {
323
+ await this._cacheFile(file);
324
+ }
325
+ }
226
326
  });
227
327
  this._wireStandardEvents(wrapper);
228
- // Validate on blur if already validated
229
328
  fileInput.addEventListener('blur', () => {
230
329
  if (this._hasBeenValidated) {
231
330
  this.validate();
232
331
  }
233
332
  });
234
- // Wire sync bindings for label
235
333
  const labelSync = this._syncBindings.find(b => b.property === 'label');
236
334
  if (labelSync) {
237
335
  const transform = labelSync.toComponent || ((v) => String(v));
@@ -2,6 +2,8 @@ import { BaseComponent, BaseState } from './base/BaseComponent.js';
2
2
  import { renderIcon } from './icons.js';
3
3
  import { formatIdAsLabel } from '../utils/formatId.js';
4
4
  import { FileStorage } from '../storage/FileStorage.js';
5
+ import { TabularDriver } from '../storage/TabularDriver.js';
6
+ import { DataFrame } from '../storage/DataFrame.js';
5
7
 
6
8
  // Event definitions
7
9
  const TRIGGER_EVENTS = [] as const;
@@ -43,6 +45,16 @@ export class FileUpload extends BaseComponent<FileUploadState> {
43
45
  private _storage: FileStorage | null = null;
44
46
  private _metadata: Record<string, any> | undefined;
45
47
 
48
+ /** IndexedDB caching */
49
+ private _cacheEnabled: boolean = false;
50
+ private _cacheDriver: TabularDriver | null = null;
51
+ private _cacheDbName: string = 'jux-dataframes';
52
+ private _cacheStoreName: string = 'frames';
53
+ private _cacheKeyPrefix: string = '';
54
+ private _storageKeys: Map<string, string> = new Map();
55
+ private _lastStorageKey: string | null = null;
56
+ private _cachedDataFrames: Map<string, DataFrame> = new Map();
57
+
46
58
  constructor(id: string, options: FileUploadOptions = {}) {
47
59
  super(id, {
48
60
  visible: true,
@@ -111,6 +123,64 @@ export class FileUpload extends BaseComponent<FileUploadState> {
111
123
  return this;
112
124
  }
113
125
 
126
+ /**
127
+ * Enable IndexedDB caching of uploaded files.
128
+ * After upload, the file is parsed and stored as a DataFrame.
129
+ * The storage key is available via .storageKey and a 'stored' event fires.
130
+ *
131
+ * Usage:
132
+ * jux.fileUpload('my-upload', { accept: '.csv' })
133
+ * .cache() // enable with defaults
134
+ * .cache({ prefix: 'raw/' }) // key prefix
135
+ * .cache({ dbName: 'my-db' }) // custom DB
136
+ *
137
+ * upload.bind('stored', (key, df) => {
138
+ * await data().fromStorage(key).into(myTable);
139
+ * });
140
+ */
141
+ cache(options: {
142
+ enabled?: boolean;
143
+ dbName?: string;
144
+ storeName?: string;
145
+ prefix?: string;
146
+ } = {}): this {
147
+ this._cacheEnabled = options.enabled ?? true;
148
+ if (options.dbName) this._cacheDbName = options.dbName;
149
+ if (options.storeName) this._cacheStoreName = options.storeName;
150
+ if (options.prefix) this._cacheKeyPrefix = options.prefix;
151
+
152
+ if (this._cacheEnabled && !this._cacheDriver) {
153
+ this._cacheDriver = new TabularDriver(this._cacheDbName, this._cacheStoreName);
154
+ }
155
+
156
+ return this;
157
+ }
158
+
159
+ /* ═════════════════════════════════════════════════════════════════
160
+ * STORAGE KEY ACCESSORS
161
+ * ═════════════════════════════════════════════════════════════════ */
162
+
163
+ /** The IndexedDB key for the most recently cached upload, or null. */
164
+ get storageKey(): string | null {
165
+ return this._lastStorageKey;
166
+ }
167
+
168
+ /** Map of filename → storage key for all cached files this session. */
169
+ get storageKeys(): ReadonlyMap<string, string> {
170
+ return this._storageKeys;
171
+ }
172
+
173
+ /** Get the storage key for a specific filename. */
174
+ storageKeyFor(filename: string): string | null {
175
+ return this._storageKeys.get(filename) ?? null;
176
+ }
177
+
178
+ /** The cached DataFrame for the most recent upload (after 'stored' fires). */
179
+ get cachedDf(): DataFrame | null {
180
+ if (!this._lastStorageKey) return null;
181
+ return this._cachedDataFrames.get(this._lastStorageKey) ?? null;
182
+ }
183
+
114
184
  clear(): this {
115
185
  this.state.files = [];
116
186
  if (this._inputElement) {
@@ -119,6 +189,7 @@ export class FileUpload extends BaseComponent<FileUploadState> {
119
189
  if (this._fileListElement) {
120
190
  this._fileListElement.innerHTML = '';
121
191
  }
192
+ this._lastStorageKey = null;
122
193
  this._triggerCallback('clear', null, null, this);
123
194
  return this;
124
195
  }
@@ -217,6 +288,49 @@ export class FileUpload extends BaseComponent<FileUploadState> {
217
288
  return Math.round(bytes / Math.pow(k, i) * 100) / 100 + ' ' + sizes[i];
218
289
  }
219
290
 
291
+ /* ═════════════════════════════════════════════════════════════════
292
+ * INTERNAL — CACHE FILE TO INDEXEDDB
293
+ * ═════════════════════════════════════════════════════════════════ */
294
+
295
+ private async _cacheFile(file: File): Promise<void> {
296
+ if (!this._cacheEnabled || !this._cacheDriver) return;
297
+
298
+ try {
299
+ const key = this._cacheKeyPrefix + file.name;
300
+ let df: DataFrame;
301
+
302
+ const isExcel = /\.(xlsx?|xls)$/i.test(file.name);
303
+ if (isExcel) {
304
+ const sheets = await this._cacheDriver.streamFileMultiSheet(file, {});
305
+ const sheetNames = Object.keys(sheets);
306
+ if (sheetNames.length === 0) return;
307
+ df = sheets[sheetNames[0]];
308
+ } else {
309
+ const text = await file.text();
310
+ df = this._cacheDriver.parseCSV(text, {
311
+ autoDetectDelimiter: true,
312
+ hasHeader: true
313
+ });
314
+ }
315
+
316
+ await this._cacheDriver.store(key, df, {
317
+ source: file.name,
318
+ size: file.size,
319
+ type: file.type,
320
+ cachedAt: new Date().toISOString(),
321
+ ...this._metadata
322
+ });
323
+
324
+ this._lastStorageKey = key;
325
+ this._storageKeys.set(file.name, key);
326
+ this._cachedDataFrames.set(key, df);
327
+
328
+ this._triggerCallback('stored', key, null, this);
329
+ } catch (err: any) {
330
+ console.warn(`[FileUpload] Failed to cache ${file.name}:`, err.message);
331
+ }
332
+ }
333
+
220
334
  /* ═════════════════════════════════════════════════════════════════
221
335
  * RENDER
222
336
  * ═════════════════════════════════════════════════════════════════ */
@@ -236,7 +350,6 @@ export class FileUpload extends BaseComponent<FileUploadState> {
236
350
  if (className) wrapper.className += ` ${className}`;
237
351
  if (style) wrapper.setAttribute('style', style);
238
352
 
239
- // Hidden file input
240
353
  const fileInput = document.createElement('input');
241
354
  fileInput.type = 'file';
242
355
  fileInput.accept = accept;
@@ -245,7 +358,6 @@ export class FileUpload extends BaseComponent<FileUploadState> {
245
358
  fileInput.id = `${this._id}-input`;
246
359
  this._inputElement = fileInput;
247
360
 
248
- // Button
249
361
  const btn = document.createElement('button');
250
362
  btn.className = `jux-fileupload-button jux-button-${variant}`;
251
363
  btn.type = 'button';
@@ -261,14 +373,12 @@ export class FileUpload extends BaseComponent<FileUploadState> {
261
373
  labelSpan.textContent = label;
262
374
  btn.appendChild(labelSpan);
263
375
 
264
- // Button container
265
376
  const buttonContainer = document.createElement('div');
266
377
  buttonContainer.className = 'jux-fileupload-button-container';
267
378
  buttonContainer.appendChild(fileInput);
268
379
  buttonContainer.appendChild(btn);
269
380
  wrapper.appendChild(buttonContainer);
270
381
 
271
- // File list
272
382
  if (showFileList) {
273
383
  const fileList = document.createElement('div');
274
384
  fileList.className = 'jux-fileupload-filelist';
@@ -276,14 +386,12 @@ export class FileUpload extends BaseComponent<FileUploadState> {
276
386
  wrapper.appendChild(fileList);
277
387
  }
278
388
 
279
- // Error element
280
389
  wrapper.appendChild(this._renderError());
281
390
 
282
- // Wire up button click to trigger file input
283
391
  btn.addEventListener('click', () => fileInput.click());
284
392
 
285
- // Wire up file input change
286
- fileInput.addEventListener('change', (e) => {
393
+ // Wire up file input change — with caching
394
+ fileInput.addEventListener('change', async (e) => {
287
395
  const files = Array.from(fileInput.files || []);
288
396
  this.state.files = files;
289
397
  this._updateFileList();
@@ -299,18 +407,23 @@ export class FileUpload extends BaseComponent<FileUploadState> {
299
407
  console.error('[FileUpload] Storage error:', err);
300
408
  });
301
409
  }
410
+
411
+ // Cache to IndexedDB as parsed DataFrames
412
+ if (this._cacheEnabled && files.length > 0) {
413
+ for (const file of files) {
414
+ await this._cacheFile(file);
415
+ }
416
+ }
302
417
  });
303
418
 
304
419
  this._wireStandardEvents(wrapper);
305
420
 
306
- // Validate on blur if already validated
307
421
  fileInput.addEventListener('blur', () => {
308
422
  if (this._hasBeenValidated) {
309
423
  this.validate();
310
424
  }
311
425
  });
312
426
 
313
- // Wire sync bindings for label
314
427
  const labelSync = this._syncBindings.find(b => b.property === 'label');
315
428
  if (labelSync) {
316
429
  const transform = labelSync.toComponent || ((v: any) => String(v));