@smallwebco/tinypivot-react 1.0.57 → 1.0.59
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +6 -4
- package/dist/index.cjs +116 -11
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +14 -2
- package/dist/index.d.ts +14 -2
- package/dist/index.js +117 -12
- package/dist/index.js.map +1 -1
- package/package.json +11 -3
package/dist/index.d.cts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as _smallwebco_tinypivot_core from '@smallwebco/tinypivot-core';
|
|
3
2
|
import { AIAnalystConfig, AIDataLoadedEvent, AIConversationUpdateEvent, AIQueryExecutedEvent, AIErrorEvent, ColumnStats, NumericRange, FieldStats, PivotValueField, CalculatedField, AggregationFunction, PivotResult, AIConversation, AITableSchema, AIDataSource, PivotExportData, ExportOptions, SelectionBounds, PaginationOptions, LicenseInfo } from '@smallwebco/tinypivot-core';
|
|
4
3
|
export { AIAnalystConfig, AIColumnSchema, AIConversation, AIConversationUpdateEvent, AIDataLoadedEvent, AIDataSource, AIErrorEvent, AIMessage, AIMessageMetadata, AIQueryExecutedEvent, AITableSchema, AggregationFunction, CellClickEvent, ColumnStats, CopyEvent, DataGridProps, ExportEvent, ExportOptions, FieldStats, FilterEvent, GridOptions, LicenseInfo, LicenseType, PaginationOptions, PivotCell, PivotConfig as PivotConfigType, PivotField, PivotResult, PivotTableProps, PivotValueField, RowSelectionChangeEvent, SelectionBounds, SelectionChangeEvent, SortEvent, formatCellValue, getAggregationLabel, getColumnUniqueValues } from '@smallwebco/tinypivot-core';
|
|
5
4
|
import * as react from 'react';
|
|
6
5
|
import react__default from 'react';
|
|
6
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
import * as _tanstack_react_table from '@tanstack/react-table';
|
|
8
8
|
import { SortingState, ColumnFiltersState, VisibilityState } from '@tanstack/react-table';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* TinyPivot React - AI Data Analyst Component
|
|
12
|
+
* Split-panel layout: 1/4 chat, 3/4 data preview
|
|
13
|
+
* Each query step shows data visually with expandable SQL
|
|
14
|
+
*/
|
|
15
|
+
|
|
10
16
|
interface AIAnalystProps {
|
|
11
17
|
config: AIAnalystConfig;
|
|
12
18
|
theme?: 'light' | 'dark';
|
|
@@ -19,7 +25,11 @@ interface AIAnalystProps {
|
|
|
19
25
|
query: string;
|
|
20
26
|
}) => void;
|
|
21
27
|
}
|
|
22
|
-
|
|
28
|
+
interface AIAnalystHandle {
|
|
29
|
+
loadFullData: () => Promise<Record<string, unknown>[] | null>;
|
|
30
|
+
selectedDataSource: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
declare const AIAnalyst: react__default.ForwardRefExoticComponent<AIAnalystProps & react__default.RefAttributes<AIAnalystHandle>>;
|
|
23
33
|
|
|
24
34
|
interface ColumnFilterProps {
|
|
25
35
|
columnId: string;
|
|
@@ -172,6 +182,8 @@ declare function useAIAnalyst(options: UseAIAnalystOptions): {
|
|
|
172
182
|
importConversation: (conv: AIConversation) => void;
|
|
173
183
|
/** Refresh table list from endpoint */
|
|
174
184
|
fetchTables: () => Promise<void>;
|
|
185
|
+
/** Load full data for the currently selected data source */
|
|
186
|
+
loadFullData: () => Promise<Record<string, unknown>[] | null>;
|
|
175
187
|
};
|
|
176
188
|
|
|
177
189
|
interface ExcelGridOptions<T> {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,18 @@
|
|
|
1
|
-
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
2
1
|
import * as _smallwebco_tinypivot_core from '@smallwebco/tinypivot-core';
|
|
3
2
|
import { AIAnalystConfig, AIDataLoadedEvent, AIConversationUpdateEvent, AIQueryExecutedEvent, AIErrorEvent, ColumnStats, NumericRange, FieldStats, PivotValueField, CalculatedField, AggregationFunction, PivotResult, AIConversation, AITableSchema, AIDataSource, PivotExportData, ExportOptions, SelectionBounds, PaginationOptions, LicenseInfo } from '@smallwebco/tinypivot-core';
|
|
4
3
|
export { AIAnalystConfig, AIColumnSchema, AIConversation, AIConversationUpdateEvent, AIDataLoadedEvent, AIDataSource, AIErrorEvent, AIMessage, AIMessageMetadata, AIQueryExecutedEvent, AITableSchema, AggregationFunction, CellClickEvent, ColumnStats, CopyEvent, DataGridProps, ExportEvent, ExportOptions, FieldStats, FilterEvent, GridOptions, LicenseInfo, LicenseType, PaginationOptions, PivotCell, PivotConfig as PivotConfigType, PivotField, PivotResult, PivotTableProps, PivotValueField, RowSelectionChangeEvent, SelectionBounds, SelectionChangeEvent, SortEvent, formatCellValue, getAggregationLabel, getColumnUniqueValues } from '@smallwebco/tinypivot-core';
|
|
5
4
|
import * as react from 'react';
|
|
6
5
|
import react__default from 'react';
|
|
6
|
+
import * as react_jsx_runtime from 'react/jsx-runtime';
|
|
7
7
|
import * as _tanstack_react_table from '@tanstack/react-table';
|
|
8
8
|
import { SortingState, ColumnFiltersState, VisibilityState } from '@tanstack/react-table';
|
|
9
9
|
|
|
10
|
+
/**
|
|
11
|
+
* TinyPivot React - AI Data Analyst Component
|
|
12
|
+
* Split-panel layout: 1/4 chat, 3/4 data preview
|
|
13
|
+
* Each query step shows data visually with expandable SQL
|
|
14
|
+
*/
|
|
15
|
+
|
|
10
16
|
interface AIAnalystProps {
|
|
11
17
|
config: AIAnalystConfig;
|
|
12
18
|
theme?: 'light' | 'dark';
|
|
@@ -19,7 +25,11 @@ interface AIAnalystProps {
|
|
|
19
25
|
query: string;
|
|
20
26
|
}) => void;
|
|
21
27
|
}
|
|
22
|
-
|
|
28
|
+
interface AIAnalystHandle {
|
|
29
|
+
loadFullData: () => Promise<Record<string, unknown>[] | null>;
|
|
30
|
+
selectedDataSource: string | undefined;
|
|
31
|
+
}
|
|
32
|
+
declare const AIAnalyst: react__default.ForwardRefExoticComponent<AIAnalystProps & react__default.RefAttributes<AIAnalystHandle>>;
|
|
23
33
|
|
|
24
34
|
interface ColumnFilterProps {
|
|
25
35
|
columnId: string;
|
|
@@ -172,6 +182,8 @@ declare function useAIAnalyst(options: UseAIAnalystOptions): {
|
|
|
172
182
|
importConversation: (conv: AIConversation) => void;
|
|
173
183
|
/** Refresh table list from endpoint */
|
|
174
184
|
fetchTables: () => Promise<void>;
|
|
185
|
+
/** Load full data for the currently selected data source */
|
|
186
|
+
loadFullData: () => Promise<Record<string, unknown>[] | null>;
|
|
175
187
|
};
|
|
176
188
|
|
|
177
189
|
interface ExcelGridOptions<T> {
|
package/dist/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// src/components/AIAnalyst.tsx
|
|
2
2
|
import { stripSQLFromContent } from "@smallwebco/tinypivot-core";
|
|
3
|
-
import { useCallback as useCallback2, useEffect as useEffect2, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
3
|
+
import { forwardRef, useCallback as useCallback2, useEffect as useEffect2, useImperativeHandle, useMemo as useMemo2, useRef as useRef2, useState as useState2 } from "react";
|
|
4
4
|
|
|
5
5
|
// src/hooks/useAIAnalyst.ts
|
|
6
6
|
import {
|
|
@@ -523,6 +523,82 @@ What would you like to know about this data?`
|
|
|
523
523
|
setIsLoading(false);
|
|
524
524
|
}
|
|
525
525
|
}, [isLoading, schemas, effectiveDataSources, callAIEndpoint, executeQuery, handleDemoResponse, onConversationUpdate, onError]);
|
|
526
|
+
const loadFullData = useCallback(async () => {
|
|
527
|
+
const dataSourceId = conversation.dataSourceId;
|
|
528
|
+
if (!dataSourceId) {
|
|
529
|
+
return null;
|
|
530
|
+
}
|
|
531
|
+
const dataSource = effectiveDataSources.find((ds) => ds.id === dataSourceId);
|
|
532
|
+
if (!dataSource) {
|
|
533
|
+
return null;
|
|
534
|
+
}
|
|
535
|
+
const currentConfig = configRef.current;
|
|
536
|
+
if (currentConfig.dataSourceLoader) {
|
|
537
|
+
try {
|
|
538
|
+
const { data } = await currentConfig.dataSourceLoader(dataSourceId);
|
|
539
|
+
if (data && data.length > 0) {
|
|
540
|
+
return data;
|
|
541
|
+
}
|
|
542
|
+
} catch (err) {
|
|
543
|
+
console.warn("Failed to load full data:", err);
|
|
544
|
+
onError?.({
|
|
545
|
+
message: err instanceof Error ? err.message : "Failed to load full data",
|
|
546
|
+
type: "network"
|
|
547
|
+
});
|
|
548
|
+
}
|
|
549
|
+
return null;
|
|
550
|
+
}
|
|
551
|
+
if (currentConfig.queryExecutor) {
|
|
552
|
+
try {
|
|
553
|
+
const result = await currentConfig.queryExecutor(
|
|
554
|
+
`SELECT * FROM ${dataSource.table}`,
|
|
555
|
+
dataSource.table
|
|
556
|
+
);
|
|
557
|
+
if (result.data && result.data.length > 0) {
|
|
558
|
+
return result.data;
|
|
559
|
+
}
|
|
560
|
+
} catch (err) {
|
|
561
|
+
console.warn("Failed to load full data via query:", err);
|
|
562
|
+
onError?.({
|
|
563
|
+
message: err instanceof Error ? err.message : "Failed to load full data",
|
|
564
|
+
type: "network"
|
|
565
|
+
});
|
|
566
|
+
}
|
|
567
|
+
return null;
|
|
568
|
+
}
|
|
569
|
+
if (currentConfig.endpoint) {
|
|
570
|
+
try {
|
|
571
|
+
const response = await fetch(currentConfig.endpoint, {
|
|
572
|
+
method: "POST",
|
|
573
|
+
headers: { "Content-Type": "application/json" },
|
|
574
|
+
body: JSON.stringify({
|
|
575
|
+
action: "query",
|
|
576
|
+
sql: `SELECT * FROM ${dataSource.table}`,
|
|
577
|
+
table: dataSource.table
|
|
578
|
+
})
|
|
579
|
+
});
|
|
580
|
+
if (!response.ok) {
|
|
581
|
+
throw new Error(`Failed to load data: ${response.statusText}`);
|
|
582
|
+
}
|
|
583
|
+
const data = await response.json();
|
|
584
|
+
if (data.data && data.data.length > 0) {
|
|
585
|
+
return data.data;
|
|
586
|
+
}
|
|
587
|
+
} catch (err) {
|
|
588
|
+
console.warn("Failed to load full data from endpoint:", err);
|
|
589
|
+
onError?.({
|
|
590
|
+
message: err instanceof Error ? err.message : "Failed to load full data",
|
|
591
|
+
type: "network"
|
|
592
|
+
});
|
|
593
|
+
}
|
|
594
|
+
return null;
|
|
595
|
+
}
|
|
596
|
+
if (currentConfig.demoMode) {
|
|
597
|
+
const initialData = getInitialDemoData(dataSourceId);
|
|
598
|
+
return initialData || null;
|
|
599
|
+
}
|
|
600
|
+
return null;
|
|
601
|
+
}, [conversation.dataSourceId, effectiveDataSources, onError]);
|
|
526
602
|
const clearConversation = useCallback(() => {
|
|
527
603
|
const newConv = createConversation(configRef.current.sessionId);
|
|
528
604
|
setConversation(newConv);
|
|
@@ -558,13 +634,15 @@ What would you like to know about this data?`
|
|
|
558
634
|
exportConversation,
|
|
559
635
|
importConversation,
|
|
560
636
|
/** Refresh table list from endpoint */
|
|
561
|
-
fetchTables
|
|
637
|
+
fetchTables,
|
|
638
|
+
/** Load full data for the currently selected data source */
|
|
639
|
+
loadFullData
|
|
562
640
|
};
|
|
563
641
|
}
|
|
564
642
|
|
|
565
643
|
// src/components/AIAnalyst.tsx
|
|
566
644
|
import { Fragment, jsx, jsxs } from "react/jsx-runtime";
|
|
567
|
-
|
|
645
|
+
var AIAnalyst = forwardRef(({
|
|
568
646
|
config,
|
|
569
647
|
theme = "light",
|
|
570
648
|
onDataLoaded,
|
|
@@ -572,7 +650,7 @@ function AIAnalyst({
|
|
|
572
650
|
onQueryExecuted,
|
|
573
651
|
onError,
|
|
574
652
|
onViewResults
|
|
575
|
-
}) {
|
|
653
|
+
}, ref) => {
|
|
576
654
|
const {
|
|
577
655
|
messages,
|
|
578
656
|
hasMessages,
|
|
@@ -585,7 +663,8 @@ function AIAnalyst({
|
|
|
585
663
|
dataSources,
|
|
586
664
|
selectDataSource,
|
|
587
665
|
sendMessage,
|
|
588
|
-
clearConversation
|
|
666
|
+
clearConversation,
|
|
667
|
+
loadFullData
|
|
589
668
|
} = useAIAnalyst({
|
|
590
669
|
config,
|
|
591
670
|
onDataLoaded,
|
|
@@ -593,6 +672,10 @@ function AIAnalyst({
|
|
|
593
672
|
onQueryExecuted,
|
|
594
673
|
onError
|
|
595
674
|
});
|
|
675
|
+
useImperativeHandle(ref, () => ({
|
|
676
|
+
loadFullData,
|
|
677
|
+
selectedDataSource
|
|
678
|
+
}), [loadFullData, selectedDataSource]);
|
|
596
679
|
const [inputText, setInputText] = useState2("");
|
|
597
680
|
const [searchQuery, setSearchQuery] = useState2("");
|
|
598
681
|
const [selectedMessageId, setSelectedMessageId] = useState2(null);
|
|
@@ -1101,7 +1184,7 @@ function AIAnalyst({
|
|
|
1101
1184
|
)
|
|
1102
1185
|
] })
|
|
1103
1186
|
] }) });
|
|
1104
|
-
}
|
|
1187
|
+
});
|
|
1105
1188
|
|
|
1106
1189
|
// src/components/CalculatedFieldModal.tsx
|
|
1107
1190
|
import { validateSimpleFormula } from "@smallwebco/tinypivot-core";
|
|
@@ -4640,7 +4723,9 @@ function DataGrid({
|
|
|
4640
4723
|
const [showCopyToast, setShowCopyToast] = useState13(false);
|
|
4641
4724
|
const [copyToastMessage, setCopyToastMessage] = useState13("");
|
|
4642
4725
|
const [viewMode, setViewMode] = useState13("grid");
|
|
4726
|
+
const aiAnalystRef = useRef4(null);
|
|
4643
4727
|
const [aiLoadedData, setAiLoadedData] = useState13(null);
|
|
4728
|
+
const [isLoadingFullData, setIsLoadingFullData] = useState13(false);
|
|
4644
4729
|
const displayData = useMemo13(() => aiLoadedData || data, [aiLoadedData, data]);
|
|
4645
4730
|
const [_chartConfig, setChartConfig] = useState13(null);
|
|
4646
4731
|
const handleChartConfigChange = useCallback13((config) => {
|
|
@@ -4998,9 +5083,27 @@ function DataGrid({
|
|
|
4998
5083
|
[isSelecting]
|
|
4999
5084
|
);
|
|
5000
5085
|
const isShowingAIData = aiLoadedData !== null;
|
|
5001
|
-
const resetToFullData = useCallback13(() => {
|
|
5002
|
-
|
|
5003
|
-
|
|
5086
|
+
const resetToFullData = useCallback13(async () => {
|
|
5087
|
+
if (aiAnalystRef.current?.selectedDataSource) {
|
|
5088
|
+
setIsLoadingFullData(true);
|
|
5089
|
+
try {
|
|
5090
|
+
const fullData = await aiAnalystRef.current.loadFullData();
|
|
5091
|
+
if (fullData && fullData.length > 0) {
|
|
5092
|
+
setAiLoadedData(fullData);
|
|
5093
|
+
} else {
|
|
5094
|
+
setAiLoadedData(null);
|
|
5095
|
+
}
|
|
5096
|
+
} catch (err) {
|
|
5097
|
+
console.warn("Failed to load full data:", err);
|
|
5098
|
+
setAiLoadedData(null);
|
|
5099
|
+
} finally {
|
|
5100
|
+
setIsLoadingFullData(false);
|
|
5101
|
+
}
|
|
5102
|
+
} else {
|
|
5103
|
+
setAiLoadedData(null);
|
|
5104
|
+
}
|
|
5105
|
+
clearAllFilters();
|
|
5106
|
+
}, [clearAllFilters]);
|
|
5004
5107
|
const handleAIDataLoaded = useCallback13(
|
|
5005
5108
|
(payload) => {
|
|
5006
5109
|
setAiLoadedData(payload.data);
|
|
@@ -5418,11 +5521,12 @@ function DataGrid({
|
|
|
5418
5521
|
viewMode === "grid" && isShowingAIData && /* @__PURE__ */ jsxs8(
|
|
5419
5522
|
"button",
|
|
5420
5523
|
{
|
|
5421
|
-
className:
|
|
5524
|
+
className: `vpg-reset-data-btn${isLoadingFullData ? " vpg-loading-btn" : ""}`,
|
|
5525
|
+
disabled: isLoadingFullData,
|
|
5422
5526
|
title: "Reset to full dataset",
|
|
5423
5527
|
onClick: resetToFullData,
|
|
5424
5528
|
children: [
|
|
5425
|
-
/* @__PURE__ */ jsx8("svg", { className:
|
|
5529
|
+
/* @__PURE__ */ jsx8("svg", { className: `vpg-icon${isLoadingFullData ? " vpg-spin" : ""}`, fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8(
|
|
5426
5530
|
"path",
|
|
5427
5531
|
{
|
|
5428
5532
|
strokeLinecap: "round",
|
|
@@ -5431,7 +5535,7 @@ function DataGrid({
|
|
|
5431
5535
|
d: "M4 4v5h.582m15.356 2A8.001 8.001 0 004.582 9m0 0H9m11 11v-5h-.581m0 0a8.003 8.003 0 01-15.357-2m15.357 2H15"
|
|
5432
5536
|
}
|
|
5433
5537
|
) }),
|
|
5434
|
-
/* @__PURE__ */ jsx8("span", { children: "Full Data" })
|
|
5538
|
+
/* @__PURE__ */ jsx8("span", { children: isLoadingFullData ? "Loading..." : "Full Data" })
|
|
5435
5539
|
]
|
|
5436
5540
|
}
|
|
5437
5541
|
),
|
|
@@ -5499,6 +5603,7 @@ function DataGrid({
|
|
|
5499
5603
|
showAIAnalyst && aiAnalyst && /* @__PURE__ */ jsx8("div", { className: "vpg-ai-view", style: { display: viewMode === "ai" ? void 0 : "none" }, children: /* @__PURE__ */ jsx8(
|
|
5500
5604
|
AIAnalyst,
|
|
5501
5605
|
{
|
|
5606
|
+
ref: aiAnalystRef,
|
|
5502
5607
|
config: aiAnalyst,
|
|
5503
5608
|
theme: currentTheme,
|
|
5504
5609
|
onDataLoaded: handleAIDataLoaded,
|