@malloy-publisher/server 0.9.15 → 0.9.16
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/app/api-doc.yaml +0 -2
- package/dist/app/assets/{EnvironmentPage-FClzTET1.js → EnvironmentPage-BfcAd2yi.js} +1 -1
- package/dist/app/assets/{HomePage-DY8r_SPS.js → HomePage-Ci_5k81i.js} +1 -1
- package/dist/app/assets/{MainPage-COdVBhUS.js → MainPage-D3QeyfA0.js} +1 -1
- package/dist/app/assets/{ModelPage-DpS3jA5N.js → ModelPage-Bpeml30i.js} +2 -2
- package/dist/app/assets/{PackagePage-CS5ePUwy.js → PackagePage-C9Ptmg2y.js} +1 -1
- package/dist/app/assets/RenderedResult-DxmHGZNQ.es-D0Ona1d0.js +171 -0
- package/dist/app/assets/{RouteError-BnzLDEen.js → RouteError-C8LT19mj.js} +1 -1
- package/dist/app/assets/{ThemeEditorPage-CjaJvkiM.js → ThemeEditorPage-CynRJmSm.js} +1 -1
- package/dist/app/assets/{ToggleButtonGroup-DpS5j6nj.js → ToggleButtonGroup-ChqidtS2.js} +1 -1
- package/dist/app/assets/{documentRoutes-BGPsDJa0.js → documentRoutes-DZ-IkgZk.js} +1 -1
- package/dist/app/assets/{editPageMounted-C8_ZEAFO.js → editPageMounted-BaIkidNI.js} +2 -2
- package/dist/app/assets/{index-D6CnUO9j.js → index-CIAQd_g-.js} +1 -1
- package/dist/app/assets/{index-Bohg2UjZ.js → index-CtRRADnY.js} +1 -1
- package/dist/app/assets/{index-BLeRYy_z.js → index-Cu8EElmh.js} +1 -1
- package/dist/app/assets/{index-JQs0UPrG.js → index-Dzz5PLbw.js} +80 -80
- package/dist/app/assets/index.es-DvW5H23D.js +144 -0
- package/dist/app/index.html +1 -1
- package/dist/instrumentation.mjs +1 -1
- package/dist/server.mjs +7 -46
- package/package.json +1 -1
- package/dist/app/assets/RenderedResult-YBzVMVH2.es-CzJTpX_s.js +0 -154
- package/dist/app/assets/index.es-JmXuGuMS.js +0 -144
package/dist/app/index.html
CHANGED
|
@@ -16,7 +16,7 @@ SPDX-License-Identifier: MIT
|
|
|
16
16
|
href="https://fonts.googleapis.com/css2?family=Inter:wght@400;500;600;700&family=JetBrains+Mono:wght@400;500&display=swap"
|
|
17
17
|
/>
|
|
18
18
|
<title>Malloy Publisher</title>
|
|
19
|
-
<script type="module" crossorigin src="/assets/index-
|
|
19
|
+
<script type="module" crossorigin src="/assets/index-Dzz5PLbw.js"></script>
|
|
20
20
|
<link rel="stylesheet" crossorigin href="/assets/index-DUqldpo0.css">
|
|
21
21
|
</head>
|
|
22
22
|
<body>
|
package/dist/instrumentation.mjs
CHANGED
|
@@ -167049,7 +167049,7 @@ async function withSpan(name, attributes, fn, spanTracer = tracer()) {
|
|
|
167049
167049
|
});
|
|
167050
167050
|
}
|
|
167051
167051
|
// package.json
|
|
167052
|
-
var version = "0.9.
|
|
167052
|
+
var version = "0.9.16";
|
|
167053
167053
|
|
|
167054
167054
|
// src/version.ts
|
|
167055
167055
|
var SERVER_VERSION = version;
|
package/dist/server.mjs
CHANGED
|
@@ -328879,7 +328879,7 @@ async function withSpan(name, attributes, fn, spanTracer = tracer()) {
|
|
|
328879
328879
|
});
|
|
328880
328880
|
}
|
|
328881
328881
|
// package.json
|
|
328882
|
-
var version = "0.9.
|
|
328882
|
+
var version = "0.9.16";
|
|
328883
328883
|
|
|
328884
328884
|
// src/version.ts
|
|
328885
328885
|
var SERVER_VERSION = version;
|
|
@@ -353468,7 +353468,6 @@ function assertFilterGivensParse(declared, givens) {
|
|
|
353468
353468
|
|
|
353469
353469
|
// src/service/report_manifest.ts
|
|
353470
353470
|
var MAX_BUILDER_COLUMNS = 24;
|
|
353471
|
-
var COMPONENT_FILE_SUFFIXES = [".jsx", ".tsx"];
|
|
353472
353471
|
function queryTiles(tiles) {
|
|
353473
353472
|
return (tiles ?? []).filter((tile) => tile.kind === "query");
|
|
353474
353473
|
}
|
|
@@ -353511,11 +353510,11 @@ var ARTIFACT_PROPERTIES = [
|
|
|
353511
353510
|
"tiles",
|
|
353512
353511
|
"givens",
|
|
353513
353512
|
"autorun",
|
|
353514
|
-
"layout"
|
|
353515
|
-
"dashboard_columns"
|
|
353513
|
+
"layout"
|
|
353516
353514
|
];
|
|
353517
353515
|
var ARTIFACT_PROPERTY_REPLACEMENTS = {
|
|
353518
|
-
dashboard: "The grid width is a sibling of the artifact tag, not a property of it: " + "close the artifact braces first, as `## artifact { … } dashboard { columns=N }`."
|
|
353516
|
+
dashboard: "The grid width is a sibling of the artifact tag, not a property of it: " + "close the artifact braces first, as `## artifact { … } dashboard { columns=N }`.",
|
|
353517
|
+
dashboard_columns: "The grid width is written beside the artifact tag: " + "`## artifact { … } dashboard { columns=N }`."
|
|
353519
353518
|
};
|
|
353520
353519
|
var ENTRY_LAYOUT_PROPERTIES = ["row", "col", "colspan", "rowspan", "break"];
|
|
353521
353520
|
var PLACED_DEFAULT_ROWSPAN = { query: 5, text: 2 };
|
|
@@ -353560,7 +353559,7 @@ function readArtifactTag(tag, declaredType) {
|
|
|
353560
353559
|
title: tagText(artifact, "title"),
|
|
353561
353560
|
kind: tagText(artifact, "kind"),
|
|
353562
353561
|
tiles: readTileEntries(artifact),
|
|
353563
|
-
dashboardColumns: tag.tag("dashboard")?.has("columns") ? positiveInteger(tagNumeric(tag.tag("dashboard"), "columns")) :
|
|
353562
|
+
dashboardColumns: tag.tag("dashboard")?.has("columns") ? positiveInteger(tagNumeric(tag.tag("dashboard"), "columns")) : undefined,
|
|
353564
353563
|
givens,
|
|
353565
353564
|
autorun
|
|
353566
353565
|
};
|
|
@@ -365942,7 +365941,7 @@ For anything not covered here, call \`search_malloy_docs\` with the topic (for e
|
|
|
365942
365941
|
{
|
|
365943
365942
|
name: "malloy-reports/grid",
|
|
365944
365943
|
description: "Grid layout. Reference detail for the malloy-reports skill.",
|
|
365945
|
-
body: "# Grid layout\n\nA report with `layout=grid` places its tiles across `dashboard { columns=N }` by each tile's `# colspan` and `# break`. Everything not about placement (tiles, text tiles, givens, drill, lint) is in the skill itself.\n\n## The four tags that make a page line up\n\nCards and tiles share one grid. Publisher reads these tags off the view each tile in `tiles=[…]` names. Copy this recipe and use the same count on every grid report in the package so they read as one product:\n\n1. **`columns=12`** on the `dashboard { … }` tag after the artifact tag. Twelve divides by 2, 3, 4 and 6, so a row is even with three cards or four. The builder has no grid-width setting; set it in the file.\n2. **A `# colspan` on every tile, summing to 12 per row.** Four cards at 3, three at 4, two tiles at 6, a full-width table at 12. Omit them and every item falls to a single column, a twelfth of the width, too narrow for a line chart to draw in at all. A colspan wider than `columns` is clamped, and the package warnings say so.\n3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps on its own.\n4. **`# label=\"...\"` on every tile view and aggregate**, including the aggregates inside a table view, whose column headers are field names too. A view referenced **by name** is the exception: you can label the tile, but its own field names still reach the axes and column headers, so label the fields in the view if you want those too.\n\nAll four go on the view a tile names, which is why a thin re-declaration (`view: revenue_trend is sales_by_month`) is the place to put them: the modelled view stays reusable and each report decides its own widths. `# subtitle=\"...\"` and `# borderless` go there too; a tile reads all five. Any tile's entry in `tiles=[…]` can take `colspan`, `break` and `rowspan` directly, and wins over the view's tags: `\"overview -> kpis\" { colspan=12 rowspan=2 }`, `intro { kind=text colspan=6 break }`. That is how one view sits at two widths on two reports, how a tile on an imported view is laid out, and the only way to set a height: `rowspan=N` is N rows of 80px plus the 16px gaps between them, so `rowspan=5` is 464px. Without one a tile is as tall as its result (up to 400px); a chart fills a set height, a table scrolls in it, and a map keeps its own size. `break=false` turns off a view's `# break`. `row=N col=N` on entries place tiles at grid cells: once any tile has a `row` the report is placed, its rows are fixed 80px tracks, tiles sit exactly where they say (so one can sit under a short neighbour beside a tall one), a missing `rowspan` is 5 rows (text 2), and phones stack tiles in reading order. The Console builder writes layout on the entry, and its moves place tiles.\n\n**Do not forget `columns=` itself.** Without it the grid falls back to a narrow default and every colspan is clamped to it, which is the usual reason a page comes out one item per row; the package warnings name it. `dashboard_columns=N` inside the artifact tag is
|
|
365944
|
+
body: "# Grid layout\n\nA report with `layout=grid` places its tiles across `dashboard { columns=N }` by each tile's `# colspan` and `# break`. Everything not about placement (tiles, text tiles, givens, drill, lint) is in the skill itself.\n\n## The four tags that make a page line up\n\nCards and tiles share one grid. Publisher reads these tags off the view each tile in `tiles=[…]` names. Copy this recipe and use the same count on every grid report in the package so they read as one product:\n\n1. **`columns=12`** on the `dashboard { … }` tag after the artifact tag. Twelve divides by 2, 3, 4 and 6, so a row is even with three cards or four. The builder has no grid-width setting; set it in the file.\n2. **A `# colspan` on every tile, summing to 12 per row.** Four cards at 3, three at 4, two tiles at 6, a full-width table at 12. Omit them and every item falls to a single column, a twelfth of the width, too narrow for a line chart to draw in at all. A colspan wider than `columns` is clamped, and the package warnings say so.\n3. **`# break` on the first tile after the cards.** Otherwise it flows into the columns left beside the cards and the next tile wraps. Not needed per row: once a row sums to 12 the next item wraps on its own.\n4. **`# label=\"...\"` on every tile view and aggregate**, including the aggregates inside a table view, whose column headers are field names too. A view referenced **by name** is the exception: you can label the tile, but its own field names still reach the axes and column headers, so label the fields in the view if you want those too.\n\nAll four go on the view a tile names, which is why a thin re-declaration (`view: revenue_trend is sales_by_month`) is the place to put them: the modelled view stays reusable and each report decides its own widths. `# subtitle=\"...\"` and `# borderless` go there too; a tile reads all five. Any tile's entry in `tiles=[…]` can take `colspan`, `break` and `rowspan` directly, and wins over the view's tags: `\"overview -> kpis\" { colspan=12 rowspan=2 }`, `intro { kind=text colspan=6 break }`. That is how one view sits at two widths on two reports, how a tile on an imported view is laid out, and the only way to set a height: `rowspan=N` is N rows of 80px plus the 16px gaps between them, so `rowspan=5` is 464px. Without one a tile is as tall as its result (up to 400px); a chart fills a set height, a table scrolls in it, and a map keeps its own size. `break=false` turns off a view's `# break`. `row=N col=N` on entries place tiles at grid cells: once any tile has a `row` the report is placed, its rows are fixed 80px tracks, tiles sit exactly where they say (so one can sit under a short neighbour beside a tall one), a missing `rowspan` is 5 rows (text 2), and phones stack tiles in reading order. The Console builder writes layout on the entry, and its moves place tiles.\n\n**Do not forget `columns=` itself.** Without it the grid falls back to a narrow default and every colspan is clamped to it, which is the usual reason a page comes out one item per row; the package warnings name it. `dashboard_columns=N` inside the artifact tag is not read (it is reported, with the fix). Any other property inside the artifact tag that Publisher does not read is a package warning naming it.\n\n## The traps\n\n- **A KPI row is a view of only measures.** A tile is one whole result, so a tile that comes back as one row of measures renders as big-value cards on its own (`# big_value` on the view says so explicitly; `# table` opts out).\n- **No `# size=fill` on a grid tile.** It measures against the container the whole grid was handed, not the tile, so it yields a chart thousands of pixels tall. Tiles already size to their colspan.\n- **A table tile sitting in the left part of a wide card with the rest blank** is not filled by the grid. `# table { size=fill }` on the view forces it. That is the table's own tag, not the chart `# size=fill` above.\n- **A KPI card's label is one line that ellipses** rather than wrapping. Widen the card or shorten the label.\n- **A ratio needs a number format.** `order_count / customer_count` renders as `10.695` on a card; `# number=\"#,##0.0\"` is the precision it carries.\n- **A `# shape_map` legend is titled with the measure's field _name_, not its `# label`.** Rename it in the view: `aggregate: revenue is total_sales`. Renaming drops the measure's format tags, so re-tag `# currency` at the rename.\n- **A series legend sizes itself from the longer of the series label and its widest value**, then truncates both. A 4-character label over 4-digit years clips to `20…`; `# label=\"Order year\"` instead of `\"Year\"` buys the room.\n\nThe last two are upstream renderer behavior, cheap to work around in the model. Height is the one thing the surface decides: a grid caps each tile.\n\n## Lint findings specific to a grid\n\n- A non-positive `dashboard { columns }`, or one above 24.\n- A tile view's `# colspan` that is not a positive integer, or is wider than the grid (clamped).\n- A `dashboard_columns=` inside the artifact tag, which is not read.\n- A tile entry whose `colspan` or `rowspan` is not a positive integer, or that carries a property other than `row`, `col`, `colspan`, `rowspan` and `break`; an entry `colspan` wider than the grid; a placed tile past the right edge, or two placed tiles covering each other.\n\n## One query as the whole page\n\nA `# dashboard` query lays out its one result itself: top-level `aggregate:` measures are KPI cards, each `nest:` a tile. List it as the report's one tile, a model-level named query:\n\n```malloy\n## artifact { layout=grid title=\"Overview\" givens { REGION=f'US' } tiles=[\"overview\"] }\n# dashboard { columns=12 }\nquery: overview is orders -> { … }\n```\n\nA `# artifact` on the `query:` itself (the older single-query form) is not read: the file is an ordinary model. Move the tag to model level as above.\n\n- It cannot span sources, and a guessed field inside it fails the whole package load, where a bad tile fails alone.\n- A top-level `aggregate:` there is the KPI card; do not nest a `# big_value` view for it (that renders each measure as a full-width bar in one tile). Nest views for tiles; give each a `# colspan` and the first tile after the cards a `# break`. `# dashboard { gap=N table { max_height=N | none } }` are options only a `# dashboard` query reads.\n- **One plain nested table, every `# colspan` and `# break` dropped**: a `f'…'` filter literal in a `givens { … }` block shares a line with `# dashboard`. Put `# dashboard` on its own line.\n- **Nothing lines up**: `# colspan` without `columns=N`, so items flow at their natural widths.\n\nTo tell those two apart, run the tile, `{\"query\": \"run: overview\"}`, and read `renderLogs` on the response: `Unknown render tag 'colspan'` means the renderer never saw a `# dashboard` tag (check both causes); `Ignored # colspan … only applies in columns mode` means it saw the tag but no count. Neither reaches the package warnings."
|
|
365946
365945
|
},
|
|
365947
365946
|
{
|
|
365948
365947
|
name: "malloy-reports/linear",
|
|
@@ -384575,7 +384574,6 @@ function lintNotebookText(modelPath, text) {
|
|
|
384575
384574
|
const tag2 = motlyTag([tagNote.text])?.tag("artifact");
|
|
384576
384575
|
if (!tag2)
|
|
384577
384576
|
return;
|
|
384578
|
-
const properties = Object.keys(tag2.dict ?? {});
|
|
384579
384577
|
lintLayout(tagNote.line, tagText(tag2, "layout"));
|
|
384580
384578
|
if (linear) {
|
|
384581
384579
|
for (const entry of tag2.array("tiles") ?? []) {
|
|
@@ -384598,16 +384596,6 @@ function lintNotebookText(modelPath, text) {
|
|
|
384598
384596
|
add(tagNote.line, "notebook-columns-ignored", "`dashboard { columns }` other than 1 is ignored on a notebook, which is always one column. Fix: remove `dashboard { columns }`.");
|
|
384599
384597
|
}
|
|
384600
384598
|
}
|
|
384601
|
-
if (properties.includes("dashboard_columns") && properties.includes("tiles")) {
|
|
384602
|
-
const alias = tagText(tag2, "dashboard_columns") ?? "";
|
|
384603
|
-
const dashboardTag = motlyTag(modelNotes)?.tag("dashboard");
|
|
384604
|
-
const canonical = dashboardTag?.has("columns") ? tagText(dashboardTag, "columns") ?? "" : undefined;
|
|
384605
|
-
if (canonical !== undefined && tagNumeric(tag2, "dashboard_columns") !== tagNumeric(dashboardTag, "columns")) {
|
|
384606
|
-
add(tagNote.line, "notebook-columns-conflict", `\`dashboard_columns=${alias}\` in the artifact tag and \`dashboard { columns=${canonical} }\` disagree about the grid width. Fix: keep \`dashboard { columns=… }\` and remove \`dashboard_columns\`.`, "error");
|
|
384607
|
-
} else {
|
|
384608
|
-
add(tagNote.line, "notebook-columns-alias", `\`dashboard_columns=${alias}\` is a deprecated spelling of the grid width. Fix: write \`dashboard { columns=${alias} }\` instead.`);
|
|
384609
|
-
}
|
|
384610
|
-
}
|
|
384611
384599
|
}
|
|
384612
384600
|
}
|
|
384613
384601
|
function reportedByDashboardLint(finding, modelPath, dashboardFindings) {
|
|
@@ -384744,7 +384732,6 @@ var PREAGG_WARN_ONLY = new Set([
|
|
|
384744
384732
|
"non_public_measure",
|
|
384745
384733
|
"grain_dimension_not_public"
|
|
384746
384734
|
]);
|
|
384747
|
-
var MALLOYYO_DASHBOARDS_DIR = "dashboards";
|
|
384748
384735
|
function toTableNameManifest(entries) {
|
|
384749
384736
|
const out = {};
|
|
384750
384737
|
for (const [sourceEntityId, entry] of Object.entries(entries)) {
|
|
@@ -386252,16 +386239,13 @@ class Package {
|
|
|
386252
386239
|
for (const finding of lintGivenTags(drillFacts)) {
|
|
386253
386240
|
warnings.push(finding);
|
|
386254
386241
|
}
|
|
386255
|
-
for (const finding of await this.unsupportedComponentWarnings()) {
|
|
386256
|
-
warnings.push(finding);
|
|
386257
|
-
}
|
|
386258
386242
|
} catch (err) {
|
|
386259
386243
|
logger.warn("Report lint failed", {
|
|
386260
386244
|
packageName: this.packageName,
|
|
386261
386245
|
error: errMessage(err)
|
|
386262
386246
|
});
|
|
386263
386247
|
warnings.push({
|
|
386264
|
-
message: `Report lint stopped early, so this list is incomplete: an ` + `unknown subset of the tile, report, drill, given
|
|
386248
|
+
message: `Report lint stopped early, so this list is incomplete: an ` + `unknown subset of the tile, report, drill, given and curation ` + `checks did not run. Treat a missing ` + `finding of any of those kinds as unknown rather than clean. ` + `Reload the package to run the lint again. The ` + `reports themselves are unaffected, because they are ` + `discovered before the lint runs. An operator can find the ` + `cause in the server log under "Report lint failed".`,
|
|
386265
386249
|
severity: "warn"
|
|
386266
386250
|
});
|
|
386267
386251
|
}
|
|
@@ -386314,29 +386298,6 @@ class Package {
|
|
|
386314
386298
|
}
|
|
386315
386299
|
return warnings;
|
|
386316
386300
|
}
|
|
386317
|
-
async unsupportedComponentWarnings() {
|
|
386318
|
-
const dir = safeJoinUnderRoot(this.packagePath, MALLOYYO_DASHBOARDS_DIR);
|
|
386319
|
-
let entries;
|
|
386320
|
-
try {
|
|
386321
|
-
entries = await fs15.readdir(dir);
|
|
386322
|
-
} catch {
|
|
386323
|
-
return [];
|
|
386324
|
-
}
|
|
386325
|
-
const warnings = [];
|
|
386326
|
-
for (const entry of entries.sort()) {
|
|
386327
|
-
const suffix = COMPONENT_FILE_SUFFIXES.find((s2) => entry.endsWith(s2));
|
|
386328
|
-
if (!suffix)
|
|
386329
|
-
continue;
|
|
386330
|
-
const base = entry.slice(0, -suffix.length);
|
|
386331
|
-
warnings.push({
|
|
386332
|
-
model: `${MALLOYYO_DASHBOARDS_DIR}/${entry}`,
|
|
386333
|
-
subject: base,
|
|
386334
|
-
message: `Custom dashboard components are not supported, so ` + `"${MALLOYYO_DASHBOARDS_DIR}/${entry}" is ignored. Any ` + `${MALLOYYO_DASHBOARDS_DIR}/${base}${MODEL_FILE_SUFFIX} beside it still ` + `renders from its tags. For a page that runs its own code, use ` + `an HTML data app in the package's public/ directory.`,
|
|
386335
|
-
severity: "warn"
|
|
386336
|
-
});
|
|
386337
|
-
}
|
|
386338
|
-
return warnings;
|
|
386339
|
-
}
|
|
386340
386301
|
servedReportPaths() {
|
|
386341
386302
|
return this.reports.keys();
|
|
386342
386303
|
}
|
package/package.json
CHANGED
|
@@ -1,154 +0,0 @@
|
|
|
1
|
-
import{ae as ye,j as O,I as ne,r as f,aU as pe,U as fe,aV as ge,aH as ve,aW as we,aX as Y,aY as ke,aZ as xe,a_ as Ce,a$ as Ae,b0 as Se,b1 as Ee,b2 as ce,b3 as le}from"./index-JQs0UPrG.js";const w=ye.drillCell;function Me(e,t){const r=new Set;let a;try{a=e.getMetadata()?.getAllFields()??[]}catch{return r}const o=new Set;for(const n of a)!n||t(n)||n.tag?.tag("hidden")===void 0&&o.add(n.tag?.text("label")??n.name);for(const n of a){if(!n||!t(n))continue;const l=n.tag?.text("label")??n.name;o.has(l)||r.add(l)}return r}function B(e){const t=/^\s*(\d+)/.exec(e.style?.gridColumn??"");return t?t[1]:void 0}function ze(e,t){if(t.size!==0)for(const r of e.querySelectorAll(".malloy-table")){const a=l=>l.closest(".malloy-table")===r,o=new Set;for(const l of r.querySelectorAll(".column-cell.th")){if(!a(l))continue;const i=(l.textContent??"").replace(/\u200b/g,"").trim(),y=B(l);y&&t.has(i)&&o.add(y)}if(o.size===0)continue;const n=new Set;for(const l of r.querySelectorAll(`.${w}[tabindex="0"]`)){if(!a(l))continue;const i=B(l);i&&(n.has(i)?l.tabIndex=-1:n.add(i))}for(const l of r.querySelectorAll(".column-cell.td")){if(!a(l))continue;const i=B(l),y=!l.querySelector(".value-null")&&(l.textContent??"").trim()!=="";if(i&&y&&o.has(i)&&!l.querySelector(".malloy-table")){if(l.classList.add(w),l.tabIndex===0)n.add(i);else{const g=n.has(i);g||n.add(i),l.tabIndex=g?-1:0}l.setAttribute("role","button")}}}}function je(e){const t=e.closest(".malloy-table"),r=B(e);return!t||!r?[]:Array.from(t.querySelectorAll(`.${w}`)).filter(a=>a.closest(".malloy-table")===t&&B(a)===r)}function ie(e,t){const r=je(e),a=r.indexOf(e);if(a===-1)return;const o=t==="first"?r[0]:t==="last"?r[r.length-1]:r[Math.min(Math.max(a+t,0),r.length-1)];if(o){for(const n of r)n.tabIndex=n===o?0:-1;return o}}function Fe(e){return{background:e.dashboardRoot,tableBackground:e.tableBackground,tableHeaderColor:e.tableHeader,tableBodyColor:e.tableBody,tableBorder:e.border,tablePinnedBackground:e.tableHeaderBackground,tablePinnedBorder:e.pinnedBorder,tableFontSize:`${e.font.size}px`,fontFamily:e.font.family,mapColor:e.mapColor}}function _e(e){const{foreground:t,axisFaint:r,gridline:a,font:o}=e,n={background:e.background,font:o.family,title:{color:t,font:o.family},axis:{labelColor:t,titleColor:t,domainColor:r,tickColor:r,gridColor:a,labelFont:o.family,titleFont:o.family},legend:{labelColor:t,titleColor:t,labelFont:o.family,titleFont:o.family},header:{labelColor:t,titleColor:t},range:{category:e.series}},l={...n,legend:{...n.legend,orient:"bottom"}};return i=>i==="shape_map"?l:n}function Re(e){if(!e)return;const t=e.tag("theme");if(!t)return;const r={},a=t.tag("palette");if(a){const n={},l=a.textArray("series");l&&l.length>0&&(n.series=l);for(const i of pe){const y=a.tag(i);if(!y)continue;const g={},x=y.text("light"),k=y.text("dark");x&&(g.light=x),k&&(g.dark=k),Object.keys(g).length>0&&(n[i]=g)}Object.keys(n).length>0&&(r.palette=n)}const o=t.tag("font");if(o){const n={},l=o.text("family");l&&(n.family=l);const i=o.numeric("size");typeof i=="number"&&Number.isFinite(i)&&(n.size=i),Object.keys(n).length>0&&(r.font=n)}return Object.keys(r).length>0?r:void 0}const K=e=>(e??[]).map(t=>t?.value).filter(t=>typeof t=="string");function $e(e){const t=e.replace(/[-_]+/g," ").replace(/\s+/g," ").trim();return t.charAt(0).toUpperCase()+t.slice(1)}const Oe=e=>K(e.annotations).some(t=>{const r=t.trim();return/^#[^("]/.test(r)&&/(?:^#|[\s{])label\s*=/.test(r)}),Te=e=>`"${e.replace(/\\/g,"\\\\").replace(/"/g,'\\"')}"`,Be=e=>e.type?.element_type?.fields??e.type?.fields;function he(e,t){let r=!1;return{fields:e.map(a=>{const o=Be(a);if(o){const l=he(o,ce(K(a.annotations)));if(!l.changed)return a;r=!0;const i=a.type?.element_type?{...a.type,element_type:{...a.type.element_type,fields:l.fields}}:{...a.type,fields:l.fields};return{...a,type:i}}if(!t||!a.name||Oe(a))return a;const n=$e(a.name);return n===a.name?a:(r=!0,{...a,annotations:[{value:`# label=${Te(n)}
|
|
2
|
-
`},...a.annotations??[]]})}),changed:r}}function De(e){if(!e||typeof e!="object")return e;const t=e,r=t.schema?.fields;if(!Array.isArray(r))return e;const{fields:a,changed:o}=he(r,ce(K(t.annotations)));return o?{...t,schema:{...t.schema,fields:a}}:e}const se=320,Le=10,Ie=96,qe=4e3;function Ne(e,t){if(e!=="bar"&&e!=="combo"&&e!=="line"&&e!=="chart"&&e!=="scatter_chart")return;if(e!=="bar"&&e!=="combo")return se;const r=t?.data?.array_value?.length,a=r===void 0?0:r*Le+Ie;return Math.min(Math.max(se,a),qe)}const T="data-publisher-fill",Pe=async(e,t)=>{if(typeof window>"u")throw new Error("MalloyRenderer can only be used in browser environment");const{MalloyRenderer:r}=await xe();return new r({onClick:t,vegaConfigOverride:_e(e),theme:Fe(e),onError:a=>{console.error("Error rendering visualization:",typeof a,a)}}).createViz()};async function Ve(e){if(!e||typeof e!="object")return;const t=e,r=[...Array.isArray(t.model_annotations)?t.model_annotations:[],...Array.isArray(t.source_annotations)?t.source_annotations:[],...Array.isArray(t.annotations)?t.annotations:[]].map(o=>typeof o?.value=="string"?o.value:void 0).filter(o=>!!o);if(r.length===0)return;let a;try{({parseAnnotation:a}=await ke())}catch{return}try{const{tag:o}=a(r);return Re(o)}catch(o){console.warn("Failed to parse # theme.* annotations; chart will render with the shell theme.",{error:o,lines:r});return}}function He(e,t,r){const a=Ce(t,r);for(const[o,n]of Object.entries(a))e.style.setProperty(o,n)}const Ue=`
|
|
3
|
-
/* Card geometry, through the renderer's OWN theme vars rather than over the top
|
|
4
|
-
of them. It declares --malloy-theme--dashboard-* on .malloy-render itself and
|
|
5
|
-
passes each through to the --malloy-render--* the card CSS reads, so a value
|
|
6
|
-
inherited from our wrapper loses to that declaration however early it is set:
|
|
7
|
-
this has to be a rule on the same element, at higher specificity. Doubling the
|
|
8
|
-
class does that, and then no !important is needed — which matters, because
|
|
9
|
-
!important here also lands on .dashboard-item-borderless and repaints a card
|
|
10
|
-
the author asked to have no card. */
|
|
11
|
-
.malloy-render.malloy-render {
|
|
12
|
-
--malloy-theme--dashboard-card-radius: var(--publisher-dashboard-card-radius);
|
|
13
|
-
--malloy-theme--dashboard-card-padding: var(--publisher-dashboard-card-padding);
|
|
14
|
-
}
|
|
15
|
-
/* dashboard.css hardcodes background: #f7f9fc on .malloy-dashboard
|
|
16
|
-
and .dashboard-row-header, which would paint light grey in dark
|
|
17
|
-
mode and also bleed an operator-picked palette.background across
|
|
18
|
-
the panel chrome in light. Both surfaces use
|
|
19
|
-
--malloy-render--background here, which buildMalloyExplicitTheme
|
|
20
|
-
wires to dashboardRoot: a mode-keyed neutral that stays decoupled
|
|
21
|
-
from palette.background (the chart canvas) on purpose. Selectors
|
|
22
|
-
are duplicated at higher specificity to beat the renderer s own
|
|
23
|
-
scoped rules. */
|
|
24
|
-
.malloy-render .malloy-dashboard,
|
|
25
|
-
.malloy-render.malloy-render .malloy-dashboard,
|
|
26
|
-
div.malloy-render .malloy-dashboard {
|
|
27
|
-
/* --publisher-dashboard-root is set on the outer Publisher wrapper
|
|
28
|
-
(see buildTableCssVars) and stays decoupled from
|
|
29
|
-
--malloy-render--background, which gets shadowed inside the
|
|
30
|
-
renderer when a theme.background annotation is present. The
|
|
31
|
-
dedicated var means the panel between tiles always paints the
|
|
32
|
-
operator neutral dashboardRoot regardless of annotations. */
|
|
33
|
-
background: var(--publisher-dashboard-root) !important;
|
|
34
|
-
background-color: var(--publisher-dashboard-root) !important;
|
|
35
|
-
color: var(--malloy-render--table-body-color) !important;
|
|
36
|
-
}
|
|
37
|
-
.malloy-render .malloy-dashboard .dashboard-row,
|
|
38
|
-
.malloy-render .malloy-dashboard .dashboard-row-body,
|
|
39
|
-
.malloy-render .malloy-dashboard .dashboard-row-header,
|
|
40
|
-
.malloy-render.malloy-render .malloy-dashboard .dashboard-row-header,
|
|
41
|
-
div.malloy-render .malloy-dashboard .dashboard-row-header {
|
|
42
|
-
/* Belt + suspenders: dashboard.css hardcodes .dashboard-row-header
|
|
43
|
-
to #f7f9fc, and row / row-body have no explicit background but
|
|
44
|
-
get caught in any annotation-driven inline writes. Force all of
|
|
45
|
-
them to the neutral panel colour. */
|
|
46
|
-
background: var(--publisher-dashboard-root) !important;
|
|
47
|
-
background-color: var(--publisher-dashboard-root) !important;
|
|
48
|
-
}
|
|
49
|
-
.malloy-render .malloy-dashboard .dashboard-item:not(.dashboard-item-borderless) {
|
|
50
|
-
/* Tile padding around each chart / table. Uses our custom
|
|
51
|
-
--malloy-render--tile-background so the operator can theme the
|
|
52
|
-
tile separately from the table header row (which paints from
|
|
53
|
-
--malloy-render--table-pinned-background below).
|
|
54
|
-
|
|
55
|
-
A flat bordered card rather than the renderer's shadow ring, which is what
|
|
56
|
-
makes it match the composite tile's Paper. :not(borderless) because
|
|
57
|
-
# borderless asks for no card at all, and an !important background and
|
|
58
|
-
border drew one anyway. */
|
|
59
|
-
background: var(--malloy-render--tile-background) !important;
|
|
60
|
-
color: var(--malloy-render--table-body-color) !important;
|
|
61
|
-
box-shadow: none !important;
|
|
62
|
-
/* The card edge, not the table gridline: --publisher-dashboard-card-border,
|
|
63
|
-
the same value TileCard paints, so the renderer's card and the composite
|
|
64
|
-
one are outlined identically. */
|
|
65
|
-
border: var(--publisher-dashboard-card-border) !important;
|
|
66
|
-
}
|
|
67
|
-
/* The renderer's big-value card — the small card a "# big_value" measure sits
|
|
68
|
-
in, several to a row inside one tile. (No backticks in here: this is inside
|
|
69
|
-
a template literal.) Its own CSS gives it a 0.55px edge in
|
|
70
|
-
#e5e7eb: a sub-pixel width, and a grey off the slate ramp everything else on
|
|
71
|
-
the page is on. It is a dashboard card like any other, so it takes the card
|
|
72
|
-
edge and stops being the one card outlined in a hairline nobody can see. */
|
|
73
|
-
.malloy-render .malloy-big-value-card {
|
|
74
|
-
border: var(--publisher-dashboard-card-border) !important;
|
|
75
|
-
/* Its shadow is a four-layer Tailwind stack, and only two of those layers
|
|
76
|
-
are a shadow: the other is "0 0 0 1px #e5e7eb", a spread ring drawn as a
|
|
77
|
-
second border OVER the real one. So the border above was landing
|
|
78
|
-
underneath a ring in the renderer's own grey, and the card kept both a
|
|
79
|
-
lifted look the flat cards around it do not have and an edge we did not
|
|
80
|
-
pick. Dropped whole rather than rebuilt layer by layer: the ring's job is
|
|
81
|
-
the border's job, and the border is already doing it. */
|
|
82
|
-
box-shadow: none !important;
|
|
83
|
-
}
|
|
84
|
-
/* The strip of big-value cards as a grid, so cards share a width and the last one does not wrap
|
|
85
|
-
alone. The renderer's wrapping flex row sizes each card to its own text. The 250px floor is the
|
|
86
|
-
widest realistic value: it is nowrap at 32px in an overflow:visible card, so a narrower track
|
|
87
|
-
would spill it onto its neighbour. Embedded (a nested big_value) is left as the renderer draws it. */
|
|
88
|
-
.malloy-render .malloy-big-value:not(.malloy-big-value--embedded) {
|
|
89
|
-
display: grid;
|
|
90
|
-
grid-template-columns: repeat(auto-fit, minmax(250px, 1fr));
|
|
91
|
-
}
|
|
92
|
-
.malloy-render .malloy-big-value-card:not(.malloy-big-value-card--embedded) {
|
|
93
|
-
width: auto;
|
|
94
|
-
max-width: none;
|
|
95
|
-
}
|
|
96
|
-
.malloy-render .malloy-dashboard .dashboard-row-header-separator {
|
|
97
|
-
background: var(--malloy-render--table-border) !important;
|
|
98
|
-
}
|
|
99
|
-
/* !important because the malloy-explorer stylesheet pins these same properties with it. */
|
|
100
|
-
[${T}] .malloy-table.root {
|
|
101
|
-
height: 100% !important;
|
|
102
|
-
align-content: start !important;
|
|
103
|
-
grid-template-columns: repeat(var(--total-header-size), minmax(max-content, 1fr)) !important;
|
|
104
|
-
}
|
|
105
|
-
.malloy-render .malloy-table .th.column-cell {
|
|
106
|
-
/* Non-pinned tables have no header background in the renderer's
|
|
107
|
-
own CSS (only pinned scrolled tables paint the pinned-header
|
|
108
|
-
with this var). Force the same value here so every table has a
|
|
109
|
-
visible header band reflecting the operator's choice. */
|
|
110
|
-
background: var(--malloy-render--table-pinned-background) !important;
|
|
111
|
-
}
|
|
112
|
-
.malloy-render .dashboard-item-title,
|
|
113
|
-
.malloy-render .dashboard-dimension-name {
|
|
114
|
-
color: var(--malloy-render--label-color) !important;
|
|
115
|
-
}
|
|
116
|
-
.malloy-render .dashboard-item-value,
|
|
117
|
-
.malloy-render .dashboard-item-value-measure,
|
|
118
|
-
.malloy-render .dashboard-dimension-value {
|
|
119
|
-
color: var(--malloy-render--value-color) !important;
|
|
120
|
-
}
|
|
121
|
-
.malloy-render .malloy-table,
|
|
122
|
-
.malloy-render .malloy-list {
|
|
123
|
-
color: var(--malloy-render--table-body-color) !important;
|
|
124
|
-
}
|
|
125
|
-
.malloy-render .column-cell.th,
|
|
126
|
-
.malloy-render .cell-content.header {
|
|
127
|
-
color: var(--malloy-render--table-header-color) !important;
|
|
128
|
-
}
|
|
129
|
-
/* A cell whose column declares a "# drill" this surface can honor, marked
|
|
130
|
-
by markDrillableCells. Ordinary text with a pointer cursor until hovered,
|
|
131
|
-
then it reads as a link: the same affordance Malloyyo gives the same tag,
|
|
132
|
-
and the only thing that tells a reader a cell navigates. Kept off the
|
|
133
|
-
resting state deliberately: a column painted blue competes with the data.
|
|
134
|
-
(No backticks in here: this is inside a template literal.) */
|
|
135
|
-
.malloy-render .${w} {
|
|
136
|
-
cursor: pointer;
|
|
137
|
-
}
|
|
138
|
-
.malloy-render .${w}:hover > .cell-content {
|
|
139
|
-
color: var(--publisher-drill-link) !important;
|
|
140
|
-
text-decoration: underline;
|
|
141
|
-
}
|
|
142
|
-
/* Keyboard focus reads the same as hover, plus a ring: a hover colour is not
|
|
143
|
-
something a keyboard user can produce, and a focus ring is not something a
|
|
144
|
-
mouse user sees. focus-visible rather than focus, so a click does not leave a
|
|
145
|
-
ring behind it. (No backticks in here: this is inside a template literal.) */
|
|
146
|
-
.malloy-render .${w}:focus-visible {
|
|
147
|
-
outline: 2px solid var(--publisher-drill-link);
|
|
148
|
-
outline-offset: -2px;
|
|
149
|
-
}
|
|
150
|
-
.malloy-render .${w}:focus-visible > .cell-content {
|
|
151
|
-
color: var(--publisher-drill-link) !important;
|
|
152
|
-
text-decoration: underline;
|
|
153
|
-
}
|
|
154
|
-
`,de="publisher-malloy-renderer-overrides";function We(){if(typeof document>"u"||document.getElementById(de))return;const e=document.createElement("style");e.id=de,e.textContent=Ue,document.head.appendChild(e)}function Xe({result:e,height:t,drill:r,onSizeChange:a,onSizing:o,fill:n}){const l=f.useRef(null),i=f.useRef(r);i.current=r;const y=f.useCallback(d=>{i.current?.onClick(d)},[]),g=r!==void 0,x=f.useRef(!1),k=f.useRef(null),P=f.useRef(0),{theme:D,layers:V,mode:J}=fe(),Z=f.useMemo(()=>({radius:D.ui.radius.tile,padding:`${ge}px`}),[D.ui.radius.tile]);return f.useEffect(()=>()=>{P.current+=1;const d=k.current;d?.viz.remove(),d?.node.remove(),k.current=null},[]),f.useLayoutEffect(()=>{if(!l.current||!e)return;We();const d=l.current,ue=P.current+=1;let H=!1;const U=()=>!H&&ue===P.current;let c,b,L=null,W=0,I=null,q=null,N=null,j=null,F=null,_=null,G=null,Q=0,A=null;x.current=!1;let p,S;const X=v=>{if(H||!v.firstElementChild||Y(p,S)==="container")return;const R=Ae(p,S);if(x.current&&!R)return;const $=d.hasAttribute(T);$&&d.removeAttribute(T);const E=Se(v,le(p));$&&d.setAttribute(T,""),E>0&&(x.current=!0,a&&E!==Q&&(Q=E,a(E)));const h=Ee(v,le(p));R&&h&&G!==h&&(G=h,_?.disconnect(),_=new ResizeObserver(()=>X(v)),_.observe(h))};return(async()=>{let v;try{v=JSON.parse(e)}catch(m){console.error("Error parsing visualization result:",m);return}const R=await Ve(v),$=R?ve([...V,we(V,R)],J):D;if(!U())return;try{b=await Pe($,y)}catch(m){console.error("Failed to create renderer:",m);return}if(!U()){b.remove(),b=void 0;return}const E=k.current;c=document.createElement("div"),c.style.width="100%",c.style.height="100%",He(c,$,Z),d.childElementCount>0&&(d.style.position="relative",c.style.position="absolute",c.style.inset="0",c.style.visibility="hidden"),d.appendChild(c);const h=c;j=new MutationObserver(()=>{F&&clearTimeout(F),F=setTimeout(()=>{X(h),x.current&&j?.disconnect()},100)}),j.observe(c,{childList:!0,subtree:!0,attributes:!0});const me=b;let ee=!1;const te=()=>{if(!(ee||!U())){ee=!0,A&&(clearTimeout(A),A=null),E?.viz.remove();for(const m of Array.from(d.children))m!==h&&d.removeChild(m);h.style.position="",h.style.inset="",h.style.visibility="",d.style.position="",k.current={viz:me,node:h},X(h)}};try{b.setResult(De(v));try{const C=b.getMetadata(),M=C?.getRootField();p=M?.renderAs(),S=M?C?.getPluginsForField(M.key)?.[0]?.sizingStrategy:void 0}catch{p=void 0,S=void 0}o?.(Y(p,S),p),c.dataset.malloyRenderAs=p??"unknown",c.dataset.malloySizing=Y(p,S);const m=Ne(p,v);c.style.minWidth=m===void 0?"":`${m}px`,d.style.overflowX=m===void 0?"":"auto",d.style.overflowY=m===void 0?"":"hidden",b.render(c);const re=i.current;try{if(re){const C=Me(b,re.canDrill),M=()=>{try{ze(h,C)}catch(s){console.warn("Drill affordance skipped:",s)}};M();const ae=s=>{const u=s.querySelector(".cell-content")??s,z=u.getBoundingClientRect();u.dispatchEvent(new MouseEvent("click",{bubbles:!0,cancelable:!0,view:u.ownerDocument.defaultView,clientX:Math.round(z.left+z.width/2),clientY:Math.round(z.bottom)}))},oe=s=>s.target?.closest?.(`.${w}`)??null,be=(s,u)=>{ie(s,u)?.focus()};I=s=>{const u=oe(s);if(!u)return;const z=s.key==="ArrowDown"?1:s.key==="ArrowUp"?-1:s.key==="Home"?"first":s.key==="End"?"last":void 0;if(z!==void 0){s.preventDefault(),be(u,z);return}s.key==="Enter"?(s.preventDefault(),ae(u)):s.key===" "&&s.preventDefault()},q=s=>{if(s.key!==" ")return;const u=oe(s);u&&(s.preventDefault(),ae(u))},N=s=>{const u=s.target?.closest?.(`.${w}`);u&&ie(u,0)},h.addEventListener("keydown",I),h.addEventListener("keyup",q),h.addEventListener("focusin",N),C.size>0&&typeof MutationObserver<"u"&&(L=new MutationObserver(()=>{cancelAnimationFrame(W),W=requestAnimationFrame(M)}),L.observe(h,{childList:!0,subtree:!0}))}}catch(C){console.warn("Drill affordance skipped:",C)}b.onReady(te),A=setTimeout(te,1e4)}catch(m){console.error("Error rendering visualization:",m),j?.disconnect(),_?.disconnect(),L?.disconnect(),b.remove(),b=void 0,h.parentNode===d&&d.removeChild(h)}})(),()=>{H=!0,j?.disconnect(),_?.disconnect(),F&&clearTimeout(F),A&&clearTimeout(A),c&&k.current?.node!==c&&(I&&c.removeEventListener("keydown",I),q&&c.removeEventListener("keyup",q),N&&c.removeEventListener("focusin",N),L?.disconnect(),cancelAnimationFrame(W),b?.remove(),c.parentNode===d&&d.removeChild(c),d.style.position="")}},[e,y,g,a,o,D,V,J,Z]),O.jsx("div",{ref:l,...n?{[T]:""}:{},style:{width:"100%",height:t?`${t}px`:"400px"}})}function Ye(e){return typeof window>"u"?O.jsx(ne,{sx:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"text.secondary"},children:"Loading..."}):O.jsx(f.Suspense,{fallback:O.jsx(ne,{sx:{width:"100%",height:e.height?`${e.height}px`:"100%",display:"flex",alignItems:"center",justifyContent:"center",color:"text.secondary"},children:"Loading visualization..."}),children:O.jsx(Xe,{...e})})}const Je=Object.freeze(Object.defineProperty({__proto__:null,default:Ye},Symbol.toStringTag,{value:"Module"}));export{w as D,Ye as R,_e as a,Fe as b,Je as c,Me as d,ze as m,Re as r};
|