@quantumwake/terminal-ux-dashboard-components 0.1.3 → 0.1.7

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.cjs CHANGED
@@ -10,10 +10,13 @@ var heatmap = require('@nivo/heatmap');
10
10
  var PivotTableUI = require('react-pivottable/PivotTableUI');
11
11
  require('react-pivottable/pivottable.css');
12
12
  var lucideReact = require('lucide-react');
13
+ var CodeMirror = require('@uiw/react-codemirror');
14
+ var langSql = require('@codemirror/lang-sql');
13
15
 
14
16
  function _interopDefault (e) { return e && e.__esModule ? e : { default: e }; }
15
17
 
16
18
  var PivotTableUI__default = /*#__PURE__*/_interopDefault(PivotTableUI);
19
+ var CodeMirror__default = /*#__PURE__*/_interopDefault(CodeMirror);
17
20
 
18
21
  // src/context/DashboardContext.tsx
19
22
  var DashboardContext = react.createContext(null);
@@ -649,18 +652,19 @@ var renderCell = (v) => {
649
652
  if (typeof v === "boolean") return v ? "true" : "false";
650
653
  return String(v);
651
654
  };
652
- function SqlConsole({ columns = [], stateId }) {
655
+ function SqlConsole({ columns = [], stateId, defaultColumnsOpen = false, defaultSql = DEFAULT_SQL, autoRun = false }) {
653
656
  const { theme, runQuery } = useDashboard();
654
- const [sql, setSql] = react.useState(DEFAULT_SQL);
657
+ const [sql, setSql] = react.useState(defaultSql);
655
658
  const [result, setResult] = react.useState(null);
656
659
  const [error, setError] = react.useState(null);
657
660
  const [running, setRunning] = react.useState(false);
658
- const run = async () => {
659
- if (running || !sql.trim()) return;
661
+ const [columnsOpen, setColumnsOpen] = react.useState(defaultColumnsOpen);
662
+ const run = async (text = sql) => {
663
+ if (running || !text.trim()) return;
660
664
  setRunning(true);
661
665
  setError(null);
662
666
  try {
663
- const data = await runQuery(sql, stateId);
667
+ const data = await runQuery(text, stateId);
664
668
  setResult({ columns: data.columns || [], rows: data.rows || [] });
665
669
  } catch (err) {
666
670
  setResult(null);
@@ -669,12 +673,29 @@ function SqlConsole({ columns = [], stateId }) {
669
673
  setRunning(false);
670
674
  }
671
675
  };
672
- const onKeyDown = (e) => {
673
- if ((e.metaKey || e.ctrlKey) && e.key === "Enter") {
674
- e.preventDefault();
675
- void run();
676
- }
676
+ react.useEffect(() => {
677
+ if (!autoRun || !stateId) return;
678
+ setSql(defaultSql);
679
+ void run(defaultSql);
680
+ }, [stateId, autoRun, defaultSql]);
681
+ const runRef = react.useRef(() => {
682
+ });
683
+ runRef.current = () => {
684
+ void run();
677
685
  };
686
+ const schemaKey = columns.map((c) => c.name).join(",");
687
+ const extensions = react.useMemo(
688
+ () => [
689
+ langSql.sql({ schema: { data: columns.map((c) => c.name) }, upperCaseKeywords: true }),
690
+ CodeMirror.EditorView.lineWrapping,
691
+ CodeMirror.Prec.highest(CodeMirror.keymap.of([{ key: "Mod-Enter", run: () => {
692
+ runRef.current();
693
+ return true;
694
+ } }]))
695
+ ],
696
+ [schemaKey]
697
+ // eslint-disable-line react-hooks/exhaustive-deps
698
+ );
678
699
  const resultColumns = result?.columns || [];
679
700
  return /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex flex-col h-full", children: [
680
701
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: `border-b ${theme.border} bg-midnight-elevated p-3 shrink-0`, children: [
@@ -698,30 +719,46 @@ function SqlConsole({ columns = [], stateId }) {
698
719
  )
699
720
  ] }),
700
721
  /* @__PURE__ */ jsxRuntime.jsx(
701
- "textarea",
722
+ CodeMirror__default.default,
702
723
  {
703
724
  value: sql,
704
- onChange: (e) => setSql(e.target.value),
705
- onKeyDown,
706
- spellCheck: false,
707
- rows: 5,
708
- className: "w-full bg-midnight-surface border border-midnight-border px-3 py-2 text-sm font-mono outline-none text-midnight-text-body resize-y focus:border-midnight-accent transition-colors",
709
- placeholder: "SELECT ... FROM data"
725
+ onChange: setSql,
726
+ extensions,
727
+ theme: "dark",
728
+ basicSetup: { lineNumbers: false, foldGutter: false, highlightActiveLine: false },
729
+ height: "120px",
730
+ placeholder: "SELECT ... FROM data",
731
+ className: "border border-midnight-border text-sm overflow-hidden focus-within:border-midnight-accent"
710
732
  }
711
733
  ),
712
- columns.length > 0 && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-2 flex flex-wrap gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
713
- "button",
714
- {
715
- title: `Insert ${c.name}`,
716
- onClick: () => setSql((s) => `${s}${s.endsWith(" ") || s.endsWith("\n") || !s ? "" : " "}${c.name}`),
717
- className: "px-1.5 py-0.5 border border-midnight-border text-[11px] font-mono text-midnight-text-muted hover:bg-midnight-raised transition-colors",
718
- children: [
719
- c.name,
720
- /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50 ml-1", children: c.type })
721
- ]
722
- },
723
- c.name
724
- )) })
734
+ columns.length > 0 && /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "mt-2", children: [
735
+ /* @__PURE__ */ jsxRuntime.jsxs(
736
+ "button",
737
+ {
738
+ onClick: () => setColumnsOpen((o) => !o),
739
+ className: "flex items-center gap-1 text-[11px] font-mono text-midnight-text-muted hover:text-midnight-text-body transition-colors",
740
+ children: [
741
+ /* @__PURE__ */ jsxRuntime.jsx(lucideReact.ChevronDown, { className: `w-3 h-3 transition-transform ${columnsOpen ? "" : "-rotate-90"}` }),
742
+ "Columns (",
743
+ columns.length,
744
+ ")"
745
+ ]
746
+ }
747
+ ),
748
+ columnsOpen && /* @__PURE__ */ jsxRuntime.jsx("div", { className: "mt-1.5 flex flex-wrap gap-1", children: columns.map((c) => /* @__PURE__ */ jsxRuntime.jsxs(
749
+ "button",
750
+ {
751
+ title: `Insert ${c.name}`,
752
+ onClick: () => setSql((s) => `${s}${s.endsWith(" ") || s.endsWith("\n") || !s ? "" : " "}${c.name}`),
753
+ className: "px-1.5 py-0.5 border border-midnight-border text-[11px] font-mono text-midnight-text-muted hover:bg-midnight-raised transition-colors",
754
+ children: [
755
+ c.name,
756
+ /* @__PURE__ */ jsxRuntime.jsx("span", { className: "opacity-50 ml-1", children: c.type })
757
+ ]
758
+ },
759
+ c.name
760
+ )) })
761
+ ] })
725
762
  ] }),
726
763
  (result || error) && /* @__PURE__ */ jsxRuntime.jsx("div", { className: `flex items-center gap-4 px-3 py-1.5 border-b ${theme.border} text-xs font-mono shrink-0 ${error ? "text-red-400" : "text-midnight-text-muted"}`, children: error ? /* @__PURE__ */ jsxRuntime.jsxs("span", { className: "flex items-center gap-1.5", children: [
727
764
  /* @__PURE__ */ jsxRuntime.jsx(lucideReact.AlertCircle, { className: "w-3.5 h-3.5" }),
@@ -1612,6 +1649,11 @@ function ModeTab({ active, onClick, icon: Icon, label }) {
1612
1649
  }
1613
1650
  );
1614
1651
  }
1652
+ var MODE_TABS = [
1653
+ { mode: "dashboard", icon: lucideReact.Sparkles, label: "Dashboard" },
1654
+ { mode: "builder", icon: lucideReact.BarChart3, label: "Chart Builder" },
1655
+ { mode: "sql", icon: lucideReact.Terminal, label: "SQL" }
1656
+ ];
1615
1657
  function DataExplorer({
1616
1658
  records,
1617
1659
  columns,
@@ -1623,12 +1665,15 @@ function DataExplorer({
1623
1665
  savedDashboards = [],
1624
1666
  loading = false,
1625
1667
  analyzing = false,
1668
+ defaultMode = "dashboard",
1669
+ tabs = ["dashboard", "builder", "sql"],
1626
1670
  onSelectState,
1627
1671
  onRefreshStates
1628
1672
  }) {
1629
1673
  const { theme, newDashboard, addPanel, saveDashboard, listDashboards, searchDashboards, loadDashboard, deleteDashboard, analyzeDataset, refineDashboard } = useDashboard();
1630
1674
  const caps = useCapabilities();
1631
- const [mode, setMode] = react.useState("dashboard");
1675
+ const visibleTabs = MODE_TABS.filter((t) => tabs.includes(t.mode));
1676
+ const [mode, setMode] = react.useState(tabs.includes(defaultMode) ? defaultMode : tabs[0] ?? "sql");
1632
1677
  const [fullscreen, setFullscreen] = react.useState(false);
1633
1678
  const [showSaved, setShowSaved] = react.useState(false);
1634
1679
  const [saveName, setSaveName] = react.useState("");
@@ -1693,9 +1738,7 @@ function DataExplorer({
1693
1738
  )
1694
1739
  ] }),
1695
1740
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex items-center gap-1 mb-3 shrink-0", children: [
1696
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "dashboard", onClick: () => setMode("dashboard"), icon: lucideReact.Sparkles, label: "Dashboard" }),
1697
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "builder", onClick: () => setMode("builder"), icon: lucideReact.BarChart3, label: "Chart Builder" }),
1698
- /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === "sql", onClick: () => setMode("sql"), icon: lucideReact.Terminal, label: "SQL" }),
1741
+ visibleTabs.map((t) => /* @__PURE__ */ jsxRuntime.jsx(ModeTab, { active: mode === t.mode, onClick: () => setMode(t.mode), icon: t.icon, label: t.label }, t.mode)),
1699
1742
  caps.canNew && /* @__PURE__ */ jsxRuntime.jsxs(
1700
1743
  "button",
1701
1744
  {