@keboola/design 2.1.0 → 3.1.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/README.md +10 -9
- package/dist/icons.cjs +13 -737
- package/dist/icons.cjs.map +1 -1
- package/dist/icons.d.cts +2 -1
- package/dist/icons.d.ts +2 -1
- package/dist/icons.js +8 -2
- package/dist/icons.js.map +1 -1
- package/dist/index.cjs +13517 -12393
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +4717 -4449
- package/dist/index.d.ts +4717 -4449
- package/dist/index.js +13252 -12160
- package/dist/index.js.map +1 -1
- package/dist/markdown-editor.cjs +294 -170
- package/dist/markdown-editor.cjs.map +1 -1
- package/dist/markdown-editor.js +292 -170
- package/dist/markdown-editor.js.map +1 -1
- package/dist/mermaid.cjs +142 -49
- package/dist/mermaid.cjs.map +1 -1
- package/dist/mermaid.js +124 -51
- package/dist/mermaid.js.map +1 -1
- package/dist/styles.css +1 -1
- package/dist/testing.cjs +89 -0
- package/dist/testing.cjs.map +1 -0
- package/dist/testing.d.cts +19 -0
- package/dist/testing.d.ts +19 -0
- package/dist/testing.js +87 -0
- package/dist/testing.js.map +1 -0
- package/package.json +35 -16
- package/src/styles.css +19 -18
- package/dist/icon-names.cjs +0 -18
- package/dist/icon-names.cjs.map +0 -1
- package/dist/icon-names.d.cts +0 -7
- package/dist/icon-names.d.ts +0 -7
- package/dist/icon-names.js +0 -16
- package/dist/icon-names.js.map +0 -1
package/dist/testing.cjs
ADDED
|
@@ -0,0 +1,89 @@
|
|
|
1
|
+
'use strict';
|
|
2
|
+
|
|
3
|
+
var react$1 = require('@testing-library/react');
|
|
4
|
+
var brandRegistry = require('@keboola/brand-registry');
|
|
5
|
+
var react = require('react');
|
|
6
|
+
var jsxRuntime = require('react/jsx-runtime');
|
|
7
|
+
|
|
8
|
+
// src/testing/renderWithTheme.tsx
|
|
9
|
+
var AIGradientsSvg = () => /* @__PURE__ */ jsxRuntime.jsx("svg", { width: "0", height: "0", style: { position: "absolute", visibility: "hidden" }, children: /* @__PURE__ */ jsxRuntime.jsxs("defs", { children: [
|
|
10
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "aigradient-600", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [
|
|
11
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#61E4FF" }),
|
|
12
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "50%", stopColor: "#1F8FFF" }),
|
|
13
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#7C5CFF" })
|
|
14
|
+
] }),
|
|
15
|
+
/* @__PURE__ */ jsxRuntime.jsxs("linearGradient", { id: "aigradient-700", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [
|
|
16
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "0%", stopColor: "#075fb8" }),
|
|
17
|
+
/* @__PURE__ */ jsxRuntime.jsx("stop", { offset: "100%", stopColor: "#5b2cb8" })
|
|
18
|
+
] })
|
|
19
|
+
] }) });
|
|
20
|
+
|
|
21
|
+
// src/components/ThemeProvider/brandToCssVars.ts
|
|
22
|
+
var brandToCssVars = (brand) => {
|
|
23
|
+
const vars = {
|
|
24
|
+
"--color-background": brand.colors.background,
|
|
25
|
+
"--color-foreground": brand.colors.foreground,
|
|
26
|
+
"--color-muted": brand.colors.muted,
|
|
27
|
+
"--color-muted-foreground": brand.colors.mutedForeground,
|
|
28
|
+
"--color-border": brand.colors.border,
|
|
29
|
+
"--color-primary": brand.colors.primary,
|
|
30
|
+
"--color-primary-foreground": brand.colors.primaryForeground,
|
|
31
|
+
"--color-destructive": brand.colors.destructive,
|
|
32
|
+
"--color-destructive-foreground": brand.colors.destructiveForeground
|
|
33
|
+
};
|
|
34
|
+
if (brand.radii) {
|
|
35
|
+
vars["--radius-sm"] = brand.radii.sm;
|
|
36
|
+
vars["--radius-md"] = brand.radii.md;
|
|
37
|
+
vars["--radius-lg"] = brand.radii.lg;
|
|
38
|
+
}
|
|
39
|
+
return vars;
|
|
40
|
+
};
|
|
41
|
+
var ThemeContext = react.createContext(null);
|
|
42
|
+
var ThemeProvider = ({ brand = brandRegistry.keboola, setBrand, children }) => {
|
|
43
|
+
react.useEffect(() => {
|
|
44
|
+
if (typeof document === "undefined") return;
|
|
45
|
+
const root = document.documentElement;
|
|
46
|
+
const previousBrand = root.getAttribute("data-brand");
|
|
47
|
+
const vars = brandToCssVars(brand);
|
|
48
|
+
const previousValues = {};
|
|
49
|
+
for (const name of Object.keys(vars)) {
|
|
50
|
+
previousValues[name] = root.style.getPropertyValue(name);
|
|
51
|
+
}
|
|
52
|
+
root.setAttribute("data-brand", brand.id);
|
|
53
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
54
|
+
root.style.setProperty(name, value);
|
|
55
|
+
}
|
|
56
|
+
return () => {
|
|
57
|
+
for (const [name, previousValue] of Object.entries(previousValues)) {
|
|
58
|
+
if (root.style.getPropertyValue(name) !== vars[name]) continue;
|
|
59
|
+
if (previousValue === "") {
|
|
60
|
+
root.style.removeProperty(name);
|
|
61
|
+
} else {
|
|
62
|
+
root.style.setProperty(name, previousValue);
|
|
63
|
+
}
|
|
64
|
+
}
|
|
65
|
+
if (root.getAttribute("data-brand") === brand.id) {
|
|
66
|
+
if (previousBrand === null) {
|
|
67
|
+
root.removeAttribute("data-brand");
|
|
68
|
+
} else {
|
|
69
|
+
root.setAttribute("data-brand", previousBrand);
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
};
|
|
73
|
+
}, [brand]);
|
|
74
|
+
return /* @__PURE__ */ jsxRuntime.jsxs(ThemeContext, { value: { brand, setBrand }, children: [
|
|
75
|
+
/* @__PURE__ */ jsxRuntime.jsx(AIGradientsSvg, {}),
|
|
76
|
+
children
|
|
77
|
+
] });
|
|
78
|
+
};
|
|
79
|
+
var renderWithTheme = (ui, options = {}) => {
|
|
80
|
+
const { brand = brandRegistry.keboola, ...rest } = options;
|
|
81
|
+
return react$1.render(ui, {
|
|
82
|
+
...rest,
|
|
83
|
+
wrapper: ({ children }) => /* @__PURE__ */ jsxRuntime.jsx(ThemeProvider, { brand, children })
|
|
84
|
+
});
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
exports.renderWithTheme = renderWithTheme;
|
|
88
|
+
//# sourceMappingURL=testing.cjs.map
|
|
89
|
+
//# sourceMappingURL=testing.cjs.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsx","jsxs","createContext","keboola","useEffect","render"],"mappings":"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","file":"testing.cjs","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Brands today only expose a subset of the semantic surface shipped by\n * `@keboola/design/styles` (`accent`, `popover`, `sidebar.*`, `success`,\n * `input` are not yet brand-overridable). Slots the brand doesn't define\n * are omitted — `ThemeProvider` only captures/restores the keys returned\n * here, so unset slots inherit whatever is already live on `<html>` (an\n * outer provider's value, or the `:root` defaults in `styles.css` if no\n * outer provider has touched them).\n */\nexport const brandToCssVars = (brand: Brand): Record<string, string> => {\n const vars: Record<string, string> = {\n '--color-background': brand.colors.background,\n '--color-foreground': brand.colors.foreground,\n '--color-muted': brand.colors.muted,\n '--color-muted-foreground': brand.colors.mutedForeground,\n '--color-border': brand.colors.border,\n '--color-primary': brand.colors.primary,\n '--color-primary-foreground': brand.colors.primaryForeground,\n '--color-destructive': brand.colors.destructive,\n '--color-destructive-foreground': brand.colors.destructiveForeground,\n };\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, useEffect } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { ThemeContext } from './themeContext';\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - Render hidden SVG gradient definitions (`aigradient-600`, `aigradient-700`).\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables so chrome re-skins for the supplied brand.\n * - Expose `{ brand, setBrand? }` via `useTheme()` (throws outside the provider).\n *\n * The slots a brand can override today are a subset of the canonical\n * `:root` block in `styles.css`. Slots the brand doesn't define fall back\n * to the `:root` defaults — we deliberately do not write empty strings.\n *\n * `data-brand` (brand id) is intentionally separate from `data-theme`\n * (light/dark mode, defined by the `[data-theme='dark']` block in\n * `styles.css`) so the two axes compose without clobbering each other.\n */\nexport const ThemeProvider = ({ brand = keboola, setBrand, children }: Props) => {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand]);\n\n return (\n <ThemeContext value={{ brand, setBrand }}>\n <AIGradientsSvg />\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { RenderOptions, RenderResult } from '@testing-library/react';
|
|
3
|
+
import { Brand } from '@keboola/brand-registry';
|
|
4
|
+
|
|
5
|
+
type Options = Omit<RenderOptions, 'wrapper'> & {
|
|
6
|
+
/** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */
|
|
7
|
+
brand?: Brand;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.
|
|
11
|
+
*
|
|
12
|
+
* Use in unit tests for any component that calls `useTheme()` or relies on
|
|
13
|
+
* the brand-driven CSS variables. Defaults to the canonical Keboola brand so
|
|
14
|
+
* callers can opt into a specific brand only when they need to assert
|
|
15
|
+
* brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).
|
|
16
|
+
*/
|
|
17
|
+
declare const renderWithTheme: (ui: ReactElement, options?: Options) => RenderResult;
|
|
18
|
+
|
|
19
|
+
export { renderWithTheme };
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ReactElement } from 'react';
|
|
2
|
+
import { RenderOptions, RenderResult } from '@testing-library/react';
|
|
3
|
+
import { Brand } from '@keboola/brand-registry';
|
|
4
|
+
|
|
5
|
+
type Options = Omit<RenderOptions, 'wrapper'> & {
|
|
6
|
+
/** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */
|
|
7
|
+
brand?: Brand;
|
|
8
|
+
};
|
|
9
|
+
/**
|
|
10
|
+
* `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.
|
|
11
|
+
*
|
|
12
|
+
* Use in unit tests for any component that calls `useTheme()` or relies on
|
|
13
|
+
* the brand-driven CSS variables. Defaults to the canonical Keboola brand so
|
|
14
|
+
* callers can opt into a specific brand only when they need to assert
|
|
15
|
+
* brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).
|
|
16
|
+
*/
|
|
17
|
+
declare const renderWithTheme: (ui: ReactElement, options?: Options) => RenderResult;
|
|
18
|
+
|
|
19
|
+
export { renderWithTheme };
|
package/dist/testing.js
ADDED
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import { render } from '@testing-library/react';
|
|
2
|
+
import { keboola } from '@keboola/brand-registry';
|
|
3
|
+
import { createContext, useEffect } from 'react';
|
|
4
|
+
import { jsx, jsxs } from 'react/jsx-runtime';
|
|
5
|
+
|
|
6
|
+
// src/testing/renderWithTheme.tsx
|
|
7
|
+
var AIGradientsSvg = () => /* @__PURE__ */ jsx("svg", { width: "0", height: "0", style: { position: "absolute", visibility: "hidden" }, children: /* @__PURE__ */ jsxs("defs", { children: [
|
|
8
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "aigradient-600", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [
|
|
9
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#61E4FF" }),
|
|
10
|
+
/* @__PURE__ */ jsx("stop", { offset: "50%", stopColor: "#1F8FFF" }),
|
|
11
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#7C5CFF" })
|
|
12
|
+
] }),
|
|
13
|
+
/* @__PURE__ */ jsxs("linearGradient", { id: "aigradient-700", x1: "0%", y1: "0%", x2: "100%", y2: "100%", children: [
|
|
14
|
+
/* @__PURE__ */ jsx("stop", { offset: "0%", stopColor: "#075fb8" }),
|
|
15
|
+
/* @__PURE__ */ jsx("stop", { offset: "100%", stopColor: "#5b2cb8" })
|
|
16
|
+
] })
|
|
17
|
+
] }) });
|
|
18
|
+
|
|
19
|
+
// src/components/ThemeProvider/brandToCssVars.ts
|
|
20
|
+
var brandToCssVars = (brand) => {
|
|
21
|
+
const vars = {
|
|
22
|
+
"--color-background": brand.colors.background,
|
|
23
|
+
"--color-foreground": brand.colors.foreground,
|
|
24
|
+
"--color-muted": brand.colors.muted,
|
|
25
|
+
"--color-muted-foreground": brand.colors.mutedForeground,
|
|
26
|
+
"--color-border": brand.colors.border,
|
|
27
|
+
"--color-primary": brand.colors.primary,
|
|
28
|
+
"--color-primary-foreground": brand.colors.primaryForeground,
|
|
29
|
+
"--color-destructive": brand.colors.destructive,
|
|
30
|
+
"--color-destructive-foreground": brand.colors.destructiveForeground
|
|
31
|
+
};
|
|
32
|
+
if (brand.radii) {
|
|
33
|
+
vars["--radius-sm"] = brand.radii.sm;
|
|
34
|
+
vars["--radius-md"] = brand.radii.md;
|
|
35
|
+
vars["--radius-lg"] = brand.radii.lg;
|
|
36
|
+
}
|
|
37
|
+
return vars;
|
|
38
|
+
};
|
|
39
|
+
var ThemeContext = createContext(null);
|
|
40
|
+
var ThemeProvider = ({ brand = keboola, setBrand, children }) => {
|
|
41
|
+
useEffect(() => {
|
|
42
|
+
if (typeof document === "undefined") return;
|
|
43
|
+
const root = document.documentElement;
|
|
44
|
+
const previousBrand = root.getAttribute("data-brand");
|
|
45
|
+
const vars = brandToCssVars(brand);
|
|
46
|
+
const previousValues = {};
|
|
47
|
+
for (const name of Object.keys(vars)) {
|
|
48
|
+
previousValues[name] = root.style.getPropertyValue(name);
|
|
49
|
+
}
|
|
50
|
+
root.setAttribute("data-brand", brand.id);
|
|
51
|
+
for (const [name, value] of Object.entries(vars)) {
|
|
52
|
+
root.style.setProperty(name, value);
|
|
53
|
+
}
|
|
54
|
+
return () => {
|
|
55
|
+
for (const [name, previousValue] of Object.entries(previousValues)) {
|
|
56
|
+
if (root.style.getPropertyValue(name) !== vars[name]) continue;
|
|
57
|
+
if (previousValue === "") {
|
|
58
|
+
root.style.removeProperty(name);
|
|
59
|
+
} else {
|
|
60
|
+
root.style.setProperty(name, previousValue);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
if (root.getAttribute("data-brand") === brand.id) {
|
|
64
|
+
if (previousBrand === null) {
|
|
65
|
+
root.removeAttribute("data-brand");
|
|
66
|
+
} else {
|
|
67
|
+
root.setAttribute("data-brand", previousBrand);
|
|
68
|
+
}
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
}, [brand]);
|
|
72
|
+
return /* @__PURE__ */ jsxs(ThemeContext, { value: { brand, setBrand }, children: [
|
|
73
|
+
/* @__PURE__ */ jsx(AIGradientsSvg, {}),
|
|
74
|
+
children
|
|
75
|
+
] });
|
|
76
|
+
};
|
|
77
|
+
var renderWithTheme = (ui, options = {}) => {
|
|
78
|
+
const { brand = keboola, ...rest } = options;
|
|
79
|
+
return render(ui, {
|
|
80
|
+
...rest,
|
|
81
|
+
wrapper: ({ children }) => /* @__PURE__ */ jsx(ThemeProvider, { brand, children })
|
|
82
|
+
});
|
|
83
|
+
};
|
|
84
|
+
|
|
85
|
+
export { renderWithTheme };
|
|
86
|
+
//# sourceMappingURL=testing.js.map
|
|
87
|
+
//# sourceMappingURL=testing.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"sources":["../src/components/ThemeProvider/AIGradientSvg.tsx","../src/components/ThemeProvider/brandToCssVars.ts","../src/components/ThemeProvider/themeContext.ts","../src/components/ThemeProvider/ThemeProvider.tsx","../src/testing/renderWithTheme.tsx"],"names":["jsxs","jsx","keboola"],"mappings":"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","file":"testing.js","sourcesContent":["export const AIGradientsSvg = () => (\n <svg width=\"0\" height=\"0\" style={{ position: 'absolute', visibility: 'hidden' }}>\n <defs>\n {/*aigradient-600*/}\n <linearGradient id=\"aigradient-600\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#61E4FF\" />\n <stop offset=\"50%\" stopColor=\"#1F8FFF\" />\n <stop offset=\"100%\" stopColor=\"#7C5CFF\" />\n </linearGradient>\n\n {/*aigradient-700*/}\n <linearGradient id=\"aigradient-700\" x1=\"0%\" y1=\"0%\" x2=\"100%\" y2=\"100%\">\n <stop offset=\"0%\" stopColor=\"#075fb8\" />\n <stop offset=\"100%\" stopColor=\"#5b2cb8\" />\n </linearGradient>\n </defs>\n </svg>\n);\n","import type { Brand } from '@keboola/brand-registry';\n\n/**\n * Map every brand-overridable slot to the matching CSS variable on `:root`.\n *\n * Brand colours are already stored as `R G B` channel strings (validated by\n * the brand schema), matching the shape Tailwind's\n * `rgb(var(--color-*) / <alpha-value>)` template consumes — they're written\n * straight into the CSS variable without conversion.\n *\n * Brands today only expose a subset of the semantic surface shipped by\n * `@keboola/design/styles` (`accent`, `popover`, `sidebar.*`, `success`,\n * `input` are not yet brand-overridable). Slots the brand doesn't define\n * are omitted — `ThemeProvider` only captures/restores the keys returned\n * here, so unset slots inherit whatever is already live on `<html>` (an\n * outer provider's value, or the `:root` defaults in `styles.css` if no\n * outer provider has touched them).\n */\nexport const brandToCssVars = (brand: Brand): Record<string, string> => {\n const vars: Record<string, string> = {\n '--color-background': brand.colors.background,\n '--color-foreground': brand.colors.foreground,\n '--color-muted': brand.colors.muted,\n '--color-muted-foreground': brand.colors.mutedForeground,\n '--color-border': brand.colors.border,\n '--color-primary': brand.colors.primary,\n '--color-primary-foreground': brand.colors.primaryForeground,\n '--color-destructive': brand.colors.destructive,\n '--color-destructive-foreground': brand.colors.destructiveForeground,\n };\n\n if (brand.radii) {\n vars['--radius-sm'] = brand.radii.sm;\n vars['--radius-md'] = brand.radii.md;\n vars['--radius-lg'] = brand.radii.lg;\n }\n\n return vars;\n};\n","import { createContext } from 'react';\n\nimport type { Brand } from '@keboola/brand-registry';\n\nexport type ThemeContextValue = {\n brand: Brand;\n setBrand?: (brand: Brand) => void;\n};\n\nexport const ThemeContext = createContext<ThemeContextValue | null>(null);\n","import { type ReactNode, useEffect } from 'react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { AIGradientsSvg } from './AIGradientSvg';\nimport { brandToCssVars } from './brandToCssVars';\nimport { ThemeContext } from './themeContext';\n\ntype Props = {\n /**\n * Brand whose tokens drive the chrome (`@keboola/design`'s semantic CSS\n * variables) inside this provider's subtree. Defaults to the canonical\n * `keboola` brand so existing apps that mount `<ThemeProvider>` without a\n * `brand` prop continue to render as today.\n */\n brand?: Brand;\n /**\n * Optional setter exposed through `useTheme()` so consumers can flip\n * brands at runtime (Storybook toolbar, a customer-impersonation switch).\n */\n setBrand?: (brand: Brand) => void;\n children: ReactNode;\n};\n\n/**\n * Responsibilities:\n * - Render hidden SVG gradient definitions (`aigradient-600`, `aigradient-700`).\n * - Set `data-brand=\"<brand.id>\"` on `<html>` and inject the matching `--color-*` /\n * `--radius-*` CSS variables so chrome re-skins for the supplied brand.\n * - Expose `{ brand, setBrand? }` via `useTheme()` (throws outside the provider).\n *\n * The slots a brand can override today are a subset of the canonical\n * `:root` block in `styles.css`. Slots the brand doesn't define fall back\n * to the `:root` defaults — we deliberately do not write empty strings.\n *\n * `data-brand` (brand id) is intentionally separate from `data-theme`\n * (light/dark mode, defined by the `[data-theme='dark']` block in\n * `styles.css`) so the two axes compose without clobbering each other.\n */\nexport const ThemeProvider = ({ brand = keboola, setBrand, children }: Props) => {\n useEffect(() => {\n if (typeof document === 'undefined') return;\n const root = document.documentElement;\n\n // Capture the previous `data-brand` + per-property inline-style values\n // so cleanup can restore them. Cleanup only restores values that\n // weren't overwritten by some other provider in between — without that\n // check, an inner provider unmounting after an outer one wrote new\n // values would clobber the outer's brand back to bare `:root` (React\n // runs child effects before parent effects on mount, so the inner's\n // captured \"previous\" is stale by the time it unmounts).\n const previousBrand = root.getAttribute('data-brand');\n const vars = brandToCssVars(brand);\n const previousValues: Record<string, string> = {};\n for (const name of Object.keys(vars)) {\n previousValues[name] = root.style.getPropertyValue(name);\n }\n\n root.setAttribute('data-brand', brand.id);\n for (const [name, value] of Object.entries(vars)) {\n root.style.setProperty(name, value);\n }\n\n return () => {\n for (const [name, previousValue] of Object.entries(previousValues)) {\n // Only roll back if our value is still live — if someone else\n // (an outer provider, a sibling) has since written a different\n // value, leave it alone.\n if (root.style.getPropertyValue(name) !== vars[name]) continue;\n if (previousValue === '') {\n root.style.removeProperty(name);\n } else {\n root.style.setProperty(name, previousValue);\n }\n }\n if (root.getAttribute('data-brand') === brand.id) {\n if (previousBrand === null) {\n root.removeAttribute('data-brand');\n } else {\n root.setAttribute('data-brand', previousBrand);\n }\n }\n };\n }, [brand]);\n\n return (\n <ThemeContext value={{ brand, setBrand }}>\n <AIGradientsSvg />\n {children}\n </ThemeContext>\n );\n};\n","import type { ReactElement } from 'react';\nimport { render, type RenderOptions, type RenderResult } from '@testing-library/react';\n\nimport { type Brand, keboola } from '@keboola/brand-registry';\n\nimport { ThemeProvider } from '../components/ThemeProvider';\n\ntype Options = Omit<RenderOptions, 'wrapper'> & {\n /** Brand to drive the wrapping `<ThemeProvider>`. Defaults to `keboola`. */\n brand?: Brand;\n};\n\n/**\n * `@testing-library/react` `render` wrapped in a `<ThemeProvider brand={...}>`.\n *\n * Use in unit tests for any component that calls `useTheme()` or relies on\n * the brand-driven CSS variables. Defaults to the canonical Keboola brand so\n * callers can opt into a specific brand only when they need to assert\n * brand-dependent behaviour (e.g. chrome re-skins under `keboola-legacy`).\n */\nexport const renderWithTheme = (ui: ReactElement, options: Options = {}): RenderResult => {\n const { brand = keboola, ...rest } = options;\n return render(ui, {\n ...rest,\n wrapper: ({ children }) => <ThemeProvider brand={brand}>{children}</ThemeProvider>,\n });\n};\n"]}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@keboola/design",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "3.1.0",
|
|
4
4
|
"description": "Keboola UI platform design system — shadcn-aligned React primitives, Keboola-branded blocks, and the form bindings built on TanStack Form.",
|
|
5
5
|
"license": "MIT",
|
|
6
6
|
"type": "module",
|
|
@@ -21,8 +21,8 @@
|
|
|
21
21
|
"icons": [
|
|
22
22
|
"./dist/icons.d.ts"
|
|
23
23
|
],
|
|
24
|
-
"
|
|
25
|
-
"./dist/
|
|
24
|
+
"testing": [
|
|
25
|
+
"./dist/testing.d.ts"
|
|
26
26
|
]
|
|
27
27
|
}
|
|
28
28
|
},
|
|
@@ -69,14 +69,14 @@
|
|
|
69
69
|
"default": "./dist/icons.cjs"
|
|
70
70
|
}
|
|
71
71
|
},
|
|
72
|
-
"./
|
|
72
|
+
"./testing": {
|
|
73
73
|
"import": {
|
|
74
|
-
"types": "./dist/
|
|
75
|
-
"default": "./dist/
|
|
74
|
+
"types": "./dist/testing.d.ts",
|
|
75
|
+
"default": "./dist/testing.js"
|
|
76
76
|
},
|
|
77
77
|
"require": {
|
|
78
|
-
"types": "./dist/
|
|
79
|
-
"default": "./dist/
|
|
78
|
+
"types": "./dist/testing.d.cts",
|
|
79
|
+
"default": "./dist/testing.cjs"
|
|
80
80
|
}
|
|
81
81
|
}
|
|
82
82
|
},
|
|
@@ -115,15 +115,21 @@
|
|
|
115
115
|
"build:css": "tailwindcss --postcss -c tailwind.config.js -i src/styles.css -o dist/styles.css --minify",
|
|
116
116
|
"build:check": "attw --pack . --exclude-entrypoints ./styles ./styles/compiled",
|
|
117
117
|
"prepublishOnly": "yarn build && yarn build:check",
|
|
118
|
-
"test": "vitest run --passWithNoTests --reporter=dot",
|
|
119
|
-
"test:stories": "vitest run --config vitest.
|
|
120
|
-
"test:stories:coverage": "vitest run --config vitest.
|
|
121
|
-
"
|
|
118
|
+
"test": "vitest run --config vite.config.ts --passWithNoTests --reporter=dot",
|
|
119
|
+
"test:stories": "vitest run --config vitest.config.ts",
|
|
120
|
+
"test:stories:coverage": "vitest run --config vitest.config.ts --coverage",
|
|
121
|
+
"test:visual": "playwright test -c playwright.visual.config.ts",
|
|
122
|
+
"test:visual:update": "playwright test -c playwright.visual.config.ts --update-snapshots",
|
|
123
|
+
"lint": "yarn lint:format && yarn lint:oxlint && yarn lint:design",
|
|
122
124
|
"lint:fix": "oxlint --fix . && oxfmt",
|
|
123
125
|
"knip:check": "yarn --cwd ../.. exec knip --workspace packages/design",
|
|
124
126
|
"knip:fix": "yarn --cwd ../.. exec knip --workspace packages/design --fix-type types,exports,files",
|
|
125
127
|
"lint:format": "oxfmt --check",
|
|
126
128
|
"lint:oxlint": "oxlint . --quiet",
|
|
129
|
+
"lint:design": "designmd lint DESIGN.md",
|
|
130
|
+
"ci-checks": "yarn check:api",
|
|
131
|
+
"generate:api": "tsx scripts/generate-component-api.ts",
|
|
132
|
+
"check:api": "tsx scripts/generate-component-api.ts --check",
|
|
127
133
|
"type-check": "tsc"
|
|
128
134
|
},
|
|
129
135
|
"engines": {
|
|
@@ -131,9 +137,12 @@
|
|
|
131
137
|
},
|
|
132
138
|
"devDependencies": {
|
|
133
139
|
"@arethetypeswrong/cli": "^0.18.2",
|
|
140
|
+
"@google/design.md": "0.2.0",
|
|
134
141
|
"@keboola/oxlint-config": "workspace:^",
|
|
135
142
|
"@keboola/postcss-config": "workspace:^",
|
|
136
143
|
"@keboola/tsconfig": "workspace:^",
|
|
144
|
+
"@playwright/test": "1.60.0",
|
|
145
|
+
"@storybook/addon-a11y": "10.2.19",
|
|
137
146
|
"@storybook/addon-docs": "^10.2.19",
|
|
138
147
|
"@storybook/addon-links": "^10.2.19",
|
|
139
148
|
"@storybook/addon-vitest": "10.2.19",
|
|
@@ -150,15 +159,24 @@
|
|
|
150
159
|
"oxfmt": "^0.44.0",
|
|
151
160
|
"oxlint": "^1.56.0",
|
|
152
161
|
"playwright": "1.60.0",
|
|
162
|
+
"react-docgen-typescript": "^2.2.2",
|
|
153
163
|
"storybook": "^10.2.19",
|
|
154
164
|
"tailwindcss": "^3.4.17",
|
|
155
165
|
"tsup": "^8.5.1",
|
|
166
|
+
"tsx": "^4.19.4",
|
|
156
167
|
"typescript": "^6.0.3",
|
|
157
168
|
"vite": "^8.0.16",
|
|
158
169
|
"vitest": "^4.1.8"
|
|
159
170
|
},
|
|
171
|
+
"peerDependencies": {
|
|
172
|
+
"@testing-library/react": "^16.3.2"
|
|
173
|
+
},
|
|
174
|
+
"peerDependenciesMeta": {
|
|
175
|
+
"@testing-library/react": {
|
|
176
|
+
"optional": true
|
|
177
|
+
}
|
|
178
|
+
},
|
|
160
179
|
"dependencies": {
|
|
161
|
-
"@awesome.me/kit-be8bcf7e5e": "1.0.223",
|
|
162
180
|
"@base-ui/react": "1.5.0",
|
|
163
181
|
"@codemirror/autocomplete": "^6.20.1",
|
|
164
182
|
"@codemirror/commands": "^6.10.3",
|
|
@@ -172,11 +190,10 @@
|
|
|
172
190
|
"@codemirror/merge": "^6.12.1",
|
|
173
191
|
"@codemirror/state": "^6.6.0",
|
|
174
192
|
"@codemirror/view": "^6.41.0",
|
|
175
|
-
"@
|
|
176
|
-
"@fortawesome/react-fontawesome": "^0.2.2",
|
|
193
|
+
"@keboola/brand-registry": "^1.0.0",
|
|
177
194
|
"@keboola/codemirror-lang-sfsql": "^1.2.0",
|
|
178
195
|
"@keboola/codemirror-lang-sql": "^0.2.0",
|
|
179
|
-
"@keboola/tailwind-config": "^0.
|
|
196
|
+
"@keboola/tailwind-config": "^0.2.0",
|
|
180
197
|
"@lexical/code": "^0.42.0",
|
|
181
198
|
"@lexical/link": "^0.42.0",
|
|
182
199
|
"@lexical/list": "^0.42.0",
|
|
@@ -214,6 +231,7 @@
|
|
|
214
231
|
"clsx": "^2.1.1",
|
|
215
232
|
"cmdk": "^1.1.1",
|
|
216
233
|
"diff": "^9.0.0",
|
|
234
|
+
"dompurify": "^3.4.11",
|
|
217
235
|
"input-otp": "^1.4.2",
|
|
218
236
|
"lexical": "^0.42.0",
|
|
219
237
|
"lucide-react": "^1.17.0",
|
|
@@ -222,6 +240,7 @@
|
|
|
222
240
|
"react": "^19.2.6",
|
|
223
241
|
"react-day-picker": "^9.13.0",
|
|
224
242
|
"react-dom": "^19.2.6",
|
|
243
|
+
"react-dropzone": "^14.2.3",
|
|
225
244
|
"react-resizable-panels": "^3.0.4",
|
|
226
245
|
"react-textarea-autosize": "^8.5.9",
|
|
227
246
|
"sonner": "^2.0.7",
|
package/src/styles.css
CHANGED
|
@@ -10,6 +10,7 @@
|
|
|
10
10
|
*/
|
|
11
11
|
:root {
|
|
12
12
|
/* Surface */
|
|
13
|
+
--color-surface: 255 255 255; /* #ffffff — raised container chrome (Modal/Card/Sheet) */
|
|
13
14
|
--color-background: 249 250 251; /* #f9fafb */
|
|
14
15
|
--color-foreground: 30 41 59; /* #1e293b */
|
|
15
16
|
|
|
@@ -51,48 +52,48 @@
|
|
|
51
52
|
--color-sidebar-ring: 31 143 255; /* #1f8fff */
|
|
52
53
|
}
|
|
53
54
|
|
|
54
|
-
/* Dark
|
|
55
|
-
/* TODO finalize dark palette (UT-3936 follow-up) */
|
|
55
|
+
/* Dark mode (Graphite) — Keboola-blue accent, not reachable until the theme toggle ships. */
|
|
56
56
|
[data-theme='dark'] {
|
|
57
57
|
/* Surface */
|
|
58
|
-
--color-
|
|
59
|
-
--color-
|
|
58
|
+
--color-surface: 34 34 40; /* #222228 — raised container chrome (Modal/Card/Sheet) */
|
|
59
|
+
--color-background: 19 19 23; /* #131317 */
|
|
60
|
+
--color-foreground: 229 230 236; /* #e5e6ec */
|
|
60
61
|
|
|
61
|
-
/* Primary — Keboola blue (kept consistent across themes
|
|
62
|
+
/* Primary — Keboola blue (kept consistent across themes). */
|
|
62
63
|
--color-primary: 31 143 255; /* #1f8fff */
|
|
63
64
|
--color-primary-foreground: 255 255 255; /* #ffffff */
|
|
64
65
|
|
|
65
66
|
/* Popover */
|
|
66
|
-
--color-popover:
|
|
67
|
-
--color-popover-foreground:
|
|
67
|
+
--color-popover: 38 38 45; /* #26262d */
|
|
68
|
+
--color-popover-foreground: 229 230 236; /* #e5e6ec */
|
|
68
69
|
|
|
69
70
|
/* Accent */
|
|
70
71
|
--color-accent: 30 58 95; /* #1e3a5f */
|
|
71
72
|
--color-accent-foreground: 147 197 253; /* #93c5fd */
|
|
72
73
|
|
|
73
74
|
/* Border / Input */
|
|
74
|
-
--color-border:
|
|
75
|
-
--color-input:
|
|
75
|
+
--color-border: 52 53 62; /* #34353e */
|
|
76
|
+
--color-input: 60 61 71; /* #3c3d47 */
|
|
76
77
|
|
|
77
78
|
/* Destructive */
|
|
78
|
-
--color-destructive:
|
|
79
|
+
--color-destructive: 248 113 113; /* #f87171 */
|
|
79
80
|
--color-destructive-foreground: 255 255 255; /* #ffffff */
|
|
80
81
|
|
|
81
82
|
/* Success */
|
|
82
|
-
--color-success:
|
|
83
|
+
--color-success: 62 207 127; /* #3ecf7f */
|
|
83
84
|
|
|
84
85
|
/* Muted */
|
|
85
|
-
--color-muted:
|
|
86
|
-
--color-muted-foreground:
|
|
86
|
+
--color-muted: 38 38 45; /* #26262d */
|
|
87
|
+
--color-muted-foreground: 141 144 153; /* #8d9099 */
|
|
87
88
|
|
|
88
89
|
/* Sidebar */
|
|
89
|
-
--color-sidebar:
|
|
90
|
-
--color-sidebar-foreground:
|
|
90
|
+
--color-sidebar: 14 14 18; /* #0e0e12 */
|
|
91
|
+
--color-sidebar-foreground: 229 230 236; /* #e5e6ec */
|
|
91
92
|
--color-sidebar-primary: 31 143 255; /* #1f8fff */
|
|
92
93
|
--color-sidebar-primary-foreground: 255 255 255; /* #ffffff */
|
|
93
|
-
--color-sidebar-accent:
|
|
94
|
-
--color-sidebar-accent-foreground:
|
|
95
|
-
--color-sidebar-border:
|
|
94
|
+
--color-sidebar-accent: 38 38 45; /* #26262d */
|
|
95
|
+
--color-sidebar-accent-foreground: 229 230 236; /* #e5e6ec */
|
|
96
|
+
--color-sidebar-border: 48 49 57; /* #303139 */
|
|
96
97
|
--color-sidebar-ring: 31 143 255; /* #1f8fff */
|
|
97
98
|
}
|
|
98
99
|
|
package/dist/icon-names.cjs
DELETED
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
'use strict';
|
|
2
|
-
|
|
3
|
-
var icons = require('@awesome.me/kit-be8bcf7e5e/icons');
|
|
4
|
-
|
|
5
|
-
// src/icon-names.ts
|
|
6
|
-
var getAvailableIconNames = () => {
|
|
7
|
-
const names = /* @__PURE__ */ new Set();
|
|
8
|
-
for (const def of Object.values(icons.fas)) {
|
|
9
|
-
if (def !== null && typeof def === "object" && "iconName" in def && typeof def.iconName === "string") {
|
|
10
|
-
names.add(def.iconName);
|
|
11
|
-
}
|
|
12
|
-
}
|
|
13
|
-
return [...names].sort();
|
|
14
|
-
};
|
|
15
|
-
|
|
16
|
-
exports.getAvailableIconNames = getAvailableIconNames;
|
|
17
|
-
//# sourceMappingURL=icon-names.cjs.map
|
|
18
|
-
//# sourceMappingURL=icon-names.cjs.map
|
package/dist/icon-names.cjs.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/icon-names.ts"],"names":["fas"],"mappings":";;;;;AAMO,IAAM,wBAAwB,MAAgB;AACnD,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,MAAA,CAAOA,SAAG,CAAA,EAAG;AACpC,IAAA,IACE,GAAA,KAAQ,IAAA,IACR,OAAO,GAAA,KAAQ,QAAA,IACf,cAAc,GAAA,IACd,OAAO,GAAA,CAAI,QAAA,KAAa,QAAA,EACxB;AACA,MAAA,KAAA,CAAM,GAAA,CAAI,IAAI,QAAQ,CAAA;AAAA,IACxB;AAAA,EACF;AACA,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,IAAA,EAAK;AACzB","file":"icon-names.cjs","sourcesContent":["import { fas } from '@awesome.me/kit-be8bcf7e5e/icons';\n\n/**\n * Reads available FontAwesome solid icon names from the FA kit.\n * Returns deduplicated, sorted kebab-case icon names (e.g. \"chart-bar\", \"circle-check\").\n */\nexport const getAvailableIconNames = (): string[] => {\n const names = new Set<string>();\n for (const def of Object.values(fas)) {\n if (\n def !== null &&\n typeof def === 'object' &&\n 'iconName' in def &&\n typeof def.iconName === 'string'\n ) {\n names.add(def.iconName);\n }\n }\n return [...names].sort();\n};\n"]}
|
package/dist/icon-names.d.cts
DELETED
package/dist/icon-names.d.ts
DELETED
package/dist/icon-names.js
DELETED
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
import { fas } from '@awesome.me/kit-be8bcf7e5e/icons';
|
|
2
|
-
|
|
3
|
-
// src/icon-names.ts
|
|
4
|
-
var getAvailableIconNames = () => {
|
|
5
|
-
const names = /* @__PURE__ */ new Set();
|
|
6
|
-
for (const def of Object.values(fas)) {
|
|
7
|
-
if (def !== null && typeof def === "object" && "iconName" in def && typeof def.iconName === "string") {
|
|
8
|
-
names.add(def.iconName);
|
|
9
|
-
}
|
|
10
|
-
}
|
|
11
|
-
return [...names].sort();
|
|
12
|
-
};
|
|
13
|
-
|
|
14
|
-
export { getAvailableIconNames };
|
|
15
|
-
//# sourceMappingURL=icon-names.js.map
|
|
16
|
-
//# sourceMappingURL=icon-names.js.map
|
package/dist/icon-names.js.map
DELETED
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"sources":["../src/icon-names.ts"],"names":[],"mappings":";;;AAMO,IAAM,wBAAwB,MAAgB;AACnD,EAAA,MAAM,KAAA,uBAAY,GAAA,EAAY;AAC9B,EAAA,KAAA,MAAW,GAAA,IAAO,MAAA,CAAO,MAAA,CAAO,GAAG,CAAA,EAAG;AACpC,IAAA,IACE,GAAA,KAAQ,IAAA,IACR,OAAO,GAAA,KAAQ,QAAA,IACf,cAAc,GAAA,IACd,OAAO,GAAA,CAAI,QAAA,KAAa,QAAA,EACxB;AACA,MAAA,KAAA,CAAM,GAAA,CAAI,IAAI,QAAQ,CAAA;AAAA,IACxB;AAAA,EACF;AACA,EAAA,OAAO,CAAC,GAAG,KAAK,CAAA,CAAE,IAAA,EAAK;AACzB","file":"icon-names.js","sourcesContent":["import { fas } from '@awesome.me/kit-be8bcf7e5e/icons';\n\n/**\n * Reads available FontAwesome solid icon names from the FA kit.\n * Returns deduplicated, sorted kebab-case icon names (e.g. \"chart-bar\", \"circle-check\").\n */\nexport const getAvailableIconNames = (): string[] => {\n const names = new Set<string>();\n for (const def of Object.values(fas)) {\n if (\n def !== null &&\n typeof def === 'object' &&\n 'iconName' in def &&\n typeof def.iconName === 'string'\n ) {\n names.add(def.iconName);\n }\n }\n return [...names].sort();\n};\n"]}
|