@opendata-ai/openchart-core 6.11.0 → 6.12.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/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opendata-ai/openchart-core",
3
- "version": "6.11.0",
3
+ "version": "6.12.0",
4
4
  "description": "Types, theme, colors, accessibility, and utilities for openchart",
5
5
  "license": "Apache-2.0",
6
6
  "author": "Riley Hilliard",
@@ -190,4 +190,19 @@ describe('computeChrome', () => {
190
190
  expect(result.title).toBeDefined();
191
191
  // Just verify it runs without error; exact values depend on measure fn
192
192
  });
193
+
194
+ it('returns zero bottom height when watermark is false and no bottom chrome', () => {
195
+ const chrome: Chrome = { title: 'Title' };
196
+ const result = computeChrome(chrome, theme, 600, undefined, 'full', undefined, false);
197
+ expect(result.bottomHeight).toBe(0);
198
+ });
199
+
200
+ it('does not reserve brand width for bottom text when watermark is false', () => {
201
+ const chrome: Chrome = { source: 'Source: World Bank' };
202
+ const withWatermark = computeChrome(chrome, theme, 600, undefined, 'full', undefined, true);
203
+ const withoutWatermark = computeChrome(chrome, theme, 600, undefined, 'full', undefined, false);
204
+
205
+ // Without watermark, source text gets full width (not reduced by BRAND_RESERVE_WIDTH)
206
+ expect(withoutWatermark.source!.maxWidth).toBeGreaterThan(withWatermark.source!.maxWidth);
207
+ });
193
208
  });
@@ -148,6 +148,7 @@ export function computeChrome(
148
148
  measureText?: MeasureTextFn,
149
149
  chromeMode: ChromeMode = 'full',
150
150
  padding?: number,
151
+ watermark: boolean = true,
151
152
  ): ResolvedChrome {
152
153
  if (!chrome || chromeMode === 'hidden') {
153
154
  // Brand watermark is also skipped at cramped sizes (height < 200px triggers
@@ -217,7 +218,7 @@ export function computeChrome(
217
218
  // renders for wide-enough charts. Reserve space so it doesn't overflow.
218
219
  if (chromeMode === 'compact') {
219
220
  let compactBottom = 0;
220
- if (width >= BRAND_MIN_WIDTH) {
221
+ if (watermark && width >= BRAND_MIN_WIDTH) {
221
222
  const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
222
223
  compactBottom = theme.spacing.chartToFooter + brandHeight + pad;
223
224
  }
@@ -230,7 +231,8 @@ export function computeChrome(
230
231
 
231
232
  // Bottom elements: source, byline, footer
232
233
  // Reserve space on the right for the brand watermark so text doesn't overlap it
233
- const bottomMaxWidth = maxWidth - BRAND_RESERVE_WIDTH;
234
+ const shouldReserveBrandWidth = watermark && width >= BRAND_MIN_WIDTH;
235
+ const bottomMaxWidth = maxWidth - (shouldReserveBrandWidth ? BRAND_RESERVE_WIDTH : 0);
234
236
  const bottomElements: Partial<Pick<ResolvedChrome, 'source' | 'byline' | 'footer'>> = {};
235
237
  let bottomHeight = 0;
236
238
 
@@ -300,7 +302,7 @@ export function computeChrome(
300
302
  // Ensure bottom height accommodates the brand watermark, which renders
301
303
  // at the same Y as the first bottom chrome item but is taller (20px font
302
304
  // vs 12px source). Without this, the brand overflows the SVG viewBox.
303
- if (width >= BRAND_MIN_WIDTH) {
305
+ if (watermark && width >= BRAND_MIN_WIDTH) {
304
306
  const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
305
307
  // firstItemY is chartToFooter (the Y offset of the first bottom item).
306
308
  // The brand needs brandHeight below that point; bottom chrome content
@@ -313,7 +315,7 @@ export function computeChrome(
313
315
 
314
316
  // Add bottom padding
315
317
  bottomHeight += pad;
316
- } else if (width >= BRAND_MIN_WIDTH) {
318
+ } else if (watermark && width >= BRAND_MIN_WIDTH) {
317
319
  // No bottom chrome items, but brand watermark still renders.
318
320
  // Reserve space: chartToFooter gap + brand text height + padding.
319
321
  const brandHeight = estimateTextHeight(BRAND_FONT_SIZE, 1);
@@ -667,6 +667,8 @@ export interface ChartLayout {
667
667
  dimensions: { width: number; height: number };
668
668
  /** Resolved animation config. Present only when animation is enabled. */
669
669
  animation?: ResolvedAnimation;
670
+ /** Whether the tryOpenData.ai watermark is enabled. */
671
+ watermark: boolean;
670
672
  }
671
673
 
672
674
  // ---------------------------------------------------------------------------
@@ -843,6 +845,8 @@ export interface TableLayout {
843
845
  theme: ResolvedTheme;
844
846
  /** Resolved animation config. Present only when animation is enabled. */
845
847
  animation?: ResolvedAnimation;
848
+ /** Whether the tryOpenData.ai watermark is enabled. */
849
+ watermark: boolean;
846
850
  }
847
851
 
848
852
  // ---------------------------------------------------------------------------
@@ -1020,6 +1024,8 @@ export interface SankeyLayout {
1020
1024
  dimensions: { width: number; height: number };
1021
1025
  /** Resolved animation config. Present only when animation is enabled. */
1022
1026
  animation?: ResolvedAnimation;
1027
+ /** Whether the tryOpenData.ai watermark is enabled. */
1028
+ watermark: boolean;
1023
1029
  }
1024
1030
 
1025
1031
  // ---------------------------------------------------------------------------
@@ -1054,6 +1060,8 @@ export interface CompileOptions {
1054
1060
  * before calling compile. The engine always receives a resolved boolean.
1055
1061
  */
1056
1062
  darkMode?: boolean;
1063
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
1064
+ watermark?: boolean;
1057
1065
  /**
1058
1066
  * Real text measurement function provided by the adapter.
1059
1067
  * Uses a hidden canvas or DOM element for accurate text dimensions.
package/src/types/spec.ts CHANGED
@@ -804,6 +804,8 @@ export interface ChartSpec {
804
804
  theme?: ThemeConfig;
805
805
  /** Dark mode behavior. Defaults to "off". */
806
806
  darkMode?: DarkMode;
807
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
808
+ watermark?: boolean;
807
809
  /** Series names to hide from rendering. Hidden series remain in the legend but are visually dimmed. */
808
810
  hiddenSeries?: string[];
809
811
  /** Per-series visual overrides, keyed by series name (the color field value). */
@@ -844,6 +846,8 @@ export interface TableSpec {
844
846
  theme?: ThemeConfig;
845
847
  /** Dark mode behavior. */
846
848
  darkMode?: DarkMode;
849
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
850
+ watermark?: boolean;
847
851
  /** Enable client-side search/filter. */
848
852
  search?: boolean;
849
853
  /** Pagination configuration. True for defaults, or an object with pageSize. */
@@ -911,6 +915,8 @@ export interface GraphSpec {
911
915
  theme?: ThemeConfig;
912
916
  /** Dark mode behavior. */
913
917
  darkMode?: DarkMode;
918
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
919
+ watermark?: boolean;
914
920
  }
915
921
 
916
922
  // ---------------------------------------------------------------------------
@@ -964,6 +970,8 @@ export interface LayerSpec {
964
970
  theme?: ThemeConfig;
965
971
  /** Dark mode behavior. Defaults to "off". */
966
972
  darkMode?: DarkMode;
973
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
974
+ watermark?: boolean;
967
975
  /** Resolution strategy for shared scales/axes/legends. */
968
976
  resolve?: ResolveConfig;
969
977
  /** Hidden series names. */
@@ -1030,6 +1038,8 @@ export interface SankeySpec {
1030
1038
  theme?: ThemeConfig;
1031
1039
  /** Dark mode behavior. Defaults to "off". */
1032
1040
  darkMode?: DarkMode;
1041
+ /** Whether to show the tryOpenData.ai watermark. Defaults to true. */
1042
+ watermark?: boolean;
1033
1043
  /** Animation configuration for entrance animations. */
1034
1044
  animation?: AnimationSpec;
1035
1045
  /**