@opendata-ai/openchart-vanilla 8.3.1 → 8.4.0
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/{chunk-GRA2NIRZ.js → chunk-45XZOWYC.js} +497 -52
- package/dist/chunk-45XZOWYC.js.map +1 -0
- package/dist/{chunk-DCODCLWI.js → chunk-FETYAVTZ.js} +3 -3
- package/dist/{chunk-RM5XS6NR.js → chunk-HGRNXLDF.js} +16 -11
- package/dist/chunk-HGRNXLDF.js.map +1 -0
- package/dist/{chunk-ERRV4WFS.js → chunk-LNPRDSK3.js} +37 -5
- package/dist/chunk-LNPRDSK3.js.map +1 -0
- package/dist/{chunk-T32UHMZA.js → chunk-UOB422LY.js} +2 -2
- package/dist/{chunk-W76C53MC.js → chunk-YGYMB7KB.js} +105 -14
- package/dist/chunk-YGYMB7KB.js.map +1 -0
- package/dist/export-gif.js +3 -3
- package/dist/index.d.ts +6 -1
- package/dist/index.js +306 -103
- package/dist/index.js.map +1 -1
- package/dist/static.js +3 -3
- package/dist/story/index.js +4 -4
- package/dist/styles.css +1 -1
- package/dist/styles.css.map +1 -1
- package/package.json +3 -3
- package/dist/chunk-ERRV4WFS.js.map +0 -1
- package/dist/chunk-GRA2NIRZ.js.map +0 -1
- package/dist/chunk-RM5XS6NR.js.map +0 -1
- package/dist/chunk-W76C53MC.js.map +0 -1
- /package/dist/{chunk-DCODCLWI.js.map → chunk-FETYAVTZ.js.map} +0 -0
- /package/dist/{chunk-T32UHMZA.js.map → chunk-UOB422LY.js.map} +0 -0
|
@@ -5,10 +5,10 @@ import {
|
|
|
5
5
|
getSVGBackgroundColor,
|
|
6
6
|
getSVGDimensions,
|
|
7
7
|
rasterizeSVGToCanvas
|
|
8
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-UOB422LY.js";
|
|
9
9
|
import {
|
|
10
10
|
injectThemeStyleBlock
|
|
11
|
-
} from "./chunk-
|
|
11
|
+
} from "./chunk-HGRNXLDF.js";
|
|
12
12
|
|
|
13
13
|
// src/gif-encode.ts
|
|
14
14
|
function readCanvasSRGB(canvas) {
|
|
@@ -231,4 +231,4 @@ export {
|
|
|
231
231
|
applyFrameState,
|
|
232
232
|
exportGIF
|
|
233
233
|
};
|
|
234
|
-
//# sourceMappingURL=chunk-
|
|
234
|
+
//# sourceMappingURL=chunk-FETYAVTZ.js.map
|
|
@@ -30,7 +30,7 @@ function applyTextStyle(el, style) {
|
|
|
30
30
|
import { adaptForLightLineStroke, cssTokenDefault } from "@opendata-ai/openchart-core";
|
|
31
31
|
function buildThemeStyleBlock(theme) {
|
|
32
32
|
const accent = theme.colors.categorical[0] ?? cssTokenDefault("--oc-accent", "light");
|
|
33
|
-
const bg = theme.colors.
|
|
33
|
+
const bg = theme.colors.neutral.surface;
|
|
34
34
|
const props = [
|
|
35
35
|
`--oc-font-family: ${theme.fonts.family}`,
|
|
36
36
|
`--oc-font-mono: ${theme.fonts.mono}`,
|
|
@@ -48,9 +48,12 @@ function buildThemeStyleBlock(theme) {
|
|
|
48
48
|
`--oc-bg: ${bg}`,
|
|
49
49
|
`--oc-text: ${theme.colors.text}`,
|
|
50
50
|
`--oc-text-muted: ${theme.colors.axis}`,
|
|
51
|
-
`--oc-text-
|
|
51
|
+
`--oc-text-secondary: ${theme.colors.neutral.secondary}`,
|
|
52
|
+
`--oc-text-faint: ${theme.colors.neutral.faint}`,
|
|
53
|
+
`--oc-border: ${theme.colors.neutral.border}`,
|
|
52
54
|
`--oc-gridline: ${theme.colors.gridline}`,
|
|
53
|
-
|
|
55
|
+
// Hairline, not the tick-label ink.
|
|
56
|
+
`--oc-axis: ${theme.colors.hairline}`,
|
|
54
57
|
`--oc-border-radius: ${theme.borderRadius}px`,
|
|
55
58
|
`--oc-accent: ${accent}`,
|
|
56
59
|
`--oc-accent-strong: ${adaptForLightLineStroke(accent)}`,
|
|
@@ -61,27 +64,29 @@ function buildThemeStyleBlock(theme) {
|
|
|
61
64
|
`--oc-space-4: ${theme.spacing.padding}px`
|
|
62
65
|
];
|
|
63
66
|
const rules = [
|
|
64
|
-
|
|
67
|
+
// Tabular figures everywhere in the chart's own text: axis ticks, value
|
|
68
|
+
// labels and legend entries all line up column-wise.
|
|
69
|
+
`svg.oc-chart { ${props.join("; ")}; font-variant-numeric: tabular-nums; }`,
|
|
65
70
|
`.oc-chrome { font-family: var(--oc-font-family); }`,
|
|
66
71
|
`.oc-eyebrow { font-size: var(--oc-eyebrow-size); font-weight: var(--oc-eyebrow-weight); letter-spacing: var(--oc-eyebrow-tracking); text-transform: uppercase; fill: var(--oc-accent); }`,
|
|
67
72
|
`.oc-title { font-size: var(--oc-title-size); font-weight: var(--oc-title-weight); letter-spacing: var(--oc-title-tracking); fill: var(--oc-text); }`,
|
|
68
73
|
`.oc-subtitle { font-size: var(--oc-subtitle-size); font-weight: var(--oc-subtitle-weight); fill: var(--oc-text-muted); }`,
|
|
69
74
|
`.oc-source, .oc-byline, .oc-footer { font-size: var(--oc-source-size); font-weight: var(--oc-source-weight); fill: var(--oc-text-muted); }`,
|
|
70
|
-
`.oc-brand { font-size: 11px; font-weight:
|
|
75
|
+
`.oc-brand { font-size: 11px; font-weight: 500; letter-spacing: 0.02em; fill: var(--oc-text-faint); }`,
|
|
71
76
|
`.oc-brand-dot { fill: var(--oc-accent); }`,
|
|
72
77
|
`.oc-eyebrow-dot { fill: var(--oc-accent); }`,
|
|
73
78
|
`.oc-metrics { font-family: var(--oc-font-family); }`,
|
|
74
|
-
`.oc-metric-label { font-size:
|
|
75
|
-
`.oc-metric-value { font-size: 22px; font-weight:
|
|
76
|
-
`.oc-metric-delta-up { fill: var(--oc-positive); font-size: 12px; font-weight:
|
|
77
|
-
`.oc-metric-delta-down { fill: var(--oc-negative); font-size: 12px; font-weight:
|
|
79
|
+
`.oc-metric-label { font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; fill: var(--oc-text-muted); }`,
|
|
80
|
+
`.oc-metric-value { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; fill: var(--oc-text); font-variant-numeric: tabular-nums; }`,
|
|
81
|
+
`.oc-metric-delta-up { fill: var(--oc-positive); font-size: 12px; font-weight: 500; }`,
|
|
82
|
+
`.oc-metric-delta-down { fill: var(--oc-negative); font-size: 12px; font-weight: 500; }`,
|
|
78
83
|
`.oc-axis-tick-inline { font-size: 11px; font-weight: 400; fill: var(--oc-text-muted); }`,
|
|
79
84
|
`.oc-endpoint-labels { font-family: var(--oc-font-family); }`,
|
|
80
85
|
`.oc-endpoint-label { fill: var(--oc-endpoint-label-color, var(--oc-text)); }`,
|
|
81
86
|
`.oc-endpoint-value { fill: var(--oc-endpoint-value-color, var(--oc-text-muted)); }`,
|
|
82
87
|
`.oc-endpoint-leader { stroke: var(--oc-endpoint-leader-color, currentColor); }`,
|
|
83
88
|
`.oc-annotation-subtitle { fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted)); }`,
|
|
84
|
-
`.oc-metric-secondary { fill: var(--oc-
|
|
89
|
+
`.oc-metric-secondary { fill: var(--oc-text-muted); font-size: 12px; font-weight: 400; }`,
|
|
85
90
|
`.oc-legend { font-family: var(--oc-font-family); font-size: var(--oc-body-size); }`,
|
|
86
91
|
`.oc-legend-entry { cursor: default; }`,
|
|
87
92
|
`.oc-legend text { fill: var(--oc-legend-text); }`
|
|
@@ -110,4 +115,4 @@ export {
|
|
|
110
115
|
buildThemeStyleBlock,
|
|
111
116
|
injectThemeStyleBlock
|
|
112
117
|
};
|
|
113
|
-
//# sourceMappingURL=chunk-
|
|
118
|
+
//# sourceMappingURL=chunk-HGRNXLDF.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/renderers/svg-dom.ts","../src/theme-style-block.ts"],"sourcesContent":["/**\n * Shared SVG DOM helpers used across the per-concern renderers.\n *\n * Pure, stateless utilities. No layout/theme knowledge.\n */\n\nimport type { TextStyle } from '@opendata-ai/openchart-core';\n\nexport const SVG_NS = 'http://www.w3.org/2000/svg';\nexport const XLINK_NS = 'http://www.w3.org/1999/xlink';\n\nexport function createSVGElement(tag: string): SVGElement {\n return document.createElementNS(SVG_NS, tag);\n}\n\nexport function setAttrs(el: SVGElement, attrs: Record<string, string | number>): void {\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, String(value));\n }\n}\n\nexport function applyTextStyle(el: SVGElement, style: TextStyle): void {\n // Use inline styles so engine-computed values take priority over CSS class\n // defaults (e.g. .oc-title { font-size: var(--oc-title-size) } would otherwise\n // override the responsive scaling applied by the chrome layout).\n const inline = (el as SVGElement & ElementCSSInlineStyle).style;\n inline.setProperty('fill', style.fill);\n inline.setProperty('font-size', `${style.fontSize}px`);\n inline.setProperty('font-weight', String(style.fontWeight));\n inline.setProperty('font-family', style.fontFamily);\n if (style.textAnchor) {\n el.setAttribute('text-anchor', style.textAnchor);\n }\n if (style.dominantBaseline) {\n el.setAttribute('dominant-baseline', style.dominantBaseline);\n }\n if (style.fontVariant) {\n el.setAttribute('font-variant', style.fontVariant);\n }\n}\n","/**\n * Theme `<style>` block for self-contained SVG export.\n *\n * Charts render most fills as inline SVG attributes, but a handful of chrome\n * elements (metric cells, the brand watermark dot, legend text, endpoint labels)\n * take their fill from CSS classes in `chrome.css` via `--oc-*` variables. That\n * works on-screen because the live SVG inherits the page stylesheet — but a\n * serialized/rasterized export (PNG/JPG/GIF) is detached from the page, so those\n * class-based fills vanish. This module builds a `<style>` block that resolves\n * every such rule against a concrete `ResolvedTheme`, so injecting it into an\n * export clone makes the SVG fully self-contained.\n *\n * Both the headless renderer (`static.ts`) and the browser export path use this,\n * so class-based styling round-trips identically no matter which path produced\n * the SVG. Browser-safe (no Node imports) so the browser exporters can import it.\n */\n\nimport type { ResolvedTheme } from '@opendata-ai/openchart-core';\nimport { adaptForLightLineStroke, cssTokenDefault } from '@opendata-ai/openchart-core';\nimport { SVG_NS } from './renderers/svg-dom';\n\n/**\n * Build the theme `<style>` block CSS text for a resolved theme. Sets the\n * `--oc-*` custom properties on `svg.oc-chart` and defines every class-based\n * chrome rule against them.\n */\nexport function buildThemeStyleBlock(theme: ResolvedTheme): string {\n const accent = theme.colors.categorical[0] ?? cssTokenDefault('--oc-accent', 'light');\n const bg = theme.colors.neutral.surface;\n\n const props = [\n `--oc-font-family: ${theme.fonts.family}`,\n `--oc-font-mono: ${theme.fonts.mono}`,\n `--oc-title-size: ${theme.chrome.title.fontSize}px`,\n `--oc-title-weight: ${theme.chrome.title.fontWeight}`,\n `--oc-title-tracking: ${cssTokenDefault('--oc-title-tracking', 'light')}`,\n `--oc-subtitle-size: ${theme.chrome.subtitle.fontSize}px`,\n `--oc-subtitle-weight: ${theme.chrome.subtitle.fontWeight}`,\n `--oc-source-size: ${theme.chrome.source.fontSize}px`,\n `--oc-source-weight: ${theme.chrome.source.fontWeight}`,\n `--oc-body-size: ${theme.fonts.sizes.body}px`,\n `--oc-eyebrow-size: ${theme.chrome.eyebrow.fontSize}px`,\n `--oc-eyebrow-weight: ${theme.chrome.eyebrow.fontWeight}`,\n `--oc-eyebrow-tracking: ${cssTokenDefault('--oc-eyebrow-tracking', 'light')}`,\n `--oc-bg: ${bg}`,\n `--oc-text: ${theme.colors.text}`,\n `--oc-text-muted: ${theme.colors.axis}`,\n `--oc-text-secondary: ${theme.colors.neutral.secondary}`,\n `--oc-text-faint: ${theme.colors.neutral.faint}`,\n `--oc-border: ${theme.colors.neutral.border}`,\n `--oc-gridline: ${theme.colors.gridline}`,\n // Hairline, not the tick-label ink.\n `--oc-axis: ${theme.colors.hairline}`,\n `--oc-border-radius: ${theme.borderRadius}px`,\n `--oc-accent: ${accent}`,\n `--oc-accent-strong: ${adaptForLightLineStroke(accent)}`,\n `--oc-positive: ${theme.colors.positive}`,\n `--oc-negative: ${theme.colors.negative}`,\n `--oc-legend-text: ${theme.isDark ? cssTokenDefault('--oc-legend-text', 'dark') : cssTokenDefault('--oc-legend-text', 'light')}`,\n `--oc-space-2: ${theme.spacing.chromeGap * 2}px`,\n `--oc-space-4: ${theme.spacing.padding}px`,\n ];\n\n const rules = [\n // Tabular figures everywhere in the chart's own text: axis ticks, value\n // labels and legend entries all line up column-wise.\n `svg.oc-chart { ${props.join('; ')}; font-variant-numeric: tabular-nums; }`,\n `.oc-chrome { font-family: var(--oc-font-family); }`,\n `.oc-eyebrow { font-size: var(--oc-eyebrow-size); font-weight: var(--oc-eyebrow-weight); letter-spacing: var(--oc-eyebrow-tracking); text-transform: uppercase; fill: var(--oc-accent); }`,\n `.oc-title { font-size: var(--oc-title-size); font-weight: var(--oc-title-weight); letter-spacing: var(--oc-title-tracking); fill: var(--oc-text); }`,\n `.oc-subtitle { font-size: var(--oc-subtitle-size); font-weight: var(--oc-subtitle-weight); fill: var(--oc-text-muted); }`,\n `.oc-source, .oc-byline, .oc-footer { font-size: var(--oc-source-size); font-weight: var(--oc-source-weight); fill: var(--oc-text-muted); }`,\n `.oc-brand { font-size: 11px; font-weight: 500; letter-spacing: 0.02em; fill: var(--oc-text-faint); }`,\n `.oc-brand-dot { fill: var(--oc-accent); }`,\n `.oc-eyebrow-dot { fill: var(--oc-accent); }`,\n `.oc-metrics { font-family: var(--oc-font-family); }`,\n `.oc-metric-label { font-size: 11px; font-weight: 500; letter-spacing: 0.06em; text-transform: uppercase; fill: var(--oc-text-muted); }`,\n `.oc-metric-value { font-size: 22px; font-weight: 600; letter-spacing: -0.02em; fill: var(--oc-text); font-variant-numeric: tabular-nums; }`,\n `.oc-metric-delta-up { fill: var(--oc-positive); font-size: 12px; font-weight: 500; }`,\n `.oc-metric-delta-down { fill: var(--oc-negative); font-size: 12px; font-weight: 500; }`,\n `.oc-axis-tick-inline { font-size: 11px; font-weight: 400; fill: var(--oc-text-muted); }`,\n `.oc-endpoint-labels { font-family: var(--oc-font-family); }`,\n `.oc-endpoint-label { fill: var(--oc-endpoint-label-color, var(--oc-text)); }`,\n `.oc-endpoint-value { fill: var(--oc-endpoint-value-color, var(--oc-text-muted)); }`,\n `.oc-endpoint-leader { stroke: var(--oc-endpoint-leader-color, currentColor); }`,\n `.oc-annotation-subtitle { fill: var(--oc-annotation-subtitle-color, var(--oc-text-muted)); }`,\n `.oc-metric-secondary { fill: var(--oc-text-muted); font-size: 12px; font-weight: 400; }`,\n `.oc-legend { font-family: var(--oc-font-family); font-size: var(--oc-body-size); }`,\n `.oc-legend-entry { cursor: default; }`,\n `.oc-legend text { fill: var(--oc-legend-text); }`,\n ];\n\n return rules.join('\\n');\n}\n\n/**\n * Inject the theme style block into an SVG clone's `<defs>` so class-based fills\n * survive serialization. Idempotent-ish: adds one `<style data-oc-theme>` at the\n * front of `<defs>`; call once per clone before serializing. No-op if `theme` is\n * undefined (nothing to resolve against).\n */\nexport function injectThemeStyleBlock(svg: SVGElement, theme: ResolvedTheme | undefined): void {\n if (!theme) return;\n let defs = svg.querySelector('defs');\n if (!defs) {\n defs = document.createElementNS(SVG_NS, 'defs');\n svg.insertBefore(defs, svg.firstChild);\n }\n const style = document.createElementNS(SVG_NS, 'style');\n style.setAttribute('data-oc-theme', '');\n style.textContent = buildThemeStyleBlock(theme);\n defs.insertBefore(style, defs.firstChild);\n}\n"],"mappings":";AAQO,IAAM,SAAS;AACf,IAAM,WAAW;AAEjB,SAAS,iBAAiB,KAAyB;AACxD,SAAO,SAAS,gBAAgB,QAAQ,GAAG;AAC7C;AAEO,SAAS,SAAS,IAAgB,OAA8C;AACrF,aAAW,CAAC,KAAK,KAAK,KAAK,OAAO,QAAQ,KAAK,GAAG;AAChD,OAAG,aAAa,KAAK,OAAO,KAAK,CAAC;AAAA,EACpC;AACF;AAEO,SAAS,eAAe,IAAgB,OAAwB;AAIrE,QAAM,SAAU,GAA0C;AAC1D,SAAO,YAAY,QAAQ,MAAM,IAAI;AACrC,SAAO,YAAY,aAAa,GAAG,MAAM,QAAQ,IAAI;AACrD,SAAO,YAAY,eAAe,OAAO,MAAM,UAAU,CAAC;AAC1D,SAAO,YAAY,eAAe,MAAM,UAAU;AAClD,MAAI,MAAM,YAAY;AACpB,OAAG,aAAa,eAAe,MAAM,UAAU;AAAA,EACjD;AACA,MAAI,MAAM,kBAAkB;AAC1B,OAAG,aAAa,qBAAqB,MAAM,gBAAgB;AAAA,EAC7D;AACA,MAAI,MAAM,aAAa;AACrB,OAAG,aAAa,gBAAgB,MAAM,WAAW;AAAA,EACnD;AACF;;;ACrBA,SAAS,yBAAyB,uBAAuB;AAQlD,SAAS,qBAAqB,OAA8B;AACjE,QAAM,SAAS,MAAM,OAAO,YAAY,CAAC,KAAK,gBAAgB,eAAe,OAAO;AACpF,QAAM,KAAK,MAAM,OAAO,QAAQ;AAEhC,QAAM,QAAQ;AAAA,IACZ,qBAAqB,MAAM,MAAM,MAAM;AAAA,IACvC,mBAAmB,MAAM,MAAM,IAAI;AAAA,IACnC,oBAAoB,MAAM,OAAO,MAAM,QAAQ;AAAA,IAC/C,sBAAsB,MAAM,OAAO,MAAM,UAAU;AAAA,IACnD,wBAAwB,gBAAgB,uBAAuB,OAAO,CAAC;AAAA,IACvE,uBAAuB,MAAM,OAAO,SAAS,QAAQ;AAAA,IACrD,yBAAyB,MAAM,OAAO,SAAS,UAAU;AAAA,IACzD,qBAAqB,MAAM,OAAO,OAAO,QAAQ;AAAA,IACjD,uBAAuB,MAAM,OAAO,OAAO,UAAU;AAAA,IACrD,mBAAmB,MAAM,MAAM,MAAM,IAAI;AAAA,IACzC,sBAAsB,MAAM,OAAO,QAAQ,QAAQ;AAAA,IACnD,wBAAwB,MAAM,OAAO,QAAQ,UAAU;AAAA,IACvD,0BAA0B,gBAAgB,yBAAyB,OAAO,CAAC;AAAA,IAC3E,YAAY,EAAE;AAAA,IACd,cAAc,MAAM,OAAO,IAAI;AAAA,IAC/B,oBAAoB,MAAM,OAAO,IAAI;AAAA,IACrC,wBAAwB,MAAM,OAAO,QAAQ,SAAS;AAAA,IACtD,oBAAoB,MAAM,OAAO,QAAQ,KAAK;AAAA,IAC9C,gBAAgB,MAAM,OAAO,QAAQ,MAAM;AAAA,IAC3C,kBAAkB,MAAM,OAAO,QAAQ;AAAA;AAAA,IAEvC,cAAc,MAAM,OAAO,QAAQ;AAAA,IACnC,uBAAuB,MAAM,YAAY;AAAA,IACzC,gBAAgB,MAAM;AAAA,IACtB,uBAAuB,wBAAwB,MAAM,CAAC;AAAA,IACtD,kBAAkB,MAAM,OAAO,QAAQ;AAAA,IACvC,kBAAkB,MAAM,OAAO,QAAQ;AAAA,IACvC,qBAAqB,MAAM,SAAS,gBAAgB,oBAAoB,MAAM,IAAI,gBAAgB,oBAAoB,OAAO,CAAC;AAAA,IAC9H,iBAAiB,MAAM,QAAQ,YAAY,CAAC;AAAA,IAC5C,iBAAiB,MAAM,QAAQ,OAAO;AAAA,EACxC;AAEA,QAAM,QAAQ;AAAA;AAAA;AAAA,IAGZ,kBAAkB,MAAM,KAAK,IAAI,CAAC;AAAA,IAClC;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,IACA;AAAA,EACF;AAEA,SAAO,MAAM,KAAK,IAAI;AACxB;AAQO,SAAS,sBAAsB,KAAiB,OAAwC;AAC7F,MAAI,CAAC,MAAO;AACZ,MAAI,OAAO,IAAI,cAAc,MAAM;AACnC,MAAI,CAAC,MAAM;AACT,WAAO,SAAS,gBAAgB,QAAQ,MAAM;AAC9C,QAAI,aAAa,MAAM,IAAI,UAAU;AAAA,EACvC;AACA,QAAM,QAAQ,SAAS,gBAAgB,QAAQ,OAAO;AACtD,QAAM,aAAa,iBAAiB,EAAE;AACtC,QAAM,cAAc,qBAAqB,KAAK;AAC9C,OAAK,aAAa,OAAO,KAAK,UAAU;AAC1C;","names":[]}
|
|
@@ -3,7 +3,7 @@ import {
|
|
|
3
3
|
renderChromeElement,
|
|
4
4
|
renderLegend,
|
|
5
5
|
stampAnimationVars
|
|
6
|
-
} from "./chunk-
|
|
6
|
+
} from "./chunk-YGYMB7KB.js";
|
|
7
7
|
|
|
8
8
|
// src/tilemap-renderer.ts
|
|
9
9
|
var SVG_NS = "http://www.w3.org/2000/svg";
|
|
@@ -154,7 +154,10 @@ function renderTiles(parent, tiles, animation) {
|
|
|
154
154
|
"dominant-baseline": "central",
|
|
155
155
|
"font-family": tile.label.style.fontFamily,
|
|
156
156
|
"font-size": tile.label.style.fontSize,
|
|
157
|
-
"font-weight": tile.label.style.fontWeight
|
|
157
|
+
"font-weight": tile.label.style.fontWeight,
|
|
158
|
+
// A hair of tracking is what keeps a two-letter code legible at 8px now
|
|
159
|
+
// that it is set at 600 instead of 700.
|
|
160
|
+
"letter-spacing": "0.02em"
|
|
158
161
|
});
|
|
159
162
|
codeLabel.style.setProperty(
|
|
160
163
|
"fill",
|
|
@@ -221,8 +224,8 @@ function renderGradientLegend(parent, layout) {
|
|
|
221
224
|
y: gradientLegend.bounds.y,
|
|
222
225
|
width: gradientLegend.bounds.width,
|
|
223
226
|
height: barHeight,
|
|
224
|
-
|
|
225
|
-
|
|
227
|
+
fill: `url(#${gradientId})`,
|
|
228
|
+
"shape-rendering": "crispEdges"
|
|
226
229
|
});
|
|
227
230
|
g.appendChild(bar);
|
|
228
231
|
const minText = createSVGElement("text");
|
|
@@ -255,6 +258,35 @@ function renderGradientLegend(parent, layout) {
|
|
|
255
258
|
);
|
|
256
259
|
maxText.textContent = gradientLegend.maxLabel;
|
|
257
260
|
g.appendChild(maxText);
|
|
261
|
+
if (gradientLegend.noData) {
|
|
262
|
+
const nd = gradientLegend.noData;
|
|
263
|
+
const swatch = createSVGElement("rect");
|
|
264
|
+
swatch.setAttribute("class", "oc-legend-nodata");
|
|
265
|
+
setAttrs(swatch, {
|
|
266
|
+
x: nd.x,
|
|
267
|
+
y: nd.y,
|
|
268
|
+
width: nd.size,
|
|
269
|
+
height: nd.size,
|
|
270
|
+
fill: nd.fill,
|
|
271
|
+
"shape-rendering": "crispEdges"
|
|
272
|
+
});
|
|
273
|
+
g.appendChild(swatch);
|
|
274
|
+
const ndLabel = createSVGElement("text");
|
|
275
|
+
setAttrs(ndLabel, {
|
|
276
|
+
x: nd.labelX,
|
|
277
|
+
y: nd.labelY,
|
|
278
|
+
"text-anchor": "start",
|
|
279
|
+
"font-family": gradientLegend.labelStyle.fontFamily,
|
|
280
|
+
"font-size": gradientLegend.labelStyle.fontSize,
|
|
281
|
+
"font-weight": gradientLegend.labelStyle.fontWeight
|
|
282
|
+
});
|
|
283
|
+
ndLabel.style.setProperty(
|
|
284
|
+
"fill",
|
|
285
|
+
gradientLegend.labelStyle.fill
|
|
286
|
+
);
|
|
287
|
+
ndLabel.textContent = nd.label;
|
|
288
|
+
g.appendChild(ndLabel);
|
|
289
|
+
}
|
|
258
290
|
parent.appendChild(g);
|
|
259
291
|
}
|
|
260
292
|
function renderTileMapSVG(layout, opts) {
|
|
@@ -295,4 +327,4 @@ function renderTileMapSVG(layout, opts) {
|
|
|
295
327
|
export {
|
|
296
328
|
renderTileMapSVG
|
|
297
329
|
};
|
|
298
|
-
//# sourceMappingURL=chunk-
|
|
330
|
+
//# sourceMappingURL=chunk-LNPRDSK3.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/tilemap-renderer.ts"],"sourcesContent":["/**\n * TileMap SVG renderer: converts a TileMapLayout into SVG DOM elements.\n *\n * Creates an <svg> with tile rectangles, state code labels, value labels,\n * gradient legend, and chrome. All styling via inline SVG attributes from\n * layout data. Animation is pure CSS, driven by data attributes.\n */\n\nimport type {\n ResolvedAnimation,\n TileMapLayout,\n TileMapTileMark,\n} from '@opendata-ai/openchart-core';\nimport { stampAnimationVars } from './animation-vars';\nimport { renderChromeElement } from './renderers/chrome';\nimport { renderLegend } from './renderers/legend';\nimport { nextSvgId } from './svg-ids';\n\nconst SVG_NS = 'http://www.w3.org/2000/svg';\nconst XLINK_NS = 'http://www.w3.org/1999/xlink';\nconst BRAND_URL = 'https://tryopendata.ai';\n\n// ---------------------------------------------------------------------------\n// Helpers\n// ---------------------------------------------------------------------------\n\nfunction createSVGElement(tag: string): SVGElement {\n return document.createElementNS(SVG_NS, tag);\n}\n\nfunction setAttrs(el: SVGElement, attrs: Record<string, string | number>): void {\n for (const [key, value] of Object.entries(attrs)) {\n el.setAttribute(key, String(value));\n }\n}\n\n// ---------------------------------------------------------------------------\n// Chrome rendering (delegates to the shared helper for text wrapping)\n// ---------------------------------------------------------------------------\n\nfunction renderChrome(parent: SVGElement, layout: TileMapLayout): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'oc-chrome');\n\n const { chrome, measureText } = layout;\n const bottomOffset = layout.area.y + layout.area.height;\n\n if (chrome.title) {\n renderChromeElement(g, chrome.title, 'oc-title', 'title', measureText);\n }\n if (chrome.subtitle) {\n renderChromeElement(g, chrome.subtitle, 'oc-subtitle', 'subtitle', measureText);\n }\n if (chrome.source) {\n renderChromeElement(\n g,\n { ...chrome.source, y: bottomOffset + chrome.source.y },\n 'oc-source',\n 'source',\n measureText,\n );\n }\n if (chrome.byline) {\n renderChromeElement(\n g,\n { ...chrome.byline, y: bottomOffset + chrome.byline.y },\n 'oc-byline',\n 'byline',\n measureText,\n );\n }\n if (chrome.footer) {\n renderChromeElement(\n g,\n { ...chrome.footer, y: bottomOffset + chrome.footer.y },\n 'oc-footer',\n 'footer',\n measureText,\n );\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Watermark rendering\n// ---------------------------------------------------------------------------\n\nfunction renderWatermark(parent: SVGElement, layout: TileMapLayout): void {\n if (layout.width < 480) return; // Don't render if too narrow\n\n const { width, height } = layout;\n const { theme } = layout;\n const padding = theme.spacing.padding;\n const rightEdge = width - padding;\n const bottomEdge = height - padding;\n const fill = theme.colors.axis;\n\n const a = createSVGElement('a');\n a.setAttribute('href', BRAND_URL);\n a.setAttributeNS(XLINK_NS, 'xlink:href', BRAND_URL);\n a.setAttribute('target', '_blank');\n a.setAttribute('rel', 'noopener');\n a.setAttribute('class', 'oc-chrome-ref');\n\n const text = createSVGElement('text');\n setAttrs(text, {\n x: rightEdge,\n y: bottomEdge,\n 'dominant-baseline': 'alphabetic',\n 'text-anchor': 'end',\n 'font-family': theme.fonts.family,\n 'font-size': 12,\n 'fill-opacity': 0.55,\n });\n (text as SVGElement & ElementCSSInlineStyle).style.setProperty('fill', fill);\n\n const trySpan = createSVGElement('tspan');\n setAttrs(trySpan, { 'font-weight': 500 });\n trySpan.textContent = 'try';\n\n const openDataSpan = createSVGElement('tspan');\n setAttrs(openDataSpan, { 'font-weight': 600, 'font-size': 16 });\n openDataSpan.textContent = 'OpenData';\n\n const aiSpan = createSVGElement('tspan');\n setAttrs(aiSpan, { 'font-weight': 500 });\n aiSpan.textContent = '.ai';\n\n text.appendChild(trySpan);\n text.appendChild(openDataSpan);\n text.appendChild(aiSpan);\n a.appendChild(text);\n parent.appendChild(a);\n}\n\n// ---------------------------------------------------------------------------\n// Tiles rendering\n// ---------------------------------------------------------------------------\n\nfunction renderTiles(\n parent: SVGElement,\n tiles: TileMapTileMark[],\n animation?: ResolvedAnimation,\n): void {\n const g = createSVGElement('g');\n g.setAttribute('class', 'oc-tilemap-tiles');\n g.setAttribute('role', 'list');\n\n // Compute per-tile delays with jitter for organic feel.\n // Base delay spreads tiles across ~800ms window, jitter adds +-40% variation\n // so some tiles pop in clusters while others have longer gaps.\n const tileDelays: number[] = [];\n if (animation?.enter) {\n const baseStagger = 800 / Math.max(tiles.length, 1);\n let seed = 17;\n for (let i = 0; i < tiles.length; i++) {\n const idx = tiles[i].animationIndex ?? i;\n seed = (seed * 1103515245 + 12345) & 0x7fffffff;\n const jitter = ((seed % 1000) / 1000 - 0.5) * 0.8;\n tileDelays.push(Math.max(0, Math.round(idx * baseStagger * (1 + jitter))));\n }\n }\n\n for (let i = 0; i < tiles.length; i++) {\n const tile = tiles[i];\n const tileGroup = createSVGElement('g');\n tileGroup.setAttribute('class', 'oc-tilemap-tile');\n tileGroup.setAttribute('data-state', tile.stateCode);\n tileGroup.setAttribute('role', 'listitem');\n if (tile.aria?.label) {\n tileGroup.setAttribute('aria-label', tile.aria.label);\n }\n\n if (animation?.enter) {\n const idx = tile.animationIndex ?? i;\n tileGroup.setAttribute('data-animation-index', String(idx));\n (tileGroup as SVGElement & ElementCSSInlineStyle).style.setProperty(\n '--oc-mark-index',\n String(idx),\n );\n (tileGroup as SVGElement & ElementCSSInlineStyle).style.setProperty(\n '--oc-tile-delay',\n `${tileDelays[i]}ms`,\n );\n }\n\n // Tile background rect\n const rect = createSVGElement('rect');\n setAttrs(rect, {\n x: tile.x,\n y: tile.y,\n width: tile.size,\n height: tile.size,\n rx: tile.cornerRadius,\n fill: tile.fill,\n 'fill-opacity': tile.fillOpacity ?? 1,\n stroke: tile.stroke,\n 'stroke-width': tile.strokeWidth,\n });\n tileGroup.appendChild(rect);\n\n // State code label\n const codeLabel = createSVGElement('text');\n setAttrs(codeLabel, {\n x: tile.label.x,\n y: tile.label.y,\n 'text-anchor': 'middle',\n 'dominant-baseline': 'central',\n 'font-family': tile.label.style.fontFamily,\n 'font-size': tile.label.style.fontSize,\n 'font-weight': tile.label.style.fontWeight,\n // A hair of tracking is what keeps a two-letter code legible at 8px now\n // that it is set at 600 instead of 700.\n 'letter-spacing': '0.02em',\n });\n (codeLabel as SVGElement & ElementCSSInlineStyle).style.setProperty(\n 'fill',\n tile.label.style.fill,\n );\n codeLabel.textContent = tile.label.text;\n tileGroup.appendChild(codeLabel);\n\n // Value label (if visible)\n if (tile.valueLabel.visible && tile.valueLabel.text) {\n const valueLabel = createSVGElement('text');\n setAttrs(valueLabel, {\n x: tile.valueLabel.x,\n y: tile.valueLabel.y,\n 'text-anchor': 'middle',\n 'dominant-baseline': 'central',\n 'font-family': tile.valueLabel.style.fontFamily,\n 'font-size': tile.valueLabel.style.fontSize,\n 'font-weight': tile.valueLabel.style.fontWeight,\n });\n (valueLabel as SVGElement & ElementCSSInlineStyle).style.setProperty(\n 'fill',\n tile.valueLabel.style.fill,\n );\n valueLabel.textContent = tile.valueLabel.text;\n tileGroup.appendChild(valueLabel);\n }\n\n g.appendChild(tileGroup);\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Gradient legend rendering\n// ---------------------------------------------------------------------------\n\nfunction renderGradientLegend(parent: SVGElement, layout: TileMapLayout): void {\n if (!layout.gradientLegend) return;\n\n const { gradientLegend } = layout;\n const g = createSVGElement('g');\n g.setAttribute('class', 'oc-tilemap-legend');\n\n // Build linear gradient in defs\n const defs = parent.querySelector('defs') || createSVGElement('defs');\n const exists = parent.querySelector('defs');\n if (!exists) {\n parent.insertBefore(defs, parent.firstChild);\n }\n\n const gradientId = nextSvgId('oc-tilemap-legend-gradient');\n const grad = createSVGElement('linearGradient');\n grad.id = gradientId;\n grad.setAttribute('x1', '0%');\n grad.setAttribute('y1', '0%');\n grad.setAttribute('x2', '100%');\n grad.setAttribute('y2', '0%');\n\n for (const stop of gradientLegend.colorStops) {\n const s = createSVGElement('stop');\n const attrs: Record<string, string | number> = {\n offset: `${stop.offset * 100}%`,\n 'stop-color': stop.color,\n };\n if (stop.opacity !== undefined) {\n attrs['stop-opacity'] = stop.opacity;\n }\n setAttrs(s, attrs);\n grad.appendChild(s);\n }\n\n (defs as SVGElement).appendChild(grad);\n\n // Gradient bar: square-cornered, so it reads as a color scale rather than a\n // pill-shaped UI control.\n const barHeight = gradientLegend.bounds.height;\n const bar = createSVGElement('rect');\n setAttrs(bar, {\n x: gradientLegend.bounds.x,\n y: gradientLegend.bounds.y,\n width: gradientLegend.bounds.width,\n height: barHeight,\n fill: `url(#${gradientId})`,\n 'shape-rendering': 'crispEdges',\n });\n g.appendChild(bar);\n\n // Min label\n const minText = createSVGElement('text');\n setAttrs(minText, {\n x: gradientLegend.bounds.x,\n y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,\n 'text-anchor': 'start',\n 'font-family': gradientLegend.labelStyle.fontFamily,\n 'font-size': gradientLegend.labelStyle.fontSize,\n 'font-weight': gradientLegend.labelStyle.fontWeight,\n });\n (minText as SVGElement & ElementCSSInlineStyle).style.setProperty(\n 'fill',\n gradientLegend.labelStyle.fill,\n );\n minText.textContent = gradientLegend.minLabel;\n g.appendChild(minText);\n\n // Max label\n const maxText = createSVGElement('text');\n setAttrs(maxText, {\n x: gradientLegend.bounds.x + gradientLegend.bounds.width,\n y: gradientLegend.bounds.y + gradientLegend.bounds.height + 14,\n 'text-anchor': 'end',\n 'font-family': gradientLegend.labelStyle.fontFamily,\n 'font-size': gradientLegend.labelStyle.fontSize,\n 'font-weight': gradientLegend.labelStyle.fontWeight,\n });\n (maxText as SVGElement & ElementCSSInlineStyle).style.setProperty(\n 'fill',\n gradientLegend.labelStyle.fill,\n );\n maxText.textContent = gradientLegend.maxLabel;\n g.appendChild(maxText);\n\n // Detached \"no data\" swatch, keyed beside the ramp rather than at its low end.\n if (gradientLegend.noData) {\n const nd = gradientLegend.noData;\n const swatch = createSVGElement('rect');\n swatch.setAttribute('class', 'oc-legend-nodata');\n setAttrs(swatch, {\n x: nd.x,\n y: nd.y,\n width: nd.size,\n height: nd.size,\n fill: nd.fill,\n 'shape-rendering': 'crispEdges',\n });\n g.appendChild(swatch);\n\n const ndLabel = createSVGElement('text');\n setAttrs(ndLabel, {\n x: nd.labelX,\n y: nd.labelY,\n 'text-anchor': 'start',\n 'font-family': gradientLegend.labelStyle.fontFamily,\n 'font-size': gradientLegend.labelStyle.fontSize,\n 'font-weight': gradientLegend.labelStyle.fontWeight,\n });\n (ndLabel as SVGElement & ElementCSSInlineStyle).style.setProperty(\n 'fill',\n gradientLegend.labelStyle.fill,\n );\n ndLabel.textContent = nd.label;\n g.appendChild(ndLabel);\n }\n\n parent.appendChild(g);\n}\n\n// ---------------------------------------------------------------------------\n// Public API\n// ---------------------------------------------------------------------------\n\n/**\n * Render a TileMapLayout to an SVG element.\n */\nexport function renderTileMapSVG(\n layout: TileMapLayout,\n opts?: { animate?: boolean },\n): SVGSVGElement {\n const { width, height, tiles, a11y, watermark, animation } = layout;\n const animate = opts?.animate && !!animation?.enter;\n\n const svg = createSVGElement('svg') as SVGSVGElement;\n svg.setAttribute('viewBox', `0 0 ${width} ${height}`);\n svg.setAttribute('role', 'img');\n if (a11y.altText) {\n svg.setAttribute('aria-label', a11y.altText);\n }\n\n const classes = animate ? 'oc-tilemap oc-animate' : 'oc-tilemap';\n svg.setAttribute('class', classes);\n\n if (animate && animation?.enter) {\n // Target ~1s total: stagger window ~800ms + per-tile pop ~200ms\n const stagger = Math.max(5, Math.round(800 / Math.max(tiles.length, 1)));\n stampAnimationVars(svg, {\n duration: animation.enter.duration,\n stagger,\n annotationDelay: animation.annotationDelay,\n ease: animation.enter.ease,\n });\n }\n\n // Empty defs element (will be filled by gradient legend)\n const defs = createSVGElement('defs');\n svg.appendChild(defs);\n\n // Render chrome\n renderChrome(svg, layout);\n\n // Render tiles\n renderTiles(svg, tiles, animate ? animation : undefined);\n\n // Render legend (gradient for quantitative, categorical for nominal)\n if (layout.categoricalLegend) {\n renderLegend(svg, layout.categoricalLegend);\n } else {\n renderGradientLegend(svg, layout);\n }\n\n // Render watermark\n if (watermark) {\n renderWatermark(svg, layout);\n }\n\n return svg;\n}\n"],"mappings":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
injectThemeStyleBlock
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-HGRNXLDF.js";
|
|
4
4
|
|
|
5
5
|
// src/export.ts
|
|
6
6
|
function getSVGDimensions(svg) {
|
|
@@ -279,4 +279,4 @@ export {
|
|
|
279
279
|
exportJPG,
|
|
280
280
|
exportCSV
|
|
281
281
|
};
|
|
282
|
-
//# sourceMappingURL=chunk-
|
|
282
|
+
//# sourceMappingURL=chunk-UOB422LY.js.map
|
|
@@ -4,7 +4,7 @@ import {
|
|
|
4
4
|
applyTextStyle,
|
|
5
5
|
createSVGElement,
|
|
6
6
|
setAttrs
|
|
7
|
-
} from "./chunk-
|
|
7
|
+
} from "./chunk-HGRNXLDF.js";
|
|
8
8
|
|
|
9
9
|
// src/svg-ids.ts
|
|
10
10
|
var counter = 0;
|
|
@@ -195,7 +195,11 @@ function renderLineMark(mark, index) {
|
|
|
195
195
|
d,
|
|
196
196
|
fill: "none",
|
|
197
197
|
stroke: mark.stroke,
|
|
198
|
-
"stroke-width": mark.strokeWidth
|
|
198
|
+
"stroke-width": mark.strokeWidth,
|
|
199
|
+
// Round joins/caps: at 2px a mitre on a sharp data spike renders as a
|
|
200
|
+
// spur past the vertex, and a butt cap leaves a squared-off line end.
|
|
201
|
+
"stroke-linejoin": "round",
|
|
202
|
+
"stroke-linecap": "round"
|
|
199
203
|
});
|
|
200
204
|
if (mark.strokeDasharray) {
|
|
201
205
|
path.setAttribute("stroke-dasharray", mark.strokeDasharray);
|
|
@@ -253,7 +257,9 @@ function renderAreaMark(mark, index) {
|
|
|
253
257
|
d: mark.topPath,
|
|
254
258
|
fill: "none",
|
|
255
259
|
stroke: mark.stroke,
|
|
256
|
-
"stroke-width": mark.strokeWidth ?? 1
|
|
260
|
+
"stroke-width": mark.strokeWidth ?? 1,
|
|
261
|
+
"stroke-linejoin": "round",
|
|
262
|
+
"stroke-linecap": "round"
|
|
257
263
|
});
|
|
258
264
|
g.appendChild(strokePath);
|
|
259
265
|
}
|
|
@@ -311,6 +317,9 @@ function renderRectMark(mark, index) {
|
|
|
311
317
|
if (mark.strokeWidth) {
|
|
312
318
|
shapeEl.setAttribute("stroke-width", String(mark.strokeWidth));
|
|
313
319
|
}
|
|
320
|
+
if (mark.shapeRendering) {
|
|
321
|
+
shapeEl.setAttribute("shape-rendering", mark.shapeRendering);
|
|
322
|
+
}
|
|
314
323
|
g.appendChild(shapeEl);
|
|
315
324
|
return g;
|
|
316
325
|
}
|
|
@@ -402,6 +411,9 @@ function renderRuleMark(mark, index) {
|
|
|
402
411
|
if (mark.strokeDasharray) {
|
|
403
412
|
line.setAttribute("stroke-dasharray", mark.strokeDasharray);
|
|
404
413
|
}
|
|
414
|
+
if (mark.strokeOpacity != null) {
|
|
415
|
+
line.setAttribute("stroke-opacity", String(mark.strokeOpacity));
|
|
416
|
+
}
|
|
405
417
|
if (mark.opacity != null) {
|
|
406
418
|
line.setAttribute("opacity", String(mark.opacity));
|
|
407
419
|
}
|
|
@@ -454,6 +466,10 @@ function getMarkSeries(mark) {
|
|
|
454
466
|
if (mark.type === "line" || mark.type === "area") {
|
|
455
467
|
return mark.seriesKey;
|
|
456
468
|
}
|
|
469
|
+
const explicitSeries = mark.seriesKey;
|
|
470
|
+
if (explicitSeries) {
|
|
471
|
+
return explicitSeries;
|
|
472
|
+
}
|
|
457
473
|
if (mark.type === "arc") {
|
|
458
474
|
const label = mark.aria.label;
|
|
459
475
|
if (!label) return void 0;
|
|
@@ -1172,7 +1188,22 @@ function renderChrome(parent, layout) {
|
|
|
1172
1188
|
const g = createSVGElement("g");
|
|
1173
1189
|
g.setAttribute("class", "oc-chrome");
|
|
1174
1190
|
const { chrome, measureText } = layout;
|
|
1175
|
-
if (chrome.
|
|
1191
|
+
if (chrome.rule) {
|
|
1192
|
+
const rule = createSVGElement("rect");
|
|
1193
|
+
rule.setAttribute("class", "oc-chrome-rule");
|
|
1194
|
+
setAttrs(rule, {
|
|
1195
|
+
x: chrome.rule.x,
|
|
1196
|
+
y: chrome.rule.y,
|
|
1197
|
+
width: chrome.rule.width,
|
|
1198
|
+
height: chrome.rule.thickness
|
|
1199
|
+
});
|
|
1200
|
+
rule.setAttribute("fill", chrome.rule.color);
|
|
1201
|
+
rule.setAttribute("shape-rendering", "crispEdges");
|
|
1202
|
+
g.appendChild(rule);
|
|
1203
|
+
}
|
|
1204
|
+
if (chrome.eyebrow && chrome.rule) {
|
|
1205
|
+
renderChromeElement(g, chrome.eyebrow, "oc-eyebrow", "eyebrow", measureText, true);
|
|
1206
|
+
} else if (chrome.eyebrow) {
|
|
1176
1207
|
const eyebrow = chrome.eyebrow;
|
|
1177
1208
|
const dotR = 3;
|
|
1178
1209
|
const dotGap = 8;
|
|
@@ -1397,6 +1428,8 @@ function renderEndpointLabels(parent, layout) {
|
|
|
1397
1428
|
|
|
1398
1429
|
// src/renderers/legend.ts
|
|
1399
1430
|
import { estimateTextWidth as estimateTextWidth2 } from "@opendata-ai/openchart-core";
|
|
1431
|
+
var LEGEND_HIT_HEIGHT = 24;
|
|
1432
|
+
var LEGEND_HIT_PAD = 4;
|
|
1400
1433
|
function isCategorical(legend) {
|
|
1401
1434
|
return !legend.type || legend.type === "categorical";
|
|
1402
1435
|
}
|
|
@@ -1446,7 +1479,8 @@ function renderContinuousLegend(parent, legend) {
|
|
|
1446
1479
|
});
|
|
1447
1480
|
g.appendChild(bar);
|
|
1448
1481
|
} else {
|
|
1449
|
-
for (
|
|
1482
|
+
for (let i = 0; i < legend.bins.length; i++) {
|
|
1483
|
+
const bin = legend.bins[i];
|
|
1450
1484
|
const rect = createSVGElement("rect");
|
|
1451
1485
|
rect.setAttribute("class", "oc-legend-bin");
|
|
1452
1486
|
setAttrs(rect, {
|
|
@@ -1454,11 +1488,21 @@ function renderContinuousLegend(parent, legend) {
|
|
|
1454
1488
|
y: legend.bar.y,
|
|
1455
1489
|
width: bin.width,
|
|
1456
1490
|
height: legend.bar.height,
|
|
1457
|
-
fill: bin.color
|
|
1491
|
+
fill: bin.color,
|
|
1492
|
+
"shape-rendering": "crispEdges"
|
|
1458
1493
|
});
|
|
1494
|
+
rect.setAttribute("data-bin-index", String(i));
|
|
1459
1495
|
g.appendChild(rect);
|
|
1460
1496
|
}
|
|
1461
1497
|
}
|
|
1498
|
+
if (legend.title && legend.titleY !== void 0) {
|
|
1499
|
+
const title = createSVGElement("text");
|
|
1500
|
+
title.setAttribute("class", "oc-legend-title");
|
|
1501
|
+
setAttrs(title, { x: legend.bar.x, y: legend.titleY, "text-anchor": "start" });
|
|
1502
|
+
applyTextStyle(title, legend.titleStyle ?? legend.labelStyle);
|
|
1503
|
+
title.textContent = legend.title;
|
|
1504
|
+
g.appendChild(title);
|
|
1505
|
+
}
|
|
1462
1506
|
for (const tick of legend.ticks) {
|
|
1463
1507
|
const label = createSVGElement("text");
|
|
1464
1508
|
setAttrs(label, { x: tick.x, y: legend.labelY, "text-anchor": tick.anchor });
|
|
@@ -1466,6 +1510,28 @@ function renderContinuousLegend(parent, legend) {
|
|
|
1466
1510
|
label.textContent = tick.label;
|
|
1467
1511
|
g.appendChild(label);
|
|
1468
1512
|
}
|
|
1513
|
+
if (legend.noData) {
|
|
1514
|
+
const swatch = createSVGElement("rect");
|
|
1515
|
+
swatch.setAttribute("class", "oc-legend-nodata");
|
|
1516
|
+
setAttrs(swatch, {
|
|
1517
|
+
x: legend.noData.x,
|
|
1518
|
+
y: legend.noData.y,
|
|
1519
|
+
width: legend.noData.size,
|
|
1520
|
+
height: legend.noData.size,
|
|
1521
|
+
fill: legend.noData.fill,
|
|
1522
|
+
"shape-rendering": "crispEdges"
|
|
1523
|
+
});
|
|
1524
|
+
g.appendChild(swatch);
|
|
1525
|
+
const label = createSVGElement("text");
|
|
1526
|
+
setAttrs(label, {
|
|
1527
|
+
x: legend.noData.labelX,
|
|
1528
|
+
y: legend.noData.labelY,
|
|
1529
|
+
"text-anchor": "start"
|
|
1530
|
+
});
|
|
1531
|
+
applyTextStyle(label, legend.labelStyle);
|
|
1532
|
+
label.textContent = legend.noData.label;
|
|
1533
|
+
g.appendChild(label);
|
|
1534
|
+
}
|
|
1469
1535
|
parent.appendChild(g);
|
|
1470
1536
|
}
|
|
1471
1537
|
function renderSizeLegend(parent, legend) {
|
|
@@ -1477,6 +1543,14 @@ function renderSizeLegend(parent, legend) {
|
|
|
1477
1543
|
const oy = legend.bounds.y;
|
|
1478
1544
|
const labelX = ox + legend.circles[0].cx + legend.circles[0].radius + 8;
|
|
1479
1545
|
const leaderX = ox + legend.circles[0].cx + legend.circles[0].radius + 4;
|
|
1546
|
+
if (legend.title && legend.titleY !== void 0) {
|
|
1547
|
+
const title = createSVGElement("text");
|
|
1548
|
+
title.setAttribute("class", "oc-legend-title");
|
|
1549
|
+
setAttrs(title, { x: ox, y: oy + legend.titleY, "text-anchor": "start" });
|
|
1550
|
+
applyTextStyle(title, legend.titleStyle ?? legend.labelStyle);
|
|
1551
|
+
title.textContent = legend.title;
|
|
1552
|
+
g.appendChild(title);
|
|
1553
|
+
}
|
|
1480
1554
|
for (const circle of legend.circles) {
|
|
1481
1555
|
const c = createSVGElement("circle");
|
|
1482
1556
|
setAttrs(c, {
|
|
@@ -1522,7 +1596,7 @@ function renderLegend(parent, legend) {
|
|
|
1522
1596
|
if (!isCategorical(legend) || legend.entries.length === 0) return;
|
|
1523
1597
|
const g = createSVGElement("g");
|
|
1524
1598
|
g.setAttribute("class", "oc-legend");
|
|
1525
|
-
g.setAttribute("role", "
|
|
1599
|
+
g.setAttribute("role", "group");
|
|
1526
1600
|
g.setAttribute("aria-label", "Chart legend");
|
|
1527
1601
|
const isHorizontal = legend.position === "top" || legend.position === "top-left" || legend.position === "bottom";
|
|
1528
1602
|
const positions = "entryPositions" in legend ? legend.entryPositions : void 0;
|
|
@@ -1548,7 +1622,6 @@ function renderLegend(parent, legend) {
|
|
|
1548
1622
|
}
|
|
1549
1623
|
const entryG = createSVGElement("g");
|
|
1550
1624
|
entryG.setAttribute("class", "oc-legend-entry");
|
|
1551
|
-
entryG.setAttribute("role", "listitem");
|
|
1552
1625
|
entryG.setAttribute("data-legend-index", String(i));
|
|
1553
1626
|
entryG.setAttribute("data-legend-label", entry.label);
|
|
1554
1627
|
if (entry.overflow) {
|
|
@@ -1556,14 +1629,32 @@ function renderLegend(parent, legend) {
|
|
|
1556
1629
|
entryG.setAttribute("aria-label", entry.label);
|
|
1557
1630
|
entryG.setAttribute("opacity", "0.5");
|
|
1558
1631
|
} else {
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
);
|
|
1632
|
+
const visible = entry.active !== false;
|
|
1633
|
+
entryG.setAttribute("role", "button");
|
|
1634
|
+
entryG.setAttribute("tabindex", "0");
|
|
1635
|
+
entryG.setAttribute("aria-pressed", String(visible));
|
|
1636
|
+
entryG.setAttribute("data-legend-active", String(visible));
|
|
1637
|
+
entryG.setAttribute("aria-label", `${entry.label}: ${visible ? "visible" : "hidden"}`);
|
|
1563
1638
|
entryG.setAttribute("style", "cursor: pointer");
|
|
1564
|
-
if (
|
|
1639
|
+
if (!visible) {
|
|
1565
1640
|
entryG.setAttribute("opacity", "0.3");
|
|
1566
1641
|
}
|
|
1642
|
+
const labelWidth = estimateTextWidth2(
|
|
1643
|
+
entry.label,
|
|
1644
|
+
legend.labelStyle.fontSize,
|
|
1645
|
+
legend.labelStyle.fontWeight
|
|
1646
|
+
);
|
|
1647
|
+
const hit = createSVGElement("rect");
|
|
1648
|
+
hit.setAttribute("class", "oc-legend-hit");
|
|
1649
|
+
setAttrs(hit, {
|
|
1650
|
+
x: offsetX - LEGEND_HIT_PAD,
|
|
1651
|
+
y: offsetY + legend.swatchSize / 2 - LEGEND_HIT_HEIGHT / 2,
|
|
1652
|
+
width: legend.swatchSize + legend.swatchGap + labelWidth + LEGEND_HIT_PAD * 2,
|
|
1653
|
+
height: LEGEND_HIT_HEIGHT,
|
|
1654
|
+
rx: 2,
|
|
1655
|
+
fill: "transparent"
|
|
1656
|
+
});
|
|
1657
|
+
entryG.appendChild(hit);
|
|
1567
1658
|
}
|
|
1568
1659
|
const midX = offsetX + legend.swatchSize / 2;
|
|
1569
1660
|
const midY = offsetY + legend.swatchSize / 2;
|
|
@@ -1952,4 +2043,4 @@ export {
|
|
|
1952
2043
|
renderSingleMark,
|
|
1953
2044
|
renderChartSVG
|
|
1954
2045
|
};
|
|
1955
|
-
//# sourceMappingURL=chunk-
|
|
2046
|
+
//# sourceMappingURL=chunk-YGYMB7KB.js.map
|