11import { setup } from "@storybook/vue3-vite" ;
2+ import { presets } from "../client-app/assets/presets" ;
23import { useThemeContext } from "../client-app/core/composables" ;
34import { setGlobals } from "../client-app/core/globals" ;
45import { createI18n } from "../client-app/i18n" ;
@@ -74,25 +75,87 @@ setThemeContext({
7475 } ,
7576} as StoreResponseType ) ;
7677
77- async function configureThemeSettings ( ) {
78- const module = ( await import ( `@/assets/presets/coffee.json` ) ) as {
79- default : IThemeConfigPreset ;
80- } ;
81- const preset = module . default ;
82-
83- if ( preset ) {
84- const styleElement = document . createElement ( "style" ) ;
85- styleElement . innerText = ":root {" ;
86- Object . entries ( preset ) . forEach ( ( [ key , value ] ) => {
87- styleElement . innerText += `--${ key . replace ( / _ / g, "-" ) } : ${ value } ;` ;
78+ // List of available theme presets
79+ const PRESETS = Object . keys ( presets ) ;
80+ type PresetNameType = keyof typeof presets ;
81+
82+ let currentStyleElement : HTMLStyleElement | null = null ;
83+ let currentPresetName : PresetNameType | null = null ;
84+ let loadingPreset : PresetNameType | null = null ;
85+ let i18nConfigured = false ;
86+
87+ // Enable for debugging: track DOM changes (only in dev mode)
88+ const ENABLE_DOM_MONITORING = import . meta. env . MODE === "development" ;
89+ if ( ENABLE_DOM_MONITORING && typeof window !== "undefined" ) {
90+ const observer = new MutationObserver ( ( mutations ) => {
91+ mutations . forEach ( ( mutation ) => {
92+ if ( mutation . type === "childList" && mutation . target === document . head ) {
93+ // eslint-disable-next-line no-console
94+ console . log ( "DOM changed in <head>:" , {
95+ addedNodes : mutation . addedNodes . length ,
96+ removedNodes : mutation . removedNodes . length ,
97+ target : mutation . target ,
98+ } ) ;
99+ }
88100 } ) ;
89- styleElement . innerText += "}" ;
90- document . head . prepend ( styleElement ) ;
101+ } ) ;
102+
103+ observer . observe ( document . head , {
104+ childList : true ,
105+ subtree : false ,
106+ } ) ;
107+
108+ // eslint-disable-next-line no-console
109+ console . log ( "MutationObserver activated for monitoring <head>" ) ;
110+ }
111+
112+ async function configureThemeSettings ( presetName : PresetNameType = "default" ) {
113+ if ( currentPresetName === presetName && currentStyleElement ) {
114+ return ;
115+ }
116+ if ( loadingPreset === presetName ) {
117+ return ;
118+ }
119+
120+ loadingPreset = presetName ;
121+
122+ try {
123+ const module = ( await import ( `@/assets/presets/${ presetName } .json` ) ) as {
124+ default : IThemeConfigPreset ;
125+ } ;
126+ const preset = module . default ;
127+
128+ if ( preset ) {
129+ if ( currentStyleElement && currentStyleElement . parentNode ) {
130+ currentStyleElement . parentNode . removeChild ( currentStyleElement ) ;
131+ }
132+
133+ currentStyleElement = document . createElement ( "style" ) ;
134+ currentStyleElement . innerText = ":root {" ;
135+ Object . entries ( preset ) . forEach ( ( [ key , value ] ) => {
136+ currentStyleElement ! . innerText += `--${ key . replace ( / _ / g, "-" ) } : ${ value } ;` ;
137+ } ) ;
138+ currentStyleElement . innerText += "}" ;
139+ document . head . prepend ( currentStyleElement ) ;
140+ currentPresetName = presetName ;
141+ }
142+ } catch ( error ) {
143+ // eslint-disable-next-line no-console
144+ console . error ( `Failed to load preset "${ presetName } ":` , error ) ;
145+ } finally {
146+ if ( loadingPreset === presetName ) {
147+ loadingPreset = null ;
148+ }
91149 }
92150}
93151
94152function configureI18N ( ) {
153+ if ( i18nConfigured ) {
154+ return ;
155+ }
156+
95157 i18n . global . setLocaleMessage ( DEFAULT_LOCALE , UI_KIT_DEFAULT_MESSAGE ) ;
158+ i18nConfigured = true ;
96159}
97160
98161setGlobals ( { i18n } ) ;
@@ -111,15 +174,28 @@ setup((app) => {
111174 console . error ( "Storybook Vue setup error:" , error ) ;
112175 }
113176
114- configureThemeSettings ( ) . catch ( ( ) => {
115- // eslint-disable-next-line no-console
116- console . error ( "Storybook theme setup error:" ) ;
117- } ) ;
118-
119177 configureI18N ( ) ;
120178} ) ;
121179
122180const preview : Preview = {
181+ globalTypes : {
182+ themePreset : {
183+ description : "Select theme preset" ,
184+ defaultValue : "default" ,
185+ toolbar : {
186+ title : "Theme preset" ,
187+ icon : "paintbrush" ,
188+ items : PRESETS . map ( ( preset ) => ( {
189+ value : preset ,
190+ title : preset . charAt ( 0 ) . toUpperCase ( ) + preset . slice ( 1 ) . replace ( / - / g, " " ) ,
191+ } ) ) ,
192+ dynamicTitle : true ,
193+ } ,
194+ } ,
195+ } ,
196+ initialGlobals : {
197+ themePreset : "default" ,
198+ } ,
123199 parameters : {
124200 controls : {
125201 matchers : {
@@ -135,6 +211,20 @@ const preview: Preview = {
135211 } ,
136212 a11y : a11yConfig ,
137213 } ,
214+ decorators : [
215+ ( story , context ) => {
216+ const presetName = context . globals . themePreset || "default" ;
217+
218+ if ( presetName !== currentPresetName && presetName !== loadingPreset ) {
219+ configureThemeSettings ( presetName ) . catch ( ( ) => {
220+ // eslint-disable-next-line no-console
221+ console . error ( "Storybook theme setup error" ) ;
222+ } ) ;
223+ }
224+
225+ return story ( ) ;
226+ } ,
227+ ] ,
138228 tags : [ "autodocs" ] ,
139229} ;
140230
0 commit comments