@flanksource/clicky-ui 0.2.7 → 0.2.9

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.
Files changed (145) hide show
  1. package/dist/components/Combobox.cjs +89 -49
  2. package/dist/components/Combobox.cjs.map +1 -1
  3. package/dist/components/Combobox.d.ts +3 -0
  4. package/dist/components/Combobox.d.ts.map +1 -1
  5. package/dist/components/Combobox.js +91 -51
  6. package/dist/components/Combobox.js.map +1 -1
  7. package/dist/components/FilterBar.cjs +42 -44
  8. package/dist/components/FilterBar.cjs.map +1 -1
  9. package/dist/components/FilterBar.d.ts +19 -0
  10. package/dist/components/FilterBar.d.ts.map +1 -1
  11. package/dist/components/FilterBar.js +43 -45
  12. package/dist/components/FilterBar.js.map +1 -1
  13. package/dist/components/JsonSchemaForm.cjs +35 -0
  14. package/dist/components/JsonSchemaForm.cjs.map +1 -0
  15. package/dist/components/JsonSchemaForm.d.ts +3 -0
  16. package/dist/components/JsonSchemaForm.d.ts.map +1 -0
  17. package/dist/components/JsonSchemaForm.js +35 -0
  18. package/dist/components/JsonSchemaForm.js.map +1 -0
  19. package/dist/components/json-schema-form-array.cjs +169 -0
  20. package/dist/components/json-schema-form-array.cjs.map +1 -0
  21. package/dist/components/json-schema-form-array.d.ts +8 -0
  22. package/dist/components/json-schema-form-array.d.ts.map +1 -0
  23. package/dist/components/json-schema-form-array.js +169 -0
  24. package/dist/components/json-schema-form-array.js.map +1 -0
  25. package/dist/components/json-schema-form-fields.cjs +365 -0
  26. package/dist/components/json-schema-form-fields.cjs.map +1 -0
  27. package/dist/components/json-schema-form-fields.d.ts +19 -0
  28. package/dist/components/json-schema-form-fields.d.ts.map +1 -0
  29. package/dist/components/json-schema-form-fields.js +365 -0
  30. package/dist/components/json-schema-form-fields.js.map +1 -0
  31. package/dist/components/json-schema-form-render.cjs +154 -0
  32. package/dist/components/json-schema-form-render.cjs.map +1 -0
  33. package/dist/components/json-schema-form-render.d.ts +40 -0
  34. package/dist/components/json-schema-form-render.d.ts.map +1 -0
  35. package/dist/components/json-schema-form-render.js +154 -0
  36. package/dist/components/json-schema-form-render.js.map +1 -0
  37. package/dist/components/json-schema-form-resolve.cjs +121 -0
  38. package/dist/components/json-schema-form-resolve.cjs.map +1 -0
  39. package/dist/components/json-schema-form-resolve.d.ts +19 -0
  40. package/dist/components/json-schema-form-resolve.d.ts.map +1 -0
  41. package/dist/components/json-schema-form-resolve.js +121 -0
  42. package/dist/components/json-schema-form-resolve.js.map +1 -0
  43. package/dist/components/json-schema-form-types.d.ts +108 -0
  44. package/dist/components/json-schema-form-types.d.ts.map +1 -0
  45. package/dist/components.cjs +7 -0
  46. package/dist/components.cjs.map +1 -1
  47. package/dist/components.d.ts +3 -0
  48. package/dist/components.d.ts.map +1 -1
  49. package/dist/components.js +8 -1
  50. package/dist/components.js.map +1 -1
  51. package/dist/data/Clicky.cjs +1 -1
  52. package/dist/data/Clicky.cjs.map +1 -1
  53. package/dist/data/Clicky.js +1 -1
  54. package/dist/data/Clicky.js.map +1 -1
  55. package/dist/data/DataTable.cjs +16 -15
  56. package/dist/data/DataTable.cjs.map +1 -1
  57. package/dist/data/DataTable.d.ts +2 -1
  58. package/dist/data/DataTable.d.ts.map +1 -1
  59. package/dist/data/DataTable.js +16 -15
  60. package/dist/data/DataTable.js.map +1 -1
  61. package/dist/data/Icon.cjs +11 -0
  62. package/dist/data/Icon.cjs.map +1 -1
  63. package/dist/data/Icon.d.ts +12 -1
  64. package/dist/data/Icon.d.ts.map +1 -1
  65. package/dist/data/Icon.js +12 -1
  66. package/dist/data/Icon.js.map +1 -1
  67. package/dist/data/LogDetails.cjs +277 -0
  68. package/dist/data/LogDetails.cjs.map +1 -0
  69. package/dist/data/LogDetails.d.ts +5 -0
  70. package/dist/data/LogDetails.d.ts.map +1 -0
  71. package/dist/data/LogDetails.js +277 -0
  72. package/dist/data/LogDetails.js.map +1 -0
  73. package/dist/data/LogsTable.cjs +6 -202
  74. package/dist/data/LogsTable.cjs.map +1 -1
  75. package/dist/data/LogsTable.d.ts.map +1 -1
  76. package/dist/data/LogsTable.js +4 -200
  77. package/dist/data/LogsTable.js.map +1 -1
  78. package/dist/data/MatrixTable.cjs +17 -9
  79. package/dist/data/MatrixTable.cjs.map +1 -1
  80. package/dist/data/MatrixTable.d.ts +3 -1
  81. package/dist/data/MatrixTable.d.ts.map +1 -1
  82. package/dist/data/MatrixTable.js +17 -9
  83. package/dist/data/MatrixTable.js.map +1 -1
  84. package/dist/data/Properties.cjs +2 -2
  85. package/dist/data/Properties.cjs.map +1 -1
  86. package/dist/data/Properties.js +2 -2
  87. package/dist/data/Properties.js.map +1 -1
  88. package/dist/data/cells/TagList.cjs +19 -3
  89. package/dist/data/cells/TagList.cjs.map +1 -1
  90. package/dist/data/cells/TagList.d.ts +11 -1
  91. package/dist/data/cells/TagList.d.ts.map +1 -1
  92. package/dist/data/cells/TagList.js +19 -3
  93. package/dist/data/cells/TagList.js.map +1 -1
  94. package/dist/data/cells/Timestamp.cjs +17 -2
  95. package/dist/data/cells/Timestamp.cjs.map +1 -1
  96. package/dist/data/cells/Timestamp.d.ts +2 -0
  97. package/dist/data/cells/Timestamp.d.ts.map +1 -1
  98. package/dist/data/cells/Timestamp.js +17 -2
  99. package/dist/data/cells/Timestamp.js.map +1 -1
  100. package/dist/data/log-utils.cjs +7 -0
  101. package/dist/data/log-utils.cjs.map +1 -0
  102. package/dist/data/log-utils.d.ts +2 -0
  103. package/dist/data/log-utils.d.ts.map +1 -0
  104. package/dist/data/log-utils.js +7 -0
  105. package/dist/data/log-utils.js.map +1 -0
  106. package/dist/data.cjs +3 -0
  107. package/dist/data.cjs.map +1 -1
  108. package/dist/data.d.ts +2 -2
  109. package/dist/data.d.ts.map +1 -1
  110. package/dist/data.js +5 -2
  111. package/dist/index.cjs +13 -0
  112. package/dist/index.cjs.map +1 -1
  113. package/dist/index.js +15 -2
  114. package/dist/index.js.map +1 -1
  115. package/dist/rpc/EntityExplorerApp.cjs +3 -2
  116. package/dist/rpc/EntityExplorerApp.cjs.map +1 -1
  117. package/dist/rpc/EntityExplorerApp.js +3 -2
  118. package/dist/rpc/EntityExplorerApp.js.map +1 -1
  119. package/dist/rpc/OperationActionDialog.cjs +1 -1
  120. package/dist/rpc/OperationActionDialog.cjs.map +1 -1
  121. package/dist/rpc/OperationActionDialog.js +1 -1
  122. package/dist/rpc/OperationActionDialog.js.map +1 -1
  123. package/dist/rpc/OperationCommandPage.cjs +21 -14
  124. package/dist/rpc/OperationCommandPage.cjs.map +1 -1
  125. package/dist/rpc/OperationCommandPage.d.ts.map +1 -1
  126. package/dist/rpc/OperationCommandPage.js +21 -14
  127. package/dist/rpc/OperationCommandPage.js.map +1 -1
  128. package/dist/rpc/apiClient.cjs +316 -0
  129. package/dist/rpc/apiClient.cjs.map +1 -0
  130. package/dist/rpc/apiClient.d.ts +44 -0
  131. package/dist/rpc/apiClient.d.ts.map +1 -0
  132. package/dist/rpc/apiClient.js +316 -0
  133. package/dist/rpc/apiClient.js.map +1 -0
  134. package/dist/rpc/useOperations.cjs.map +1 -1
  135. package/dist/rpc/useOperations.d.ts +2 -1
  136. package/dist/rpc/useOperations.d.ts.map +1 -1
  137. package/dist/rpc/useOperations.js.map +1 -1
  138. package/dist/rpc.cjs +3 -0
  139. package/dist/rpc.cjs.map +1 -1
  140. package/dist/rpc.d.ts +1 -0
  141. package/dist/rpc.d.ts.map +1 -1
  142. package/dist/rpc.js +3 -0
  143. package/dist/rpc.js.map +1 -1
  144. package/dist/styles.css +1 -1
  145. package/package.json +1 -1
@@ -1,13 +1,10 @@
1
- import { jsx, jsxs } from "react/jsx-runtime";
2
- import { useMemo, useState } from "react";
1
+ import { jsx } from "react/jsx-runtime";
2
+ import { useMemo } from "react";
3
3
  import { cn } from "../lib/utils.js";
4
4
  import { AnsiHtml } from "./AnsiHtml.js";
5
5
  import { DataTable } from "./DataTable.js";
6
- import { Properties } from "./Properties.js";
7
- import { formatPropertyLabel } from "./properties-utils.js";
8
- import { UiCopy } from "../icons/components/UiCopy.js";
9
- import { UiZoomIn } from "../icons/components/UiZoomIn.js";
10
- import { UiZoomOut } from "../icons/components/UiZoomOut.js";
6
+ import { renderLogDetails } from "./LogDetails.js";
7
+ import { asRecord } from "./log-utils.js";
11
8
  const ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\[[0-9;?]*[ -/]*[@-~]`);
12
9
  const DEFAULT_LOG_COLUMNS = [
13
10
  {
@@ -214,196 +211,6 @@ function buildTags({
214
211
  }
215
212
  return tags;
216
213
  }
217
- function renderLogDetails(row) {
218
- return /* @__PURE__ */ jsx(LogDetails, { row });
219
- }
220
- function LogDetails({ row }) {
221
- const [openPaths, setOpenPaths] = useState({});
222
- const setPathOpen = (path, open) => {
223
- setOpenPaths((current) => ({ ...current, [path]: open }));
224
- };
225
- const details = useMemo(() => buildProcessedLogDetails(row), [row]);
226
- const labelForPath = (path) => formatPropertyLabel(path.split(".").pop() ?? path);
227
- const prefixActions = [
228
- {
229
- id: "expand",
230
- icon: UiZoomIn,
231
- label: (key) => `Expand ${labelForPath(key)}`,
232
- visible: (_k, _v, item) => !!item.expandable,
233
- disabled: (_k, _v, item) => !!item.expanded,
234
- onClick: (_k, _v, item) => {
235
- var _a;
236
- return (_a = item.onToggle) == null ? void 0 : _a.call(item, true);
237
- }
238
- },
239
- {
240
- id: "collapse",
241
- icon: UiZoomOut,
242
- label: (key) => `Collapse ${labelForPath(key)}`,
243
- visible: (_k, _v, item) => !!item.expandable,
244
- disabled: (_k, _v, item) => !item.expanded,
245
- onClick: (_k, _v, item) => {
246
- var _a;
247
- return (_a = item.onToggle) == null ? void 0 : _a.call(item, false);
248
- }
249
- }
250
- ];
251
- const suffixActions = [
252
- {
253
- id: "copy",
254
- icon: UiCopy,
255
- label: (key) => `Copy ${labelForPath(key)}`,
256
- onClick: (_k, value) => copyLogDetailsValue(value)
257
- }
258
- ];
259
- const renderLabel = (key) => labelForPath(key);
260
- const renderValue = (_key, value, item) => item.expandable ? /* @__PURE__ */ jsx(LogValueSummary, { value }) : /* @__PURE__ */ jsx(LogScalarValue, { value });
261
- const toItems = (value, path, depth) => getValueEntries(value).map(([key, entryValue]) => {
262
- const entryPath = `${path}.${String(key)}`;
263
- const expandable = isExpandableValue(entryValue);
264
- const open = openPaths[entryPath] ?? depth < 1;
265
- return {
266
- key: entryPath,
267
- value: entryValue,
268
- expandable,
269
- expanded: open,
270
- onToggle: (next) => setPathOpen(entryPath, next),
271
- renderChildren: () => /* @__PURE__ */ jsx(
272
- Properties,
273
- {
274
- density: "compact",
275
- items: toItems(entryValue, entryPath, depth + 1),
276
- renderLabel,
277
- renderValue,
278
- prefixActions,
279
- suffixActions,
280
- className: "mt-density-1"
281
- }
282
- )
283
- };
284
- });
285
- return /* @__PURE__ */ jsx("div", { className: "space-y-density-3 text-xs", children: /* @__PURE__ */ jsx(
286
- Properties,
287
- {
288
- density: "compact",
289
- items: toItems(details, "details", 0),
290
- renderLabel,
291
- renderValue,
292
- prefixActions,
293
- suffixActions
294
- }
295
- ) });
296
- }
297
- function LogValueSummary({ value }) {
298
- if (Array.isArray(value)) {
299
- return /* @__PURE__ */ jsxs("span", { className: "font-mono text-muted-foreground", children: [
300
- "[",
301
- value.length,
302
- " items]"
303
- ] });
304
- }
305
- const record = asRecord(value);
306
- if (record) {
307
- const count = Object.keys(record).length;
308
- return /* @__PURE__ */ jsxs("span", { className: "font-mono text-muted-foreground", children: [
309
- "{ ",
310
- count,
311
- " properties",
312
- " }"
313
- ] });
314
- }
315
- return /* @__PURE__ */ jsx(LogScalarValue, { value });
316
- }
317
- function LogScalarValue({ value }) {
318
- if (value === null || value === void 0) {
319
- return /* @__PURE__ */ jsx("span", { className: "font-mono italic text-muted-foreground", children: "null" });
320
- }
321
- if (typeof value === "string") {
322
- return /* @__PURE__ */ jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground", children: value });
323
- }
324
- if (typeof value === "number" || typeof value === "boolean") {
325
- return /* @__PURE__ */ jsx("span", { className: "font-mono text-blue-700 dark:text-blue-400", children: String(value) });
326
- }
327
- return /* @__PURE__ */ jsx("pre", { className: "max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground", children: stablePrettyString(value) });
328
- }
329
- function getValueEntries(value) {
330
- if (Array.isArray(value)) {
331
- return value.map((entry, index) => [index, entry]);
332
- }
333
- return Object.entries(asRecord(value) ?? {});
334
- }
335
- function isExpandableValue(value) {
336
- return getValueEntries(value).length > 0;
337
- }
338
- function stablePrettyString(value) {
339
- if (typeof value === "string") return value;
340
- try {
341
- return JSON.stringify(value, null, 2);
342
- } catch {
343
- return String(value);
344
- }
345
- }
346
- function copyLogDetailsValue(value) {
347
- var _a;
348
- if (typeof navigator !== "undefined" && ((_a = navigator.clipboard) == null ? void 0 : _a.writeText)) {
349
- Promise.resolve(navigator.clipboard.writeText(stablePrettyString(value))).catch(
350
- () => void 0
351
- );
352
- }
353
- }
354
- function buildProcessedLogDetails(row) {
355
- const attributes = buildProcessedLogAttributes(row);
356
- return stripEmptyProperties({
357
- timestamp: row.timestamp,
358
- level: row.level,
359
- pod: row.pod,
360
- logger: row.logger,
361
- thread: row.thread,
362
- message: row.message,
363
- tags: row.tags,
364
- ...Object.keys(attributes).length > 0 ? { attributes } : {}
365
- });
366
- }
367
- function buildProcessedLogAttributes(row) {
368
- const attributes = {
369
- ...asRecord(row.raw),
370
- ...asRecord(row.parsedLine)
371
- };
372
- const hiddenKeys = /* @__PURE__ */ new Set([
373
- "@timestamp",
374
- "container",
375
- "event.dataset",
376
- "labels",
377
- "level",
378
- "line",
379
- "log.level",
380
- "log.logger",
381
- "logger",
382
- "message",
383
- "msg",
384
- "pod",
385
- "process.thread.name",
386
- "service",
387
- "service.name",
388
- "severity",
389
- "thread",
390
- "time",
391
- "timestamp"
392
- ]);
393
- return stripEmptyProperties(
394
- Object.fromEntries(Object.entries(attributes).filter(([key]) => !hiddenKeys.has(key)))
395
- );
396
- }
397
- function stripEmptyProperties(record) {
398
- return Object.fromEntries(
399
- Object.entries(record).filter(([, value]) => {
400
- if (value == null) return false;
401
- if (typeof value === "string") return value.trim().length > 0;
402
- if (Array.isArray(value)) return value.length > 0;
403
- return true;
404
- })
405
- );
406
- }
407
214
  function tryParseJson(value) {
408
215
  try {
409
216
  return JSON.parse(value);
@@ -411,9 +218,6 @@ function tryParseJson(value) {
411
218
  return JSON_PARSE_FAILED;
412
219
  }
413
220
  }
414
- function asRecord(value) {
415
- return value && typeof value === "object" && !Array.isArray(value) ? value : void 0;
416
- }
417
221
  function pick(record, path) {
418
222
  if (!record) return void 0;
419
223
  if (Object.prototype.hasOwnProperty.call(record, path)) return record[path];
@@ -1 +1 @@
1
- {"version":3,"file":"LogsTable.js","sources":["../../src/data/LogsTable.tsx"],"sourcesContent":["import { useMemo, useState, type ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { AnsiHtml } from \"./AnsiHtml\";\nimport { DataTable, type DataTableColumn, type DataTableProps } from \"./DataTable\";\nimport { Properties, type PropertiesAction, type PropertiesItem } from \"./Properties\";\nimport { formatPropertyLabel } from \"./properties-utils\";\nimport { UiCopy, UiZoomIn, UiZoomOut } from \"../icons\";\n\nexport type LogsTableInput = string | Record<string, unknown>;\n\nexport type LogsTableRow = {\n id: string;\n timestamp: string;\n level: string;\n pod: string;\n logger: string;\n thread: string;\n message: string;\n tags: string[];\n line: string;\n parsedLine?: unknown;\n raw: unknown;\n};\n\nexport type LogsTableProps = Omit<DataTableProps<LogsTableRow>, \"data\" | \"columns\"> & {\n /** Raw newline-delimited log text or pre-parsed log records. */\n logs: string | LogsTableInput[];\n /** Optional custom columns; defaults to timestamp, level, pod, logger, thread, message, and tags. */\n columns?: DataTableColumn<LogsTableRow>[];\n /** Render parsed/raw log details when a row is expanded. */\n showRawDetails?: boolean;\n};\n\n// Matches CSI / SGR escape sequences (e.g. \"\\x1b[31m\"). Used to skip the\n// AnsiHtml render for plain messages so the cell stays a fast text node.\nconst ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\\\[[0-9;?]*[ -/]*[@-~]`);\n\nconst DEFAULT_LOG_COLUMNS: DataTableColumn<LogsTableRow>[] = [\n {\n key: \"timestamp\",\n label: \"Timestamp\",\n kind: \"timestamp\",\n shrink: true,\n minWidth: 180,\n },\n {\n key: \"level\",\n label: \"Level\",\n kind: \"status\",\n shrink: true,\n minWidth: 96,\n status: { showLabel: true },\n },\n { key: \"pod\", label: \"Pod\", shrink: true, minWidth: 180 },\n { key: \"logger\", label: \"Logger\", shrink: true, minWidth: 220 },\n { key: \"thread\", label: \"Thread\", shrink: true, minWidth: 180 },\n {\n key: \"message\",\n label: \"Message\",\n grow: true,\n minWidth: 360,\n cellClassName: \"font-mono text-xs\",\n render: (value) => {\n const text = typeof value === \"string\" ? value : value == null ? \"\" : String(value);\n if (!ANSI_PATTERN.test(text)) return text;\n return <AnsiHtml as=\"span\" text={text} className=\"whitespace-pre-wrap break-words\" />;\n },\n },\n {\n key: \"tags\",\n label: \"Tags\",\n kind: \"tags\",\n grow: true,\n tags: { maxVisible: 4 },\n },\n];\n\nconst DEFAULT_SORT = {\n key: \"timestamp\",\n dir: \"desc\",\n} satisfies NonNullable<DataTableProps<LogsTableRow>[\"defaultSort\"]>;\nconst JSON_PARSE_FAILED = Symbol(\"json-parse-failed\");\n\nexport function LogsTable({\n logs,\n columns,\n showRawDetails = true,\n className,\n autoFilter = true,\n defaultSort,\n getRowId,\n renderExpandedRow,\n showDensityControl = true,\n showThemeControl = true,\n showFullscreenControl = true,\n fullscreenTitle = \"Logs\",\n fullscreenButtonLabel = \"Open logs full screen\",\n defaultDensity = \"compact\",\n ...tableProps\n}: LogsTableProps) {\n const rows = useMemo(() => normalizeLogsTableRows(logs), [logs]);\n const resolvedRenderExpandedRow =\n renderExpandedRow ?? (showRawDetails ? renderLogDetails : undefined);\n return (\n <DataTable\n {...tableProps}\n data={rows}\n columns={columns ?? DEFAULT_LOG_COLUMNS}\n className={cn(\"rounded-md bg-background p-2\", className)}\n autoFilter={autoFilter}\n defaultDensity={defaultDensity}\n defaultSort={defaultSort ?? DEFAULT_SORT}\n getRowId={getRowId ?? ((row) => row.id)}\n showDensityControl={showDensityControl}\n showThemeControl={showThemeControl}\n showFullscreenControl={showFullscreenControl}\n fullscreenTitle={fullscreenTitle}\n fullscreenButtonLabel={fullscreenButtonLabel}\n {...(resolvedRenderExpandedRow ? { renderExpandedRow: resolvedRenderExpandedRow } : {})}\n />\n );\n}\n\nexport function normalizeLogsTableRows(logs: string | LogsTableInput[]): LogsTableRow[] {\n const entries = typeof logs === \"string\" ? splitLogLines(logs) : logs;\n return entries.map((entry, index) => normalizeLogEntry(entry, index));\n}\n\nfunction splitLogLines(logs: string): string[] {\n return logs.split(/\\r?\\n/).filter((line) => line.length > 0);\n}\n\nfunction normalizeLogEntry(entry: LogsTableInput, index: number): LogsTableRow {\n const parsedOuterValue = typeof entry === \"string\" ? tryParseJson(entry) : entry;\n const parsedOuter = parsedOuterValue === JSON_PARSE_FAILED ? entry : parsedOuterValue;\n const outer = asRecord(parsedOuter);\n const outerLine = stringValue(outer?.line);\n const parsedLineValue = outerLine ? tryParseJson(outerLine) : JSON_PARSE_FAILED;\n const parsedLine = parsedLineValue === JSON_PARSE_FAILED ? undefined : parsedLineValue;\n const inner = asRecord(parsedLine) ?? (outerLine ? undefined : outer);\n const labels = asRecord(outer?.labels);\n\n const timestamp = firstString(\n pick(outer, \"timestamp\"),\n pick(inner, \"@timestamp\"),\n pick(outer, \"ts\"),\n pick(inner, \"timestamp\"),\n pick(outer, \"time\"),\n pick(inner, \"time\"),\n );\n const level = firstString(\n pick(inner, \"log.level\"),\n pick(inner, \"level\"),\n pick(outer, \"level\"),\n pick(outer, \"severity\"),\n pick(inner, \"severity\"),\n );\n const pod = firstString(\n pick(outer, \"pod\"),\n pick(labels, \"pod\"),\n pick(inner, \"kubernetes.pod.name\"),\n );\n const namespace = firstString(\n pick(outer, \"namespace\"),\n pick(labels, \"namespace\"),\n pick(inner, \"kubernetes.namespace\"),\n );\n const container = firstString(\n pick(outer, \"container\"),\n pick(labels, \"container\"),\n pick(inner, \"container.name\"),\n );\n const service = firstString(\n pick(inner, \"service.name\"),\n pick(inner, \"service\"),\n pick(outer, \"service\"),\n pick(labels, \"service\"),\n );\n const dataset = firstString(pick(inner, \"event.dataset\"), pick(inner, \"dataset\"));\n const logger = firstString(\n pick(inner, \"log.logger\"),\n pick(inner, \"logger\"),\n pick(outer, \"logger\"),\n );\n const thread = firstString(\n pick(inner, \"process.thread.name\"),\n pick(inner, \"thread\"),\n pick(outer, \"thread\"),\n );\n const message = firstString(\n pick(inner, \"message\"),\n pick(inner, \"msg\"),\n pick(outer, \"message\"),\n outerLine,\n typeof entry === \"string\" ? entry : undefined,\n );\n\n const tags = buildTags({\n namespace,\n container,\n service,\n dataset,\n ecsVersion: firstString(pick(inner, \"ecs.version\")),\n labels,\n });\n\n return {\n id: `${index}:${timestamp || pod || message || stableString(entry)}`,\n timestamp,\n level,\n pod,\n logger,\n thread,\n message,\n tags,\n line: outerLine ?? (typeof entry === \"string\" ? entry : stableString(entry)),\n raw: parsedOuter,\n ...(parsedLine !== undefined ? { parsedLine } : {}),\n };\n}\n\nfunction buildTags({\n namespace,\n container,\n service,\n dataset,\n ecsVersion,\n labels,\n}: {\n namespace: string;\n container: string;\n service: string;\n dataset: string;\n ecsVersion: string;\n labels: Record<string, unknown> | undefined;\n}) {\n const tags: string[] = [];\n const seen = new Set<string>();\n const addTag = (key: string, value: unknown) => {\n const valueString = stringValue(value);\n if (!valueString) return;\n const tag = `${key}=${valueString}`;\n if (seen.has(tag)) return;\n seen.add(tag);\n tags.push(tag);\n };\n\n addTag(\"namespace\", namespace);\n addTag(\"container\", container);\n addTag(\"service\", service);\n addTag(\"dataset\", dataset);\n addTag(\"ecs.version\", ecsVersion);\n\n if (labels) {\n for (const [key, value] of Object.entries(labels)) {\n addTag(key, value);\n }\n }\n\n return tags;\n}\n\nfunction renderLogDetails(row: LogsTableRow): ReactNode {\n return <LogDetails row={row} />;\n}\n\nfunction LogDetails({ row }: { row: LogsTableRow }) {\n const [openPaths, setOpenPaths] = useState<Record<string, boolean>>({});\n const setPathOpen = (path: string, open: boolean) => {\n setOpenPaths((current) => ({ ...current, [path]: open }));\n };\n const details = useMemo(() => buildProcessedLogDetails(row), [row]);\n\n const labelForPath = (path: string) => formatPropertyLabel(path.split(\".\").pop() ?? path);\n\n const prefixActions: PropertiesAction<unknown>[] = [\n {\n id: \"expand\",\n icon: UiZoomIn,\n label: (key) => `Expand ${labelForPath(key)}`,\n visible: (_k, _v, item) => !!item.expandable,\n disabled: (_k, _v, item) => !!item.expanded,\n onClick: (_k, _v, item) => item.onToggle?.(true),\n },\n {\n id: \"collapse\",\n icon: UiZoomOut,\n label: (key) => `Collapse ${labelForPath(key)}`,\n visible: (_k, _v, item) => !!item.expandable,\n disabled: (_k, _v, item) => !item.expanded,\n onClick: (_k, _v, item) => item.onToggle?.(false),\n },\n ];\n\n const suffixActions: PropertiesAction<unknown>[] = [\n {\n id: \"copy\",\n icon: UiCopy,\n label: (key) => `Copy ${labelForPath(key)}`,\n onClick: (_k, value) => copyLogDetailsValue(value),\n },\n ];\n\n const renderLabel = (key: string) => labelForPath(key);\n const renderValue = (_key: string, value: unknown, item: PropertiesItem<unknown>) =>\n item.expandable ? <LogValueSummary value={value} /> : <LogScalarValue value={value} />;\n\n const toItems = (value: unknown, path: string, depth: number): PropertiesItem<unknown>[] =>\n getValueEntries(value).map(([key, entryValue]) => {\n const entryPath = `${path}.${String(key)}`;\n const expandable = isExpandableValue(entryValue);\n const open = openPaths[entryPath] ?? depth < 1;\n return {\n key: entryPath,\n value: entryValue,\n expandable,\n expanded: open,\n onToggle: (next) => setPathOpen(entryPath, next),\n renderChildren: () => (\n <Properties\n density=\"compact\"\n items={toItems(entryValue, entryPath, depth + 1)}\n renderLabel={renderLabel}\n renderValue={renderValue}\n prefixActions={prefixActions}\n suffixActions={suffixActions}\n className=\"mt-density-1\"\n />\n ),\n };\n });\n\n return (\n <div className=\"space-y-density-3 text-xs\">\n <Properties\n density=\"compact\"\n items={toItems(details, \"details\", 0)}\n renderLabel={renderLabel}\n renderValue={renderValue}\n prefixActions={prefixActions}\n suffixActions={suffixActions}\n />\n </div>\n );\n}\n\nfunction LogValueSummary({ value }: { value: unknown }) {\n if (Array.isArray(value)) {\n return <span className=\"font-mono text-muted-foreground\">[{value.length} items]</span>;\n }\n const record = asRecord(value);\n if (record) {\n const count = Object.keys(record).length;\n return (\n <span className=\"font-mono text-muted-foreground\">\n {\"{ \"}\n {count} properties{\" }\"}\n </span>\n );\n }\n return <LogScalarValue value={value} />;\n}\n\nfunction LogScalarValue({ value }: { value: unknown }) {\n if (value === null || value === undefined) {\n return <span className=\"font-mono italic text-muted-foreground\">null</span>;\n }\n\n if (typeof value === \"string\") {\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground\">\n {value}\n </pre>\n );\n }\n\n if (typeof value === \"number\" || typeof value === \"boolean\") {\n return <span className=\"font-mono text-blue-700 dark:text-blue-400\">{String(value)}</span>;\n }\n\n return (\n <pre className=\"max-h-48 overflow-auto whitespace-pre-wrap break-words font-mono text-[11px] leading-relaxed text-foreground\">\n {stablePrettyString(value)}\n </pre>\n );\n}\n\nfunction getValueEntries(value: unknown): Array<[string | number, unknown]> {\n if (Array.isArray(value)) {\n return value.map((entry, index) => [index, entry]);\n }\n return Object.entries(asRecord(value) ?? {});\n}\n\nfunction isExpandableValue(value: unknown) {\n return getValueEntries(value).length > 0;\n}\n\nfunction stablePrettyString(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value, null, 2);\n } catch {\n return String(value);\n }\n}\n\nfunction copyLogDetailsValue(value: unknown) {\n if (typeof navigator !== \"undefined\" && navigator.clipboard?.writeText) {\n Promise.resolve(navigator.clipboard.writeText(stablePrettyString(value))).catch(\n () => undefined,\n );\n }\n}\n\nfunction buildProcessedLogDetails(row: LogsTableRow): Record<string, unknown> {\n const attributes = buildProcessedLogAttributes(row);\n return stripEmptyProperties({\n timestamp: row.timestamp,\n level: row.level,\n pod: row.pod,\n logger: row.logger,\n thread: row.thread,\n message: row.message,\n tags: row.tags,\n ...(Object.keys(attributes).length > 0 ? { attributes } : {}),\n });\n}\n\nfunction buildProcessedLogAttributes(row: LogsTableRow): Record<string, unknown> {\n const attributes = {\n ...asRecord(row.raw),\n ...asRecord(row.parsedLine),\n };\n const hiddenKeys = new Set([\n \"@timestamp\",\n \"container\",\n \"event.dataset\",\n \"labels\",\n \"level\",\n \"line\",\n \"log.level\",\n \"log.logger\",\n \"logger\",\n \"message\",\n \"msg\",\n \"pod\",\n \"process.thread.name\",\n \"service\",\n \"service.name\",\n \"severity\",\n \"thread\",\n \"time\",\n \"timestamp\",\n ]);\n\n return stripEmptyProperties(\n Object.fromEntries(Object.entries(attributes).filter(([key]) => !hiddenKeys.has(key))),\n );\n}\n\nfunction stripEmptyProperties(record: Record<string, unknown>): Record<string, unknown> {\n return Object.fromEntries(\n Object.entries(record).filter(([, value]) => {\n if (value == null) return false;\n if (typeof value === \"string\") return value.trim().length > 0;\n if (Array.isArray(value)) return value.length > 0;\n return true;\n }),\n );\n}\n\nfunction tryParseJson(value: string): unknown | typeof JSON_PARSE_FAILED {\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return JSON_PARSE_FAILED;\n }\n}\n\nfunction asRecord(value: unknown): Record<string, unknown> | undefined {\n return value && typeof value === \"object\" && !Array.isArray(value)\n ? (value as Record<string, unknown>)\n : undefined;\n}\n\nfunction pick(record: Record<string, unknown> | undefined, path: string): unknown {\n if (!record) return undefined;\n if (Object.prototype.hasOwnProperty.call(record, path)) return record[path];\n return path.split(\".\").reduce<unknown>((current, key) => {\n if (current && typeof current === \"object\") {\n return (current as Record<string, unknown>)[key];\n }\n return undefined;\n }, record);\n}\n\nfunction firstString(...values: unknown[]): string {\n for (const value of values) {\n const string = stringValue(value);\n if (string) return string;\n }\n return \"\";\n}\n\nfunction stringValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value.trim();\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n return \"\";\n}\n\nfunction stableString(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"LogsTable.js","sources":["../../src/data/LogsTable.tsx"],"sourcesContent":["import { useMemo } from \"react\";\nimport { cn } from \"../lib/utils\";\nimport { AnsiHtml } from \"./AnsiHtml\";\nimport { DataTable, type DataTableColumn, type DataTableProps } from \"./DataTable\";\nimport { renderLogDetails } from \"./LogDetails\";\nimport { asRecord } from \"./log-utils\";\n\nexport type LogsTableInput = string | Record<string, unknown>;\n\nexport type LogsTableRow = {\n id: string;\n timestamp: string;\n level: string;\n pod: string;\n logger: string;\n thread: string;\n message: string;\n tags: string[];\n line: string;\n parsedLine?: unknown;\n raw: unknown;\n};\n\nexport type LogsTableProps = Omit<DataTableProps<LogsTableRow>, \"data\" | \"columns\"> & {\n /** Raw newline-delimited log text or pre-parsed log records. */\n logs: string | LogsTableInput[];\n /** Optional custom columns; defaults to timestamp, level, pod, logger, thread, message, and tags. */\n columns?: DataTableColumn<LogsTableRow>[];\n /** Render parsed/raw log details when a row is expanded. */\n showRawDetails?: boolean;\n};\n\n// Matches CSI / SGR escape sequences (e.g. \"\\x1b[31m\"). Used to skip the\n// AnsiHtml render for plain messages so the cell stays a fast text node.\nconst ANSI_PATTERN = new RegExp(`${String.fromCharCode(27)}\\\\[[0-9;?]*[ -/]*[@-~]`);\n\nconst DEFAULT_LOG_COLUMNS: DataTableColumn<LogsTableRow>[] = [\n {\n key: \"timestamp\",\n label: \"Timestamp\",\n kind: \"timestamp\",\n shrink: true,\n minWidth: 180,\n },\n {\n key: \"level\",\n label: \"Level\",\n kind: \"status\",\n shrink: true,\n minWidth: 96,\n status: { showLabel: true },\n },\n { key: \"pod\", label: \"Pod\", shrink: true, minWidth: 180 },\n { key: \"logger\", label: \"Logger\", shrink: true, minWidth: 220 },\n { key: \"thread\", label: \"Thread\", shrink: true, minWidth: 180 },\n {\n key: \"message\",\n label: \"Message\",\n grow: true,\n minWidth: 360,\n cellClassName: \"font-mono text-xs\",\n render: (value) => {\n const text = typeof value === \"string\" ? value : value == null ? \"\" : String(value);\n if (!ANSI_PATTERN.test(text)) return text;\n return <AnsiHtml as=\"span\" text={text} className=\"whitespace-pre-wrap break-words\" />;\n },\n },\n {\n key: \"tags\",\n label: \"Tags\",\n kind: \"tags\",\n grow: true,\n tags: { maxVisible: 4 },\n },\n];\n\nconst DEFAULT_SORT = {\n key: \"timestamp\",\n dir: \"desc\",\n} satisfies NonNullable<DataTableProps<LogsTableRow>[\"defaultSort\"]>;\nconst JSON_PARSE_FAILED = Symbol(\"json-parse-failed\");\n\nexport function LogsTable({\n logs,\n columns,\n showRawDetails = true,\n className,\n autoFilter = true,\n defaultSort,\n getRowId,\n renderExpandedRow,\n showDensityControl = true,\n showThemeControl = true,\n showFullscreenControl = true,\n fullscreenTitle = \"Logs\",\n fullscreenButtonLabel = \"Open logs full screen\",\n defaultDensity = \"compact\",\n ...tableProps\n}: LogsTableProps) {\n const rows = useMemo(() => normalizeLogsTableRows(logs), [logs]);\n const resolvedRenderExpandedRow =\n renderExpandedRow ?? (showRawDetails ? renderLogDetails : undefined);\n return (\n <DataTable\n {...tableProps}\n data={rows}\n columns={columns ?? DEFAULT_LOG_COLUMNS}\n className={cn(\"rounded-md bg-background p-2\", className)}\n autoFilter={autoFilter}\n defaultDensity={defaultDensity}\n defaultSort={defaultSort ?? DEFAULT_SORT}\n getRowId={getRowId ?? ((row) => row.id)}\n showDensityControl={showDensityControl}\n showThemeControl={showThemeControl}\n showFullscreenControl={showFullscreenControl}\n fullscreenTitle={fullscreenTitle}\n fullscreenButtonLabel={fullscreenButtonLabel}\n {...(resolvedRenderExpandedRow ? { renderExpandedRow: resolvedRenderExpandedRow } : {})}\n />\n );\n}\n\nexport function normalizeLogsTableRows(logs: string | LogsTableInput[]): LogsTableRow[] {\n const entries = typeof logs === \"string\" ? splitLogLines(logs) : logs;\n return entries.map((entry, index) => normalizeLogEntry(entry, index));\n}\n\nfunction splitLogLines(logs: string): string[] {\n return logs.split(/\\r?\\n/).filter((line) => line.length > 0);\n}\n\nfunction normalizeLogEntry(entry: LogsTableInput, index: number): LogsTableRow {\n const parsedOuterValue = typeof entry === \"string\" ? tryParseJson(entry) : entry;\n const parsedOuter = parsedOuterValue === JSON_PARSE_FAILED ? entry : parsedOuterValue;\n const outer = asRecord(parsedOuter);\n const outerLine = stringValue(outer?.line);\n const parsedLineValue = outerLine ? tryParseJson(outerLine) : JSON_PARSE_FAILED;\n const parsedLine = parsedLineValue === JSON_PARSE_FAILED ? undefined : parsedLineValue;\n const inner = asRecord(parsedLine) ?? (outerLine ? undefined : outer);\n const labels = asRecord(outer?.labels);\n\n const timestamp = firstString(\n pick(outer, \"timestamp\"),\n pick(inner, \"@timestamp\"),\n pick(outer, \"ts\"),\n pick(inner, \"timestamp\"),\n pick(outer, \"time\"),\n pick(inner, \"time\"),\n );\n const level = firstString(\n pick(inner, \"log.level\"),\n pick(inner, \"level\"),\n pick(outer, \"level\"),\n pick(outer, \"severity\"),\n pick(inner, \"severity\"),\n );\n const pod = firstString(\n pick(outer, \"pod\"),\n pick(labels, \"pod\"),\n pick(inner, \"kubernetes.pod.name\"),\n );\n const namespace = firstString(\n pick(outer, \"namespace\"),\n pick(labels, \"namespace\"),\n pick(inner, \"kubernetes.namespace\"),\n );\n const container = firstString(\n pick(outer, \"container\"),\n pick(labels, \"container\"),\n pick(inner, \"container.name\"),\n );\n const service = firstString(\n pick(inner, \"service.name\"),\n pick(inner, \"service\"),\n pick(outer, \"service\"),\n pick(labels, \"service\"),\n );\n const dataset = firstString(pick(inner, \"event.dataset\"), pick(inner, \"dataset\"));\n const logger = firstString(\n pick(inner, \"log.logger\"),\n pick(inner, \"logger\"),\n pick(outer, \"logger\"),\n );\n const thread = firstString(\n pick(inner, \"process.thread.name\"),\n pick(inner, \"thread\"),\n pick(outer, \"thread\"),\n );\n const message = firstString(\n pick(inner, \"message\"),\n pick(inner, \"msg\"),\n pick(outer, \"message\"),\n outerLine,\n typeof entry === \"string\" ? entry : undefined,\n );\n\n const tags = buildTags({\n namespace,\n container,\n service,\n dataset,\n ecsVersion: firstString(pick(inner, \"ecs.version\")),\n labels,\n });\n\n return {\n id: `${index}:${timestamp || pod || message || stableString(entry)}`,\n timestamp,\n level,\n pod,\n logger,\n thread,\n message,\n tags,\n line: outerLine ?? (typeof entry === \"string\" ? entry : stableString(entry)),\n raw: parsedOuter,\n ...(parsedLine !== undefined ? { parsedLine } : {}),\n };\n}\n\nfunction buildTags({\n namespace,\n container,\n service,\n dataset,\n ecsVersion,\n labels,\n}: {\n namespace: string;\n container: string;\n service: string;\n dataset: string;\n ecsVersion: string;\n labels: Record<string, unknown> | undefined;\n}) {\n const tags: string[] = [];\n const seen = new Set<string>();\n const addTag = (key: string, value: unknown) => {\n const valueString = stringValue(value);\n if (!valueString) return;\n const tag = `${key}=${valueString}`;\n if (seen.has(tag)) return;\n seen.add(tag);\n tags.push(tag);\n };\n\n addTag(\"namespace\", namespace);\n addTag(\"container\", container);\n addTag(\"service\", service);\n addTag(\"dataset\", dataset);\n addTag(\"ecs.version\", ecsVersion);\n\n if (labels) {\n for (const [key, value] of Object.entries(labels)) {\n addTag(key, value);\n }\n }\n\n return tags;\n}\n\nfunction tryParseJson(value: string): unknown | typeof JSON_PARSE_FAILED {\n try {\n return JSON.parse(value) as unknown;\n } catch {\n return JSON_PARSE_FAILED;\n }\n}\n\nfunction pick(record: Record<string, unknown> | undefined, path: string): unknown {\n if (!record) return undefined;\n if (Object.prototype.hasOwnProperty.call(record, path)) return record[path];\n return path.split(\".\").reduce<unknown>((current, key) => {\n if (current && typeof current === \"object\") {\n return (current as Record<string, unknown>)[key];\n }\n return undefined;\n }, record);\n}\n\nfunction firstString(...values: unknown[]): string {\n for (const value of values) {\n const string = stringValue(value);\n if (string) return string;\n }\n return \"\";\n}\n\nfunction stringValue(value: unknown): string {\n if (value == null) return \"\";\n if (typeof value === \"string\") return value.trim();\n if (typeof value === \"number\" || typeof value === \"boolean\") return String(value);\n return \"\";\n}\n\nfunction stableString(value: unknown): string {\n if (typeof value === \"string\") return value;\n try {\n return JSON.stringify(value);\n } catch {\n return String(value);\n }\n}\n"],"names":[],"mappings":"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"}
@@ -11,10 +11,12 @@ function MatrixTable({
11
11
  density = "default",
12
12
  columnWidth = 48,
13
13
  headerHeight = 120,
14
+ maxHeight,
14
15
  className,
15
16
  columnClassName,
16
17
  rowLabelClassName,
17
- cellClassName
18
+ cellClassName,
19
+ headerClassName
18
20
  }) {
19
21
  if (rows.length === 0 || columns.length === 0) {
20
22
  return /* @__PURE__ */ jsxRuntime.jsx("div", { className: "text-sm text-muted-foreground", children: emptyMessage });
@@ -29,21 +31,25 @@ function MatrixTable({
29
31
  "div",
30
32
  {
31
33
  className: utils.cn("overflow-auto rounded-md border border-border", className),
32
- style: angledHeaders ? { paddingRight: diagonalOverhang } : void 0,
34
+ style: {
35
+ ...angledHeaders ? { paddingRight: diagonalOverhang } : void 0,
36
+ ...maxHeight !== void 0 ? { maxHeight } : void 0
37
+ },
33
38
  children: /* @__PURE__ */ jsxRuntime.jsxs("table", { className: utils.cn("w-max border-collapse", compact ? "text-xs" : "text-sm"), children: [
34
39
  angledHeaders && /* @__PURE__ */ jsxRuntime.jsxs("colgroup", { children: [
35
40
  /* @__PURE__ */ jsxRuntime.jsx("col", {}),
36
41
  columns.map((_, index) => /* @__PURE__ */ jsxRuntime.jsx("col", { style: { width: columnWidth } }, index))
37
42
  ] }),
38
- /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: utils.cn("bg-muted/40", !angledHeaders && "border-b border-border"), children: [
43
+ /* @__PURE__ */ jsxRuntime.jsx("thead", { children: /* @__PURE__ */ jsxRuntime.jsxs("tr", { className: utils.cn("bg-muted/40", !angledHeaders && "border-b border-border", headerClassName), children: [
39
44
  /* @__PURE__ */ jsxRuntime.jsx(
40
45
  "th",
41
46
  {
42
47
  scope: "col",
43
48
  className: utils.cn(
44
- "sticky left-0 z-10 min-w-44 border-r border-border bg-muted/40 text-left font-medium",
49
+ "sticky left-0 top-0 z-30 min-w-44 border-r border-border bg-muted text-left font-medium",
45
50
  headerCellPadding,
46
- angledHeaders ? "align-bottom" : columnClassName
51
+ angledHeaders ? "align-bottom" : utils.cn("border-b border-border", columnClassName),
52
+ headerClassName
47
53
  ),
48
54
  children: corner
49
55
  }
@@ -55,8 +61,9 @@ function MatrixTable({
55
61
  scope: "col",
56
62
  title: columnTitle(column),
57
63
  className: utils.cn(
58
- "relative overflow-visible border-b border-border p-0 align-bottom",
59
- columnClassName
64
+ "sticky top-0 z-20 overflow-visible border-b border-border bg-muted p-0 align-bottom",
65
+ columnClassName,
66
+ headerClassName
60
67
  ),
61
68
  style: {
62
69
  width: columnWidth,
@@ -110,9 +117,10 @@ function MatrixTable({
110
117
  {
111
118
  scope: "col",
112
119
  className: utils.cn(
113
- "min-w-24 whitespace-nowrap text-center text-xs font-medium text-muted-foreground",
120
+ "sticky top-0 z-20 min-w-24 whitespace-nowrap border-b border-border bg-muted text-center text-xs font-medium text-muted-foreground",
114
121
  headerCellPadding,
115
- columnClassName
122
+ columnClassName,
123
+ headerClassName
116
124
  ),
117
125
  children: column
118
126
  },
@@ -1 +1 @@
1
- {"version":3,"file":"MatrixTable.cjs","sources":["../../src/data/MatrixTable.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type MatrixTableRow = {\n key: string;\n label: ReactNode;\n cells: ReactNode[];\n};\n\nexport type MatrixTableProps = {\n columns: ReactNode[];\n rows: MatrixTableRow[];\n corner?: ReactNode;\n emptyMessage?: ReactNode;\n angledHeaders?: boolean;\n density?: \"default\" | \"compact\";\n columnWidth?: number;\n headerHeight?: number;\n className?: string;\n columnClassName?: string;\n rowLabelClassName?: string;\n cellClassName?: string;\n};\n\nexport function MatrixTable({\n columns,\n rows,\n corner,\n emptyMessage = \"No data\",\n angledHeaders = false,\n density = \"default\",\n columnWidth = 48,\n headerHeight = 120,\n className,\n columnClassName,\n rowLabelClassName,\n cellClassName,\n}: MatrixTableProps) {\n if (rows.length === 0 || columns.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const textWidth = Math.round(headerHeight * 1.41);\n const diagonalOverhang = Math.round(headerHeight * 0.71);\n const compact = density === \"compact\";\n const headerCellPadding = compact ? \"px-density-2 py-density-1\" : \"px-density-3 py-density-2\";\n const rowLabelPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n const bodyCellPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n\n return (\n <div\n className={cn(\"overflow-auto rounded-md border border-border\", className)}\n style={angledHeaders ? { paddingRight: diagonalOverhang } : undefined}\n >\n <table className={cn(\"w-max border-collapse\", compact ? \"text-xs\" : \"text-sm\")}>\n {angledHeaders && (\n <colgroup>\n <col />\n {columns.map((_, index) => (\n <col key={index} style={{ width: columnWidth }} />\n ))}\n </colgroup>\n )}\n <thead>\n <tr className={cn(\"bg-muted/40\", !angledHeaders && \"border-b border-border\")}>\n <th\n scope=\"col\"\n className={cn(\n \"sticky left-0 z-10 min-w-44 border-r border-border bg-muted/40 text-left font-medium\",\n headerCellPadding,\n angledHeaders ? \"align-bottom\" : columnClassName,\n )}\n >\n {corner}\n </th>\n {columns.map((column, index) =>\n angledHeaders ? (\n <th\n key={index}\n scope=\"col\"\n title={columnTitle(column)}\n className={cn(\n \"relative overflow-visible border-b border-border p-0 align-bottom\",\n columnClassName,\n )}\n style={{\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n height: headerHeight,\n }}\n >\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: columnWidth,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n {index === 0 && (\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: 0,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n )}\n <div\n className=\"absolute bottom-2 overflow-hidden text-ellipsis whitespace-nowrap pl-1 text-left text-xs font-medium leading-none text-muted-foreground\"\n style={{\n left: columnWidth / 2,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n >\n {column}\n </div>\n </th>\n ) : (\n <th\n key={index}\n scope=\"col\"\n className={cn(\n \"min-w-24 whitespace-nowrap text-center text-xs font-medium text-muted-foreground\",\n headerCellPadding,\n columnClassName,\n )}\n >\n {column}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.key} className=\"border-b border-border last:border-b-0\">\n <th\n scope=\"row\"\n className={cn(\n \"sticky left-0 z-10 border-r border-border bg-background text-left font-medium\",\n rowLabelPadding,\n rowLabelClassName,\n )}\n >\n {row.label}\n </th>\n {columns.map((_, index) => (\n <td\n key={index}\n className={cn(\n angledHeaders\n ? \"p-0 text-center align-middle\"\n : cn(bodyCellPadding, \"text-center align-middle\"),\n cellClassName,\n )}\n style={\n angledHeaders\n ? {\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n }\n : undefined\n }\n >\n {row.cells[index] ?? null}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction columnTitle(column: ReactNode) {\n if (typeof column === \"string\" || typeof column === \"number\") {\n return String(column);\n }\n return undefined;\n}\n"],"names":["jsx","cn","jsxs"],"mappings":"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"}
1
+ {"version":3,"file":"MatrixTable.cjs","sources":["../../src/data/MatrixTable.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type MatrixTableRow = {\n key: string;\n label: ReactNode;\n cells: ReactNode[];\n};\n\nexport type MatrixTableProps = {\n columns: ReactNode[];\n rows: MatrixTableRow[];\n corner?: ReactNode;\n emptyMessage?: ReactNode;\n angledHeaders?: boolean;\n density?: \"default\" | \"compact\";\n columnWidth?: number;\n headerHeight?: number;\n // maxHeight caps the scroll viewport so the body scrolls under frozen\n // headers (Excel-style freeze panes). Accepts any CSS length; number => px.\n maxHeight?: number | string;\n className?: string;\n columnClassName?: string;\n rowLabelClassName?: string;\n cellClassName?: string;\n // headerClassName is appended to the header row and every header cell (corner\n // + columns). Use it to override the default gray header background, e.g.\n // \"bg-transparent\", since tailwind-merge lets the later class win.\n headerClassName?: string;\n};\n\nexport function MatrixTable({\n columns,\n rows,\n corner,\n emptyMessage = \"No data\",\n angledHeaders = false,\n density = \"default\",\n columnWidth = 48,\n headerHeight = 120,\n maxHeight,\n className,\n columnClassName,\n rowLabelClassName,\n cellClassName,\n headerClassName,\n}: MatrixTableProps) {\n if (rows.length === 0 || columns.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const textWidth = Math.round(headerHeight * 1.41);\n const diagonalOverhang = Math.round(headerHeight * 0.71);\n const compact = density === \"compact\";\n const headerCellPadding = compact ? \"px-density-2 py-density-1\" : \"px-density-3 py-density-2\";\n const rowLabelPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n const bodyCellPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n\n return (\n <div\n className={cn(\"overflow-auto rounded-md border border-border\", className)}\n style={{\n ...(angledHeaders ? { paddingRight: diagonalOverhang } : undefined),\n ...(maxHeight !== undefined ? { maxHeight } : undefined),\n }}\n >\n <table className={cn(\"w-max border-collapse\", compact ? \"text-xs\" : \"text-sm\")}>\n {angledHeaders && (\n <colgroup>\n <col />\n {columns.map((_, index) => (\n <col key={index} style={{ width: columnWidth }} />\n ))}\n </colgroup>\n )}\n <thead>\n <tr className={cn(\"bg-muted/40\", !angledHeaders && \"border-b border-border\", headerClassName)}>\n <th\n scope=\"col\"\n className={cn(\n \"sticky left-0 top-0 z-30 min-w-44 border-r border-border bg-muted text-left font-medium\",\n headerCellPadding,\n angledHeaders ? \"align-bottom\" : cn(\"border-b border-border\", columnClassName),\n headerClassName,\n )}\n >\n {corner}\n </th>\n {columns.map((column, index) =>\n angledHeaders ? (\n <th\n key={index}\n scope=\"col\"\n title={columnTitle(column)}\n className={cn(\n \"sticky top-0 z-20 overflow-visible border-b border-border bg-muted p-0 align-bottom\",\n columnClassName,\n headerClassName,\n )}\n style={{\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n height: headerHeight,\n }}\n >\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: columnWidth,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n {index === 0 && (\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: 0,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n )}\n <div\n className=\"absolute bottom-2 overflow-hidden text-ellipsis whitespace-nowrap pl-1 text-left text-xs font-medium leading-none text-muted-foreground\"\n style={{\n left: columnWidth / 2,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n >\n {column}\n </div>\n </th>\n ) : (\n <th\n key={index}\n scope=\"col\"\n className={cn(\n \"sticky top-0 z-20 min-w-24 whitespace-nowrap border-b border-border bg-muted text-center text-xs font-medium text-muted-foreground\",\n headerCellPadding,\n columnClassName,\n headerClassName,\n )}\n >\n {column}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.key} className=\"border-b border-border last:border-b-0\">\n <th\n scope=\"row\"\n className={cn(\n \"sticky left-0 z-10 border-r border-border bg-background text-left font-medium\",\n rowLabelPadding,\n rowLabelClassName,\n )}\n >\n {row.label}\n </th>\n {columns.map((_, index) => (\n <td\n key={index}\n className={cn(\n angledHeaders\n ? \"p-0 text-center align-middle\"\n : cn(bodyCellPadding, \"text-center align-middle\"),\n cellClassName,\n )}\n style={\n angledHeaders\n ? {\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n }\n : undefined\n }\n >\n {row.cells[index] ?? null}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction columnTitle(column: ReactNode) {\n if (typeof column === \"string\" || typeof column === \"number\") {\n return String(column);\n }\n return undefined;\n}\n"],"names":["jsx","cn","jsxs"],"mappings":"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"}
@@ -13,10 +13,12 @@ export type MatrixTableProps = {
13
13
  density?: "default" | "compact";
14
14
  columnWidth?: number;
15
15
  headerHeight?: number;
16
+ maxHeight?: number | string;
16
17
  className?: string;
17
18
  columnClassName?: string;
18
19
  rowLabelClassName?: string;
19
20
  cellClassName?: string;
21
+ headerClassName?: string;
20
22
  };
21
- export declare function MatrixTable({ columns, rows, corner, emptyMessage, angledHeaders, density, columnWidth, headerHeight, className, columnClassName, rowLabelClassName, cellClassName, }: MatrixTableProps): import("react/jsx-runtime").JSX.Element;
23
+ export declare function MatrixTable({ columns, rows, corner, emptyMessage, angledHeaders, density, columnWidth, headerHeight, maxHeight, className, columnClassName, rowLabelClassName, cellClassName, headerClassName, }: MatrixTableProps): import("react/jsx-runtime").JSX.Element;
22
24
  //# sourceMappingURL=MatrixTable.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"MatrixTable.d.ts","sourceRoot":"","sources":["../../src/data/MatrixTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IACtB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;CACxB,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,IAAI,EACJ,MAAM,EACN,YAAwB,EACxB,aAAqB,EACrB,OAAmB,EACnB,WAAgB,EAChB,YAAkB,EAClB,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,aAAa,GACd,EAAE,gBAAgB,2CAgJlB"}
1
+ {"version":3,"file":"MatrixTable.d.ts","sourceRoot":"","sources":["../../src/data/MatrixTable.tsx"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,OAAO,CAAC;AAGvC,MAAM,MAAM,cAAc,GAAG;IAC3B,GAAG,EAAE,MAAM,CAAC;IACZ,KAAK,EAAE,SAAS,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,MAAM,MAAM,gBAAgB,GAAG;IAC7B,OAAO,EAAE,SAAS,EAAE,CAAC;IACrB,IAAI,EAAE,cAAc,EAAE,CAAC;IACvB,MAAM,CAAC,EAAE,SAAS,CAAC;IACnB,YAAY,CAAC,EAAE,SAAS,CAAC;IACzB,aAAa,CAAC,EAAE,OAAO,CAAC;IACxB,OAAO,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;IAChC,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,YAAY,CAAC,EAAE,MAAM,CAAC;IAGtB,SAAS,CAAC,EAAE,MAAM,GAAG,MAAM,CAAC;IAC5B,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,eAAe,CAAC,EAAE,MAAM,CAAC;IACzB,iBAAiB,CAAC,EAAE,MAAM,CAAC;IAC3B,aAAa,CAAC,EAAE,MAAM,CAAC;IAIvB,eAAe,CAAC,EAAE,MAAM,CAAC;CAC1B,CAAC;AAEF,wBAAgB,WAAW,CAAC,EAC1B,OAAO,EACP,IAAI,EACJ,MAAM,EACN,YAAwB,EACxB,aAAqB,EACrB,OAAmB,EACnB,WAAgB,EAChB,YAAkB,EAClB,SAAS,EACT,SAAS,EACT,eAAe,EACf,iBAAiB,EACjB,aAAa,EACb,eAAe,GAChB,EAAE,gBAAgB,2CAsJlB"}
@@ -9,10 +9,12 @@ function MatrixTable({
9
9
  density = "default",
10
10
  columnWidth = 48,
11
11
  headerHeight = 120,
12
+ maxHeight,
12
13
  className,
13
14
  columnClassName,
14
15
  rowLabelClassName,
15
- cellClassName
16
+ cellClassName,
17
+ headerClassName
16
18
  }) {
17
19
  if (rows.length === 0 || columns.length === 0) {
18
20
  return /* @__PURE__ */ jsx("div", { className: "text-sm text-muted-foreground", children: emptyMessage });
@@ -27,21 +29,25 @@ function MatrixTable({
27
29
  "div",
28
30
  {
29
31
  className: cn("overflow-auto rounded-md border border-border", className),
30
- style: angledHeaders ? { paddingRight: diagonalOverhang } : void 0,
32
+ style: {
33
+ ...angledHeaders ? { paddingRight: diagonalOverhang } : void 0,
34
+ ...maxHeight !== void 0 ? { maxHeight } : void 0
35
+ },
31
36
  children: /* @__PURE__ */ jsxs("table", { className: cn("w-max border-collapse", compact ? "text-xs" : "text-sm"), children: [
32
37
  angledHeaders && /* @__PURE__ */ jsxs("colgroup", { children: [
33
38
  /* @__PURE__ */ jsx("col", {}),
34
39
  columns.map((_, index) => /* @__PURE__ */ jsx("col", { style: { width: columnWidth } }, index))
35
40
  ] }),
36
- /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: cn("bg-muted/40", !angledHeaders && "border-b border-border"), children: [
41
+ /* @__PURE__ */ jsx("thead", { children: /* @__PURE__ */ jsxs("tr", { className: cn("bg-muted/40", !angledHeaders && "border-b border-border", headerClassName), children: [
37
42
  /* @__PURE__ */ jsx(
38
43
  "th",
39
44
  {
40
45
  scope: "col",
41
46
  className: cn(
42
- "sticky left-0 z-10 min-w-44 border-r border-border bg-muted/40 text-left font-medium",
47
+ "sticky left-0 top-0 z-30 min-w-44 border-r border-border bg-muted text-left font-medium",
43
48
  headerCellPadding,
44
- angledHeaders ? "align-bottom" : columnClassName
49
+ angledHeaders ? "align-bottom" : cn("border-b border-border", columnClassName),
50
+ headerClassName
45
51
  ),
46
52
  children: corner
47
53
  }
@@ -53,8 +59,9 @@ function MatrixTable({
53
59
  scope: "col",
54
60
  title: columnTitle(column),
55
61
  className: cn(
56
- "relative overflow-visible border-b border-border p-0 align-bottom",
57
- columnClassName
62
+ "sticky top-0 z-20 overflow-visible border-b border-border bg-muted p-0 align-bottom",
63
+ columnClassName,
64
+ headerClassName
58
65
  ),
59
66
  style: {
60
67
  width: columnWidth,
@@ -108,9 +115,10 @@ function MatrixTable({
108
115
  {
109
116
  scope: "col",
110
117
  className: cn(
111
- "min-w-24 whitespace-nowrap text-center text-xs font-medium text-muted-foreground",
118
+ "sticky top-0 z-20 min-w-24 whitespace-nowrap border-b border-border bg-muted text-center text-xs font-medium text-muted-foreground",
112
119
  headerCellPadding,
113
- columnClassName
120
+ columnClassName,
121
+ headerClassName
114
122
  ),
115
123
  children: column
116
124
  },
@@ -1 +1 @@
1
- {"version":3,"file":"MatrixTable.js","sources":["../../src/data/MatrixTable.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type MatrixTableRow = {\n key: string;\n label: ReactNode;\n cells: ReactNode[];\n};\n\nexport type MatrixTableProps = {\n columns: ReactNode[];\n rows: MatrixTableRow[];\n corner?: ReactNode;\n emptyMessage?: ReactNode;\n angledHeaders?: boolean;\n density?: \"default\" | \"compact\";\n columnWidth?: number;\n headerHeight?: number;\n className?: string;\n columnClassName?: string;\n rowLabelClassName?: string;\n cellClassName?: string;\n};\n\nexport function MatrixTable({\n columns,\n rows,\n corner,\n emptyMessage = \"No data\",\n angledHeaders = false,\n density = \"default\",\n columnWidth = 48,\n headerHeight = 120,\n className,\n columnClassName,\n rowLabelClassName,\n cellClassName,\n}: MatrixTableProps) {\n if (rows.length === 0 || columns.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const textWidth = Math.round(headerHeight * 1.41);\n const diagonalOverhang = Math.round(headerHeight * 0.71);\n const compact = density === \"compact\";\n const headerCellPadding = compact ? \"px-density-2 py-density-1\" : \"px-density-3 py-density-2\";\n const rowLabelPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n const bodyCellPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n\n return (\n <div\n className={cn(\"overflow-auto rounded-md border border-border\", className)}\n style={angledHeaders ? { paddingRight: diagonalOverhang } : undefined}\n >\n <table className={cn(\"w-max border-collapse\", compact ? \"text-xs\" : \"text-sm\")}>\n {angledHeaders && (\n <colgroup>\n <col />\n {columns.map((_, index) => (\n <col key={index} style={{ width: columnWidth }} />\n ))}\n </colgroup>\n )}\n <thead>\n <tr className={cn(\"bg-muted/40\", !angledHeaders && \"border-b border-border\")}>\n <th\n scope=\"col\"\n className={cn(\n \"sticky left-0 z-10 min-w-44 border-r border-border bg-muted/40 text-left font-medium\",\n headerCellPadding,\n angledHeaders ? \"align-bottom\" : columnClassName,\n )}\n >\n {corner}\n </th>\n {columns.map((column, index) =>\n angledHeaders ? (\n <th\n key={index}\n scope=\"col\"\n title={columnTitle(column)}\n className={cn(\n \"relative overflow-visible border-b border-border p-0 align-bottom\",\n columnClassName,\n )}\n style={{\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n height: headerHeight,\n }}\n >\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: columnWidth,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n {index === 0 && (\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: 0,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n )}\n <div\n className=\"absolute bottom-2 overflow-hidden text-ellipsis whitespace-nowrap pl-1 text-left text-xs font-medium leading-none text-muted-foreground\"\n style={{\n left: columnWidth / 2,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n >\n {column}\n </div>\n </th>\n ) : (\n <th\n key={index}\n scope=\"col\"\n className={cn(\n \"min-w-24 whitespace-nowrap text-center text-xs font-medium text-muted-foreground\",\n headerCellPadding,\n columnClassName,\n )}\n >\n {column}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.key} className=\"border-b border-border last:border-b-0\">\n <th\n scope=\"row\"\n className={cn(\n \"sticky left-0 z-10 border-r border-border bg-background text-left font-medium\",\n rowLabelPadding,\n rowLabelClassName,\n )}\n >\n {row.label}\n </th>\n {columns.map((_, index) => (\n <td\n key={index}\n className={cn(\n angledHeaders\n ? \"p-0 text-center align-middle\"\n : cn(bodyCellPadding, \"text-center align-middle\"),\n cellClassName,\n )}\n style={\n angledHeaders\n ? {\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n }\n : undefined\n }\n >\n {row.cells[index] ?? null}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction columnTitle(column: ReactNode) {\n if (typeof column === \"string\" || typeof column === \"number\") {\n return String(column);\n }\n return undefined;\n}\n"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"MatrixTable.js","sources":["../../src/data/MatrixTable.tsx"],"sourcesContent":["import type { ReactNode } from \"react\";\nimport { cn } from \"../lib/utils\";\n\nexport type MatrixTableRow = {\n key: string;\n label: ReactNode;\n cells: ReactNode[];\n};\n\nexport type MatrixTableProps = {\n columns: ReactNode[];\n rows: MatrixTableRow[];\n corner?: ReactNode;\n emptyMessage?: ReactNode;\n angledHeaders?: boolean;\n density?: \"default\" | \"compact\";\n columnWidth?: number;\n headerHeight?: number;\n // maxHeight caps the scroll viewport so the body scrolls under frozen\n // headers (Excel-style freeze panes). Accepts any CSS length; number => px.\n maxHeight?: number | string;\n className?: string;\n columnClassName?: string;\n rowLabelClassName?: string;\n cellClassName?: string;\n // headerClassName is appended to the header row and every header cell (corner\n // + columns). Use it to override the default gray header background, e.g.\n // \"bg-transparent\", since tailwind-merge lets the later class win.\n headerClassName?: string;\n};\n\nexport function MatrixTable({\n columns,\n rows,\n corner,\n emptyMessage = \"No data\",\n angledHeaders = false,\n density = \"default\",\n columnWidth = 48,\n headerHeight = 120,\n maxHeight,\n className,\n columnClassName,\n rowLabelClassName,\n cellClassName,\n headerClassName,\n}: MatrixTableProps) {\n if (rows.length === 0 || columns.length === 0) {\n return <div className=\"text-sm text-muted-foreground\">{emptyMessage}</div>;\n }\n\n const textWidth = Math.round(headerHeight * 1.41);\n const diagonalOverhang = Math.round(headerHeight * 0.71);\n const compact = density === \"compact\";\n const headerCellPadding = compact ? \"px-density-2 py-density-1\" : \"px-density-3 py-density-2\";\n const rowLabelPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n const bodyCellPadding = compact ? \"px-density-2 py-1\" : \"px-density-3 py-density-2\";\n\n return (\n <div\n className={cn(\"overflow-auto rounded-md border border-border\", className)}\n style={{\n ...(angledHeaders ? { paddingRight: diagonalOverhang } : undefined),\n ...(maxHeight !== undefined ? { maxHeight } : undefined),\n }}\n >\n <table className={cn(\"w-max border-collapse\", compact ? \"text-xs\" : \"text-sm\")}>\n {angledHeaders && (\n <colgroup>\n <col />\n {columns.map((_, index) => (\n <col key={index} style={{ width: columnWidth }} />\n ))}\n </colgroup>\n )}\n <thead>\n <tr className={cn(\"bg-muted/40\", !angledHeaders && \"border-b border-border\", headerClassName)}>\n <th\n scope=\"col\"\n className={cn(\n \"sticky left-0 top-0 z-30 min-w-44 border-r border-border bg-muted text-left font-medium\",\n headerCellPadding,\n angledHeaders ? \"align-bottom\" : cn(\"border-b border-border\", columnClassName),\n headerClassName,\n )}\n >\n {corner}\n </th>\n {columns.map((column, index) =>\n angledHeaders ? (\n <th\n key={index}\n scope=\"col\"\n title={columnTitle(column)}\n className={cn(\n \"sticky top-0 z-20 overflow-visible border-b border-border bg-muted p-0 align-bottom\",\n columnClassName,\n headerClassName,\n )}\n style={{\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n height: headerHeight,\n }}\n >\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: columnWidth,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n {index === 0 && (\n <div\n className=\"absolute bottom-0 border-b border-border\"\n style={{\n left: 0,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n />\n )}\n <div\n className=\"absolute bottom-2 overflow-hidden text-ellipsis whitespace-nowrap pl-1 text-left text-xs font-medium leading-none text-muted-foreground\"\n style={{\n left: columnWidth / 2,\n width: textWidth,\n transform: \"rotate(-45deg)\",\n transformOrigin: \"0 100%\",\n }}\n >\n {column}\n </div>\n </th>\n ) : (\n <th\n key={index}\n scope=\"col\"\n className={cn(\n \"sticky top-0 z-20 min-w-24 whitespace-nowrap border-b border-border bg-muted text-center text-xs font-medium text-muted-foreground\",\n headerCellPadding,\n columnClassName,\n headerClassName,\n )}\n >\n {column}\n </th>\n ),\n )}\n </tr>\n </thead>\n <tbody>\n {rows.map((row) => (\n <tr key={row.key} className=\"border-b border-border last:border-b-0\">\n <th\n scope=\"row\"\n className={cn(\n \"sticky left-0 z-10 border-r border-border bg-background text-left font-medium\",\n rowLabelPadding,\n rowLabelClassName,\n )}\n >\n {row.label}\n </th>\n {columns.map((_, index) => (\n <td\n key={index}\n className={cn(\n angledHeaders\n ? \"p-0 text-center align-middle\"\n : cn(bodyCellPadding, \"text-center align-middle\"),\n cellClassName,\n )}\n style={\n angledHeaders\n ? {\n width: columnWidth,\n minWidth: columnWidth,\n maxWidth: columnWidth,\n }\n : undefined\n }\n >\n {row.cells[index] ?? null}\n </td>\n ))}\n </tr>\n ))}\n </tbody>\n </table>\n </div>\n );\n}\n\nfunction columnTitle(column: ReactNode) {\n if (typeof column === \"string\" || typeof column === \"number\") {\n return String(column);\n }\n return undefined;\n}\n"],"names":[],"mappings":"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"}
@@ -70,7 +70,7 @@ function Properties({
70
70
  /* @__PURE__ */ jsxRuntime.jsxs("dd", { className: utils.cn("min-w-0 space-y-density-1", valueClassName), children: [
71
71
  /* @__PURE__ */ jsxRuntime.jsxs("div", { className: "flex min-w-0 items-start gap-density-1", children: [
72
72
  /* @__PURE__ */ jsxRuntime.jsx(ActionList, { actions: prefixActions, item, className: "shrink-0 pt-0.5" }),
73
- /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("min-w-0 max-w-full flex-1", styles.value), children: renderValue ? renderValue(item.key, item.value, item) : defaultRenderValue(item.value) }),
73
+ /* @__PURE__ */ jsxRuntime.jsx("div", { className: utils.cn("min-w-0 max-w-full", styles.value), children: renderValue ? renderValue(item.key, item.value, item) : defaultRenderValue(item.value) }),
74
74
  /* @__PURE__ */ jsxRuntime.jsx(ActionList, { actions: suffixActions, item, className: "shrink-0 pt-0.5" })
75
75
  ] }),
76
76
  item.expandable && item.expanded && item.renderChildren ? item.renderChildren() : null
@@ -93,7 +93,7 @@ function ActionList({
93
93
  (action) => !action.visible || action.visible(item.key, item.value, item)
94
94
  );
95
95
  if (rendered.length === 0) return null;
96
- return /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("inline-flex items-center gap-0.5", className), children: rendered.map((action) => /* @__PURE__ */ jsxRuntime.jsx(PropertiesActionButton, { action, item }, action.id)) });
96
+ return /* @__PURE__ */ jsxRuntime.jsx("span", { className: utils.cn("inline-flex items-center", className), children: rendered.map((action) => /* @__PURE__ */ jsxRuntime.jsx(PropertiesActionButton, { action, item }, action.id)) });
97
97
  }
98
98
  function PropertiesActionButton({
99
99
  action,