@squadbase/vantage 0.6.2-dev1.01caa72 → 0.6.2-dev1.02aab78
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{build-X5NVCQKT.js → build-YOPOGPQB.js} +7 -7
- package/dist/{build-X5NVCQKT.js.map → build-YOPOGPQB.js.map} +1 -1
- package/dist/{check-ZBIH422T.js → check-FBHT6W6J.js} +5 -5
- package/dist/{check-ZBIH422T.js.map → check-FBHT6W6J.js.map} +1 -1
- package/dist/chunk-2IAPIOPS.js +152 -0
- package/dist/chunk-2IAPIOPS.js.map +1 -0
- package/dist/chunk-57GHR5LN.js +163 -0
- package/dist/chunk-57GHR5LN.js.map +1 -0
- package/dist/{chunk-7DZGJQ3P.js → chunk-5C2SEDPH.js} +21 -4
- package/dist/chunk-5C2SEDPH.js.map +1 -0
- package/dist/chunk-6OTTGAPO.js +44 -0
- package/dist/chunk-6OTTGAPO.js.map +1 -0
- package/dist/{chunk-2ICNHUG6.js → chunk-7652CGJJ.js} +10 -3
- package/dist/{chunk-2ICNHUG6.js.map → chunk-7652CGJJ.js.map} +1 -1
- package/dist/chunk-JXFCSF3K.js +673 -0
- package/dist/chunk-JXFCSF3K.js.map +1 -0
- package/dist/{chunk-IUPSBD6W.js → chunk-QOXXOFGL.js} +23 -886
- package/dist/chunk-QOXXOFGL.js.map +1 -0
- package/dist/{chunk-WAMZWZJB.js → chunk-R6MTJAHX.js} +6 -6
- package/dist/chunk-R6MTJAHX.js.map +1 -0
- package/dist/{chunk-CEXAQQ5W.js → chunk-S5MDTHH7.js} +180 -18
- package/dist/chunk-S5MDTHH7.js.map +1 -0
- package/dist/{chunk-JKLLFVEU.js → chunk-UWSQUHX6.js} +4 -99
- package/dist/chunk-UWSQUHX6.js.map +1 -0
- package/dist/cli.js +6 -6
- package/dist/components/index.d.ts +11 -247
- package/dist/components/index.js +4 -1
- package/dist/delta-text-CvwBGJ95.d.ts +15 -0
- package/dist/{dev-4XEDTELV.js → dev-GUKQT6U6.js} +7 -7
- package/dist/{dev-4XEDTELV.js.map → dev-GUKQT6U6.js.map} +1 -1
- package/dist/doc/index.d.ts +215 -2
- package/dist/doc/index.js +307 -3
- package/dist/doc/index.js.map +1 -1
- package/dist/{doctor-5E2VEZPK.js → doctor-B2O3GJDU.js} +5 -5
- package/dist/{doctor-5E2VEZPK.js.map → doctor-B2O3GJDU.js.map} +1 -1
- package/dist/format-U0cQl069.d.ts +243 -0
- package/dist/{routes-3SGAFDMC.js → routes-PVKNT777.js} +4 -4
- package/dist/{routes-3SGAFDMC.js.map → routes-PVKNT777.js.map} +1 -1
- package/dist/slide/index.d.ts +272 -1
- package/dist/slide/index.js +972 -2
- package/dist/slide/index.js.map +1 -1
- package/dist/table/index.js +4 -1
- package/dist/table/index.js.map +1 -1
- package/dist/{upgrade-JHXZBGQS.js → upgrade-7ZXMCTTR.js} +4 -4
- package/dist/{upgrade-JHXZBGQS.js.map → upgrade-7ZXMCTTR.js.map} +1 -1
- package/dist/vite/index.js +3 -3
- package/docs/en/changelog.md +2 -0
- package/docs/en/components.md +13 -2
- package/docs/en/doc/delta-text.md +40 -0
- package/docs/en/doc/doc-action-items.md +49 -0
- package/docs/en/doc/doc-callout.md +38 -0
- package/docs/en/doc/doc-data-table.md +60 -0
- package/docs/en/doc/doc-executive-summary.md +64 -0
- package/docs/en/doc/doc-figure.md +56 -0
- package/docs/en/doc/doc-key-findings.md +61 -0
- package/docs/en/doc/doc-kpi-scorecard.md +80 -0
- package/docs/en/doc/doc-notes-and-sources.md +43 -0
- package/docs/en/doc/doc-report-header.md +46 -0
- package/docs/en/pages-and-metadata.md +15 -0
- package/docs/en/parts/area-chart.md +99 -0
- package/docs/en/parts/bar-chart.md +91 -0
- package/docs/en/parts/bar-list.md +50 -0
- package/docs/en/parts/category-bar.md +37 -0
- package/docs/en/parts/chart-frame.md +39 -0
- package/docs/en/parts/delta.md +4 -3
- package/docs/en/parts/donut-chart.md +72 -0
- package/docs/en/parts/funnel-steps.md +2 -1
- package/docs/en/parts/heatmap.md +53 -0
- package/docs/en/parts/kpi-card.md +2 -1
- package/docs/en/parts/legend.md +35 -0
- package/docs/en/parts/line-chart.md +94 -0
- package/docs/en/parts/progress-circle.md +33 -0
- package/docs/en/parts/sample-data.md +39 -0
- package/docs/en/parts/scatter-chart.md +56 -0
- package/docs/en/parts/tracker.md +36 -0
- package/docs/en/parts/updated-at.md +30 -0
- package/docs/en/parts/waterfall-chart.md +56 -0
- package/docs/en/slide/slide-chart-full.md +48 -0
- package/docs/en/slide/slide-chart-insight.md +52 -0
- package/docs/en/slide/slide-columns.md +46 -0
- package/docs/en/slide/slide-comparison.md +68 -0
- package/docs/en/slide/slide-cover.md +39 -0
- package/docs/en/slide/slide-exec-summary.md +47 -0
- package/docs/en/slide/slide-heading.md +38 -0
- package/docs/en/slide/slide-kpi-grid.md +55 -0
- package/docs/en/slide/slide-kpi-hero.md +58 -0
- package/docs/en/slide/slide-next-actions.md +49 -0
- package/docs/en/slide/slide-scorecard-table.md +54 -0
- package/docs/en/slide/slide-section.md +44 -0
- package/docs/en/slide/slide-source-line.md +34 -0
- package/docs/en/ui/alert-dialog.md +42 -0
- package/docs/en/ui/avatar.md +31 -0
- package/docs/en/ui/combobox.md +74 -0
- package/docs/en/ui/drawer.md +41 -0
- package/docs/en/ui/empty.md +1 -1
- package/docs/en/ui/error-state.md +4 -3
- package/docs/en/ui/field.md +42 -0
- package/docs/en/ui/hover-card.md +21 -0
- package/docs/en/ui/kbd.md +17 -0
- package/docs/en/ui/loading.md +1 -1
- package/docs/en/ui/pagination.md +36 -0
- package/docs/en/ui/radio-group.md +21 -0
- package/docs/en/ui/toast.md +44 -0
- package/docs/en/ui/toggle-group.md +4 -3
- package/docs/index.json +857 -77
- package/docs/ja/changelog.md +2 -0
- package/docs/ja/components.md +12 -1
- package/docs/ja/doc/delta-text.md +40 -0
- package/docs/ja/doc/doc-action-items.md +49 -0
- package/docs/ja/doc/doc-callout.md +38 -0
- package/docs/ja/doc/doc-data-table.md +60 -0
- package/docs/ja/doc/doc-executive-summary.md +64 -0
- package/docs/ja/doc/doc-figure.md +56 -0
- package/docs/ja/doc/doc-key-findings.md +61 -0
- package/docs/ja/doc/doc-kpi-scorecard.md +80 -0
- package/docs/ja/doc/doc-notes-and-sources.md +43 -0
- package/docs/ja/doc/doc-report-header.md +46 -0
- package/docs/ja/page-types/slide.md +1 -0
- package/docs/ja/pages-and-metadata.md +15 -0
- package/docs/ja/parts/area-chart.md +49 -25
- package/docs/ja/parts/bar-chart.md +50 -24
- package/docs/ja/parts/bar-list.md +31 -8
- package/docs/ja/parts/chart-frame.md +36 -0
- package/docs/ja/parts/delta.md +5 -4
- package/docs/ja/parts/donut-chart.md +45 -26
- package/docs/ja/parts/funnel-steps.md +27 -8
- package/docs/ja/parts/heatmap.md +45 -0
- package/docs/ja/parts/kpi-card.md +2 -1
- package/docs/ja/parts/legend.md +36 -0
- package/docs/ja/parts/line-chart.md +48 -22
- package/docs/ja/parts/sample-data.md +61 -0
- package/docs/ja/parts/scatter-chart.md +69 -0
- package/docs/ja/parts/updated-at.md +27 -0
- package/docs/ja/parts/waterfall-chart.md +61 -0
- package/docs/ja/slide/slide-chart-full.md +48 -0
- package/docs/ja/slide/slide-chart-insight.md +52 -0
- package/docs/ja/slide/slide-columns.md +46 -0
- package/docs/ja/slide/slide-comparison.md +68 -0
- package/docs/ja/slide/slide-cover.md +39 -0
- package/docs/ja/slide/slide-exec-summary.md +47 -0
- package/docs/ja/slide/slide-heading.md +38 -0
- package/docs/ja/slide/slide-kpi-grid.md +55 -0
- package/docs/ja/slide/slide-kpi-hero.md +58 -0
- package/docs/ja/slide/slide-next-actions.md +49 -0
- package/docs/ja/slide/slide-scorecard-table.md +54 -0
- package/docs/ja/slide/slide-section.md +44 -0
- package/docs/ja/slide/slide-source-line.md +34 -0
- package/docs/ja/ui/empty.md +1 -1
- package/docs/ja/ui/error-state.md +4 -3
- package/docs/ja/ui/loading.md +1 -1
- package/docs/ja/ui/toggle-group.md +4 -3
- package/package.json +2 -2
- package/registry/ui/delta.tsx +7 -4
- package/registry/ui/echart.tsx +123 -1
- package/registry/ui/kpi-card.tsx +9 -4
- package/skills/vantage-pitfalls/SKILL.md +5 -0
- package/skills/vantage-slide/SKILL.md +54 -36
- package/templates/AGENTS.md +4 -2
- package/templates/agents/doc.md +48 -0
- package/templates/agents/page.md +22 -1
- package/templates/agents/slide.md +70 -2
- package/theme.css +10 -7
- package/dist/chunk-7DZGJQ3P.js.map +0 -1
- package/dist/chunk-CEXAQQ5W.js.map +0 -1
- package/dist/chunk-IUPSBD6W.js.map +0 -1
- package/dist/chunk-JKLLFVEU.js.map +0 -1
- package/dist/chunk-WAMZWZJB.js.map +0 -1
package/dist/table/index.js
CHANGED
|
@@ -2,7 +2,10 @@ import { FILE_PAGE_MAX_ROWS, FILE_PAGE_MAX_BYTES } from '../chunk-C2CX6GLX.js';
|
|
|
2
2
|
import { useQuery } from '../chunk-V3LF7BE7.js';
|
|
3
3
|
import '../chunk-XQUSGIDG.js';
|
|
4
4
|
import { Loading, ErrorState } from '../chunk-2N72FKLB.js';
|
|
5
|
-
import { PageShell, PageShellHeader, PageShellHeading, PageShellTitle, PageShellMeta, PageShellActions, PageShellContent } from '../chunk-
|
|
5
|
+
import { PageShell, PageShellHeader, PageShellHeading, PageShellTitle, PageShellMeta, PageShellActions, PageShellContent } from '../chunk-QOXXOFGL.js';
|
|
6
|
+
import '../chunk-JXFCSF3K.js';
|
|
7
|
+
import '../chunk-2IAPIOPS.js';
|
|
8
|
+
import '../chunk-57GHR5LN.js';
|
|
6
9
|
import '../chunk-7S52M2SL.js';
|
|
7
10
|
import { Button } from '../chunk-PDOBHMUQ.js';
|
|
8
11
|
import { cn } from '../chunk-U736PVHY.js';
|
package/dist/table/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../../src/table/infer.ts","../../src/table/parse.ts","../../src/table/sheet-grid.tsx","../../src/table/table-viewer.tsx","../../src/table/file-page-view.tsx"],"names":["useState","useLayoutEffect","useRef","jsxs","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Turning raw rows into a readable table: which row is the header, what each\n * column holds, and how a cell should read. Deliberately shallow — the goal is\n * \"legible as a spreadsheet\", not a reimplementation of one. Formulas, charts\n * and cell formatting are dropped; their values are what survive.\n */\nimport { FILE_PAGE_MAX_ROWS } from \"../core/constants\"\nimport type { CellFormat, TableColumn, TableSheet } from \"./types\"\n\n/** Rows sampled when deciding a column's format. */\nconst TYPE_SAMPLE_ROWS = 500\n\n/** Share of non-empty sampled values that must agree before a type is claimed. */\nconst TYPE_CONFIDENCE = 0.9\n\nfunction isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === \"\"\n}\n\n/**\n * `YYYY-MM-DD`, optionally with a time. XLSX hands back real `Date`s, but CSV\n * carries dates as text — recognising the ISO shape is what lets a date column\n * from a CSV align (and sort) like one. The string itself is left alone; only\n * the alignment follows from this.\n */\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2})?)?$/\n\nfunction classify(value: unknown): CellFormat {\n if (value instanceof Date) return \"date\"\n if (typeof value === \"number\" && Number.isFinite(value)) return \"number\"\n if (typeof value === \"string\" && ISO_DATE.test(value)) return \"date\"\n return \"text\"\n}\n\n/**\n * Header labels are kept exactly as the file wrote them — blank stays blank,\n * and two columns titled \"Amount\" stay two columns titled \"Amount\". The viewer\n * draws this row as the file's row 1, so anything invented here (`Column 3`,\n * `Amount (2)`) would be text the reader cannot find in their own file.\n * Uniqueness lives in `key`, which is positional.\n */\nfunction toColumns(header: unknown[], width: number, body: unknown[][]): TableColumn[] {\n const columns: TableColumn[] = []\n\n for (let index = 0; index < width; index++) {\n const raw = header[index]\n const label = isEmpty(raw) ? \"\" : String(raw).trim()\n\n let numbers = 0\n let dates = 0\n let filled = 0\n for (let row = 0; row < Math.min(body.length, TYPE_SAMPLE_ROWS); row++) {\n const cell = body[row]?.[index]\n if (isEmpty(cell)) continue\n filled++\n const kind = classify(cell)\n if (kind === \"number\") numbers++\n else if (kind === \"date\") dates++\n }\n let format: CellFormat = \"text\"\n if (filled > 0) {\n if (numbers / filled >= TYPE_CONFIDENCE) format = \"number\"\n else if (dates / filled >= TYPE_CONFIDENCE) format = \"date\"\n }\n\n // The key is positional rather than derived from the label: two columns\n // titled \"Amount\" must stay distinguishable, and a label can be any text.\n // Nothing in the viewer reads it — it is there for callers rendering their\n // own table from `toSheet`.\n columns.push({ key: `c${index}`, label, format })\n }\n return columns\n}\n\n/**\n * Build a sheet from raw rows. The first row is the header — the near-universal\n * spreadsheet convention, and guessing otherwise (scoring rows for\n * \"headerness\") misfires on data that happens to start with text.\n */\nexport function toSheet(name: string, rows: unknown[][]): TableSheet {\n if (rows.length === 0) {\n return { name, columns: [], rows: [], totalRows: 0, truncated: false }\n }\n const header = rows[0] ?? []\n const body = rows.slice(1)\n const width = rows.reduce((max, row) => Math.max(max, row.length), 0)\n const truncated = body.length > FILE_PAGE_MAX_ROWS\n const kept = truncated ? body.slice(0, FILE_PAGE_MAX_ROWS) : body\n return {\n name,\n columns: toColumns(header, width, kept),\n // Pad short rows so every row has a value at every column index; ragged\n // rows are common in hand-edited CSV and would otherwise read as holes.\n rows: kept.map((row) => Array.from({ length: width }, (_, i) => row[i] ?? null)),\n totalRows: body.length,\n truncated,\n }\n}\n\nconst numberFormat = new Intl.NumberFormat()\n\n/**\n * XLSX dates have no time zone: read-excel-file turns the serial number into\n * a `Date` whose UTC fields hold the wall-clock value the file shows\n * (2026-06-02 → 2026-06-02T00:00:00Z). Formatting in the browser's zone would\n * move it — a day early west of UTC, an evening time into the next day east\n * of it — so it is always read in UTC. The raw `Date` is left as is, so\n * comparing two of them still orders them as the file does.\n */\nconst WALL_CLOCK = { timeZone: \"UTC\" } as const\n\n/**\n * Cell text. The raw value stays in the row so sorting compares numbers as\n * numbers and dates as dates; only the display passes through here.\n */\nexport function formatCell(value: unknown, format: CellFormat): string {\n if (isEmpty(value)) return \"\"\n // A date that arrived as text is shown exactly as the file wrote it: the\n // point of the viewer is to read the file, not to restate it.\n if (format === \"date\" && typeof value === \"string\") return value\n if (format === \"date\" && value instanceof Date) return value.toLocaleDateString(undefined, WALL_CLOCK)\n if (format === \"number\" && typeof value === \"number\") return numberFormat.format(value)\n if (value instanceof Date) return value.toLocaleString(undefined, WALL_CLOCK)\n if (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\"\n return String(value)\n}\n","/**\n * Reading a data file in the browser. Both parsers are loaded through\n * `await import()` so an app without a data-file page never ships them, and an\n * app with one only pays when such a page is opened.\n *\n * Parsing happens on the client rather than at generate time because Vantage\n * has an SPA mode (no `server/`), so there is no server to parse on — and\n * because it makes dev, `build` and `preview` run the same code over the same\n * bytes, which is what \"the same URL returns the same thing\" rests on.\n */\nimport { FILE_PAGE_MAX_BYTES } from \"../core/constants\"\nimport { toSheet } from \"./infer\"\nimport type { Workbook } from \"./types\"\n\n/** Thrown for every failure a file page can show; the page renders the message. */\nexport class FilePageError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"FilePageError\"\n }\n}\n\nfunction megabytes(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`\n}\n\n/**\n * Decode CSV bytes. A Japanese spreadsheet exported from Excel is usually\n * CP932, not UTF-8, and decoding it as UTF-8 turns every non-ASCII character\n * into U+FFFD. Rather than ask the user to declare an encoding, decode as\n * UTF-8 and fall back when the result is visibly full of replacement\n * characters — a real UTF-8 file has none.\n */\nfunction decodeCsv(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n const hasBom = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf\n const body = hasBom ? bytes.subarray(3) : bytes\n const utf8 = new TextDecoder(\"utf-8\").decode(body)\n const replacements = (utf8.match(/�/g) ?? []).length\n if (replacements > 0 && replacements / Math.max(utf8.length, 1) > 0.0005) {\n try {\n return new TextDecoder(\"shift-jis\").decode(body)\n } catch {\n // Some engines ship without the legacy decoder; the UTF-8 read is still\n // the best available answer.\n }\n }\n return utf8\n}\n\nasync function parseCsv(buffer: ArrayBuffer): Promise<Workbook> {\n const { default: Papa } = await import(\"papaparse\")\n const text = decodeCsv(buffer)\n // `header: false` on purpose: lifting the header row is done in `infer.ts`,\n // which puts CSV and XLSX on one code path instead of two.\n const result = Papa.parse<unknown[]>(text, {\n header: false,\n dynamicTyping: true,\n skipEmptyLines: \"greedy\",\n })\n return { sheets: [toSheet(\"Sheet1\", result.data)] }\n}\n\nasync function parseXlsx(buffer: ArrayBuffer): Promise<Workbook> {\n // The browser build specifically: the package has no root export, and the\n // node build reaches for `fs`.\n const { default: readXlsxFile } = await import(\"read-excel-file/browser\")\n const sheets = await readXlsxFile(buffer)\n return {\n sheets: sheets.map((sheet) => toSheet(sheet.sheet, sheet.data as unknown[][])),\n }\n}\n\n/**\n * Fetch and parse the file behind a file page. Every failure — network, size,\n * a corrupt workbook, an extension with no parser — surfaces as a rejected\n * promise so the page can render it in place instead of taking the app down.\n */\nexport async function loadWorkbook(src: string, kind: string): Promise<Workbook> {\n let buffer: ArrayBuffer\n try {\n const response = await fetch(src)\n if (!response.ok) {\n throw new FilePageError(`Could not load the file (HTTP ${response.status}).`)\n }\n buffer = await response.arrayBuffer()\n } catch (err) {\n if (err instanceof FilePageError) throw err\n throw new FilePageError(`Could not load the file: ${(err as Error).message}`)\n }\n\n if (buffer.byteLength > FILE_PAGE_MAX_BYTES) {\n throw new FilePageError(\n `This file is ${megabytes(buffer.byteLength)}, over the ${megabytes(\n FILE_PAGE_MAX_BYTES,\n )} limit for a data-file page. Download it, or serve the data from server/api/ and render it with DataTable.`,\n )\n }\n\n try {\n switch (kind) {\n case \"csv\":\n return await parseCsv(buffer)\n case \"xlsx\":\n return await parseXlsx(buffer)\n default:\n // Reached when an extension is added to FILE_PAGE_EXTENSIONS without a\n // parser here. Naming it beats rendering an empty table.\n throw new FilePageError(`No viewer for \".${kind}\" files.`)\n }\n } catch (err) {\n if (err instanceof FilePageError) throw err\n throw new FilePageError(\n `This file could not be read. It may be corrupt, password-protected, or not really a .${kind} file. (${\n (err as Error).message\n })`,\n )\n }\n}\n","/**\n * The spreadsheet grid. A data file page should read the way the file reads in\n * Excel or Sheets — column letters across the top, row numbers down the side,\n * a ruled canvas of fixed-height rows that runs past the data, and a selected\n * cell. That is why this is a grid and not a styled `DataTable`: the card, the\n * pagination and the sort affordances all say \"web table\", and none of them\n * exist in a spreadsheet.\n *\n * It is a viewer, not an editor. No toolbar, no formula bar, no editing; the\n * only interactions are moving the selection and copying a cell.\n *\n * Rows and columns are windowed. That is not an optimisation to revisit later:\n * a file page is capped at 200,000 rows, and mounting them would be hundreds of\n * thousands of DOM nodes. Fixed row height makes the window arithmetic exact,\n * so no virtualiser dependency is needed — `first = scrollTop / ROW_HEIGHT`.\n */\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n} from \"react\"\n\nimport { cn } from \"@squadbase/squadkit/lib/utils\"\nimport { formatCell } from \"./infer\"\nimport type { CellFormat, TableSheet } from \"./types\"\n\n/** Row height, matching a spreadsheet's default line rather than a web table's. */\nconst ROW_HEIGHT = 22\n/** Height of the A / B / C band. */\nconst HEAD_HEIGHT = 22\nconst MIN_GUTTER_WIDTH = 44\nconst MIN_COL_WIDTH = 56\nconst MAX_COL_WIDTH = 420\n/** Width of the empty columns that carry the ruling past the data. */\nconst FILLER_COL_WIDTH = 100\n/** Horizontal padding plus borders, added to the measured text width. */\nconst COL_PADDING = 18\nconst OVERSCAN_ROWS = 8\nconst OVERSCAN_COLS = 2\n/** Rows sampled when sizing a column. */\nconst WIDTH_SAMPLE_ROWS = 200\n/** Must match the cell font below, or measured widths will not fit the text. */\nconst CELL_FONT =\n '13px -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Hiragino Sans\", \"Hiragino Kaku Gothic ProN\", \"Noto Sans JP\", Meiryo, ui-sans-serif, system-ui, sans-serif'\n\n/** `0 → A`, `25 → Z`, `26 → AA` — spreadsheet column names. */\nexport function columnLabel(index: number): string {\n let label = \"\"\n for (let n = index; n >= 0; n = Math.floor(n / 26) - 1) {\n label = String.fromCharCode(65 + (n % 26)) + label\n }\n return label\n}\n\n/**\n * Size each column to its content, the way a spreadsheet does on import.\n * Measured with the canvas text metrics of the font the cells use, over a\n * sample spread across the whole sheet — sampling the first N rows only would\n * size a column on its header and miss the long values further down.\n */\nfunction measureWidths(sheet: TableSheet): number[] {\n const context =\n typeof document === \"undefined\" ? null : document.createElement(\"canvas\").getContext(\"2d\")\n if (context) context.font = CELL_FONT\n // Without a canvas (SSR, or a browser that refuses the context) fall back to\n // an average character width: the grid still lays out, just less snugly.\n const widthOf = (text: string) => (context ? context.measureText(text).width : text.length * 7.2)\n const stride = Math.max(1, Math.ceil(sheet.rows.length / WIDTH_SAMPLE_ROWS))\n\n return sheet.columns.map((column, index) => {\n let max = widthOf(column.label)\n for (let row = 0; row < sheet.rows.length; row += stride) {\n const text = formatCell(sheet.rows[row]?.[index], column.format)\n if (text !== \"\") max = Math.max(max, widthOf(text))\n }\n return Math.min(MAX_COL_WIDTH, Math.max(MIN_COL_WIDTH, Math.ceil(max) + COL_PADDING))\n })\n}\n\n/** Running x of each column, so windowing and scrolling are lookups. */\nfunction runningOffsets(widths: number[]): number[] {\n const offsets = [0]\n for (const width of widths) offsets.push(offsets[offsets.length - 1]! + width)\n return offsets\n}\n\n/** Index of the last offset at or before `x`. */\nfunction columnAt(offsets: number[], x: number): number {\n let low = 0\n let high = offsets.length - 2\n while (low < high) {\n const mid = (low + high + 1) >> 1\n if (offsets[mid]! <= x) low = mid\n else high = mid - 1\n }\n return Math.max(0, low)\n}\n\ninterface Selection {\n /** `0` is the header row (the file's row 1); `n` is body row `n - 1`. */\n row: number\n col: number\n}\n\nexport interface SheetGridProps {\n sheet: TableSheet\n className?: string\n}\n\n/**\n * One sheet as a grid. Fills its container, so give the parent a height —\n * `TableViewer` is the component that does that for a page.\n */\nexport function SheetGrid({ sheet, className }: SheetGridProps) {\n const scrollRef = useRef<HTMLDivElement>(null)\n const [scroll, setScroll] = useState({ top: 0, left: 0 })\n const [viewport, setViewport] = useState({ width: 0, height: 0 })\n const [selected, setSelected] = useState<Selection>({ row: 0, col: 0 })\n\n const dataWidths = useMemo(() => measureWidths(sheet), [sheet])\n const dataCols = sheet.columns.length\n const dataRows = sheet.rows.length\n\n // Wide enough for the largest row number: the gutter must not resize as the\n // window scrolls into five-digit rows.\n const gutterWidth = Math.max(MIN_GUTTER_WIDTH, String(dataRows + 1).length * 8 + 16)\n\n // Past the data the canvas keeps going, ruled and empty, as it does in every\n // spreadsheet. Enough of it to cover the viewport — beyond that nobody can\n // tell whether the sheet ends at column F or column XFD.\n const dataWidth = dataWidths.reduce((sum, width) => sum + width, 0)\n const fillerCols = Math.max(\n 0,\n Math.ceil((viewport.width - gutterWidth - dataWidth) / FILLER_COL_WIDTH),\n )\n const fillerRows = Math.max(\n 0,\n Math.ceil((viewport.height - HEAD_HEIGHT - (dataRows + 1) * ROW_HEIGHT) / ROW_HEIGHT),\n )\n const colCount = dataCols + fillerCols\n const rowCount = dataRows + fillerRows\n\n const widths = useMemo(\n () => [...dataWidths, ...Array.from({ length: fillerCols }, () => FILLER_COL_WIDTH)],\n [dataWidths, fillerCols],\n )\n const offsets = useMemo(() => runningOffsets(widths), [widths])\n const totalWidth = offsets[offsets.length - 1] ?? 0\n\n useLayoutEffect(() => {\n const element = scrollRef.current\n if (!element) return\n const measure = () => setViewport({ width: element.clientWidth, height: element.clientHeight })\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(element)\n return () => observer.disconnect()\n }, [])\n\n // Selection is positional, so a new sheet must not keep pointing at a cell\n // that no longer exists.\n useEffect(() => {\n setSelected({ row: 0, col: 0 })\n scrollRef.current?.scrollTo({ top: 0, left: 0 })\n }, [sheet])\n\n // Body rows live below the two frozen bands, so the first visible one falls\n // straight out of scrollTop.\n const firstRow = Math.max(0, Math.floor(scroll.top / ROW_HEIGHT) - OVERSCAN_ROWS)\n const lastRow = Math.min(\n rowCount,\n Math.ceil((scroll.top + viewport.height - HEAD_HEIGHT - ROW_HEIGHT) / ROW_HEIGHT) +\n OVERSCAN_ROWS,\n )\n const firstCol = Math.max(0, columnAt(offsets, scroll.left) - OVERSCAN_COLS)\n const lastCol = Math.min(\n colCount,\n columnAt(offsets, scroll.left + viewport.width) + 1 + OVERSCAN_COLS,\n )\n const leadWidth = offsets[firstCol] ?? 0\n const tailWidth = totalWidth - (offsets[lastCol] ?? totalWidth)\n const visibleCols = Array.from({ length: Math.max(0, lastCol - firstCol) }, (_, i) => firstCol + i)\n\n /** Bring a cell inside the scrollport, allowing for the frozen bands. */\n const reveal = useCallback(\n (next: Selection) => {\n const element = scrollRef.current\n if (!element) return\n if (next.row > 0) {\n const top = HEAD_HEIGHT + ROW_HEIGHT + (next.row - 1) * ROW_HEIGHT\n const frozen = HEAD_HEIGHT + ROW_HEIGHT\n if (top - element.scrollTop < frozen) element.scrollTop = top - frozen\n else if (top + ROW_HEIGHT - element.scrollTop > element.clientHeight) {\n element.scrollTop = top + ROW_HEIGHT - element.clientHeight\n }\n }\n const left = offsets[next.col] ?? 0\n const width = widths[next.col] ?? 0\n if (left - element.scrollLeft < 0) element.scrollLeft = left\n else if (left + width + gutterWidth - element.scrollLeft > element.clientWidth) {\n element.scrollLeft = left + width + gutterWidth - element.clientWidth\n }\n },\n [gutterWidth, offsets, widths],\n )\n\n const cellText = useCallback(\n (row: number, col: number): string => {\n const column = sheet.columns[col]\n if (!column) return \"\"\n if (row === 0) return column.label\n return formatCell(sheet.rows[row - 1]?.[col], column.format)\n },\n [sheet],\n )\n\n const onKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n // The selection stays on the data. The empty canvas past it is ruling,\n // not cells anyone needs to land on.\n //\n // `escapable` is what keeps this from being a keyboard trap: the grid is\n // focusable, so if Tab were always swallowed there would be no way out of\n // it. Tab that cannot move the selection any further is left to the\n // browser, which moves focus on to the sheet tabs.\n const step = (rows: number, cols: number, escapable = false) => {\n const next = {\n row: Math.min(dataRows, Math.max(0, selected.row + rows)),\n col: Math.min(dataCols - 1, Math.max(0, selected.col + cols)),\n }\n const moved = next.row !== selected.row || next.col !== selected.col\n if (moved || !escapable) event.preventDefault()\n if (!moved) return\n setSelected(next)\n reveal(next)\n }\n const page = Math.max(1, Math.floor(viewport.height / ROW_HEIGHT) - 1)\n\n switch (event.key) {\n case \"ArrowDown\":\n return step(1, 0)\n case \"ArrowUp\":\n return step(-1, 0)\n case \"Tab\":\n return step(0, event.shiftKey ? -1 : 1, true)\n case \"ArrowRight\":\n return step(0, 1)\n case \"ArrowLeft\":\n return step(0, -1)\n case \"PageDown\":\n return step(page, 0)\n case \"PageUp\":\n return step(-page, 0)\n case \"Home\":\n return step(event.ctrlKey || event.metaKey ? -dataRows : 0, -dataCols)\n case \"End\":\n return step(event.ctrlKey || event.metaKey ? dataRows : 0, dataCols)\n case \"c\":\n case \"C\":\n // The one thing a reader wants out of a viewer: the value, in full,\n // even when the column is too narrow to show it.\n if (event.metaKey || event.ctrlKey) {\n // A refused or unavailable clipboard is not worth an unhandled\n // rejection in the app's error reporting.\n navigator.clipboard?.writeText(cellText(selected.row, selected.col)).catch(() => {})\n }\n return\n default:\n return\n }\n },\n [cellText, dataCols, dataRows, reveal, selected, viewport.height],\n )\n\n if (dataCols === 0) {\n return <p className={cn(\"p-4 text-sm text-muted-foreground\", className)}>This sheet is empty.</p>\n }\n\n const select = (row: number, col: number) => {\n setSelected({ row, col })\n scrollRef.current?.focus({ preventScroll: true })\n }\n\n const rowStyle: CSSProperties = { height: ROW_HEIGHT, width: gutterWidth + totalWidth }\n\n return (\n <div\n ref={scrollRef}\n role=\"grid\"\n aria-label={sheet.name}\n aria-rowcount={dataRows + 1}\n aria-colcount={dataCols}\n tabIndex={0}\n onScroll={(event) =>\n setScroll({ top: event.currentTarget.scrollTop, left: event.currentTarget.scrollLeft })\n }\n onKeyDown={onKeyDown}\n className={cn(\n \"relative h-full overflow-auto bg-background text-[13px] outline-none select-none [overscroll-behavior:contain]\",\n className,\n )}\n >\n <div role=\"rowgroup\" style={{ width: gutterWidth + totalWidth }}>\n {/* A / B / C. Sticky on both axes, so the corner stays put. */}\n <div\n className=\"sticky top-0 z-40 flex\"\n style={{ ...rowStyle, height: HEAD_HEIGHT }}\n role=\"row\"\n >\n <div\n className=\"sticky left-0 z-10 shrink-0 border-r border-b border-border bg-muted\"\n style={{ width: gutterWidth, height: HEAD_HEIGHT }}\n />\n <div className=\"shrink-0 border-b border-border bg-muted\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => (\n <div\n key={col}\n role={col < dataCols ? \"columnheader\" : \"presentation\"}\n aria-colindex={col < dataCols ? col + 1 : undefined}\n onMouseDown={col < dataCols ? () => select(selected.row, col) : undefined}\n className={cn(\n \"flex shrink-0 items-center justify-center border-r border-b border-border bg-muted text-[11px] text-muted-foreground\",\n selected.col === col && col < dataCols && \"bg-accent font-medium text-foreground\",\n )}\n style={{ width: widths[col], height: HEAD_HEIGHT }}\n >\n {columnLabel(col)}\n </div>\n ))}\n <div className=\"shrink-0 border-b border-border bg-muted\" style={{ width: tailWidth }} />\n </div>\n\n {/* The file's row 1. Frozen under the letters, because a data file's\n first row is its header and reading row 40,000 without it is\n guesswork — the first thing anyone does to a sheet by hand. */}\n {/* Opaque: body rows scroll underneath this one. */}\n <div\n className=\"sticky z-30 flex bg-background\"\n style={{ ...rowStyle, top: HEAD_HEIGHT }}\n role=\"row\"\n >\n <RowHeader\n label=\"1\"\n width={gutterWidth}\n active={selected.row === 0}\n onSelect={() => select(0, selected.col)}\n />\n <div className=\"shrink-0 border-b border-foreground/25\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => (\n <Cell\n key={col}\n width={widths[col]!}\n // A header label is text, and a spreadsheet aligns a cell by what\n // is in it — so it reads left even over a column of numbers.\n format=\"text\"\n text={sheet.columns[col]?.label ?? \"\"}\n header\n selected={selected.row === 0 && selected.col === col}\n onSelect={col < dataCols ? () => select(0, col) : undefined}\n colIndex={col < dataCols ? col : undefined}\n />\n ))}\n <div className=\"shrink-0 border-b border-foreground/25\" style={{ width: tailWidth }} />\n </div>\n\n {/* `presentation` so the rows below stay owned by the rowgroup above. */}\n <div role=\"presentation\" className=\"relative\" style={{ height: rowCount * ROW_HEIGHT }}>\n {Array.from({ length: Math.max(0, lastRow - firstRow) }, (_, offset) => {\n const row = firstRow + offset\n const cells = sheet.rows[row]\n return (\n <div\n key={row}\n role=\"row\"\n aria-rowindex={row < dataRows ? row + 2 : undefined}\n className=\"absolute flex\"\n style={{ ...rowStyle, top: row * ROW_HEIGHT }}\n >\n <RowHeader\n label={String(row + 2)}\n width={gutterWidth}\n active={selected.row === row + 1}\n onSelect={row < dataRows ? () => select(row + 1, selected.col) : undefined}\n />\n <div className=\"shrink-0\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => {\n const column = sheet.columns[col]\n return (\n <Cell\n key={col}\n width={widths[col]!}\n format={column?.format ?? \"text\"}\n text={column ? formatCell(cells?.[col], column.format) : \"\"}\n selected={selected.row === row + 1 && selected.col === col}\n onSelect={\n row < dataRows && col < dataCols ? () => select(row + 1, col) : undefined\n }\n colIndex={col < dataCols ? col : undefined}\n />\n )\n })}\n <div className=\"shrink-0\" style={{ width: tailWidth }} />\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\nSheetGrid.displayName = \"SheetGrid\"\n\nfunction RowHeader({\n label,\n width,\n active,\n onSelect,\n}: {\n label: string\n width: number\n active: boolean\n onSelect?: () => void\n}) {\n return (\n <div\n role={onSelect ? \"rowheader\" : \"presentation\"}\n onMouseDown={onSelect}\n className={cn(\n // Above a selected cell (`z-10`): scrolling a selected column under the\n // gutter must not paint its outline over the row number.\n \"sticky left-0 z-20 flex h-full shrink-0 items-center justify-center border-r border-b border-border bg-muted text-[11px] text-muted-foreground\",\n active && \"bg-accent font-medium text-foreground\",\n )}\n style={{ width }}\n >\n {label}\n </div>\n )\n}\n\nfunction Cell({\n width,\n format,\n text,\n header,\n selected,\n onSelect,\n colIndex,\n}: {\n width: number\n format: CellFormat\n text: string\n header?: boolean\n selected: boolean\n onSelect?: () => void\n colIndex?: number\n}) {\n return (\n <div\n role={onSelect ? \"gridcell\" : \"presentation\"}\n aria-colindex={colIndex === undefined ? undefined : colIndex + 1}\n aria-selected={onSelect ? selected : undefined}\n onMouseDown={onSelect}\n // Columns are sized to their content but capped, so a long value still\n // gets clipped — the title is how the rest of it stays reachable\n // without a formula bar.\n title={text || undefined}\n className={cn(\n \"flex h-full shrink-0 items-center border-r border-b border-border px-1.5\",\n header && \"border-b-foreground/25 font-medium\",\n // Numbers and dates read right-aligned, as they do in a spreadsheet.\n format === \"text\" ? \"justify-start\" : \"justify-end tabular-nums\",\n selected && onSelect && \"relative z-10 outline-2 -outline-offset-2 outline-primary\",\n )}\n style={{ width }}\n >\n <span className=\"truncate\">{text}</span>\n </div>\n )\n}\n","/**\n * One sheet, framed the way a spreadsheet application frames one: the grid\n * fills the window, and a status bar sits under it with the sheet tabs on the\n * left and the shape of the sheet on the right.\n *\n * The frame is what makes the grid usable — a grid needs a definite height to\n * scroll inside, and a Vantage page has no guaranteed one (layouts belong to\n * the app). So it measures the space left below itself in the viewport rather\n * than asking the app to arrange for a height.\n */\nimport { useLayoutEffect, useRef, useState, type RefObject } from \"react\"\n\nimport { cn } from \"@squadbase/squadkit/lib/utils\"\nimport { SheetGrid } from \"./sheet-grid\"\nimport type { TableSheet } from \"./types\"\n\n/** Never collapse below this, however little viewport is left. */\nconst MIN_HEIGHT = 320\n/** Breathing room under the frame when the layout leaves none of its own. */\nconst MIN_BOTTOM_GAP = 8\n\nexport interface TableViewerProps {\n /** The sheet to render. */\n sheet: TableSheet\n /** Every sheet's name. Given more than one, a tab strip is rendered. */\n sheetNames?: string[]\n activeSheet?: number\n onSheetChange?: (index: number) => void\n /**\n * Fixed height for the frame. Without it the viewer fills the rest of the\n * viewport, which is right for a file page and wrong for a table embedded in\n * a dashboard among other cards.\n */\n height?: number | string\n className?: string\n}\n\n/**\n * What the layout keeps under the frame: every ancestor's bottom padding,\n * border and margin. It has to come out of the height, or the page ends up\n * scrollable by exactly that much (a page shell nested in a layout shell means\n * two lots of padding). Read from the box model rather than from the document\n * height, which a `min-h-screen` layout pins to the viewport and would make\n * this feed back on its own result.\n */\nfunction spaceBelow(element: HTMLElement): number {\n let total = 0\n for (let node = element.parentElement; node && node !== document.body; node = node.parentElement) {\n const style = getComputedStyle(node)\n total +=\n parseFloat(style.paddingBottom) + parseFloat(style.borderBottomWidth) + parseFloat(style.marginBottom)\n }\n return Number.isFinite(total) ? total : 0\n}\n\n/**\n * Fill the rest of the viewport. Measured rather than expressed in CSS: the\n * frame's distance from the top of the window depends on whatever the app's\n * layout put above it, which no `calc()` here can know.\n */\nfunction useFillHeight(ref: RefObject<HTMLElement | null>, enabled: boolean): number {\n const [height, setHeight] = useState(MIN_HEIGHT)\n\n useLayoutEffect(() => {\n const element = ref.current\n if (!element || !enabled) return\n const measure = () => {\n // Document-relative, not viewport-relative: `rect.top` alone shrinks as\n // the page scrolls, so a measurement taken while scrolled (a mobile URL\n // bar collapsing fires `resize`) would make the frame taller than the\n // viewport and add page scroll that never goes away.\n const rect = element.getBoundingClientRect()\n const top = rect.top + window.scrollY\n const next = Math.max(\n MIN_HEIGHT,\n window.innerHeight - top - Math.max(spaceBelow(element), MIN_BOTTOM_GAP),\n )\n // Only a real change: the frame's own height feeds back into the\n // document height, and rounding noise would loop.\n setHeight((current) => (Math.abs(current - next) > 1 ? next : current))\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(document.body)\n window.addEventListener(\"resize\", measure)\n return () => {\n observer.disconnect()\n window.removeEventListener(\"resize\", measure)\n }\n }, [enabled, ref])\n\n return height\n}\n\nfunction plural(count: number, noun: string): string {\n return `${count.toLocaleString()} ${noun}${count === 1 ? \"\" : \"s\"}`\n}\n\n/** Render one parsed sheet. Exported so an app can drive it with its own data. */\nexport function TableViewer({\n sheet,\n sheetNames,\n activeSheet = 0,\n onSheetChange,\n height,\n className,\n}: TableViewerProps) {\n const frameRef = useRef<HTMLDivElement>(null)\n const filled = useFillHeight(frameRef, height === undefined)\n const tabs = sheetNames && sheetNames.length > 1 ? sheetNames : null\n\n return (\n <div\n ref={frameRef}\n className={cn(\n \"flex flex-col overflow-hidden rounded-md border border-border bg-background\",\n className,\n )}\n style={{ height: height ?? filled }}\n >\n <div className=\"min-h-0 flex-1\">\n <SheetGrid sheet={sheet} />\n </div>\n\n {/* The status bar: sheet tabs and the shape of the sheet, where a\n spreadsheet puts them. */}\n <div className=\"flex items-center gap-4 border-t border-border bg-muted/60 px-2 py-1\">\n {tabs ? (\n <div className=\"flex min-w-0 items-end gap-px overflow-x-auto\" role=\"tablist\">\n {tabs.map((name, index) => (\n <button\n key={name}\n type=\"button\"\n role=\"tab\"\n aria-selected={index === activeSheet}\n onClick={() => onSheetChange?.(index)}\n className={cn(\n \"shrink-0 rounded-t-sm border-t-2 px-3 py-1 text-xs whitespace-nowrap\",\n index === activeSheet\n ? \"border-primary bg-background font-medium text-foreground\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {name}\n </button>\n ))}\n </div>\n ) : null}\n\n <div className=\"ml-auto flex shrink-0 items-center gap-3 text-[11px] text-muted-foreground\">\n {sheet.truncated ? (\n <span className=\"font-medium text-foreground\">\n First {sheet.rows.length.toLocaleString()} of {sheet.totalRows.toLocaleString()} rows —\n download the file for the rest\n </span>\n ) : null}\n <span>\n {plural(sheet.totalRows, \"row\")} × {plural(sheet.columns.length, \"column\")}\n </span>\n </div>\n </div>\n </div>\n )\n}\nTableViewer.displayName = \"TableViewer\"\n","/**\n * The page a `.csv` or `.xlsx` under `src/` becomes. Generated wrappers in\n * `.vantage/file-pages/` mount this; apps can mount it themselves to take over\n * the title or to render a file kept out of routing with an `_` prefix.\n */\nimport { useState } from \"react\"\nimport { DownloadIcon } from \"lucide-react\"\nimport {\n PageShell,\n PageShellActions,\n PageShellContent,\n PageShellHeader,\n PageShellHeading,\n PageShellMeta,\n PageShellTitle,\n} from \"../components\"\nimport { useQuery } from \"../query\"\nimport { Button } from \"@squadbase/squadkit/ui/button\"\nimport { Loading, ErrorState } from \"@squadbase/squadkit/ui/states\"\nimport { loadWorkbook } from \"./parse\"\nimport { TableViewer } from \"./table-viewer\"\n\nexport interface FilePageViewProps {\n /** Extension without the dot, e.g. `\"xlsx\"`. */\n kind: string\n /** URL of the file, produced by Vite's asset pipeline. */\n src: string\n /** Original file name, used for the heading fallback and the download. */\n fileName: string\n /** Title derived from the file name at generate time. */\n title?: string\n}\n\nexport function FilePageView({ kind, src, fileName, title }: FilePageViewProps) {\n const [active, setActive] = useState(0)\n\n // The framework's QueryClient is already above this in the tree, so the\n // parsed workbook is cached across navigations for free and the retry and\n // networkMode policies are the managed ones — no second provider.\n const workbook = useQuery({\n queryKey: [\"vantage:file-page\", src],\n queryFn: () => loadWorkbook(src, kind),\n staleTime: Infinity,\n })\n\n const download = (\n // `nativeButton={false}` because this renders an `<a>`: Base UI needs to\n // know it is not driving a real button, or it warns and keeps button\n // semantics that would fight the link's.\n <Button\n variant=\"outline\"\n size=\"sm\"\n nativeButton={false}\n render={<a href={src} download={fileName} />}\n >\n <DownloadIcon aria-hidden=\"true\" />\n Download\n </Button>\n )\n\n const sheets = workbook.data?.sheets ?? []\n const sheet = sheets[Math.min(active, Math.max(sheets.length - 1, 0))]\n\n return (\n <PageShell>\n {/* A slim band: the grid below wants the vertical room, and a data file\n has no description to carry. */}\n <PageShellHeader className=\"py-6\">\n <PageShellHeading>\n <PageShellTitle>{title ?? fileName}</PageShellTitle>\n <PageShellMeta>{fileName}</PageShellMeta>\n </PageShellHeading>\n <PageShellActions>{download}</PageShellActions>\n </PageShellHeader>\n\n <PageShellContent>\n {workbook.isPending ? <Loading label={`Reading ${fileName}…`} /> : null}\n\n {/* Every failure path lands here, inside the page. A data file the\n browser cannot read must not take the rest of the app down. */}\n {workbook.isError ? (\n <ErrorState\n title={`Could not display ${fileName}`}\n message={(workbook.error as Error).message}\n action={download}\n />\n ) : null}\n\n {sheet ? (\n <TableViewer\n sheet={sheet}\n sheetNames={sheets.map((s) => s.name)}\n activeSheet={active}\n onSheetChange={setActive}\n />\n ) : null}\n </PageShellContent>\n </PageShell>\n )\n}\nFilePageView.displayName = \"FilePageView\"\n"]}
|
|
1
|
+
{"version":3,"sources":["../../src/table/infer.ts","../../src/table/parse.ts","../../src/table/sheet-grid.tsx","../../src/table/table-viewer.tsx","../../src/table/file-page-view.tsx"],"names":["useState","useLayoutEffect","useRef","jsxs","jsx"],"mappings":"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","file":"index.js","sourcesContent":["/**\n * Turning raw rows into a readable table: which row is the header, what each\n * column holds, and how a cell should read. Deliberately shallow — the goal is\n * \"legible as a spreadsheet\", not a reimplementation of one. Formulas, charts\n * and cell formatting are dropped; their values are what survive.\n */\nimport { FILE_PAGE_MAX_ROWS } from \"../core/constants\"\nimport type { CellFormat, TableColumn, TableSheet } from \"./types\"\n\n/** Rows sampled when deciding a column's format. */\nconst TYPE_SAMPLE_ROWS = 500\n\n/** Share of non-empty sampled values that must agree before a type is claimed. */\nconst TYPE_CONFIDENCE = 0.9\n\nfunction isEmpty(value: unknown): boolean {\n return value === null || value === undefined || value === \"\"\n}\n\n/**\n * `YYYY-MM-DD`, optionally with a time. XLSX hands back real `Date`s, but CSV\n * carries dates as text — recognising the ISO shape is what lets a date column\n * from a CSV align (and sort) like one. The string itself is left alone; only\n * the alignment follows from this.\n */\nconst ISO_DATE = /^\\d{4}-\\d{2}-\\d{2}(?:[T ]\\d{2}:\\d{2}(?::\\d{2})?)?$/\n\nfunction classify(value: unknown): CellFormat {\n if (value instanceof Date) return \"date\"\n if (typeof value === \"number\" && Number.isFinite(value)) return \"number\"\n if (typeof value === \"string\" && ISO_DATE.test(value)) return \"date\"\n return \"text\"\n}\n\n/**\n * Header labels are kept exactly as the file wrote them — blank stays blank,\n * and two columns titled \"Amount\" stay two columns titled \"Amount\". The viewer\n * draws this row as the file's row 1, so anything invented here (`Column 3`,\n * `Amount (2)`) would be text the reader cannot find in their own file.\n * Uniqueness lives in `key`, which is positional.\n */\nfunction toColumns(header: unknown[], width: number, body: unknown[][]): TableColumn[] {\n const columns: TableColumn[] = []\n\n for (let index = 0; index < width; index++) {\n const raw = header[index]\n const label = isEmpty(raw) ? \"\" : String(raw).trim()\n\n let numbers = 0\n let dates = 0\n let filled = 0\n for (let row = 0; row < Math.min(body.length, TYPE_SAMPLE_ROWS); row++) {\n const cell = body[row]?.[index]\n if (isEmpty(cell)) continue\n filled++\n const kind = classify(cell)\n if (kind === \"number\") numbers++\n else if (kind === \"date\") dates++\n }\n let format: CellFormat = \"text\"\n if (filled > 0) {\n if (numbers / filled >= TYPE_CONFIDENCE) format = \"number\"\n else if (dates / filled >= TYPE_CONFIDENCE) format = \"date\"\n }\n\n // The key is positional rather than derived from the label: two columns\n // titled \"Amount\" must stay distinguishable, and a label can be any text.\n // Nothing in the viewer reads it — it is there for callers rendering their\n // own table from `toSheet`.\n columns.push({ key: `c${index}`, label, format })\n }\n return columns\n}\n\n/**\n * Build a sheet from raw rows. The first row is the header — the near-universal\n * spreadsheet convention, and guessing otherwise (scoring rows for\n * \"headerness\") misfires on data that happens to start with text.\n */\nexport function toSheet(name: string, rows: unknown[][]): TableSheet {\n if (rows.length === 0) {\n return { name, columns: [], rows: [], totalRows: 0, truncated: false }\n }\n const header = rows[0] ?? []\n const body = rows.slice(1)\n const width = rows.reduce((max, row) => Math.max(max, row.length), 0)\n const truncated = body.length > FILE_PAGE_MAX_ROWS\n const kept = truncated ? body.slice(0, FILE_PAGE_MAX_ROWS) : body\n return {\n name,\n columns: toColumns(header, width, kept),\n // Pad short rows so every row has a value at every column index; ragged\n // rows are common in hand-edited CSV and would otherwise read as holes.\n rows: kept.map((row) => Array.from({ length: width }, (_, i) => row[i] ?? null)),\n totalRows: body.length,\n truncated,\n }\n}\n\nconst numberFormat = new Intl.NumberFormat()\n\n/**\n * XLSX dates have no time zone: read-excel-file turns the serial number into\n * a `Date` whose UTC fields hold the wall-clock value the file shows\n * (2026-06-02 → 2026-06-02T00:00:00Z). Formatting in the browser's zone would\n * move it — a day early west of UTC, an evening time into the next day east\n * of it — so it is always read in UTC. The raw `Date` is left as is, so\n * comparing two of them still orders them as the file does.\n */\nconst WALL_CLOCK = { timeZone: \"UTC\" } as const\n\n/**\n * Cell text. The raw value stays in the row so sorting compares numbers as\n * numbers and dates as dates; only the display passes through here.\n */\nexport function formatCell(value: unknown, format: CellFormat): string {\n if (isEmpty(value)) return \"\"\n // A date that arrived as text is shown exactly as the file wrote it: the\n // point of the viewer is to read the file, not to restate it.\n if (format === \"date\" && typeof value === \"string\") return value\n if (format === \"date\" && value instanceof Date) return value.toLocaleDateString(undefined, WALL_CLOCK)\n if (format === \"number\" && typeof value === \"number\") return numberFormat.format(value)\n if (value instanceof Date) return value.toLocaleString(undefined, WALL_CLOCK)\n if (typeof value === \"boolean\") return value ? \"TRUE\" : \"FALSE\"\n return String(value)\n}\n","/**\n * Reading a data file in the browser. Both parsers are loaded through\n * `await import()` so an app without a data-file page never ships them, and an\n * app with one only pays when such a page is opened.\n *\n * Parsing happens on the client rather than at generate time because Vantage\n * has an SPA mode (no `server/`), so there is no server to parse on — and\n * because it makes dev, `build` and `preview` run the same code over the same\n * bytes, which is what \"the same URL returns the same thing\" rests on.\n */\nimport { FILE_PAGE_MAX_BYTES } from \"../core/constants\"\nimport { toSheet } from \"./infer\"\nimport type { Workbook } from \"./types\"\n\n/** Thrown for every failure a file page can show; the page renders the message. */\nexport class FilePageError extends Error {\n constructor(message: string) {\n super(message)\n this.name = \"FilePageError\"\n }\n}\n\nfunction megabytes(bytes: number): string {\n return `${(bytes / 1024 / 1024).toFixed(1)} MB`\n}\n\n/**\n * Decode CSV bytes. A Japanese spreadsheet exported from Excel is usually\n * CP932, not UTF-8, and decoding it as UTF-8 turns every non-ASCII character\n * into U+FFFD. Rather than ask the user to declare an encoding, decode as\n * UTF-8 and fall back when the result is visibly full of replacement\n * characters — a real UTF-8 file has none.\n */\nfunction decodeCsv(buffer: ArrayBuffer): string {\n const bytes = new Uint8Array(buffer)\n const hasBom = bytes[0] === 0xef && bytes[1] === 0xbb && bytes[2] === 0xbf\n const body = hasBom ? bytes.subarray(3) : bytes\n const utf8 = new TextDecoder(\"utf-8\").decode(body)\n const replacements = (utf8.match(/�/g) ?? []).length\n if (replacements > 0 && replacements / Math.max(utf8.length, 1) > 0.0005) {\n try {\n return new TextDecoder(\"shift-jis\").decode(body)\n } catch {\n // Some engines ship without the legacy decoder; the UTF-8 read is still\n // the best available answer.\n }\n }\n return utf8\n}\n\nasync function parseCsv(buffer: ArrayBuffer): Promise<Workbook> {\n const { default: Papa } = await import(\"papaparse\")\n const text = decodeCsv(buffer)\n // `header: false` on purpose: lifting the header row is done in `infer.ts`,\n // which puts CSV and XLSX on one code path instead of two.\n const result = Papa.parse<unknown[]>(text, {\n header: false,\n dynamicTyping: true,\n skipEmptyLines: \"greedy\",\n })\n return { sheets: [toSheet(\"Sheet1\", result.data)] }\n}\n\nasync function parseXlsx(buffer: ArrayBuffer): Promise<Workbook> {\n // The browser build specifically: the package has no root export, and the\n // node build reaches for `fs`.\n const { default: readXlsxFile } = await import(\"read-excel-file/browser\")\n const sheets = await readXlsxFile(buffer)\n return {\n sheets: sheets.map((sheet) => toSheet(sheet.sheet, sheet.data as unknown[][])),\n }\n}\n\n/**\n * Fetch and parse the file behind a file page. Every failure — network, size,\n * a corrupt workbook, an extension with no parser — surfaces as a rejected\n * promise so the page can render it in place instead of taking the app down.\n */\nexport async function loadWorkbook(src: string, kind: string): Promise<Workbook> {\n let buffer: ArrayBuffer\n try {\n const response = await fetch(src)\n if (!response.ok) {\n throw new FilePageError(`Could not load the file (HTTP ${response.status}).`)\n }\n buffer = await response.arrayBuffer()\n } catch (err) {\n if (err instanceof FilePageError) throw err\n throw new FilePageError(`Could not load the file: ${(err as Error).message}`)\n }\n\n if (buffer.byteLength > FILE_PAGE_MAX_BYTES) {\n throw new FilePageError(\n `This file is ${megabytes(buffer.byteLength)}, over the ${megabytes(\n FILE_PAGE_MAX_BYTES,\n )} limit for a data-file page. Download it, or serve the data from server/api/ and render it with DataTable.`,\n )\n }\n\n try {\n switch (kind) {\n case \"csv\":\n return await parseCsv(buffer)\n case \"xlsx\":\n return await parseXlsx(buffer)\n default:\n // Reached when an extension is added to FILE_PAGE_EXTENSIONS without a\n // parser here. Naming it beats rendering an empty table.\n throw new FilePageError(`No viewer for \".${kind}\" files.`)\n }\n } catch (err) {\n if (err instanceof FilePageError) throw err\n throw new FilePageError(\n `This file could not be read. It may be corrupt, password-protected, or not really a .${kind} file. (${\n (err as Error).message\n })`,\n )\n }\n}\n","/**\n * The spreadsheet grid. A data file page should read the way the file reads in\n * Excel or Sheets — column letters across the top, row numbers down the side,\n * a ruled canvas of fixed-height rows that runs past the data, and a selected\n * cell. That is why this is a grid and not a styled `DataTable`: the card, the\n * pagination and the sort affordances all say \"web table\", and none of them\n * exist in a spreadsheet.\n *\n * It is a viewer, not an editor. No toolbar, no formula bar, no editing; the\n * only interactions are moving the selection and copying a cell.\n *\n * Rows and columns are windowed. That is not an optimisation to revisit later:\n * a file page is capped at 200,000 rows, and mounting them would be hundreds of\n * thousands of DOM nodes. Fixed row height makes the window arithmetic exact,\n * so no virtualiser dependency is needed — `first = scrollTop / ROW_HEIGHT`.\n */\nimport {\n useCallback,\n useEffect,\n useLayoutEffect,\n useMemo,\n useRef,\n useState,\n type CSSProperties,\n type KeyboardEvent,\n} from \"react\"\n\nimport { cn } from \"@squadbase/squadkit/lib/utils\"\nimport { formatCell } from \"./infer\"\nimport type { CellFormat, TableSheet } from \"./types\"\n\n/** Row height, matching a spreadsheet's default line rather than a web table's. */\nconst ROW_HEIGHT = 22\n/** Height of the A / B / C band. */\nconst HEAD_HEIGHT = 22\nconst MIN_GUTTER_WIDTH = 44\nconst MIN_COL_WIDTH = 56\nconst MAX_COL_WIDTH = 420\n/** Width of the empty columns that carry the ruling past the data. */\nconst FILLER_COL_WIDTH = 100\n/** Horizontal padding plus borders, added to the measured text width. */\nconst COL_PADDING = 18\nconst OVERSCAN_ROWS = 8\nconst OVERSCAN_COLS = 2\n/** Rows sampled when sizing a column. */\nconst WIDTH_SAMPLE_ROWS = 200\n/** Must match the cell font below, or measured widths will not fit the text. */\nconst CELL_FONT =\n '13px -apple-system, BlinkMacSystemFont, \"Segoe UI\", Roboto, \"Hiragino Sans\", \"Hiragino Kaku Gothic ProN\", \"Noto Sans JP\", Meiryo, ui-sans-serif, system-ui, sans-serif'\n\n/** `0 → A`, `25 → Z`, `26 → AA` — spreadsheet column names. */\nexport function columnLabel(index: number): string {\n let label = \"\"\n for (let n = index; n >= 0; n = Math.floor(n / 26) - 1) {\n label = String.fromCharCode(65 + (n % 26)) + label\n }\n return label\n}\n\n/**\n * Size each column to its content, the way a spreadsheet does on import.\n * Measured with the canvas text metrics of the font the cells use, over a\n * sample spread across the whole sheet — sampling the first N rows only would\n * size a column on its header and miss the long values further down.\n */\nfunction measureWidths(sheet: TableSheet): number[] {\n const context =\n typeof document === \"undefined\" ? null : document.createElement(\"canvas\").getContext(\"2d\")\n if (context) context.font = CELL_FONT\n // Without a canvas (SSR, or a browser that refuses the context) fall back to\n // an average character width: the grid still lays out, just less snugly.\n const widthOf = (text: string) => (context ? context.measureText(text).width : text.length * 7.2)\n const stride = Math.max(1, Math.ceil(sheet.rows.length / WIDTH_SAMPLE_ROWS))\n\n return sheet.columns.map((column, index) => {\n let max = widthOf(column.label)\n for (let row = 0; row < sheet.rows.length; row += stride) {\n const text = formatCell(sheet.rows[row]?.[index], column.format)\n if (text !== \"\") max = Math.max(max, widthOf(text))\n }\n return Math.min(MAX_COL_WIDTH, Math.max(MIN_COL_WIDTH, Math.ceil(max) + COL_PADDING))\n })\n}\n\n/** Running x of each column, so windowing and scrolling are lookups. */\nfunction runningOffsets(widths: number[]): number[] {\n const offsets = [0]\n for (const width of widths) offsets.push(offsets[offsets.length - 1]! + width)\n return offsets\n}\n\n/** Index of the last offset at or before `x`. */\nfunction columnAt(offsets: number[], x: number): number {\n let low = 0\n let high = offsets.length - 2\n while (low < high) {\n const mid = (low + high + 1) >> 1\n if (offsets[mid]! <= x) low = mid\n else high = mid - 1\n }\n return Math.max(0, low)\n}\n\ninterface Selection {\n /** `0` is the header row (the file's row 1); `n` is body row `n - 1`. */\n row: number\n col: number\n}\n\nexport interface SheetGridProps {\n sheet: TableSheet\n className?: string\n}\n\n/**\n * One sheet as a grid. Fills its container, so give the parent a height —\n * `TableViewer` is the component that does that for a page.\n */\nexport function SheetGrid({ sheet, className }: SheetGridProps) {\n const scrollRef = useRef<HTMLDivElement>(null)\n const [scroll, setScroll] = useState({ top: 0, left: 0 })\n const [viewport, setViewport] = useState({ width: 0, height: 0 })\n const [selected, setSelected] = useState<Selection>({ row: 0, col: 0 })\n\n const dataWidths = useMemo(() => measureWidths(sheet), [sheet])\n const dataCols = sheet.columns.length\n const dataRows = sheet.rows.length\n\n // Wide enough for the largest row number: the gutter must not resize as the\n // window scrolls into five-digit rows.\n const gutterWidth = Math.max(MIN_GUTTER_WIDTH, String(dataRows + 1).length * 8 + 16)\n\n // Past the data the canvas keeps going, ruled and empty, as it does in every\n // spreadsheet. Enough of it to cover the viewport — beyond that nobody can\n // tell whether the sheet ends at column F or column XFD.\n const dataWidth = dataWidths.reduce((sum, width) => sum + width, 0)\n const fillerCols = Math.max(\n 0,\n Math.ceil((viewport.width - gutterWidth - dataWidth) / FILLER_COL_WIDTH),\n )\n const fillerRows = Math.max(\n 0,\n Math.ceil((viewport.height - HEAD_HEIGHT - (dataRows + 1) * ROW_HEIGHT) / ROW_HEIGHT),\n )\n const colCount = dataCols + fillerCols\n const rowCount = dataRows + fillerRows\n\n const widths = useMemo(\n () => [...dataWidths, ...Array.from({ length: fillerCols }, () => FILLER_COL_WIDTH)],\n [dataWidths, fillerCols],\n )\n const offsets = useMemo(() => runningOffsets(widths), [widths])\n const totalWidth = offsets[offsets.length - 1] ?? 0\n\n useLayoutEffect(() => {\n const element = scrollRef.current\n if (!element) return\n const measure = () => setViewport({ width: element.clientWidth, height: element.clientHeight })\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(element)\n return () => observer.disconnect()\n }, [])\n\n // Selection is positional, so a new sheet must not keep pointing at a cell\n // that no longer exists.\n useEffect(() => {\n setSelected({ row: 0, col: 0 })\n scrollRef.current?.scrollTo({ top: 0, left: 0 })\n }, [sheet])\n\n // Body rows live below the two frozen bands, so the first visible one falls\n // straight out of scrollTop.\n const firstRow = Math.max(0, Math.floor(scroll.top / ROW_HEIGHT) - OVERSCAN_ROWS)\n const lastRow = Math.min(\n rowCount,\n Math.ceil((scroll.top + viewport.height - HEAD_HEIGHT - ROW_HEIGHT) / ROW_HEIGHT) +\n OVERSCAN_ROWS,\n )\n const firstCol = Math.max(0, columnAt(offsets, scroll.left) - OVERSCAN_COLS)\n const lastCol = Math.min(\n colCount,\n columnAt(offsets, scroll.left + viewport.width) + 1 + OVERSCAN_COLS,\n )\n const leadWidth = offsets[firstCol] ?? 0\n const tailWidth = totalWidth - (offsets[lastCol] ?? totalWidth)\n const visibleCols = Array.from({ length: Math.max(0, lastCol - firstCol) }, (_, i) => firstCol + i)\n\n /** Bring a cell inside the scrollport, allowing for the frozen bands. */\n const reveal = useCallback(\n (next: Selection) => {\n const element = scrollRef.current\n if (!element) return\n if (next.row > 0) {\n const top = HEAD_HEIGHT + ROW_HEIGHT + (next.row - 1) * ROW_HEIGHT\n const frozen = HEAD_HEIGHT + ROW_HEIGHT\n if (top - element.scrollTop < frozen) element.scrollTop = top - frozen\n else if (top + ROW_HEIGHT - element.scrollTop > element.clientHeight) {\n element.scrollTop = top + ROW_HEIGHT - element.clientHeight\n }\n }\n const left = offsets[next.col] ?? 0\n const width = widths[next.col] ?? 0\n if (left - element.scrollLeft < 0) element.scrollLeft = left\n else if (left + width + gutterWidth - element.scrollLeft > element.clientWidth) {\n element.scrollLeft = left + width + gutterWidth - element.clientWidth\n }\n },\n [gutterWidth, offsets, widths],\n )\n\n const cellText = useCallback(\n (row: number, col: number): string => {\n const column = sheet.columns[col]\n if (!column) return \"\"\n if (row === 0) return column.label\n return formatCell(sheet.rows[row - 1]?.[col], column.format)\n },\n [sheet],\n )\n\n const onKeyDown = useCallback(\n (event: KeyboardEvent<HTMLDivElement>) => {\n // The selection stays on the data. The empty canvas past it is ruling,\n // not cells anyone needs to land on.\n //\n // `escapable` is what keeps this from being a keyboard trap: the grid is\n // focusable, so if Tab were always swallowed there would be no way out of\n // it. Tab that cannot move the selection any further is left to the\n // browser, which moves focus on to the sheet tabs.\n const step = (rows: number, cols: number, escapable = false) => {\n const next = {\n row: Math.min(dataRows, Math.max(0, selected.row + rows)),\n col: Math.min(dataCols - 1, Math.max(0, selected.col + cols)),\n }\n const moved = next.row !== selected.row || next.col !== selected.col\n if (moved || !escapable) event.preventDefault()\n if (!moved) return\n setSelected(next)\n reveal(next)\n }\n const page = Math.max(1, Math.floor(viewport.height / ROW_HEIGHT) - 1)\n\n switch (event.key) {\n case \"ArrowDown\":\n return step(1, 0)\n case \"ArrowUp\":\n return step(-1, 0)\n case \"Tab\":\n return step(0, event.shiftKey ? -1 : 1, true)\n case \"ArrowRight\":\n return step(0, 1)\n case \"ArrowLeft\":\n return step(0, -1)\n case \"PageDown\":\n return step(page, 0)\n case \"PageUp\":\n return step(-page, 0)\n case \"Home\":\n return step(event.ctrlKey || event.metaKey ? -dataRows : 0, -dataCols)\n case \"End\":\n return step(event.ctrlKey || event.metaKey ? dataRows : 0, dataCols)\n case \"c\":\n case \"C\":\n // The one thing a reader wants out of a viewer: the value, in full,\n // even when the column is too narrow to show it.\n if (event.metaKey || event.ctrlKey) {\n // A refused or unavailable clipboard is not worth an unhandled\n // rejection in the app's error reporting.\n navigator.clipboard?.writeText(cellText(selected.row, selected.col)).catch(() => {})\n }\n return\n default:\n return\n }\n },\n [cellText, dataCols, dataRows, reveal, selected, viewport.height],\n )\n\n if (dataCols === 0) {\n return <p className={cn(\"p-4 text-sm text-muted-foreground\", className)}>This sheet is empty.</p>\n }\n\n const select = (row: number, col: number) => {\n setSelected({ row, col })\n scrollRef.current?.focus({ preventScroll: true })\n }\n\n const rowStyle: CSSProperties = { height: ROW_HEIGHT, width: gutterWidth + totalWidth }\n\n return (\n <div\n ref={scrollRef}\n role=\"grid\"\n aria-label={sheet.name}\n aria-rowcount={dataRows + 1}\n aria-colcount={dataCols}\n tabIndex={0}\n onScroll={(event) =>\n setScroll({ top: event.currentTarget.scrollTop, left: event.currentTarget.scrollLeft })\n }\n onKeyDown={onKeyDown}\n className={cn(\n \"relative h-full overflow-auto bg-background text-[13px] outline-none select-none [overscroll-behavior:contain]\",\n className,\n )}\n >\n <div role=\"rowgroup\" style={{ width: gutterWidth + totalWidth }}>\n {/* A / B / C. Sticky on both axes, so the corner stays put. */}\n <div\n className=\"sticky top-0 z-40 flex\"\n style={{ ...rowStyle, height: HEAD_HEIGHT }}\n role=\"row\"\n >\n <div\n className=\"sticky left-0 z-10 shrink-0 border-r border-b border-border bg-muted\"\n style={{ width: gutterWidth, height: HEAD_HEIGHT }}\n />\n <div className=\"shrink-0 border-b border-border bg-muted\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => (\n <div\n key={col}\n role={col < dataCols ? \"columnheader\" : \"presentation\"}\n aria-colindex={col < dataCols ? col + 1 : undefined}\n onMouseDown={col < dataCols ? () => select(selected.row, col) : undefined}\n className={cn(\n \"flex shrink-0 items-center justify-center border-r border-b border-border bg-muted text-[11px] text-muted-foreground\",\n selected.col === col && col < dataCols && \"bg-accent font-medium text-foreground\",\n )}\n style={{ width: widths[col], height: HEAD_HEIGHT }}\n >\n {columnLabel(col)}\n </div>\n ))}\n <div className=\"shrink-0 border-b border-border bg-muted\" style={{ width: tailWidth }} />\n </div>\n\n {/* The file's row 1. Frozen under the letters, because a data file's\n first row is its header and reading row 40,000 without it is\n guesswork — the first thing anyone does to a sheet by hand. */}\n {/* Opaque: body rows scroll underneath this one. */}\n <div\n className=\"sticky z-30 flex bg-background\"\n style={{ ...rowStyle, top: HEAD_HEIGHT }}\n role=\"row\"\n >\n <RowHeader\n label=\"1\"\n width={gutterWidth}\n active={selected.row === 0}\n onSelect={() => select(0, selected.col)}\n />\n <div className=\"shrink-0 border-b border-foreground/25\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => (\n <Cell\n key={col}\n width={widths[col]!}\n // A header label is text, and a spreadsheet aligns a cell by what\n // is in it — so it reads left even over a column of numbers.\n format=\"text\"\n text={sheet.columns[col]?.label ?? \"\"}\n header\n selected={selected.row === 0 && selected.col === col}\n onSelect={col < dataCols ? () => select(0, col) : undefined}\n colIndex={col < dataCols ? col : undefined}\n />\n ))}\n <div className=\"shrink-0 border-b border-foreground/25\" style={{ width: tailWidth }} />\n </div>\n\n {/* `presentation` so the rows below stay owned by the rowgroup above. */}\n <div role=\"presentation\" className=\"relative\" style={{ height: rowCount * ROW_HEIGHT }}>\n {Array.from({ length: Math.max(0, lastRow - firstRow) }, (_, offset) => {\n const row = firstRow + offset\n const cells = sheet.rows[row]\n return (\n <div\n key={row}\n role=\"row\"\n aria-rowindex={row < dataRows ? row + 2 : undefined}\n className=\"absolute flex\"\n style={{ ...rowStyle, top: row * ROW_HEIGHT }}\n >\n <RowHeader\n label={String(row + 2)}\n width={gutterWidth}\n active={selected.row === row + 1}\n onSelect={row < dataRows ? () => select(row + 1, selected.col) : undefined}\n />\n <div className=\"shrink-0\" style={{ width: leadWidth }} />\n {visibleCols.map((col) => {\n const column = sheet.columns[col]\n return (\n <Cell\n key={col}\n width={widths[col]!}\n format={column?.format ?? \"text\"}\n text={column ? formatCell(cells?.[col], column.format) : \"\"}\n selected={selected.row === row + 1 && selected.col === col}\n onSelect={\n row < dataRows && col < dataCols ? () => select(row + 1, col) : undefined\n }\n colIndex={col < dataCols ? col : undefined}\n />\n )\n })}\n <div className=\"shrink-0\" style={{ width: tailWidth }} />\n </div>\n )\n })}\n </div>\n </div>\n </div>\n )\n}\nSheetGrid.displayName = \"SheetGrid\"\n\nfunction RowHeader({\n label,\n width,\n active,\n onSelect,\n}: {\n label: string\n width: number\n active: boolean\n onSelect?: () => void\n}) {\n return (\n <div\n role={onSelect ? \"rowheader\" : \"presentation\"}\n onMouseDown={onSelect}\n className={cn(\n // Above a selected cell (`z-10`): scrolling a selected column under the\n // gutter must not paint its outline over the row number.\n \"sticky left-0 z-20 flex h-full shrink-0 items-center justify-center border-r border-b border-border bg-muted text-[11px] text-muted-foreground\",\n active && \"bg-accent font-medium text-foreground\",\n )}\n style={{ width }}\n >\n {label}\n </div>\n )\n}\n\nfunction Cell({\n width,\n format,\n text,\n header,\n selected,\n onSelect,\n colIndex,\n}: {\n width: number\n format: CellFormat\n text: string\n header?: boolean\n selected: boolean\n onSelect?: () => void\n colIndex?: number\n}) {\n return (\n <div\n role={onSelect ? \"gridcell\" : \"presentation\"}\n aria-colindex={colIndex === undefined ? undefined : colIndex + 1}\n aria-selected={onSelect ? selected : undefined}\n onMouseDown={onSelect}\n // Columns are sized to their content but capped, so a long value still\n // gets clipped — the title is how the rest of it stays reachable\n // without a formula bar.\n title={text || undefined}\n className={cn(\n \"flex h-full shrink-0 items-center border-r border-b border-border px-1.5\",\n header && \"border-b-foreground/25 font-medium\",\n // Numbers and dates read right-aligned, as they do in a spreadsheet.\n format === \"text\" ? \"justify-start\" : \"justify-end tabular-nums\",\n selected && onSelect && \"relative z-10 outline-2 -outline-offset-2 outline-primary\",\n )}\n style={{ width }}\n >\n <span className=\"truncate\">{text}</span>\n </div>\n )\n}\n","/**\n * One sheet, framed the way a spreadsheet application frames one: the grid\n * fills the window, and a status bar sits under it with the sheet tabs on the\n * left and the shape of the sheet on the right.\n *\n * The frame is what makes the grid usable — a grid needs a definite height to\n * scroll inside, and a Vantage page has no guaranteed one (layouts belong to\n * the app). So it measures the space left below itself in the viewport rather\n * than asking the app to arrange for a height.\n */\nimport { useLayoutEffect, useRef, useState, type RefObject } from \"react\"\n\nimport { cn } from \"@squadbase/squadkit/lib/utils\"\nimport { SheetGrid } from \"./sheet-grid\"\nimport type { TableSheet } from \"./types\"\n\n/** Never collapse below this, however little viewport is left. */\nconst MIN_HEIGHT = 320\n/** Breathing room under the frame when the layout leaves none of its own. */\nconst MIN_BOTTOM_GAP = 8\n\nexport interface TableViewerProps {\n /** The sheet to render. */\n sheet: TableSheet\n /** Every sheet's name. Given more than one, a tab strip is rendered. */\n sheetNames?: string[]\n activeSheet?: number\n onSheetChange?: (index: number) => void\n /**\n * Fixed height for the frame. Without it the viewer fills the rest of the\n * viewport, which is right for a file page and wrong for a table embedded in\n * a dashboard among other cards.\n */\n height?: number | string\n className?: string\n}\n\n/**\n * What the layout keeps under the frame: every ancestor's bottom padding,\n * border and margin. It has to come out of the height, or the page ends up\n * scrollable by exactly that much (a page shell nested in a layout shell means\n * two lots of padding). Read from the box model rather than from the document\n * height, which a `min-h-screen` layout pins to the viewport and would make\n * this feed back on its own result.\n */\nfunction spaceBelow(element: HTMLElement): number {\n let total = 0\n for (let node = element.parentElement; node && node !== document.body; node = node.parentElement) {\n const style = getComputedStyle(node)\n total +=\n parseFloat(style.paddingBottom) + parseFloat(style.borderBottomWidth) + parseFloat(style.marginBottom)\n }\n return Number.isFinite(total) ? total : 0\n}\n\n/**\n * Fill the rest of the viewport. Measured rather than expressed in CSS: the\n * frame's distance from the top of the window depends on whatever the app's\n * layout put above it, which no `calc()` here can know.\n */\nfunction useFillHeight(ref: RefObject<HTMLElement | null>, enabled: boolean): number {\n const [height, setHeight] = useState(MIN_HEIGHT)\n\n useLayoutEffect(() => {\n const element = ref.current\n if (!element || !enabled) return\n const measure = () => {\n // Document-relative, not viewport-relative: `rect.top` alone shrinks as\n // the page scrolls, so a measurement taken while scrolled (a mobile URL\n // bar collapsing fires `resize`) would make the frame taller than the\n // viewport and add page scroll that never goes away.\n const rect = element.getBoundingClientRect()\n const top = rect.top + window.scrollY\n const next = Math.max(\n MIN_HEIGHT,\n window.innerHeight - top - Math.max(spaceBelow(element), MIN_BOTTOM_GAP),\n )\n // Only a real change: the frame's own height feeds back into the\n // document height, and rounding noise would loop.\n setHeight((current) => (Math.abs(current - next) > 1 ? next : current))\n }\n measure()\n const observer = new ResizeObserver(measure)\n observer.observe(document.body)\n window.addEventListener(\"resize\", measure)\n return () => {\n observer.disconnect()\n window.removeEventListener(\"resize\", measure)\n }\n }, [enabled, ref])\n\n return height\n}\n\nfunction plural(count: number, noun: string): string {\n return `${count.toLocaleString()} ${noun}${count === 1 ? \"\" : \"s\"}`\n}\n\n/** Render one parsed sheet. Exported so an app can drive it with its own data. */\nexport function TableViewer({\n sheet,\n sheetNames,\n activeSheet = 0,\n onSheetChange,\n height,\n className,\n}: TableViewerProps) {\n const frameRef = useRef<HTMLDivElement>(null)\n const filled = useFillHeight(frameRef, height === undefined)\n const tabs = sheetNames && sheetNames.length > 1 ? sheetNames : null\n\n return (\n <div\n ref={frameRef}\n className={cn(\n \"flex flex-col overflow-hidden rounded-md border border-border bg-background\",\n className,\n )}\n style={{ height: height ?? filled }}\n >\n <div className=\"min-h-0 flex-1\">\n <SheetGrid sheet={sheet} />\n </div>\n\n {/* The status bar: sheet tabs and the shape of the sheet, where a\n spreadsheet puts them. */}\n <div className=\"flex items-center gap-4 border-t border-border bg-muted/60 px-2 py-1\">\n {tabs ? (\n <div className=\"flex min-w-0 items-end gap-px overflow-x-auto\" role=\"tablist\">\n {tabs.map((name, index) => (\n <button\n key={name}\n type=\"button\"\n role=\"tab\"\n aria-selected={index === activeSheet}\n onClick={() => onSheetChange?.(index)}\n className={cn(\n \"shrink-0 rounded-t-sm border-t-2 px-3 py-1 text-xs whitespace-nowrap\",\n index === activeSheet\n ? \"border-primary bg-background font-medium text-foreground\"\n : \"border-transparent text-muted-foreground hover:text-foreground\",\n )}\n >\n {name}\n </button>\n ))}\n </div>\n ) : null}\n\n <div className=\"ml-auto flex shrink-0 items-center gap-3 text-[11px] text-muted-foreground\">\n {sheet.truncated ? (\n <span className=\"font-medium text-foreground\">\n First {sheet.rows.length.toLocaleString()} of {sheet.totalRows.toLocaleString()} rows —\n download the file for the rest\n </span>\n ) : null}\n <span>\n {plural(sheet.totalRows, \"row\")} × {plural(sheet.columns.length, \"column\")}\n </span>\n </div>\n </div>\n </div>\n )\n}\nTableViewer.displayName = \"TableViewer\"\n","/**\n * The page a `.csv` or `.xlsx` under `src/` becomes. Generated wrappers in\n * `.vantage/file-pages/` mount this; apps can mount it themselves to take over\n * the title or to render a file kept out of routing with an `_` prefix.\n */\nimport { useState } from \"react\"\nimport { DownloadIcon } from \"lucide-react\"\nimport {\n PageShell,\n PageShellActions,\n PageShellContent,\n PageShellHeader,\n PageShellHeading,\n PageShellMeta,\n PageShellTitle,\n} from \"../components\"\nimport { useQuery } from \"../query\"\nimport { Button } from \"@squadbase/squadkit/ui/button\"\nimport { Loading, ErrorState } from \"@squadbase/squadkit/ui/states\"\nimport { loadWorkbook } from \"./parse\"\nimport { TableViewer } from \"./table-viewer\"\n\nexport interface FilePageViewProps {\n /** Extension without the dot, e.g. `\"xlsx\"`. */\n kind: string\n /** URL of the file, produced by Vite's asset pipeline. */\n src: string\n /** Original file name, used for the heading fallback and the download. */\n fileName: string\n /** Title derived from the file name at generate time. */\n title?: string\n}\n\nexport function FilePageView({ kind, src, fileName, title }: FilePageViewProps) {\n const [active, setActive] = useState(0)\n\n // The framework's QueryClient is already above this in the tree, so the\n // parsed workbook is cached across navigations for free and the retry and\n // networkMode policies are the managed ones — no second provider.\n const workbook = useQuery({\n queryKey: [\"vantage:file-page\", src],\n queryFn: () => loadWorkbook(src, kind),\n staleTime: Infinity,\n })\n\n const download = (\n // `nativeButton={false}` because this renders an `<a>`: Base UI needs to\n // know it is not driving a real button, or it warns and keeps button\n // semantics that would fight the link's.\n <Button\n variant=\"outline\"\n size=\"sm\"\n nativeButton={false}\n render={<a href={src} download={fileName} />}\n >\n <DownloadIcon aria-hidden=\"true\" />\n Download\n </Button>\n )\n\n const sheets = workbook.data?.sheets ?? []\n const sheet = sheets[Math.min(active, Math.max(sheets.length - 1, 0))]\n\n return (\n <PageShell>\n {/* A slim band: the grid below wants the vertical room, and a data file\n has no description to carry. */}\n <PageShellHeader className=\"py-6\">\n <PageShellHeading>\n <PageShellTitle>{title ?? fileName}</PageShellTitle>\n <PageShellMeta>{fileName}</PageShellMeta>\n </PageShellHeading>\n <PageShellActions>{download}</PageShellActions>\n </PageShellHeader>\n\n <PageShellContent>\n {workbook.isPending ? <Loading label={`Reading ${fileName}…`} /> : null}\n\n {/* Every failure path lands here, inside the page. A data file the\n browser cannot read must not take the rest of the app down. */}\n {workbook.isError ? (\n <ErrorState\n title={`Could not display ${fileName}`}\n message={(workbook.error as Error).message}\n action={download}\n />\n ) : null}\n\n {sheet ? (\n <TableViewer\n sheet={sheet}\n sheetNames={sheets.map((s) => s.name)}\n activeSheet={active}\n onSheetChange={setActive}\n />\n ) : null}\n </PageShellContent>\n </PageShell>\n )\n}\nFilePageView.displayName = \"FilePageView\"\n"]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { syncAgentsFile } from './chunk-AABMKHEU.js';
|
|
2
|
-
import { generateArtifacts } from './chunk-
|
|
3
|
-
import './chunk-
|
|
2
|
+
import { generateArtifacts } from './chunk-R6MTJAHX.js';
|
|
3
|
+
import './chunk-S5MDTHH7.js';
|
|
4
4
|
import { scanProject } from './chunk-RNTDBX6S.js';
|
|
5
5
|
import { frameworkVersion, printJson, log } from './chunk-GNXCX4WE.js';
|
|
6
6
|
import './chunk-C2CX6GLX.js';
|
|
@@ -25,5 +25,5 @@ async function upgrade(root, args) {
|
|
|
25
25
|
}
|
|
26
26
|
|
|
27
27
|
export { upgrade };
|
|
28
|
-
//# sourceMappingURL=upgrade-
|
|
29
|
-
//# sourceMappingURL=upgrade-
|
|
28
|
+
//# sourceMappingURL=upgrade-7ZXMCTTR.js.map
|
|
29
|
+
//# sourceMappingURL=upgrade-7ZXMCTTR.js.map
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/cli/upgrade.ts"],"names":[],"mappings":";;;;;;;;;;AAWA,eAAsB,OAAA,CAAQ,MAAc,IAAA,EAAmC;AAC7E,EAAA,MAAM,UAAU,gBAAA,EAAiB;AACjC,EAAA,MAAM,KAAA,GAAQ,YAAY,IAAI,CAAA;AAC9B,EAAA,iBAAA,CAAkB,EAAE,IAAA,EAAM,KAAA,EAAO,CAAA;AAKjC,EAAA,MAAM,MAAA,GAAS,eAAe,IAAI,CAAA;AAElC,EAAA,IAAI,IAAA,CAAK,MAAM,IAAA,EAAM;AACnB,IAAA,SAAA,CAAU,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,UAAA,EAAY,EAAC,EAAG,WAAA,EAAa,IAAA,EAAM,YAAA,EAAc,MAAA,KAAW,SAAA,EAAW,CAAA;AACtG,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,GAAA,CAAI,KAAA,CAAM,CAAA,gBAAA,EAAc,OAAO,CAAA,CAAE,CAAA;AACjC,EAAA,GAAA,CAAI,GAAG,iCAAiC,CAAA;AACxC,EAAA,IAAI,MAAA,KAAW,SAAA,EAAW,GAAA,CAAI,EAAA,CAAG,kDAAkD,CAAA;AACnF,EAAA,GAAA,CAAI,KAAK,wDAAwD,CAAA;AACjE,EAAA,OAAO,CAAA;AACT","file":"upgrade-
|
|
1
|
+
{"version":3,"sources":["../src/cli/upgrade.ts"],"names":[],"mappings":";;;;;;;;;;AAWA,eAAsB,OAAA,CAAQ,MAAc,IAAA,EAAmC;AAC7E,EAAA,MAAM,UAAU,gBAAA,EAAiB;AACjC,EAAA,MAAM,KAAA,GAAQ,YAAY,IAAI,CAAA;AAC9B,EAAA,iBAAA,CAAkB,EAAE,IAAA,EAAM,KAAA,EAAO,CAAA;AAKjC,EAAA,MAAM,MAAA,GAAS,eAAe,IAAI,CAAA;AAElC,EAAA,IAAI,IAAA,CAAK,MAAM,IAAA,EAAM;AACnB,IAAA,SAAA,CAAU,EAAE,EAAA,EAAI,IAAA,EAAM,OAAA,EAAS,UAAA,EAAY,EAAC,EAAG,WAAA,EAAa,IAAA,EAAM,YAAA,EAAc,MAAA,KAAW,SAAA,EAAW,CAAA;AACtG,IAAA,OAAO,CAAA;AAAA,EACT;AAEA,EAAA,GAAA,CAAI,KAAA,CAAM,CAAA,gBAAA,EAAc,OAAO,CAAA,CAAE,CAAA;AACjC,EAAA,GAAA,CAAI,GAAG,iCAAiC,CAAA;AACxC,EAAA,IAAI,MAAA,KAAW,SAAA,EAAW,GAAA,CAAI,EAAA,CAAG,kDAAkD,CAAA;AACnF,EAAA,GAAA,CAAI,KAAK,wDAAwD,CAAA;AACjE,EAAA,OAAO,CAAA;AACT","file":"upgrade-7ZXMCTTR.js","sourcesContent":["import { generateArtifacts } from \"../core/generate\"\nimport { scanProject } from \"../core/scan\"\nimport { syncAgentsFile } from \"./add\"\nimport { frameworkVersion, log, printJson, type ParsedArgs } from \"./util\"\n\n/**\n * `vantage upgrade` — reconcile generated artifacts with the installed\n * framework version and apply any needed code transforms. v0.1 only\n * regenerates artifacts and re-syncs a managed AGENTS.md + agents/*.md; the\n * migration hook exists for future major versions.\n */\nexport async function upgrade(root: string, args: ParsedArgs): Promise<number> {\n const version = frameworkVersion()\n const model = scanProject(root)\n generateArtifacts({ root, model })\n\n // Keep an adopted AGENTS.md + agents/*.md (the app-author map and its\n // per-artifact-type knowledge files) in step with the framework's canonical\n // copies. No-op when the app never ran `vantage add agents`.\n const agents = syncAgentsFile(root)\n\n if (args.flags.json) {\n printJson({ ok: true, version, transforms: [], regenerated: true, agentsSynced: agents === \"updated\" })\n return 0\n }\n\n log.brand(`upgrade → v${version}`)\n log.ok(\"Regenerated .vantage artifacts.\")\n if (agents === \"updated\") log.ok(\"Synced AGENTS.md and agents/ from the framework.\")\n log.info(\"No breaking code transforms required for this version.\")\n return 0\n}\n"]}
|
package/dist/vite/index.js
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
|
-
export { vantagePlugin } from '../chunk-
|
|
2
|
-
import '../chunk-
|
|
3
|
-
import '../chunk-
|
|
1
|
+
export { vantagePlugin } from '../chunk-7652CGJJ.js';
|
|
2
|
+
import '../chunk-R6MTJAHX.js';
|
|
3
|
+
import '../chunk-S5MDTHH7.js';
|
|
4
4
|
import '../chunk-RNTDBX6S.js';
|
|
5
5
|
import '../chunk-46QI6GFC.js';
|
|
6
6
|
import '../chunk-B7LLBNLV.js';
|
package/docs/en/changelog.md
CHANGED
|
@@ -15,6 +15,8 @@ The dev server can now run behind a reverse proxy (2026-09-23).
|
|
|
15
15
|
|
|
16
16
|
### Added
|
|
17
17
|
|
|
18
|
+
- **App display language `lang`.** `export const lang = "ja"` (a literal) in the root `_layout.tsx` becomes `<html lang>`.
|
|
19
|
+
Japanese slides and docs no longer break mid-phrase, and the slide toolbar switches to Japanese. Unset stays `"en"`.
|
|
18
20
|
- **`--host [addr]` and `--allowed-hosts <list|true>`** on `vantage dev` / SPA `vantage preview`.
|
|
19
21
|
Vite rejects with 403 any request whose `Host` header is not `localhost`, so anything with
|
|
20
22
|
**another hostname in front of the server** — a sandbox preview proxy, a tunnel — could not reach
|
package/docs/en/components.md
CHANGED
|
@@ -2,14 +2,16 @@
|
|
|
2
2
|
|
|
3
3
|
> Reference for the bundled shadcn/ui (Base UI) kit and the composite dashboard components.
|
|
4
4
|
|
|
5
|
-
Vantage ships **shadcn/ui** as-is. Nothing to install, nothing to copy — just import. There are
|
|
6
|
-
public entry points, and **the sidebar is sectioned by those
|
|
5
|
+
Vantage ships **shadcn/ui** as-is. Nothing to install, nothing to copy — just import. There are five
|
|
6
|
+
public entry points, and **the sidebar is sectioned by those five** (`/slide` and `/doc` hold the blocks for slide and document pages).
|
|
7
7
|
|
|
8
8
|
| Subpath | What's in it | Count |
|
|
9
9
|
| --- | --- | --- |
|
|
10
10
|
| [`@squadbase/vantage/ui`](ui/button) | The shadcn/ui primitives (Base UI build) plus utilities like `cn()` | 37 |
|
|
11
11
|
| [`@squadbase/vantage/components`](parts/page-shell) | Dashboard parts composed from them | 15 |
|
|
12
12
|
| [`@squadbase/vantage/markdown`](markdown/markdown-renderer) | Markdown rendering alone (kept apart — it's heavy) | 1 |
|
|
13
|
+
| [`@squadbase/vantage/slide`](slide/slide-cover) | Blocks for the content of one slide in a `.slide.tsx` (7: cover, summary, KPI, chart, scorecard, next actions) | 7 |
|
|
14
|
+
| [`@squadbase/vantage/doc`](doc/doc-report-header) | Section and part blocks placed inside `<Document>` in a `.doc.tsx` | 8 |
|
|
13
15
|
|
|
14
16
|
```tsx
|
|
15
17
|
import { Button, Card, Badge } from "@squadbase/vantage/ui";
|
|
@@ -148,6 +150,15 @@ A subpath holding only [`MarkdownRenderer`](markdown/markdown-renderer). Markdow
|
|
|
148
150
|
rendering carries Shiki with every language grammar; mixed into the `components` barrel it would add
|
|
149
151
|
over 10 MB to every build, so it is deliberately kept apart.
|
|
150
152
|
|
|
153
|
+
## `@squadbase/vantage/slide` and `@squadbase/vantage/doc`
|
|
154
|
+
|
|
155
|
+
Blocks for [slide](page-types/slide) and [document](page-types/doc) pages. They live on their own subpaths, apart from `/components`, and `vantage add` does not copy them — import and use them.
|
|
156
|
+
|
|
157
|
+
- **slide**: the content of one `<Slide>` (not `<Slide>` itself). [`SlideCover`](slide/slide-cover) / [`SlideExecSummary`](slide/slide-exec-summary) / [`SlideKpiHero`](slide/slide-kpi-hero) / [`SlideKpiGrid`](slide/slide-kpi-grid) / [`SlideChartInsight`](slide/slide-chart-insight) / [`SlideScorecardTable`](slide/slide-scorecard-table) / [`SlideNextActions`](slide/slide-next-actions) / [`SlideSection`](slide/slide-section) / [`SlideComparison`](slide/slide-comparison) / [`SlideChartFull`](slide/slide-chart-full)
|
|
158
|
+
- **slide parts**: parts used inside a slide (not whole slides). [`SlideHeading`](slide/slide-heading) / [`SlideSourceLine`](slide/slide-source-line) / [`SlideColumns`, `SlideColumn`](slide/slide-columns)
|
|
159
|
+
- **doc**: sections and parts placed inside `<Document>` (`DocFigure` / `DocDataTable` / `DocCallout` go inside a section). [`DocReportHeader`](doc/doc-report-header) / [`DocExecutiveSummary`](doc/doc-executive-summary) / [`DocKpiScorecard`](doc/doc-kpi-scorecard) / [`DocFigure`](doc/doc-figure) / [`DocDataTable`](doc/doc-data-table) / [`DocCallout`](doc/doc-callout) / [`DocKeyFindings`](doc/doc-key-findings) / [`DocActionItems`](doc/doc-action-items) / [`DocNotesAndSources`](doc/doc-notes-and-sources)
|
|
160
|
+
- **doc parts**: a part placed inside a sentence (also usable in slides). [`DeltaText`](doc/delta-text)
|
|
161
|
+
|
|
151
162
|
## Putting it together
|
|
152
163
|
|
|
153
164
|
```tsx
|
|
@@ -0,0 +1,40 @@
|
|
|
1
|
+
# DeltaText
|
|
2
|
+
|
|
3
|
+
> [Part] An inline change — ▲ ▼ and a signed value — for documents and slides.
|
|
4
|
+
|
|
5
|
+
A change as a bare inline `span`: a mark plus a signed value ("▲ +3.2%"), so print and colour-blind readers get the direction; colour only adds good or bad. It sets no text size and takes the size of the line it sits in. It is a **part** usable in both documents and slides, not a section or a slide block.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DeltaText } from "@squadbase/vantage/doc";
|
|
9
|
+
import type { DeltaTextProps } from "@squadbase/vantage/doc";
|
|
10
|
+
// The same part is available from the slide subpath
|
|
11
|
+
// import { DeltaText } from "@squadbase/vantage/slide";
|
|
12
|
+
```
|
|
13
|
+
|
|
14
|
+
> [!NOTE]
|
|
15
|
+
> This is **a part to place inside a sentence**. It has no dimensions or text size of its own and follows the line it sits in. It is separate from the dashboard `Delta`.
|
|
16
|
+
|
|
17
|
+
## Props
|
|
18
|
+
|
|
19
|
+
| Prop | Type | Default | Description |
|
|
20
|
+
| --- | --- | --- | --- |
|
|
21
|
+
| `value` | `number \| null` | | The change itself; its direction comes from the sign. `null` shows 「—」. **Required.** |
|
|
22
|
+
| `format` | `ValueFormat` | | How to format the value (`"percent"`, `"currency"`, …). Printed with an explicit sign. |
|
|
23
|
+
| `betterWhen` | `"up" \| "down"` | `"up"` | Which direction is good; decides the colour. Use `"down"` for churn and cost. |
|
|
24
|
+
| `comparisonLabel` | `string` | | Name of the comparison ("vs last month"). Printed before the change, muted. |
|
|
25
|
+
|
|
26
|
+
## Usage
|
|
27
|
+
|
|
28
|
+
```tsx
|
|
29
|
+
import { DeltaText } from "@squadbase/vantage/doc";
|
|
30
|
+
|
|
31
|
+
<p>
|
|
32
|
+
Revenue in September was ¥540M (<DeltaText value={0.074} format="percent" comparisonLabel="vs last month" />).
|
|
33
|
+
Churn is 1.8% (<DeltaText value={-0.006} format="percent" betterWhen="down" comparisonLabel="vs last month" />).
|
|
34
|
+
</p>
|
|
35
|
+
```
|
|
36
|
+
|
|
37
|
+
## Notes
|
|
38
|
+
|
|
39
|
+
- Zero is shown as 「→」 in the muted colour.
|
|
40
|
+
- The spoken words (「増」, 「減」, 「横ばい」, 「改善」, 「悪化」) in the accessible label are fixed in Japanese.
|
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
# DocActionItems
|
|
2
|
+
|
|
3
|
+
> What to do next — action, owner, due date, expected effect and priority.
|
|
4
|
+
|
|
5
|
+
The recommendations section: action, owner, due date, expected effect and priority. The default `table` is a table; `asks` is a short bulleted list of requests to the reader (introductions, a decision, a hire). Priority is only the word 高 / 中 / 低, with no badge and no colour.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DocActionItems } from "@squadbase/vantage/doc";
|
|
9
|
+
import type { DocActionItemsProps, DocActionItem, DocActionPriority } from "@squadbase/vantage/doc";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> This is **one section to place directly under `<Document>`**. The document sets heading and body sizes and the width, so no dimensions are set here. `action` is a `ReactNode`; Markdown strings are not parsed. The table headings ("対応", "担当", "期限", "期待する効果", "優先度"), the priority words (高 / 中 / 低) and the "まで" after a due date in `asks` are fixed in Japanese and cannot be overridden. Write `action`, `owner`, `due` and `impact` in English yourself; the default heading "推奨する対応" is Japanese, so pass `title`. For the whole page, see [Document](page-types/doc).
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Default | Description |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `title` | `string` | `"推奨する対応"` | Heading text. |
|
|
20
|
+
| `headingLevel` | `2 \| 3` | `2` | Heading level: 2 directly under `<Document>`, 3 inside another section. |
|
|
21
|
+
| `items` | `DocActionItem[]` | | Actions, most important first. **Required.** |
|
|
22
|
+
| `items[].action` | `ReactNode` | | What to do. **Required.** |
|
|
23
|
+
| `items[].owner` | `string` | | Owner. |
|
|
24
|
+
| `items[].due` | `string` | | Due date, shown as written ("2026-11-15", "11 月末"). |
|
|
25
|
+
| `items[].impact` | `string` | | Expected effect ("解約率 −0.5pt"). Shown in the `table` only. |
|
|
26
|
+
| `items[].priority` | `"high" \| "medium" \| "low"` | | Priority, printed as the word 高 / 中 / 低 (高 in semibold). Shown in the `table` only. |
|
|
27
|
+
| `variant` | `"table" \| "asks"` | `"table"` | A table, or a bulleted list of requests to the reader. |
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Document, DocActionItems } from "@squadbase/vantage/doc";
|
|
33
|
+
|
|
34
|
+
<Document>
|
|
35
|
+
<DocActionItems
|
|
36
|
+
items={[
|
|
37
|
+
{ action: "法人プランの導入支援を増員する", owner: "CS 部", due: "2026-11-15", impact: "解約率 −0.5pt", priority: "high" },
|
|
38
|
+
{ action: "紹介プログラムの特典を見直す", owner: "マーケティング部", due: "11 月末", impact: "紹介経由の新規 +10 社", priority: "medium" },
|
|
39
|
+
{ action: "料金ページの文言を改善する", owner: "プロダクト部", priority: "low" },
|
|
40
|
+
]}
|
|
41
|
+
/>
|
|
42
|
+
</Document>
|
|
43
|
+
```
|
|
44
|
+
|
|
45
|
+
## Notes
|
|
46
|
+
|
|
47
|
+
- `due` is shown as written ("2026-11-15", "11 月末"). A missing owner, due or impact shows "—".
|
|
48
|
+
- `variant="asks"` adds only the owner and "`due` まで" (e.g. "(CS 部・11 月末 まで)"); the effect and priority are not shown.
|
|
49
|
+
- When printed, each item of `asks` stays on one page and does not break across pages.
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
# DocCallout
|
|
2
|
+
|
|
3
|
+
> A boxed remark inside the text — a note, insight, warning or decision.
|
|
4
|
+
|
|
5
|
+
A box for the one thing a reader must not miss — a `div` with `role="note"` and a left rule in the kind's semantic colour (a decision is neutral). The kind also prints as a word ("注意", "示唆"), so colour is never the only signal. Don't use it for decoration.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DocCallout } from "@squadbase/vantage/doc";
|
|
9
|
+
import type { DocCalloutProps, DocCalloutKind } from "@squadbase/vantage/doc";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> This is **a part to place inside body text or a section**, not a section of its own directly under `<Document>`. The document sets heading and body sizes and the width, so no dimensions are set here. `children` is a `string` or a `ReactNode`; Markdown strings are not parsed. The kind words ("補足", "示唆", "注意", "決定事項") are fixed in Japanese and cannot be overridden, so an English callout reads "注意: your title". Write `title` in English. For the whole page, see [Document](page-types/doc).
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Default | Description |
|
|
18
|
+
| --- | --- | --- | --- |
|
|
19
|
+
| `kind` | `"note" \| "insight" \| "warning" \| "decision"` | `"note"` | Kind: note (補足) / insight (示唆) / warning (注意) / decision (決定事項). |
|
|
20
|
+
| `title` | `string` | | Heading of the box, one short line. When given, it prints as "<kind word>: <title>". Omit it and only the kind word shows. A title equal to the kind word is shown once. |
|
|
21
|
+
| `children` | `ReactNode` | | The body. A `string` becomes a paragraph. Markdown strings are not parsed. **Required.** |
|
|
22
|
+
|
|
23
|
+
## Usage
|
|
24
|
+
|
|
25
|
+
```tsx
|
|
26
|
+
import { Document, DocCallout } from "@squadbase/vantage/doc";
|
|
27
|
+
|
|
28
|
+
<Document>
|
|
29
|
+
<DocCallout kind="warning" title="9 月の数値は速報値">
|
|
30
|
+
10 月 10 日の確定で変わることがあります。
|
|
31
|
+
</DocCallout>
|
|
32
|
+
</Document>
|
|
33
|
+
```
|
|
34
|
+
|
|
35
|
+
## Notes
|
|
36
|
+
|
|
37
|
+
- The kind word is added by the block; do not repeat it in `title`, which should hold only the heading itself.
|
|
38
|
+
- When printed, it stays on one page and does not break across pages.
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
# DocDataTable
|
|
2
|
+
|
|
3
|
+
> A table with the notes a printed table needs — title, unit, total row, footnotes and source.
|
|
4
|
+
|
|
5
|
+
A table with what a printed table needs: title, unit, right-aligned numbers, a total row, highlighted rows, footnotes and a source line. It is a plain `table` styled by `<Document>`, which also keeps rows together when printed. For screens that need sorting or scrolling, use `DataTable`.
|
|
6
|
+
|
|
7
|
+
```tsx
|
|
8
|
+
import { DocDataTable } from "@squadbase/vantage/doc";
|
|
9
|
+
import type { DocDataTableProps, DocTableColumn, DocTableRow, DocTableCell } from "@squadbase/vantage/doc";
|
|
10
|
+
```
|
|
11
|
+
|
|
12
|
+
> [!NOTE]
|
|
13
|
+
> This is **a part to place inside body text or a section**, not a section of its own directly under `<Document>`. The document sets heading and body sizes and the width, so no dimensions are set here. A cell is a string or a number; a `ReactNode` is not accepted. The caption labels "出典" and "データ時点" are fixed in Japanese and cannot be overridden; column labels, `title`, `unit` and footnotes are yours, so write them in English. For the whole page, see [Document](page-types/doc).
|
|
14
|
+
|
|
15
|
+
## Props
|
|
16
|
+
|
|
17
|
+
| Prop | Type | Description |
|
|
18
|
+
| --- | --- | --- |
|
|
19
|
+
| `title` | `string` | Table title, as a caption above the table. |
|
|
20
|
+
| `unit` | `string` | Unit or scale ("単位: 百万円"). With a title it follows in parentheses. |
|
|
21
|
+
| `columns` | `DocTableColumn[]` | Columns (`{ key; label; format?: ValueFormat; align?: "left" \| "right" \| "center" }`). Without `format`, numbers use a plain thousands format. Without `align`, a column of numbers is right-aligned and others left-aligned. **Required.** |
|
|
22
|
+
| `rows` | `DocTableRow[]` | Rows: objects keyed by the `key` of `columns`. A cell is a string or a number (`null`, `undefined` and an empty string show "—"). **Required.** |
|
|
23
|
+
| `total` | `DocTableRow` | The total row (`tfoot`), keyed like `rows`. |
|
|
24
|
+
| `highlightRows` | `number[]` | Indexes of the rows to set in semibold (zero-based, indexes into `rows`). |
|
|
25
|
+
| `footnotes` | `{ mark: string; text: string }[]` | Footnotes. Write the same `mark` ("※1") in a cell string to point at one. |
|
|
26
|
+
| `source` | `string` | Source. Shown as "出典: …" in the caption. |
|
|
27
|
+
| `asOf` | `string` | The date the data was taken, as a date only ("2026-10-03"). The block adds the label "データ時点". |
|
|
28
|
+
|
|
29
|
+
## Usage
|
|
30
|
+
|
|
31
|
+
```tsx
|
|
32
|
+
import { Document, DocDataTable } from "@squadbase/vantage/doc";
|
|
33
|
+
|
|
34
|
+
<Document>
|
|
35
|
+
<DocDataTable
|
|
36
|
+
title="部門別の売上"
|
|
37
|
+
unit="単位: 百万円"
|
|
38
|
+
columns={[
|
|
39
|
+
{ key: "dept", label: "部門" },
|
|
40
|
+
{ key: "sales", label: "売上", format: "number" },
|
|
41
|
+
{ key: "share", label: "構成比", format: { type: "percent", digits: 1 } },
|
|
42
|
+
]}
|
|
43
|
+
rows={[
|
|
44
|
+
{ dept: "法人営業", sales: 312, share: 0.58 },
|
|
45
|
+
{ dept: "パートナー", sales: 148, share: 0.27 },
|
|
46
|
+
{ dept: "オンライン ※1", sales: 80, share: 0.15 },
|
|
47
|
+
]}
|
|
48
|
+
total={{ dept: "合計", sales: 540, share: 1 }}
|
|
49
|
+
highlightRows={[0]}
|
|
50
|
+
footnotes={[{ mark: "※1", text: "9 月から集計対象に追加" }]}
|
|
51
|
+
source="会計システム"
|
|
52
|
+
asOf="2026-10-03"
|
|
53
|
+
/>
|
|
54
|
+
</Document>
|
|
55
|
+
```
|
|
56
|
+
|
|
57
|
+
## Notes
|
|
58
|
+
|
|
59
|
+
- Numbers line up with `tabular-nums`, and a missing cell shows "—".
|
|
60
|
+
- When printed, a row does not break across pages. A long table may break between rows, and the caption (footnotes and source) stays on one page.
|