@datalayer/primer-addons 1.0.20 → 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.
- package/README.md +14 -0
- package/lib/components/appearance/AppearanceMenuWithStore.d.ts +26 -0
- package/lib/components/appearance/AppearanceMenuWithStore.js +21 -0
- package/lib/components/appearance/CollaboratorPalette.d.ts +65 -0
- package/lib/components/appearance/CollaboratorPalette.js +160 -0
- package/lib/components/appearance/index.d.ts +2 -0
- package/lib/components/appearance/index.js +2 -0
- package/lib/components/box/Box.d.ts +17 -5
- package/lib/components/box/Box.js +3 -5
- package/lib/components/calendar-picker/CalendarPicker.d.ts +35 -0
- package/lib/components/calendar-picker/CalendarPicker.js +146 -0
- package/lib/components/calendar-picker/index.d.ts +1 -0
- package/lib/components/calendar-picker/index.js +5 -0
- package/lib/components/card/Card.d.ts +6 -1
- package/lib/components/card/Card.js +1 -1
- package/lib/components/color/ColorSwatch.d.ts +1 -1
- package/lib/components/color-picker/ColorPicker.d.ts +97 -0
- package/lib/components/color-picker/ColorPicker.js +358 -0
- package/lib/components/color-picker/index.d.ts +1 -0
- package/lib/components/color-picker/index.js +5 -0
- package/lib/components/content-loader/ContentLoader.d.ts +1 -1
- package/lib/components/content-loader/ContentLoader.stories.d.ts +1 -1
- package/lib/components/date-picker/DatePicker.d.ts +46 -0
- package/lib/components/date-picker/DatePicker.js +98 -0
- package/lib/components/date-picker/index.d.ts +1 -0
- package/lib/components/date-picker/index.js +5 -0
- package/lib/components/icons/CircleIcon.d.ts +1 -1
- package/lib/components/icons/CircleIcon.stories.d.ts +1 -1
- package/lib/components/index.d.ts +5 -0
- package/lib/components/index.js +5 -0
- package/lib/components/logo/DatalayerText.d.ts +1 -1
- package/lib/components/logo/index.d.ts +0 -1
- package/lib/components/logo/index.js +0 -1
- package/lib/components/side-overlay/SideOverlay.d.ts +5 -5
- package/lib/components/side-overlay/SideOverlay.stories.d.ts +1 -1
- package/lib/components/slider/Slider.d.ts +1 -1
- package/lib/components/slider/Slider.stories.d.ts +1 -1
- package/lib/components/sliding-panel/SlidingPanel.d.ts +1 -1
- package/lib/components/sliding-panel/SlidingPanel.stories.d.ts +1 -1
- package/lib/components/toolbar/Toolbar.d.ts +2 -2
- package/lib/components/toolbar/Toolbar.js +0 -0
- package/lib/components/toolbar/ToolbarButton.d.ts +3 -1
- package/lib/components/toolbar/ToolbarButton.js +54 -22
- package/lib/components/toolbar/ToolbarDivider.d.ts +1 -1
- package/lib/components/toolbar/ToolbarDropdown.d.ts +3 -3
- package/lib/components/toolbar/ToolbarDropdown.js +66 -35
- package/lib/components/toolbar/ToolbarRenderer.d.ts +20 -3
- package/lib/components/toolbar/ToolbarRenderer.js +16 -16
- package/lib/components/toolbar/types.d.ts +15 -8
- package/lib/reactor/AppearancePlugin.d.ts +31 -0
- package/lib/reactor/AppearancePlugin.js +63 -0
- package/lib/reactor/ThemePlugin.d.ts +5 -0
- package/lib/reactor/ThemePlugin.js +98 -0
- package/lib/reactor/index.d.ts +30 -0
- package/lib/reactor/index.js +34 -0
- package/lib/reactor/page-layout/PageLayout.d.ts +148 -0
- package/lib/reactor/page-layout/PageLayout.js +310 -0
- package/lib/reactor/page-layout/PageLayoutPlugin.d.ts +59 -0
- package/lib/reactor/page-layout/PageLayoutPlugin.js +105 -0
- package/lib/reactor/page-layout/PagePanelToggle.d.ts +21 -0
- package/lib/reactor/page-layout/PagePanelToggle.js +29 -0
- package/lib/reactor/page-layout/SlotPageLayout.d.ts +36 -0
- package/lib/reactor/page-layout/SlotPageLayout.js +25 -0
- package/lib/reactor/page-layout/panelState.d.ts +28 -0
- package/lib/reactor/page-layout/panelState.js +49 -0
- package/lib/story-helpers.d.ts +2 -2
- package/lib/theme/DatalayerBrandThemeProvider.d.ts +1 -1
- package/lib/theme/DatalayerThemeProvider.d.ts +5 -5
- package/lib/theme/DatalayerThemeProvider.js +119 -58
- package/lib/theme/css/createThemeCSSVars.d.ts +1 -0
- package/lib/theme/css/createThemeCSSVars.js +9 -0
- package/lib/theme/fontStacks.d.ts +2 -0
- package/lib/theme/fontStacks.js +6 -0
- package/lib/theme/index.d.ts +2 -0
- package/lib/theme/index.js +2 -0
- package/lib/theme/palettes/ColorPalette.d.ts +3 -3
- package/lib/theme/portableTheme.d.ts +32 -0
- package/lib/theme/portableTheme.js +45 -0
- package/lib/utils/Helper.d.ts +2 -2
- package/lib/utils/Portals.d.ts +15 -0
- package/lib/utils/Portals.js +83 -28
- package/lib/utils/Styles.d.ts +1 -1
- package/package.json +15 -14
- package/lib/components/logo/DI.d.ts +0 -19
- 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
|
+
}
|
package/lib/story-helpers.d.ts
CHANGED
|
@@ -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) => "
|
|
11
|
-
export declare const withThemeProvider: (Story: React.FC<React.PropsWithChildren<StoryContext>>, context: StoryContext) => string | number | boolean | Iterable<React.ReactNode> |
|
|
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
|
|
2
|
-
import { ThemeProviderProps } from
|
|
3
|
-
export interface IDatalayerThemeProviderProps extends Omit<ThemeProviderProps,
|
|
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?:
|
|
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
|
|
7
|
-
import { BaseStyles, ThemeProvider } from
|
|
8
|
-
import { useSystemColorMode } from
|
|
9
|
-
import { datalayerTheme, datalayerThemeStyles } from
|
|
10
|
-
import {
|
|
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
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
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
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
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
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
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
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
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
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
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
|
-
*
|
|
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
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
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
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
fontFamily:
|
|
88
|
-
WebkitFontSmoothing:
|
|
89
|
-
MozOsxFontSmoothing:
|
|
90
|
-
textRendering:
|
|
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
|
-
|
|
97
|
-
|
|
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
|
-
|
|
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:
|
|
117
|
-
lineHeight:
|
|
118
|
-
transition:
|
|
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,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"';
|
package/lib/theme/index.d.ts
CHANGED
|
@@ -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';
|
package/lib/theme/index.js
CHANGED
|
@@ -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
|
|
24
|
+
export declare const LightBoostFilter: () => import("react").JSX.Element;
|
|
25
25
|
export declare const SharedDefs: ({ p }: {
|
|
26
26
|
p: ColorPalette;
|
|
27
|
-
}) => import("react
|
|
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
|
|
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
|
+
}
|
package/lib/utils/Helper.d.ts
CHANGED
|
@@ -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
|
-
}) =>
|
|
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>):
|
|
14
|
+
export declare function RedlineBackground({ height, hasBorder, ...rest }: PropsWithChildren<RedlineBackgroundProps>): React.JSX.Element;
|
|
15
15
|
/**
|
|
16
16
|
* Base Types
|
|
17
17
|
*
|
package/lib/utils/Portals.d.ts
CHANGED
|
@@ -1,4 +1,12 @@
|
|
|
1
1
|
import { type CSSProperties } from 'react';
|
|
2
|
+
/**
|
|
3
|
+
* Id of the element Primer portals render under.
|
|
4
|
+
*
|
|
5
|
+
* Exported because a host may need to mark that element as its own — the
|
|
6
|
+
* JupyterLab theming of jupyter-react adds `jp-ThemedContainer` to it, so
|
|
7
|
+
* the rules JupyterLab scopes to that class reach portaled content too.
|
|
8
|
+
*/
|
|
9
|
+
export declare const PRIMER_PORTAL_ROOT_ID = "__primerPortalRoot__";
|
|
2
10
|
type Colormode = 'light' | 'dark' | 'auto';
|
|
3
11
|
/**
|
|
4
12
|
* Ensure we define a root for Primer portal root.
|
|
@@ -11,6 +19,13 @@ type Colormode = 'light' | 'dark' | 'auto';
|
|
|
11
19
|
* @see https://github.com/primer/react/blob/030fe020b48b7f12c2994c6614e5d4191fe764ee/src/Portal/Portal.tsx#L33
|
|
12
20
|
*/
|
|
13
21
|
export declare const setupPrimerPortals: (colormode?: Colormode) => void;
|
|
22
|
+
/**
|
|
23
|
+
* The element Primer portals render under, when it has been set up.
|
|
24
|
+
*
|
|
25
|
+
* `setupPrimerPortals` creates it; before that call there is none, and this
|
|
26
|
+
* answers `null` rather than inventing one.
|
|
27
|
+
*/
|
|
28
|
+
export declare const getPrimerPortalRoot: () => HTMLElement | null;
|
|
14
29
|
/**
|
|
15
30
|
* Sync a set of CSS properties (including CSS custom properties) to
|
|
16
31
|
* `document.body` so that Primer portal content — which is a DOM child
|