react-open-source-grid 1.6.7 → 1.7.2

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.
@@ -337,6 +337,7 @@ __export(index_exports, {
337
337
  AdvancedFilterBuilder: () => AdvancedFilterBuilder,
338
338
  BadgeCell: () => BadgeCell,
339
339
  ButtonCell: () => ButtonCell,
340
+ ChartOverlay: () => ChartOverlay,
340
341
  ColumnChooser: () => ColumnChooser,
341
342
  ColumnFilters: () => ColumnFilters,
342
343
  CurrencyCell: () => CurrencyCell,
@@ -362,6 +363,7 @@ __export(index_exports, {
362
363
  PivotToolbar: () => PivotToolbar,
363
364
  PriorityIndicator: () => PriorityIndicator,
364
365
  ProgressBar: () => ProgressBar,
366
+ QuickChart: () => QuickChart,
365
367
  Rating: () => Rating,
366
368
  RichSelectEditor: () => RichSelectEditor,
367
369
  ServerAdapter: () => ServerAdapter,
@@ -373,6 +375,7 @@ __export(index_exports, {
373
375
  VirtualScroller: () => VirtualScroller,
374
376
  WebSocketMockFeed: () => WebSocketMockFeed,
375
377
  alpineTheme: () => alpineTheme,
378
+ buildChartConfigFromRange: () => buildChartConfigFromRange,
376
379
  buildPivot: () => buildPivot,
377
380
  buildTreeFromFlat: () => buildTreeFromFlat,
378
381
  collapseAllNodes: () => collapseAllNodes,
@@ -411,11 +414,14 @@ __export(index_exports, {
411
414
  isTreeNode: () => isTreeNode,
412
415
  loadDensityMode: () => loadDensityMode,
413
416
  materialTheme: () => materialTheme,
417
+ normalizeRange: () => normalizeRange,
414
418
  parseFormattedNumber: () => parseFormattedNumber,
415
419
  quartzTheme: () => quartzTheme,
416
420
  saveDensityMode: () => saveDensityMode,
417
421
  themes: () => themes,
418
422
  toggleNodeExpansion: () => toggleNodeExpansion,
423
+ updateChartTheme: () => updateChartTheme,
424
+ updateChartType: () => updateChartType,
419
425
  useDensityMode: () => useDensityMode,
420
426
  useEditorAutoFocus: () => useEditorAutoFocus,
421
427
  useEditorClickOutside: () => useEditorClickOutside,
@@ -5718,6 +5724,7 @@ var ContextMenu = ({
5718
5724
  onClose
5719
5725
  }) => {
5720
5726
  const menuRef = (0, import_react15.useRef)(null);
5727
+ const [openSubmenu, setOpenSubmenu] = import_react15.default.useState(null);
5721
5728
  (0, import_react15.useEffect)(() => {
5722
5729
  const handleClickOutside = (event) => {
5723
5730
  if (menuRef.current && !menuRef.current.contains(event.target)) {
@@ -5772,16 +5779,18 @@ var ContextMenu = ({
5772
5779
  }
5773
5780
  }, [x, y]);
5774
5781
  const handleItemClick = (0, import_react15.useCallback)((item) => {
5775
- if (item.disabled || item.type === "separator") {
5782
+ if (item.disabled) {
5783
+ return;
5784
+ }
5785
+ if (item.submenu) {
5786
+ setOpenSubmenu(openSubmenu === item.id ? null : item.id || null);
5776
5787
  return;
5777
5788
  }
5778
5789
  if (item.onClick) {
5779
5790
  item.onClick();
5780
5791
  }
5781
- if (!item.submenu) {
5782
- onClose();
5783
- }
5784
- }, [onClose]);
5792
+ onClose();
5793
+ }, [onClose, openSubmenu]);
5785
5794
  const renderMenuItem = (item, index) => {
5786
5795
  if (item.type === "separator") {
5787
5796
  return /* @__PURE__ */ import_react15.default.createElement(
@@ -5793,11 +5802,10 @@ var ContextMenu = ({
5793
5802
  }
5794
5803
  );
5795
5804
  }
5796
- return /* @__PURE__ */ import_react15.default.createElement(
5805
+ return /* @__PURE__ */ import_react15.default.createElement("div", { key: item.id || index, className: "context-menu-item-wrapper" }, /* @__PURE__ */ import_react15.default.createElement(
5797
5806
  "div",
5798
5807
  {
5799
- key: item.id || index,
5800
- className: `context-menu-item ${item.disabled ? "disabled" : ""} ${item.danger ? "danger" : ""}`,
5808
+ className: `context-menu-item ${item.disabled ? "disabled" : ""} ${item.danger ? "danger" : ""} ${item.submenu ? "has-submenu" : ""}`,
5801
5809
  onClick: () => handleItemClick(item),
5802
5810
  role: "menuitem",
5803
5811
  "aria-disabled": item.disabled,
@@ -5807,7 +5815,7 @@ var ContextMenu = ({
5807
5815
  /* @__PURE__ */ import_react15.default.createElement("span", { className: "context-menu-label" }, item.label),
5808
5816
  item.shortcut && /* @__PURE__ */ import_react15.default.createElement("span", { className: "context-menu-shortcut" }, item.shortcut),
5809
5817
  item.submenu && /* @__PURE__ */ import_react15.default.createElement("span", { className: "context-menu-arrow" }, "\u25B6")
5810
- );
5818
+ ), item.submenu && openSubmenu === item.id && /* @__PURE__ */ import_react15.default.createElement("div", { className: "context-menu-submenu" }, item.submenu.map((subItem, subIndex) => renderMenuItem(subItem, subIndex))));
5811
5819
  };
5812
5820
  return /* @__PURE__ */ import_react15.default.createElement(
5813
5821
  "div",
@@ -5975,16 +5983,20 @@ var useContextMenu = ({
5975
5983
  showExport: (config == null ? void 0 : config.showExport) !== false,
5976
5984
  showColumnOptions: (config == null ? void 0 : config.showColumnOptions) !== false,
5977
5985
  showFilterByValue: (config == null ? void 0 : config.showFilterByValue) !== false,
5986
+ showChartOptions: (config == null ? void 0 : config.showChartOptions) !== false,
5978
5987
  customItems: (config == null ? void 0 : config.customItems) || [],
5979
- onBeforeShow: config == null ? void 0 : config.onBeforeShow
5988
+ onBeforeShow: config == null ? void 0 : config.onBeforeShow,
5989
+ onCreateChart: config == null ? void 0 : config.onCreateChart
5980
5990
  }), [
5981
5991
  config == null ? void 0 : config.enabled,
5982
5992
  config == null ? void 0 : config.showCopy,
5983
5993
  config == null ? void 0 : config.showExport,
5984
5994
  config == null ? void 0 : config.showColumnOptions,
5985
5995
  config == null ? void 0 : config.showFilterByValue,
5996
+ config == null ? void 0 : config.showChartOptions,
5986
5997
  config == null ? void 0 : config.customItems,
5987
- config == null ? void 0 : config.onBeforeShow
5998
+ config == null ? void 0 : config.onBeforeShow,
5999
+ config == null ? void 0 : config.onCreateChart
5988
6000
  ]);
5989
6001
  const buildCellMenuItems = (0, import_react16.useCallback)((row, column) => {
5990
6002
  const items = [];
@@ -6070,6 +6082,58 @@ var useContextMenu = ({
6070
6082
  disabled: cellValue == null
6071
6083
  });
6072
6084
  }
6085
+ if (menuConfig.showChartOptions !== false && hasSelection) {
6086
+ if (items.length > 0) {
6087
+ items.push({ type: "separator" });
6088
+ }
6089
+ items.push({
6090
+ id: "create-chart",
6091
+ label: "Create Chart",
6092
+ icon: "\u{1F4CA}",
6093
+ submenu: [
6094
+ {
6095
+ id: "chart-line",
6096
+ label: "Line Chart",
6097
+ icon: "\u{1F4C8}",
6098
+ onClick: () => {
6099
+ if (menuConfig.onCreateChart) {
6100
+ menuConfig.onCreateChart("line", selectedRows, row, column);
6101
+ }
6102
+ }
6103
+ },
6104
+ {
6105
+ id: "chart-bar",
6106
+ label: "Bar Chart",
6107
+ icon: "\u{1F4CA}",
6108
+ onClick: () => {
6109
+ if (menuConfig.onCreateChart) {
6110
+ menuConfig.onCreateChart("bar", selectedRows, row, column);
6111
+ }
6112
+ }
6113
+ },
6114
+ {
6115
+ id: "chart-area",
6116
+ label: "Area Chart",
6117
+ icon: "\u{1F4C9}",
6118
+ onClick: () => {
6119
+ if (menuConfig.onCreateChart) {
6120
+ menuConfig.onCreateChart("area", selectedRows, row, column);
6121
+ }
6122
+ }
6123
+ },
6124
+ {
6125
+ id: "chart-pie",
6126
+ label: "Pie Chart",
6127
+ icon: "\u{1F967}",
6128
+ onClick: () => {
6129
+ if (menuConfig.onCreateChart) {
6130
+ menuConfig.onCreateChart("pie", selectedRows, row, column);
6131
+ }
6132
+ }
6133
+ }
6134
+ ]
6135
+ });
6136
+ }
6073
6137
  if (menuConfig.customItems.length > 0) {
6074
6138
  if (items.length > 0) {
6075
6139
  items.push({ type: "separator" });
@@ -13937,11 +14001,501 @@ function renderMarkdownPreview(markdown) {
13937
14001
  }
13938
14002
  return html;
13939
14003
  }
14004
+
14005
+ // src/charts/rangeToChart.ts
14006
+ function normalizeRange(range) {
14007
+ const startRow = Math.min(range.start.rowIndex, range.end.rowIndex);
14008
+ const endRow = Math.max(range.start.rowIndex, range.end.rowIndex);
14009
+ const startCol = Math.min(range.start.colIndex, range.end.colIndex);
14010
+ const endCol = Math.max(range.start.colIndex, range.end.colIndex);
14011
+ return { startRow, endRow, startCol, endCol };
14012
+ }
14013
+ function isNumeric(value) {
14014
+ if (value === null || value === void 0 || value === "") {
14015
+ return false;
14016
+ }
14017
+ const num = Number(value);
14018
+ return !isNaN(num) && isFinite(num);
14019
+ }
14020
+ function toNumber2(value) {
14021
+ if (isNumeric(value)) {
14022
+ return Number(value);
14023
+ }
14024
+ return 0;
14025
+ }
14026
+ function generateChartId() {
14027
+ return `chart-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
14028
+ }
14029
+ var DEFAULT_COLORS = [
14030
+ "#2563eb",
14031
+ // vibrant blue
14032
+ "#10b981",
14033
+ // emerald green
14034
+ "#f59e0b",
14035
+ // amber/orange
14036
+ "#ef4444",
14037
+ // bright red
14038
+ "#8b5cf6",
14039
+ // purple
14040
+ "#ec4899",
14041
+ // pink
14042
+ "#06b6d4",
14043
+ // cyan
14044
+ "#f97316",
14045
+ // orange
14046
+ "#14b8a6",
14047
+ // teal
14048
+ "#a855f7",
14049
+ // violet
14050
+ "#84cc16",
14051
+ // lime
14052
+ "#f43f5e"
14053
+ // rose
14054
+ ];
14055
+ function buildChartConfigFromRange(options) {
14056
+ const {
14057
+ range,
14058
+ rows,
14059
+ columns,
14060
+ chartType,
14061
+ useFirstColumnAsCategory = true,
14062
+ title,
14063
+ theme = "light"
14064
+ } = options;
14065
+ const normalized = normalizeRange(range);
14066
+ const { startRow, endRow, startCol, endCol } = normalized;
14067
+ if (startRow < 0 || endRow >= rows.length) {
14068
+ throw new Error("Invalid row range");
14069
+ }
14070
+ if (startCol < 0 || endCol >= columns.length) {
14071
+ throw new Error("Invalid column range");
14072
+ }
14073
+ const selectedRows = rows.slice(startRow, endRow + 1);
14074
+ const selectedColumns = columns.slice(startCol, endCol + 1);
14075
+ let categoryColumnIndex = 0;
14076
+ let dataColumnStartIndex = 0;
14077
+ if (useFirstColumnAsCategory && selectedColumns.length > 1) {
14078
+ categoryColumnIndex = 0;
14079
+ dataColumnStartIndex = 1;
14080
+ } else {
14081
+ categoryColumnIndex = -1;
14082
+ dataColumnStartIndex = 0;
14083
+ }
14084
+ const xLabels = [];
14085
+ if (categoryColumnIndex >= 0) {
14086
+ const categoryField = selectedColumns[categoryColumnIndex].field;
14087
+ selectedRows.forEach((row) => {
14088
+ const value = row[categoryField];
14089
+ xLabels.push(value !== null && value !== void 0 ? String(value) : "");
14090
+ });
14091
+ } else {
14092
+ for (let i = 0; i < selectedRows.length; i++) {
14093
+ xLabels.push(i + 1);
14094
+ }
14095
+ }
14096
+ const series = [];
14097
+ const dataColumns = selectedColumns.slice(dataColumnStartIndex);
14098
+ dataColumns.forEach((column, index) => {
14099
+ const seriesData = [];
14100
+ const field = column.field;
14101
+ let hasNumericData = false;
14102
+ selectedRows.forEach((row) => {
14103
+ const value = row[field];
14104
+ if (isNumeric(value)) {
14105
+ hasNumericData = true;
14106
+ seriesData.push(toNumber2(value));
14107
+ } else {
14108
+ seriesData.push(0);
14109
+ }
14110
+ });
14111
+ if (hasNumericData) {
14112
+ series.push({
14113
+ name: column.headerName || field,
14114
+ data: seriesData,
14115
+ color: DEFAULT_COLORS[index % DEFAULT_COLORS.length]
14116
+ });
14117
+ }
14118
+ });
14119
+ if (series.length === 0) {
14120
+ throw new Error("No numeric data found in the selected range");
14121
+ }
14122
+ if (chartType === "pie") {
14123
+ if (series.length === 1) {
14124
+ } else {
14125
+ const summedData = new Array(xLabels.length).fill(0);
14126
+ series.forEach((s) => {
14127
+ s.data.forEach((value, idx) => {
14128
+ summedData[idx] += value;
14129
+ });
14130
+ });
14131
+ series.length = 0;
14132
+ series.push({
14133
+ name: "Total",
14134
+ data: summedData,
14135
+ color: DEFAULT_COLORS[0]
14136
+ });
14137
+ }
14138
+ }
14139
+ return {
14140
+ id: generateChartId(),
14141
+ type: chartType,
14142
+ title: title || `${chartType.charAt(0).toUpperCase() + chartType.slice(1)} Chart`,
14143
+ xLabels,
14144
+ series,
14145
+ theme
14146
+ };
14147
+ }
14148
+ function updateChartType(config, newType) {
14149
+ var _a;
14150
+ return {
14151
+ ...config,
14152
+ type: newType,
14153
+ title: ((_a = config.title) == null ? void 0 : _a.replace(
14154
+ /^(Line|Bar|Area|Pie)/i,
14155
+ newType.charAt(0).toUpperCase() + newType.slice(1)
14156
+ )) || `${newType.charAt(0).toUpperCase() + newType.slice(1)} Chart`
14157
+ };
14158
+ }
14159
+ function updateChartTheme(config, newTheme) {
14160
+ return {
14161
+ ...config,
14162
+ theme: newTheme
14163
+ };
14164
+ }
14165
+
14166
+ // src/charts/QuickChart.tsx
14167
+ var import_react40 = __toESM(require("react"), 1);
14168
+ var import_recharts = require("recharts");
14169
+ var import_html_to_image = require("html-to-image");
14170
+ var QuickChart = ({
14171
+ config,
14172
+ onClose,
14173
+ onChangeType,
14174
+ onToggleTheme,
14175
+ allowTypeSwitch = true,
14176
+ allowThemeSwitch = true,
14177
+ width = 600,
14178
+ height = 400
14179
+ }) => {
14180
+ const chartRef = (0, import_react40.useRef)(null);
14181
+ const theme = config.theme || "light";
14182
+ const transformedData = config.xLabels.map((label, index) => {
14183
+ const dataPoint = { name: label };
14184
+ config.series.forEach((series) => {
14185
+ dataPoint[series.name] = series.data[index] || 0;
14186
+ });
14187
+ return dataPoint;
14188
+ });
14189
+ const pieData = config.series.length > 0 ? config.xLabels.map((label, index) => ({
14190
+ name: label,
14191
+ value: config.series[0].data[index] || 0
14192
+ })) : [];
14193
+ const handleExportPNG = async () => {
14194
+ if (!chartRef.current) return;
14195
+ try {
14196
+ const dataUrl = await (0, import_html_to_image.toPng)(chartRef.current, {
14197
+ quality: 1,
14198
+ pixelRatio: 2,
14199
+ backgroundColor: theme === "dark" ? "#1a1a1a" : "#ffffff"
14200
+ });
14201
+ const link = document.createElement("a");
14202
+ link.download = `${config.title || "chart"}-${Date.now()}.png`;
14203
+ link.href = dataUrl;
14204
+ link.click();
14205
+ } catch (error) {
14206
+ console.error("Failed to export chart:", error);
14207
+ }
14208
+ };
14209
+ const chartTypeIcon = (type) => {
14210
+ switch (type) {
14211
+ case "line":
14212
+ return "\u{1F4C8}";
14213
+ case "bar":
14214
+ return "\u{1F4CA}";
14215
+ case "area":
14216
+ return "\u{1F4C9}";
14217
+ case "pie":
14218
+ return "\u{1F967}";
14219
+ default:
14220
+ return "\u{1F4CA}";
14221
+ }
14222
+ };
14223
+ const renderChart = () => {
14224
+ const commonProps = {
14225
+ data: config.type === "pie" ? pieData : transformedData,
14226
+ margin: { top: 5, right: 30, left: 20, bottom: 5 }
14227
+ };
14228
+ const axisProps = {
14229
+ stroke: theme === "dark" ? "#888" : "#666"
14230
+ };
14231
+ const gridProps = {
14232
+ strokeDasharray: "3 3",
14233
+ stroke: theme === "dark" ? "#333" : "#ddd"
14234
+ };
14235
+ switch (config.type) {
14236
+ case "line":
14237
+ return /* @__PURE__ */ import_react40.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.LineChart, { ...commonProps }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.CartesianGrid, { ...gridProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.XAxis, { dataKey: "name", ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.YAxis, { ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(
14238
+ import_recharts.Tooltip,
14239
+ {
14240
+ contentStyle: {
14241
+ backgroundColor: theme === "dark" ? "#2a2a2a" : "#fff",
14242
+ border: `1px solid ${theme === "dark" ? "#444" : "#ccc"}`,
14243
+ color: theme === "dark" ? "#fff" : "#000"
14244
+ }
14245
+ }
14246
+ ), /* @__PURE__ */ import_react40.default.createElement(import_recharts.Legend, null), config.series.map((series) => /* @__PURE__ */ import_react40.default.createElement(
14247
+ import_recharts.Line,
14248
+ {
14249
+ key: series.name,
14250
+ type: "monotone",
14251
+ dataKey: series.name,
14252
+ stroke: series.color,
14253
+ strokeWidth: 2,
14254
+ dot: { r: 4 },
14255
+ activeDot: { r: 6 }
14256
+ }
14257
+ ))));
14258
+ case "bar":
14259
+ return /* @__PURE__ */ import_react40.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.BarChart, { ...commonProps }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.CartesianGrid, { ...gridProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.XAxis, { dataKey: "name", ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.YAxis, { ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(
14260
+ import_recharts.Tooltip,
14261
+ {
14262
+ contentStyle: {
14263
+ backgroundColor: theme === "dark" ? "#2a2a2a" : "#fff",
14264
+ border: `1px solid ${theme === "dark" ? "#444" : "#ccc"}`,
14265
+ color: theme === "dark" ? "#fff" : "#000"
14266
+ }
14267
+ }
14268
+ ), /* @__PURE__ */ import_react40.default.createElement(import_recharts.Legend, null), config.series.map((series) => /* @__PURE__ */ import_react40.default.createElement(import_recharts.Bar, { key: series.name, dataKey: series.name, fill: series.color }))));
14269
+ case "area":
14270
+ return /* @__PURE__ */ import_react40.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.AreaChart, { ...commonProps }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.CartesianGrid, { ...gridProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.XAxis, { dataKey: "name", ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(import_recharts.YAxis, { ...axisProps }), /* @__PURE__ */ import_react40.default.createElement(
14271
+ import_recharts.Tooltip,
14272
+ {
14273
+ contentStyle: {
14274
+ backgroundColor: theme === "dark" ? "#2a2a2a" : "#fff",
14275
+ border: `1px solid ${theme === "dark" ? "#444" : "#ccc"}`,
14276
+ color: theme === "dark" ? "#fff" : "#000"
14277
+ }
14278
+ }
14279
+ ), /* @__PURE__ */ import_react40.default.createElement(import_recharts.Legend, null), config.series.map((series) => /* @__PURE__ */ import_react40.default.createElement(
14280
+ import_recharts.Area,
14281
+ {
14282
+ key: series.name,
14283
+ type: "monotone",
14284
+ dataKey: series.name,
14285
+ fill: series.color,
14286
+ stroke: series.color,
14287
+ fillOpacity: 0.6
14288
+ }
14289
+ ))));
14290
+ case "pie":
14291
+ return /* @__PURE__ */ import_react40.default.createElement(import_recharts.ResponsiveContainer, { width: "100%", height: "100%" }, /* @__PURE__ */ import_react40.default.createElement(import_recharts.PieChart, null, /* @__PURE__ */ import_react40.default.createElement(
14292
+ import_recharts.Pie,
14293
+ {
14294
+ data: pieData,
14295
+ cx: "50%",
14296
+ cy: "50%",
14297
+ labelLine: false,
14298
+ label: ({ name, percent }) => `${name}: ${(percent * 100).toFixed(0)}%`,
14299
+ outerRadius: Math.min(height, width) / 4,
14300
+ fill: "#8884d8",
14301
+ dataKey: "value"
14302
+ },
14303
+ pieData.map((_, index) => /* @__PURE__ */ import_react40.default.createElement(
14304
+ import_recharts.Cell,
14305
+ {
14306
+ key: `cell-${index}`,
14307
+ fill: DEFAULT_COLORS[index % DEFAULT_COLORS.length]
14308
+ }
14309
+ ))
14310
+ ), /* @__PURE__ */ import_react40.default.createElement(
14311
+ import_recharts.Tooltip,
14312
+ {
14313
+ contentStyle: {
14314
+ backgroundColor: theme === "dark" ? "#2a2a2a" : "#fff",
14315
+ border: `1px solid ${theme === "dark" ? "#444" : "#ccc"}`,
14316
+ color: theme === "dark" ? "#fff" : "#000"
14317
+ }
14318
+ }
14319
+ ), /* @__PURE__ */ import_react40.default.createElement(import_recharts.Legend, null)));
14320
+ default:
14321
+ return /* @__PURE__ */ import_react40.default.createElement("div", null, "Unsupported chart type");
14322
+ }
14323
+ };
14324
+ return /* @__PURE__ */ import_react40.default.createElement(
14325
+ "div",
14326
+ {
14327
+ ref: chartRef,
14328
+ className: `quick-chart quick-chart--${theme}`,
14329
+ style: { width, height }
14330
+ },
14331
+ /* @__PURE__ */ import_react40.default.createElement("div", { className: "quick-chart__header" }, /* @__PURE__ */ import_react40.default.createElement("h3", { className: "quick-chart__title" }, config.title), /* @__PURE__ */ import_react40.default.createElement("div", { className: "quick-chart__controls" }, allowTypeSwitch && onChangeType && /* @__PURE__ */ import_react40.default.createElement("div", { className: "quick-chart__type-selector" }, ["line", "bar", "area", "pie"].map((type) => /* @__PURE__ */ import_react40.default.createElement(
14332
+ "button",
14333
+ {
14334
+ key: type,
14335
+ className: `quick-chart__type-btn ${config.type === type ? "quick-chart__type-btn--active" : ""}`,
14336
+ onClick: () => onChangeType(type),
14337
+ title: `${type.charAt(0).toUpperCase() + type.slice(1)} Chart`,
14338
+ "aria-label": `Switch to ${type} chart`
14339
+ },
14340
+ chartTypeIcon(type)
14341
+ ))), allowThemeSwitch && onToggleTheme && /* @__PURE__ */ import_react40.default.createElement(
14342
+ "button",
14343
+ {
14344
+ className: "quick-chart__btn",
14345
+ onClick: onToggleTheme,
14346
+ title: "Toggle theme",
14347
+ "aria-label": "Toggle theme"
14348
+ },
14349
+ theme === "dark" ? "\u2600\uFE0F" : "\u{1F319}"
14350
+ ), /* @__PURE__ */ import_react40.default.createElement(
14351
+ "button",
14352
+ {
14353
+ className: "quick-chart__btn",
14354
+ onClick: handleExportPNG,
14355
+ title: "Export as PNG",
14356
+ "aria-label": "Export chart as PNG"
14357
+ },
14358
+ "\u{1F4E5}"
14359
+ ), onClose && /* @__PURE__ */ import_react40.default.createElement(
14360
+ "button",
14361
+ {
14362
+ className: "quick-chart__btn quick-chart__close",
14363
+ onClick: onClose,
14364
+ title: "Close",
14365
+ "aria-label": "Close chart"
14366
+ },
14367
+ "\xD7"
14368
+ ))),
14369
+ /* @__PURE__ */ import_react40.default.createElement("div", { className: "quick-chart__body" }, renderChart())
14370
+ );
14371
+ };
14372
+
14373
+ // src/charts/ChartOverlay.tsx
14374
+ var import_react41 = __toESM(require("react"), 1);
14375
+ var ChartOverlay = ({
14376
+ config,
14377
+ onClose,
14378
+ onChangeType,
14379
+ onToggleTheme,
14380
+ position = "bottom-right",
14381
+ draggable = true
14382
+ }) => {
14383
+ const overlayRef = (0, import_react41.useRef)(null);
14384
+ const [isDragging, setIsDragging] = (0, import_react41.useState)(false);
14385
+ const [dragOffset, setDragOffset] = (0, import_react41.useState)({ x: 0, y: 0 });
14386
+ const [chartPosition, setChartPosition] = (0, import_react41.useState)({ x: 0, y: 0 });
14387
+ const [dimensions] = (0, import_react41.useState)({ width: 600, height: 400 });
14388
+ (0, import_react41.useEffect)(() => {
14389
+ if (!overlayRef.current) return;
14390
+ const updatePosition = () => {
14391
+ const viewport = {
14392
+ width: window.innerWidth,
14393
+ height: window.innerHeight
14394
+ };
14395
+ let x = 0;
14396
+ let y = 0;
14397
+ switch (position) {
14398
+ case "top-right":
14399
+ x = viewport.width - dimensions.width - 40;
14400
+ y = 40;
14401
+ break;
14402
+ case "top-left":
14403
+ x = 40;
14404
+ y = 40;
14405
+ break;
14406
+ case "bottom-right":
14407
+ x = viewport.width - dimensions.width - 40;
14408
+ y = viewport.height - dimensions.height - 40;
14409
+ break;
14410
+ case "bottom-left":
14411
+ x = 40;
14412
+ y = viewport.height - dimensions.height - 40;
14413
+ break;
14414
+ case "center":
14415
+ x = (viewport.width - dimensions.width) / 2;
14416
+ y = (viewport.height - dimensions.height) / 2;
14417
+ break;
14418
+ }
14419
+ setChartPosition({ x, y });
14420
+ };
14421
+ updatePosition();
14422
+ window.addEventListener("resize", updatePosition);
14423
+ return () => window.removeEventListener("resize", updatePosition);
14424
+ }, [position, dimensions]);
14425
+ const handleMouseDown = (e) => {
14426
+ if (!draggable) return;
14427
+ if (e.target.closest(".quick-chart__body")) return;
14428
+ setIsDragging(true);
14429
+ setDragOffset({
14430
+ x: e.clientX - chartPosition.x,
14431
+ y: e.clientY - chartPosition.y
14432
+ });
14433
+ };
14434
+ (0, import_react41.useEffect)(() => {
14435
+ if (!isDragging) return;
14436
+ const handleMouseMove = (e) => {
14437
+ const newX = e.clientX - dragOffset.x;
14438
+ const newY = e.clientY - dragOffset.y;
14439
+ const maxX = window.innerWidth - dimensions.width;
14440
+ const maxY = window.innerHeight - dimensions.height;
14441
+ setChartPosition({
14442
+ x: Math.max(0, Math.min(newX, maxX)),
14443
+ y: Math.max(0, Math.min(newY, maxY))
14444
+ });
14445
+ };
14446
+ const handleMouseUp = () => {
14447
+ setIsDragging(false);
14448
+ };
14449
+ document.addEventListener("mousemove", handleMouseMove);
14450
+ document.addEventListener("mouseup", handleMouseUp);
14451
+ return () => {
14452
+ document.removeEventListener("mousemove", handleMouseMove);
14453
+ document.removeEventListener("mouseup", handleMouseUp);
14454
+ };
14455
+ }, [isDragging, dragOffset, dimensions]);
14456
+ (0, import_react41.useEffect)(() => {
14457
+ const handleKeyDown = (e) => {
14458
+ if (e.key === "Escape") {
14459
+ onClose();
14460
+ }
14461
+ };
14462
+ document.addEventListener("keydown", handleKeyDown);
14463
+ return () => document.removeEventListener("keydown", handleKeyDown);
14464
+ }, [onClose]);
14465
+ return /* @__PURE__ */ import_react41.default.createElement("div", { className: "chart-overlay" }, /* @__PURE__ */ import_react41.default.createElement("div", { className: "chart-overlay__backdrop", onClick: onClose }), /* @__PURE__ */ import_react41.default.createElement(
14466
+ "div",
14467
+ {
14468
+ ref: overlayRef,
14469
+ className: `chart-overlay__container ${isDragging ? "chart-overlay__container--dragging" : ""} ${draggable ? "chart-overlay__container--draggable" : ""}`,
14470
+ style: {
14471
+ left: chartPosition.x,
14472
+ top: chartPosition.y,
14473
+ width: dimensions.width,
14474
+ height: dimensions.height
14475
+ },
14476
+ onMouseDown: handleMouseDown
14477
+ },
14478
+ /* @__PURE__ */ import_react41.default.createElement(
14479
+ QuickChart,
14480
+ {
14481
+ config,
14482
+ onClose,
14483
+ onChangeType,
14484
+ onToggleTheme,
14485
+ allowTypeSwitch: true,
14486
+ allowThemeSwitch: true,
14487
+ width: dimensions.width,
14488
+ height: dimensions.height
14489
+ }
14490
+ )
14491
+ ));
14492
+ };
13940
14493
  // Annotate the CommonJS export names for ESM import in node:
13941
14494
  0 && (module.exports = {
13942
14495
  AdvancedFilterBuilder,
13943
14496
  BadgeCell,
13944
14497
  ButtonCell,
14498
+ ChartOverlay,
13945
14499
  ColumnChooser,
13946
14500
  ColumnFilters,
13947
14501
  CurrencyCell,
@@ -13967,6 +14521,7 @@ function renderMarkdownPreview(markdown) {
13967
14521
  PivotToolbar,
13968
14522
  PriorityIndicator,
13969
14523
  ProgressBar,
14524
+ QuickChart,
13970
14525
  Rating,
13971
14526
  RichSelectEditor,
13972
14527
  ServerAdapter,
@@ -13978,6 +14533,7 @@ function renderMarkdownPreview(markdown) {
13978
14533
  VirtualScroller,
13979
14534
  WebSocketMockFeed,
13980
14535
  alpineTheme,
14536
+ buildChartConfigFromRange,
13981
14537
  buildPivot,
13982
14538
  buildTreeFromFlat,
13983
14539
  collapseAllNodes,
@@ -14016,11 +14572,14 @@ function renderMarkdownPreview(markdown) {
14016
14572
  isTreeNode,
14017
14573
  loadDensityMode,
14018
14574
  materialTheme,
14575
+ normalizeRange,
14019
14576
  parseFormattedNumber,
14020
14577
  quartzTheme,
14021
14578
  saveDensityMode,
14022
14579
  themes,
14023
14580
  toggleNodeExpansion,
14581
+ updateChartTheme,
14582
+ updateChartType,
14024
14583
  useDensityMode,
14025
14584
  useEditorAutoFocus,
14026
14585
  useEditorClickOutside,