juxscript 1.1.165 → 1.1.166
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.
|
@@ -29,6 +29,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
29
29
|
private _uploadRef;
|
|
30
30
|
private _storageKey;
|
|
31
31
|
private _pendingSource;
|
|
32
|
+
private _inlineUpload;
|
|
32
33
|
constructor(id: string, options?: DataFrameOptions);
|
|
33
34
|
protected getTriggerEvents(): readonly string[];
|
|
34
35
|
protected getCallbackEvents(): readonly string[];
|
|
@@ -44,6 +45,14 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
44
45
|
* Load from raw data — array of objects or Record<string, any[]>
|
|
45
46
|
*/
|
|
46
47
|
fromData(data: Record<string, any>[] | Record<string, any[]>): this;
|
|
48
|
+
/**
|
|
49
|
+
* Add an inline file upload control above the table.
|
|
50
|
+
* Auto-wires parsing, storage, and display — zero callbacks needed.
|
|
51
|
+
*
|
|
52
|
+
* @param label - Button/label text (default: 'Upload File')
|
|
53
|
+
* @param accept - File types (default: '.csv,.tsv,.txt,.xlsx,.xls')
|
|
54
|
+
*/
|
|
55
|
+
withUpload(label?: string, accept?: string): this;
|
|
47
56
|
/**
|
|
48
57
|
* Apply a transform to the current DataFrame and update the table
|
|
49
58
|
*/
|
|
@@ -100,6 +109,7 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
100
109
|
filterable(v: boolean): this;
|
|
101
110
|
paginated(v: boolean): this;
|
|
102
111
|
rowsPerPage(v: number): this;
|
|
112
|
+
private _updateStatus;
|
|
103
113
|
private _setDataFrame;
|
|
104
114
|
private _updateTable;
|
|
105
115
|
update(prop: string, value: any): void;
|
|
@@ -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,aAAa,EAAE,MAAM,6BAA6B,CAAC;AAC5D,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,KAAK,EAAE,MAAM,YAAY,CAAC;AAKnC,MAAM,WAAW,gBAAgB;IAC7B,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,OAAO,CAAC,EAAE,OAAO,CAAC;IAClB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,QAAQ,CAAC,EAAE,OAAO,CAAC;IACnB,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,KAAK,cAAc,GAAG,SAAS,GAAG;IAC9B,MAAM,EAAE,OAAO,CAAC;IAChB,UAAU,EAAE,MAAM,CAAC;IACnB,QAAQ,EAAE,MAAM,CAAC;IACjB,QAAQ,EAAE,MAAM,CAAC;CACpB,CAAC;AAEF,qBAAa,kBAAmB,SAAQ,aAAa,CAAC,cAAc,CAAC;IACjE,OAAO,CAAC,GAAG,CAA0B;IACrC,OAAO,CAAC,OAAO,CAAgB;IAC/B,OAAO,CAAC,MAAM,CAAsB;IACpC,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,6 +1,7 @@
|
|
|
1
1
|
import { BaseComponent } from './base/BaseComponent.js';
|
|
2
2
|
import { DataFrame } from '../storage/DataFrame.js';
|
|
3
3
|
import { TabularDriver } from '../storage/TabularDriver.js';
|
|
4
|
+
import { FileUpload } from './fileupload.js';
|
|
4
5
|
import { Table } from './table.js';
|
|
5
6
|
const TRIGGER_EVENTS = [];
|
|
6
7
|
const CALLBACK_EVENTS = ['load', 'error', 'transform'];
|
|
@@ -22,6 +23,7 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
22
23
|
this._uploadRef = null;
|
|
23
24
|
this._storageKey = null;
|
|
24
25
|
this._pendingSource = null;
|
|
26
|
+
this._inlineUpload = null;
|
|
25
27
|
this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
|
|
26
28
|
this._tableOptions = {
|
|
27
29
|
striped: options.striped ?? true,
|
|
@@ -103,6 +105,17 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
103
105
|
};
|
|
104
106
|
return this;
|
|
105
107
|
}
|
|
108
|
+
/**
|
|
109
|
+
* Add an inline file upload control above the table.
|
|
110
|
+
* Auto-wires parsing, storage, and display — zero callbacks needed.
|
|
111
|
+
*
|
|
112
|
+
* @param label - Button/label text (default: 'Upload File')
|
|
113
|
+
* @param accept - File types (default: '.csv,.tsv,.txt,.xlsx,.xls')
|
|
114
|
+
*/
|
|
115
|
+
withUpload(label = 'Upload File', accept = '.csv,.tsv,.txt,.xlsx,.xls') {
|
|
116
|
+
this._inlineUpload = { label, accept };
|
|
117
|
+
return this;
|
|
118
|
+
}
|
|
106
119
|
/* ═══════════════════════════════════════════════════
|
|
107
120
|
* TRANSFORM API (returns new DataFrameComponent view)
|
|
108
121
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -207,6 +220,11 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
207
220
|
/* ═══════════════════════════════════════════════════
|
|
208
221
|
* INTERNAL
|
|
209
222
|
* ═══════════════════════════════════════════════════ */
|
|
223
|
+
_updateStatus(text) {
|
|
224
|
+
const el = document.getElementById(`${this._id}-status`);
|
|
225
|
+
if (el)
|
|
226
|
+
el.textContent = text;
|
|
227
|
+
}
|
|
210
228
|
_setDataFrame(df, sourceName) {
|
|
211
229
|
this._df = df;
|
|
212
230
|
this.state.loaded = true;
|
|
@@ -214,12 +232,12 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
214
232
|
this.state.sourceName = sourceName;
|
|
215
233
|
this.state.rowCount = df.height;
|
|
216
234
|
this.state.colCount = df.width;
|
|
217
|
-
// Clean __EMPTY columns from xlsx artifacts
|
|
218
235
|
const cleanCols = df.columns.filter(c => !c.startsWith('__EMPTY'));
|
|
219
236
|
if (cleanCols.length < df.columns.length) {
|
|
220
237
|
this._df = df.select(...cleanCols);
|
|
221
238
|
}
|
|
222
239
|
this._updateTable();
|
|
240
|
+
this._updateStatus(`✅ ${sourceName} — ${this._df.height} rows × ${this._df.width} cols`);
|
|
223
241
|
this._triggerCallback('load', this._df, null, this);
|
|
224
242
|
}
|
|
225
243
|
_updateTable() {
|
|
@@ -241,6 +259,44 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
241
259
|
wrapper.className += ` ${className}`;
|
|
242
260
|
if (style)
|
|
243
261
|
wrapper.setAttribute('style', style);
|
|
262
|
+
// Inline upload (if withUpload was called)
|
|
263
|
+
if (this._inlineUpload) {
|
|
264
|
+
const upload = new FileUpload(`${this._id}-upload`, {
|
|
265
|
+
label: this._inlineUpload.label,
|
|
266
|
+
accept: this._inlineUpload.accept
|
|
267
|
+
});
|
|
268
|
+
// Wire it as a fromUpload source
|
|
269
|
+
this._uploadRef = upload;
|
|
270
|
+
this._pendingSource = async () => {
|
|
271
|
+
upload.bind('change', async (files) => {
|
|
272
|
+
if (!files || files.length === 0)
|
|
273
|
+
return;
|
|
274
|
+
const file = files[0];
|
|
275
|
+
this.state.loading = true;
|
|
276
|
+
this._updateStatus('⏳ Parsing ' + file.name + '...');
|
|
277
|
+
try {
|
|
278
|
+
const df = await this._driver.streamFile(file);
|
|
279
|
+
await this._driver.store(file.name, df, { source: file.name });
|
|
280
|
+
this._setDataFrame(df, file.name);
|
|
281
|
+
}
|
|
282
|
+
catch (err) {
|
|
283
|
+
this._triggerCallback('error', err.message, null, this);
|
|
284
|
+
this.state.loading = false;
|
|
285
|
+
this._updateStatus('❌ ' + err.message);
|
|
286
|
+
}
|
|
287
|
+
});
|
|
288
|
+
};
|
|
289
|
+
// Render upload into wrapper before status/table
|
|
290
|
+
const uploadContainer = document.createElement('div');
|
|
291
|
+
uploadContainer.className = 'jux-dataframe-upload';
|
|
292
|
+
uploadContainer.id = `${this._id}-upload-container`;
|
|
293
|
+
wrapper.appendChild(uploadContainer);
|
|
294
|
+
container.appendChild(wrapper);
|
|
295
|
+
upload.render(uploadContainer);
|
|
296
|
+
}
|
|
297
|
+
else {
|
|
298
|
+
container.appendChild(wrapper);
|
|
299
|
+
}
|
|
244
300
|
// Status bar
|
|
245
301
|
const statusBar = document.createElement('div');
|
|
246
302
|
statusBar.className = 'jux-dataframe-status';
|
|
@@ -248,9 +304,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
248
304
|
statusBar.style.cssText = 'font-size:13px;color:#888;margin-bottom:8px;';
|
|
249
305
|
statusBar.textContent = 'No data loaded.';
|
|
250
306
|
wrapper.appendChild(statusBar);
|
|
251
|
-
container.appendChild(wrapper);
|
|
252
307
|
// Create internal table
|
|
253
|
-
|
|
308
|
+
const tbl = new Table(`${this._id}-table`, {
|
|
254
309
|
striped: this._tableOptions.striped,
|
|
255
310
|
hoverable: this._tableOptions.hoverable,
|
|
256
311
|
sortable: this._tableOptions.sortable,
|
|
@@ -258,30 +313,13 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
258
313
|
paginated: this._tableOptions.paginated,
|
|
259
314
|
rowsPerPage: this._tableOptions.rowsPerPage
|
|
260
315
|
});
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
const origUpdate = this.update.bind(this);
|
|
264
|
-
this.update = (prop, value) => {
|
|
265
|
-
origUpdate(prop, value);
|
|
266
|
-
if (prop === 'loaded' || prop === 'loading' || prop === 'rowCount' || prop === 'colCount' || prop === 'sourceName') {
|
|
267
|
-
const el = document.getElementById(`${this._id}-status`);
|
|
268
|
-
if (el) {
|
|
269
|
-
if (this.state.loading) {
|
|
270
|
-
el.textContent = '⏳ Loading...';
|
|
271
|
-
}
|
|
272
|
-
else if (this.state.loaded) {
|
|
273
|
-
el.textContent = `✅ ${this.state.sourceName} — ${this.state.rowCount} rows × ${this.state.colCount} cols`;
|
|
274
|
-
}
|
|
275
|
-
else {
|
|
276
|
-
el.textContent = 'No data loaded.';
|
|
277
|
-
}
|
|
278
|
-
}
|
|
279
|
-
}
|
|
280
|
-
};
|
|
316
|
+
tbl.render(wrapper);
|
|
317
|
+
this._table = tbl;
|
|
281
318
|
// Execute pending data source
|
|
282
319
|
if (this._pendingSource) {
|
|
283
|
-
this._pendingSource
|
|
320
|
+
const fn = this._pendingSource;
|
|
284
321
|
this._pendingSource = null;
|
|
322
|
+
fn();
|
|
285
323
|
}
|
|
286
324
|
this._wireStandardEvents(wrapper);
|
|
287
325
|
return this;
|
|
@@ -42,6 +42,7 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
42
42
|
private _uploadRef: FileUpload | null = null;
|
|
43
43
|
private _storageKey: string | null = null;
|
|
44
44
|
private _pendingSource: (() => Promise<void>) | null = null;
|
|
45
|
+
private _inlineUpload: { label: string; accept: string } | null = null;
|
|
45
46
|
|
|
46
47
|
constructor(id: string, options: DataFrameOptions = {}) {
|
|
47
48
|
super(id, {
|
|
@@ -144,6 +145,18 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
144
145
|
return this;
|
|
145
146
|
}
|
|
146
147
|
|
|
148
|
+
/**
|
|
149
|
+
* Add an inline file upload control above the table.
|
|
150
|
+
* Auto-wires parsing, storage, and display — zero callbacks needed.
|
|
151
|
+
*
|
|
152
|
+
* @param label - Button/label text (default: 'Upload File')
|
|
153
|
+
* @param accept - File types (default: '.csv,.tsv,.txt,.xlsx,.xls')
|
|
154
|
+
*/
|
|
155
|
+
withUpload(label: string = 'Upload File', accept: string = '.csv,.tsv,.txt,.xlsx,.xls'): this {
|
|
156
|
+
this._inlineUpload = { label, accept };
|
|
157
|
+
return this;
|
|
158
|
+
}
|
|
159
|
+
|
|
147
160
|
/* ═══════════════════════════════════════════════════
|
|
148
161
|
* TRANSFORM API (returns new DataFrameComponent view)
|
|
149
162
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -268,6 +281,11 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
268
281
|
* INTERNAL
|
|
269
282
|
* ═══════════════════════════════════════════════════ */
|
|
270
283
|
|
|
284
|
+
private _updateStatus(text: string): void {
|
|
285
|
+
const el = document.getElementById(`${this._id}-status`);
|
|
286
|
+
if (el) el.textContent = text;
|
|
287
|
+
}
|
|
288
|
+
|
|
271
289
|
private _setDataFrame(df: DataFrame, sourceName: string): void {
|
|
272
290
|
this._df = df;
|
|
273
291
|
this.state.loaded = true;
|
|
@@ -276,13 +294,13 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
276
294
|
this.state.rowCount = df.height;
|
|
277
295
|
this.state.colCount = df.width;
|
|
278
296
|
|
|
279
|
-
// Clean __EMPTY columns from xlsx artifacts
|
|
280
297
|
const cleanCols = df.columns.filter(c => !c.startsWith('__EMPTY'));
|
|
281
298
|
if (cleanCols.length < df.columns.length) {
|
|
282
299
|
this._df = df.select(...cleanCols);
|
|
283
300
|
}
|
|
284
301
|
|
|
285
302
|
this._updateTable();
|
|
303
|
+
this._updateStatus(`✅ ${sourceName} — ${this._df!.height} rows × ${this._df!.width} cols`);
|
|
286
304
|
this._triggerCallback('load', this._df, null, this);
|
|
287
305
|
}
|
|
288
306
|
|
|
@@ -307,6 +325,45 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
307
325
|
if (className) wrapper.className += ` ${className}`;
|
|
308
326
|
if (style) wrapper.setAttribute('style', style);
|
|
309
327
|
|
|
328
|
+
// Inline upload (if withUpload was called)
|
|
329
|
+
if (this._inlineUpload) {
|
|
330
|
+
const upload = new FileUpload(`${this._id}-upload`, {
|
|
331
|
+
label: this._inlineUpload.label,
|
|
332
|
+
accept: this._inlineUpload.accept
|
|
333
|
+
});
|
|
334
|
+
|
|
335
|
+
// Wire it as a fromUpload source
|
|
336
|
+
this._uploadRef = upload;
|
|
337
|
+
this._pendingSource = async () => {
|
|
338
|
+
upload.bind('change', async (files: File[]) => {
|
|
339
|
+
if (!files || files.length === 0) return;
|
|
340
|
+
const file = files[0];
|
|
341
|
+
this.state.loading = true;
|
|
342
|
+
this._updateStatus('⏳ Parsing ' + file.name + '...');
|
|
343
|
+
|
|
344
|
+
try {
|
|
345
|
+
const df = await this._driver.streamFile(file);
|
|
346
|
+
await this._driver.store(file.name, df, { source: file.name });
|
|
347
|
+
this._setDataFrame(df, file.name);
|
|
348
|
+
} catch (err: any) {
|
|
349
|
+
this._triggerCallback('error', err.message, null, this);
|
|
350
|
+
this.state.loading = false;
|
|
351
|
+
this._updateStatus('❌ ' + err.message);
|
|
352
|
+
}
|
|
353
|
+
});
|
|
354
|
+
};
|
|
355
|
+
|
|
356
|
+
// Render upload into wrapper before status/table
|
|
357
|
+
const uploadContainer = document.createElement('div');
|
|
358
|
+
uploadContainer.className = 'jux-dataframe-upload';
|
|
359
|
+
uploadContainer.id = `${this._id}-upload-container`;
|
|
360
|
+
wrapper.appendChild(uploadContainer);
|
|
361
|
+
container.appendChild(wrapper);
|
|
362
|
+
upload.render(uploadContainer);
|
|
363
|
+
} else {
|
|
364
|
+
container.appendChild(wrapper);
|
|
365
|
+
}
|
|
366
|
+
|
|
310
367
|
// Status bar
|
|
311
368
|
const statusBar = document.createElement('div');
|
|
312
369
|
statusBar.className = 'jux-dataframe-status';
|
|
@@ -315,10 +372,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
315
372
|
statusBar.textContent = 'No data loaded.';
|
|
316
373
|
wrapper.appendChild(statusBar);
|
|
317
374
|
|
|
318
|
-
container.appendChild(wrapper);
|
|
319
|
-
|
|
320
375
|
// Create internal table
|
|
321
|
-
|
|
376
|
+
const tbl = new Table(`${this._id}-table`, {
|
|
322
377
|
striped: this._tableOptions.striped,
|
|
323
378
|
hoverable: this._tableOptions.hoverable,
|
|
324
379
|
sortable: this._tableOptions.sortable,
|
|
@@ -326,30 +381,14 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
326
381
|
paginated: this._tableOptions.paginated,
|
|
327
382
|
rowsPerPage: this._tableOptions.rowsPerPage
|
|
328
383
|
});
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
// Watch state for status updates
|
|
332
|
-
const origUpdate = this.update.bind(this);
|
|
333
|
-
this.update = (prop: string, value: any) => {
|
|
334
|
-
origUpdate(prop, value);
|
|
335
|
-
if (prop === 'loaded' || prop === 'loading' || prop === 'rowCount' || prop === 'colCount' || prop === 'sourceName') {
|
|
336
|
-
const el = document.getElementById(`${this._id}-status`);
|
|
337
|
-
if (el) {
|
|
338
|
-
if (this.state.loading) {
|
|
339
|
-
el.textContent = '⏳ Loading...';
|
|
340
|
-
} else if (this.state.loaded) {
|
|
341
|
-
el.textContent = `✅ ${this.state.sourceName} — ${this.state.rowCount} rows × ${this.state.colCount} cols`;
|
|
342
|
-
} else {
|
|
343
|
-
el.textContent = 'No data loaded.';
|
|
344
|
-
}
|
|
345
|
-
}
|
|
346
|
-
}
|
|
347
|
-
};
|
|
384
|
+
tbl.render(wrapper);
|
|
385
|
+
this._table = tbl;
|
|
348
386
|
|
|
349
387
|
// Execute pending data source
|
|
350
388
|
if (this._pendingSource) {
|
|
351
|
-
this._pendingSource
|
|
389
|
+
const fn = this._pendingSource;
|
|
352
390
|
this._pendingSource = null;
|
|
391
|
+
fn();
|
|
353
392
|
}
|
|
354
393
|
|
|
355
394
|
this._wireStandardEvents(wrapper);
|