@opendata-ai/openchart-vanilla 8.4.0 → 8.4.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/{chunk-LNPRDSK3.js → chunk-3UIUANPI.js} +2 -2
- package/dist/{chunk-45XZOWYC.js → chunk-5A2NLYUW.js} +38 -67
- package/dist/chunk-5A2NLYUW.js.map +1 -0
- package/dist/{chunk-HGRNXLDF.js → chunk-5RX6ZQGZ.js} +10 -1
- package/dist/chunk-5RX6ZQGZ.js.map +1 -0
- package/dist/{chunk-UOB422LY.js → chunk-6B5OBGIO.js} +2 -2
- package/dist/{chunk-YGYMB7KB.js → chunk-KANNWAEA.js} +109 -30
- package/dist/chunk-KANNWAEA.js.map +1 -0
- package/dist/{chunk-FETYAVTZ.js → chunk-RYVV3YIG.js} +3 -3
- package/dist/export-gif.js +3 -3
- package/dist/index.js +7 -7
- 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-45XZOWYC.js.map +0 -1
- package/dist/chunk-HGRNXLDF.js.map +0 -1
- package/dist/chunk-YGYMB7KB.js.map +0 -1
- /package/dist/{chunk-LNPRDSK3.js.map → chunk-3UIUANPI.js.map} +0 -0
- /package/dist/{chunk-UOB422LY.js.map → chunk-6B5OBGIO.js.map} +0 -0
- /package/dist/{chunk-FETYAVTZ.js.map → chunk-RYVV3YIG.js.map} +0 -0
|
@@ -9,6 +9,14 @@ function setAttrs(el, attrs) {
|
|
|
9
9
|
el.setAttribute(key, String(value));
|
|
10
10
|
}
|
|
11
11
|
}
|
|
12
|
+
function applyKnockoutHalo(el, color, fontSize) {
|
|
13
|
+
setAttrs(el, {
|
|
14
|
+
stroke: color,
|
|
15
|
+
"stroke-width": Math.round(fontSize * 0.3),
|
|
16
|
+
"stroke-linejoin": "round",
|
|
17
|
+
"paint-order": "stroke"
|
|
18
|
+
});
|
|
19
|
+
}
|
|
12
20
|
function applyTextStyle(el, style) {
|
|
13
21
|
const inline = el.style;
|
|
14
22
|
inline.setProperty("fill", style.fill);
|
|
@@ -111,8 +119,9 @@ export {
|
|
|
111
119
|
XLINK_NS,
|
|
112
120
|
createSVGElement,
|
|
113
121
|
setAttrs,
|
|
122
|
+
applyKnockoutHalo,
|
|
114
123
|
applyTextStyle,
|
|
115
124
|
buildThemeStyleBlock,
|
|
116
125
|
injectThemeStyleBlock
|
|
117
126
|
};
|
|
118
|
-
//# sourceMappingURL=chunk-
|
|
127
|
+
//# sourceMappingURL=chunk-5RX6ZQGZ.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\n/**\n * Cut a knockout halo behind text so it stays legible where it crosses\n * gridlines, marks, or a neighbouring series: a surface-colored stroke painted\n * under the glyphs.\n *\n * Presentation attributes, not inline styles, so a stylesheet can still\n * override the halo. The width scales with the font size -- a fixed width rings\n * small glyphs and disappears behind large ones.\n */\nexport function applyKnockoutHalo(el: SVGElement, color: string, fontSize: number): void {\n setAttrs(el, {\n stroke: color,\n 'stroke-width': Math.round(fontSize * 0.3),\n 'stroke-linejoin': 'round',\n 'paint-order': 'stroke',\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":"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","names":[]}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {
|
|
2
2
|
injectThemeStyleBlock
|
|
3
|
-
} from "./chunk-
|
|
3
|
+
} from "./chunk-5RX6ZQGZ.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-6B5OBGIO.js.map
|
|
@@ -1,10 +1,11 @@
|
|
|
1
1
|
import {
|
|
2
2
|
SVG_NS,
|
|
3
3
|
XLINK_NS,
|
|
4
|
+
applyKnockoutHalo,
|
|
4
5
|
applyTextStyle,
|
|
5
6
|
createSVGElement,
|
|
6
7
|
setAttrs
|
|
7
|
-
} from "./chunk-
|
|
8
|
+
} from "./chunk-5RX6ZQGZ.js";
|
|
8
9
|
|
|
9
10
|
// src/svg-ids.ts
|
|
10
11
|
var counter = 0;
|
|
@@ -156,15 +157,22 @@ function resolvePatternFill(pattern, patternMap) {
|
|
|
156
157
|
var currentAnimation;
|
|
157
158
|
var currentGradientMap = /* @__PURE__ */ new Map();
|
|
158
159
|
var currentPatternMap = /* @__PURE__ */ new Map();
|
|
160
|
+
var currentSurface = "";
|
|
159
161
|
function setMarkRenderState(state) {
|
|
160
162
|
currentAnimation = state.animation;
|
|
161
163
|
currentGradientMap = state.gradientMap;
|
|
162
164
|
currentPatternMap = state.patternMap ?? /* @__PURE__ */ new Map();
|
|
165
|
+
currentSurface = state.surface ?? "";
|
|
163
166
|
}
|
|
164
167
|
function resetMarkRenderState() {
|
|
165
168
|
currentAnimation = void 0;
|
|
166
169
|
currentGradientMap = /* @__PURE__ */ new Map();
|
|
167
170
|
currentPatternMap = /* @__PURE__ */ new Map();
|
|
171
|
+
currentSurface = "";
|
|
172
|
+
}
|
|
173
|
+
function applyLabelHalo(text, label) {
|
|
174
|
+
if (!currentSurface || label.halo === false) return;
|
|
175
|
+
applyKnockoutHalo(text, currentSurface, label.style.fontSize);
|
|
168
176
|
}
|
|
169
177
|
function resolveFillOrPattern(fill, pattern) {
|
|
170
178
|
if (pattern) return resolvePatternFill(pattern, currentPatternMap);
|
|
@@ -217,6 +225,7 @@ function renderLineMark(mark, index) {
|
|
|
217
225
|
}
|
|
218
226
|
setAttrs(label, { x: mark.label.x, y: mark.label.y });
|
|
219
227
|
applyTextStyle(label, mark.label.style);
|
|
228
|
+
applyLabelHalo(label, mark.label);
|
|
220
229
|
label.textContent = mark.label.text;
|
|
221
230
|
g.appendChild(label);
|
|
222
231
|
if (mark.label.connector) {
|
|
@@ -345,6 +354,7 @@ function renderArcMark(mark, index) {
|
|
|
345
354
|
y: mark.label.y - mark.center.y
|
|
346
355
|
});
|
|
347
356
|
applyTextStyle(label, mark.label.style);
|
|
357
|
+
applyLabelHalo(label, mark.label);
|
|
348
358
|
label.textContent = mark.label.text;
|
|
349
359
|
g.appendChild(label);
|
|
350
360
|
}
|
|
@@ -529,6 +539,7 @@ function renderMarks(parent, layout, opts) {
|
|
|
529
539
|
label.setAttribute("class", "oc-mark-label");
|
|
530
540
|
setAttrs(label, { x: rect.label.x, y: rect.label.y });
|
|
531
541
|
applyTextStyle(label, rect.label.style);
|
|
542
|
+
applyLabelHalo(label, rect.label);
|
|
532
543
|
label.textContent = rect.label.text;
|
|
533
544
|
if (currentAnimation?.enter) {
|
|
534
545
|
const idx = rect.animationIndex ?? i;
|
|
@@ -574,6 +585,61 @@ import {
|
|
|
574
585
|
DEFAULT_LINE_HEIGHT,
|
|
575
586
|
parseAnnotationSpans
|
|
576
587
|
} from "@opendata-ai/openchart-engine";
|
|
588
|
+
|
|
589
|
+
// src/theme-tokens.ts
|
|
590
|
+
import { adaptForLightLineStroke, cssTokenDefault } from "@opendata-ai/openchart-core";
|
|
591
|
+
function resolvedSurface(theme) {
|
|
592
|
+
return theme.colors.neutral.surface;
|
|
593
|
+
}
|
|
594
|
+
function stampThemeProperties(el, theme) {
|
|
595
|
+
const accent = theme.colors.categorical[0] ?? cssTokenDefault("--oc-accent", "light");
|
|
596
|
+
const bg = resolvedSurface(theme);
|
|
597
|
+
const n = theme.colors.neutral;
|
|
598
|
+
const props = [
|
|
599
|
+
["--oc-font-family", theme.fonts.family],
|
|
600
|
+
["--oc-font-mono", theme.fonts.mono],
|
|
601
|
+
["--oc-title-size", `${theme.chrome.title.fontSize}px`],
|
|
602
|
+
["--oc-title-weight", `${theme.chrome.title.fontWeight}`],
|
|
603
|
+
["--oc-subtitle-size", `${theme.chrome.subtitle.fontSize}px`],
|
|
604
|
+
["--oc-subtitle-weight", `${theme.chrome.subtitle.fontWeight}`],
|
|
605
|
+
["--oc-source-size", `${theme.chrome.source.fontSize}px`],
|
|
606
|
+
["--oc-source-weight", `${theme.chrome.source.fontWeight}`],
|
|
607
|
+
["--oc-body-size", `${theme.fonts.sizes.body}px`],
|
|
608
|
+
["--oc-eyebrow-size", `${theme.chrome.eyebrow.fontSize}px`],
|
|
609
|
+
["--oc-eyebrow-weight", `${theme.chrome.eyebrow.fontWeight}`],
|
|
610
|
+
["--oc-bg", bg],
|
|
611
|
+
["--oc-text", theme.colors.text],
|
|
612
|
+
["--oc-text-muted", theme.colors.axis],
|
|
613
|
+
// Secondary grays are derived from the theme's own text/background pair
|
|
614
|
+
// (ResolvedTheme.colors.neutral), so a warm or cool theme gets warm or
|
|
615
|
+
// cool grays instead of zinc. One direction only: theme -> CSS.
|
|
616
|
+
["--oc-text-secondary", n.secondary],
|
|
617
|
+
["--oc-text-faint", n.faint],
|
|
618
|
+
["--oc-border", n.border],
|
|
619
|
+
["--oc-gray-100", n[100]],
|
|
620
|
+
["--oc-gray-200", n[200]],
|
|
621
|
+
["--oc-gray-300", n[300]],
|
|
622
|
+
["--oc-gray-400", n[400]],
|
|
623
|
+
["--oc-gray-600", n[600]],
|
|
624
|
+
["--oc-gray-800", n[800]],
|
|
625
|
+
["--oc-gridline", theme.colors.gridline],
|
|
626
|
+
// --oc-axis is the hairline the axis line is drawn with, not the ink its
|
|
627
|
+
// tick labels take (that is --oc-text-muted, above).
|
|
628
|
+
["--oc-axis", theme.colors.hairline],
|
|
629
|
+
["--oc-border-radius", `${theme.borderRadius}px`],
|
|
630
|
+
["--oc-accent", accent],
|
|
631
|
+
["--oc-accent-strong", adaptForLightLineStroke(accent)],
|
|
632
|
+
["--oc-positive", theme.colors.positive],
|
|
633
|
+
["--oc-negative", theme.colors.negative],
|
|
634
|
+
["--oc-space-2", `${theme.spacing.chromeGap * 2}px`],
|
|
635
|
+
["--oc-space-4", `${theme.spacing.padding}px`]
|
|
636
|
+
];
|
|
637
|
+
for (const [name, value] of props) {
|
|
638
|
+
el.style.setProperty(name, value);
|
|
639
|
+
}
|
|
640
|
+
}
|
|
641
|
+
|
|
642
|
+
// src/renderers/annotations.ts
|
|
577
643
|
var CONNECTOR_STROKE_WIDTH = 1.25;
|
|
578
644
|
function renderArrowhead(parent, tipX, tipY, tangentX, tangentY, stroke) {
|
|
579
645
|
const head = computeArrowheadPoints(tipX, tipY, tangentX, tangentY);
|
|
@@ -801,10 +867,7 @@ function renderAnnotation(parent, annotation, index, bgColor) {
|
|
|
801
867
|
});
|
|
802
868
|
g.appendChild(bgRect);
|
|
803
869
|
} else if (bgColor && annotation.label.halo !== false) {
|
|
804
|
-
text
|
|
805
|
-
text.style.stroke = bgColor;
|
|
806
|
-
text.style.strokeWidth = `${Math.round(fontSize * 0.3)}px`;
|
|
807
|
-
text.style.strokeLinejoin = "round";
|
|
870
|
+
applyKnockoutHalo(text, bgColor, fontSize);
|
|
808
871
|
}
|
|
809
872
|
g.appendChild(text);
|
|
810
873
|
if (annotation.subtitle) {
|
|
@@ -824,7 +887,7 @@ function renderAnnotations(parent, layout) {
|
|
|
824
887
|
if (layout.annotations.length === 0) return;
|
|
825
888
|
const g = createSVGElement("g");
|
|
826
889
|
g.setAttribute("class", "oc-annotations");
|
|
827
|
-
const bgColor = layout.theme
|
|
890
|
+
const bgColor = resolvedSurface(layout.theme);
|
|
828
891
|
for (let i = 0; i < layout.annotations.length; i++) {
|
|
829
892
|
renderAnnotation(g, layout.annotations[i], i, bgColor);
|
|
830
893
|
}
|
|
@@ -1433,6 +1496,29 @@ var LEGEND_HIT_PAD = 4;
|
|
|
1433
1496
|
function isCategorical(legend) {
|
|
1434
1497
|
return !legend.type || legend.type === "categorical";
|
|
1435
1498
|
}
|
|
1499
|
+
function renderNoDataSwatch(parent, noData, labelStyle, chip) {
|
|
1500
|
+
const swatch = createSVGElement("rect");
|
|
1501
|
+
swatch.setAttribute("class", "oc-legend-nodata");
|
|
1502
|
+
setAttrs(swatch, {
|
|
1503
|
+
x: noData.x,
|
|
1504
|
+
y: noData.y,
|
|
1505
|
+
width: noData.size,
|
|
1506
|
+
height: noData.size,
|
|
1507
|
+
fill: noData.fill,
|
|
1508
|
+
...chip ? { rx: chip.rx, ry: chip.rx } : { "shape-rendering": "crispEdges" }
|
|
1509
|
+
});
|
|
1510
|
+
parent.appendChild(swatch);
|
|
1511
|
+
const label = createSVGElement("text");
|
|
1512
|
+
setAttrs(label, {
|
|
1513
|
+
x: noData.labelX,
|
|
1514
|
+
y: noData.labelY,
|
|
1515
|
+
"text-anchor": "start",
|
|
1516
|
+
...chip ? { "dominant-baseline": chip.baseline } : {}
|
|
1517
|
+
});
|
|
1518
|
+
applyTextStyle(label, labelStyle);
|
|
1519
|
+
label.textContent = noData.label;
|
|
1520
|
+
parent.appendChild(label);
|
|
1521
|
+
}
|
|
1436
1522
|
function renderContinuousLegend(parent, legend) {
|
|
1437
1523
|
if (legend.bounds.width <= 0 || legend.bounds.height <= 0) return;
|
|
1438
1524
|
const g = createSVGElement("g");
|
|
@@ -1511,26 +1597,7 @@ function renderContinuousLegend(parent, legend) {
|
|
|
1511
1597
|
g.appendChild(label);
|
|
1512
1598
|
}
|
|
1513
1599
|
if (legend.noData) {
|
|
1514
|
-
|
|
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);
|
|
1600
|
+
renderNoDataSwatch(g, legend.noData, legend.labelStyle);
|
|
1534
1601
|
}
|
|
1535
1602
|
parent.appendChild(g);
|
|
1536
1603
|
}
|
|
@@ -1720,6 +1787,9 @@ function renderLegend(parent, legend) {
|
|
|
1720
1787
|
}
|
|
1721
1788
|
}
|
|
1722
1789
|
}
|
|
1790
|
+
if (legend.noData) {
|
|
1791
|
+
renderNoDataSwatch(g, legend.noData, legend.labelStyle, { rx: 2, baseline: "central" });
|
|
1792
|
+
}
|
|
1723
1793
|
parent.appendChild(g);
|
|
1724
1794
|
}
|
|
1725
1795
|
|
|
@@ -1854,7 +1924,12 @@ function renderChartSVG(layout, container, opts) {
|
|
|
1854
1924
|
defs
|
|
1855
1925
|
);
|
|
1856
1926
|
svg.appendChild(defs);
|
|
1857
|
-
setMarkRenderState({
|
|
1927
|
+
setMarkRenderState({
|
|
1928
|
+
animation,
|
|
1929
|
+
gradientMap,
|
|
1930
|
+
patternMap,
|
|
1931
|
+
surface: resolvedSurface(layout.theme)
|
|
1932
|
+
});
|
|
1858
1933
|
try {
|
|
1859
1934
|
if (layout.facet) {
|
|
1860
1935
|
renderFacetedPanels(svg, layout, layout.facet.panels, defs);
|
|
@@ -1928,10 +2003,10 @@ function renderChartSVG(layout, container, opts) {
|
|
|
1928
2003
|
clippedGroup.appendChild(dotsGroup);
|
|
1929
2004
|
}
|
|
1930
2005
|
svg.appendChild(clipGroup);
|
|
2006
|
+
renderAnnotations(svg, layout);
|
|
1931
2007
|
if (markLabelsOverlay) {
|
|
1932
2008
|
svg.appendChild(markLabelsOverlay);
|
|
1933
2009
|
}
|
|
1934
|
-
renderAnnotations(svg, layout);
|
|
1935
2010
|
renderEndpointLabels(svg, layout);
|
|
1936
2011
|
const epEntries = layout.endpointLabels?.entries ?? [];
|
|
1937
2012
|
if (epEntries.length > 0) {
|
|
@@ -2022,10 +2097,10 @@ function renderFacetedPanels(svg, layout, panels, defs) {
|
|
|
2022
2097
|
clippedGroup.setAttribute("clip-path", `url(#${panelClipId})`);
|
|
2023
2098
|
const panelLabelsOverlay = renderMarks(clippedGroup, panelLayout);
|
|
2024
2099
|
g.appendChild(clippedGroup);
|
|
2100
|
+
renderAnnotations(g, panelLayout);
|
|
2025
2101
|
if (panelLabelsOverlay) {
|
|
2026
2102
|
g.appendChild(panelLabelsOverlay);
|
|
2027
2103
|
}
|
|
2028
|
-
renderAnnotations(g, panelLayout);
|
|
2029
2104
|
svg.appendChild(g);
|
|
2030
2105
|
}
|
|
2031
2106
|
}
|
|
@@ -2036,6 +2111,10 @@ export {
|
|
|
2036
2111
|
resetSvgIdCounter,
|
|
2037
2112
|
buildGradientDefs,
|
|
2038
2113
|
resolveMarkFill,
|
|
2114
|
+
buildPatternDefs,
|
|
2115
|
+
resolvePatternFill,
|
|
2116
|
+
resolvedSurface,
|
|
2117
|
+
stampThemeProperties,
|
|
2039
2118
|
renderChromeElement,
|
|
2040
2119
|
renderLegend,
|
|
2041
2120
|
registerMarkRenderer,
|
|
@@ -2043,4 +2122,4 @@ export {
|
|
|
2043
2122
|
renderSingleMark,
|
|
2044
2123
|
renderChartSVG
|
|
2045
2124
|
};
|
|
2046
|
-
//# sourceMappingURL=chunk-
|
|
2125
|
+
//# sourceMappingURL=chunk-KANNWAEA.js.map
|