@datalayer/primer-addons 1.0.11 → 1.0.12
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/appearance/AppearanceControls.d.ts +10 -0
- package/lib/components/appearance/AppearanceControls.js +42 -0
- package/lib/components/appearance/AppearanceControlsWithStore.d.ts +8 -0
- package/lib/{theme/ThemeSwitcher.js → components/appearance/AppearanceControlsWithStore.js} +5 -3
- package/lib/components/appearance/index.d.ts +2 -0
- package/lib/components/appearance/index.js +6 -0
- package/lib/components/index.d.ts +4 -0
- package/lib/components/index.js +4 -0
- package/lib/components/logo/DatalayerLogo.d.ts +18 -0
- package/lib/components/logo/DatalayerLogo.js +63 -0
- package/lib/components/logo/DatalayerLogoText.d.ts +22 -0
- package/lib/components/logo/DatalayerLogoText.js +29 -0
- package/lib/components/logo/DatalayerText.d.ts +16 -0
- package/lib/components/logo/DatalayerText.js +16 -0
- package/lib/components/logo/index.d.ts +3 -0
- package/lib/components/logo/index.js +3 -0
- package/lib/index.js +0 -1
- package/lib/theme/Palette.d.ts +8 -4
- package/lib/theme/Palette.js +13 -6
- package/lib/theme/colors/index.d.ts +1 -0
- package/lib/theme/colors/index.js +1 -0
- package/lib/theme/colors/indicatorColors.d.ts +50 -0
- package/lib/theme/colors/indicatorColors.js +68 -0
- package/lib/theme/index.d.ts +0 -1
- package/lib/theme/index.js +0 -1
- package/lib/utils/Portals.d.ts +4 -0
- package/lib/utils/Portals.js +61 -9
- package/package.json +1 -1
- package/lib/_utils/story-helpers.d.ts +0 -31
- package/lib/_utils/story-helpers.js +0 -105
- package/lib/theme/ThemeSwitcher.d.ts +0 -17
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
import type { ReactElement } from 'react';
|
|
2
|
+
import { type ColorMode, type ThemeVariant } from '../../theme';
|
|
3
|
+
export interface AppearanceControlsProps {
|
|
4
|
+
colorMode: ColorMode;
|
|
5
|
+
themeVariant: ThemeVariant;
|
|
6
|
+
onColorModeChange: (mode: ColorMode) => void;
|
|
7
|
+
onThemeChange: (theme: ThemeVariant) => void;
|
|
8
|
+
}
|
|
9
|
+
export declare function AppearanceControls({ colorMode, themeVariant, onColorModeChange, onThemeChange, }: AppearanceControlsProps): ReactElement;
|
|
10
|
+
export default AppearanceControls;
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } from "react/jsx-runtime";
|
|
2
|
+
import { SegmentedControl } from '@primer/react';
|
|
3
|
+
import { MoonIcon, SunIcon, DeviceDesktopIcon, } from '@primer/octicons-react';
|
|
4
|
+
import { Box } from '../box/Box';
|
|
5
|
+
import { themeVariants, themeConfigs, } from '../../theme';
|
|
6
|
+
export function AppearanceControls({ colorMode, themeVariant, onColorModeChange, onThemeChange, }) {
|
|
7
|
+
return (_jsxs(_Fragment, { children: [_jsx(Box, { sx: { px: 2, py: 2, display: 'flex', justifyContent: 'center' }, children: _jsxs(SegmentedControl, { "aria-label": "Color mode", size: "small", onChange: (index) => {
|
|
8
|
+
const modes = ['light', 'dark', 'auto'];
|
|
9
|
+
onColorModeChange(modes[index]);
|
|
10
|
+
}, children: [_jsx(SegmentedControl.IconButton, { selected: colorMode === 'light', icon: SunIcon, "aria-label": "Light" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'dark', icon: MoonIcon, "aria-label": "Dark" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'auto', icon: DeviceDesktopIcon, "aria-label": "Auto" })] }) }), _jsx(Box, { sx: {
|
|
11
|
+
px: 2,
|
|
12
|
+
pb: 2,
|
|
13
|
+
display: 'flex',
|
|
14
|
+
justifyContent: 'center',
|
|
15
|
+
gap: 2,
|
|
16
|
+
}, children: themeVariants.map((variant) => {
|
|
17
|
+
const cfg = themeConfigs[variant];
|
|
18
|
+
const isSelected = themeVariant === variant;
|
|
19
|
+
return (_jsx(Box, { as: "button", "aria-label": cfg.label, "aria-pressed": isSelected, onClick: () => onThemeChange(variant), sx: {
|
|
20
|
+
width: 24,
|
|
21
|
+
height: 24,
|
|
22
|
+
borderRadius: '50%',
|
|
23
|
+
backgroundColor: cfg.brandColor,
|
|
24
|
+
border: '2px solid',
|
|
25
|
+
borderColor: isSelected ? 'accent.fg' : 'border.default',
|
|
26
|
+
cursor: 'pointer',
|
|
27
|
+
padding: 0,
|
|
28
|
+
outline: 'none',
|
|
29
|
+
transition: 'border-color 0.15s ease',
|
|
30
|
+
boxShadow: isSelected
|
|
31
|
+
? '0 0 0 2px var(--bgColor-accent-muted, rgba(9,105,218,0.3))'
|
|
32
|
+
: 'none',
|
|
33
|
+
'&:hover': {
|
|
34
|
+
borderColor: 'accent.fg',
|
|
35
|
+
},
|
|
36
|
+
'&:focus-visible': {
|
|
37
|
+
boxShadow: '0 0 0 2px var(--bgColor-accent-muted, rgba(9,105,218,0.3))',
|
|
38
|
+
},
|
|
39
|
+
} }, variant));
|
|
40
|
+
}) })] }));
|
|
41
|
+
}
|
|
42
|
+
export default AppearanceControls;
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import type { UseBoundStore, StoreApi } from 'zustand';
|
|
3
|
+
import type { ThemeState } from '../../theme/useThemeStore';
|
|
4
|
+
export interface AppearanceControlsWithStoreProps {
|
|
5
|
+
useStore: UseBoundStore<StoreApi<ThemeState>>;
|
|
6
|
+
}
|
|
7
|
+
export declare const AppearanceControlsWithStore: React.FC<AppearanceControlsWithStoreProps>;
|
|
8
|
+
export default AppearanceControlsWithStore;
|
|
@@ -1,8 +1,9 @@
|
|
|
1
1
|
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import {
|
|
2
|
+
import { IconButton, SegmentedControl, Tooltip } from '@primer/react';
|
|
3
3
|
import { SunIcon, MoonIcon, DeviceDesktopIcon, CircleIcon, } from '@primer/octicons-react';
|
|
4
|
-
import {
|
|
5
|
-
|
|
4
|
+
import { Box } from '../box/Box';
|
|
5
|
+
import { themeConfigs, themeVariants } from '../../theme/themeRegistry';
|
|
6
|
+
export const AppearanceControlsWithStore = ({ useStore }) => {
|
|
6
7
|
const { colorMode, theme: themeVariant } = useStore();
|
|
7
8
|
return (_jsxs(Box, { sx: { display: 'flex', alignItems: 'center', gap: 3 }, children: [_jsx(Box, { sx: { display: 'flex', alignItems: 'center', gap: 1 }, children: themeVariants.map(variant => {
|
|
8
9
|
const tcfg = themeConfigs[variant];
|
|
@@ -17,3 +18,4 @@ export const ThemeSwitcher = ({ useStore }) => {
|
|
|
17
18
|
useStore.getState().setColorMode(modes[index]);
|
|
18
19
|
}, children: [_jsx(SegmentedControl.IconButton, { selected: colorMode === 'light', icon: SunIcon, "aria-label": "Light" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'dark', icon: MoonIcon, "aria-label": "Dark" }), _jsx(SegmentedControl.IconButton, { selected: colorMode === 'auto', icon: DeviceDesktopIcon, "aria-label": "Auto" })] })] }));
|
|
19
20
|
};
|
|
21
|
+
export default AppearanceControlsWithStore;
|
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export * from "./appearance/AppearanceControls";
|
|
2
|
+
export * from "./appearance/AppearanceControlsWithStore";
|
|
1
3
|
export * from "./box/Box";
|
|
2
4
|
export * from "./card/Card";
|
|
3
5
|
export * from "./content-loader/ContentLoader";
|
|
@@ -5,4 +7,6 @@ export * from "./closeable-flash/CloseableFlash";
|
|
|
5
7
|
export * from "./icons/CircleIcon";
|
|
6
8
|
export * from "./overlay/Overlay";
|
|
7
9
|
export * from "./slider/Slider";
|
|
10
|
+
export * from "./logo";
|
|
11
|
+
export * from "./appearance";
|
|
8
12
|
export * from "./toolbar";
|
package/lib/components/index.js
CHANGED
|
@@ -1,3 +1,5 @@
|
|
|
1
|
+
export * from "./appearance/AppearanceControls";
|
|
2
|
+
export * from "./appearance/AppearanceControlsWithStore";
|
|
1
3
|
export * from "./box/Box";
|
|
2
4
|
export * from "./card/Card";
|
|
3
5
|
export * from "./content-loader/ContentLoader";
|
|
@@ -5,4 +7,6 @@ export * from "./closeable-flash/CloseableFlash";
|
|
|
5
7
|
export * from "./icons/CircleIcon";
|
|
6
8
|
export * from "./overlay/Overlay";
|
|
7
9
|
export * from "./slider/Slider";
|
|
10
|
+
export * from "./logo";
|
|
11
|
+
export * from "./appearance";
|
|
8
12
|
export * from "./toolbar";
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { type SVGProps } from 'react';
|
|
2
|
+
import { type ThemeVariant } from '../../theme';
|
|
3
|
+
export interface LogoColorPair {
|
|
4
|
+
primary: string;
|
|
5
|
+
secondary: string;
|
|
6
|
+
}
|
|
7
|
+
export declare const THEME_LOGO_COLORS: Record<ThemeVariant, {
|
|
8
|
+
light: LogoColorPair;
|
|
9
|
+
dark: LogoColorPair;
|
|
10
|
+
}>;
|
|
11
|
+
export declare function getLogoColors(variant?: ThemeVariant, colorMode?: 'light' | 'dark' | 'auto'): LogoColorPair;
|
|
12
|
+
export interface DatalayerLogoProps extends Omit<SVGProps<SVGSVGElement>, 'ref'> {
|
|
13
|
+
size?: number;
|
|
14
|
+
primaryColor?: string;
|
|
15
|
+
secondaryColor?: string;
|
|
16
|
+
}
|
|
17
|
+
export declare const DatalayerLogo: import("react").ForwardRefExoticComponent<DatalayerLogoProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
18
|
+
export default DatalayerLogo;
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/*
|
|
3
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
4
|
+
* Distributed under the terms of the Modified BSD License.
|
|
5
|
+
*/
|
|
6
|
+
import { forwardRef } from 'react';
|
|
7
|
+
import { datalayerColors, spatialColors, lovelyColors, matrixColors, } from '../../theme';
|
|
8
|
+
export const THEME_LOGO_COLORS = {
|
|
9
|
+
datalayer: {
|
|
10
|
+
light: {
|
|
11
|
+
primary: datalayerColors.greenBrand,
|
|
12
|
+
secondary: datalayerColors.greenText,
|
|
13
|
+
},
|
|
14
|
+
dark: {
|
|
15
|
+
primary: datalayerColors.greenBright,
|
|
16
|
+
secondary: datalayerColors.greenAccent,
|
|
17
|
+
},
|
|
18
|
+
},
|
|
19
|
+
spatial: {
|
|
20
|
+
light: {
|
|
21
|
+
primary: spatialColors.indigoBrand,
|
|
22
|
+
secondary: spatialColors.indigoText,
|
|
23
|
+
},
|
|
24
|
+
dark: {
|
|
25
|
+
primary: spatialColors.indigoBright,
|
|
26
|
+
secondary: spatialColors.indigoAccent,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
lovely: {
|
|
30
|
+
light: {
|
|
31
|
+
primary: lovelyColors.roseBrand,
|
|
32
|
+
secondary: lovelyColors.roseText,
|
|
33
|
+
},
|
|
34
|
+
dark: {
|
|
35
|
+
primary: lovelyColors.roseBright,
|
|
36
|
+
secondary: lovelyColors.roseAccent,
|
|
37
|
+
},
|
|
38
|
+
},
|
|
39
|
+
matrix: {
|
|
40
|
+
light: {
|
|
41
|
+
primary: matrixColors.greenBrand,
|
|
42
|
+
secondary: matrixColors.greenText,
|
|
43
|
+
},
|
|
44
|
+
dark: {
|
|
45
|
+
primary: matrixColors.greenGlow,
|
|
46
|
+
secondary: matrixColors.greenPhosphor,
|
|
47
|
+
},
|
|
48
|
+
},
|
|
49
|
+
};
|
|
50
|
+
export function getLogoColors(variant = 'datalayer', colorMode = 'light') {
|
|
51
|
+
const mode = colorMode === 'auto' ? 'light' : colorMode;
|
|
52
|
+
return THEME_LOGO_COLORS[variant]?.[mode] ?? THEME_LOGO_COLORS.datalayer.light;
|
|
53
|
+
}
|
|
54
|
+
const BAR_HEIGHT = 25;
|
|
55
|
+
const BARS = [
|
|
56
|
+
{ y: 0, secondaryWidth: 30, primaryWidth: 70 },
|
|
57
|
+
{ y: 37.5, secondaryWidth: 50, primaryWidth: 50 },
|
|
58
|
+
{ y: 75, secondaryWidth: 70, primaryWidth: 30 },
|
|
59
|
+
];
|
|
60
|
+
export const DatalayerLogo = forwardRef(function DatalayerLogo({ size = 16, primaryColor = 'currentColor', secondaryColor = 'currentColor', ...svgProps }, ref) {
|
|
61
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 100 100", width: size, height: size, "aria-hidden": "true", ref: ref, ...svgProps, children: BARS.map(({ y, secondaryWidth, primaryWidth }) => (_jsxs("g", { children: [_jsx("rect", { x: 0, y: y, width: secondaryWidth, height: BAR_HEIGHT, fill: secondaryColor }), _jsx("rect", { x: secondaryWidth, y: y, width: primaryWidth, height: BAR_HEIGHT, fill: primaryColor })] }, y))) }));
|
|
62
|
+
});
|
|
63
|
+
export default DatalayerLogo;
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import { type SVGProps } from 'react';
|
|
2
|
+
import type { ThemeVariant } from '../../theme';
|
|
3
|
+
export interface DatalayerLogoTextProps extends Omit<SVGProps<SVGSVGElement>, 'ref'> {
|
|
4
|
+
size?: number;
|
|
5
|
+
scale?: number;
|
|
6
|
+
colorMode?: 'light' | 'dark' | 'auto';
|
|
7
|
+
variant?: ThemeVariant;
|
|
8
|
+
primaryColor?: string;
|
|
9
|
+
secondaryColor?: string;
|
|
10
|
+
textSizeMultiplier?: number;
|
|
11
|
+
/**
|
|
12
|
+
* When true, render logo mark first then text.
|
|
13
|
+
* When false (default), render text first then logo mark.
|
|
14
|
+
*/
|
|
15
|
+
inverse?: boolean;
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Datalayer icon + wordmark based on official brand proportions.
|
|
19
|
+
* Uses themed colors by default and allows explicit color overrides.
|
|
20
|
+
*/
|
|
21
|
+
export declare const DatalayerLogoText: import("react").ForwardRefExoticComponent<DatalayerLogoTextProps & import("react").RefAttributes<SVGSVGElement>>;
|
|
22
|
+
export default DatalayerLogoText;
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs, Fragment as _Fragment } 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
|
+
import { DatalayerText } from './DatalayerText';
|
|
9
|
+
/**
|
|
10
|
+
* Datalayer icon + wordmark based on official brand proportions.
|
|
11
|
+
* Uses themed colors by default and allows explicit color overrides.
|
|
12
|
+
*/
|
|
13
|
+
export const DatalayerLogoText = forwardRef(function DatalayerLogoText({ size = 25, scale = 1, colorMode = 'light', variant = 'datalayer', primaryColor, secondaryColor,
|
|
14
|
+
// 2.380655 ~= 25 / (0.7 * 15.00187)
|
|
15
|
+
// This makes the wordmark glyph height match the 25-unit icon height.
|
|
16
|
+
textSizeMultiplier = 2.380655, inverse = false, ...svgProps }, ref) {
|
|
17
|
+
const themed = getLogoColors(variant, colorMode);
|
|
18
|
+
const primary = primaryColor ?? themed.primary;
|
|
19
|
+
const secondary = secondaryColor ?? themed.secondary;
|
|
20
|
+
const baseHeight = size;
|
|
21
|
+
const baseWidth = (149.672 / 25) * size;
|
|
22
|
+
const height = baseHeight * scale;
|
|
23
|
+
const width = baseWidth * scale;
|
|
24
|
+
const iconGroup = (_jsxs("g", { transform: "translate(0 0)", children: [_jsx("rect", { x: "0", y: "0", width: "10", height: "6.25", fill: secondary }), _jsx("rect", { x: "10", y: "0", width: "15", height: "6.25", fill: primary }), _jsx("rect", { x: "0", y: "9.375", width: "15", height: "6.25", fill: secondary }), _jsx("rect", { x: "15", y: "9.375", width: "10", height: "6.25", fill: primary }), _jsx("rect", { x: "0", y: "18.75", width: "20", height: "6.25", fill: secondary }), _jsx("rect", { x: "20", y: "18.75", width: "5", height: "6.25", fill: primary })] }));
|
|
25
|
+
const logoFirst = (_jsxs(_Fragment, { children: [iconGroup, _jsx(DatalayerText, { primaryColor: primary, secondaryColor: secondary, sizeMultiplier: textSizeMultiplier, x: 16 })] }));
|
|
26
|
+
const textFirst = (_jsxs(_Fragment, { children: [_jsx(DatalayerText, { primaryColor: primary, secondaryColor: secondary, sizeMultiplier: textSizeMultiplier, x: 0 }), _jsxs("g", { transform: "translate(124.672 0)", children: [_jsx("rect", { x: "0", y: "0", width: "10", height: "6.25", fill: secondary }), _jsx("rect", { x: "10", y: "0", width: "15", height: "6.25", fill: primary }), _jsx("rect", { x: "0", y: "9.375", width: "15", height: "6.25", fill: secondary }), _jsx("rect", { x: "15", y: "9.375", width: "10", height: "6.25", fill: primary }), _jsx("rect", { x: "0", y: "18.75", width: "20", height: "6.25", fill: secondary }), _jsx("rect", { x: "20", y: "18.75", width: "5", height: "6.25", fill: primary })] })] }));
|
|
27
|
+
return (_jsx("svg", { xmlns: "http://www.w3.org/2000/svg", viewBox: "0 0 149.672 25", width: width, height: height, role: "img", "aria-label": "Datalayer", ref: ref, ...svgProps, children: inverse ? logoFirst : textFirst }));
|
|
28
|
+
});
|
|
29
|
+
export default DatalayerLogoText;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import type { SVGProps } from 'react';
|
|
2
|
+
export interface DatalayerTextProps extends Omit<SVGProps<SVGGElement>, 'ref'> {
|
|
3
|
+
primaryColor?: string;
|
|
4
|
+
secondaryColor?: string;
|
|
5
|
+
/** Multiplies the base wordmark size. 1.0 keeps current dimensions. */
|
|
6
|
+
sizeMultiplier?: number;
|
|
7
|
+
/** X translation in the parent SVG coordinate system. */
|
|
8
|
+
x?: number;
|
|
9
|
+
/** Y translation in the parent SVG coordinate system. */
|
|
10
|
+
y?: number;
|
|
11
|
+
}
|
|
12
|
+
/**
|
|
13
|
+
* Official Datalayer wordmark outlines (BebasNeue converted to paths).
|
|
14
|
+
*/
|
|
15
|
+
export declare const DatalayerText: ({ primaryColor, secondaryColor, sizeMultiplier, x, y, ...groupProps }: DatalayerTextProps) => JSX.Element;
|
|
16
|
+
export default DatalayerText;
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
+
/**
|
|
3
|
+
* Official Datalayer wordmark outlines (BebasNeue converted to paths).
|
|
4
|
+
*/
|
|
5
|
+
export const DatalayerText = ({ primaryColor = 'currentColor', secondaryColor = 'currentColor', sizeMultiplier = 1, x = 20, y = -190.15, ...groupProps }) => {
|
|
6
|
+
const baseScale = 0.7;
|
|
7
|
+
const scale = baseScale * sizeMultiplier;
|
|
8
|
+
// Keep the wordmark anchored when scaling so larger multipliers do not
|
|
9
|
+
// move the paths out of the parent SVG viewBox.
|
|
10
|
+
const anchorX = 17.35761;
|
|
11
|
+
const anchorY = 289.499065;
|
|
12
|
+
const translateX = x + anchorX * (baseScale - scale);
|
|
13
|
+
const translateY = y + anchorY * (baseScale - scale);
|
|
14
|
+
return (_jsxs("g", { transform: `matrix(${scale},0,0,${scale},${translateX},${translateY})`, ...groupProps, children: [_jsx("path", { d: "m 17.35761,297 h 3.729038 c 2.357437,0 3.514725,-1.30731 3.514725,-3.70761 v -7.58666 c 0,-2.4003 -1.157288,-3.7076 -3.514725,-3.7076 H 17.35761 Z m 3.686175,-12.85875 c 0.750094,0 1.20015,0.38576 1.20015,1.45733 v 7.80097 c 0,1.07156 -0.450056,1.45733 -1.20015,1.45733 h -1.328737 v -10.71563 z", fill: secondaryColor }), _jsx("path", { d: "M 31.29328,281.99813 H 27.842849 L 25.442549,297 h 2.164556 l 0.407194,-2.72177 h 2.893219 L 31.314711,297 h 2.378869 z m -1.864519,2.65747 h 0.04286 l 1.114425,7.58666 h -2.271713 z", fill: secondaryColor }), _jsx("path", { d: "m 33.2894,284.14125 h 2.464594 V 297 h 2.357438 v -12.85875 h 2.464593 v -2.14312 H 33.2894 Z", fill: secondaryColor }), _jsx("path", { d: "M 46.027265,281.99813 H 42.576834 L 40.176534,297 h 2.164556 l 0.407194,-2.72177 h 2.893219 L 46.048696,297 h 2.378869 z m -1.864519,2.65747 h 0.04286 l 1.114425,7.58666 h -2.271713 z", fill: secondaryColor }), _jsx("path", { d: "m 51.175594,297 h 6.236494 v -2.14312 h -3.879056 v -12.85875 h -2.357438 z", fill: primaryColor }), _jsx("path", { d: "M 63.688095,281.99813 H 60.237664 L 57.837364,297 h 2.164556 l 0.407194,-2.72177 h 2.893219 L 63.709526,297 h 2.378869 z m -1.864519,2.65747 h 0.04286 l 1.114425,7.58666 h -2.271713 z", fill: primaryColor }), _jsx("path", { d: "m 68.326286,297 h 2.357438 v -4.97205 l 2.978944,-10.02982 h -2.250282 l -1.778793,6.83656 h -0.04286 l -1.778794,-6.83656 h -2.464593 l 2.978943,10.02982 z", fill: primaryColor }), _jsx("path", { d: "m 76.973461,284.14125 h 4.071938 v -2.14312 H 74.616024 V 297 h 6.429375 v -2.14312 h -4.071938 v -4.39341 h 3.236119 v -2.14313 h -3.236119 z", fill: primaryColor }), _jsx("path", { d: "m 89.814797,297 c -0.235744,-0.55721 -0.257175,-1.09299 -0.257175,-1.82166 v -2.31457 c 0,-1.56448 -0.385763,-2.67891 -1.564481,-3.17183 v -0.0429 c 1.050131,-0.49292 1.543049,-1.47875 1.543049,-3.0218 v -1.17872 c 0,-2.31458 -1.050131,-3.45043 -3.493293,-3.45043 H 82.485309 V 297 h 2.357438 v -6.10791 h 0.814387 c 1.071563,0 1.54305,0.51435 1.54305,1.90739 v 2.35743 c 0,1.22158 0.08573,1.45733 0.214313,1.84309 z m -3.836194,-12.85875 c 0.835819,0 1.20015,0.47149 1.20015,1.54305 v 1.47876 c 0,1.20015 -0.535781,1.58591 -1.414462,1.58591 h -0.921544 v -4.60772 z", fill: primaryColor })] }));
|
|
15
|
+
};
|
|
16
|
+
export default DatalayerText;
|
package/lib/index.js
CHANGED
package/lib/theme/Palette.d.ts
CHANGED
|
@@ -1,4 +1,8 @@
|
|
|
1
|
-
|
|
2
|
-
export declare const
|
|
3
|
-
|
|
4
|
-
export declare const
|
|
1
|
+
/** @deprecated Use `defaultIndicatorColors.reservation` */
|
|
2
|
+
export declare const RESERVATION_CIRCLE_COLOR: string;
|
|
3
|
+
/** @deprecated Use `defaultIndicatorColors.credits` */
|
|
4
|
+
export declare const CREDITS_CIRCLE_COLOR: string;
|
|
5
|
+
/** @deprecated Use `defaultIndicatorColors.inviteJoined` */
|
|
6
|
+
export declare const JOINED_INVITE_COLOR: string;
|
|
7
|
+
/** @deprecated Use `defaultIndicatorColors.invitePending` */
|
|
8
|
+
export declare const PENDING_INVITE_COLOR: string;
|
package/lib/theme/Palette.js
CHANGED
|
@@ -2,9 +2,16 @@
|
|
|
2
2
|
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
3
|
* Distributed under the terms of the Modified BSD License.
|
|
4
4
|
*/
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
5
|
+
/**
|
|
6
|
+
* @deprecated Use `indicatorColors` / `defaultIndicatorColors` from
|
|
7
|
+
* `@datalayer/primer-addons/lib/theme` instead.
|
|
8
|
+
*/
|
|
9
|
+
import { defaultIndicatorColors } from './colors/indicatorColors';
|
|
10
|
+
/** @deprecated Use `defaultIndicatorColors.reservation` */
|
|
11
|
+
export const RESERVATION_CIRCLE_COLOR = defaultIndicatorColors.reservation;
|
|
12
|
+
/** @deprecated Use `defaultIndicatorColors.credits` */
|
|
13
|
+
export const CREDITS_CIRCLE_COLOR = defaultIndicatorColors.credits;
|
|
14
|
+
/** @deprecated Use `defaultIndicatorColors.inviteJoined` */
|
|
15
|
+
export const JOINED_INVITE_COLOR = defaultIndicatorColors.inviteJoined;
|
|
16
|
+
/** @deprecated Use `defaultIndicatorColors.invitePending` */
|
|
17
|
+
export const PENDING_INVITE_COLOR = defaultIndicatorColors.invitePending;
|
|
@@ -2,3 +2,4 @@ export { datalayerColors } from './datalayerColors';
|
|
|
2
2
|
export { spatialColors } from './spatialColors';
|
|
3
3
|
export { lovelyColors } from './lovelyColors';
|
|
4
4
|
export { matrixColors } from './matrixColors';
|
|
5
|
+
export { indicatorColors, defaultIndicatorColors, type IndicatorColors, } from './indicatorColors';
|
|
@@ -6,3 +6,4 @@ export { datalayerColors } from './datalayerColors';
|
|
|
6
6
|
export { spatialColors } from './spatialColors';
|
|
7
7
|
export { lovelyColors } from './lovelyColors';
|
|
8
8
|
export { matrixColors } from './matrixColors';
|
|
9
|
+
export { indicatorColors, defaultIndicatorColors, } from './indicatorColors';
|
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Indicator Color Palette — Semantic status colours for indicators,
|
|
3
|
+
* badges, progress rings, and status dots.
|
|
4
|
+
*
|
|
5
|
+
* Each theme provides its own `IndicatorColors` so consumers pick up
|
|
6
|
+
* the correct tones automatically. The palette contains both
|
|
7
|
+
* **status colours** (success, warning, danger …) and
|
|
8
|
+
* **domain-specific** colours (reservation, credits, invite …).
|
|
9
|
+
*
|
|
10
|
+
* Usage:
|
|
11
|
+
* ```ts
|
|
12
|
+
* import { indicatorColors } from '@datalayer/primer-addons/lib/theme';
|
|
13
|
+
* const colors = indicatorColors.datalayer; // or .spatial, .lovely, .matrix
|
|
14
|
+
* <Box sx={{ bg: colors.success }} />
|
|
15
|
+
* ```
|
|
16
|
+
*/
|
|
17
|
+
export interface IndicatorColors {
|
|
18
|
+
/** Neutral / default — nothing notable */
|
|
19
|
+
neutral: string;
|
|
20
|
+
/** Muted — defined but inactive */
|
|
21
|
+
muted: string;
|
|
22
|
+
/** In-progress / loading */
|
|
23
|
+
pending: string;
|
|
24
|
+
/** Warning / attention-needed */
|
|
25
|
+
warning: string;
|
|
26
|
+
/** Error / failure */
|
|
27
|
+
danger: string;
|
|
28
|
+
/** Healthy / success */
|
|
29
|
+
success: string;
|
|
30
|
+
/** Informational / accent */
|
|
31
|
+
info: string;
|
|
32
|
+
/** Reservation indicator (e.g. usage rings) */
|
|
33
|
+
reservation: string;
|
|
34
|
+
/** Credits indicator (e.g. usage rings) */
|
|
35
|
+
credits: string;
|
|
36
|
+
/** Accepted / joined invite */
|
|
37
|
+
inviteJoined: string;
|
|
38
|
+
/** Pending invite */
|
|
39
|
+
invitePending: string;
|
|
40
|
+
}
|
|
41
|
+
export declare const indicatorColors: {
|
|
42
|
+
readonly datalayer: IndicatorColors;
|
|
43
|
+
readonly spatial: IndicatorColors;
|
|
44
|
+
readonly lovely: IndicatorColors;
|
|
45
|
+
readonly matrix: IndicatorColors;
|
|
46
|
+
};
|
|
47
|
+
/**
|
|
48
|
+
* Convenience: default indicator palette (Datalayer theme).
|
|
49
|
+
*/
|
|
50
|
+
export declare const defaultIndicatorColors: IndicatorColors;
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
/*
|
|
2
|
+
* Copyright (c) 2023-2025 Datalayer, Inc.
|
|
3
|
+
* Distributed under the terms of the Modified BSD License.
|
|
4
|
+
*/
|
|
5
|
+
/* ── Per-theme palettes ────────────────────────────────── */
|
|
6
|
+
const datalayer = {
|
|
7
|
+
neutral: '#656D76',
|
|
8
|
+
muted: '#8b949e',
|
|
9
|
+
pending: '#d29922',
|
|
10
|
+
warning: '#d29922',
|
|
11
|
+
danger: '#d1242f',
|
|
12
|
+
success: '#16A085',
|
|
13
|
+
info: '#0969da',
|
|
14
|
+
reservation: '#656D76',
|
|
15
|
+
credits: '#16A085',
|
|
16
|
+
inviteJoined: '#0969da',
|
|
17
|
+
invitePending: '#cfd3d7',
|
|
18
|
+
};
|
|
19
|
+
const spatial = {
|
|
20
|
+
neutral: '#8892B0',
|
|
21
|
+
muted: '#6B7394',
|
|
22
|
+
pending: '#E2B340',
|
|
23
|
+
warning: '#E2B340',
|
|
24
|
+
danger: '#FF5252',
|
|
25
|
+
success: '#4F46E5',
|
|
26
|
+
info: '#448AFF',
|
|
27
|
+
reservation: '#8892B0',
|
|
28
|
+
credits: '#4F46E5',
|
|
29
|
+
inviteJoined: '#448AFF',
|
|
30
|
+
invitePending: '#6B7394',
|
|
31
|
+
};
|
|
32
|
+
const lovely = {
|
|
33
|
+
neutral: '#9D7A8F',
|
|
34
|
+
muted: '#C9A2B8',
|
|
35
|
+
pending: '#FFD740',
|
|
36
|
+
warning: '#FFD740',
|
|
37
|
+
danger: '#FF1744',
|
|
38
|
+
success: '#DB2777',
|
|
39
|
+
info: '#536DFE',
|
|
40
|
+
reservation: '#9D7A8F',
|
|
41
|
+
credits: '#DB2777',
|
|
42
|
+
inviteJoined: '#536DFE',
|
|
43
|
+
invitePending: '#C9A2B8',
|
|
44
|
+
};
|
|
45
|
+
const matrix = {
|
|
46
|
+
neutral: '#5A6E3A',
|
|
47
|
+
muted: '#7A9A50',
|
|
48
|
+
pending: '#FFD600',
|
|
49
|
+
warning: '#FFD600',
|
|
50
|
+
danger: '#FF3D00',
|
|
51
|
+
success: '#00C853',
|
|
52
|
+
info: '#00E676',
|
|
53
|
+
reservation: '#5A6E3A',
|
|
54
|
+
credits: '#00C853',
|
|
55
|
+
inviteJoined: '#00E676',
|
|
56
|
+
invitePending: '#7A9A50',
|
|
57
|
+
};
|
|
58
|
+
/* ── Public map ────────────────────────────────────────── */
|
|
59
|
+
export const indicatorColors = {
|
|
60
|
+
datalayer,
|
|
61
|
+
spatial,
|
|
62
|
+
lovely,
|
|
63
|
+
matrix,
|
|
64
|
+
};
|
|
65
|
+
/**
|
|
66
|
+
* Convenience: default indicator palette (Datalayer theme).
|
|
67
|
+
*/
|
|
68
|
+
export const defaultIndicatorColors = datalayer;
|
package/lib/theme/index.d.ts
CHANGED
package/lib/theme/index.js
CHANGED
package/lib/utils/Portals.d.ts
CHANGED
|
@@ -4,6 +4,10 @@ type Colormode = 'light' | 'dark' | 'auto';
|
|
|
4
4
|
/**
|
|
5
5
|
* Ensure we define a root for Primer portal root.
|
|
6
6
|
*
|
|
7
|
+
* Creates a dedicated `<div>` appended to `<body>` with a high z-index
|
|
8
|
+
* so that portaled content (overlays, tooltips, action menus) renders
|
|
9
|
+
* above other positioned UI elements such as chat panels.
|
|
10
|
+
*
|
|
7
11
|
* @see https://github.com/primer/react/blob/main/packages/react/src/Portal/Portal.tsx#L23
|
|
8
12
|
* @see https://github.com/primer/react/blob/030fe020b48b7f12c2994c6614e5d4191fe764ee/src/Portal/Portal.tsx#L33
|
|
9
13
|
*/
|
package/lib/utils/Portals.js
CHANGED
|
@@ -19,21 +19,73 @@ const resolveColormode = (colormode) => {
|
|
|
19
19
|
}
|
|
20
20
|
return colormode;
|
|
21
21
|
};
|
|
22
|
+
/**
|
|
23
|
+
* Infer current app color mode from existing DOM theme markers.
|
|
24
|
+
*
|
|
25
|
+
* This avoids accidentally forcing light mode when callers invoke
|
|
26
|
+
* setupPrimerPortals() without passing an explicit mode.
|
|
27
|
+
*/
|
|
28
|
+
const inferCurrentColormode = () => {
|
|
29
|
+
const portalRoot = document.getElementById(PRIMER_PORTAL_ROOT_ID);
|
|
30
|
+
const portalMode = portalRoot?.getAttribute('data-color-mode');
|
|
31
|
+
if (portalMode === 'light' || portalMode === 'dark' || portalMode === 'auto') {
|
|
32
|
+
return portalMode;
|
|
33
|
+
}
|
|
34
|
+
const htmlMode = document.documentElement.getAttribute('data-color-mode');
|
|
35
|
+
if (htmlMode === 'light' || htmlMode === 'dark' || htmlMode === 'auto') {
|
|
36
|
+
return htmlMode;
|
|
37
|
+
}
|
|
38
|
+
const bodyMode = document.body.getAttribute('data-color-mode');
|
|
39
|
+
if (bodyMode === 'light' || bodyMode === 'dark' || bodyMode === 'auto') {
|
|
40
|
+
return bodyMode;
|
|
41
|
+
}
|
|
42
|
+
return 'auto';
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* Infer Primer light/dark theme names from existing DOM markers.
|
|
46
|
+
*/
|
|
47
|
+
const inferCurrentThemeNames = () => {
|
|
48
|
+
const portalRoot = document.getElementById(PRIMER_PORTAL_ROOT_ID);
|
|
49
|
+
const lightTheme = portalRoot?.getAttribute('data-light-theme') ||
|
|
50
|
+
document.documentElement.getAttribute('data-light-theme') ||
|
|
51
|
+
document.body.getAttribute('data-light-theme') ||
|
|
52
|
+
'light';
|
|
53
|
+
const darkTheme = portalRoot?.getAttribute('data-dark-theme') ||
|
|
54
|
+
document.documentElement.getAttribute('data-dark-theme') ||
|
|
55
|
+
document.body.getAttribute('data-dark-theme') ||
|
|
56
|
+
'dark';
|
|
57
|
+
return { lightTheme, darkTheme };
|
|
58
|
+
};
|
|
22
59
|
/**
|
|
23
60
|
* Ensure we define a root for Primer portal root.
|
|
24
61
|
*
|
|
62
|
+
* Creates a dedicated `<div>` appended to `<body>` with a high z-index
|
|
63
|
+
* so that portaled content (overlays, tooltips, action menus) renders
|
|
64
|
+
* above other positioned UI elements such as chat panels.
|
|
65
|
+
*
|
|
25
66
|
* @see https://github.com/primer/react/blob/main/packages/react/src/Portal/Portal.tsx#L23
|
|
26
67
|
* @see https://github.com/primer/react/blob/030fe020b48b7f12c2994c6614e5d4191fe764ee/src/Portal/Portal.tsx#L33
|
|
27
68
|
*/
|
|
28
|
-
export const setupPrimerPortals = (colormode
|
|
29
|
-
const
|
|
30
|
-
const
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
69
|
+
export const setupPrimerPortals = (colormode) => {
|
|
70
|
+
const effectiveColormode = colormode ?? inferCurrentColormode();
|
|
71
|
+
const resolved = resolveColormode(effectiveColormode);
|
|
72
|
+
const { lightTheme, darkTheme } = inferCurrentThemeNames();
|
|
73
|
+
// Create or reuse a dedicated portal root div.
|
|
74
|
+
let portalRoot = document.getElementById(PRIMER_PORTAL_ROOT_ID);
|
|
75
|
+
if (!portalRoot) {
|
|
76
|
+
portalRoot = document.createElement('div');
|
|
77
|
+
portalRoot.id = PRIMER_PORTAL_ROOT_ID;
|
|
78
|
+
document.body.appendChild(portalRoot);
|
|
79
|
+
}
|
|
80
|
+
// Primer theme attributes — required for proper theming inside portals.
|
|
81
|
+
portalRoot.dataset['portalRoot'] = 'true';
|
|
82
|
+
portalRoot.dataset['colorMode'] = resolved;
|
|
83
|
+
portalRoot.dataset['lightTheme'] = lightTheme;
|
|
84
|
+
portalRoot.dataset['darkTheme'] = darkTheme;
|
|
85
|
+
// High z-index so overlays render above positioned UI (e.g. chat z-index 1001).
|
|
86
|
+
portalRoot.style.position = 'relative';
|
|
87
|
+
portalRoot.style.zIndex = '9999';
|
|
88
|
+
registerPortalRoot(portalRoot);
|
|
37
89
|
};
|
|
38
90
|
/* ─── camelCase → kebab-case ─────────────────────────────────────────── */
|
|
39
91
|
const camelToKebab = (s) => s.replace(/[A-Z]/g, (m) => `-${m.toLowerCase()}`);
|
package/package.json
CHANGED
|
@@ -1,31 +0,0 @@
|
|
|
1
|
-
import React from 'react';
|
|
2
|
-
import { ArgTypes } from '@storybook/react';
|
|
3
|
-
import { Icon } from '@primer/octicons-react';
|
|
4
|
-
type StoryContext = Record<string, unknown> & {
|
|
5
|
-
globals: {
|
|
6
|
-
colorScheme: string;
|
|
7
|
-
};
|
|
8
|
-
parameters: Record<string, unknown>;
|
|
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;
|
|
12
|
-
export declare const toolbarTypes: {
|
|
13
|
-
colorScheme: {
|
|
14
|
-
name: string;
|
|
15
|
-
description: string;
|
|
16
|
-
defaultValue: string;
|
|
17
|
-
toolbar: {
|
|
18
|
-
icon: string;
|
|
19
|
-
items: string[];
|
|
20
|
-
title: string;
|
|
21
|
-
};
|
|
22
|
-
};
|
|
23
|
-
};
|
|
24
|
-
export declare const inputWrapperArgTypes: ArgTypes;
|
|
25
|
-
export declare const getTextInputArgTypes: (category?: string) => Record<string, unknown>;
|
|
26
|
-
export declare const OcticonArgType: (iconList: Icon[]) => {
|
|
27
|
-
options: string[];
|
|
28
|
-
control: string;
|
|
29
|
-
mapping: Record<string, Icon>;
|
|
30
|
-
};
|
|
31
|
-
export {};
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
2
|
-
import { createGlobalStyle } from 'styled-components';
|
|
3
|
-
import { ThemeProvider, themeGet, BaseStyles, theme } from '@primer/react';
|
|
4
|
-
// set global theme styles for each story
|
|
5
|
-
const GlobalStyle = createGlobalStyle `
|
|
6
|
-
body,
|
|
7
|
-
.docs-story {
|
|
8
|
-
background-color: ${themeGet('colors.canvas.default')};
|
|
9
|
-
color: ${themeGet('colors.fg.default')};
|
|
10
|
-
}
|
|
11
|
-
`;
|
|
12
|
-
export const colormodeFromScheme = (colorScheme) => {
|
|
13
|
-
return colorScheme.startsWith('light') ? 'light' : 'dark';
|
|
14
|
-
};
|
|
15
|
-
export const withThemeProvider = (Story, context) => {
|
|
16
|
-
// used for testing ThemeProvider.stories.tsx
|
|
17
|
-
if (context.parameters.disableThemeDecorator)
|
|
18
|
-
return Story(context);
|
|
19
|
-
const { colorScheme } = context.globals;
|
|
20
|
-
return (_jsxs(ThemeProvider, { theme: theme, colorMode: colormodeFromScheme(colorScheme), dayScheme: colorScheme, nightScheme: colorScheme, children: [colorScheme.startsWith('light') ? (_jsx(GlobalStyle, { "$lightTheme": true })) : (_jsx(GlobalStyle, {})), _jsx(BaseStyles, { children: _jsx("div", { id: "html-addon-root", children: Story(context) }) })] }));
|
|
21
|
-
};
|
|
22
|
-
export const toolbarTypes = {
|
|
23
|
-
colorScheme: {
|
|
24
|
-
name: 'Color Scheme',
|
|
25
|
-
description: 'Switch color scheme',
|
|
26
|
-
defaultValue: 'light',
|
|
27
|
-
toolbar: {
|
|
28
|
-
icon: 'photo',
|
|
29
|
-
items: [...Object.keys(theme.colorSchemes)],
|
|
30
|
-
title: 'Color Scheme',
|
|
31
|
-
},
|
|
32
|
-
},
|
|
33
|
-
};
|
|
34
|
-
export const inputWrapperArgTypes = {
|
|
35
|
-
block: {
|
|
36
|
-
defaultValue: false,
|
|
37
|
-
control: 'boolean',
|
|
38
|
-
},
|
|
39
|
-
contrast: {
|
|
40
|
-
defaultValue: false,
|
|
41
|
-
control: 'boolean',
|
|
42
|
-
},
|
|
43
|
-
disabled: {
|
|
44
|
-
defaultValue: false,
|
|
45
|
-
control: 'boolean',
|
|
46
|
-
},
|
|
47
|
-
placeholder: {
|
|
48
|
-
defaultValue: '',
|
|
49
|
-
control: 'text',
|
|
50
|
-
},
|
|
51
|
-
size: {
|
|
52
|
-
name: 'size (input)', // TODO: remove '(input)'
|
|
53
|
-
defaultValue: 'medium',
|
|
54
|
-
options: ['small', 'medium', 'large'],
|
|
55
|
-
control: 'radio',
|
|
56
|
-
},
|
|
57
|
-
validationStatus: {
|
|
58
|
-
defaultValue: undefined,
|
|
59
|
-
options: ['error', 'success', undefined],
|
|
60
|
-
control: 'radio',
|
|
61
|
-
},
|
|
62
|
-
};
|
|
63
|
-
const textInputArgTypesUnsorted = {
|
|
64
|
-
...inputWrapperArgTypes,
|
|
65
|
-
loading: {
|
|
66
|
-
defaultValue: false,
|
|
67
|
-
control: 'boolean',
|
|
68
|
-
},
|
|
69
|
-
loaderPosition: {
|
|
70
|
-
defaultValue: 'auto',
|
|
71
|
-
options: ['auto', 'leading', 'trailing'],
|
|
72
|
-
control: 'radio',
|
|
73
|
-
},
|
|
74
|
-
monospace: {
|
|
75
|
-
defaultValue: false,
|
|
76
|
-
control: 'boolean',
|
|
77
|
-
},
|
|
78
|
-
};
|
|
79
|
-
// Alphabetize and optionally categorize the props
|
|
80
|
-
export const getTextInputArgTypes = (category) => Object.keys(textInputArgTypesUnsorted)
|
|
81
|
-
.sort()
|
|
82
|
-
.reduce((obj, key) => {
|
|
83
|
-
obj[key] = category
|
|
84
|
-
? {
|
|
85
|
-
// have to do weird type casting so we can spread the object
|
|
86
|
-
...textInputArgTypesUnsorted[key],
|
|
87
|
-
table: {
|
|
88
|
-
category,
|
|
89
|
-
},
|
|
90
|
-
}
|
|
91
|
-
: textInputArgTypesUnsorted[key];
|
|
92
|
-
return obj;
|
|
93
|
-
}, {});
|
|
94
|
-
// Use this function for icon options in the controls. Desired icons are passed in as an array of Octicons
|
|
95
|
-
export const OcticonArgType = (iconList) => {
|
|
96
|
-
const icons = iconList.reduce((obj, icon) => {
|
|
97
|
-
obj[icon.displayName || 'Icon'] = icon;
|
|
98
|
-
return obj;
|
|
99
|
-
}, {});
|
|
100
|
-
return {
|
|
101
|
-
options: Object.keys(icons),
|
|
102
|
-
control: 'select',
|
|
103
|
-
mapping: icons,
|
|
104
|
-
};
|
|
105
|
-
};
|
|
@@ -1,17 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* ThemeSwitcher – Themed colour circles + light/dark/auto segmented control.
|
|
3
|
-
*
|
|
4
|
-
* The component requires an external Zustand `ThemeState` store (created
|
|
5
|
-
* with `createThemeStore`). Consumers pass the `useStore` hook as a prop
|
|
6
|
-
* so the switcher is completely decoupled from any specific app's store.
|
|
7
|
-
*
|
|
8
|
-
* @module theme/ThemeSwitcher
|
|
9
|
-
*/
|
|
10
|
-
import React from 'react';
|
|
11
|
-
import type { ThemeState } from './useThemeStore';
|
|
12
|
-
import type { UseBoundStore, StoreApi } from 'zustand';
|
|
13
|
-
export interface ThemeSwitcherProps {
|
|
14
|
-
/** A Zustand store hook created via `createThemeStore()`. */
|
|
15
|
-
useStore: UseBoundStore<StoreApi<ThemeState>>;
|
|
16
|
-
}
|
|
17
|
-
export declare const ThemeSwitcher: React.FC<ThemeSwitcherProps>;
|