@gooddata/sdk-ui-theme-provider 11.42.0-alpha.3 → 11.42.0-alpha.4

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.
@@ -1 +1 @@
1
- {"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../../src/ThemeProvider/ThemeProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAEpE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAWlD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAE7B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IAEzB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC1B,QAAQ,EACR,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,cAAc,EACzB,QAAmB,EACnB,0BAAiC,EACjC,0BAAiC,EACpC,EAAE,mBAAmB,2CA2DrB"}
1
+ {"version":3,"file":"ThemeProvider.d.ts","sourceRoot":"","sources":["../../src/ThemeProvider/ThemeProvider.tsx"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,SAAS,EAA+B,MAAM,OAAO,CAAC;AAEpE,OAAO,EAAE,KAAK,kBAAkB,EAAE,MAAM,2BAA2B,CAAC;AACpE,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAWlD;;GAEG;AACH,MAAM,MAAM,aAAa,GAAG,CAAC,KAAK,EAAE,MAAM,KAAK,MAAM,CAAC;AAEtD;;GAEG;AACH,MAAM,WAAW,mBAAmB;IAChC;;;;;;;OAOG;IACH,KAAK,CAAC,EAAE,MAAM,CAAC;IAEf;;;;;;OAMG;IACH,OAAO,CAAC,EAAE,kBAAkB,CAAC;IAE7B;;;;;;OAMG;IACH,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB;;OAEG;IACH,QAAQ,CAAC,EAAE,aAAa,CAAC;IAEzB;;;;;;OAMG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;;;OAIG;IACH,0BAA0B,CAAC,EAAE,OAAO,CAAC;IAErC;;OAEG;IACH,QAAQ,CAAC,EAAE,SAAS,CAAC;CACxB;AAED;;;;;;;;;;GAUG;AACH,wBAAgB,aAAa,CAAC,EAC1B,QAAQ,EACR,KAAK,EAAE,UAAU,EACjB,OAAO,EAAE,YAAY,EACrB,SAAS,EAAE,cAAc,EACzB,QAAmB,EACnB,0BAAiC,EACjC,0BAAiC,EACpC,EAAE,mBAAmB,2CAoFrB"}
@@ -27,13 +27,31 @@ export function ThemeProvider({ children, theme: themeParam, backend: backendPar
27
27
  const lastWorkspace = useRef(undefined);
28
28
  lastWorkspace.current = workspace;
29
29
  useEffect(() => {
30
+ // A malformed theme (e.g. an unparseable color) must never block rendering. Preparing and
31
+ // applying the theme is wrapped so any failure falls back to the default theme and always
32
+ // resolves the loading state, instead of leaving the app stuck on the loading screen.
33
+ const applyTheme = (themeToApply) => {
34
+ try {
35
+ const preparedTheme = prepareTheme(themeToApply, enableComplementaryPalette);
36
+ setTheme(preparedTheme);
37
+ clearCssProperties();
38
+ setCssProperties(preparedTheme, isDarkTheme(preparedTheme));
39
+ }
40
+ catch (error) {
41
+ console.error("Failed to apply the theme, falling back to the default theme.", error);
42
+ // reset both channels (context theme and global CSS) to the default theme so
43
+ // context consumers stay consistent with the cleared CSS variables
44
+ setTheme({});
45
+ clearCssProperties();
46
+ }
47
+ finally {
48
+ setIsLoading(false);
49
+ setStatus("success");
50
+ }
51
+ };
30
52
  // no need to load anything if the themeParam is present
31
53
  if (themeParam) {
32
- const preparedTheme = prepareTheme(themeParam, enableComplementaryPalette);
33
- setTheme(preparedTheme);
34
- setStatus("success");
35
- clearCssProperties();
36
- setCssProperties(preparedTheme, isDarkTheme(preparedTheme));
54
+ applyTheme(themeParam);
37
55
  return;
38
56
  }
39
57
  const fetchData = async () => {
@@ -43,15 +61,23 @@ export function ThemeProvider({ children, theme: themeParam, backend: backendPar
43
61
  }
44
62
  setIsLoading(true);
45
63
  setStatus("loading");
46
- const selectedTheme = await backend.workspace(workspace).styling().getTheme();
47
- if (lastWorkspace.current === workspace) {
48
- const modifiedTheme = modifier(selectedTheme);
49
- const preparedTheme = prepareTheme(modifiedTheme, enableComplementaryPalette);
50
- setTheme(preparedTheme);
51
- clearCssProperties();
52
- setCssProperties(preparedTheme, isDarkTheme(preparedTheme));
53
- setIsLoading(false);
54
- setStatus("success");
64
+ try {
65
+ const selectedTheme = await backend.workspace(workspace).styling().getTheme();
66
+ if (lastWorkspace.current === workspace) {
67
+ applyTheme(modifier(selectedTheme));
68
+ }
69
+ }
70
+ catch (error) {
71
+ if (lastWorkspace.current === workspace) {
72
+ // covers both the backend fetch and the modifier transformation of the theme
73
+ console.error("Failed to load or process the theme from the backend.", error);
74
+ // reset both channels (context theme and global CSS) to the default theme so
75
+ // context consumers stay consistent with the cleared CSS variables
76
+ setTheme({});
77
+ clearCssProperties();
78
+ setIsLoading(false);
79
+ setStatus("success");
80
+ }
55
81
  }
56
82
  };
57
83
  void fetchData();
@@ -1,4 +1,16 @@
1
1
  import { type ITheme } from "@gooddata/sdk-model";
2
+ /**
3
+ * Drops palette colors that the `polished` color functions cannot parse.
4
+ *
5
+ * @remarks
6
+ * A single invalid color (e.g. a typo like "#1616D") would otherwise make theme application throw.
7
+ * An invalid color is treated exactly like an omitted one: it is removed so the standard default
8
+ * applies (missing shades are interpolated, missing family/complementary colors fall back to the
9
+ * default theme), while every other valid color is preserved. A color family whose base is invalid
10
+ * is dropped as a whole, since the family is meaningless without a base. The try/catch in
11
+ * ThemeProvider remains the hard backstop for any color path not covered here.
12
+ */
13
+ export declare const sanitizePalette: (theme: ITheme) => ITheme;
2
14
  export declare const prepareComplementaryPalette: (theme: ITheme) => ITheme;
3
15
  export declare const prepareBaseColors: (theme: ITheme) => ITheme;
4
16
  export declare const stripComplementaryPalette: (theme: ITheme) => ITheme;
@@ -1 +1 @@
1
- {"version":3,"file":"prepareTheme.d.ts","sourceRoot":"","sources":["../../src/ThemeProvider/prepareTheme.ts"],"names":[],"mappings":"AAGA,OAAO,EAAE,KAAK,MAAM,EAAE,MAAM,qBAAqB,CAAC;AAYlD,eAAO,MAAM,2BAA2B,2BAYvC,CAAC;AAEF,eAAO,MAAM,iBAAiB,2BAoB7B,CAAC;AAEF,eAAO,MAAM,yBAAyB,2BAgBrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,2BAsB/B,CAAC;AAEF,eAAO,MAAM,YAAY,iEAQxB,CAAC"}
1
+ {"version":3,"file":"prepareTheme.d.ts","sourceRoot":"","sources":["../../src/ThemeProvider/prepareTheme.ts"],"names":[],"mappings":"AAIA,OAAO,EAAE,KAAK,MAAM,EAA2D,MAAM,qBAAqB,CAAC;AAmD3G;;;;;;;;;;GAUG;AACH,eAAO,MAAM,eAAe,2BA0B3B,CAAC;AAEF,eAAO,MAAM,2BAA2B,2BAYvC,CAAC;AAEF,eAAO,MAAM,iBAAiB,2BAe7B,CAAC;AAEF,eAAO,MAAM,yBAAyB,2BAgBrC,CAAC;AAEF,eAAO,MAAM,mBAAmB,2BAsB/B,CAAC;AAEF,eAAO,MAAM,YAAY,iEAUxB,CAAC"}
@@ -1,5 +1,6 @@
1
- // (C) 2021-2025 GoodData Corporation
1
+ // (C) 2021-2026 GoodData Corporation
2
2
  import { getContrast, shade } from "polished";
3
+ import { isValidThemeColor } from "../colorValidation.js";
3
4
  import { getComplementaryPalette } from "../complementaryPalette.js";
4
5
  /**
5
6
  * Minimum contrast ratio n:1 recommended by W3C
@@ -7,6 +8,69 @@ import { getComplementaryPalette } from "../complementaryPalette.js";
7
8
  */
8
9
  const MIN_CONTRAST_RATIO = 3;
9
10
  const DEFAULT_BACKGROUND_COLOR = "#fff";
11
+ const DEFAULT_PRIMARY = "#14b2e2";
12
+ const DEFAULT_SUCCESS = "#00c18d";
13
+ const DEFAULT_ERROR = "#e54d42";
14
+ const DEFAULT_WARNING = "#f18600";
15
+ const sanitizeColorFamily = (family) => {
16
+ // without a usable base the whole family is dropped, so a default base is supplied downstream
17
+ if (!isValidThemeColor(family.base)) {
18
+ return undefined;
19
+ }
20
+ const sanitized = { base: family.base };
21
+ ["light", "dark", "contrast"].forEach((key) => {
22
+ const value = family[key];
23
+ if (value !== undefined && isValidThemeColor(value)) {
24
+ sanitized[key] = value;
25
+ }
26
+ });
27
+ return sanitized;
28
+ };
29
+ const sanitizeComplementaryPalette = (complementary) => {
30
+ const sanitized = {};
31
+ Object.keys(complementary).forEach((key) => {
32
+ const value = complementary[key];
33
+ if (value !== undefined && isValidThemeColor(value)) {
34
+ sanitized[key] = value;
35
+ }
36
+ });
37
+ return sanitized;
38
+ };
39
+ /**
40
+ * Drops palette colors that the `polished` color functions cannot parse.
41
+ *
42
+ * @remarks
43
+ * A single invalid color (e.g. a typo like "#1616D") would otherwise make theme application throw.
44
+ * An invalid color is treated exactly like an omitted one: it is removed so the standard default
45
+ * applies (missing shades are interpolated, missing family/complementary colors fall back to the
46
+ * default theme), while every other valid color is preserved. A color family whose base is invalid
47
+ * is dropped as a whole, since the family is meaningless without a base. The try/catch in
48
+ * ThemeProvider remains the hard backstop for any color path not covered here.
49
+ */
50
+ export const sanitizePalette = (theme) => {
51
+ if (!theme?.palette) {
52
+ return theme;
53
+ }
54
+ const palette = { ...theme.palette };
55
+ const families = ["primary", "error", "warning", "success", "info"];
56
+ families.forEach((key) => {
57
+ const family = palette[key];
58
+ if (family === undefined) {
59
+ return;
60
+ }
61
+ const sanitized = sanitizeColorFamily(family);
62
+ if (sanitized) {
63
+ palette[key] = sanitized;
64
+ }
65
+ else {
66
+ delete palette[key];
67
+ }
68
+ });
69
+ if (palette.complementary) {
70
+ palette.complementary = sanitizeComplementaryPalette(palette.complementary);
71
+ }
72
+ return { ...theme, palette };
73
+ };
10
74
  export const prepareComplementaryPalette = (theme) => {
11
75
  if (theme?.palette?.complementary) {
12
76
  return {
@@ -20,19 +84,15 @@ export const prepareComplementaryPalette = (theme) => {
20
84
  return theme;
21
85
  };
22
86
  export const prepareBaseColors = (theme) => {
23
- const defaultPrimary = "#14b2e2";
24
- const defaultSuccess = "#00c18d";
25
- const defaultError = "#e54d42";
26
- const defaultWarning = "#f18600";
27
87
  if (theme?.palette?.complementary) {
28
88
  return {
29
89
  ...theme,
30
90
  palette: {
31
91
  ...theme.palette,
32
- ...(theme.palette.primary ? {} : { primary: { base: defaultPrimary } }),
33
- ...(theme.palette.success ? {} : { success: { base: defaultSuccess } }),
34
- ...(theme.palette.error ? {} : { error: { base: defaultError } }),
35
- ...(theme.palette.warning ? {} : { warning: { base: defaultWarning } }),
92
+ ...(theme.palette.primary ? {} : { primary: { base: DEFAULT_PRIMARY } }),
93
+ ...(theme.palette.success ? {} : { success: { base: DEFAULT_SUCCESS } }),
94
+ ...(theme.palette.error ? {} : { error: { base: DEFAULT_ERROR } }),
95
+ ...(theme.palette.warning ? {} : { warning: { base: DEFAULT_WARNING } }),
36
96
  },
37
97
  };
38
98
  }
@@ -74,10 +134,11 @@ export const preparePrimaryColor = (theme) => {
74
134
  };
75
135
  };
76
136
  export const prepareTheme = (theme, enableComplementaryPalette = true) => {
137
+ const sanitizedTheme = sanitizePalette(theme);
77
138
  if (!enableComplementaryPalette) {
78
- const themeWithContrastPrimaryColor = preparePrimaryColor(theme);
139
+ const themeWithContrastPrimaryColor = preparePrimaryColor(sanitizedTheme);
79
140
  return stripComplementaryPalette(themeWithContrastPrimaryColor);
80
141
  }
81
- const themeWithComplementaryPalette = prepareComplementaryPalette(theme);
142
+ const themeWithComplementaryPalette = prepareComplementaryPalette(sanitizedTheme);
82
143
  return prepareBaseColors(themeWithComplementaryPalette);
83
144
  };
@@ -0,0 +1,35 @@
1
+ import { type ITheme } from "@gooddata/sdk-model";
2
+ /**
3
+ * Returns true if the provided value is a color string that the `polished` library can parse
4
+ * (hex, rgb(a), hsl(a) or a named color).
5
+ *
6
+ * @remarks
7
+ * `polished` is the single source of truth for what the theme application code can consume:
8
+ * the same parser (`parseToRgb`) backs `mix`, `getContrast`, `shade`, `transparentize` and
9
+ * `getLuminance`. Validating with it guarantees this check agrees exactly with the functions
10
+ * that would otherwise throw on an unparseable color.
11
+ *
12
+ * @internal
13
+ */
14
+ export declare const isValidThemeColor: (color: unknown) => boolean;
15
+ /**
16
+ * A palette color that cannot be parsed, together with its dot-separated location in the palette
17
+ * (e.g. `complementary.c9` or `primary.base`).
18
+ *
19
+ * @internal
20
+ */
21
+ export interface IInvalidThemeColor {
22
+ path: string;
23
+ value: string;
24
+ }
25
+ /**
26
+ * Walks the theme palette and returns the colors that cannot be parsed, each with its location.
27
+ *
28
+ * @remarks
29
+ * Only palette colors are inspected, as those are the values fed into the `polished` color
30
+ * functions during theme application. An empty array means every palette color is valid.
31
+ *
32
+ * @internal
33
+ */
34
+ export declare const findInvalidThemeColors: (theme: ITheme | undefined) => IInvalidThemeColor[];
35
+ //# sourceMappingURL=colorValidation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"colorValidation.d.ts","sourceRoot":"","sources":["../src/colorValidation.ts"],"names":[],"mappings":"AAIA,OAAO,EACH,KAAK,MAAM,EAId,MAAM,qBAAqB,CAAC;AAE7B;;;;;;;;;;;GAWG;AACH,eAAO,MAAM,iBAAiB,6BAW7B,CAAC;AAEF;;;;;GAKG;AACH,MAAM,WAAW,kBAAkB;IAC/B,IAAI,EAAE,MAAM,CAAC;IACb,KAAK,EAAE,MAAM,CAAC;CACjB;AA6BD;;;;;;;;GAQG;AACH,eAAO,MAAM,sBAAsB,qDAelC,CAAC"}
@@ -0,0 +1,66 @@
1
+ // (C) 2026 GoodData Corporation
2
+ import { parseToRgb } from "polished";
3
+ /**
4
+ * Returns true if the provided value is a color string that the `polished` library can parse
5
+ * (hex, rgb(a), hsl(a) or a named color).
6
+ *
7
+ * @remarks
8
+ * `polished` is the single source of truth for what the theme application code can consume:
9
+ * the same parser (`parseToRgb`) backs `mix`, `getContrast`, `shade`, `transparentize` and
10
+ * `getLuminance`. Validating with it guarantees this check agrees exactly with the functions
11
+ * that would otherwise throw on an unparseable color.
12
+ *
13
+ * @internal
14
+ */
15
+ export const isValidThemeColor = (color) => {
16
+ if (typeof color !== "string" || color.trim() === "") {
17
+ return false;
18
+ }
19
+ try {
20
+ parseToRgb(color);
21
+ return true;
22
+ }
23
+ catch {
24
+ return false;
25
+ }
26
+ };
27
+ const colorFamilyKeys = ["base", "light", "dark", "contrast"];
28
+ const collectInvalidFamilyColors = (family, familyKey) => {
29
+ if (!family) {
30
+ return [];
31
+ }
32
+ return colorFamilyKeys
33
+ .filter((key) => family[key] !== undefined && !isValidThemeColor(family[key]))
34
+ .map((key) => ({ path: `${familyKey}.${key}`, value: family[key] }));
35
+ };
36
+ const collectInvalidComplementaryColors = (complementary) => {
37
+ if (!complementary) {
38
+ return [];
39
+ }
40
+ return Object.keys(complementary)
41
+ .filter((key) => complementary[key] !== undefined && !isValidThemeColor(complementary[key]))
42
+ .map((key) => ({ path: `complementary.${key}`, value: complementary[key] }));
43
+ };
44
+ /**
45
+ * Walks the theme palette and returns the colors that cannot be parsed, each with its location.
46
+ *
47
+ * @remarks
48
+ * Only palette colors are inspected, as those are the values fed into the `polished` color
49
+ * functions during theme application. An empty array means every palette color is valid.
50
+ *
51
+ * @internal
52
+ */
53
+ export const findInvalidThemeColors = (theme) => {
54
+ const palette = theme?.palette;
55
+ if (!palette) {
56
+ return [];
57
+ }
58
+ return [
59
+ ...collectInvalidFamilyColors(palette.primary, "primary"),
60
+ ...collectInvalidFamilyColors(palette.error, "error"),
61
+ ...collectInvalidFamilyColors(palette.warning, "warning"),
62
+ ...collectInvalidFamilyColors(palette.success, "success"),
63
+ ...collectInvalidFamilyColors(palette.info, "info"),
64
+ ...collectInvalidComplementaryColors(palette.complementary),
65
+ ];
66
+ };
package/esm/index.d.ts CHANGED
@@ -14,4 +14,5 @@ export { ScopedThemeProvider, type IScopedThemeProviderProps } from "./ThemeProv
14
14
  export { ConditionalScopedThemeProvider } from "./ThemeProvider/ConditionalScopedThemeProvider.js";
15
15
  export { isDarkTheme } from "./ThemeProvider/isDarkTheme.js";
16
16
  export { withTheme, useTheme, useThemeIsLoading, useIsScopeThemed, useThemeStatus, useIsDarkTheme, ThemeContextProvider, type IThemeContextProviderProps, type ThemeStatus, } from "./ThemeProvider/Context.js";
17
+ export { isValidThemeColor, findInvalidThemeColors, type IInvalidThemeColor } from "./colorValidation.js";
17
18
  //# sourceMappingURL=index.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EACH,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AAC7G,OAAO,EAAE,8BAA8B,EAAE,MAAM,mDAAmD,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EACH,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,GACnB,MAAM,4BAA4B,CAAC"}
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAIA;;;;;;;;;GASG;AACH,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAC3E,OAAO,EACH,aAAa,EACb,KAAK,mBAAmB,EACxB,KAAK,aAAa,GACrB,MAAM,kCAAkC,CAAC;AAC1C,OAAO,EAAE,mBAAmB,EAAE,KAAK,yBAAyB,EAAE,MAAM,wCAAwC,CAAC;AAC7G,OAAO,EAAE,8BAA8B,EAAE,MAAM,mDAAmD,CAAC;AACnG,OAAO,EAAE,WAAW,EAAE,MAAM,gCAAgC,CAAC;AAC7D,OAAO,EACH,SAAS,EACT,QAAQ,EACR,iBAAiB,EACjB,gBAAgB,EAChB,cAAc,EACd,cAAc,EACd,oBAAoB,EACpB,KAAK,0BAA0B,EAC/B,KAAK,WAAW,GACnB,MAAM,4BAA4B,CAAC;AACpC,OAAO,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,KAAK,kBAAkB,EAAE,MAAM,sBAAsB,CAAC"}
package/esm/index.js CHANGED
@@ -16,3 +16,4 @@ export { ScopedThemeProvider } from "./ThemeProvider/ScopedThemeProvider.js";
16
16
  export { ConditionalScopedThemeProvider } from "./ThemeProvider/ConditionalScopedThemeProvider.js";
17
17
  export { isDarkTheme } from "./ThemeProvider/isDarkTheme.js";
18
18
  export { withTheme, useTheme, useThemeIsLoading, useIsScopeThemed, useThemeStatus, useIsDarkTheme, ThemeContextProvider, } from "./ThemeProvider/Context.js";
19
+ export { isValidThemeColor, findInvalidThemeColors } from "./colorValidation.js";
@@ -37,6 +37,28 @@ export declare function ConditionalScopedThemeProvider({ children }: {
37
37
  */
38
38
  export declare const defaultHeaderTheme: IThemeHeader;
39
39
 
40
+ /**
41
+ * Walks the theme palette and returns the colors that cannot be parsed, each with its location.
42
+ *
43
+ * @remarks
44
+ * Only palette colors are inspected, as those are the values fed into the `polished` color
45
+ * functions during theme application. An empty array means every palette color is valid.
46
+ *
47
+ * @internal
48
+ */
49
+ export declare const findInvalidThemeColors: (theme: ITheme | undefined) => IInvalidThemeColor[];
50
+
51
+ /**
52
+ * A palette color that cannot be parsed, together with its dot-separated location in the palette
53
+ * (e.g. `complementary.c9` or `primary.base`).
54
+ *
55
+ * @internal
56
+ */
57
+ export declare interface IInvalidThemeColor {
58
+ path: string;
59
+ value: string;
60
+ }
61
+
40
62
  /**
41
63
  * @internal
42
64
  */
@@ -95,6 +117,20 @@ export declare interface IScopedThemeProviderProps {
95
117
  */
96
118
  export declare const isDarkTheme: (theme: ITheme | undefined) => boolean;
97
119
 
120
+ /**
121
+ * Returns true if the provided value is a color string that the `polished` library can parse
122
+ * (hex, rgb(a), hsl(a) or a named color).
123
+ *
124
+ * @remarks
125
+ * `polished` is the single source of truth for what the theme application code can consume:
126
+ * the same parser (`parseToRgb`) backs `mix`, `getContrast`, `shade`, `transparentize` and
127
+ * `getLuminance`. Validating with it guarantees this check agrees exactly with the functions
128
+ * that would otherwise throw on an unparseable color.
129
+ *
130
+ * @internal
131
+ */
132
+ export declare const isValidThemeColor: (color: unknown) => boolean;
133
+
98
134
  /**
99
135
  * @public
100
136
  */
@@ -1 +1 @@
1
- {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../../src/variablesSpec/internal/geo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,yBAAyB,EAAE,wBAAwB,EAW/D,CAAC"}
1
+ {"version":3,"file":"geo.d.ts","sourceRoot":"","sources":["../../../src/variablesSpec/internal/geo.ts"],"names":[],"mappings":"AAEA,OAAO,EAAE,KAAK,wBAAwB,EAAE,MAAM,aAAa,CAAC;AAE5D,eAAO,MAAM,yBAAyB,EAAE,wBAAwB,EAwB/D,CAAC"}
@@ -1,4 +1,4 @@
1
- // (C) 2025 GoodData Corporation
1
+ // (C) 2025-2026 GoodData Corporation
2
2
  export const internalGeoThemeVariables = [
3
3
  {
4
4
  type: "internal",
@@ -10,4 +10,17 @@ export const internalGeoThemeVariables = [
10
10
  variableName: "--gd-geo-multi-layer-legend__toggle-duration",
11
11
  defaultValue: "0.2s",
12
12
  },
13
+ // Tooltip background, shared between the card and its clipped-content fade; referenced without a
14
+ // fallback, so no default value to validate.
15
+ {
16
+ type: "internal",
17
+ variableName: "--gd-viz-tooltip-bg",
18
+ defaultValue: null,
19
+ },
20
+ // Dynamic height cap fed from JS; unset (none) means no clamp.
21
+ {
22
+ type: "internal",
23
+ variableName: "--gd-viz-tooltip-max-height",
24
+ defaultValue: "none",
25
+ },
13
26
  ];
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@gooddata/sdk-ui-theme-provider",
3
- "version": "11.42.0-alpha.3",
3
+ "version": "11.42.0-alpha.4",
4
4
  "description": "GoodData SDK - Theme provider",
5
5
  "license": "MIT",
6
6
  "author": "GoodData Corporation",
@@ -32,10 +32,10 @@
32
32
  "postcss-value-parser": "^4.2.0",
33
33
  "ts-invariant": "0.10.3",
34
34
  "tslib": "2.8.1",
35
- "@gooddata/sdk-model": "11.42.0-alpha.3",
36
- "@gooddata/util": "11.42.0-alpha.3",
37
- "@gooddata/sdk-backend-spi": "11.42.0-alpha.3",
38
- "@gooddata/sdk-ui": "11.42.0-alpha.3"
35
+ "@gooddata/sdk-backend-spi": "11.42.0-alpha.4",
36
+ "@gooddata/sdk-model": "11.42.0-alpha.4",
37
+ "@gooddata/sdk-ui": "11.42.0-alpha.4",
38
+ "@gooddata/util": "11.42.0-alpha.4"
39
39
  },
40
40
  "devDependencies": {
41
41
  "@microsoft/api-documenter": "^7.17.0",
@@ -73,10 +73,10 @@
73
73
  "react-dom": "19.1.1",
74
74
  "typescript": "5.9.3",
75
75
  "vitest": "4.1.8",
76
- "@gooddata/eslint-config": "11.42.0-alpha.3",
77
- "@gooddata/oxlint-config": "11.42.0-alpha.3",
78
- "@gooddata/reference-workspace": "11.42.0-alpha.3",
79
- "@gooddata/sdk-backend-mockingbird": "11.42.0-alpha.3"
76
+ "@gooddata/eslint-config": "11.42.0-alpha.4",
77
+ "@gooddata/oxlint-config": "11.42.0-alpha.4",
78
+ "@gooddata/reference-workspace": "11.42.0-alpha.4",
79
+ "@gooddata/sdk-backend-mockingbird": "11.42.0-alpha.4"
80
80
  },
81
81
  "peerDependencies": {
82
82
  "react": "^18.0.0 || ^19.0.0",