@djangocfg/ui-core 2.1.157 → 2.1.159

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 CHANGED
@@ -1,6 +1,6 @@
1
1
  # @djangocfg/ui-core
2
2
 
3
- Pure React UI library with 61 components built on Radix UI + Tailwind CSS v4.
3
+ Pure React UI library with 60+ components built on Radix UI + Tailwind CSS v4.
4
4
 
5
5
  **No Next.js dependencies** — works with Electron, Vite, CRA, and any React environment.
6
6
 
@@ -19,9 +19,10 @@ pnpm add @djangocfg/ui-core
19
19
  | Package | Use Case |
20
20
  |---------|----------|
21
21
  | `@djangocfg/ui-core` | Electron, Vite, CRA, any React app |
22
+ | `@djangocfg/ui-tools` | Heavy tools with lazy loading |
22
23
  | `@djangocfg/ui-nextjs` | Next.js apps (extends ui-core) |
23
24
 
24
- ## Components (60)
25
+ ## Components (60+)
25
26
 
26
27
  ### Forms (17)
27
28
  `Label` `Button` `ButtonLink` `Input` `Checkbox` `RadioGroup` `Select` `Textarea` `Switch` `Slider` `Combobox` `MultiSelect` `CountrySelect` `InputOTP` `PhoneInput` `Form` `Field`
@@ -62,6 +63,46 @@ pnpm add @djangocfg/ui-core
62
63
  | `useBrowserDetect` | Browser detection (Chrome, Safari, in-app browsers, etc.) |
63
64
  | `useDeviceDetect` | Device detection (mobile, tablet, desktop, OS, etc.) |
64
65
 
66
+ ## Theme Palette Hooks
67
+
68
+ Hooks for accessing theme colors from CSS variables (useful for charts, diagrams, etc.):
69
+
70
+ | Hook | Description |
71
+ |------|-------------|
72
+ | `useThemePalette()` | Full color palette from CSS variables |
73
+ | `useStylePresets()` | Pre-built style configs for diagrams |
74
+ | `useBoxColors()` | Colors for boxes/containers |
75
+
76
+ ```tsx
77
+ import { useStylePresets, useBoxColors } from '@djangocfg/ui-core/styles/palette';
78
+
79
+ function MyChart() {
80
+ const presets = useStylePresets();
81
+ // presets.success = { fill: '#22c55e', stroke: '#16a34a', color: '#ffffff' }
82
+ // presets.warning = { fill: '#f59e0b', stroke: '#d97706', color: '#000000' }
83
+ // presets.danger = { fill: '#ef4444', stroke: '#dc2626', color: '#ffffff' }
84
+ // presets.info = { fill: '#3b82f6', stroke: '#2563eb', color: '#ffffff' }
85
+ // presets.primary = { fill: '#...', stroke: '#...', color: '#...' }
86
+
87
+ const boxes = useBoxColors();
88
+ // boxes.primary = 'rgba(59, 130, 246, 0.2)'
89
+ // boxes.success = 'rgba(34, 197, 94, 0.2)'
90
+ // etc.
91
+
92
+ return <Chart colors={[presets.success.fill, presets.warning.fill]} />;
93
+ }
94
+ ```
95
+
96
+ ### Color Utilities
97
+
98
+ ```tsx
99
+ import { hslToHex, hslToRgbString, hslToRgba } from '@djangocfg/ui-core/styles/palette';
100
+
101
+ hslToHex('217 91% 60%'); // '#3b82f6'
102
+ hslToRgbString('217 91% 60%'); // 'rgb(59, 130, 246)'
103
+ hslToRgba('217 91% 60%', 0.5); // 'rgba(59, 130, 246, 0.5)'
104
+ ```
105
+
65
106
  ## Dialog Service
66
107
 
67
108
  Universal dialog service to replace native `window.alert`, `window.confirm`, `window.prompt` with beautiful shadcn dialogs.
@@ -153,6 +194,7 @@ import '@djangocfg/ui-core/styles/globals';
153
194
  | `@djangocfg/ui-core/lib` | Utilities (cn, etc.) |
154
195
  | `@djangocfg/ui-core/lib/dialog-service` | Dialog service |
155
196
  | `@djangocfg/ui-core/styles` | CSS |
197
+ | `@djangocfg/ui-core/styles/palette` | Theme palette hooks & utilities |
156
198
 
157
199
  ## What's NOT included (use ui-nextjs)
158
200
 
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@djangocfg/ui-core",
3
- "version": "2.1.157",
3
+ "version": "2.1.159",
4
4
  "description": "Pure React UI component library without Next.js dependencies - for Electron, Vite, CRA apps",
5
5
  "keywords": [
6
6
  "ui-components",
@@ -57,7 +57,12 @@
57
57
  "./styles/globals": "./src/styles/globals.css",
58
58
  "./styles/theme": "./src/styles/theme.css",
59
59
  "./styles/base": "./src/styles/base.css",
60
- "./styles/utilities": "./src/styles/utilities.css"
60
+ "./styles/utilities": "./src/styles/utilities.css",
61
+ "./styles/palette": {
62
+ "types": "./src/styles/palette/index.ts",
63
+ "import": "./src/styles/palette/index.ts",
64
+ "require": "./src/styles/palette/index.ts"
65
+ }
61
66
  },
62
67
  "files": [
63
68
  "dist",
@@ -71,7 +76,7 @@
71
76
  "playground": "playground dev"
72
77
  },
73
78
  "peerDependencies": {
74
- "@djangocfg/i18n": "^2.1.157",
79
+ "@djangocfg/i18n": "^2.1.159",
75
80
  "react-device-detect": "^2.2.3",
76
81
  "consola": "^3.4.2",
77
82
  "lucide-react": "^0.545.0",
@@ -133,9 +138,9 @@
133
138
  "vaul": "1.1.2"
134
139
  },
135
140
  "devDependencies": {
136
- "@djangocfg/i18n": "^2.1.157",
141
+ "@djangocfg/i18n": "^2.1.159",
137
142
  "@djangocfg/playground": "workspace:*",
138
- "@djangocfg/typescript-config": "^2.1.157",
143
+ "@djangocfg/typescript-config": "^2.1.159",
139
144
  "@types/node": "^24.7.2",
140
145
  "@types/react": "^19.1.0",
141
146
  "@types/react-dom": "^19.1.0",
@@ -0,0 +1,55 @@
1
+ /**
2
+ * Theme Palette
3
+ *
4
+ * Hooks and utilities for accessing theme colors programmatically.
5
+ * Colors are read directly from CSS variables, ensuring consistency
6
+ * with the theme system.
7
+ *
8
+ * @example
9
+ * ```tsx
10
+ * import {
11
+ * useThemePalette,
12
+ * useStylePresets,
13
+ * useBoxColors
14
+ * } from '@djangocfg/ui-core/styles/palette';
15
+ *
16
+ * function MyComponent() {
17
+ * const palette = useThemePalette();
18
+ * const presets = useStylePresets();
19
+ * const boxes = useBoxColors();
20
+ *
21
+ * // Use in canvas
22
+ * ctx.fillStyle = palette.primary;
23
+ *
24
+ * // Use in Mermaid FlowDiagram
25
+ * flow.style.define('success', presets.success);
26
+ *
27
+ * // Use in Mermaid SequenceDiagram rect
28
+ * rect(boxes.primary, () => { ... });
29
+ * }
30
+ * ```
31
+ *
32
+ * @module ui-core/styles/palette
33
+ */
34
+
35
+ // Types
36
+ export type {
37
+ ThemePalette,
38
+ StyleColors,
39
+ StylePresets,
40
+ BoxColors,
41
+ } from './types';
42
+
43
+ // Hooks
44
+ export {
45
+ useThemePalette,
46
+ useStylePresets,
47
+ useBoxColors,
48
+ } from './useThemePalette';
49
+
50
+ // Utils (for advanced use)
51
+ export {
52
+ hslToHex,
53
+ hslToRgbString,
54
+ hslToRgba,
55
+ } from './utils';
@@ -0,0 +1,90 @@
1
+ /**
2
+ * Theme Palette Types
3
+ * @module ui-core/styles/palette/types
4
+ */
5
+
6
+ /**
7
+ * Complete semantic color palette for programmatic use
8
+ * (Canvas, SVG, Mermaid diagrams, etc.)
9
+ */
10
+ export interface ThemePalette {
11
+ // Base Colors
12
+ background: string;
13
+ foreground: string;
14
+ card: string;
15
+ cardForeground: string;
16
+ popover: string;
17
+ popoverForeground: string;
18
+ muted: string;
19
+ mutedForeground: string;
20
+ border: string;
21
+ input: string;
22
+ ring: string;
23
+
24
+ // Semantic Colors
25
+ primary: string;
26
+ primaryForeground: string;
27
+ secondary: string;
28
+ secondaryForeground: string;
29
+ accent: string;
30
+ accentForeground: string;
31
+ destructive: string;
32
+ destructiveForeground: string;
33
+
34
+ // Status Colors
35
+ success: string;
36
+ successForeground: string;
37
+ warning: string;
38
+ warningForeground: string;
39
+ info: string;
40
+ infoForeground: string;
41
+
42
+ // Chart Colors
43
+ chart1: string;
44
+ chart2: string;
45
+ chart3: string;
46
+ chart4: string;
47
+ chart5: string;
48
+ }
49
+
50
+ /**
51
+ * Style colors for elements (fill, stroke, text)
52
+ */
53
+ export interface StyleColors {
54
+ fill: string;
55
+ stroke: string;
56
+ color: string;
57
+ }
58
+
59
+ /**
60
+ * Style presets for common UI patterns
61
+ */
62
+ export interface StylePresets {
63
+ primary: StyleColors;
64
+ secondary: StyleColors;
65
+ success: StyleColors;
66
+ danger: StyleColors;
67
+ warning: StyleColors;
68
+ info: StyleColors;
69
+ muted: StyleColors;
70
+ card: StyleColors;
71
+ accent: StyleColors;
72
+ chart1: StyleColors;
73
+ chart2: StyleColors;
74
+ chart3: StyleColors;
75
+ chart4: StyleColors;
76
+ chart5: StyleColors;
77
+ }
78
+
79
+ /**
80
+ * Background colors for boxes/regions
81
+ * (e.g., Mermaid rect blocks, highlighted sections)
82
+ */
83
+ export interface BoxColors {
84
+ primary: string;
85
+ success: string;
86
+ warning: string;
87
+ danger: string;
88
+ info: string;
89
+ muted: string;
90
+ }
@@ -0,0 +1,233 @@
1
+ 'use client';
2
+
3
+ /**
4
+ * Hook to get theme colors from CSS variables
5
+ *
6
+ * Reads colors directly from document, converting HSL to hex for compatibility
7
+ * with Canvas, SVG, Mermaid, and other tools that don't support CSS variables.
8
+ *
9
+ * @module ui-core/styles/palette/useThemePalette
10
+ */
11
+
12
+ import { useMemo } from 'react';
13
+ import { useResolvedTheme } from '../../hooks/useResolvedTheme';
14
+ import { hslToHex, hslToRgba } from './utils';
15
+ import type { ThemePalette, StylePresets, BoxColors } from './types';
16
+
17
+ /**
18
+ * Read a CSS variable from document and convert to hex
19
+ */
20
+ function getCssColorAsHex(varName: string): string {
21
+ if (typeof document === 'undefined') return '#000000';
22
+
23
+ const style = getComputedStyle(document.documentElement);
24
+ const value = style.getPropertyValue(`--${varName}`).trim();
25
+
26
+ if (!value) return '#000000';
27
+
28
+ // If already hex, return as-is
29
+ if (value.startsWith('#')) return value;
30
+
31
+ // Convert HSL to hex
32
+ return hslToHex(value);
33
+ }
34
+
35
+ /**
36
+ * Read a CSS variable from document and convert to rgba with opacity
37
+ */
38
+ function getCssColorAsRgba(varName: string, alpha: number): string {
39
+ if (typeof document === 'undefined') return `rgba(0, 0, 0, ${alpha})`;
40
+
41
+ const style = getComputedStyle(document.documentElement);
42
+ const value = style.getPropertyValue(`--${varName}`).trim();
43
+
44
+ if (!value) return `rgba(0, 0, 0, ${alpha})`;
45
+
46
+ return hslToRgba(value, alpha);
47
+ }
48
+
49
+ /**
50
+ * Hook to get the full theme palette from CSS variables
51
+ *
52
+ * @example
53
+ * ```tsx
54
+ * function MyComponent() {
55
+ * const palette = useThemePalette();
56
+ *
57
+ * // Use in canvas
58
+ * ctx.fillStyle = palette.primary;
59
+ *
60
+ * // Use in inline styles
61
+ * <div style={{ backgroundColor: palette.success }} />
62
+ * }
63
+ * ```
64
+ */
65
+ export function useThemePalette(): ThemePalette {
66
+ const theme = useResolvedTheme();
67
+
68
+ return useMemo(() => {
69
+ return {
70
+ // Base colors
71
+ background: getCssColorAsHex('background'),
72
+ foreground: getCssColorAsHex('foreground'),
73
+ card: getCssColorAsHex('card'),
74
+ cardForeground: getCssColorAsHex('card-foreground'),
75
+ popover: getCssColorAsHex('popover'),
76
+ popoverForeground: getCssColorAsHex('popover-foreground'),
77
+ muted: getCssColorAsHex('muted'),
78
+ mutedForeground: getCssColorAsHex('muted-foreground'),
79
+ border: getCssColorAsHex('border'),
80
+ input: getCssColorAsHex('input'),
81
+ ring: getCssColorAsHex('ring'),
82
+
83
+ // Semantic colors
84
+ primary: getCssColorAsHex('primary'),
85
+ primaryForeground: getCssColorAsHex('primary-foreground'),
86
+ secondary: getCssColorAsHex('secondary'),
87
+ secondaryForeground: getCssColorAsHex('secondary-foreground'),
88
+ accent: getCssColorAsHex('accent'),
89
+ accentForeground: getCssColorAsHex('accent-foreground'),
90
+ destructive: getCssColorAsHex('destructive'),
91
+ destructiveForeground: getCssColorAsHex('destructive-foreground'),
92
+
93
+ // Status colors (mapped from chart colors)
94
+ success: getCssColorAsHex('chart-2'), // Green
95
+ successForeground: '#ffffff',
96
+ warning: getCssColorAsHex('chart-4'), // Orange
97
+ warningForeground: '#ffffff',
98
+ info: getCssColorAsHex('chart-1'), // Blue (same as primary)
99
+ infoForeground: '#ffffff',
100
+
101
+ // Chart colors
102
+ chart1: getCssColorAsHex('chart-1'),
103
+ chart2: getCssColorAsHex('chart-2'),
104
+ chart3: getCssColorAsHex('chart-3'),
105
+ chart4: getCssColorAsHex('chart-4'),
106
+ chart5: getCssColorAsHex('chart-5'),
107
+ };
108
+ }, [theme]);
109
+ }
110
+
111
+ /**
112
+ * Hook to get style presets for elements (nodes, boxes, etc.)
113
+ *
114
+ * @example
115
+ * ```tsx
116
+ * function MyDiagram() {
117
+ * const presets = useStylePresets();
118
+ *
119
+ * // Use in Mermaid FlowDiagram
120
+ * flow.style.define('success', presets.success);
121
+ * flow.style.define('primary', presets.primary);
122
+ * }
123
+ * ```
124
+ */
125
+ export function useStylePresets(): StylePresets {
126
+ const palette = useThemePalette();
127
+
128
+ return useMemo(() => ({
129
+ primary: {
130
+ fill: palette.primary,
131
+ stroke: palette.primary,
132
+ color: palette.primaryForeground,
133
+ },
134
+ secondary: {
135
+ fill: palette.secondary,
136
+ stroke: palette.secondary,
137
+ color: palette.secondaryForeground,
138
+ },
139
+ success: {
140
+ fill: palette.success,
141
+ stroke: palette.success,
142
+ color: palette.successForeground,
143
+ },
144
+ danger: {
145
+ fill: palette.destructive,
146
+ stroke: palette.destructive,
147
+ color: palette.destructiveForeground,
148
+ },
149
+ warning: {
150
+ fill: palette.warning,
151
+ stroke: palette.warning,
152
+ color: palette.warningForeground,
153
+ },
154
+ info: {
155
+ fill: palette.info,
156
+ stroke: palette.info,
157
+ color: palette.infoForeground,
158
+ },
159
+ muted: {
160
+ fill: palette.muted,
161
+ stroke: palette.border,
162
+ color: palette.mutedForeground,
163
+ },
164
+ card: {
165
+ fill: palette.card,
166
+ stroke: palette.border,
167
+ color: palette.cardForeground,
168
+ },
169
+ accent: {
170
+ fill: palette.accent,
171
+ stroke: palette.border,
172
+ color: palette.accentForeground,
173
+ },
174
+ chart1: {
175
+ fill: palette.chart1,
176
+ stroke: palette.chart1,
177
+ color: palette.primaryForeground,
178
+ },
179
+ chart2: {
180
+ fill: palette.chart2,
181
+ stroke: palette.chart2,
182
+ color: palette.primaryForeground,
183
+ },
184
+ chart3: {
185
+ fill: palette.chart3,
186
+ stroke: palette.chart3,
187
+ color: palette.primaryForeground,
188
+ },
189
+ chart4: {
190
+ fill: palette.chart4,
191
+ stroke: palette.chart4,
192
+ color: palette.primaryForeground,
193
+ },
194
+ chart5: {
195
+ fill: palette.chart5,
196
+ stroke: palette.chart5,
197
+ color: palette.primaryForeground,
198
+ },
199
+ }), [palette]);
200
+ }
201
+
202
+ /**
203
+ * Hook to get box/region colors (e.g., for Mermaid rect blocks)
204
+ *
205
+ * @example
206
+ * ```tsx
207
+ * function MySequenceDiagram() {
208
+ * const boxes = useBoxColors();
209
+ *
210
+ * // Use in Mermaid SequenceDiagram
211
+ * rect(boxes.primary, () => {
212
+ * d.Alice.sync.Bob.msg('Hello');
213
+ * });
214
+ * }
215
+ * ```
216
+ */
217
+ export function useBoxColors(): BoxColors {
218
+ const theme = useResolvedTheme();
219
+
220
+ return useMemo(() => {
221
+ // Semi-transparent backgrounds for boxes
222
+ const alpha = theme === 'dark' ? 0.3 : 0.15;
223
+
224
+ return {
225
+ primary: getCssColorAsRgba('primary', alpha),
226
+ success: getCssColorAsRgba('chart-2', alpha),
227
+ warning: getCssColorAsRgba('chart-4', alpha),
228
+ danger: getCssColorAsRgba('destructive', alpha),
229
+ info: getCssColorAsRgba('chart-3', alpha),
230
+ muted: getCssColorAsRgba('muted', alpha),
231
+ };
232
+ }, [theme]);
233
+ }
@@ -0,0 +1,91 @@
1
+ /**
2
+ * Color conversion utilities
3
+ * @module ui-core/styles/palette/utils
4
+ */
5
+
6
+ /**
7
+ * Parse HSL string from CSS variable (e.g., "217 91% 60%") to components
8
+ */
9
+ function parseHslString(hsl: string): { h: number; s: number; l: number } | null {
10
+ const match = hsl.trim().match(/^(\d+)\s+(\d+)%\s+(\d+)%$/);
11
+ if (!match) return null;
12
+ return {
13
+ h: parseInt(match[1], 10),
14
+ s: parseInt(match[2], 10),
15
+ l: parseInt(match[3], 10),
16
+ };
17
+ }
18
+
19
+ /**
20
+ * Convert HSL to RGB
21
+ */
22
+ function hslToRgb(h: number, s: number, l: number): { r: number; g: number; b: number } {
23
+ s /= 100;
24
+ l /= 100;
25
+
26
+ const c = (1 - Math.abs(2 * l - 1)) * s;
27
+ const x = c * (1 - Math.abs(((h / 60) % 2) - 1));
28
+ const m = l - c / 2;
29
+
30
+ let r = 0, g = 0, b = 0;
31
+
32
+ if (h >= 0 && h < 60) { r = c; g = x; b = 0; }
33
+ else if (h >= 60 && h < 120) { r = x; g = c; b = 0; }
34
+ else if (h >= 120 && h < 180) { r = 0; g = c; b = x; }
35
+ else if (h >= 180 && h < 240) { r = 0; g = x; b = c; }
36
+ else if (h >= 240 && h < 300) { r = x; g = 0; b = c; }
37
+ else { r = c; g = 0; b = x; }
38
+
39
+ return {
40
+ r: Math.round((r + m) * 255),
41
+ g: Math.round((g + m) * 255),
42
+ b: Math.round((b + m) * 255),
43
+ };
44
+ }
45
+
46
+ /**
47
+ * Convert RGB to hex string
48
+ */
49
+ function rgbToHex(r: number, g: number, b: number): string {
50
+ const toHex = (n: number) => n.toString(16).padStart(2, '0');
51
+ return `#${toHex(r)}${toHex(g)}${toHex(b)}`;
52
+ }
53
+
54
+ /**
55
+ * Convert CSS HSL variable value to hex
56
+ * Input: "217 91% 60%" (from CSS --primary: 217 91% 60%)
57
+ * Output: "#3b82f6"
58
+ */
59
+ export function hslToHex(hslString: string): string {
60
+ const parsed = parseHslString(hslString);
61
+ if (!parsed) return '#000000';
62
+
63
+ const { r, g, b } = hslToRgb(parsed.h, parsed.s, parsed.l);
64
+ return rgbToHex(r, g, b);
65
+ }
66
+
67
+ /**
68
+ * Convert CSS HSL variable value to rgb() string
69
+ * Input: "217 91% 60%"
70
+ * Output: "rgb(59, 130, 246)"
71
+ */
72
+ export function hslToRgbString(hslString: string): string {
73
+ const parsed = parseHslString(hslString);
74
+ if (!parsed) return 'rgb(0, 0, 0)';
75
+
76
+ const { r, g, b } = hslToRgb(parsed.h, parsed.s, parsed.l);
77
+ return `rgb(${r}, ${g}, ${b})`;
78
+ }
79
+
80
+ /**
81
+ * Convert CSS HSL variable value to rgba() string with opacity
82
+ * Input: "217 91% 60%", 0.2
83
+ * Output: "rgba(59, 130, 246, 0.2)"
84
+ */
85
+ export function hslToRgba(hslString: string, alpha: number): string {
86
+ const parsed = parseHslString(hslString);
87
+ if (!parsed) return `rgba(0, 0, 0, ${alpha})`;
88
+
89
+ const { r, g, b } = hslToRgb(parsed.h, parsed.s, parsed.l);
90
+ return `rgba(${r}, ${g}, ${b}, ${alpha})`;
91
+ }