juxscript 1.1.233 → 1.1.235
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/index.d.ts +2 -0
- package/index.d.ts.map +1 -1
- package/index.js +2 -0
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.ts +0 -1
- package/lib/components/fileupload.d.ts +40 -0
- package/lib/components/fileupload.d.ts.map +1 -1
- package/lib/components/fileupload.js +108 -10
- package/lib/components/fileupload.ts +123 -10
- package/lib/data/DataPipeline.d.ts +165 -0
- package/lib/data/DataPipeline.d.ts.map +1 -0
- package/lib/data/DataPipeline.js +324 -0
- package/lib/data/DataPipeline.ts +388 -0
- package/package.json +1 -1
package/index.d.ts
CHANGED
|
@@ -57,6 +57,8 @@ export { DataFrameSource, dataFrameSource } from './lib/storage/DataFrameSource.
|
|
|
57
57
|
export type { SourceOptions, APIOptions, StreamOptions, MultiSheetResult } from './lib/storage/DataFrameSource.js';
|
|
58
58
|
export { indexedDBDriver } from './lib/storage/IndexedDBDriver.js';
|
|
59
59
|
export { fileStorage } from './lib/storage/FileStorage.js';
|
|
60
|
+
export { data as pipeline, DataPipeline } from './lib/data/DataPipeline.js';
|
|
61
|
+
export type { PipelineOptions, FetchOptions, IntoOptions } from './lib/data/DataPipeline.js';
|
|
60
62
|
export { dataframe } from './lib/components/dataframe.js';
|
|
61
63
|
import { VStack, vstack } from './lib/components/stack/VStack.js';
|
|
62
64
|
import { HStack, hstack } from './lib/components/stack/HStack.js';
|
package/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,GAAG,EAAE,MAAM,yBAAyB,CAAC;AAC9C,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,MAAM,EAAE,MAAM,4BAA4B,CAAC;AACpD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,KAAK,EAAE,MAAM,2BAA2B,CAAC;AAClD,OAAO,EAAE,QAAQ,EAAE,MAAM,8BAA8B,CAAC;AACxD,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;AAChD,OAAO,EAAE,SAAS,EAAE,MAAM,+BAA+B,CAAC;AAC1D,OAAO,EAAE,IAAI,EAAE,MAAM,0BAA0B,CAAC;
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["index.ts"],"names":[],"mappings":"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"}
|
package/index.js
CHANGED
|
@@ -58,6 +58,8 @@ export { DataFrame, GroupedDataFrame } from './lib/storage/DataFrame.js';
|
|
|
58
58
|
export { DataFrameSource, dataFrameSource } from './lib/storage/DataFrameSource.js';
|
|
59
59
|
export { indexedDBDriver } from './lib/storage/IndexedDBDriver.js';
|
|
60
60
|
export { fileStorage } from './lib/storage/FileStorage.js';
|
|
61
|
+
// Data Pipeline
|
|
62
|
+
export { data as pipeline, DataPipeline } from './lib/data/DataPipeline.js';
|
|
61
63
|
// DataFrame UI Component (separate from data layer)
|
|
62
64
|
export { dataframe } from './lib/components/dataframe.js';
|
|
63
65
|
// Import Stack components (already added earlier)
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACnE,OAAO,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AACpD,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;
|
|
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":"AAAA,OAAO,EAAE,aAAa,EAAE,SAAS,EAAE,MAAM,yBAAyB,CAAC;AAGnE,OAAO,EAAE,WAAW,EAAE,MAAM,2BAA2B,CAAC;
|
|
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));
|