@burdenoff/microfe-bigconsole 2026.728.1 → 2026.729.1
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.
|
@@ -374,7 +374,7 @@ var _ = n(function({ widget: n, data: c = [], onDrilldown: f }) {
|
|
|
374
374
|
});
|
|
375
375
|
}), v = "application/x-pivot-field", y = ({ rowFields: e, columnFields: t, valueFields: n, availableFields: r, onMoveField: i, onChangeAggregation: o }) => {
|
|
376
376
|
let [s, l] = a(null), p = (e) => (t) => {
|
|
377
|
-
t.preventDefault(), l(null);
|
|
377
|
+
t.preventDefault(), t.stopPropagation(), l(null);
|
|
378
378
|
let n = t.dataTransfer.getData(v) || t.dataTransfer.getData("text/plain");
|
|
379
379
|
if (n) try {
|
|
380
380
|
let { field: t, from: r } = JSON.parse(n);
|
|
@@ -410,7 +410,7 @@ var _ = n(function({ widget: n, data: c = [], onDrilldown: f }) {
|
|
|
410
410
|
]
|
|
411
411
|
}, e), h = (e, t, n) => /* @__PURE__ */ d("div", {
|
|
412
412
|
onDragOver: (e) => {
|
|
413
|
-
e.preventDefault(), e.dataTransfer.dropEffect = "move", s !== t && l(t);
|
|
413
|
+
e.preventDefault(), e.stopPropagation(), e.dataTransfer.dropEffect = "move", s !== t && l(t);
|
|
414
414
|
},
|
|
415
415
|
onDragLeave: () => l((e) => e === t ? null : e),
|
|
416
416
|
onDrop: p(t),
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"PivotTableWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/pivot-table/PivotTableWidget.tsx"],"sourcesContent":["/**\n * PivotTableWidget Component\n *\n * Interactive pivot table for data analysis with rows, columns, and aggregations.\n * v2.0 spec compliant widget type.\n *\n * In edit mode it exposes a drag-and-drop field shelf: fields can be dragged\n * between the Rows / Columns / Values / Available zones, the pivot recomputes\n * live, and the new layout is persisted to widget config so it survives reload.\n */\n\nimport type { JSX } from 'react';\nimport { type FC, memo, useMemo, useState, useCallback } from 'react';\nimport type { Widget, PivotTableConfig as PivotTableConfigType, PivotValueField } from '../../../types';\nimport { ChevronDown, ChevronRight, Settings, GripVertical } from 'lucide-react';\nimport { useIsEditMode } from '../../../store';\nimport { useWidgetOperations } from '../../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// Re-export types from main types file\nexport type PivotTableConfig = PivotTableConfigType;\nexport type ValueField = PivotValueField;\n\nexport interface PivotTableWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Raw data array */\n data?: Record<string, unknown> | Record<string, unknown>[];\n /** Whether the widget is in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether data is loading */\n isLoading?: boolean;\n /** Error message if any */\n error?: string | null;\n /** Callback when widget is clicked */\n onClick?: () => void;\n /** Drilldown handler */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\ninterface PivotNode {\n key: string;\n values: Record<string, number>;\n children: Map<string, PivotNode>;\n expanded: boolean;\n depth: number;\n}\n\n/** The arrangement of fields across the pivot's three field zones. */\ninterface PivotLayout {\n rows: string[];\n columns: string[];\n values: ValueField[];\n}\n\n/** Drop targets for the drag-and-drop field shelf. */\ntype PivotZone = 'rows' | 'columns' | 'values' | 'available';\n\nconst AGGREGATIONS: ValueField['aggregation'][] = ['sum', 'count', 'average', 'min', 'max'];\n\n// ============================================================================\n// Aggregation Functions\n// ============================================================================\n\nconst aggregationFunctions: Record<string, (values: number[]) => number> = {\n sum: (values) => values.reduce((a, b) => a + b, 0),\n count: (values) => values.length,\n average: (values) => (values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0),\n min: (values) => (values.length ? Math.min(...values) : 0),\n max: (values) => (values.length ? Math.max(...values) : 0),\n};\n\n// ============================================================================\n// Pivot Engine\n// ============================================================================\n\nfunction buildPivotTree(\n data: Record<string, unknown>[],\n rowFields: string[],\n valueFields: ValueField[],\n depth: number = 0\n): PivotNode {\n const root: PivotNode = {\n key: 'root',\n values: {},\n children: new Map(),\n expanded: true,\n depth,\n };\n\n if (rowFields.length === 0) {\n // Leaf level - aggregate values\n for (const vf of valueFields) {\n const values = data.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n root.values[vf.field] = aggregationFunctions[vf.aggregation](values);\n }\n return root;\n }\n\n const currentField = rowFields[0];\n const remainingFields = rowFields.slice(1);\n\n // Group data by current field\n const groups = new Map<string, Record<string, unknown>[]>();\n for (const row of data) {\n const key = String(row[currentField] ?? 'Unknown');\n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(row);\n }\n\n // Build children recursively\n for (const [key, groupData] of groups) {\n const childNode = buildPivotTree(groupData, remainingFields, valueFields, depth + 1);\n childNode.key = key;\n root.children.set(key, childNode);\n }\n\n // Calculate aggregates for this level\n for (const vf of valueFields) {\n const values = data.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n root.values[vf.field] = aggregationFunctions[vf.aggregation](values);\n }\n\n return root;\n}\n\nfunction getColumnValues(data: Record<string, unknown>[], columnFields: string[]): string[] {\n if (columnFields.length === 0) return ['Total'];\n\n const columnField = columnFields[0];\n const uniqueValues = new Set<string>();\n for (const row of data) {\n uniqueValues.add(String(row[columnField] ?? 'Unknown'));\n }\n return Array.from(uniqueValues).sort();\n}\n\nfunction getValuesByColumn(\n data: Record<string, unknown>[],\n columnFields: string[],\n valueFields: ValueField[],\n rowFilter: Record<string, string>\n): Map<string, Record<string, number>> {\n // Filter data by row values\n let filteredData = data;\n for (const [field, value] of Object.entries(rowFilter)) {\n filteredData = filteredData.filter((row) => String(row[field]) === value);\n }\n\n const result = new Map<string, Record<string, number>>();\n\n if (columnFields.length === 0) {\n // No column grouping - just aggregate\n const values: Record<string, number> = {};\n for (const vf of valueFields) {\n const nums = filteredData.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n values[vf.field] = aggregationFunctions[vf.aggregation](nums);\n }\n result.set('Total', values);\n return result;\n }\n\n const columnField = columnFields[0];\n const columnValues = getColumnValues(data, columnFields);\n\n for (const colValue of columnValues) {\n const colData = filteredData.filter((row) => String(row[columnField]) === colValue);\n const values: Record<string, number> = {};\n for (const vf of valueFields) {\n const nums = colData.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n values[vf.field] = aggregationFunctions[vf.aggregation](nums);\n }\n result.set(colValue, values);\n }\n\n return result;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const PivotTableWidget: FC<PivotTableWidgetProps> = memo(function PivotTableWidget({\n widget,\n data: rawData = [],\n onDrilldown,\n}) {\n // Normalize data to array format\n const data = useMemo(() => (Array.isArray(rawData) ? rawData : []), [rawData]);\n const config: PivotTableConfig = useMemo(() => {\n const defaultConfig: PivotTableConfig = {\n rows: [],\n columns: [],\n values: [],\n showRowTotals: true,\n showColumnTotals: true,\n showGrandTotal: true,\n expandable: true,\n numberFormat: 'number',\n decimalPlaces: 0,\n };\n return widget.config ? { ...defaultConfig, ...(widget.config as Partial<PivotTableConfig>) } : defaultConfig;\n }, [widget.config]);\n\n const isEditMode = useIsEditMode();\n const { updateWidget } = useWidgetOperations(undefined, undefined, { skipFetch: true });\n\n // Field arrangement is local state so dragging recomputes the pivot instantly.\n // Seeded from config and kept in sync with it: if config changes externally\n // (properties panel, undo, another editor), we re-seed during render via the\n // \"adjust state on prop change\" pattern. Our own persist round-trip re-seeds\n // to the identical layout, so it can't loop.\n const configSig = JSON.stringify({ rows: config.rows, columns: config.columns, values: config.values });\n const [layout, setLayout] = useState<PivotLayout>({\n rows: config.rows,\n columns: config.columns,\n values: config.values,\n });\n const [seededSig, setSeededSig] = useState(configSig);\n if (configSig !== seededSig) {\n // Config changed externally → re-seed during render (React's documented\n // \"adjusting state on prop change\" pattern). Our own persist re-seeds to the\n // identical layout, so this can't loop.\n setSeededSig(configSig);\n setLayout({ rows: config.rows, columns: config.columns, values: config.values });\n }\n\n const { rows: rowFields, columns: columnFields, values: valueFields } = layout;\n\n // Persist the layout to config (edit mode only — viewers explore locally).\n const persistLayout = useCallback(\n (next: PivotLayout) => {\n if (!isEditMode) return;\n void updateWidget({\n id: widget.id,\n config: {\n ...(widget.config as Record<string, unknown> | undefined),\n rows: next.rows,\n columns: next.columns,\n values: next.values,\n },\n });\n },\n [isEditMode, updateWidget, widget.id, widget.config]\n );\n\n const applyLayout = useCallback(\n (next: PivotLayout) => {\n setLayout(next);\n persistLayout(next);\n },\n [persistLayout]\n );\n\n const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set(['root']));\n\n // Build pivot structure\n const pivotTree = useMemo(() => {\n return buildPivotTree(data, rowFields, valueFields);\n }, [data, rowFields, valueFields]);\n\n const columnValues = useMemo(() => {\n return getColumnValues(data, columnFields);\n }, [data, columnFields]);\n\n // Grand totals per column for the footer — computed once (was previously\n // recomputed for every column value inside the footer's render loop).\n const footerColumnData = useMemo(\n () => (config.showColumnTotals ? getValuesByColumn(data, columnFields, valueFields, {}) : null),\n [config.showColumnTotals, data, columnFields, valueFields]\n );\n\n // All fields present in the data, and those not yet placed in a zone.\n const allFields = useMemo(() => (data.length > 0 ? Object.keys(data[0]) : []), [data]);\n const availableFields = useMemo(() => {\n const used = new Set<string>([...rowFields, ...columnFields, ...valueFields.map((v) => v.field)]);\n return allFields.filter((f) => !used.has(f));\n }, [allFields, rowFields, columnFields, valueFields]);\n\n // Move a field from one zone to another and recompute + persist.\n const moveField = useCallback(\n (field: string, from: PivotZone, to: PivotZone) => {\n if (from === to) return;\n const next: PivotLayout = {\n rows: from === 'rows' ? rowFields.filter((f) => f !== field) : [...rowFields],\n columns: from === 'columns' ? columnFields.filter((f) => f !== field) : [...columnFields],\n values: from === 'values' ? valueFields.filter((v) => v.field !== field) : [...valueFields],\n };\n if (to === 'rows' && !next.rows.includes(field)) next.rows.push(field);\n else if (to === 'columns' && !next.columns.includes(field)) next.columns.push(field);\n else if (to === 'values' && !next.values.some((v) => v.field === field)) {\n next.values.push({ field, aggregation: 'sum' });\n }\n applyLayout(next);\n },\n [rowFields, columnFields, valueFields, applyLayout]\n );\n\n const changeAggregation = useCallback(\n (field: string, aggregation: ValueField['aggregation']) => {\n applyLayout({\n rows: [...rowFields],\n columns: [...columnFields],\n values: valueFields.map((v) => (v.field === field ? { ...v, aggregation } : v)),\n });\n },\n [rowFields, columnFields, valueFields, applyLayout]\n );\n\n // Format number based on config\n const formatValue = useCallback(\n (value: number): string => {\n if (isNaN(value)) return '-';\n\n switch (config.numberFormat) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value);\n case 'percentage':\n return new Intl.NumberFormat('en-US', {\n style: 'percent',\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value / 100);\n default:\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value);\n }\n },\n [config.numberFormat, config.decimalPlaces]\n );\n\n // Toggle row expansion\n const toggleRow = useCallback((rowPath: string) => {\n setExpandedRows((prev) => {\n const next = new Set(prev);\n if (next.has(rowPath)) {\n next.delete(rowPath);\n } else {\n next.add(rowPath);\n }\n return next;\n });\n }, []);\n\n // Handle cell click for drilldown\n const handleCellClick = useCallback(\n (rowFilter: Record<string, string>, columnValue: string) => {\n if (onDrilldown) {\n onDrilldown({\n rowFilter,\n columnValue,\n type: 'pivot_cell_click',\n });\n }\n },\n [onDrilldown]\n );\n\n // Render row recursively\n const renderRows = useCallback(\n (node: PivotNode, rowFilter: Record<string, string>, parentPath: string, depth: number): JSX.Element[] => {\n const rows: JSX.Element[] = [];\n const hasChildren = node.children.size > 0;\n const isExpanded = expandedRows.has(parentPath);\n const rowField = rowFields[depth - 1];\n\n if (depth > 0) {\n const columnData = getValuesByColumn(data, columnFields, valueFields, rowFilter);\n\n rows.push(\n <tr key={parentPath} className=\"hover:bg-bg-sunken/50 transition-colors\">\n {/* Row header */}\n <td\n className=\"sticky left-0 bg-bg-surface border-r border-border-default px-3 py-2 font-medium text-sm\"\n style={{ paddingLeft: `${depth * 16 + 8}px` }}\n >\n <div className=\"flex items-center gap-1\">\n {config.expandable && hasChildren && (\n <button\n type=\"button\"\n onClick={() => toggleRow(parentPath)}\n className=\"p-0.5 hover:bg-bg-sunken rounded\"\n >\n {isExpanded ? (\n <ChevronDown className=\"w-4 h-4 text-text-secondary\" />\n ) : (\n <ChevronRight className=\"w-4 h-4 text-text-secondary\" />\n )}\n </button>\n )}\n {!hasChildren && config.expandable && <span className=\"w-5\" />}\n <span>{node.key}</span>\n </div>\n </td>\n\n {/* Value cells */}\n {columnValues.map((colValue) => {\n const cellValues = columnData.get(colValue) || {};\n return valueFields.map((vf) => (\n <td\n key={`${colValue}-${vf.field}`}\n className=\"px-3 py-2 text-sm text-right tabular-nums cursor-pointer hover:bg-action-primary-bg/10\"\n onClick={() => handleCellClick(rowFilter, colValue)}\n >\n {formatValue(cellValues[vf.field] || 0)}\n </td>\n ));\n })}\n\n {/* Row total */}\n {config.showRowTotals &&\n valueFields.map((vf) => (\n <td\n key={`total-${vf.field}`}\n className=\"px-3 py-2 text-sm text-right tabular-nums font-medium bg-bg-sunken/30\"\n >\n {formatValue(node.values[vf.field] || 0)}\n </td>\n ))}\n </tr>\n );\n }\n\n // Render children if expanded\n if (hasChildren && (isExpanded || depth === 0)) {\n const sortedKeys = Array.from(node.children.keys()).sort();\n for (const childKey of sortedKeys) {\n const childNode = node.children.get(childKey)!;\n const childPath = `${parentPath}/${childKey}`;\n const childFilter = rowField ? { ...rowFilter, [rowField]: childKey } : rowFilter;\n rows.push(...renderRows(childNode, childFilter, childPath, depth + 1));\n }\n }\n\n return rows;\n },\n [\n data,\n config,\n rowFields,\n columnFields,\n valueFields,\n columnValues,\n expandedRows,\n formatValue,\n handleCellClick,\n toggleRow,\n ]\n );\n\n const hasLayout = rowFields.length > 0 || valueFields.length > 0;\n\n // No data state\n if (data.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-8 text-text-secondary\">\n <Settings className=\"w-12 h-12 mb-4 opacity-50\" />\n <p className=\"text-sm\">No data available</p>\n <p className=\"text-xs mt-1\">Configure data source to populate pivot table</p>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full overflow-hidden\">\n {/* Drag-and-drop field shelf (edit mode) */}\n {isEditMode && (\n <PivotFieldShelf\n rowFields={rowFields}\n columnFields={columnFields}\n valueFields={valueFields}\n availableFields={availableFields}\n onMoveField={moveField}\n onChangeAggregation={changeAggregation}\n />\n )}\n\n {!hasLayout ? (\n <div className=\"flex flex-col items-center justify-center flex-1 p-8 text-text-secondary\">\n <Settings className=\"w-12 h-12 mb-4 opacity-50\" />\n <p className=\"text-sm\">Configure Pivot Table</p>\n <p className=\"text-xs mt-1\">\n {isEditMode\n ? 'Drag fields into the Rows and Values zones above'\n : 'Add row fields and value fields in widget settings'}\n </p>\n </div>\n ) : (\n <div className=\"flex-1 overflow-auto\">\n <table className=\"w-full border-collapse\">\n {/* Header */}\n <thead className=\"sticky top-0 z-10\">\n {/* Column headers row */}\n <tr className=\"bg-bg-sunken\">\n <th className=\"sticky left-0 bg-bg-sunken border-r border-b border-border-default px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-text-secondary\">\n {rowFields.length > 0 ? rowFields.map((r) => r.replace(/_/g, ' ')).join(' / ') : 'Values'}\n </th>\n\n {/* Column value headers */}\n {columnValues.map((colValue) => (\n <th\n key={colValue}\n colSpan={valueFields.length || 1}\n className=\"border-b border-border-default px-3 py-2 text-center text-xs font-semibold uppercase tracking-wider text-text-secondary\"\n >\n {colValue}\n </th>\n ))}\n\n {/* Total header */}\n {config.showRowTotals && (\n <th\n colSpan={valueFields.length || 1}\n className=\"border-b border-border-default px-3 py-2 text-center text-xs font-semibold uppercase tracking-wider text-text-secondary bg-bg-sunken/50\"\n >\n Total\n </th>\n )}\n </tr>\n\n {/* Value field headers row (if multiple values) */}\n {valueFields.length > 1 && (\n <tr className=\"bg-bg-surface\">\n <th className=\"sticky left-0 bg-bg-surface border-r border-b border-border-default\" />\n {columnValues.map((colValue) =>\n valueFields.map((vf) => (\n <th\n key={`${colValue}-${vf.field}`}\n className=\"border-b border-border-default px-3 py-1 text-center text-xs text-text-secondary\"\n >\n {vf.label || vf.field.replace(/_/g, ' ')}\n </th>\n ))\n )}\n {config.showRowTotals &&\n valueFields.map((vf) => (\n <th\n key={`total-${vf.field}`}\n className=\"border-b border-border-default px-3 py-1 text-center text-xs text-text-secondary bg-bg-sunken/30\"\n >\n {vf.label || vf.field.replace(/_/g, ' ')}\n </th>\n ))}\n </tr>\n )}\n </thead>\n\n {/* Body */}\n <tbody className=\"divide-y divide-border-default\">{renderRows(pivotTree, {}, 'root', 0)}</tbody>\n\n {/* Footer with column totals */}\n {config.showColumnTotals && (\n <tfoot>\n <tr className=\"bg-bg-sunken/50 font-medium\">\n <td className=\"sticky left-0 bg-bg-sunken/50 border-r border-t border-border-default px-3 py-2 text-sm\">\n Total\n </td>\n {columnValues.map((colValue) => {\n const cellValues = footerColumnData?.get(colValue) || {};\n return valueFields.map((vf) => (\n <td\n key={`footer-${colValue}-${vf.field}`}\n className=\"border-t border-border-default px-3 py-2 text-sm text-right tabular-nums\"\n >\n {formatValue(cellValues[vf.field] || 0)}\n </td>\n ));\n })}\n {config.showRowTotals &&\n config.showGrandTotal &&\n valueFields.map((vf) => (\n <td\n key={`grand-${vf.field}`}\n className=\"border-t border-border-default px-3 py-2 text-sm text-right tabular-nums font-bold bg-bg-sunken\"\n >\n {formatValue(pivotTree.values[vf.field] || 0)}\n </td>\n ))}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n )}\n </div>\n );\n});\n\n// ============================================================================\n// Field shelf (drag-and-drop zones)\n// ============================================================================\n\ninterface PivotFieldShelfProps {\n rowFields: string[];\n columnFields: string[];\n valueFields: ValueField[];\n availableFields: string[];\n onMoveField: (field: string, from: PivotZone, to: PivotZone) => void;\n onChangeAggregation: (field: string, aggregation: ValueField['aggregation']) => void;\n}\n\nconst DND_MIME = 'application/x-pivot-field';\n\nconst PivotFieldShelf: FC<PivotFieldShelfProps> = ({\n rowFields,\n columnFields,\n valueFields,\n availableFields,\n onMoveField,\n onChangeAggregation,\n}) => {\n const [dragOverZone, setDragOverZone] = useState<PivotZone | null>(null);\n\n const handleDrop = (zone: PivotZone) => (e: React.DragEvent) => {\n e.preventDefault();\n setDragOverZone(null);\n const raw = e.dataTransfer.getData(DND_MIME) || e.dataTransfer.getData('text/plain');\n if (!raw) return;\n try {\n const { field, from } = JSON.parse(raw) as { field: string; from: PivotZone };\n if (field) onMoveField(field, from, zone);\n } catch {\n // Ignore malformed drag payloads.\n }\n };\n\n const chip = (field: string, from: PivotZone, vf?: ValueField) => (\n <span\n key={field}\n draggable\n onDragStart={(e) => {\n const payload = JSON.stringify({ field, from });\n e.dataTransfer.setData(DND_MIME, payload);\n e.dataTransfer.setData('text/plain', payload);\n e.dataTransfer.effectAllowed = 'move';\n }}\n className=\"inline-flex items-center gap-1 px-2 py-1 rounded-md bg-bg-surface border border-border-default text-xs text-text-primary cursor-grab\"\n >\n <GripVertical className=\"w-3 h-3 text-text-tertiary\" aria-hidden=\"true\" />\n <span>{(vf?.label || field).replace(/_/g, ' ')}</span>\n {vf && (\n <select\n value={vf.aggregation}\n onChange={(e) => onChangeAggregation(field, e.target.value as ValueField['aggregation'])}\n onClick={(e) => e.stopPropagation()}\n aria-label={`Aggregation for ${field}`}\n className=\"ml-1 bg-bg-sunken text-text-secondary rounded px-1 py-0.5 text-[10px] uppercase\"\n >\n {AGGREGATIONS.map((agg) => (\n <option key={agg} value={agg}>\n {agg}\n </option>\n ))}\n </select>\n )}\n </span>\n );\n\n const zone = (label: string, name: PivotZone, children: JSX.Element[] | JSX.Element) => (\n <div\n onDragOver={(e) => {\n e.preventDefault();\n e.dataTransfer.dropEffect = 'move';\n if (dragOverZone !== name) setDragOverZone(name);\n }}\n onDragLeave={() => setDragOverZone((z) => (z === name ? null : z))}\n onDrop={handleDrop(name)}\n className={`flex-1 min-w-[8rem] rounded-md border p-2 transition-colors ${\n dragOverZone === name\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default bg-bg-sunken/30'\n }`}\n >\n <div className=\"text-[10px] font-semibold uppercase tracking-wider text-text-tertiary mb-1.5\">{label}</div>\n <div className=\"flex flex-wrap gap-1.5 min-h-[1.75rem]\">{children}</div>\n </div>\n );\n\n return (\n <div className=\"flex flex-wrap gap-2 p-2 border-b border-border-default bg-bg-surface\">\n {zone(\n 'Rows',\n 'rows',\n rowFields.map((f) => chip(f, 'rows'))\n )}\n {zone(\n 'Columns',\n 'columns',\n columnFields.map((f) => chip(f, 'columns'))\n )}\n {zone(\n 'Values',\n 'values',\n valueFields.map((vf) => chip(vf.field, 'values', vf))\n )}\n {zone(\n 'Available',\n 'available',\n availableFields.length > 0 ? (\n availableFields.map((f) => chip(f, 'available'))\n ) : (\n <span className=\"text-[11px] text-text-tertiary italic\">All fields in use</span>\n )\n )}\n </div>\n );\n};\n\nexport default PivotTableWidget;\n"],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"PivotTableWidget.js","names":[],"sources":["../../../../../src/bigconsole/components/widgets/pivot-table/PivotTableWidget.tsx"],"sourcesContent":["/**\n * PivotTableWidget Component\n *\n * Interactive pivot table for data analysis with rows, columns, and aggregations.\n * v2.0 spec compliant widget type.\n *\n * In edit mode it exposes a drag-and-drop field shelf: fields can be dragged\n * between the Rows / Columns / Values / Available zones, the pivot recomputes\n * live, and the new layout is persisted to widget config so it survives reload.\n */\n\nimport type { JSX } from 'react';\nimport { type FC, memo, useMemo, useState, useCallback } from 'react';\nimport type { Widget, PivotTableConfig as PivotTableConfigType, PivotValueField } from '../../../types';\nimport { ChevronDown, ChevronRight, Settings, GripVertical } from 'lucide-react';\nimport { useIsEditMode } from '../../../store';\nimport { useWidgetOperations } from '../../../hooks';\n\n// ============================================================================\n// Types\n// ============================================================================\n\n// Re-export types from main types file\nexport type PivotTableConfig = PivotTableConfigType;\nexport type ValueField = PivotValueField;\n\nexport interface PivotTableWidgetProps {\n /** Widget configuration */\n widget: Widget;\n /** Raw data array */\n data?: Record<string, unknown> | Record<string, unknown>[];\n /** Whether the widget is in edit mode */\n isEditMode?: boolean;\n /** Whether the widget is selected */\n isSelected?: boolean;\n /** Whether data is loading */\n isLoading?: boolean;\n /** Error message if any */\n error?: string | null;\n /** Callback when widget is clicked */\n onClick?: () => void;\n /** Drilldown handler */\n onDrilldown?: (selectedData: Record<string, unknown>) => void;\n}\n\ninterface PivotNode {\n key: string;\n values: Record<string, number>;\n children: Map<string, PivotNode>;\n expanded: boolean;\n depth: number;\n}\n\n/** The arrangement of fields across the pivot's three field zones. */\ninterface PivotLayout {\n rows: string[];\n columns: string[];\n values: ValueField[];\n}\n\n/** Drop targets for the drag-and-drop field shelf. */\ntype PivotZone = 'rows' | 'columns' | 'values' | 'available';\n\nconst AGGREGATIONS: ValueField['aggregation'][] = ['sum', 'count', 'average', 'min', 'max'];\n\n// ============================================================================\n// Aggregation Functions\n// ============================================================================\n\nconst aggregationFunctions: Record<string, (values: number[]) => number> = {\n sum: (values) => values.reduce((a, b) => a + b, 0),\n count: (values) => values.length,\n average: (values) => (values.length ? values.reduce((a, b) => a + b, 0) / values.length : 0),\n min: (values) => (values.length ? Math.min(...values) : 0),\n max: (values) => (values.length ? Math.max(...values) : 0),\n};\n\n// ============================================================================\n// Pivot Engine\n// ============================================================================\n\nfunction buildPivotTree(\n data: Record<string, unknown>[],\n rowFields: string[],\n valueFields: ValueField[],\n depth: number = 0\n): PivotNode {\n const root: PivotNode = {\n key: 'root',\n values: {},\n children: new Map(),\n expanded: true,\n depth,\n };\n\n if (rowFields.length === 0) {\n // Leaf level - aggregate values\n for (const vf of valueFields) {\n const values = data.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n root.values[vf.field] = aggregationFunctions[vf.aggregation](values);\n }\n return root;\n }\n\n const currentField = rowFields[0];\n const remainingFields = rowFields.slice(1);\n\n // Group data by current field\n const groups = new Map<string, Record<string, unknown>[]>();\n for (const row of data) {\n const key = String(row[currentField] ?? 'Unknown');\n if (!groups.has(key)) {\n groups.set(key, []);\n }\n groups.get(key)!.push(row);\n }\n\n // Build children recursively\n for (const [key, groupData] of groups) {\n const childNode = buildPivotTree(groupData, remainingFields, valueFields, depth + 1);\n childNode.key = key;\n root.children.set(key, childNode);\n }\n\n // Calculate aggregates for this level\n for (const vf of valueFields) {\n const values = data.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n root.values[vf.field] = aggregationFunctions[vf.aggregation](values);\n }\n\n return root;\n}\n\nfunction getColumnValues(data: Record<string, unknown>[], columnFields: string[]): string[] {\n if (columnFields.length === 0) return ['Total'];\n\n const columnField = columnFields[0];\n const uniqueValues = new Set<string>();\n for (const row of data) {\n uniqueValues.add(String(row[columnField] ?? 'Unknown'));\n }\n return Array.from(uniqueValues).sort();\n}\n\nfunction getValuesByColumn(\n data: Record<string, unknown>[],\n columnFields: string[],\n valueFields: ValueField[],\n rowFilter: Record<string, string>\n): Map<string, Record<string, number>> {\n // Filter data by row values\n let filteredData = data;\n for (const [field, value] of Object.entries(rowFilter)) {\n filteredData = filteredData.filter((row) => String(row[field]) === value);\n }\n\n const result = new Map<string, Record<string, number>>();\n\n if (columnFields.length === 0) {\n // No column grouping - just aggregate\n const values: Record<string, number> = {};\n for (const vf of valueFields) {\n const nums = filteredData.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n values[vf.field] = aggregationFunctions[vf.aggregation](nums);\n }\n result.set('Total', values);\n return result;\n }\n\n const columnField = columnFields[0];\n const columnValues = getColumnValues(data, columnFields);\n\n for (const colValue of columnValues) {\n const colData = filteredData.filter((row) => String(row[columnField]) === colValue);\n const values: Record<string, number> = {};\n for (const vf of valueFields) {\n const nums = colData.map((row) => Number(row[vf.field]) || 0).filter((v) => !isNaN(v));\n values[vf.field] = aggregationFunctions[vf.aggregation](nums);\n }\n result.set(colValue, values);\n }\n\n return result;\n}\n\n// ============================================================================\n// Component\n// ============================================================================\n\nexport const PivotTableWidget: FC<PivotTableWidgetProps> = memo(function PivotTableWidget({\n widget,\n data: rawData = [],\n onDrilldown,\n}) {\n // Normalize data to array format\n const data = useMemo(() => (Array.isArray(rawData) ? rawData : []), [rawData]);\n const config: PivotTableConfig = useMemo(() => {\n const defaultConfig: PivotTableConfig = {\n rows: [],\n columns: [],\n values: [],\n showRowTotals: true,\n showColumnTotals: true,\n showGrandTotal: true,\n expandable: true,\n numberFormat: 'number',\n decimalPlaces: 0,\n };\n return widget.config ? { ...defaultConfig, ...(widget.config as Partial<PivotTableConfig>) } : defaultConfig;\n }, [widget.config]);\n\n const isEditMode = useIsEditMode();\n const { updateWidget } = useWidgetOperations(undefined, undefined, { skipFetch: true });\n\n // Field arrangement is local state so dragging recomputes the pivot instantly.\n // Seeded from config and kept in sync with it: if config changes externally\n // (properties panel, undo, another editor), we re-seed during render via the\n // \"adjust state on prop change\" pattern. Our own persist round-trip re-seeds\n // to the identical layout, so it can't loop.\n const configSig = JSON.stringify({ rows: config.rows, columns: config.columns, values: config.values });\n const [layout, setLayout] = useState<PivotLayout>({\n rows: config.rows,\n columns: config.columns,\n values: config.values,\n });\n const [seededSig, setSeededSig] = useState(configSig);\n if (configSig !== seededSig) {\n // Config changed externally → re-seed during render (React's documented\n // \"adjusting state on prop change\" pattern). Our own persist re-seeds to the\n // identical layout, so this can't loop.\n setSeededSig(configSig);\n setLayout({ rows: config.rows, columns: config.columns, values: config.values });\n }\n\n const { rows: rowFields, columns: columnFields, values: valueFields } = layout;\n\n // Persist the layout to config (edit mode only — viewers explore locally).\n const persistLayout = useCallback(\n (next: PivotLayout) => {\n if (!isEditMode) return;\n void updateWidget({\n id: widget.id,\n config: {\n ...(widget.config as Record<string, unknown> | undefined),\n rows: next.rows,\n columns: next.columns,\n values: next.values,\n },\n });\n },\n [isEditMode, updateWidget, widget.id, widget.config]\n );\n\n const applyLayout = useCallback(\n (next: PivotLayout) => {\n setLayout(next);\n persistLayout(next);\n },\n [persistLayout]\n );\n\n const [expandedRows, setExpandedRows] = useState<Set<string>>(new Set(['root']));\n\n // Build pivot structure\n const pivotTree = useMemo(() => {\n return buildPivotTree(data, rowFields, valueFields);\n }, [data, rowFields, valueFields]);\n\n const columnValues = useMemo(() => {\n return getColumnValues(data, columnFields);\n }, [data, columnFields]);\n\n // Grand totals per column for the footer — computed once (was previously\n // recomputed for every column value inside the footer's render loop).\n const footerColumnData = useMemo(\n () => (config.showColumnTotals ? getValuesByColumn(data, columnFields, valueFields, {}) : null),\n [config.showColumnTotals, data, columnFields, valueFields]\n );\n\n // All fields present in the data, and those not yet placed in a zone.\n const allFields = useMemo(() => (data.length > 0 ? Object.keys(data[0]) : []), [data]);\n const availableFields = useMemo(() => {\n const used = new Set<string>([...rowFields, ...columnFields, ...valueFields.map((v) => v.field)]);\n return allFields.filter((f) => !used.has(f));\n }, [allFields, rowFields, columnFields, valueFields]);\n\n // Move a field from one zone to another and recompute + persist.\n const moveField = useCallback(\n (field: string, from: PivotZone, to: PivotZone) => {\n if (from === to) return;\n const next: PivotLayout = {\n rows: from === 'rows' ? rowFields.filter((f) => f !== field) : [...rowFields],\n columns: from === 'columns' ? columnFields.filter((f) => f !== field) : [...columnFields],\n values: from === 'values' ? valueFields.filter((v) => v.field !== field) : [...valueFields],\n };\n if (to === 'rows' && !next.rows.includes(field)) next.rows.push(field);\n else if (to === 'columns' && !next.columns.includes(field)) next.columns.push(field);\n else if (to === 'values' && !next.values.some((v) => v.field === field)) {\n next.values.push({ field, aggregation: 'sum' });\n }\n applyLayout(next);\n },\n [rowFields, columnFields, valueFields, applyLayout]\n );\n\n const changeAggregation = useCallback(\n (field: string, aggregation: ValueField['aggregation']) => {\n applyLayout({\n rows: [...rowFields],\n columns: [...columnFields],\n values: valueFields.map((v) => (v.field === field ? { ...v, aggregation } : v)),\n });\n },\n [rowFields, columnFields, valueFields, applyLayout]\n );\n\n // Format number based on config\n const formatValue = useCallback(\n (value: number): string => {\n if (isNaN(value)) return '-';\n\n switch (config.numberFormat) {\n case 'currency':\n return new Intl.NumberFormat('en-US', {\n style: 'currency',\n currency: 'USD',\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value);\n case 'percentage':\n return new Intl.NumberFormat('en-US', {\n style: 'percent',\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value / 100);\n default:\n return new Intl.NumberFormat('en-US', {\n minimumFractionDigits: config.decimalPlaces,\n maximumFractionDigits: config.decimalPlaces,\n }).format(value);\n }\n },\n [config.numberFormat, config.decimalPlaces]\n );\n\n // Toggle row expansion\n const toggleRow = useCallback((rowPath: string) => {\n setExpandedRows((prev) => {\n const next = new Set(prev);\n if (next.has(rowPath)) {\n next.delete(rowPath);\n } else {\n next.add(rowPath);\n }\n return next;\n });\n }, []);\n\n // Handle cell click for drilldown\n const handleCellClick = useCallback(\n (rowFilter: Record<string, string>, columnValue: string) => {\n if (onDrilldown) {\n onDrilldown({\n rowFilter,\n columnValue,\n type: 'pivot_cell_click',\n });\n }\n },\n [onDrilldown]\n );\n\n // Render row recursively\n const renderRows = useCallback(\n (node: PivotNode, rowFilter: Record<string, string>, parentPath: string, depth: number): JSX.Element[] => {\n const rows: JSX.Element[] = [];\n const hasChildren = node.children.size > 0;\n const isExpanded = expandedRows.has(parentPath);\n const rowField = rowFields[depth - 1];\n\n if (depth > 0) {\n const columnData = getValuesByColumn(data, columnFields, valueFields, rowFilter);\n\n rows.push(\n <tr key={parentPath} className=\"hover:bg-bg-sunken/50 transition-colors\">\n {/* Row header */}\n <td\n className=\"sticky left-0 bg-bg-surface border-r border-border-default px-3 py-2 font-medium text-sm\"\n style={{ paddingLeft: `${depth * 16 + 8}px` }}\n >\n <div className=\"flex items-center gap-1\">\n {config.expandable && hasChildren && (\n <button\n type=\"button\"\n onClick={() => toggleRow(parentPath)}\n className=\"p-0.5 hover:bg-bg-sunken rounded\"\n >\n {isExpanded ? (\n <ChevronDown className=\"w-4 h-4 text-text-secondary\" />\n ) : (\n <ChevronRight className=\"w-4 h-4 text-text-secondary\" />\n )}\n </button>\n )}\n {!hasChildren && config.expandable && <span className=\"w-5\" />}\n <span>{node.key}</span>\n </div>\n </td>\n\n {/* Value cells */}\n {columnValues.map((colValue) => {\n const cellValues = columnData.get(colValue) || {};\n return valueFields.map((vf) => (\n <td\n key={`${colValue}-${vf.field}`}\n className=\"px-3 py-2 text-sm text-right tabular-nums cursor-pointer hover:bg-action-primary-bg/10\"\n onClick={() => handleCellClick(rowFilter, colValue)}\n >\n {formatValue(cellValues[vf.field] || 0)}\n </td>\n ));\n })}\n\n {/* Row total */}\n {config.showRowTotals &&\n valueFields.map((vf) => (\n <td\n key={`total-${vf.field}`}\n className=\"px-3 py-2 text-sm text-right tabular-nums font-medium bg-bg-sunken/30\"\n >\n {formatValue(node.values[vf.field] || 0)}\n </td>\n ))}\n </tr>\n );\n }\n\n // Render children if expanded\n if (hasChildren && (isExpanded || depth === 0)) {\n const sortedKeys = Array.from(node.children.keys()).sort();\n for (const childKey of sortedKeys) {\n const childNode = node.children.get(childKey)!;\n const childPath = `${parentPath}/${childKey}`;\n const childFilter = rowField ? { ...rowFilter, [rowField]: childKey } : rowFilter;\n rows.push(...renderRows(childNode, childFilter, childPath, depth + 1));\n }\n }\n\n return rows;\n },\n [\n data,\n config,\n rowFields,\n columnFields,\n valueFields,\n columnValues,\n expandedRows,\n formatValue,\n handleCellClick,\n toggleRow,\n ]\n );\n\n const hasLayout = rowFields.length > 0 || valueFields.length > 0;\n\n // No data state\n if (data.length === 0) {\n return (\n <div className=\"flex flex-col items-center justify-center h-full p-8 text-text-secondary\">\n <Settings className=\"w-12 h-12 mb-4 opacity-50\" />\n <p className=\"text-sm\">No data available</p>\n <p className=\"text-xs mt-1\">Configure data source to populate pivot table</p>\n </div>\n );\n }\n\n return (\n <div className=\"flex flex-col h-full overflow-hidden\">\n {/* Drag-and-drop field shelf (edit mode) */}\n {isEditMode && (\n <PivotFieldShelf\n rowFields={rowFields}\n columnFields={columnFields}\n valueFields={valueFields}\n availableFields={availableFields}\n onMoveField={moveField}\n onChangeAggregation={changeAggregation}\n />\n )}\n\n {!hasLayout ? (\n <div className=\"flex flex-col items-center justify-center flex-1 p-8 text-text-secondary\">\n <Settings className=\"w-12 h-12 mb-4 opacity-50\" />\n <p className=\"text-sm\">Configure Pivot Table</p>\n <p className=\"text-xs mt-1\">\n {isEditMode\n ? 'Drag fields into the Rows and Values zones above'\n : 'Add row fields and value fields in widget settings'}\n </p>\n </div>\n ) : (\n <div className=\"flex-1 overflow-auto\">\n <table className=\"w-full border-collapse\">\n {/* Header */}\n <thead className=\"sticky top-0 z-10\">\n {/* Column headers row */}\n <tr className=\"bg-bg-sunken\">\n <th className=\"sticky left-0 bg-bg-sunken border-r border-b border-border-default px-3 py-2 text-left text-xs font-semibold uppercase tracking-wider text-text-secondary\">\n {rowFields.length > 0 ? rowFields.map((r) => r.replace(/_/g, ' ')).join(' / ') : 'Values'}\n </th>\n\n {/* Column value headers */}\n {columnValues.map((colValue) => (\n <th\n key={colValue}\n colSpan={valueFields.length || 1}\n className=\"border-b border-border-default px-3 py-2 text-center text-xs font-semibold uppercase tracking-wider text-text-secondary\"\n >\n {colValue}\n </th>\n ))}\n\n {/* Total header */}\n {config.showRowTotals && (\n <th\n colSpan={valueFields.length || 1}\n className=\"border-b border-border-default px-3 py-2 text-center text-xs font-semibold uppercase tracking-wider text-text-secondary bg-bg-sunken/50\"\n >\n Total\n </th>\n )}\n </tr>\n\n {/* Value field headers row (if multiple values) */}\n {valueFields.length > 1 && (\n <tr className=\"bg-bg-surface\">\n <th className=\"sticky left-0 bg-bg-surface border-r border-b border-border-default\" />\n {columnValues.map((colValue) =>\n valueFields.map((vf) => (\n <th\n key={`${colValue}-${vf.field}`}\n className=\"border-b border-border-default px-3 py-1 text-center text-xs text-text-secondary\"\n >\n {vf.label || vf.field.replace(/_/g, ' ')}\n </th>\n ))\n )}\n {config.showRowTotals &&\n valueFields.map((vf) => (\n <th\n key={`total-${vf.field}`}\n className=\"border-b border-border-default px-3 py-1 text-center text-xs text-text-secondary bg-bg-sunken/30\"\n >\n {vf.label || vf.field.replace(/_/g, ' ')}\n </th>\n ))}\n </tr>\n )}\n </thead>\n\n {/* Body */}\n <tbody className=\"divide-y divide-border-default\">{renderRows(pivotTree, {}, 'root', 0)}</tbody>\n\n {/* Footer with column totals */}\n {config.showColumnTotals && (\n <tfoot>\n <tr className=\"bg-bg-sunken/50 font-medium\">\n <td className=\"sticky left-0 bg-bg-sunken/50 border-r border-t border-border-default px-3 py-2 text-sm\">\n Total\n </td>\n {columnValues.map((colValue) => {\n const cellValues = footerColumnData?.get(colValue) || {};\n return valueFields.map((vf) => (\n <td\n key={`footer-${colValue}-${vf.field}`}\n className=\"border-t border-border-default px-3 py-2 text-sm text-right tabular-nums\"\n >\n {formatValue(cellValues[vf.field] || 0)}\n </td>\n ));\n })}\n {config.showRowTotals &&\n config.showGrandTotal &&\n valueFields.map((vf) => (\n <td\n key={`grand-${vf.field}`}\n className=\"border-t border-border-default px-3 py-2 text-sm text-right tabular-nums font-bold bg-bg-sunken\"\n >\n {formatValue(pivotTree.values[vf.field] || 0)}\n </td>\n ))}\n </tr>\n </tfoot>\n )}\n </table>\n </div>\n )}\n </div>\n );\n});\n\n// ============================================================================\n// Field shelf (drag-and-drop zones)\n// ============================================================================\n\ninterface PivotFieldShelfProps {\n rowFields: string[];\n columnFields: string[];\n valueFields: ValueField[];\n availableFields: string[];\n onMoveField: (field: string, from: PivotZone, to: PivotZone) => void;\n onChangeAggregation: (field: string, aggregation: ValueField['aggregation']) => void;\n}\n\nconst DND_MIME = 'application/x-pivot-field';\n\nconst PivotFieldShelf: FC<PivotFieldShelfProps> = ({\n rowFields,\n columnFields,\n valueFields,\n availableFields,\n onMoveField,\n onChangeAggregation,\n}) => {\n const [dragOverZone, setDragOverZone] = useState<PivotZone | null>(null);\n\n const handleDrop = (zone: PivotZone) => (e: React.DragEvent) => {\n e.preventDefault();\n // Isolate the shelf's internal DnD from the dashboard canvas drop zone. The\n // canvas onDragOver forces dropEffect='copy' (for palette widget drops); left\n // to bubble, that conflicts with the chips' effectAllowed='move' and the\n // browser rejects the drop entirely (drop event never fires).\n e.stopPropagation();\n setDragOverZone(null);\n const raw = e.dataTransfer.getData(DND_MIME) || e.dataTransfer.getData('text/plain');\n if (!raw) return;\n try {\n const { field, from } = JSON.parse(raw) as { field: string; from: PivotZone };\n if (field) onMoveField(field, from, zone);\n } catch {\n // Ignore malformed drag payloads.\n }\n };\n\n const chip = (field: string, from: PivotZone, vf?: ValueField) => (\n <span\n key={field}\n draggable\n onDragStart={(e) => {\n const payload = JSON.stringify({ field, from });\n e.dataTransfer.setData(DND_MIME, payload);\n e.dataTransfer.setData('text/plain', payload);\n e.dataTransfer.effectAllowed = 'move';\n }}\n className=\"inline-flex items-center gap-1 px-2 py-1 rounded-md bg-bg-surface border border-border-default text-xs text-text-primary cursor-grab\"\n >\n <GripVertical className=\"w-3 h-3 text-text-tertiary\" aria-hidden=\"true\" />\n <span>{(vf?.label || field).replace(/_/g, ' ')}</span>\n {vf && (\n <select\n value={vf.aggregation}\n onChange={(e) => onChangeAggregation(field, e.target.value as ValueField['aggregation'])}\n onClick={(e) => e.stopPropagation()}\n aria-label={`Aggregation for ${field}`}\n className=\"ml-1 bg-bg-sunken text-text-secondary rounded px-1 py-0.5 text-[10px] uppercase\"\n >\n {AGGREGATIONS.map((agg) => (\n <option key={agg} value={agg}>\n {agg}\n </option>\n ))}\n </select>\n )}\n </span>\n );\n\n const zone = (label: string, name: PivotZone, children: JSX.Element[] | JSX.Element) => (\n <div\n onDragOver={(e) => {\n e.preventDefault();\n // Stop bubbling to the canvas onDragOver, which would override\n // dropEffect to 'copy' and make the browser reject this 'move' drop.\n e.stopPropagation();\n e.dataTransfer.dropEffect = 'move';\n if (dragOverZone !== name) {\n setDragOverZone(name);\n }\n }}\n onDragLeave={() => setDragOverZone((z) => (z === name ? null : z))}\n onDrop={handleDrop(name)}\n className={`flex-1 min-w-[8rem] rounded-md border p-2 transition-colors ${\n dragOverZone === name\n ? 'border-action-primary-bg bg-action-primary-bg/5'\n : 'border-border-default bg-bg-sunken/30'\n }`}\n >\n <div className=\"text-[10px] font-semibold uppercase tracking-wider text-text-tertiary mb-1.5\">{label}</div>\n <div className=\"flex flex-wrap gap-1.5 min-h-[1.75rem]\">{children}</div>\n </div>\n );\n\n return (\n <div className=\"flex flex-wrap gap-2 p-2 border-b border-border-default bg-bg-surface\">\n {zone(\n 'Rows',\n 'rows',\n rowFields.map((f) => chip(f, 'rows'))\n )}\n {zone(\n 'Columns',\n 'columns',\n columnFields.map((f) => chip(f, 'columns'))\n )}\n {zone(\n 'Values',\n 'values',\n valueFields.map((vf) => chip(vf.field, 'values', vf))\n )}\n {zone(\n 'Available',\n 'available',\n availableFields.length > 0 ? (\n availableFields.map((f) => chip(f, 'available'))\n ) : (\n <span className=\"text-[11px] text-text-tertiary italic\">All fields in use</span>\n )\n )}\n </div>\n );\n};\n\nexport default PivotTableWidget;\n"],"mappings":"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"}
|