@datalayer/primer-addons 1.0.21 → 1.0.22

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.
Files changed (83) hide show
  1. package/README.md +14 -0
  2. package/lib/components/appearance/AppearanceMenuWithStore.d.ts +26 -0
  3. package/lib/components/appearance/AppearanceMenuWithStore.js +21 -0
  4. package/lib/components/appearance/CollaboratorPalette.d.ts +65 -0
  5. package/lib/components/appearance/CollaboratorPalette.js +160 -0
  6. package/lib/components/appearance/index.d.ts +2 -0
  7. package/lib/components/appearance/index.js +2 -0
  8. package/lib/components/box/Box.d.ts +17 -5
  9. package/lib/components/box/Box.js +3 -5
  10. package/lib/components/calendar-picker/CalendarPicker.d.ts +35 -0
  11. package/lib/components/calendar-picker/CalendarPicker.js +146 -0
  12. package/lib/components/calendar-picker/index.d.ts +1 -0
  13. package/lib/components/calendar-picker/index.js +5 -0
  14. package/lib/components/card/Card.d.ts +6 -1
  15. package/lib/components/card/Card.js +1 -1
  16. package/lib/components/color/ColorSwatch.d.ts +1 -1
  17. package/lib/components/color-picker/ColorPicker.d.ts +97 -0
  18. package/lib/components/color-picker/ColorPicker.js +358 -0
  19. package/lib/components/color-picker/index.d.ts +1 -0
  20. package/lib/components/color-picker/index.js +5 -0
  21. package/lib/components/content-loader/ContentLoader.d.ts +1 -1
  22. package/lib/components/content-loader/ContentLoader.stories.d.ts +1 -1
  23. package/lib/components/date-picker/DatePicker.d.ts +46 -0
  24. package/lib/components/date-picker/DatePicker.js +98 -0
  25. package/lib/components/date-picker/index.d.ts +1 -0
  26. package/lib/components/date-picker/index.js +5 -0
  27. package/lib/components/icons/CircleIcon.d.ts +1 -1
  28. package/lib/components/icons/CircleIcon.stories.d.ts +1 -1
  29. package/lib/components/index.d.ts +5 -0
  30. package/lib/components/index.js +5 -0
  31. package/lib/components/logo/DatalayerText.d.ts +1 -1
  32. package/lib/components/logo/index.d.ts +0 -1
  33. package/lib/components/logo/index.js +0 -1
  34. package/lib/components/side-overlay/SideOverlay.d.ts +5 -5
  35. package/lib/components/side-overlay/SideOverlay.stories.d.ts +1 -1
  36. package/lib/components/slider/Slider.d.ts +1 -1
  37. package/lib/components/slider/Slider.stories.d.ts +1 -1
  38. package/lib/components/sliding-panel/SlidingPanel.d.ts +1 -1
  39. package/lib/components/sliding-panel/SlidingPanel.stories.d.ts +1 -1
  40. package/lib/components/toolbar/Toolbar.d.ts +2 -2
  41. package/lib/components/toolbar/Toolbar.js +0 -0
  42. package/lib/components/toolbar/ToolbarButton.d.ts +3 -1
  43. package/lib/components/toolbar/ToolbarButton.js +54 -22
  44. package/lib/components/toolbar/ToolbarDivider.d.ts +1 -1
  45. package/lib/components/toolbar/ToolbarDropdown.d.ts +3 -3
  46. package/lib/components/toolbar/ToolbarDropdown.js +66 -35
  47. package/lib/components/toolbar/ToolbarRenderer.d.ts +20 -3
  48. package/lib/components/toolbar/ToolbarRenderer.js +16 -16
  49. package/lib/components/toolbar/types.d.ts +8 -8
  50. package/lib/reactor/AppearancePlugin.d.ts +31 -0
  51. package/lib/reactor/AppearancePlugin.js +63 -0
  52. package/lib/reactor/ThemePlugin.d.ts +5 -0
  53. package/lib/reactor/ThemePlugin.js +98 -0
  54. package/lib/reactor/index.d.ts +30 -0
  55. package/lib/reactor/index.js +34 -0
  56. package/lib/reactor/page-layout/PageLayout.d.ts +148 -0
  57. package/lib/reactor/page-layout/PageLayout.js +310 -0
  58. package/lib/reactor/page-layout/PageLayoutPlugin.d.ts +59 -0
  59. package/lib/reactor/page-layout/PageLayoutPlugin.js +105 -0
  60. package/lib/reactor/page-layout/PagePanelToggle.d.ts +21 -0
  61. package/lib/reactor/page-layout/PagePanelToggle.js +29 -0
  62. package/lib/reactor/page-layout/SlotPageLayout.d.ts +36 -0
  63. package/lib/reactor/page-layout/SlotPageLayout.js +25 -0
  64. package/lib/reactor/page-layout/panelState.d.ts +28 -0
  65. package/lib/reactor/page-layout/panelState.js +49 -0
  66. package/lib/story-helpers.d.ts +2 -2
  67. package/lib/theme/DatalayerBrandThemeProvider.d.ts +1 -1
  68. package/lib/theme/DatalayerThemeProvider.d.ts +5 -5
  69. package/lib/theme/DatalayerThemeProvider.js +119 -58
  70. package/lib/theme/css/createThemeCSSVars.d.ts +1 -0
  71. package/lib/theme/css/createThemeCSSVars.js +9 -0
  72. package/lib/theme/fontStacks.d.ts +2 -0
  73. package/lib/theme/fontStacks.js +6 -0
  74. package/lib/theme/index.d.ts +2 -0
  75. package/lib/theme/index.js +2 -0
  76. package/lib/theme/palettes/ColorPalette.d.ts +3 -3
  77. package/lib/theme/portableTheme.d.ts +32 -0
  78. package/lib/theme/portableTheme.js +45 -0
  79. package/lib/utils/Helper.d.ts +2 -2
  80. package/lib/utils/Styles.d.ts +1 -1
  81. package/package.json +15 -14
  82. package/lib/components/logo/DI.d.ts +0 -19
  83. package/lib/components/logo/DI.js +0 -20
@@ -0,0 +1,49 @@
1
+ /*
2
+ * Copyright (c) 2023-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /**
6
+ * The page layout's shared state: whether the side panel is open, what the
7
+ * sheet holds, and what is happening in the page.
8
+ *
9
+ * Signals, in a module of their own so the layout, the header toggle, the
10
+ * panel's own contents and a host can all read and write them without any
11
+ * of them importing the others.
12
+ *
13
+ * @module reactor/page-layout/panelState
14
+ */
15
+ import { signal } from "@datalayer/reactor";
16
+ /**
17
+ * Whether the side panel is open.
18
+ *
19
+ * Closed to start: the page is what a reader came for, and a panel with
20
+ * nothing in it yet is a column of nothing.
21
+ */
22
+ export const pageLayoutPanelOpen = signal(false);
23
+ /**
24
+ * What lies on the sheet: the page, or — when there is no page to show — the
25
+ * panel's own content.
26
+ *
27
+ * Written by the layout as it renders, read by the parts that only make
28
+ * sense beside a page: the toggle would open a second copy of what is
29
+ * already on the sheet, and anything hung under a band to echo the panel
30
+ * would repeat it.
31
+ */
32
+ export const pageLayoutSheet = signal("page");
33
+ /**
34
+ * What is happening in the page, in a short line — "Analyst is adding a
35
+ * cell…" — or nothing. The layout pins it to the top of the sheet while it
36
+ * is set. A host or another plugin writes it; a layout given an `activity`
37
+ * prop shows that instead.
38
+ */
39
+ export const pageLayoutActivity = signal(undefined);
40
+ /** Open the panel — for a host that wants its content seen the moment it lands. */
41
+ export function openPagePanel() {
42
+ pageLayoutPanelOpen.value = true;
43
+ }
44
+ export function closePagePanel() {
45
+ pageLayoutPanelOpen.value = false;
46
+ }
47
+ export function togglePagePanel() {
48
+ pageLayoutPanelOpen.value = !pageLayoutPanelOpen.value;
49
+ }
@@ -7,8 +7,8 @@ type StoryContext = Record<string, unknown> & {
7
7
  };
8
8
  parameters: Record<string, unknown>;
9
9
  };
10
- export declare const colormodeFromScheme: (colorScheme: string) => "dark" | "light";
11
- export declare const withThemeProvider: (Story: React.FC<React.PropsWithChildren<StoryContext>>, context: StoryContext) => string | number | boolean | Iterable<React.ReactNode> | import("react/jsx-runtime").JSX.Element | null | undefined;
10
+ export declare const colormodeFromScheme: (colorScheme: string) => "light" | "dark";
11
+ export declare const withThemeProvider: (Story: React.FC<React.PropsWithChildren<StoryContext>>, context: StoryContext) => string | number | bigint | boolean | Iterable<React.ReactNode> | React.JSX.Element | Promise<React.ReactNode> | null | undefined;
12
12
  export declare const toolbarTypes: {
13
13
  colorScheme: {
14
14
  name: string;
@@ -38,4 +38,4 @@ export interface IDatalayerBrandThemeProviderProps {
38
38
  * </DatalayerBrandThemeProvider>
39
39
  * ```
40
40
  */
41
- export declare function DatalayerBrandThemeProvider({ colorMode, brandTheme, style, children, }: IDatalayerBrandThemeProviderProps): JSX.Element;
41
+ export declare function DatalayerBrandThemeProvider({ colorMode, brandTheme, style, children, }: IDatalayerBrandThemeProviderProps): React.JSX.Element;
@@ -1,13 +1,13 @@
1
- import { type CSSProperties } from 'react';
2
- import { ThemeProviderProps } from '@primer/react';
3
- export interface IDatalayerThemeProviderProps extends Omit<ThemeProviderProps, 'theme' | 'colorMode'> {
1
+ import { type CSSProperties } from "react";
2
+ import { ThemeProviderProps } from "@primer/react";
3
+ export interface IDatalayerThemeProviderProps extends Omit<ThemeProviderProps, "theme" | "colorMode"> {
4
4
  /**
5
5
  * Color mode to use.
6
6
  * - `'light'` / `'dark'` — explicit override
7
7
  * - `'auto'` — follow the operating system preference (prefers-color-scheme)
8
8
  * - Primer's `'day'` / `'night'` are still accepted.
9
9
  */
10
- colorMode?: 'light' | 'dark' | 'auto' | 'day' | 'night';
10
+ colorMode?: "light" | "dark" | "auto" | "day" | "night";
11
11
  /**
12
12
  * Additional base styles merged on top of theme defaults.
13
13
  */
@@ -33,4 +33,4 @@ export interface IDatalayerThemeProviderProps extends Omit<ThemeProviderProps, '
33
33
  dark: CSSProperties;
34
34
  };
35
35
  }
36
- export declare function DatalayerThemeProvider(props: React.PropsWithChildren<IDatalayerThemeProviderProps>): JSX.Element;
36
+ export declare function DatalayerThemeProvider(props: React.PropsWithChildren<IDatalayerThemeProviderProps>): React.JSX.Element;
@@ -1,20 +1,20 @@
1
- import { jsx as _jsx } from "react/jsx-runtime";
1
+ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
2
  /*
3
3
  * Copyright (c) 2023-2025 Datalayer, Inc.
4
4
  * Distributed under the terms of the Modified BSD License.
5
5
  */
6
- import { useEffect, useMemo } from 'react';
7
- import { BaseStyles, ThemeProvider } from '@primer/react';
8
- import { useSystemColorMode } from './useSystemColorMode';
9
- import { datalayerTheme, datalayerThemeStyles } from './themes/datalayerTheme';
10
- import { setupPrimerPortals, syncPortalThemeStyles } from '../utils/Portals';
6
+ import { useEffect, useLayoutEffect, useMemo, useRef, useState, } from "react";
7
+ import { BaseStyles, ThemeProvider } from "@primer/react";
8
+ import { useSystemColorMode } from "./useSystemColorMode";
9
+ import { datalayerTheme, datalayerThemeStyles } from "./themes/datalayerTheme";
10
+ import { systemFontStack } from "./fontStacks";
11
+ import { setupPrimerPortals, syncPortalThemeStyles } from "../utils/Portals";
11
12
  /**
12
13
  * System sans-serif font stack — shared between `fontFamily` and
13
14
  * the Primer `--fontStack-*` CSS custom properties so that
14
15
  * components using the CSS `font` shorthand (e.g. `Blankslate`)
15
16
  * also pick up the themed typeface.
16
17
  */
17
- const SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"';
18
18
  /**
19
19
  * Shared typographic rhythm — clean, spacious feel inspired by
20
20
  * modern developer-blog aesthetics (generous line-height, open
@@ -31,70 +31,114 @@ const SYSTEM_FONT = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, A
31
31
  */
32
32
  const typographyVars = {
33
33
  /* ── Font stacks ───────────────────────────────────────────────── */
34
- '--fontStack-monospace': 'ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace',
35
- '--fontStack-sansSerif': SYSTEM_FONT,
36
- '--fontStack-sansSerifDisplay': SYSTEM_FONT,
37
- '--fontStack-system': SYSTEM_FONT,
34
+ "--fontStack-monospace": "ui-monospace, SFMono-Regular, SF Mono, Menlo, Consolas, Liberation Mono, monospace",
35
+ "--fontStack-sansSerif": systemFontStack,
36
+ "--fontStack-sansSerifDisplay": systemFontStack,
37
+ "--fontStack-system": systemFontStack,
38
38
  /* ── Base text weights (from @primer/primitives) ───────────────── */
39
- '--base-text-weight-light': '300',
40
- '--base-text-weight-normal': '400',
41
- '--base-text-weight-medium': '500',
42
- '--base-text-weight-semibold': '600',
39
+ "--base-text-weight-light": "300",
40
+ "--base-text-weight-normal": "400",
41
+ "--base-text-weight-medium": "500",
42
+ "--base-text-weight-semibold": "600",
43
43
  /* ── Body sizing ───────────────────────────────────────────────── */
44
- '--text-body-size-large': '1rem',
45
- '--text-body-size-medium': '0.875rem',
46
- '--text-body-size-small': '0.75rem',
47
- '--text-body-lineHeight-large': '1.5',
48
- '--text-body-lineHeight-medium': '1.4285',
49
- '--text-body-lineHeight-small': '1.6666',
44
+ "--text-body-size-large": "1rem",
45
+ "--text-body-size-medium": "0.875rem",
46
+ "--text-body-size-small": "0.75rem",
47
+ "--text-body-lineHeight-large": "1.5",
48
+ "--text-body-lineHeight-medium": "1.4285",
49
+ "--text-body-lineHeight-small": "1.6666",
50
50
  /* ── Title sizing ──────────────────────────────────────────────── */
51
- '--text-title-size-large': '2rem',
52
- '--text-title-size-medium': '1.25rem',
53
- '--text-title-size-small': '1rem',
54
- '--text-title-lineHeight-large': '1.5',
55
- '--text-title-lineHeight-medium': '1.6',
56
- '--text-title-lineHeight-small': '1.5',
51
+ "--text-title-size-large": "2rem",
52
+ "--text-title-size-medium": "1.25rem",
53
+ "--text-title-size-small": "1rem",
54
+ "--text-title-lineHeight-large": "1.5",
55
+ "--text-title-lineHeight-medium": "1.6",
56
+ "--text-title-lineHeight-small": "1.5",
57
57
  /* ── Caption / subtitle / display ──────────────────────────────── */
58
- '--text-caption-size': '0.75rem',
59
- '--text-caption-lineHeight': '1.3333',
60
- '--text-subtitle-size': '1.25rem',
61
- '--text-subtitle-lineHeight': '1.6',
62
- '--text-display-size': '2.5rem',
63
- '--text-display-lineHeight': '1.4',
58
+ "--text-caption-size": "0.75rem",
59
+ "--text-caption-lineHeight": "1.3333",
60
+ "--text-subtitle-size": "1.25rem",
61
+ "--text-subtitle-lineHeight": "1.6",
62
+ "--text-display-size": "2.5rem",
63
+ "--text-display-lineHeight": "1.4",
64
64
  /* ── Font shorthand tokens ─────────────────────────────────────── *
65
65
  * These are the tokens consumed by Primer components (Blankslate,
66
66
  * etc.) via `font: var(--text-title-shorthand-medium, <fallback>)`.
67
67
  * Defining them here prevents the hard-coded fallback from firing.
68
68
  * Each one references `var(--fontStack-sansSerif)` or
69
- * `SYSTEM_FONT` directly (no nested `var()`) so that the CSS
69
+ * the system font stack directly (no nested `var()`) so that the CSS
70
70
  * `font` shorthand parses reliably. Themes that override fonts
71
71
  * (e.g. Matrix monospace) replace these shorthand tokens via
72
72
  * `buildThemeStyles({ fontFamily })` in their `themeStyles`.
73
73
  * ─────────────────────────────────────────────────────────────── */
74
- '--text-body-shorthand-large': `400 1rem/1.5 ${SYSTEM_FONT}`,
75
- '--text-body-shorthand-medium': `400 0.875rem/1.4285 ${SYSTEM_FONT}`,
76
- '--text-body-shorthand-small': `400 0.75rem/1.6666 ${SYSTEM_FONT}`,
77
- '--text-title-shorthand-large': `600 2rem/1.5 ${SYSTEM_FONT}`,
78
- '--text-title-shorthand-medium': `600 1.25rem/1.6 ${SYSTEM_FONT}`,
79
- '--text-title-shorthand-small': `600 1rem/1.5 ${SYSTEM_FONT}`,
80
- '--text-caption-shorthand': `400 0.75rem/1.3333 ${SYSTEM_FONT}`,
81
- '--text-subtitle-shorthand': `400 1.25rem/1.6 ${SYSTEM_FONT}`,
82
- '--text-display-shorthand': `500 2.5rem/1.4 ${SYSTEM_FONT}`,
74
+ "--text-body-shorthand-large": `400 1rem/1.5 ${systemFontStack}`,
75
+ "--text-body-shorthand-medium": `400 0.875rem/1.4285 ${systemFontStack}`,
76
+ "--text-body-shorthand-small": `400 0.75rem/1.6666 ${systemFontStack}`,
77
+ "--text-title-shorthand-large": `600 2rem/1.5 ${systemFontStack}`,
78
+ "--text-title-shorthand-medium": `600 1.25rem/1.6 ${systemFontStack}`,
79
+ "--text-title-shorthand-small": `600 1rem/1.5 ${systemFontStack}`,
80
+ "--text-caption-shorthand": `400 0.75rem/1.3333 ${systemFontStack}`,
81
+ "--text-subtitle-shorthand": `400 1.25rem/1.6 ${systemFontStack}`,
82
+ "--text-display-shorthand": `500 2.5rem/1.4 ${systemFontStack}`,
83
83
  /* ── Custom overrides ──────────────────────────────────────────── */
84
- '--text-body-lineHeight': '1.7',
85
- '--text-title-lineHeight': '1.2',
86
- '--text-title-letterSpacing': '-0.02em',
87
- fontFamily: SYSTEM_FONT,
88
- WebkitFontSmoothing: 'antialiased',
89
- MozOsxFontSmoothing: 'grayscale',
90
- textRendering: 'optimizeLegibility',
84
+ "--text-body-lineHeight": "1.7",
85
+ "--text-title-lineHeight": "1.2",
86
+ "--text-title-letterSpacing": "-0.02em",
87
+ fontFamily: systemFontStack,
88
+ WebkitFontSmoothing: "antialiased",
89
+ MozOsxFontSmoothing: "grayscale",
90
+ textRendering: "optimizeLegibility",
91
91
  };
92
92
  export function DatalayerThemeProvider(props) {
93
93
  const { children, colorMode, baseStyles, theme, themeStyles, ...rest } = props;
94
94
  // Resolve 'auto' → actual system preference ('light' or 'dark').
95
95
  const systemMode = useSystemColorMode();
96
- const resolvedColorMode = colorMode === 'auto' ? systemMode : (colorMode ?? 'light');
97
- const isDark = resolvedColorMode === 'dark' || resolvedColorMode === 'night';
96
+ /*
97
+ * Whether this provider sits inside another themed element, and if so what
98
+ * that element says.
99
+ *
100
+ * The portal root is one element shared by the whole page, and every
101
+ * provider used to restamp it from its own mode — so a provider nested in
102
+ * the page (a deck in its own theme, a widget rendered into a notebook cell
103
+ * as a React root of its own) wrote its mode over the application's
104
+ * whenever it mounted, and the next overlay came up in the wrong colour.
105
+ * Nested providers leave the portal root alone. And a nested provider given
106
+ * no `colorMode` wears the mode above it rather than a default of light: a
107
+ * widget inside a dark page has no business being light. Read from the DOM
108
+ * rather than from React context, because those widget roots are separate
109
+ * React trees; what they share with the application is the document.
110
+ */
111
+ const sentinel = useRef(null);
112
+ const [themedAncestor, setThemedAncestor] = useState(null);
113
+ const [ancestorMode, setAncestorMode] = useState(null);
114
+ useLayoutEffect(() => {
115
+ // The sentinel's parent is this provider's own element; anything themed
116
+ // above *that* is another provider's.
117
+ const own = sentinel.current?.parentElement;
118
+ const ancestor = own?.parentElement?.closest("[data-color-mode]") ?? null;
119
+ setThemedAncestor(ancestor);
120
+ if (!ancestor) {
121
+ setAncestorMode(null);
122
+ return undefined;
123
+ }
124
+ const read = () => {
125
+ const mode = ancestor.getAttribute("data-color-mode");
126
+ setAncestorMode(mode === "dark" || mode === "night" ? "dark" : "light");
127
+ };
128
+ read();
129
+ // Rewritten on every toggle; follow it.
130
+ const observer = new MutationObserver(read);
131
+ observer.observe(ancestor, {
132
+ attributes: true,
133
+ attributeFilter: ["data-color-mode"],
134
+ });
135
+ return () => observer.disconnect();
136
+ }, []);
137
+ const nested = themedAncestor !== null;
138
+ const resolvedColorMode = colorMode === "auto"
139
+ ? systemMode
140
+ : (colorMode ?? (nested ? (ancestorMode ?? "light") : "light"));
141
+ const isDark = resolvedColorMode === "dark" || resolvedColorMode === "night";
98
142
  const resolvedTheme = theme ?? datalayerTheme;
99
143
  const styles = themeStyles ?? datalayerThemeStyles;
100
144
  const resolvedStyles = isDark ? styles.dark : styles.light;
@@ -109,15 +153,32 @@ export function DatalayerThemeProvider(props) {
109
153
  // Keep document.body portal-root attributes AND theme styles in sync
110
154
  // so that Primer portals (modals, dialogs, overlays) inherit the
111
155
  // correct color mode **and** theme tokens (fonts, colors, CSS vars).
156
+ //
157
+ // Only the outermost provider does this. The portal root is one element
158
+ // shared by the whole page, and every provider used to restamp it from its
159
+ // own mode — so a provider nested in the page (a deck in its own theme, a
160
+ // widget jupyter-react renders into a notebook cell as a React root of its
161
+ // own) wrote its mode over the application's whenever it mounted, and the
162
+ // next overlay came up in the wrong colour. Nesting is read from the DOM
163
+ // rather than from React context, because those widget roots are separate
164
+ // React trees: what they share with the application is the document.
112
165
  useEffect(() => {
113
- setupPrimerPortals(resolvedColorMode === 'night' ? 'dark' : resolvedColorMode === 'day' ? 'light' : resolvedColorMode);
166
+ if (nested) {
167
+ // Another provider owns the page's portal root; see above.
168
+ return;
169
+ }
170
+ setupPrimerPortals(resolvedColorMode === "night"
171
+ ? "dark"
172
+ : resolvedColorMode === "day"
173
+ ? "light"
174
+ : resolvedColorMode);
114
175
  syncPortalThemeStyles(portalStyles);
115
- }, [resolvedColorMode, portalStyles]);
116
- return (_jsx(ThemeProvider, { colorMode: resolvedColorMode, theme: resolvedTheme, ...rest, children: _jsx(BaseStyles, { style: {
117
- lineHeight: '1.7',
118
- transition: 'background-color 0.25s ease, color 0.25s ease',
176
+ }, [nested, resolvedColorMode, portalStyles]);
177
+ return (_jsx(ThemeProvider, { colorMode: resolvedColorMode, theme: resolvedTheme, ...rest, children: _jsxs(BaseStyles, { style: {
178
+ lineHeight: "1.7",
179
+ transition: "background-color 0.25s ease, color 0.25s ease",
119
180
  ...typographyVars,
120
181
  ...resolvedStyles,
121
182
  ...baseStyles,
122
- }, children: children }) }));
183
+ }, children: [_jsx("span", { ref: sentinel, hidden: true, "data-datalayer-theme-root": "" }), children] }) }));
123
184
  }
@@ -135,6 +135,7 @@ export interface ThemeColorDefs {
135
135
  * | Background | `--bgColor-default` |
136
136
  * | Foreground | `--fgColor-default` |
137
137
  * | Border | `--borderColor-default` |
138
+ * | Field (rest) | `--control-borderColor-rest` |
138
139
  * | Link | `--fgColor-link` |
139
140
  * | Accent / Success | `--bgColor-accent-emphasis` |
140
141
  * | Default button | `--button-default-bgColor-rest` |
@@ -19,6 +19,7 @@
19
19
  * | Background | `--bgColor-default` |
20
20
  * | Foreground | `--fgColor-default` |
21
21
  * | Border | `--borderColor-default` |
22
+ * | Field (rest) | `--control-borderColor-rest` |
22
23
  * | Link | `--fgColor-link` |
23
24
  * | Accent / Success | `--bgColor-accent-emphasis` |
24
25
  * | Default button | `--button-default-bgColor-rest` |
@@ -219,6 +220,14 @@ export function colorDefsToCSS(defs, mode = 'light') {
219
220
  '--control-transparent-bgColor-selected': defs.btn.selectedBg,
220
221
  '--control-transparent-borderColor-hover': 'transparent',
221
222
  '--control-transparent-borderColor-active': 'transparent',
223
+ /* ── Fields (rest state) ──────────────────────────────────────
224
+ * `TextInput` / `Textarea` / `Select` paint their inactive border
225
+ * with `var(--control-borderColor-rest, var(--color-btn-border))`
226
+ * — both legacy fallbacks apps that skip @primer/primitives CSS
227
+ * never get — so without this the border shorthand is invalid and
228
+ * the field shows no border at all until it is focused. */
229
+ '--control-borderColor-rest': defs.border?.default ?? defs.btn.border,
230
+ '--control-fgColor-rest': defs.fg.default,
222
231
  /* ── Disabled controls / fields ─────────────────────────────── */
223
232
  '--control-bgColor-disabled': defs.canvas.subtle ?? defs.btn.bg,
224
233
  '--control-borderColor-disabled': defs.border?.default ?? defs.btn.border,
@@ -0,0 +1,2 @@
1
+ /** Default typeface used when a Datalayer theme does not override typography. */
2
+ export declare const systemFontStack = "-apple-system, BlinkMacSystemFont, \"Segoe UI\", Helvetica, Arial, sans-serif, \"Apple Color Emoji\", \"Segoe UI Emoji\"";
@@ -0,0 +1,6 @@
1
+ /*
2
+ * Copyright (c) 2023-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ /** Default typeface used when a Datalayer theme does not override typography. */
6
+ export const systemFontStack = '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"';
@@ -13,6 +13,8 @@ export * from './themes/ivoryTheme';
13
13
  export * from './themes/sunTheme';
14
14
  export * from './themes-brand/spatialBrandTheme';
15
15
  export * from './themeRegistry';
16
+ export * from './portableTheme';
17
+ export * from './fontStacks';
16
18
  export * from './palettes';
17
19
  export * from './useSystemColorMode';
18
20
  export * from './useThemeStore';
@@ -17,6 +17,8 @@ export * from './themes/ivoryTheme';
17
17
  export * from './themes/sunTheme';
18
18
  export * from './themes-brand/spatialBrandTheme';
19
19
  export * from './themeRegistry';
20
+ export * from './portableTheme';
21
+ export * from './fontStacks';
20
22
  export * from './palettes';
21
23
  export * from './useSystemColorMode';
22
24
  export * from './useThemeStore';
@@ -21,12 +21,12 @@ export declare function getColorPalette(theme?: ThemeVariant, colorMode?: 'light
21
21
  export declare function useColorPalette(): ColorPalette;
22
22
  /** @deprecated Use `useColorPalette` instead. */
23
23
  export declare const useBlogColorPalette: typeof useColorPalette;
24
- export declare const LightBoostFilter: () => import("react/jsx-runtime").JSX.Element;
24
+ export declare const LightBoostFilter: () => import("react").JSX.Element;
25
25
  export declare const SharedDefs: ({ p }: {
26
26
  p: ColorPalette;
27
- }) => import("react/jsx-runtime").JSX.Element;
27
+ }) => import("react").JSX.Element;
28
28
  export declare const svgLightFilter: (p: ColorPalette) => "url(#svgLightBoost)" | undefined;
29
29
  export declare const BgGrid: ({ w, h }: {
30
30
  w?: number;
31
31
  h?: number;
32
- }) => import("react/jsx-runtime").JSX.Element;
32
+ }) => import("react").JSX.Element;
@@ -0,0 +1,32 @@
1
+ import { type ColorMode } from './DatalayerBrandThemeProvider';
2
+ import { type ThemeVariant } from './themeRegistry';
3
+ /** A valid CSS custom-property name. */
4
+ export type CssVariableName = `--${string}`;
5
+ /**
6
+ * A JSON-safe CSS-variable map.
7
+ *
8
+ * Theme values retain Primer's canonical functional and component token names
9
+ * (`--bgColor-*`, `--fgColor-*`, `--button-*`, and so on), allowing consumers
10
+ * to use the map without depending on React, styled-components, or Primer.
11
+ */
12
+ export type CssVariableMap = Record<CssVariableName, string>;
13
+ /** A portable theme with explicit light and dark CSS-variable modes. */
14
+ export interface PortableTheme {
15
+ id: ThemeVariant;
16
+ label: string;
17
+ description: string;
18
+ defaultColorMode: ColorMode;
19
+ modes: {
20
+ light: CssVariableMap;
21
+ dark: CssVariableMap;
22
+ };
23
+ }
24
+ /**
25
+ * Export a Primer Addons theme as a framework-neutral, JSON-safe token map.
26
+ *
27
+ * Framework adapters should consume these canonical CSS variables directly
28
+ * instead of translating them into framework-specific color names.
29
+ */
30
+ export declare function exportPortableTheme(variant: ThemeVariant): PortableTheme;
31
+ /** Serialize a trusted portable map for a CSS declaration block. */
32
+ export declare function cssVariableDeclarations(tokens: CssVariableMap): string;
@@ -0,0 +1,45 @@
1
+ /*
2
+ * Copyright (c) 2023-2026 Datalayer, Inc.
3
+ * Distributed under the terms of the Modified BSD License.
4
+ */
5
+ import { systemFontStack } from './fontStacks';
6
+ import { themeConfigs } from './themeRegistry';
7
+ function cssVariables(styles) {
8
+ const variables = Object.fromEntries(Object.entries(styles)
9
+ .filter(([name, value]) => name.startsWith('--') && value !== undefined && value !== null)
10
+ .map(([name, value]) => [name, String(value)]));
11
+ const fontFamily = variables['--fontStack-sansSerif'] ??
12
+ styles.fontFamily ??
13
+ systemFontStack;
14
+ return {
15
+ '--fontStack-sansSerif': fontFamily,
16
+ '--fontStack-sansSerifDisplay': fontFamily,
17
+ '--fontStack-system': fontFamily,
18
+ ...variables,
19
+ };
20
+ }
21
+ /**
22
+ * Export a Primer Addons theme as a framework-neutral, JSON-safe token map.
23
+ *
24
+ * Framework adapters should consume these canonical CSS variables directly
25
+ * instead of translating them into framework-specific color names.
26
+ */
27
+ export function exportPortableTheme(variant) {
28
+ const config = themeConfigs[variant];
29
+ return {
30
+ id: variant,
31
+ label: config.label,
32
+ description: config.description,
33
+ defaultColorMode: config.defaultColorMode,
34
+ modes: {
35
+ light: cssVariables(config.themeStyles.light),
36
+ dark: cssVariables(config.themeStyles.dark),
37
+ },
38
+ };
39
+ }
40
+ /** Serialize a trusted portable map for a CSS declaration block. */
41
+ export function cssVariableDeclarations(tokens) {
42
+ return Object.entries(tokens)
43
+ .map(([name, value]) => `${name}:${value}`)
44
+ .join(';');
45
+ }
@@ -6,12 +6,12 @@ import type { AnimateProps } from '@primer/react-brand';
6
6
  export declare const Container: ({ children, style, }: {
7
7
  children: React.ReactElement[] | React.ReactElement;
8
8
  style?: React.CSSProperties;
9
- }) => import("react/jsx-runtime").JSX.Element;
9
+ }) => React.JSX.Element;
10
10
  type RedlineBackgroundProps = {
11
11
  height?: number;
12
12
  hasBorder?: boolean;
13
13
  };
14
- export declare function RedlineBackground({ height, hasBorder, ...rest }: PropsWithChildren<RedlineBackgroundProps>): import("react/jsx-runtime").JSX.Element;
14
+ export declare function RedlineBackground({ height, hasBorder, ...rest }: PropsWithChildren<RedlineBackgroundProps>): React.JSX.Element;
15
15
  /**
16
16
  * Base Types
17
17
  *
@@ -1,2 +1,2 @@
1
- export declare const Styles: () => import("react/jsx-runtime").JSX.Element;
1
+ export declare const Styles: () => import("react").JSX.Element;
2
2
  export default Styles;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@datalayer/primer-addons",
3
- "version": "1.0.21",
3
+ "version": "1.0.22",
4
4
  "license": "BSD-3-Clause",
5
5
  "scripts": {
6
6
  "build": "npm run build:lib",
@@ -33,11 +33,12 @@
33
33
  "homepage": "https://primer-addons.datalayer.tech",
34
34
  "dependencies": {
35
35
  "@datalayer/icons-react": "^1.0.0",
36
- "@primer/octicons-react": "^19.29.1",
36
+ "@datalayer/reactor": "^1.0.0",
37
+ "@primer/octicons-react": "^19.35.0",
37
38
  "@primer/primitives": "^10.7.0",
38
39
  "@primer/react": "^37.31.0",
39
- "react": "^18.3.1 || ^19.0.0",
40
- "react-dom": "^18.3.1 || ^19.0.0",
40
+ "react": "19.2.8",
41
+ "react-dom": "19.2.8",
41
42
  "react-loading-skeleton": "^3.5.0",
42
43
  "styled-components": "^5.3.10",
43
44
  "zustand": "^5.0.0"
@@ -52,21 +53,21 @@
52
53
  "@storybook/react-vite": "^9.0.15",
53
54
  "@storybook/testing-library": "^0.2.2",
54
55
  "@types/prettier": "^3.0.0",
55
- "@types/react": "18.3.20",
56
- "@types/react-dom": "18.3.6",
56
+ "@types/react": "19.2.18",
57
+ "@types/react-dom": "19.2.5",
57
58
  "@types/styled-components": "^5.1.34",
58
- "@typescript-eslint/eslint-plugin": "^8.29.1",
59
- "@typescript-eslint/parser": "^8.29.1",
59
+ "@typescript-eslint/eslint-plugin": "^8.69.0",
60
+ "@typescript-eslint/parser": "^8.69.0",
60
61
  "@vitejs/plugin-react": "^4.5.2",
61
62
  "crypto-browserify": "^3.12.0",
62
- "eslint": "^9.0.0",
63
- "eslint-plugin-react-hooks": "^5.2.0",
64
- "eslint-plugin-react-refresh": "^0.3.4",
65
- "eslint-plugin-storybook": "^0.6.12",
63
+ "eslint": "^10.9.1",
64
+ "eslint-plugin-react-hooks": "^7.1.1",
65
+ "eslint-plugin-react-refresh": "^0.5.5",
66
+ "eslint-plugin-storybook": "^10.5.10",
66
67
  "prettier": "^3.3.2",
67
68
  "prop-types": "^15.8.1",
68
69
  "storybook": "^9.0.15",
69
- "typescript": "^5.8.3",
70
- "vite": "^7.0.0"
70
+ "typescript": "5.9.3",
71
+ "vite": "7.3.6"
71
72
  }
72
73
  }
@@ -1,19 +0,0 @@
1
- import { type SVGProps } from 'react';
2
- import { type ThemeVariant } from '../../theme';
3
- export interface DIProps extends Omit<SVGProps<SVGSVGElement>, 'ref'> {
4
- size?: number;
5
- variant?: ThemeVariant;
6
- colorMode?: 'light' | 'dark' | 'auto';
7
- /** Colour of the "I" glyph. */
8
- primaryColor?: string;
9
- /** Colour of the "D" glyph. */
10
- secondaryColor?: string;
11
- }
12
- /**
13
- * DI glyph mark.
14
- *
15
- * Uses the same public API and semantic color mapping as AI:
16
- * primaryColor styles the "I", secondaryColor styles the "D".
17
- */
18
- export declare const DI: import("react").ForwardRefExoticComponent<DIProps & import("react").RefAttributes<SVGSVGElement>>;
19
- export default DI;
@@ -1,20 +0,0 @@
1
- import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
2
- /*
3
- * Copyright (c) 2023-2026 Datalayer, Inc.
4
- * Distributed under the terms of the Modified BSD License.
5
- */
6
- import { forwardRef } from 'react';
7
- import { getLogoColors } from './DatalayerLogo';
8
- /**
9
- * DI glyph mark.
10
- *
11
- * Uses the same public API and semantic color mapping as AI:
12
- * primaryColor styles the "I", secondaryColor styles the "D".
13
- */
14
- export const DI = forwardRef(function DI({ size = 24, variant = 'datalayer', colorMode = 'light', primaryColor, secondaryColor, ...svgProps }, ref) {
15
- const themed = getLogoColors(variant, colorMode);
16
- const resolvedPrimary = primaryColor ?? themed.primary;
17
- const resolvedSecondary = secondaryColor ?? themed.textColor;
18
- return (_jsxs("svg", { xmlns: "http://www.w3.org/2000/svg", fill: "none", viewBox: "0 0 24 24", width: size, height: size, role: "img", "aria-label": "DI", ref: ref, ...svgProps, children: [_jsx("path", { d: "M2 3.932H5V20.068075H2ZM3.5 3.932H6.5L12.933 20.068075H9.933ZM2 17.068075H11.4V20.068075H2Z", fill: resolvedSecondary, strokeWidth: 0.25 }), _jsx("path", { d: "m10.788825 3.932 6.43325 16.136075h3.5279L14.316725 3.932H10.788825Z", fill: resolvedPrimary, strokeWidth: 0.25 })] }));
19
- });
20
- export default DI;