juxscript 1.1.179 → 1.1.181
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/lib/components/dataframe.d.ts +12 -0
- package/lib/components/dataframe.d.ts.map +1 -1
- package/lib/components/dataframe.js +36 -12
- package/lib/components/dataframe.ts +42 -12
- package/lib/storage/TabularDriver.d.ts +5 -4
- package/lib/storage/TabularDriver.d.ts.map +1 -1
- package/lib/storage/TabularDriver.js +73 -11
- package/lib/storage/TabularDriver.ts +88 -12
- package/lib/styles/shadcn.css +149 -303
- package/package.json +1 -1
|
@@ -14,6 +14,8 @@ export interface DataFrameOptions {
|
|
|
14
14
|
rowsPerPage?: number;
|
|
15
15
|
showStatus?: boolean;
|
|
16
16
|
icon?: string;
|
|
17
|
+
maxSheetSize?: number;
|
|
18
|
+
sheetChunkSize?: number;
|
|
17
19
|
style?: string;
|
|
18
20
|
class?: string;
|
|
19
21
|
}
|
|
@@ -36,6 +38,8 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
36
38
|
private _inlineUpload;
|
|
37
39
|
private _showStatus;
|
|
38
40
|
private _icon;
|
|
41
|
+
private _maxSheetSize;
|
|
42
|
+
private _sheetChunkSize;
|
|
39
43
|
constructor(id: string, options?: DataFrameOptions);
|
|
40
44
|
protected getTriggerEvents(): readonly string[];
|
|
41
45
|
protected getCallbackEvents(): readonly string[];
|
|
@@ -68,6 +72,14 @@ export declare class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
68
72
|
filterable(v: boolean): this;
|
|
69
73
|
paginated(v: boolean): this;
|
|
70
74
|
rowsPerPage(v: number): this;
|
|
75
|
+
/**
|
|
76
|
+
* ✅ NEW: Set max rows per sheet (prevents memory issues with huge Excel files)
|
|
77
|
+
*/
|
|
78
|
+
maxSheetSize(v: number): this;
|
|
79
|
+
/**
|
|
80
|
+
* ✅ NEW: Set chunk size for processing large sheets
|
|
81
|
+
*/
|
|
82
|
+
sheetChunkSize(v: number): this;
|
|
71
83
|
/**
|
|
72
84
|
* ✅ FIXED: Render multiple Excel sheets as tabs
|
|
73
85
|
*/
|
|
@@ -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;AAOnC,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,UAAU,CAAC,EAAE,OAAO,CAAC;IACrB,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,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,KAAK,CAAqB;IAClC,OAAO,CAAC,OAAO,CAAqC;IACpD,OAAO,CAAC,aAAa,CAOnB;IACF,OAAO,CAAC,UAAU,CAA2B;IAC7C,OAAO,CAAC,WAAW,CAAuB;IAC1C,OAAO,CAAC,cAAc,CAAsC;IAC5D,OAAO,CAAC,aAAa,CAAgE;IACrF,OAAO,CAAC,WAAW,CAAiB;IACpC,OAAO,CAAC,KAAK,CAAc;
|
|
1
|
+
{"version":3,"file":"dataframe.d.ts","sourceRoot":"","sources":["dataframe.ts"],"names":[],"mappings":"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"}
|
|
@@ -30,6 +30,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
30
30
|
this._inlineUpload = null;
|
|
31
31
|
this._showStatus = true;
|
|
32
32
|
this._icon = '';
|
|
33
|
+
this._maxSheetSize = 100000; // ✅ Default 100k rows
|
|
34
|
+
this._sheetChunkSize = 10000; // ✅ Default 10k chunk
|
|
33
35
|
this._driver = new TabularDriver(options.dbName ?? 'jux-dataframes', options.storeName ?? 'frames');
|
|
34
36
|
this._showStatus = options.showStatus ?? true;
|
|
35
37
|
this._icon = options.icon ?? '';
|
|
@@ -41,6 +43,8 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
41
43
|
paginated: options.paginated ?? true,
|
|
42
44
|
rowsPerPage: options.rowsPerPage ?? 25
|
|
43
45
|
};
|
|
46
|
+
this._maxSheetSize = options.maxSheetSize ?? 100000;
|
|
47
|
+
this._sheetChunkSize = options.sheetChunkSize ?? 10000;
|
|
44
48
|
}
|
|
45
49
|
getTriggerEvents() { return TRIGGER_EVENTS; }
|
|
46
50
|
getCallbackEvents() { return CALLBACK_EVENTS; }
|
|
@@ -86,25 +90,29 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
86
90
|
this.state.loading = true;
|
|
87
91
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
88
92
|
try {
|
|
89
|
-
// ✅ Check if multi-sheet Excel
|
|
90
93
|
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
91
94
|
file.name.toLowerCase().endsWith('.xls');
|
|
92
95
|
if (isExcel) {
|
|
93
|
-
|
|
96
|
+
// ✅ Pass chunking options for large files
|
|
97
|
+
const sheets = await this._driver.streamFileMultiSheet(file, {
|
|
98
|
+
maxSheetSize: this._maxSheetSize,
|
|
99
|
+
sheetChunkSize: this._sheetChunkSize,
|
|
100
|
+
onProgress: (loaded, total) => {
|
|
101
|
+
const pct = total ? Math.round((loaded / total) * 100) : 0;
|
|
102
|
+
this._updateStatus(`⏳ Parsing ${file.name}... ${pct}%`, 'loading');
|
|
103
|
+
}
|
|
104
|
+
});
|
|
94
105
|
const sheetNames = Object.keys(sheets);
|
|
95
|
-
// Store first sheet to IndexedDB
|
|
96
106
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
97
107
|
if (sheetNames.length > 1) {
|
|
98
|
-
// ✅ Multi-sheet: render tabs
|
|
99
108
|
this._renderMultiSheet(sheets, file.name);
|
|
100
109
|
}
|
|
101
110
|
else {
|
|
102
|
-
// Single sheet: render normally
|
|
103
111
|
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
104
112
|
}
|
|
105
113
|
}
|
|
106
114
|
else {
|
|
107
|
-
// CSV/TSV:
|
|
115
|
+
// CSV/TSV: use existing streaming
|
|
108
116
|
const df = await this._driver.streamFile(file);
|
|
109
117
|
await this._driver.store(file.name, df, { source: file.name });
|
|
110
118
|
this._setDataFrame(df, file.name);
|
|
@@ -208,6 +216,20 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
208
216
|
filterable(v) { this._tableOptions.filterable = v; return this; }
|
|
209
217
|
paginated(v) { this._tableOptions.paginated = v; return this; }
|
|
210
218
|
rowsPerPage(v) { this._tableOptions.rowsPerPage = v; return this; }
|
|
219
|
+
/**
|
|
220
|
+
* ✅ NEW: Set max rows per sheet (prevents memory issues with huge Excel files)
|
|
221
|
+
*/
|
|
222
|
+
maxSheetSize(v) {
|
|
223
|
+
this._maxSheetSize = v;
|
|
224
|
+
return this;
|
|
225
|
+
}
|
|
226
|
+
/**
|
|
227
|
+
* ✅ NEW: Set chunk size for processing large sheets
|
|
228
|
+
*/
|
|
229
|
+
sheetChunkSize(v) {
|
|
230
|
+
this._sheetChunkSize = v;
|
|
231
|
+
return this;
|
|
232
|
+
}
|
|
211
233
|
/* ═══════════════════════════════════════════════════
|
|
212
234
|
* MULTI-SHEET RENDERING
|
|
213
235
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -460,25 +482,27 @@ export class DataFrameComponent extends BaseComponent {
|
|
|
460
482
|
this.state.loading = true;
|
|
461
483
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
462
484
|
try {
|
|
463
|
-
// ✅ Check if multi-sheet Excel
|
|
464
485
|
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
465
486
|
file.name.toLowerCase().endsWith('.xls');
|
|
466
487
|
if (isExcel) {
|
|
467
|
-
const sheets = await this._driver.streamFileMultiSheet(file
|
|
488
|
+
const sheets = await this._driver.streamFileMultiSheet(file, {
|
|
489
|
+
maxSheetSize: this._maxSheetSize,
|
|
490
|
+
sheetChunkSize: this._sheetChunkSize,
|
|
491
|
+
onProgress: (loaded, total) => {
|
|
492
|
+
const pct = total ? Math.round((loaded / total) * 100) : 0;
|
|
493
|
+
this._updateStatus(`⏳ Parsing ${file.name}... ${pct}%`, 'loading');
|
|
494
|
+
}
|
|
495
|
+
});
|
|
468
496
|
const sheetNames = Object.keys(sheets);
|
|
469
|
-
// Store first sheet to IndexedDB
|
|
470
497
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
471
498
|
if (sheetNames.length > 1) {
|
|
472
|
-
// ✅ Multi-sheet: render tabs
|
|
473
499
|
this._renderMultiSheet(sheets, file.name);
|
|
474
500
|
}
|
|
475
501
|
else {
|
|
476
|
-
// Single sheet: render normally
|
|
477
502
|
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
478
503
|
}
|
|
479
504
|
}
|
|
480
505
|
else {
|
|
481
|
-
// CSV/TSV: single sheet
|
|
482
506
|
const df = await this._driver.streamFile(file);
|
|
483
507
|
await this._driver.store(file.name, df, { source: file.name });
|
|
484
508
|
this._setDataFrame(df, file.name);
|
|
@@ -20,6 +20,8 @@ export interface DataFrameOptions {
|
|
|
20
20
|
rowsPerPage?: number;
|
|
21
21
|
showStatus?: boolean;
|
|
22
22
|
icon?: string;
|
|
23
|
+
maxSheetSize?: number; // ✅ NEW: Max rows per sheet (default: 100k)
|
|
24
|
+
sheetChunkSize?: number; // ✅ NEW: Chunk size for large sheets (default: 10k)
|
|
23
25
|
style?: string;
|
|
24
26
|
class?: string;
|
|
25
27
|
}
|
|
@@ -51,6 +53,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
51
53
|
private _inlineUpload: { label: string; accept: string; icon: string } | null = null;
|
|
52
54
|
private _showStatus: boolean = true;
|
|
53
55
|
private _icon: string = '';
|
|
56
|
+
private _maxSheetSize: number = 100000; // ✅ Default 100k rows
|
|
57
|
+
private _sheetChunkSize: number = 10000; // ✅ Default 10k chunk
|
|
54
58
|
|
|
55
59
|
constructor(id: string, options: DataFrameOptions = {}) {
|
|
56
60
|
super(id, {
|
|
@@ -81,6 +85,8 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
81
85
|
paginated: options.paginated ?? true,
|
|
82
86
|
rowsPerPage: options.rowsPerPage ?? 25
|
|
83
87
|
};
|
|
88
|
+
this._maxSheetSize = options.maxSheetSize ?? 100000;
|
|
89
|
+
this._sheetChunkSize = options.sheetChunkSize ?? 10000;
|
|
84
90
|
}
|
|
85
91
|
|
|
86
92
|
protected getTriggerEvents(): readonly string[] { return TRIGGER_EVENTS; }
|
|
@@ -124,26 +130,31 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
124
130
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
125
131
|
|
|
126
132
|
try {
|
|
127
|
-
// ✅ Check if multi-sheet Excel
|
|
128
133
|
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
129
134
|
file.name.toLowerCase().endsWith('.xls');
|
|
130
135
|
|
|
131
136
|
if (isExcel) {
|
|
132
|
-
|
|
137
|
+
// ✅ Pass chunking options for large files
|
|
138
|
+
const sheets = await this._driver.streamFileMultiSheet(file, {
|
|
139
|
+
maxSheetSize: this._maxSheetSize,
|
|
140
|
+
sheetChunkSize: this._sheetChunkSize,
|
|
141
|
+
onProgress: (loaded, total) => {
|
|
142
|
+
const pct = total ? Math.round((loaded / total) * 100) : 0;
|
|
143
|
+
this._updateStatus(`⏳ Parsing ${file.name}... ${pct}%`, 'loading');
|
|
144
|
+
}
|
|
145
|
+
});
|
|
146
|
+
|
|
133
147
|
const sheetNames = Object.keys(sheets);
|
|
134
148
|
|
|
135
|
-
// Store first sheet to IndexedDB
|
|
136
149
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
137
150
|
|
|
138
151
|
if (sheetNames.length > 1) {
|
|
139
|
-
// ✅ Multi-sheet: render tabs
|
|
140
152
|
this._renderMultiSheet(sheets, file.name);
|
|
141
153
|
} else {
|
|
142
|
-
// Single sheet: render normally
|
|
143
154
|
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
144
155
|
}
|
|
145
156
|
} else {
|
|
146
|
-
// CSV/TSV:
|
|
157
|
+
// CSV/TSV: use existing streaming
|
|
147
158
|
const df = await this._driver.streamFile(file);
|
|
148
159
|
await this._driver.store(file.name, df, { source: file.name });
|
|
149
160
|
this._setDataFrame(df, file.name);
|
|
@@ -257,6 +268,22 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
257
268
|
paginated(v: boolean): this { this._tableOptions.paginated = v; return this; }
|
|
258
269
|
rowsPerPage(v: number): this { this._tableOptions.rowsPerPage = v; return this; }
|
|
259
270
|
|
|
271
|
+
/**
|
|
272
|
+
* ✅ NEW: Set max rows per sheet (prevents memory issues with huge Excel files)
|
|
273
|
+
*/
|
|
274
|
+
maxSheetSize(v: number): this {
|
|
275
|
+
this._maxSheetSize = v;
|
|
276
|
+
return this;
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
/**
|
|
280
|
+
* ✅ NEW: Set chunk size for processing large sheets
|
|
281
|
+
*/
|
|
282
|
+
sheetChunkSize(v: number): this {
|
|
283
|
+
this._sheetChunkSize = v;
|
|
284
|
+
return this;
|
|
285
|
+
}
|
|
286
|
+
|
|
260
287
|
/* ═══════════════════════════════════════════════════
|
|
261
288
|
* MULTI-SHEET RENDERING
|
|
262
289
|
* ═══════════════════════════════════════════════════ */
|
|
@@ -567,26 +594,29 @@ export class DataFrameComponent extends BaseComponent<DataFrameState> {
|
|
|
567
594
|
this._updateStatus('⏳ Parsing ' + file.name + '...', 'loading');
|
|
568
595
|
|
|
569
596
|
try {
|
|
570
|
-
// ✅ Check if multi-sheet Excel
|
|
571
597
|
const isExcel = file.name.toLowerCase().endsWith('.xlsx') ||
|
|
572
598
|
file.name.toLowerCase().endsWith('.xls');
|
|
573
599
|
|
|
574
600
|
if (isExcel) {
|
|
575
|
-
const sheets = await this._driver.streamFileMultiSheet(file
|
|
601
|
+
const sheets = await this._driver.streamFileMultiSheet(file, {
|
|
602
|
+
maxSheetSize: this._maxSheetSize,
|
|
603
|
+
sheetChunkSize: this._sheetChunkSize,
|
|
604
|
+
onProgress: (loaded, total) => {
|
|
605
|
+
const pct = total ? Math.round((loaded / total) * 100) : 0;
|
|
606
|
+
this._updateStatus(`⏳ Parsing ${file.name}... ${pct}%`, 'loading');
|
|
607
|
+
}
|
|
608
|
+
});
|
|
609
|
+
|
|
576
610
|
const sheetNames = Object.keys(sheets);
|
|
577
611
|
|
|
578
|
-
// Store first sheet to IndexedDB
|
|
579
612
|
await this._driver.store(file.name, sheets[sheetNames[0]], { source: file.name });
|
|
580
613
|
|
|
581
614
|
if (sheetNames.length > 1) {
|
|
582
|
-
// ✅ Multi-sheet: render tabs
|
|
583
615
|
this._renderMultiSheet(sheets, file.name);
|
|
584
616
|
} else {
|
|
585
|
-
// Single sheet: render normally
|
|
586
617
|
this._setDataFrame(sheets[sheetNames[0]], file.name);
|
|
587
618
|
}
|
|
588
619
|
} else {
|
|
589
|
-
// CSV/TSV: single sheet
|
|
590
620
|
const df = await this._driver.streamFile(file);
|
|
591
621
|
await this._driver.store(file.name, df, { source: file.name });
|
|
592
622
|
this._setDataFrame(df, file.name);
|
|
@@ -18,6 +18,8 @@ export interface ParseOptions {
|
|
|
18
18
|
skipRows?: number;
|
|
19
19
|
columns?: string[];
|
|
20
20
|
sheet?: string | number;
|
|
21
|
+
maxSheetSize?: number;
|
|
22
|
+
sheetChunkSize?: number;
|
|
21
23
|
}
|
|
22
24
|
export declare class TabularDriver {
|
|
23
25
|
private _dbName;
|
|
@@ -74,11 +76,10 @@ export declare class TabularDriver {
|
|
|
74
76
|
*/
|
|
75
77
|
fetch(url: string, options?: ParseOptions): Promise<DataFrame>;
|
|
76
78
|
/**
|
|
77
|
-
* ✅
|
|
78
|
-
*
|
|
79
|
-
* @returns Record<sheetName, DataFrame>
|
|
79
|
+
* ✅ OPTIMIZED: Stream Excel file with chunked row processing
|
|
80
|
+
* Handles large files by processing rows in batches
|
|
80
81
|
*/
|
|
81
|
-
streamFileMultiSheet(file: File): Promise<Record<string, DataFrame>>;
|
|
82
|
+
streamFileMultiSheet(file: File, options?: ParseOptions): Promise<Record<string, DataFrame>>;
|
|
82
83
|
private _splitLines;
|
|
83
84
|
private _parseLine;
|
|
84
85
|
private _autoType;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,SAAS,EAAE,MAAM,gBAAgB,CAAC;AAE3C,MAAM,WAAW,WAAW;IACxB,EAAE,EAAE,MAAM,CAAC;IACX,IAAI,EAAE,MAAM,CAAC;IACb,OAAO,EAAE,MAAM,EAAE,CAAC;IAClB,IAAI,EAAE,GAAG,EAAE,EAAE,CAAC;IACd,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,SAAS,EAAE,MAAM,CAAC;IAClB,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,CAAC;CAClC;AAED,MAAM,WAAW,YAAY;IACzB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,OAAO,CAAC;IACpB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,UAAU,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,IAAI,KAAK,IAAI,CAAC;IAC5D,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,EAAE,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;CAC3B;AAED,qBAAa,aAAa;IACtB,OAAO,CAAC,OAAO,CAAS;IACxB,OAAO,CAAC,UAAU,CAAS;IAC3B,OAAO,CAAC,GAAG,CAA4B;gBAE3B,MAAM,GAAE,MAAsB,EAAE,SAAS,GAAE,MAAiB;IAKlE,IAAI,IAAI,OAAO,CAAC,WAAW,CAAC;IA4BlC;;OAEG;IACH,QAAQ,CAAC,IAAI,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,SAAS;IA6C7D;;;OAGG;IACG,UAAU,CAAC,IAAI,EAAE,IAAI,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;IAoG5E;;OAEG;YACW,UAAU;IAuExB;;OAEG;IACG,aAAa,CAAC,IAAI,EAAE,IAAI,GAAG,OAAO,CAAC,MAAM,EAAE,CAAC;IAiBlD;;OAEG;IACG,KAAK,CAAC,IAAI,EAAE,MAAM,EAAE,EAAE,EAAE,SAAS,EAAE,QAAQ,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,GAAG,CAAC,GAAG,OAAO,CAAC,MAAM,CAAC;IAuBzF;;OAEG;IACG,IAAI,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IAwBjD;;OAEG;IACG,UAAU,CAAC,IAAI,EAAE,MAAM,GAAG,OAAO,CAAC,SAAS,GAAG,IAAI,CAAC;IA4BzD;;OAEG;IACG,IAAI,IAAI,OAAO,CAAC,KAAK,CAAC;QAAE,EAAE,EAAE,MAAM,CAAC;QAAC,IAAI,EAAE,MAAM,CAAC;QAAC,OAAO,EAAE,MAAM,EAAE,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAC;QAAC,SAAS,EAAE,MAAM,CAAA;KAAE,CAAC,CAAC;IAqBlH;;OAEG;IACG,MAAM,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC;IAYjC,KAAK,IAAI,OAAO,CAAC,IAAI,CAAC;IAgB5B;;OAEG;IACG,KAAK,CAAC,GAAG,EAAE,MAAM,EAAE,OAAO,GAAE,YAAiB,GAAG,OAAO,CAAC,SAAS,CAAC;IA0ExE
|
|
1
|
+
{"version":3,"file":"TabularDriver.d.ts","sourceRoot":"","sources":["TabularDriver.ts"],"names":[],"mappings":"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"}
|
|
@@ -419,12 +419,11 @@ export class TabularDriver {
|
|
|
419
419
|
return df;
|
|
420
420
|
}
|
|
421
421
|
/**
|
|
422
|
-
* ✅
|
|
423
|
-
*
|
|
424
|
-
* @returns Record<sheetName, DataFrame>
|
|
422
|
+
* ✅ OPTIMIZED: Stream Excel file with chunked row processing
|
|
423
|
+
* Handles large files by processing rows in batches
|
|
425
424
|
*/
|
|
426
|
-
async streamFileMultiSheet(file) {
|
|
427
|
-
|
|
425
|
+
async streamFileMultiSheet(file, options = {}) {
|
|
426
|
+
const { maxSheetSize = 100000, sheetChunkSize = 10000, onProgress } = options;
|
|
428
427
|
let XLSX;
|
|
429
428
|
try {
|
|
430
429
|
XLSX = await import('xlsx');
|
|
@@ -432,16 +431,79 @@ export class TabularDriver {
|
|
|
432
431
|
catch {
|
|
433
432
|
throw new Error('XLSX support requires the "xlsx" package. Install it with: npm install xlsx');
|
|
434
433
|
}
|
|
434
|
+
if (onProgress)
|
|
435
|
+
onProgress(0, file.size);
|
|
435
436
|
const buffer = await file.arrayBuffer();
|
|
436
|
-
|
|
437
|
+
if (onProgress)
|
|
438
|
+
onProgress(file.size * 0.3, file.size);
|
|
439
|
+
const workbook = XLSX.read(buffer, {
|
|
440
|
+
type: 'array',
|
|
441
|
+
sheetRows: maxSheetSize, // ✅ Limit rows read per sheet
|
|
442
|
+
dense: false // ✅ Use sparse format for memory efficiency
|
|
443
|
+
});
|
|
437
444
|
const sheets = {};
|
|
438
|
-
workbook.SheetNames.
|
|
445
|
+
const totalSheets = workbook.SheetNames.length;
|
|
446
|
+
let processedSheets = 0;
|
|
447
|
+
for (const sheetName of workbook.SheetNames) {
|
|
439
448
|
const worksheet = workbook.Sheets[sheetName];
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
449
|
+
// ✅ Get sheet range to determine size
|
|
450
|
+
const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
|
|
451
|
+
const totalRows = range.e.r - range.s.r + 1;
|
|
452
|
+
if (totalRows === 0) {
|
|
453
|
+
processedSheets++;
|
|
454
|
+
continue;
|
|
443
455
|
}
|
|
444
|
-
|
|
456
|
+
// ✅ Process in chunks if sheet is large
|
|
457
|
+
if (totalRows > sheetChunkSize) {
|
|
458
|
+
const rows = [];
|
|
459
|
+
let headers = [];
|
|
460
|
+
for (let startRow = 0; startRow < totalRows; startRow += sheetChunkSize) {
|
|
461
|
+
const endRow = Math.min(startRow + sheetChunkSize, totalRows);
|
|
462
|
+
// ✅ Read chunk with limited row range
|
|
463
|
+
const chunkData = XLSX.utils.sheet_to_json(worksheet, {
|
|
464
|
+
range: startRow,
|
|
465
|
+
header: startRow === 0 ? undefined : headers,
|
|
466
|
+
defval: null,
|
|
467
|
+
raw: false, // ✅ Convert dates/numbers to strings to reduce memory
|
|
468
|
+
blankrows: false
|
|
469
|
+
});
|
|
470
|
+
if (startRow === 0 && chunkData.length > 0) {
|
|
471
|
+
headers = Object.keys(chunkData[0]);
|
|
472
|
+
}
|
|
473
|
+
rows.push(...chunkData);
|
|
474
|
+
if (onProgress) {
|
|
475
|
+
const progress = 0.3 + (0.6 * (processedSheets + (startRow / totalRows)) / totalSheets);
|
|
476
|
+
onProgress(file.size * progress, file.size);
|
|
477
|
+
}
|
|
478
|
+
// ✅ Stop if we hit max rows
|
|
479
|
+
if (rows.length >= maxSheetSize) {
|
|
480
|
+
console.warn(`⚠️ Sheet "${sheetName}" truncated to ${maxSheetSize} rows`);
|
|
481
|
+
break;
|
|
482
|
+
}
|
|
483
|
+
}
|
|
484
|
+
if (rows.length > 0) {
|
|
485
|
+
sheets[sheetName] = new DataFrame(rows);
|
|
486
|
+
}
|
|
487
|
+
}
|
|
488
|
+
else {
|
|
489
|
+
// ✅ Small sheet: process normally
|
|
490
|
+
const jsonData = XLSX.utils.sheet_to_json(worksheet, {
|
|
491
|
+
defval: null,
|
|
492
|
+
raw: false,
|
|
493
|
+
blankrows: false
|
|
494
|
+
});
|
|
495
|
+
if (jsonData.length > 0) {
|
|
496
|
+
sheets[sheetName] = new DataFrame(jsonData);
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
processedSheets++;
|
|
500
|
+
if (onProgress) {
|
|
501
|
+
const progress = 0.3 + (0.6 * (processedSheets / totalSheets));
|
|
502
|
+
onProgress(file.size * progress, file.size);
|
|
503
|
+
}
|
|
504
|
+
}
|
|
505
|
+
if (onProgress)
|
|
506
|
+
onProgress(file.size, file.size);
|
|
445
507
|
return sheets;
|
|
446
508
|
}
|
|
447
509
|
/* ═══════════════════════════════════════════════════
|
|
@@ -19,7 +19,9 @@ export interface ParseOptions {
|
|
|
19
19
|
maxRows?: number;
|
|
20
20
|
skipRows?: number;
|
|
21
21
|
columns?: string[];
|
|
22
|
-
sheet?: string | number;
|
|
22
|
+
sheet?: string | number;
|
|
23
|
+
maxSheetSize?: number; // ✅ NEW: Max rows per sheet to prevent memory issues
|
|
24
|
+
sheetChunkSize?: number; // ✅ NEW: Process Excel in chunks
|
|
23
25
|
}
|
|
24
26
|
|
|
25
27
|
export class TabularDriver {
|
|
@@ -523,12 +525,12 @@ export class TabularDriver {
|
|
|
523
525
|
}
|
|
524
526
|
|
|
525
527
|
/**
|
|
526
|
-
* ✅
|
|
527
|
-
*
|
|
528
|
-
* @returns Record<sheetName, DataFrame>
|
|
528
|
+
* ✅ OPTIMIZED: Stream Excel file with chunked row processing
|
|
529
|
+
* Handles large files by processing rows in batches
|
|
529
530
|
*/
|
|
530
|
-
async streamFileMultiSheet(file: File): Promise<Record<string, DataFrame>> {
|
|
531
|
-
|
|
531
|
+
async streamFileMultiSheet(file: File, options: ParseOptions = {}): Promise<Record<string, DataFrame>> {
|
|
532
|
+
const { maxSheetSize = 100000, sheetChunkSize = 10000, onProgress } = options;
|
|
533
|
+
|
|
532
534
|
let XLSX: any;
|
|
533
535
|
try {
|
|
534
536
|
XLSX = await import('xlsx');
|
|
@@ -536,19 +538,93 @@ export class TabularDriver {
|
|
|
536
538
|
throw new Error('XLSX support requires the "xlsx" package. Install it with: npm install xlsx');
|
|
537
539
|
}
|
|
538
540
|
|
|
541
|
+
if (onProgress) onProgress(0, file.size);
|
|
542
|
+
|
|
539
543
|
const buffer = await file.arrayBuffer();
|
|
540
|
-
|
|
544
|
+
|
|
545
|
+
if (onProgress) onProgress(file.size * 0.3, file.size);
|
|
546
|
+
|
|
547
|
+
const workbook = XLSX.read(buffer, {
|
|
548
|
+
type: 'array',
|
|
549
|
+
sheetRows: maxSheetSize, // ✅ Limit rows read per sheet
|
|
550
|
+
dense: false // ✅ Use sparse format for memory efficiency
|
|
551
|
+
});
|
|
541
552
|
|
|
542
553
|
const sheets: Record<string, DataFrame> = {};
|
|
554
|
+
const totalSheets = workbook.SheetNames.length;
|
|
555
|
+
let processedSheets = 0;
|
|
543
556
|
|
|
544
|
-
workbook.SheetNames
|
|
557
|
+
for (const sheetName of workbook.SheetNames) {
|
|
545
558
|
const worksheet = workbook.Sheets[sheetName];
|
|
546
|
-
const jsonData: Record<string, any>[] = XLSX.utils.sheet_to_json(worksheet, { defval: null });
|
|
547
559
|
|
|
548
|
-
|
|
549
|
-
|
|
560
|
+
// ✅ Get sheet range to determine size
|
|
561
|
+
const range = XLSX.utils.decode_range(worksheet['!ref'] || 'A1');
|
|
562
|
+
const totalRows = range.e.r - range.s.r + 1;
|
|
563
|
+
|
|
564
|
+
if (totalRows === 0) {
|
|
565
|
+
processedSheets++;
|
|
566
|
+
continue;
|
|
550
567
|
}
|
|
551
|
-
|
|
568
|
+
|
|
569
|
+
// ✅ Process in chunks if sheet is large
|
|
570
|
+
if (totalRows > sheetChunkSize) {
|
|
571
|
+
const rows: Record<string, any>[] = [];
|
|
572
|
+
let headers: string[] = [];
|
|
573
|
+
|
|
574
|
+
for (let startRow = 0; startRow < totalRows; startRow += sheetChunkSize) {
|
|
575
|
+
const endRow = Math.min(startRow + sheetChunkSize, totalRows);
|
|
576
|
+
|
|
577
|
+
// ✅ Read chunk with limited row range
|
|
578
|
+
const chunkData: Record<string, any>[] = XLSX.utils.sheet_to_json(worksheet, {
|
|
579
|
+
range: startRow,
|
|
580
|
+
header: startRow === 0 ? undefined : headers,
|
|
581
|
+
defval: null,
|
|
582
|
+
raw: false, // ✅ Convert dates/numbers to strings to reduce memory
|
|
583
|
+
blankrows: false
|
|
584
|
+
});
|
|
585
|
+
|
|
586
|
+
if (startRow === 0 && chunkData.length > 0) {
|
|
587
|
+
headers = Object.keys(chunkData[0]);
|
|
588
|
+
}
|
|
589
|
+
|
|
590
|
+
rows.push(...chunkData);
|
|
591
|
+
|
|
592
|
+
if (onProgress) {
|
|
593
|
+
const progress = 0.3 + (0.6 * (processedSheets + (startRow / totalRows)) / totalSheets);
|
|
594
|
+
onProgress(file.size * progress, file.size);
|
|
595
|
+
}
|
|
596
|
+
|
|
597
|
+
// ✅ Stop if we hit max rows
|
|
598
|
+
if (rows.length >= maxSheetSize) {
|
|
599
|
+
console.warn(`⚠️ Sheet "${sheetName}" truncated to ${maxSheetSize} rows`);
|
|
600
|
+
break;
|
|
601
|
+
}
|
|
602
|
+
}
|
|
603
|
+
|
|
604
|
+
if (rows.length > 0) {
|
|
605
|
+
sheets[sheetName] = new DataFrame(rows);
|
|
606
|
+
}
|
|
607
|
+
} else {
|
|
608
|
+
// ✅ Small sheet: process normally
|
|
609
|
+
const jsonData: Record<string, any>[] = XLSX.utils.sheet_to_json(worksheet, {
|
|
610
|
+
defval: null,
|
|
611
|
+
raw: false,
|
|
612
|
+
blankrows: false
|
|
613
|
+
});
|
|
614
|
+
|
|
615
|
+
if (jsonData.length > 0) {
|
|
616
|
+
sheets[sheetName] = new DataFrame(jsonData);
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
processedSheets++;
|
|
621
|
+
if (onProgress) {
|
|
622
|
+
const progress = 0.3 + (0.6 * (processedSheets / totalSheets));
|
|
623
|
+
onProgress(file.size * progress, file.size);
|
|
624
|
+
}
|
|
625
|
+
}
|
|
626
|
+
|
|
627
|
+
if (onProgress) onProgress(file.size, file.size);
|
|
552
628
|
|
|
553
629
|
return sheets;
|
|
554
630
|
}
|
package/lib/styles/shadcn.css
CHANGED
|
@@ -1,11 +1,10 @@
|
|
|
1
1
|
@import url('https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&display=swap');
|
|
2
2
|
|
|
3
3
|
/* ═══════════════════════════════════════════════════════════════════
|
|
4
|
-
* DESIGN TOKENS
|
|
4
|
+
* DESIGN TOKENS
|
|
5
5
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
6
6
|
|
|
7
7
|
:root {
|
|
8
|
-
/* Base colors */
|
|
9
8
|
--background: 0 0% 100%;
|
|
10
9
|
--foreground: 0 0% 3.9%;
|
|
11
10
|
--muted: 0 0% 96.1%;
|
|
@@ -13,27 +12,19 @@
|
|
|
13
12
|
--border: 0 0% 89.8%;
|
|
14
13
|
--input: 0 0% 89.8%;
|
|
15
14
|
--ring: 0 0% 3.9%;
|
|
16
|
-
|
|
17
|
-
/* Accent colors */
|
|
18
15
|
--primary: 0 0% 9%;
|
|
19
16
|
--primary-foreground: 0 0% 98%;
|
|
20
17
|
--secondary: 0 0% 96.1%;
|
|
21
18
|
--secondary-foreground: 0 0% 9%;
|
|
22
19
|
--accent: 0 0% 96.1%;
|
|
23
20
|
--accent-foreground: 0 0% 9%;
|
|
24
|
-
|
|
25
|
-
/* Semantic colors */
|
|
26
21
|
--destructive: 0 84.2% 60.2%;
|
|
27
22
|
--destructive-foreground: 0 0% 98%;
|
|
28
23
|
--success: 142 71% 45%;
|
|
29
24
|
--success-foreground: 0 0% 98%;
|
|
30
25
|
--warning: 48 96% 53%;
|
|
31
26
|
--warning-foreground: 0 0% 9%;
|
|
32
|
-
|
|
33
|
-
/* Sizes */
|
|
34
|
-
--radius: 0.375rem;
|
|
35
|
-
|
|
36
|
-
/* Font */
|
|
27
|
+
--radius: 0.5rem;
|
|
37
28
|
font-family: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', sans-serif;
|
|
38
29
|
}
|
|
39
30
|
|
|
@@ -54,13 +45,12 @@
|
|
|
54
45
|
}
|
|
55
46
|
|
|
56
47
|
/* ═══════════════════════════════════════════════════════════════════
|
|
57
|
-
* BASE
|
|
48
|
+
* BASE INPUTS
|
|
58
49
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
59
50
|
|
|
60
51
|
.jux-input-element,
|
|
61
52
|
.jux-table-filter,
|
|
62
53
|
.jux-dataframe-filter-input {
|
|
63
|
-
display: flex;
|
|
64
54
|
height: 2.5rem;
|
|
65
55
|
width: 100%;
|
|
66
56
|
border-radius: var(--radius);
|
|
@@ -70,8 +60,7 @@
|
|
|
70
60
|
font-size: 0.875rem;
|
|
71
61
|
color: hsl(var(--foreground));
|
|
72
62
|
outline: none !important;
|
|
73
|
-
transition:
|
|
74
|
-
box-sizing: border-box;
|
|
63
|
+
transition: border-color 0.2s, box-shadow 0.2s;
|
|
75
64
|
}
|
|
76
65
|
|
|
77
66
|
.jux-input-element::placeholder,
|
|
@@ -83,25 +72,18 @@
|
|
|
83
72
|
.jux-input-element:focus,
|
|
84
73
|
.jux-table-filter:focus,
|
|
85
74
|
.jux-dataframe-filter-input:focus {
|
|
86
|
-
|
|
87
|
-
box-shadow: 0 0 0
|
|
88
|
-
}
|
|
89
|
-
|
|
90
|
-
.jux-input-element:disabled {
|
|
91
|
-
cursor: not-allowed;
|
|
92
|
-
opacity: 0.5;
|
|
75
|
+
border-color: hsl(var(--ring));
|
|
76
|
+
box-shadow: 0 0 0 3px hsl(var(--ring) / 0.1);
|
|
93
77
|
}
|
|
94
78
|
|
|
95
79
|
/* ═══════════════════════════════════════════════════════════════════
|
|
96
|
-
*
|
|
80
|
+
* BUTTONS
|
|
97
81
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
98
82
|
|
|
99
83
|
.jux-button,
|
|
100
84
|
.jux-modal-action,
|
|
101
85
|
.jux-table-pagination button,
|
|
102
|
-
.jux-fileupload-button
|
|
103
|
-
.jux-dropdown-trigger,
|
|
104
|
-
.jux-dmenu-trigger {
|
|
86
|
+
.jux-fileupload-button {
|
|
105
87
|
display: inline-flex;
|
|
106
88
|
align-items: center;
|
|
107
89
|
justify-content: center;
|
|
@@ -112,37 +94,24 @@
|
|
|
112
94
|
height: 2.5rem;
|
|
113
95
|
padding: 0 1rem;
|
|
114
96
|
cursor: pointer;
|
|
115
|
-
transition: all 0.
|
|
116
|
-
border:
|
|
97
|
+
transition: all 0.2s;
|
|
98
|
+
border: none;
|
|
117
99
|
background: hsl(var(--primary));
|
|
118
100
|
color: hsl(var(--primary-foreground));
|
|
119
101
|
}
|
|
120
102
|
|
|
121
103
|
.jux-button:hover,
|
|
122
|
-
.jux-
|
|
123
|
-
.jux-table-pagination button:hover:not(:disabled),
|
|
124
|
-
.jux-fileupload-button:hover,
|
|
125
|
-
.jux-dropdown-trigger:hover,
|
|
126
|
-
.jux-dmenu-trigger:hover {
|
|
104
|
+
.jux-table-pagination button:hover:not(:disabled) {
|
|
127
105
|
opacity: 0.9;
|
|
128
106
|
}
|
|
129
107
|
|
|
130
|
-
.jux-button:focus-visible,
|
|
131
|
-
.jux-modal-action:focus-visible,
|
|
132
|
-
.jux-fileupload-button:focus-visible {
|
|
133
|
-
outline: 2px solid hsl(var(--ring));
|
|
134
|
-
outline-offset: 2px;
|
|
135
|
-
}
|
|
136
|
-
|
|
137
108
|
.jux-button:disabled,
|
|
138
109
|
.jux-table-pagination button:disabled {
|
|
139
110
|
pointer-events: none;
|
|
140
111
|
opacity: 0.5;
|
|
141
112
|
}
|
|
142
113
|
|
|
143
|
-
/* Button variants */
|
|
144
114
|
.jux-button-outline,
|
|
145
|
-
.jux-modal-action-secondary,
|
|
146
115
|
.jux-fileupload-button {
|
|
147
116
|
border: 1px solid hsl(var(--input));
|
|
148
117
|
background: hsl(var(--background));
|
|
@@ -150,101 +119,75 @@
|
|
|
150
119
|
}
|
|
151
120
|
|
|
152
121
|
.jux-button-outline:hover,
|
|
153
|
-
.jux-modal-action-secondary:hover,
|
|
154
122
|
.jux-fileupload-button:hover {
|
|
155
123
|
background: hsl(var(--accent));
|
|
156
124
|
}
|
|
157
125
|
|
|
158
|
-
.jux-button-ghost
|
|
159
|
-
.jux-modal-action-ghost {
|
|
126
|
+
.jux-button-ghost {
|
|
160
127
|
background: transparent;
|
|
161
128
|
color: hsl(var(--foreground));
|
|
162
|
-
border: none;
|
|
163
129
|
}
|
|
164
130
|
|
|
165
|
-
.jux-button-ghost:hover
|
|
166
|
-
.jux-modal-action-ghost:hover {
|
|
131
|
+
.jux-button-ghost:hover {
|
|
167
132
|
background: hsl(var(--accent));
|
|
168
133
|
}
|
|
169
134
|
|
|
170
|
-
.jux-button-destructive,
|
|
171
|
-
.jux-modal-action-destructive {
|
|
172
|
-
background: hsl(var(--destructive));
|
|
173
|
-
color: hsl(var(--destructive-foreground));
|
|
174
|
-
}
|
|
175
|
-
|
|
176
|
-
.jux-button-destructive:hover,
|
|
177
|
-
.jux-modal-action-destructive:hover {
|
|
178
|
-
opacity: 0.9;
|
|
179
|
-
}
|
|
180
|
-
|
|
181
|
-
/* Button sizes */
|
|
182
|
-
.jux-button-sm { height: 2.25rem; padding: 0 0.875rem; font-size: 0.8125rem; }
|
|
183
|
-
.jux-button-lg { height: 2.75rem; padding: 0 1.25rem; font-size: 0.9375rem; }
|
|
184
|
-
.jux-button-xl { height: 3rem; padding: 0 1.5rem; font-size: 1rem; font-weight: 600; }
|
|
185
|
-
|
|
186
135
|
/* ═══════════════════════════════════════════════════════════════════
|
|
187
|
-
* TYPOGRAPHY
|
|
136
|
+
* TYPOGRAPHY
|
|
188
137
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
189
138
|
|
|
190
|
-
h1, h2, h3, h4, h5, h6
|
|
191
|
-
.jux-heading-1, .jux-heading-2, .jux-heading-3, .jux-heading-4, .jux-heading-5, .jux-heading-6 {
|
|
192
|
-
scroll-margin: 5rem;
|
|
139
|
+
h1, h2, h3, h4, h5, h6 {
|
|
193
140
|
font-weight: 600;
|
|
194
141
|
letter-spacing: -0.025em;
|
|
195
142
|
color: hsl(var(--foreground));
|
|
196
143
|
}
|
|
197
144
|
|
|
198
|
-
h1
|
|
199
|
-
h2
|
|
200
|
-
h3
|
|
201
|
-
h4
|
|
202
|
-
h5, .jux-heading-5 { font-size: 1.125rem; }
|
|
203
|
-
h6, .jux-heading-6 { font-size: 1rem; }
|
|
145
|
+
h1 { font-size: 2.25rem; font-weight: 800; }
|
|
146
|
+
h2 { font-size: 1.875rem; padding-bottom: 0.5rem; border-bottom: 1px solid hsl(var(--border)); }
|
|
147
|
+
h3 { font-size: 1.5rem; }
|
|
148
|
+
h4 { font-size: 1.25rem; }
|
|
204
149
|
|
|
205
|
-
p
|
|
206
|
-
p:not(:first-child) { margin-top: 1.5rem; }
|
|
207
|
-
|
|
208
|
-
.jux-muted { font-size: 0.875rem; color: hsl(var(--muted-foreground)); }
|
|
150
|
+
p { line-height: 1.75; color: hsl(var(--foreground)); }
|
|
209
151
|
|
|
210
152
|
/* ═══════════════════════════════════════════════════════════════════
|
|
211
|
-
* DATAFRAME
|
|
153
|
+
* DATAFRAME
|
|
212
154
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
213
155
|
|
|
214
156
|
.jux-dataframe {
|
|
215
157
|
display: flex;
|
|
216
158
|
flex-direction: column;
|
|
217
|
-
gap:
|
|
159
|
+
gap: 1rem;
|
|
218
160
|
width: 100%;
|
|
219
161
|
}
|
|
220
162
|
|
|
221
163
|
.jux-dataframe-status {
|
|
222
164
|
display: flex;
|
|
223
165
|
align-items: center;
|
|
224
|
-
|
|
225
|
-
|
|
166
|
+
gap: 0.5rem;
|
|
167
|
+
padding: 0.75rem 1rem;
|
|
168
|
+
font-size: 0.875rem;
|
|
226
169
|
font-weight: 500;
|
|
227
170
|
border-radius: var(--radius);
|
|
228
171
|
border: 1px solid hsl(var(--border));
|
|
229
|
-
background: hsl(var(--muted));
|
|
172
|
+
background: hsl(var(--muted) / 0.5);
|
|
230
173
|
color: hsl(var(--muted-foreground));
|
|
231
174
|
}
|
|
232
175
|
|
|
233
176
|
.jux-dataframe-status-success {
|
|
234
177
|
background: hsl(var(--success) / 0.1);
|
|
235
|
-
border-color: hsl(var(--success) / 0.
|
|
178
|
+
border-color: hsl(var(--success) / 0.2);
|
|
236
179
|
color: hsl(var(--success));
|
|
237
180
|
}
|
|
238
181
|
|
|
239
182
|
.jux-dataframe-status-loading {
|
|
240
183
|
background: hsl(var(--warning) / 0.1);
|
|
241
|
-
border-color: hsl(var(--warning) / 0.
|
|
184
|
+
border-color: hsl(var(--warning) / 0.2);
|
|
242
185
|
color: hsl(var(--warning));
|
|
243
186
|
}
|
|
244
187
|
|
|
245
188
|
.jux-dataframe-status-error {
|
|
246
189
|
background: hsl(var(--destructive) / 0.1);
|
|
247
|
-
border-color: hsl(var(--destructive) / 0.
|
|
190
|
+
border-color: hsl(var(--destructive) / 0.2);
|
|
248
191
|
color: hsl(var(--destructive));
|
|
249
192
|
}
|
|
250
193
|
|
|
@@ -252,7 +195,6 @@ p:not(:first-child) { margin-top: 1.5rem; }
|
|
|
252
195
|
position: relative;
|
|
253
196
|
display: flex;
|
|
254
197
|
align-items: center;
|
|
255
|
-
width: 100%;
|
|
256
198
|
margin-bottom: 0.75rem;
|
|
257
199
|
}
|
|
258
200
|
|
|
@@ -263,316 +205,220 @@ p:not(:first-child) { margin-top: 1.5rem; }
|
|
|
263
205
|
transform: translateY(-50%);
|
|
264
206
|
color: hsl(var(--muted-foreground));
|
|
265
207
|
pointer-events: none;
|
|
266
|
-
display: flex;
|
|
267
|
-
align-items: center;
|
|
268
208
|
z-index: 1;
|
|
269
209
|
}
|
|
270
210
|
|
|
271
211
|
.jux-dataframe-filter-input {
|
|
272
|
-
padding-left: 2.
|
|
212
|
+
padding-left: 2.5rem !important;
|
|
273
213
|
}
|
|
274
214
|
|
|
275
215
|
/* ═══════════════════════════════════════════════════════════════════
|
|
276
|
-
*
|
|
216
|
+
* TABS
|
|
277
217
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
278
218
|
|
|
279
|
-
.jux-
|
|
219
|
+
.jux-tabs {
|
|
220
|
+
width: 100%;
|
|
221
|
+
}
|
|
280
222
|
|
|
281
|
-
.jux-
|
|
282
|
-
display: flex;
|
|
223
|
+
.jux-tabs-list {
|
|
224
|
+
display: inline-flex;
|
|
225
|
+
height: 2.5rem;
|
|
283
226
|
align-items: center;
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
background: hsl(var(--muted));
|
|
287
|
-
border: 1px solid hsl(var(--border));
|
|
227
|
+
justify-content: flex-start;
|
|
228
|
+
gap: 0.25rem;
|
|
288
229
|
border-radius: var(--radius);
|
|
289
|
-
|
|
290
|
-
|
|
230
|
+
background: hsl(var(--muted) / 0.3);
|
|
231
|
+
padding: 0.25rem;
|
|
232
|
+
border: 1px solid hsl(var(--border));
|
|
291
233
|
}
|
|
292
234
|
|
|
293
|
-
.jux-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
text-overflow: ellipsis;
|
|
235
|
+
.jux-tabs-button {
|
|
236
|
+
display: inline-flex;
|
|
237
|
+
align-items: center;
|
|
238
|
+
justify-content: center;
|
|
298
239
|
white-space: nowrap;
|
|
240
|
+
border-radius: calc(var(--radius) - 0.125rem);
|
|
241
|
+
padding: 0.5rem 1rem;
|
|
242
|
+
font-size: 0.875rem;
|
|
243
|
+
font-weight: 500;
|
|
244
|
+
color: hsl(var(--muted-foreground));
|
|
245
|
+
cursor: pointer;
|
|
246
|
+
border: none;
|
|
247
|
+
background: transparent;
|
|
248
|
+
transition: all 0.2s;
|
|
299
249
|
}
|
|
300
250
|
|
|
301
|
-
.jux-
|
|
302
|
-
|
|
303
|
-
|
|
251
|
+
.jux-tabs-button:hover {
|
|
252
|
+
background: hsl(var(--muted) / 0.5);
|
|
253
|
+
color: hsl(var(--foreground));
|
|
304
254
|
}
|
|
305
255
|
|
|
306
|
-
.jux-
|
|
307
|
-
background:
|
|
256
|
+
.jux-tabs-button-active {
|
|
257
|
+
background: hsl(var(--background));
|
|
258
|
+
color: hsl(var(--foreground));
|
|
259
|
+
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.1);
|
|
260
|
+
font-weight: 600;
|
|
261
|
+
}
|
|
262
|
+
|
|
263
|
+
.jux-tabs-panel {
|
|
264
|
+
margin-top: 1rem;
|
|
265
|
+
border-radius: var(--radius);
|
|
266
|
+
border: 1px solid hsl(var(--border));
|
|
267
|
+
background: hsl(var(--background));
|
|
268
|
+
padding: 0;
|
|
269
|
+
overflow: hidden;
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/* ✅ Remove inner padding/border from tables inside tabs */
|
|
273
|
+
.jux-tabs-panel .jux-table-wrapper {
|
|
308
274
|
border: none;
|
|
309
|
-
|
|
310
|
-
cursor: pointer;
|
|
311
|
-
padding: 0 0.25rem;
|
|
312
|
-
border-radius: 0.25rem;
|
|
313
|
-
transition: all 0.15s;
|
|
275
|
+
border-radius: 0;
|
|
314
276
|
}
|
|
315
277
|
|
|
316
|
-
.jux-
|
|
317
|
-
|
|
318
|
-
|
|
278
|
+
.jux-tabs-panel .jux-dataframe-filter {
|
|
279
|
+
padding: 1rem 1rem 0;
|
|
280
|
+
margin-bottom: 0.75rem;
|
|
319
281
|
}
|
|
320
282
|
|
|
321
283
|
/* ═══════════════════════════════════════════════════════════════════
|
|
322
284
|
* TABLE
|
|
323
285
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
324
286
|
|
|
325
|
-
.jux-table-wrapper {
|
|
287
|
+
.jux-table-wrapper {
|
|
288
|
+
width: 100%;
|
|
289
|
+
overflow-x: auto;
|
|
290
|
+
border-radius: var(--radius);
|
|
291
|
+
border: 1px solid hsl(var(--border));
|
|
292
|
+
background: hsl(var(--background));
|
|
293
|
+
}
|
|
326
294
|
|
|
327
295
|
.jux-table {
|
|
328
296
|
width: 100%;
|
|
329
297
|
border-collapse: collapse;
|
|
330
298
|
}
|
|
331
299
|
|
|
332
|
-
.jux-table thead tr {
|
|
333
|
-
|
|
334
|
-
|
|
300
|
+
.jux-table thead tr {
|
|
301
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
302
|
+
background: hsl(var(--muted) / 0.3);
|
|
303
|
+
}
|
|
304
|
+
|
|
305
|
+
.jux-table tbody tr {
|
|
306
|
+
border-bottom: 1px solid hsl(var(--border));
|
|
307
|
+
transition: background-color 0.15s;
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
.jux-table tbody tr:last-child {
|
|
311
|
+
border-bottom: none;
|
|
312
|
+
}
|
|
335
313
|
|
|
336
314
|
.jux-table th,
|
|
337
315
|
.jux-table td {
|
|
338
|
-
|
|
339
|
-
padding: 0.5rem 1rem;
|
|
316
|
+
padding: 0.75rem 1rem;
|
|
340
317
|
text-align: left;
|
|
318
|
+
border: none;
|
|
319
|
+
}
|
|
320
|
+
|
|
321
|
+
.jux-table th {
|
|
322
|
+
font-weight: 600;
|
|
323
|
+
font-size: 0.875rem;
|
|
324
|
+
color: hsl(var(--foreground));
|
|
341
325
|
}
|
|
342
326
|
|
|
343
|
-
.jux-table
|
|
327
|
+
.jux-table td {
|
|
328
|
+
font-size: 0.875rem;
|
|
329
|
+
color: hsl(var(--foreground));
|
|
330
|
+
}
|
|
344
331
|
|
|
345
|
-
.jux-table-
|
|
346
|
-
|
|
332
|
+
.jux-table-striped tbody tr:nth-child(even) {
|
|
333
|
+
background: hsl(var(--muted) / 0.2);
|
|
347
334
|
}
|
|
348
335
|
|
|
349
336
|
.jux-table-hoverable tbody tr:hover {
|
|
350
|
-
background: hsl(var(--
|
|
337
|
+
background: hsl(var(--muted) / 0.5);
|
|
351
338
|
}
|
|
352
339
|
|
|
353
340
|
.jux-table-pagination {
|
|
354
|
-
margin-top:
|
|
341
|
+
margin-top: 1rem;
|
|
342
|
+
padding: 0.75rem 1rem;
|
|
355
343
|
display: flex;
|
|
356
|
-
gap: 0.
|
|
344
|
+
gap: 0.5rem;
|
|
357
345
|
justify-content: center;
|
|
358
346
|
align-items: center;
|
|
359
|
-
|
|
347
|
+
border-top: 1px solid hsl(var(--border));
|
|
348
|
+
background: hsl(var(--muted) / 0.2);
|
|
360
349
|
}
|
|
361
350
|
|
|
362
351
|
.jux-table-pagination span {
|
|
363
352
|
font-size: 0.875rem;
|
|
364
|
-
color: hsl(var(--
|
|
365
|
-
|
|
353
|
+
color: hsl(var(--foreground));
|
|
354
|
+
font-weight: 500;
|
|
366
355
|
}
|
|
367
356
|
|
|
368
357
|
/* ═══════════════════════════════════════════════════════════════════
|
|
369
|
-
*
|
|
358
|
+
* FILE UPLOAD
|
|
370
359
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
371
360
|
|
|
372
|
-
.jux-
|
|
373
|
-
position: fixed;
|
|
374
|
-
inset: 0;
|
|
375
|
-
z-index: 50;
|
|
376
|
-
background: rgba(0, 0, 0, 0.8);
|
|
377
|
-
backdrop-filter: blur(2px);
|
|
361
|
+
.jux-fileupload-item {
|
|
378
362
|
display: flex;
|
|
379
363
|
align-items: center;
|
|
380
|
-
justify-content: center;
|
|
381
|
-
}
|
|
382
|
-
|
|
383
|
-
.jux-modal {
|
|
384
|
-
position: relative;
|
|
385
|
-
background: hsl(var(--background));
|
|
386
|
-
border: 1px solid hsl(var(--border));
|
|
387
|
-
border-radius: 0.5rem;
|
|
388
|
-
box-shadow: 0 10px 15px -3px rgba(0, 0, 0, 0.1);
|
|
389
|
-
width: 100%;
|
|
390
|
-
max-width: 32rem;
|
|
391
|
-
z-index: 51;
|
|
392
|
-
display: flex;
|
|
393
|
-
flex-direction: column;
|
|
394
|
-
gap: 1rem;
|
|
395
|
-
padding: 1.5rem;
|
|
396
|
-
}
|
|
397
|
-
|
|
398
|
-
.jux-modal-header {
|
|
399
|
-
display: flex;
|
|
400
|
-
align-items: flex-start;
|
|
401
364
|
gap: 0.75rem;
|
|
365
|
+
padding: 0.75rem 1rem;
|
|
366
|
+
background: hsl(var(--muted) / 0.3);
|
|
367
|
+
border: 1px solid hsl(var(--border));
|
|
368
|
+
border-radius: var(--radius);
|
|
369
|
+
font-size: 0.875rem;
|
|
402
370
|
}
|
|
403
371
|
|
|
404
|
-
.jux-
|
|
372
|
+
.jux-fileupload-filename {
|
|
405
373
|
flex: 1;
|
|
406
|
-
font-
|
|
407
|
-
font-weight: 600;
|
|
374
|
+
font-weight: 500;
|
|
408
375
|
color: hsl(var(--foreground));
|
|
376
|
+
overflow: hidden;
|
|
377
|
+
text-overflow: ellipsis;
|
|
378
|
+
white-space: nowrap;
|
|
409
379
|
}
|
|
410
380
|
|
|
411
|
-
.jux-
|
|
412
|
-
font-size: 0.875rem;
|
|
381
|
+
.jux-fileupload-filesize {
|
|
413
382
|
color: hsl(var(--muted-foreground));
|
|
414
|
-
|
|
415
|
-
}
|
|
416
|
-
|
|
417
|
-
.jux-modal-footer {
|
|
418
|
-
display: flex;
|
|
419
|
-
justify-content: flex-end;
|
|
420
|
-
gap: 0.5rem;
|
|
383
|
+
font-size: 0.8125rem;
|
|
421
384
|
}
|
|
422
385
|
|
|
423
|
-
.jux-
|
|
424
|
-
position: absolute;
|
|
425
|
-
top: 1rem;
|
|
426
|
-
right: 1rem;
|
|
386
|
+
.jux-fileupload-remove {
|
|
427
387
|
background: transparent;
|
|
428
388
|
border: none;
|
|
429
|
-
cursor: pointer;
|
|
430
|
-
color: hsl(var(--muted-foreground));
|
|
431
|
-
padding: 0.25rem;
|
|
432
|
-
}
|
|
433
|
-
|
|
434
|
-
.jux-modal-close:hover {
|
|
435
|
-
color: hsl(var(--foreground));
|
|
436
|
-
}
|
|
437
|
-
|
|
438
|
-
/* ═══════════════════════════════════════════════════════════════════
|
|
439
|
-
* TABS
|
|
440
|
-
* ═══════════════════════════════════════════════════════════════════ */
|
|
441
|
-
|
|
442
|
-
.jux-tabs-list {
|
|
443
|
-
display: inline-flex;
|
|
444
|
-
height: 40px;
|
|
445
|
-
align-items: center;
|
|
446
|
-
justify-content: center;
|
|
447
|
-
border-radius: var(--radius);
|
|
448
|
-
background: hsl(var(--muted));
|
|
449
|
-
padding: 0.25rem;
|
|
450
|
-
}
|
|
451
|
-
|
|
452
|
-
.jux-tabs-button {
|
|
453
|
-
display: inline-flex;
|
|
454
|
-
align-items: center;
|
|
455
|
-
justify-content: center;
|
|
456
|
-
white-space: nowrap;
|
|
457
|
-
border-radius: calc(var(--radius) - 2px);
|
|
458
|
-
padding: 0.375rem 0.75rem;
|
|
459
|
-
font-size: 0.875rem;
|
|
460
|
-
font-weight: 500;
|
|
461
389
|
color: hsl(var(--muted-foreground));
|
|
462
390
|
cursor: pointer;
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
transition: all 0.
|
|
466
|
-
}
|
|
467
|
-
|
|
468
|
-
.jux-tabs-button-active {
|
|
469
|
-
background: hsl(var(--background));
|
|
470
|
-
color: hsl(var(--foreground));
|
|
471
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
391
|
+
padding: 0.25rem;
|
|
392
|
+
border-radius: 0.25rem;
|
|
393
|
+
transition: all 0.2s;
|
|
472
394
|
}
|
|
473
395
|
|
|
474
|
-
.jux-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
border: 1px solid hsl(var(--border));
|
|
478
|
-
padding: 1.5rem;
|
|
396
|
+
.jux-fileupload-remove:hover {
|
|
397
|
+
color: hsl(var(--destructive));
|
|
398
|
+
background: hsl(var(--destructive) / 0.1);
|
|
479
399
|
}
|
|
480
400
|
|
|
481
401
|
/* ═══════════════════════════════════════════════════════════════════
|
|
482
|
-
*
|
|
402
|
+
* UTILITIES
|
|
483
403
|
* ═══════════════════════════════════════════════════════════════════ */
|
|
484
404
|
|
|
485
|
-
.jux-
|
|
486
|
-
border-radius: 0.5rem;
|
|
487
|
-
border: 1px solid hsl(var(--border));
|
|
488
|
-
background: hsl(var(--background));
|
|
489
|
-
box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05);
|
|
490
|
-
overflow: hidden;
|
|
491
|
-
}
|
|
492
|
-
|
|
493
|
-
.jux-card-header {
|
|
494
|
-
display: flex;
|
|
495
|
-
align-items: center;
|
|
496
|
-
gap: 0.75rem;
|
|
497
|
-
padding: 1.5rem;
|
|
498
|
-
background: hsl(var(--muted) / 0.5);
|
|
499
|
-
border-bottom: 1px solid hsl(var(--border));
|
|
500
|
-
}
|
|
501
|
-
|
|
502
|
-
.jux-card-title {
|
|
503
|
-
flex: 1;
|
|
504
|
-
font-size: 1.125rem;
|
|
505
|
-
font-weight: 600;
|
|
506
|
-
color: hsl(var(--foreground));
|
|
507
|
-
}
|
|
508
|
-
|
|
509
|
-
.jux-card-body {
|
|
510
|
-
padding: 1.5rem;
|
|
405
|
+
.jux-muted {
|
|
511
406
|
font-size: 0.875rem;
|
|
512
407
|
color: hsl(var(--muted-foreground));
|
|
513
408
|
}
|
|
514
409
|
|
|
515
|
-
|
|
516
|
-
* DROPDOWN MENU
|
|
517
|
-
* ═══════════════════════════════════════════════════════════════════ */
|
|
518
|
-
|
|
519
|
-
.jux-dmenu-wrapper { position: relative; display: inline-block; }
|
|
520
|
-
|
|
521
|
-
.jux-dmenu-menu {
|
|
522
|
-
position: absolute;
|
|
523
|
-
top: calc(100% + 4px);
|
|
524
|
-
left: 0;
|
|
525
|
-
min-width: 220px;
|
|
526
|
-
padding: 4px;
|
|
527
|
-
border-radius: 0.5rem;
|
|
528
|
-
border: 1px solid hsl(var(--border));
|
|
529
|
-
background: hsl(var(--background));
|
|
530
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.08);
|
|
531
|
-
z-index: 100;
|
|
532
|
-
display: none;
|
|
533
|
-
flex-direction: column;
|
|
534
|
-
}
|
|
535
|
-
|
|
536
|
-
.jux-dmenu-item {
|
|
537
|
-
display: flex;
|
|
538
|
-
align-items: center;
|
|
539
|
-
justify-content: space-between;
|
|
540
|
-
width: 100%;
|
|
541
|
-
padding: 6px 8px;
|
|
542
|
-
font-size: 0.875rem;
|
|
543
|
-
border-radius: 4px;
|
|
544
|
-
border: none;
|
|
545
|
-
background: transparent;
|
|
546
|
-
color: hsl(var(--foreground));
|
|
547
|
-
cursor: pointer;
|
|
548
|
-
transition: background 0.1s;
|
|
549
|
-
}
|
|
550
|
-
|
|
551
|
-
.jux-dmenu-item:hover {
|
|
552
|
-
background: hsl(var(--accent));
|
|
553
|
-
}
|
|
554
|
-
|
|
555
|
-
.jux-dmenu-sep {
|
|
556
|
-
height: 1px;
|
|
557
|
-
margin: 4px -4px;
|
|
558
|
-
background: hsl(var(--border));
|
|
559
|
-
}
|
|
560
|
-
|
|
561
|
-
/* ═══════════════════════════════════════════════════════════════════
|
|
562
|
-
* UTILITIES
|
|
563
|
-
* ═══════════════════════════════════════════════════════════════════ */
|
|
564
|
-
|
|
565
|
-
hr, .jux-hr {
|
|
410
|
+
hr {
|
|
566
411
|
margin: 1.5rem 0;
|
|
567
412
|
border: none;
|
|
568
413
|
border-top: 1px solid hsl(var(--border));
|
|
569
414
|
}
|
|
570
415
|
|
|
571
|
-
code
|
|
416
|
+
code {
|
|
572
417
|
border-radius: 0.25rem;
|
|
573
418
|
background: hsl(var(--muted));
|
|
574
|
-
padding: 0.
|
|
419
|
+
padding: 0.2rem 0.4rem;
|
|
575
420
|
font-family: ui-monospace, monospace;
|
|
576
421
|
font-size: 0.875rem;
|
|
577
422
|
font-weight: 600;
|
|
423
|
+
color: hsl(var(--foreground));
|
|
578
424
|
}
|