@smallwebco/tinypivot-react 1.0.57 → 1.0.58

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/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
- declare function AIAnalyst({ config, theme, onDataLoaded, onConversationUpdate, onQueryExecuted, onError, onViewResults, }: AIAnalystProps): react_jsx_runtime.JSX.Element;
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
- declare function AIAnalyst({ config, theme, onDataLoaded, onConversationUpdate, onQueryExecuted, onError, onViewResults, }: AIAnalystProps): react_jsx_runtime.JSX.Element;
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
- function AIAnalyst({
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
- setAiLoadedData(null);
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: "vpg-reset-data-btn",
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: "vpg-icon", fill: "none", stroke: "currentColor", viewBox: "0 0 24 24", children: /* @__PURE__ */ jsx8(
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,