@datalayer/primer-addons 1.0.7 → 1.0.9
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/lib/components/box/sx.d.ts +3 -1
- package/lib/components/box/sx.js +1 -1
- package/lib/components/overlay/Overlay.js +15 -2
- package/lib/components/toolbar/FloatingToolbar.js +18 -12
- package/lib/components/toolbar/Toolbar.js +1 -1
- package/lib/components/toolbar/ToolbarButton.js +1 -1
- package/lib/components/toolbar/ToolbarDropdown.js +68 -17
- package/lib/components/toolbar/types.d.ts +6 -0
- package/lib/index.d.ts +1 -0
- package/lib/index.js +2 -0
- package/lib/theme/DatalayerBrandThemeProvider.d.ts +41 -0
- package/lib/theme/DatalayerBrandThemeProvider.js +32 -0
- package/lib/theme/DatalayerThemeProvider.d.ts +36 -0
- package/lib/theme/DatalayerThemeProvider.js +35 -0
- package/lib/theme/Palette.d.ts +4 -0
- package/lib/theme/Palette.js +10 -0
- package/lib/theme/colors/datalayerColors.d.ts +15 -0
- package/lib/theme/colors/datalayerColors.js +21 -0
- package/lib/theme/colors/index.d.ts +4 -0
- package/lib/theme/colors/index.js +8 -0
- package/lib/theme/colors/lovelyColors.d.ts +15 -0
- package/lib/theme/colors/lovelyColors.js +21 -0
- package/lib/theme/colors/matrixColors.d.ts +20 -0
- package/lib/theme/colors/matrixColors.js +26 -0
- package/lib/theme/colors/spatialColors.d.ts +15 -0
- package/lib/theme/colors/spatialColors.js +21 -0
- package/lib/theme/index.d.ts +11 -0
- package/lib/theme/index.js +15 -0
- package/lib/theme/themes/createThemeCSSVars.d.ts +131 -0
- package/lib/theme/themes/createThemeCSSVars.js +172 -0
- package/lib/theme/themes/datalayerTheme.d.ts +1093 -0
- package/lib/theme/themes/datalayerTheme.js +191 -0
- package/lib/theme/themes/lovelyTheme.d.ts +1093 -0
- package/lib/theme/themes/lovelyTheme.js +190 -0
- package/lib/theme/themes/matrixTheme.d.ts +1093 -0
- package/lib/theme/themes/matrixTheme.js +190 -0
- package/lib/theme/themes/spatialTheme.d.ts +1093 -0
- package/lib/theme/themes/spatialTheme.js +190 -0
- package/lib/theme/themes-brand/index.d.ts +1 -0
- package/lib/theme/themes-brand/index.js +5 -0
- package/lib/theme/themes-brand/spatialBrandTheme.d.ts +16 -0
- package/lib/theme/themes-brand/spatialBrandTheme.js +157 -0
- package/lib/theme/useSystemColorMode.d.ts +9 -0
- package/lib/theme/useSystemColorMode.js +26 -0
- package/package.json +1 -1
|
@@ -16,6 +16,8 @@ export type BetterSystemStyleObject = BetterCssProperties | SystemStyleObject |
|
|
|
16
16
|
export interface SxProp {
|
|
17
17
|
sx?: BetterSystemStyleObject;
|
|
18
18
|
}
|
|
19
|
-
declare const sx: (props: SxProp
|
|
19
|
+
declare const sx: (props: SxProp & {
|
|
20
|
+
theme?: Record<string, unknown>;
|
|
21
|
+
}) => import("@styled-system/css").CSSObject;
|
|
20
22
|
export default sx;
|
|
21
23
|
export { merge };
|
package/lib/components/box/sx.js
CHANGED
|
@@ -12,6 +12,19 @@ const PrimerAddonOverlay = (props) => {
|
|
|
12
12
|
setTop(headingRef?.current?.getBoundingClientRect().bottom ?? 0);
|
|
13
13
|
}, [headingRef]);
|
|
14
14
|
const closeOverlay = () => setIsOpen(false);
|
|
15
|
+
/**
|
|
16
|
+
* Don't close the overlay when the click outside lands on a portaled
|
|
17
|
+
* Dialog (role="dialog"). Primer Dialogs render in a portal at
|
|
18
|
+
* document.body, so they are "outside" the Overlay DOM tree and would
|
|
19
|
+
* otherwise trigger an unwanted close.
|
|
20
|
+
*/
|
|
21
|
+
const handleClickOutside = (e) => {
|
|
22
|
+
if (e.target instanceof HTMLElement &&
|
|
23
|
+
e.target.closest('[role="dialog"]')) {
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
closeOverlay();
|
|
27
|
+
};
|
|
15
28
|
const overlayContent = (_jsx(ThemeProvider, { theme: theme, colorMode: colorMode, children: _jsx(BaseStyles, { children: _jsxs(Box, { sx: {
|
|
16
29
|
/* We need to remove the padding */
|
|
17
30
|
height: `calc(100vh - ${top}px - 8px)`,
|
|
@@ -28,9 +41,9 @@ const PrimerAddonOverlay = (props) => {
|
|
|
28
41
|
return (closeButtonRef && openButtonRef ?
|
|
29
42
|
_jsx(_Fragment, { children: isOpen ?
|
|
30
43
|
direction === 'left' ?
|
|
31
|
-
_jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside:
|
|
44
|
+
_jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: handleClickOutside, width: "auto", anchorSide: "inside-right", left: 0, position: "fixed", top: top, children: overlayContent }) })
|
|
32
45
|
:
|
|
33
|
-
_jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside:
|
|
46
|
+
_jsx(Box, { sx: { zIndex }, children: _jsx(PrimerOverlay, { initialFocusRef: closeButtonRef, returnFocusRef: openButtonRef, ignoreClickRefs: [openButtonRef], onEscape: closeOverlay, onClickOutside: handleClickOutside, width: "auto", anchorSide: 'inside-left', right: 0, position: "fixed", top: top, children: overlayContent }) })
|
|
34
47
|
: _jsx(_Fragment, {}) })
|
|
35
48
|
:
|
|
36
49
|
_jsx(_Fragment, {}));
|
|
@@ -31,8 +31,7 @@ import { ToolbarRenderer } from './ToolbarRenderer';
|
|
|
31
31
|
* Position the floating element relative to a DOMRect within an anchor element.
|
|
32
32
|
*/
|
|
33
33
|
function setFloatingPosition(targetRect, floatingElem, anchorElem, verticalGap = 10, horizontalOffset = 5) {
|
|
34
|
-
|
|
35
|
-
if (targetRect === null || !scrollerElem) {
|
|
34
|
+
if (targetRect === null) {
|
|
36
35
|
floatingElem.style.opacity = '0';
|
|
37
36
|
floatingElem.style.transform = 'translateY(-4px)';
|
|
38
37
|
floatingElem.style.top = '-10000px';
|
|
@@ -41,17 +40,21 @@ function setFloatingPosition(targetRect, floatingElem, anchorElem, verticalGap =
|
|
|
41
40
|
}
|
|
42
41
|
const floatingElemRect = floatingElem.getBoundingClientRect();
|
|
43
42
|
const anchorElementRect = anchorElem.getBoundingClientRect();
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
//
|
|
50
|
-
if (top <
|
|
51
|
-
top = targetRect.bottom + verticalGap;
|
|
43
|
+
// Convert viewport-relative coordinates to anchor-relative coordinates.
|
|
44
|
+
// The toolbar is position:absolute inside anchorElem (position:relative),
|
|
45
|
+
// so top/left must be relative to the anchor's border box.
|
|
46
|
+
let top = targetRect.top - anchorElementRect.top - floatingElemRect.height - verticalGap;
|
|
47
|
+
let left = targetRect.left - anchorElementRect.left - horizontalOffset;
|
|
48
|
+
// If toolbar would go above the anchor, flip it below the selection
|
|
49
|
+
if (top < 0) {
|
|
50
|
+
top = targetRect.bottom - anchorElementRect.top + verticalGap;
|
|
52
51
|
}
|
|
53
|
-
|
|
54
|
-
|
|
52
|
+
// Keep within horizontal bounds
|
|
53
|
+
if (left + floatingElemRect.width > anchorElementRect.width) {
|
|
54
|
+
left = anchorElementRect.width - floatingElemRect.width - horizontalOffset;
|
|
55
|
+
}
|
|
56
|
+
if (left < horizontalOffset) {
|
|
57
|
+
left = horizontalOffset;
|
|
55
58
|
}
|
|
56
59
|
floatingElem.style.opacity = '1';
|
|
57
60
|
floatingElem.style.transform = 'translateY(0)';
|
|
@@ -85,6 +88,8 @@ export function FloatingToolbar({ items, extraItems, anchorElement, isVisible, c
|
|
|
85
88
|
return;
|
|
86
89
|
const update = () => updatePosition();
|
|
87
90
|
window.addEventListener('resize', update);
|
|
91
|
+
// Capture phase catches scroll on any ancestor (page, container, etc.)
|
|
92
|
+
window.addEventListener('scroll', update, true);
|
|
88
93
|
const scrollerElem = anchorElement?.parentElement;
|
|
89
94
|
if (scrollerElem) {
|
|
90
95
|
scrollerElem.addEventListener('scroll', update);
|
|
@@ -93,6 +98,7 @@ export function FloatingToolbar({ items, extraItems, anchorElement, isVisible, c
|
|
|
93
98
|
update();
|
|
94
99
|
return () => {
|
|
95
100
|
window.removeEventListener('resize', update);
|
|
101
|
+
window.removeEventListener('scroll', update, true);
|
|
96
102
|
if (scrollerElem) {
|
|
97
103
|
scrollerElem.removeEventListener('scroll', update);
|
|
98
104
|
}
|
|
@@ -44,7 +44,7 @@ export function Toolbar({ items, extraItems, className, disabled, ariaLabel = 'E
|
|
|
44
44
|
borderColor: 'border.default',
|
|
45
45
|
position: 'sticky',
|
|
46
46
|
top: 0,
|
|
47
|
-
zIndex:
|
|
47
|
+
zIndex: 1,
|
|
48
48
|
}, children: _jsx(ToolbarRenderer, { items: allItems, disabled: disabled, size: "medium" }) }));
|
|
49
49
|
}
|
|
50
50
|
export default Toolbar;
|
|
@@ -38,7 +38,7 @@ export function ToolbarButton({ item, size = 'medium' }) {
|
|
|
38
38
|
iconElement = _jsx("span", { style: { fontSize: 12, fontWeight: 600, lineHeight: 1 }, children: label });
|
|
39
39
|
}
|
|
40
40
|
const btnSize = size === 'small' ? 28 : 32;
|
|
41
|
-
return (_jsx("button", { type: "button", "aria-label": ariaLabel, title: title, onClick: onClick, disabled: disabled, style: {
|
|
41
|
+
return (_jsx("button", { type: "button", "aria-label": ariaLabel, title: title, onMouseDown: (e) => e.preventDefault(), onClick: onClick, disabled: disabled, style: {
|
|
42
42
|
display: 'inline-flex',
|
|
43
43
|
alignItems: 'center',
|
|
44
44
|
justifyContent: 'center',
|
|
@@ -8,34 +8,85 @@ import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
|
8
8
|
*
|
|
9
9
|
* Uses Primer React ActionMenu for accessible dropdown menus.
|
|
10
10
|
*
|
|
11
|
+
* The trigger button highlights (accent colour) when any option is active,
|
|
12
|
+
* matching the behavior of regular ToolbarButton items.
|
|
13
|
+
*
|
|
14
|
+
* All option rows render a fixed-width LeadingVisual slot so that labels
|
|
15
|
+
* stay left-aligned regardless of whether the option has an icon.
|
|
16
|
+
*
|
|
11
17
|
* @module components/toolbar/ToolbarDropdown
|
|
12
18
|
*/
|
|
13
|
-
import { isValidElement } from 'react';
|
|
19
|
+
import { isValidElement, useMemo } from 'react';
|
|
14
20
|
import { ActionMenu, ActionList, Text } from '@primer/react';
|
|
15
21
|
import { Box } from '../box/Box';
|
|
22
|
+
/**
|
|
23
|
+
* Resolve an icon prop to a ReactNode.
|
|
24
|
+
*
|
|
25
|
+
* Octicon components use React.forwardRef which returns an *object*
|
|
26
|
+
* (typeof === 'object'), NOT a function. We check isValidElement first
|
|
27
|
+
* (already-instantiated JSX), then treat anything else as a component type.
|
|
28
|
+
*/
|
|
16
29
|
function renderIcon(icon) {
|
|
17
30
|
if (!icon)
|
|
18
31
|
return null;
|
|
19
|
-
if (typeof icon === 'function') {
|
|
20
|
-
const IconComp = icon;
|
|
21
|
-
return _jsx(IconComp, { size: 16 });
|
|
22
|
-
}
|
|
23
32
|
if (isValidElement(icon)) {
|
|
24
33
|
return icon;
|
|
25
34
|
}
|
|
26
|
-
|
|
35
|
+
// Component type: function component, forwardRef, or memo wrapper
|
|
36
|
+
const IconComp = icon;
|
|
37
|
+
return _jsx(IconComp, { size: 16 });
|
|
38
|
+
}
|
|
39
|
+
/** Whether any dropdown option in the list has an icon. */
|
|
40
|
+
function hasAnyIcon(options) {
|
|
41
|
+
return options.some(o => !!o.icon);
|
|
27
42
|
}
|
|
28
43
|
export function ToolbarDropdown({ item, size = 'medium' }) {
|
|
29
|
-
const { ariaLabel, icon, label, options, disabled } = item;
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
44
|
+
const { ariaLabel, icon, label, minWidth, options, disabled } = item;
|
|
45
|
+
// Highlight the trigger when any option is active.
|
|
46
|
+
const anyActive = useMemo(() => options.some(o => o.isActive), [options]);
|
|
47
|
+
// If any option has an icon we reserve a leading-visual column for all rows.
|
|
48
|
+
const showLeadingVisual = useMemo(() => hasAnyIcon(options), [options]);
|
|
49
|
+
const btnSize = size === 'small' ? 28 : 32;
|
|
50
|
+
return (_jsxs(ActionMenu, { children: [_jsx(ActionMenu.Anchor, { children: _jsxs("button", { type: "button", "aria-label": ariaLabel, title: ariaLabel, disabled: disabled, style: {
|
|
51
|
+
display: 'inline-flex',
|
|
52
|
+
alignItems: 'center',
|
|
53
|
+
justifyContent: 'center',
|
|
54
|
+
gap: 4,
|
|
55
|
+
minWidth: btnSize,
|
|
56
|
+
height: btnSize,
|
|
57
|
+
padding: label ? '0 8px' : 0,
|
|
58
|
+
margin: 0,
|
|
59
|
+
border: 'none',
|
|
60
|
+
borderRadius: 6,
|
|
61
|
+
cursor: disabled ? 'not-allowed' : 'pointer',
|
|
62
|
+
background: anyActive
|
|
63
|
+
? 'var(--bgColor-accent-muted, rgba(9,105,218,0.1))'
|
|
64
|
+
: 'transparent',
|
|
65
|
+
color: anyActive
|
|
66
|
+
? 'var(--fgColor-accent, #0969da)'
|
|
67
|
+
: 'var(--fgColor-muted, #656d76)',
|
|
68
|
+
opacity: disabled ? 0.5 : 1,
|
|
69
|
+
fontSize: size === 'small' ? 12 : 14,
|
|
70
|
+
fontWeight: 'normal',
|
|
71
|
+
fontFamily: 'inherit',
|
|
72
|
+
lineHeight: 1,
|
|
73
|
+
}, onMouseEnter: (e) => {
|
|
74
|
+
if (!disabled) {
|
|
75
|
+
e.currentTarget.style.background =
|
|
76
|
+
'var(--bgColor-neutral-muted, rgba(175,184,193,0.2))';
|
|
77
|
+
e.currentTarget.style.color = 'var(--fgColor-default, #1f2328)';
|
|
78
|
+
}
|
|
79
|
+
}, onMouseLeave: (e) => {
|
|
80
|
+
e.currentTarget.style.background = anyActive
|
|
81
|
+
? 'var(--bgColor-accent-muted, rgba(9,105,218,0.1))'
|
|
82
|
+
: 'transparent';
|
|
83
|
+
e.currentTarget.style.color = anyActive
|
|
84
|
+
? 'var(--fgColor-accent, #0969da)'
|
|
85
|
+
: 'var(--fgColor-muted, #656d76)';
|
|
86
|
+
}, children: [renderIcon(icon), label && (_jsx("span", { style: {
|
|
87
|
+
display: 'inline-block',
|
|
88
|
+
minWidth: minWidth ? minWidth : undefined,
|
|
89
|
+
textAlign: 'left',
|
|
90
|
+
}, children: label }))] }) }), _jsx(ActionMenu.Overlay, { width: "auto", children: _jsx(ActionList, { children: options.map(option => (_jsxs(ActionList.Item, { onSelect: option.onClick, disabled: option.disabled, active: option.isActive, children: [showLeadingVisual && (_jsx(ActionList.LeadingVisual, { children: option.icon ? renderIcon(option.icon) : _jsx(Box, { sx: { width: 16 } }) })), _jsxs(Box, { sx: { display: 'flex', justifyContent: 'space-between', alignItems: 'center', width: '100%', gap: 3 }, children: [_jsx(Text, { children: option.label }), option.shortcut && (_jsx(Text, { sx: { color: 'fg.subtle', fontSize: 0, fontFamily: 'mono' }, children: option.shortcut }))] })] }, option.key))) }) })] }));
|
|
40
91
|
}
|
|
41
92
|
export default ToolbarDropdown;
|
|
@@ -73,6 +73,12 @@ export interface ToolbarDropdownItem extends ToolbarItemBase {
|
|
|
73
73
|
icon?: React.ComponentType<IconProps> | ReactNode;
|
|
74
74
|
/** Text label for the trigger */
|
|
75
75
|
label?: string;
|
|
76
|
+
/**
|
|
77
|
+
* Fixed minimum width (in px) for the trigger's label area.
|
|
78
|
+
* Set this to the width of the longest possible label so the
|
|
79
|
+
* toolbar doesn't reflow when the selection changes.
|
|
80
|
+
*/
|
|
81
|
+
minWidth?: number;
|
|
76
82
|
/** Dropdown options */
|
|
77
83
|
options: ToolbarDropdownOption[];
|
|
78
84
|
}
|
package/lib/index.d.ts
CHANGED
package/lib/index.js
CHANGED
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { type CSSProperties, type ReactNode } from 'react';
|
|
2
|
+
import type { BrandTheme } from './themes-brand/spatialBrandTheme';
|
|
3
|
+
export type ColorMode = 'light' | 'dark' | 'auto';
|
|
4
|
+
export interface IDatalayerBrandThemeProviderProps {
|
|
5
|
+
/**
|
|
6
|
+
* Color mode to use.
|
|
7
|
+
* - `'light'` / `'dark'` — explicit override
|
|
8
|
+
* - `'auto'` — follow the operating system preference (prefers-color-scheme)
|
|
9
|
+
*/
|
|
10
|
+
colorMode?: ColorMode;
|
|
11
|
+
/**
|
|
12
|
+
* A brand theme object with per-mode CSS variable overrides.
|
|
13
|
+
* Each key (`light` / `dark`) is a `CSSProperties` map whose entries
|
|
14
|
+
* are `--brand-color-*` / `--brand-button-*` custom properties.
|
|
15
|
+
*
|
|
16
|
+
* When omitted, the default Primer Brand tokens are used unchanged.
|
|
17
|
+
*/
|
|
18
|
+
brandTheme?: BrandTheme;
|
|
19
|
+
/**
|
|
20
|
+
* Additional inline styles merged on top of the resolved brand theme.
|
|
21
|
+
*/
|
|
22
|
+
style?: CSSProperties;
|
|
23
|
+
children: ReactNode;
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Theme provider for Primer Brand (`@primer/react-brand`) components.
|
|
27
|
+
*
|
|
28
|
+
* Works by passing CSS custom-property overrides as inline `style` to
|
|
29
|
+
* `ThemeProvider`, which scopes them to its DOM subtree — exactly how
|
|
30
|
+
* the VS Code extension re-skins Primer via CSS variables.
|
|
31
|
+
*
|
|
32
|
+
* ```tsx
|
|
33
|
+
* import { DatalayerBrandThemeProvider } from '@datalayer/core/lib/theme';
|
|
34
|
+
* import { spatialBrandTheme } from '@datalayer/core/lib/theme/themes-brand/spatialBrandTheme';
|
|
35
|
+
*
|
|
36
|
+
* <DatalayerBrandThemeProvider colorMode="dark" brandTheme={spatialBrandTheme}>
|
|
37
|
+
* <Hero>…</Hero>
|
|
38
|
+
* </DatalayerBrandThemeProvider>
|
|
39
|
+
* ```
|
|
40
|
+
*/
|
|
41
|
+
export declare function DatalayerBrandThemeProvider({ colorMode, brandTheme, style, children, }: IDatalayerBrandThemeProviderProps): JSX.Element;
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { ThemeProvider as PrimerBrandThemeProvider } from '@primer/react-brand';
|
|
3
|
+
import { useSystemColorMode } from './useSystemColorMode';
|
|
4
|
+
/**
|
|
5
|
+
* Theme provider for Primer Brand (`@primer/react-brand`) components.
|
|
6
|
+
*
|
|
7
|
+
* Works by passing CSS custom-property overrides as inline `style` to
|
|
8
|
+
* `ThemeProvider`, which scopes them to its DOM subtree — exactly how
|
|
9
|
+
* the VS Code extension re-skins Primer via CSS variables.
|
|
10
|
+
*
|
|
11
|
+
* ```tsx
|
|
12
|
+
* import { DatalayerBrandThemeProvider } from '@datalayer/core/lib/theme';
|
|
13
|
+
* import { spatialBrandTheme } from '@datalayer/core/lib/theme/themes-brand/spatialBrandTheme';
|
|
14
|
+
*
|
|
15
|
+
* <DatalayerBrandThemeProvider colorMode="dark" brandTheme={spatialBrandTheme}>
|
|
16
|
+
* <Hero>…</Hero>
|
|
17
|
+
* </DatalayerBrandThemeProvider>
|
|
18
|
+
* ```
|
|
19
|
+
*/
|
|
20
|
+
export function DatalayerBrandThemeProvider({ colorMode = 'light', brandTheme, style, children, }) {
|
|
21
|
+
const systemMode = useSystemColorMode();
|
|
22
|
+
const resolved = colorMode === 'auto' ? systemMode : colorMode;
|
|
23
|
+
const themeOverrides = brandTheme
|
|
24
|
+
? resolved === 'dark'
|
|
25
|
+
? brandTheme.dark
|
|
26
|
+
: brandTheme.light
|
|
27
|
+
: undefined;
|
|
28
|
+
return (_jsx(PrimerBrandThemeProvider, { colorMode: resolved, style: {
|
|
29
|
+
...themeOverrides,
|
|
30
|
+
...style,
|
|
31
|
+
}, children: children }));
|
|
32
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { type CSSProperties } from 'react';
|
|
2
|
+
import { ThemeProviderProps } from '@primer/react';
|
|
3
|
+
export interface IDatalayerThemeProviderProps extends Omit<ThemeProviderProps, 'theme' | 'colorMode'> {
|
|
4
|
+
/**
|
|
5
|
+
* Color mode to use.
|
|
6
|
+
* - `'light'` / `'dark'` — explicit override
|
|
7
|
+
* - `'auto'` — follow the operating system preference (prefers-color-scheme)
|
|
8
|
+
* - Primer's `'day'` / `'night'` are still accepted.
|
|
9
|
+
*/
|
|
10
|
+
colorMode?: 'light' | 'dark' | 'auto' | 'day' | 'night';
|
|
11
|
+
/**
|
|
12
|
+
* Additional base styles merged on top of theme defaults.
|
|
13
|
+
*/
|
|
14
|
+
baseStyles?: CSSProperties;
|
|
15
|
+
/**
|
|
16
|
+
* Optional Primer theme object. Defaults to the built-in datalayerTheme
|
|
17
|
+
* (which is the unmodified default Primer theme — all theming is done
|
|
18
|
+
* via CSS custom-property overrides in `themeStyles`).
|
|
19
|
+
*/
|
|
20
|
+
theme?: Record<string, any>;
|
|
21
|
+
/**
|
|
22
|
+
* Per-mode CSS-property overrides (background, foreground, and all
|
|
23
|
+
* Primer functional-token CSS custom properties).
|
|
24
|
+
*
|
|
25
|
+
* When provided, these replace the built-in datalayer styles entirely.
|
|
26
|
+
* The `baseStyles` prop is still merged on top.
|
|
27
|
+
*
|
|
28
|
+
* Use the `buildThemeStyles` helper from `./themes/createThemeCSSVars`
|
|
29
|
+
* to generate comprehensive overrides from a `ThemeColorDefs` pair.
|
|
30
|
+
*/
|
|
31
|
+
themeStyles?: {
|
|
32
|
+
light: CSSProperties;
|
|
33
|
+
dark: CSSProperties;
|
|
34
|
+
};
|
|
35
|
+
}
|
|
36
|
+
export declare function DatalayerThemeProvider(props: React.PropsWithChildren<IDatalayerThemeProviderProps>): JSX.Element;
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
import { jsx as _jsx } from "react/jsx-runtime";
|
|
2
|
+
import { BaseStyles, ThemeProvider } from '@primer/react';
|
|
3
|
+
import { useSystemColorMode } from './useSystemColorMode';
|
|
4
|
+
import { datalayerTheme, datalayerThemeStyles } from './themes/datalayerTheme';
|
|
5
|
+
/**
|
|
6
|
+
* Shared typographic rhythm — clean, spacious feel inspired by
|
|
7
|
+
* modern developer-blog aesthetics (generous line-height, open
|
|
8
|
+
* letter-spacing on headings, crisp body text).
|
|
9
|
+
*/
|
|
10
|
+
const typographyVars = {
|
|
11
|
+
'--text-body-lineHeight': '1.7',
|
|
12
|
+
'--text-title-lineHeight': '1.2',
|
|
13
|
+
'--text-title-letterSpacing': '-0.02em',
|
|
14
|
+
fontFamily: '-apple-system, BlinkMacSystemFont, "Segoe UI", Helvetica, Arial, sans-serif, "Apple Color Emoji", "Segoe UI Emoji"',
|
|
15
|
+
WebkitFontSmoothing: 'antialiased',
|
|
16
|
+
MozOsxFontSmoothing: 'grayscale',
|
|
17
|
+
textRendering: 'optimizeLegibility',
|
|
18
|
+
};
|
|
19
|
+
export function DatalayerThemeProvider(props) {
|
|
20
|
+
const { children, colorMode, baseStyles, theme, themeStyles, ...rest } = props;
|
|
21
|
+
// Resolve 'auto' → actual system preference ('light' or 'dark').
|
|
22
|
+
const systemMode = useSystemColorMode();
|
|
23
|
+
const resolvedColorMode = colorMode === 'auto' ? systemMode : (colorMode ?? 'light');
|
|
24
|
+
const isDark = resolvedColorMode === 'dark' || resolvedColorMode === 'night';
|
|
25
|
+
const resolvedTheme = theme ?? datalayerTheme;
|
|
26
|
+
const styles = themeStyles ?? datalayerThemeStyles;
|
|
27
|
+
const resolvedStyles = isDark ? styles.dark : styles.light;
|
|
28
|
+
return (_jsx(ThemeProvider, { colorMode: resolvedColorMode, theme: resolvedTheme, ...rest, children: _jsx(BaseStyles, { style: {
|
|
29
|
+
lineHeight: '1.7',
|
|
30
|
+
transition: 'background-color 0.25s ease, color 0.25s ease',
|
|
31
|
+
...typographyVars,
|
|
32
|
+
...resolvedStyles,
|
|
33
|
+
...baseStyles,
|
|
34
|
+
}, children: children }) }));
|
|
35
|
+
}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
// export const RESERVATION_CIRCLE_COLOR_VAR = '--data-yellow-color';
|
|
6
|
+
export const RESERVATION_CIRCLE_COLOR = '#656D76';
|
|
7
|
+
// export const CREDITS_CIRCLE_COLOR_VAR = '--data-blue-color';
|
|
8
|
+
export const CREDITS_CIRCLE_COLOR = '#16A085';
|
|
9
|
+
export const JOINED_INVITE_COLOR = '#0366d6';
|
|
10
|
+
export const PENDING_INVITE_COLOR = '#cfd3d7';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Datalayer Accessible Color System
|
|
3
|
+
* Based on Datalayer's brand manual - WCAG AA/AAA compliant
|
|
4
|
+
*/
|
|
5
|
+
export declare const datalayerColors: {
|
|
6
|
+
black: string;
|
|
7
|
+
gray: string;
|
|
8
|
+
white: string;
|
|
9
|
+
greenBrand: string;
|
|
10
|
+
greenAccent: string;
|
|
11
|
+
greenText: string;
|
|
12
|
+
greenTint: string;
|
|
13
|
+
greenBright: string;
|
|
14
|
+
greenHover: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Datalayer Accessible Color System
|
|
7
|
+
* Based on Datalayer's brand manual - WCAG AA/AAA compliant
|
|
8
|
+
*/
|
|
9
|
+
export const datalayerColors = {
|
|
10
|
+
// Core Neutrals
|
|
11
|
+
black: '#000000', // Primary text - AAA on white
|
|
12
|
+
gray: '#59595C', // Secondary text - AA on white
|
|
13
|
+
white: '#FFFFFF', // Background
|
|
14
|
+
// Greens (Brand & Accessibility)
|
|
15
|
+
greenBrand: '#16A085', // Brand accent, icons, dividers, headings
|
|
16
|
+
greenAccent: '#1ABC9C', // Icons, charts, highlights on dark surfaces
|
|
17
|
+
greenText: '#117A65', // Accessible green for text & buttons (AA+ on white)
|
|
18
|
+
greenTint: '#E9F7F1', // Soft background for success / callouts
|
|
19
|
+
greenBright: '#2ECC71', // Highlights and glow on dark backgrounds
|
|
20
|
+
greenHover: '#0E6655', // Primary button hover
|
|
21
|
+
};
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export { datalayerColors } from './datalayerColors';
|
|
6
|
+
export { spatialColors } from './spatialColors';
|
|
7
|
+
export { lovelyColors } from './lovelyColors';
|
|
8
|
+
export { matrixColors } from './matrixColors';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Lovely Color System – Warm & Pink-Inspired
|
|
3
|
+
* Rose, magenta, blush — designed for light-first usage.
|
|
4
|
+
*/
|
|
5
|
+
export declare const lovelyColors: {
|
|
6
|
+
black: string;
|
|
7
|
+
gray: string;
|
|
8
|
+
white: string;
|
|
9
|
+
roseBrand: string;
|
|
10
|
+
roseAccent: string;
|
|
11
|
+
roseText: string;
|
|
12
|
+
roseTint: string;
|
|
13
|
+
roseBright: string;
|
|
14
|
+
roseHover: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Lovely Color System – Warm & Pink-Inspired
|
|
7
|
+
* Rose, magenta, blush — designed for light-first usage.
|
|
8
|
+
*/
|
|
9
|
+
export const lovelyColors = {
|
|
10
|
+
// Core Neutrals
|
|
11
|
+
black: '#1A0A14', // Deep plum — primary dark background
|
|
12
|
+
gray: '#9D7A8F', // Mauve gray — secondary text
|
|
13
|
+
white: '#FFF5F7', // Rose white — primary light background
|
|
14
|
+
// Rose / Pink palette
|
|
15
|
+
roseBrand: '#DB2777', // Core brand — headings, icons, dividers
|
|
16
|
+
roseAccent: '#EC4899', // Charts, highlights on dark surfaces
|
|
17
|
+
roseText: '#9D174D', // Accessible text & buttons on light (AA+)
|
|
18
|
+
roseTint: '#FDF2F8', // Soft background for callouts
|
|
19
|
+
roseBright: '#F472B6', // Highlights & glow on dark
|
|
20
|
+
roseHover: '#831843', // Primary button hover
|
|
21
|
+
};
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Matrix Color System – Terminal & Phosphor-Green
|
|
3
|
+
*
|
|
4
|
+
* Inspired by the iconic green-on-black aesthetic of The Matrix.
|
|
5
|
+
* Dark-first, CRT feel. Uses Datalayer brand greens for accessible text
|
|
6
|
+
* and introduces phosphor-green accents for the signature glow.
|
|
7
|
+
*/
|
|
8
|
+
export declare const matrixColors: {
|
|
9
|
+
black: string;
|
|
10
|
+
gray: string;
|
|
11
|
+
white: string;
|
|
12
|
+
greenBrand: string;
|
|
13
|
+
greenAccent: string;
|
|
14
|
+
greenText: string;
|
|
15
|
+
greenTint: string;
|
|
16
|
+
greenPhosphor: string;
|
|
17
|
+
greenGlow: string;
|
|
18
|
+
greenHover: string;
|
|
19
|
+
greenTerminal: string;
|
|
20
|
+
};
|
|
@@ -0,0 +1,26 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Matrix Color System – Terminal & Phosphor-Green
|
|
7
|
+
*
|
|
8
|
+
* Inspired by the iconic green-on-black aesthetic of The Matrix.
|
|
9
|
+
* Dark-first, CRT feel. Uses Datalayer brand greens for accessible text
|
|
10
|
+
* and introduces phosphor-green accents for the signature glow.
|
|
11
|
+
*/
|
|
12
|
+
export const matrixColors = {
|
|
13
|
+
// Core Neutrals — CRT / terminal aesthetic
|
|
14
|
+
black: '#0D0208', // Matrix void — near-black with faint warm undertone
|
|
15
|
+
gray: '#4A7856', // Dim terminal gray-green — secondary text
|
|
16
|
+
white: '#F0FFF0', // Honeydew — light bg with subtle green cast (CRT paper)
|
|
17
|
+
// Green palette — Datalayer brand greens + Matrix phosphor highlights
|
|
18
|
+
greenBrand: '#16A085', // Datalayer brand — headings, icons, dividers
|
|
19
|
+
greenAccent: '#1ABC9C', // Datalayer accent — icons, charts on dark surfaces
|
|
20
|
+
greenText: '#117A65', // Datalayer text — accessible buttons & text on light (AA+)
|
|
21
|
+
greenTint: '#E8F5E9', // Matrix-tinted soft background for callouts
|
|
22
|
+
greenPhosphor: '#00FF41', // Iconic Matrix falling-code green — dark mode highlights
|
|
23
|
+
greenGlow: '#39FF14', // Neon phosphor glow — brightest accent on dark
|
|
24
|
+
greenHover: '#0E6655', // Datalayer hover — primary button hover
|
|
25
|
+
greenTerminal: '#003B00', // Deep terminal — dark mode subtle/canvas
|
|
26
|
+
};
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Spatial Color System – Cosmic & Space-Inspired
|
|
3
|
+
* Deep blues, indigo, nebula purples — designed for dark-first usage.
|
|
4
|
+
*/
|
|
5
|
+
export declare const spatialColors: {
|
|
6
|
+
black: string;
|
|
7
|
+
gray: string;
|
|
8
|
+
white: string;
|
|
9
|
+
indigoBrand: string;
|
|
10
|
+
indigoAccent: string;
|
|
11
|
+
indigoText: string;
|
|
12
|
+
indigoTint: string;
|
|
13
|
+
indigoBright: string;
|
|
14
|
+
indigoHover: string;
|
|
15
|
+
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/**
|
|
6
|
+
* Spatial Color System – Cosmic & Space-Inspired
|
|
7
|
+
* Deep blues, indigo, nebula purples — designed for dark-first usage.
|
|
8
|
+
*/
|
|
9
|
+
export const spatialColors = {
|
|
10
|
+
// Core Neutrals
|
|
11
|
+
black: '#0B1120', // Deep space — primary dark background
|
|
12
|
+
gray: '#8892B0', // Nebula gray — secondary text
|
|
13
|
+
white: '#F8FAFF', // Star white — primary light background
|
|
14
|
+
// Indigo / Cosmic palette
|
|
15
|
+
indigoBrand: '#4F46E5', // Core brand — headings, icons, dividers
|
|
16
|
+
indigoAccent: '#6366F1', // Charts, highlights on dark surfaces
|
|
17
|
+
indigoText: '#3730A3', // Accessible text & buttons on light (AA+)
|
|
18
|
+
indigoTint: '#EEF2FF', // Soft background for callouts
|
|
19
|
+
indigoBright: '#818CF8', // Highlights & glow on dark
|
|
20
|
+
indigoHover: '#312E81', // Primary button hover
|
|
21
|
+
};
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
export * from './colors';
|
|
2
|
+
export * from './themes/createThemeCSSVars';
|
|
3
|
+
export * from './themes/datalayerTheme';
|
|
4
|
+
export * from './DatalayerThemeProvider';
|
|
5
|
+
export * from './DatalayerBrandThemeProvider';
|
|
6
|
+
export * from './themes/spatialTheme';
|
|
7
|
+
export * from './themes/lovelyTheme';
|
|
8
|
+
export * from './themes/matrixTheme';
|
|
9
|
+
export * from './themes-brand/spatialBrandTheme';
|
|
10
|
+
export * from './Palette';
|
|
11
|
+
export * from './useSystemColorMode';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
export * from './colors';
|
|
6
|
+
export * from './themes/createThemeCSSVars';
|
|
7
|
+
export * from './themes/datalayerTheme';
|
|
8
|
+
export * from './DatalayerThemeProvider';
|
|
9
|
+
export * from './DatalayerBrandThemeProvider';
|
|
10
|
+
export * from './themes/spatialTheme';
|
|
11
|
+
export * from './themes/lovelyTheme';
|
|
12
|
+
export * from './themes/matrixTheme';
|
|
13
|
+
export * from './themes-brand/spatialBrandTheme';
|
|
14
|
+
export * from './Palette';
|
|
15
|
+
export * from './useSystemColorMode';
|