1- import { useMemo , useRef , useState , useEffect } from "react" ;
2- import { useRouter } from "next/router" ;
3- import {
4- DxcButton ,
5- DxcContainer ,
6- DxcDialog ,
7- DxcFlex ,
8- DxcHeading ,
9- DxcParagraph ,
10- DxcWizard ,
11- } from "@dxc-technology/halstack-react" ;
1+ import { useMemo , useRef , useState } from "react" ;
2+ import { DxcContainer , DxcFlex , DxcWizard } from "@dxc-technology/halstack-react" ;
123import StepHeading from "./components/StepHeading" ;
134import BottomButtons from "./components/BottomButtons" ;
145import ThemeGeneratorPreviewPage from "./ThemeGeneratorPreviewPage" ;
156import { BrandingDetails } from "./steps/BrandingDetails" ;
167import { generateTokens , handleExport } from "./utils" ;
178import { Colors , FileData , Step } from "./types" ;
189import ReviewDetails from "./steps/ReviewDetails" ;
19- import { ACTIONS , EVENTS , Joyride , STATUS , type EventData , type Step as JoyrideStep } from "react-joyride" ;
20- import TourPopover from "./components/TourPopover" ;
21- import Code from "@/common/Code" ;
10+ import Tour from "./components/Tour/Tour" ;
2211
2312const steps = [
2413 {
@@ -46,152 +35,9 @@ const wizardSteps = steps.map(({ label, description }) => ({
4635 description,
4736} ) ) ;
4837
49- const firstStepTour : JoyrideStep [ ] = [
50- {
51- title : "The 3-Step Journey" ,
52- content : (
53- < >
54- To build your theme, we'll guide you through 3 simple steps:
55- < br />
56- < br />
57- 1. Configure: Define brand colors and assets
58- < br />
59- 2. Preview: See your styles applied
60- < br />
61- 3. Export: Review and download your theme
62- </ >
63- ) ,
64- target : "#wizard-tour" ,
65- placement : "bottom" ,
66- skipBeacon : true ,
67- } ,
68- {
69- title : "Define theme colors" ,
70- content : (
71- < >
72- Set your theme colors, including core colors for your brand and semantic colors for system feedback.
73- < br />
74- < br />
75- You can update this anytime.
76- </ >
77- ) ,
78- placement : "bottom" ,
79- target : "#colors-tour" ,
80- skipBeacon : true ,
81- } ,
82- {
83- title : "Upload assets" ,
84- content : (
85- < >
86- Upload branding assets such as logo, footer logos, and favicon.
87- < br />
88- < br />
89- Applied across your theme for brand consistency.
90- </ >
91- ) ,
92- placement : "top" ,
93- target : "#assets-tour" ,
94- skipBeacon : true ,
95- arrowSpacing : 162 ,
96- } ,
97- ] ;
98-
99- const secondStepTour : JoyrideStep [ ] = [
100- {
101- title : "Preview theme" ,
102- content : (
103- < >
104- Use this toggle to preview your theme across different views. Components show individual UI elements, while
105- Layouts display predefined page structures. Use this to check color usage, consistency, and readability across
106- your theme.
107- </ >
108- ) ,
109- target : "#toggle-tour" ,
110- placement : "right" ,
111- skipBeacon : true ,
112- } ,
113- {
114- title : "Filter your preview" ,
115- content : (
116- < >
117- Choose specific component groups to preview, such as Forms or Feedback.
118- < br />
119- < br />
120- You can select one or multiple groups to see how your theme styles apply across different scenarios.
121- </ >
122- ) ,
123- placement : "left" ,
124- target : "#select-preview-tour" ,
125- skipBeacon : true ,
126- } ,
127- {
128- title : "Preview area" ,
129- content : (
130- < >
131- In this area, you can preview both Components and Layout examples.
132- < br />
133- < br />
134- Use the selector to add them to your canvas. If it gets too crowded, just click < Code > Clear</ Code > to reset the
135- view.
136- </ >
137- ) ,
138- placement : "top" ,
139- target : "#preview-area-tour" ,
140- skipBeacon : true ,
141- arrowSpacing : 162 ,
142- } ,
143- ] ;
144-
145- const thirdStepTour : JoyrideStep [ ] = [
146- {
147- title : "Review theme" ,
148- content : (
149- < >
150- Review your colors, branding, and overall configuration. Validate your setup before finalizing.
151- < br />
152- < br />
153- Helps confirm your theme is ready for export.
154- </ >
155- ) ,
156- target : "#review-tour" ,
157- placement : "top" ,
158- skipBeacon : true ,
159- } ,
160- {
161- title : "Copy configuration" ,
162- content : (
163- < >
164- Copy your theme configuration as JSON.
165- < br /> Use it for quick integration.
166- < br />
167- < br />
168- Allow faster implementation without downloading a file.
169- </ >
170- ) ,
171- placement : "left" ,
172- target : "#copy-configuration-tour" ,
173- skipBeacon : true ,
174- } ,
175- {
176- title : "Export theme" ,
177- content : (
178- < >
179- Export your theme as a file and use it across your applications. Your theme is ready to be applied across
180- projects.
181- < br />
182- < br />
183- You're all set! If you'd like to go through the tour again, click the restart button.
184- </ >
185- ) ,
186- placement : "left-start" ,
187- target : "#export-tour" ,
188- skipBeacon : true ,
189- } ,
190- ] ;
191-
19238const ThemeGeneratorConfigPage = ( ) => {
193- const router = useRouter ( ) ;
19439 const [ currentStep , setCurrentStep ] = useState < Step > ( 0 ) ;
40+ const [ tourStepIndex , setTourStepIndex ] = useState ( 0 ) ;
19541 const [ colors , setColors ] = useState < Colors > ( {
19642 primary : "#5F249F" ,
19743 secondary : "#0067B3" ,
@@ -210,26 +56,6 @@ const ThemeGeneratorConfigPage = () => {
21056 } ) ;
21157 const [ tokens , setTokens ] = useState < Record < string , string > > ( { } ) ;
21258 const lastGeneratedColorsRef = useRef < string > ( "" ) ;
213- const [ isDialogVisible , setDialogVisible ] = useState ( false ) ;
214- const [ runTour , setRunTour ] = useState ( false ) ;
215- const [ tourStepIndex , setTourStepIndex ] = useState ( 0 ) ;
216- const isTourActiveRef = useRef ( false ) ;
217- const completedToursRef = useRef < Set < Step > > ( new Set ( ) ) ;
218-
219- useEffect ( ( ) => {
220- if ( router . query . tour === "true" ) {
221- setDialogVisible ( true ) ;
222- // Remove the tour parameter from the URL without reloading
223- void router . replace ( "/theme-generator/configuration" , undefined , { shallow : true } ) ;
224- }
225- } , [ router . query . tour ] ) ;
226-
227- useEffect ( ( ) => {
228- if ( isTourActiveRef . current && ! completedToursRef . current . has ( currentStep ) ) {
229- setTourStepIndex ( 0 ) ;
230- setRunTour ( true ) ;
231- }
232- } , [ currentStep ] ) ;
23359
23460 const themeJson = useMemo ( ( ) => {
23561 const themeObject = {
@@ -273,64 +99,12 @@ const ThemeGeneratorConfigPage = () => {
27399 setCurrentStep ( step ) ;
274100 } ;
275101
276- const handleStartTour = ( ) => {
277- if ( isDialogVisible ) {
278- setDialogVisible ( false ) ;
279- }
280- isTourActiveRef . current = true ;
281- setRunTour ( true ) ;
282- } ;
283-
284- const handleFinishTour = ( ) => {
285- completedToursRef . current . add ( currentStep ) ;
286- if ( currentStep === 2 ) {
287- isTourActiveRef . current = false ;
288- }
289- setRunTour ( false ) ;
290- } ;
291-
292- const handleRestartTour = ( ) => {
293- completedToursRef . current . delete ( currentStep ) ;
294- setTourStepIndex ( 0 ) ;
295- setRunTour ( false ) ;
296- setTimeout ( ( ) => {
297- setRunTour ( true ) ;
298- } , 100 ) ;
299- } ;
300-
301- const handleTourEvent = ( { action, status, index, type } : EventData ) => {
302- if ( status === STATUS . FINISHED || status === STATUS . SKIPPED || status === STATUS . PAUSED ) {
303- handleFinishTour ( ) ;
304- return ;
305- }
306-
307- if ( action === ACTIONS . CLOSE ) {
308- handleFinishTour ( ) ;
309- return ;
310- }
311-
312- // Update index when step changes
313- if ( type === EVENTS . STEP_AFTER ) {
314- if ( action === ACTIONS . NEXT ) {
315- setTourStepIndex ( index + 1 ) ;
316- } else if ( action === ACTIONS . PREV ) {
317- setTourStepIndex ( index - 1 ) ;
318- }
319- }
320- } ;
321-
322102 const renderStepContent = ( ) => {
323103 switch ( currentStep ) {
324104 case 0 :
325105 return < BrandingDetails colors = { colors } onColorsChange = { setColors } logos = { logos } onLogosChange = { setLogos } /> ;
326106 case 1 :
327- return (
328- < ThemeGeneratorPreviewPage
329- tokens = { tokens }
330- logos = { logos }
331- showDefaultComponents = { runTour && tourStepIndex === 2 }
332- />
333- ) ;
107+ return < ThemeGeneratorPreviewPage tokens = { tokens } logos = { logos } showDefaultComponents = { tourStepIndex === 2 } /> ;
334108 case 2 :
335109 return < ReviewDetails tokens = { tokens } logos = { logos } themeJson = { themeJson } /> ;
336110 }
@@ -344,77 +118,7 @@ const ThemeGeneratorConfigPage = () => {
344118 padding = { { top : "var(--spacing-padding-xl)" } }
345119 background = { { color : "var(--color-bg-neutral-lighter)" } }
346120 >
347- { currentStep === 0 && (
348- < Joyride
349- continuous
350- run = { runTour }
351- steps = { firstStepTour }
352- stepIndex = { tourStepIndex }
353- tooltipComponent = { ( tooltipProps ) => (
354- < TourPopover { ...tooltipProps } onFinish = { handleFinishTour } onRestart = { handleRestartTour } />
355- ) }
356- onEvent = { handleTourEvent }
357- options = { {
358- overlayClickAction : false ,
359- hideOverlay : false ,
360- blockTargetInteraction : false ,
361- spotlightPadding : 4 ,
362- overlayColor : "var(--color-bg-alpha-strong)" ,
363- zIndex : 10000 ,
364- arrowBase : 12 ,
365- arrowSize : 6 ,
366- offset : 0 ,
367- } }
368- />
369- ) }
370-
371- { currentStep === 1 && (
372- < Joyride
373- continuous
374- run = { runTour }
375- steps = { secondStepTour }
376- stepIndex = { tourStepIndex }
377- tooltipComponent = { ( tooltipProps ) => (
378- < TourPopover { ...tooltipProps } onFinish = { handleFinishTour } onRestart = { handleRestartTour } />
379- ) }
380- onEvent = { handleTourEvent }
381- options = { {
382- overlayClickAction : false ,
383- hideOverlay : false ,
384- blockTargetInteraction : false ,
385- spotlightPadding : 4 ,
386- overlayColor : "var(--color-bg-alpha-strong)" ,
387- zIndex : 10000 ,
388- arrowBase : 12 ,
389- arrowSize : 6 ,
390- offset : 0 ,
391- } }
392- />
393- ) }
394-
395- { currentStep === 2 && (
396- < Joyride
397- continuous
398- run = { runTour }
399- steps = { thirdStepTour }
400- stepIndex = { tourStepIndex }
401- tooltipComponent = { ( tooltipProps ) => (
402- < TourPopover { ...tooltipProps } onFinish = { handleFinishTour } onRestart = { handleRestartTour } />
403- ) }
404- onEvent = { handleTourEvent }
405- options = { {
406- overlayClickAction : false ,
407- hideOverlay : false ,
408- blockTargetInteraction : false ,
409- spotlightPadding : 4 ,
410- overlayColor : "var(--color-bg-alpha-strong)" ,
411- zIndex : 10000 ,
412- arrowBase : 12 ,
413- arrowSize : 6 ,
414- offset : 0 ,
415- } }
416- />
417- ) }
121+ < Tour currentStep = { currentStep } onTourStepIndexChange = { setTourStepIndex } />
418122
419123 < DxcFlex direction = "column" gap = "var(--spacing-gap-xl)" fullHeight >
420124 < DxcContainer width = "80%" maxWidth = "1112px" margin = { { left : "auto" , right : "auto" } } >
@@ -449,34 +153,6 @@ const ThemeGeneratorConfigPage = () => {
449153 />
450154 </ DxcFlex >
451155 </ DxcContainer >
452- { isDialogVisible && (
453- < DxcDialog onCloseClick = { ( ) => setDialogVisible ( false ) } >
454- < DxcContainer padding = "var(--spacing-padding-l)" width = "648px" >
455- < DxcFlex direction = "column" gap = "var(--spacing-gap-s)" alignItems = "start" >
456- < DxcHeading level = { 3 } text = "Welcome to Halstack Theme Generator" />
457- < DxcParagraph >
458- This guided tour walk you through the 3 simple steps to set up your Halstack Theme. We’ll start with
459- branding, then preview in real components and product layouts and finally export your theme.
460- </ DxcParagraph >
461- < DxcFlex gap = "var(--spacing-gap-s)" justifyContent = "end" alignSelf = "stretch" >
462- < DxcButton
463- label = "Skip"
464- mode = "tertiary"
465- size = { { height : "medium" } }
466- onClick = { ( ) => setDialogVisible ( false ) }
467- />
468- < DxcButton
469- label = "Get Started!"
470- icon = "filled_play_arrow"
471- iconPosition = "after"
472- size = { { height : "medium" } }
473- onClick = { handleStartTour }
474- />
475- </ DxcFlex >
476- </ DxcFlex >
477- </ DxcContainer >
478- </ DxcDialog >
479- ) }
480156 </ >
481157 ) ;
482158} ;
0 commit comments