11import React , { type JSX , useState , useEffect , useMemo } from 'react' ;
22import { Button , BUTTON_VARIANT , Modal , ModalBody , ModalContent , Text , TreeView , TreeViewNode , TreeViewNodes } from '@ovhcloud/ods-react' ;
3-
43import styles from './themeGeneratorPaletteModal.module.css' ;
54import { generatePalette , formatPaletteAsCssVariables , COLOR_FAMILIES , PALETTE_STEPS , type ColorFamily , type PaletteResult } from './paletteGenerator' ;
6- import { ThemeGeneratorColorPicker } from '../ThemeGeneratorColorPicker' ;
5+ import { ThemeGeneratorColorPicker } from '../ThemeGeneratorColorPicker/ThemeGeneratorColorPicker' ;
6+
7+ const ODS_COLOR_PREFIX = '--ods-color' ;
8+
9+ /**
10+ * Builds a CSS variable name for a color token
11+ * @param family - Color family (e.g., 'primary', 'neutral')
12+ * @param step - Optional color step (e.g., '500', '100')
13+ * @returns CSS variable name (e.g., '--ods-color-primary-500')
14+ */
15+ function buildColorVar ( family : string , step ?: string ) : string {
16+ return step ? `${ ODS_COLOR_PREFIX } -${ family } -${ step } ` : `${ ODS_COLOR_PREFIX } -${ family } ` ;
17+ }
718
819interface TreeItem {
920 id : string ;
@@ -22,35 +33,31 @@ interface ThemeGeneratorPaletteModalProps {
2233const ThemeGeneratorPaletteModal = ( { open, onClose, onApply, currentVariables } : ThemeGeneratorPaletteModalProps ) : JSX . Element => {
2334 const [ generatedPalettes , setGeneratedPalettes ] = useState < Record < ColorFamily , PaletteResult > > ( { } as Record < ColorFamily , PaletteResult > ) ;
2435 const [ seedColors , setSeedColors ] = useState < Record < ColorFamily , string > > ( { } as Record < ColorFamily , string > ) ;
36+ const [ errors , setErrors ] = useState < Partial < Record < ColorFamily , string > > > ( { } ) ;
2537
2638 useEffect ( ( ) => {
2739 const initialSeeds : Record < ColorFamily , string > = { } as Record < ColorFamily , string > ;
2840 COLOR_FAMILIES . forEach ( ( family ) => {
29- const defaultColorVar = `--ods-color- ${ family } - 500` ;
41+ const defaultColorVar = buildColorVar ( family , ' 500' ) ;
3042 initialSeeds [ family ] = currentVariables [ defaultColorVar ] || '#000000' ;
3143 } ) ;
3244 setSeedColors ( initialSeeds ) ;
3345 } , [ currentVariables ] ) ;
3446
3547 const treeItems = useMemo ( ( ) => {
3648 return COLOR_FAMILIES . map ( ( family ) => {
37- let palette : PaletteResult ;
38-
39- if ( generatedPalettes [ family ] ) {
40- palette = generatedPalettes [ family ] ;
41- } else {
42- palette = { } ;
43- for ( const step of PALETTE_STEPS ) {
44- const varName = `--ods-color-${ family } -${ step } ` ;
45- palette [ step ] = currentVariables [ varName ] || '#000000' ;
46- }
47- }
48-
49- const children : TreeItem [ ] = PALETTE_STEPS . map ( ( step ) => ( {
50- id : `${ family } -${ step } ` ,
51- name : `--ods-color-${ family } -${ step } ` ,
52- value : palette [ step ] || '#000000' ,
53- } ) ) ;
49+ const palette = generatedPalettes [ family ] ;
50+
51+ const children : TreeItem [ ] = PALETTE_STEPS . map ( ( step ) => {
52+ const varName = buildColorVar ( family , step ) ;
53+ const value = palette ?. [ step ] || currentVariables [ varName ] || '#000000' ;
54+
55+ return {
56+ id : `${ family } -${ step } ` ,
57+ name : varName ,
58+ value,
59+ } ;
60+ } ) ;
5461
5562 return {
5663 id : family ,
@@ -72,6 +79,13 @@ const ThemeGeneratorPaletteModal = ({ open, onClose, onApply, currentVariables }
7279 [ family ] : color ,
7380 } ) ) ;
7481
82+ // Clear previous error for this family
83+ setErrors ( prev => {
84+ const newErrors = { ...prev } ;
85+ delete newErrors [ family ] ;
86+ return newErrors ;
87+ } ) ;
88+
7589 // Generate palette automatically when seed color changes
7690 try {
7791 const palette = generatePalette ( color ) ;
@@ -80,7 +94,11 @@ const ThemeGeneratorPaletteModal = ({ open, onClose, onApply, currentVariables }
8094 [ family ] : palette ,
8195 } ) ) ;
8296 } catch ( error ) {
83- console . error ( 'Failed to generate palette:' , error ) ;
97+ const errorMessage = error instanceof Error ? error . message : 'Failed to generate palette' ;
98+ setErrors ( prev => ( {
99+ ...prev ,
100+ [ family ] : errorMessage ,
101+ } ) ) ;
84102 }
85103 }
86104
@@ -108,31 +126,42 @@ const ThemeGeneratorPaletteModal = ({ open, onClose, onApply, currentVariables }
108126 < TreeViewNodes >
109127 { treeItems . map ( ( item ) => (
110128 < TreeViewNode key = { item . id } item = { item } >
111- { ( { item, isBranch } : { item : TreeItem ; isBranch : boolean } ) => (
112- < div className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item' ] } >
113- { isBranch ? (
114- < >
115- < Text className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item__name' ] } >
116- { item . name }
117- </ Text >
118- < ThemeGeneratorColorPicker
119- value = { seedColors [ item . id as ColorFamily ] || '#000000' }
120- onChange = { ( color ) => handleSeedColorChange ( item . id as ColorFamily , color ) }
121- showLabel = { false }
122- />
123- </ >
124- ) : (
125- item . value && (
126- < ThemeGeneratorColorPicker
127- label = { item . name }
128- value = { item . value }
129- onChange = { ( ) => { } }
130- disabled
131- />
132- )
133- ) }
134- </ div >
135- ) }
129+ { ( { item, isBranch } : { item : TreeItem ; isBranch : boolean } ) => {
130+ const familyError = isBranch ? errors [ item . id as ColorFamily ] : undefined ;
131+
132+ return (
133+ < div className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item' ] } >
134+ { isBranch ? (
135+ < >
136+ < div className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item__header' ] } >
137+ < Text className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item__name' ] } >
138+ { item . name }
139+ </ Text >
140+ < ThemeGeneratorColorPicker
141+ value = { seedColors [ item . id as ColorFamily ] || '#000000' }
142+ onChange = { ( color ) => handleSeedColorChange ( item . id as ColorFamily , color ) }
143+ showLabel = { false }
144+ />
145+ </ div >
146+ { familyError && (
147+ < Text className = { styles [ 'theme-generator-palette-modal__preview__tree-view__tree-item__error' ] } >
148+ { familyError }
149+ </ Text >
150+ ) }
151+ </ >
152+ ) : (
153+ item . value && (
154+ < ThemeGeneratorColorPicker
155+ label = { item . name }
156+ value = { item . value }
157+ onChange = { ( ) => { } }
158+ disabled
159+ />
160+ )
161+ ) }
162+ </ div >
163+ ) ;
164+ } }
136165 </ TreeViewNode >
137166 ) ) }
138167 </ TreeViewNodes >
0 commit comments