64
64
</div >
65
65
<div class =' sb-header-item sb-table-cell' >
66
66
<div id =' sb-header-text' class =' e-sb-header-text' >
67
- <span class =' sb-header-text-left' >Essential Studio<sup >®</sup > for </span >
67
+ <span class =' sb-header-text-left' >Essential Studio<sup >®</sup > UI Suite for </span >
68
68
<span class =' sb-header-text-right' role =" button" tabindex =" 0" >Vue</span >
69
69
</div >
70
70
</div >
@@ -489,6 +489,9 @@ const sampleRegex: RegExp = /#\/(([^\/]+\/)+[^\/\.]+)/;
489
489
const sbArray: string [] = [' angular' , ' react' , ' nextjs' , ' javascript' , ' aspnetcore' , ' aspnetmvc' , ' typescript' , ' blazor' ];
490
490
// Regex for removing hidden
491
491
const reg: RegExp = / . * custom code start([\S\s ] *? )custom code end. * / g ;
492
+ let skipReload: boolean = false ; // Flag to skip reload when switching themes
493
+ let isReloadingTheme = false ; // Flag to indicate if the theme is being reloaded
494
+ let hasHandledInitialTheme = false ; // Flag to check if the initial theme has been handled
492
495
let selectedTheme: string = location .hash .split (' /' )[1 ] || ' tailwind3' ;
493
496
const themeCollection: string [] = [' material3' , ' bootstrap5' , ' fluent2' , ' tailwind3' , ' fluent2-highcontrast' , ' highcontrast' , ' tailwind' , ' fluent' ];
494
497
const themesToRedirect: string [] = [' material' , ' material-dark' , ' bootstrap4' , ' bootstrap' , ' bootstrap-dark' , ' fabric' , ' fabric-dark' ];
@@ -577,12 +580,26 @@ onBeforeMount(() => {
577
580
});
578
581
579
582
const urlChange = async () => {
583
+
584
+ // Check if this is just a dark/light mode switch
585
+ const currentHash = location .hash ;
586
+ const currentParts = currentHash .split (' /' );
587
+ const prevParts = prev .split (' /' );
588
+ // If only the theme part changed (index 1) but component and sample are the same, skip urlChange
589
+ const isThemeOnlyChange = prevParts .length >= 3 && currentParts .length >= 3 &&
590
+ currentParts [2 ] === prevParts [2 ] && // same component
591
+ currentParts [3 ] === prevParts [3 ] && // same sample
592
+ currentParts [1 ] !== prevParts [1 ]; // different theme
593
+ if (isThemeOnlyChange ) {
594
+ prev = currentHash ;
595
+ return ;
596
+ }
580
597
await router .isReady ();
581
598
sb .vars .contentTab .selectedItem = 0 ;
582
599
updateBreadCrumb ();
583
600
updateDescription ();
584
601
updatesourceTab ();
585
- };
602
+ }
586
603
587
604
onMounted (async () => {
588
605
rootEle = homeEle .value as HTMLElement ;
@@ -654,8 +671,11 @@ onMounted(async () => {
654
671
sidebar .appendTo (' #left-sidebar' );
655
672
}
656
673
loadJSON ();
674
+ const themeFromHash = location .hash .split (" /" )[1 ] || selectedTheme ;
675
+ selectedTheme = themeFromHash ;
676
+ loadTheme (selectedTheme , false ); // full setup on initial load
677
+ hasHandledInitialTheme = true ; // prevent hashchange duplication
657
678
router .afterEach (urlChange );
658
- updateBreadCrumb ();
659
679
updateDescription ();
660
680
});
661
681
@@ -1047,6 +1067,7 @@ const rendersbPopup = (): void => {
1047
1067
else {
1048
1068
selectedTheme = selectedTheme .replace (" -dark" , " " );
1049
1069
}
1070
+ skipReload = true ;
1050
1071
switchTheme (selectedTheme );
1051
1072
},
1052
1073
});
@@ -1260,6 +1281,8 @@ const eventBinding = (): void => {
1260
1281
};
1261
1282
const onSwitchMode = (arg : any ): void => {
1262
1283
selectedTheme = selectedTheme .endsWith (" -dark" ) ? selectedTheme .replace (" -dark" , " " ) : selectedTheme + " -dark" ;
1284
+ localStorage .setItem (" selectedTheme" , selectedTheme );
1285
+ skipReload = true ; // signal that we're toggling mode, not switching base theme
1263
1286
switchTheme (selectedTheme );
1264
1287
};
1265
1288
const changeRtl = (args : any ): void => {
@@ -1388,6 +1411,8 @@ const changeTheme = (e: MouseEvent): void => {
1388
1411
if ((select (" #sb-icon-text" ) as HTMLElement ).textContent === " LIGHT" && ! [' highcontrast' , ' fluent2-highcontrast' ].includes (themeName )) {
1389
1412
themeName += " -dark" ;
1390
1413
}
1414
+ skipReload = false ; // Force full reload for dropdown
1415
+ isReloadingTheme = true ; // flag reload to skip hashchange theme apply
1391
1416
switchTheme (themeName );
1392
1417
let imageEditorElem = document .querySelector (" .e-image-editor" ) as HTMLElement ;
1393
1418
if (imageEditorElem != null ) {
@@ -1396,15 +1421,20 @@ const changeTheme = (e: MouseEvent): void => {
1396
1421
}
1397
1422
};
1398
1423
1399
- const switchTheme = (str : string ): void => {
1400
- str = str .includes (' _' ) ? str .replace (' _' , ' .' ) : str ;
1401
- let hash: string [] = location .hash .split (' /' );
1402
- if (hash [1 ] !== str ) {
1403
- hash [1 ] = str ;
1404
- location .hash = hash .join (' /' );
1405
- location .reload ();
1424
+ const switchTheme = (themeName : string ): void => {
1425
+ themeName = themeName .includes (" _" ) ? themeName .replace (" _" , " ." ) : themeName ;
1426
+ const hashParts = location .hash .split (" /" );
1427
+ hashParts [1 ] = themeName ;
1428
+ location .hash = hashParts .join (" /" );
1429
+ if (skipReload ) {
1430
+ loadTheme (themeName , true ); // soft switch for toggle
1431
+ skipReload = false ; // Reset the flag after soft switch
1432
+ } else {
1433
+ localStorage .setItem (" selectedTheme" , themeName ); // ensures theme survives reload
1434
+ location .reload (); // hard reload for dropdown
1406
1435
}
1407
1436
};
1437
+
1408
1438
const setThemeModesButton = (): void => {
1409
1439
let themeModeText = select (" #sb-icon-text" ) as HTMLElement ;
1410
1440
let darkThemeIcon = select (" #icon-dark" ) as HTMLElement ;
@@ -1425,35 +1455,109 @@ const setThemeModesButton = (): void => {
1425
1455
mobileDarkThemeIcon .classList .add (" hide" );
1426
1456
}
1427
1457
};
1428
- const loadTheme = (theme : string ): void => {
1429
- theme = themesToRedirect .indexOf (theme ) !== - 1 ? ' tailwind3' : theme ;
1430
- let body: HTMLElement = document .body ;
1431
- if (body .classList .length > 0 ) {
1432
- for (let themeItem of themeCollection ) {
1433
- body .classList .remove (themeItem );
1458
+ const loadTheme = (theme : string , softSwitch = false ): void => {
1459
+ selectedTheme = theme ;
1460
+ theme = themesToRedirect .includes (theme ) ? " tailwind3" : theme ;
1461
+ // Theme class resolution
1462
+ const resolvedClass = theme .includes (" bootstrap5" )
1463
+ ? theme .replace (" bootstrap5" , " bootstrap5_3" )
1464
+ : theme ;
1465
+ const themeFile = theme .includes (" bootstrap5" )
1466
+ ? theme .replace (" bootstrap5" , " bootstrap5.3" )
1467
+ : theme ;
1468
+ const body = document .body ;
1469
+ const themePrefixes = [" tailwind3" , " fluent2" , " bootstrap5_3" , " material3" , " highcontrast" ];
1470
+ // Remove previous theme classes
1471
+ body .className = body .className
1472
+ .split (" " )
1473
+ .filter (cls => ! themePrefixes .some (prefix =>
1474
+ cls .startsWith (prefix ) || cls .startsWith (prefix + " -dark" )))
1475
+ .join (" " );
1476
+ body .classList .add (resolvedClass );
1477
+ // Update dynamic theme stylesheet
1478
+ const cssLink = document .getElementById (" dynamic-theme" ) as HTMLLinkElement ;
1479
+ if (cssLink ) {
1480
+ cssLink .href = ` ./styles/${themeFile }.css ` ;
1481
+ }
1482
+ // Highlight selected theme in UI
1483
+ themeList ?.querySelector (" .active" )?.classList .remove (" active" );
1484
+ const normalized = theme .endsWith (" -dark" ) ? theme .replace (" -dark" , " " ) : theme ;
1485
+ themeList ?.querySelector (` #${normalized } ` )?.classList .add (" active" );
1486
+ // Handle unsupported toggle modes
1487
+ const unsupported = [" highcontrast" , " fluent2-highcontrast" ];
1488
+ const toggleBtn = select (" #themeMode-switch-btn" );
1489
+ const modeSection = select (" .sb-setting-themeModes-section" );
1490
+ if (unsupported .includes (selectedTheme )) {
1491
+ toggleBtn ?.classList .add (" hide" );
1492
+ modeSection ?.classList .add (" hide" );
1493
+ } else {
1494
+ toggleBtn ?.classList .remove (" hide" );
1495
+ modeSection ?.classList .remove (" hide" );
1496
+ }
1497
+ // Ripple effect and toggle button visuals
1498
+ enableRipple (selectedTheme .indexOf (" material" ) !== - 1 || ! selectedTheme );
1499
+ setThemeModesButton ();
1500
+ // Re-bind UI components only on full load
1501
+ if (! softSwitch ) {
1502
+ renderLeftPaneComponents (); // Sidebar and left pane
1503
+ rendersbPopup (); // Settings and popups
1504
+ eventBinding (); // Interaction and event listeners
1505
+ refreshDynamicContent (); // Update content and search
1506
+ }else {
1507
+ // Soft switch: only update theme and refresh samples
1508
+ refreshDynamicContent ();
1509
+ refreshSamples ();
1510
+ }
1511
+ // UI layout cleanup
1512
+ setTimeout (() => {
1513
+ select (" .sb-loading-overlay" )?.classList .add (" hide" );
1514
+ window .dispatchEvent (new Event (" resize" ));
1515
+ }, 500 );
1516
+ };
1517
+
1518
+ // data visualization components
1519
+ const doControls: string [] = [
1520
+ " chart" , " three-dimension-chart" , " circular-3d-chart" , " stock-chart" , " arc-gauge" , " circular-gauge" ,
1521
+ " diagram" , " heatmap" , " linear-gauge" , " maps" , " range-navigator" , " smith-chart" ,
1522
+ " barcode" , " sparkline" , " treemap" , " bullet-chart" ," kanban"
1523
+ ];
1524
+
1525
+ // components rerendering for every dark/light toggle
1526
+ const refreshSamples = () => {
1527
+ const currentControl = location .hash .split (' /' )[2 ];
1528
+ if (doControls .includes (currentControl )) {
1529
+ const demo = document .querySelector (' .sb-demo-section' );
1530
+ if (demo ) {
1531
+ const controls = demo .getElementsByClassName (' e-control e-lib' );
1532
+ for (let i = 0 ; i < controls .length ; i ++ ) {
1533
+ const instance = (controls [i ] as any ).ej2_instances ;
1534
+ if (instance ?.[0 ]?.refresh instanceof Function ) {
1535
+ instance [0 ].refresh ();
1434
1536
}
1537
+ }
1435
1538
}
1436
- body .classList .add (theme .includes (' bootstrap5' ) ? theme .replace (' bootstrap5' , ' bootstrap5_3' ) : theme );
1437
- themeList .querySelector (" .active" ).classList .remove (" active" );
1438
- if (theme .endsWith (" -dark" )) {
1439
- theme = theme .replace (" -dark" , " " );
1440
- }
1441
- else if ([" highcontrast" , " fluent2-highcontrast" ].includes (selectedTheme )) {
1442
- (select (" #themeMode-switch-btn" ) as HTMLElement ).classList .add (" hide" );
1443
- (select (" .sb-setting-themeModes-section" ) as HTMLElement ).classList .add (" hide" );
1444
- }
1445
- themeList .querySelector (" #" + theme )?.classList .add (" active" );
1446
- setThemeModesButton ();
1447
- renderLeftPaneComponents ();
1448
- rendersbPopup ();
1449
- eventBinding ();
1450
- sampleArray ();
1451
- updatesourceTab ();
1452
- (elasticlunr as any ).clearStopWords ();
1453
- searchInstance = (elasticlunr as any ).Index .load (searchJson );
1454
- setTimeout (() => { window .dispatchEvent (new Event (' resize' )); }, 500 );
1539
+ }
1540
+ };
1541
+
1542
+ const refreshDynamicContent = () => {
1543
+ updateBreadCrumb ();
1544
+ sampleArray ();
1545
+ updatesourceTab ();
1546
+ searchInstance = (elasticlunr as any ).Index .load (searchJson );
1547
+ (elasticlunr as any ).clearStopWords ();
1455
1548
};
1456
1549
1550
+ window .addEventListener (" hashchange" , () => {
1551
+ if (isReloadingTheme || ! hasHandledInitialTheme ) return ;
1552
+
1553
+ const hashTheme = location .hash .split (" /" )[1 ];
1554
+ if (hashTheme && hashTheme !== selectedTheme ) {
1555
+ selectedTheme = hashTheme ;
1556
+ localStorage .setItem (" selectedTheme" , selectedTheme );
1557
+ loadTheme (selectedTheme , true ); // soft switch
1558
+ }
1559
+ });
1560
+
1457
1561
const setMouseOrTouch = (e : MouseEvent ): void => {
1458
1562
let ele: HTMLElement = <any >closest (<any >e .target , ' .sb-responsive-items' );
1459
1563
let switchType: string = ele .id ;
@@ -1936,9 +2040,8 @@ const loadJSON = (): void => {
1936
2040
}
1937
2041
overlay ();
1938
2042
changeMouseOrTouch (switchText );
1939
- localStorage .removeItem (' ej2-switch' );
2043
+ // localStorage.removeItem('ej2-switch');
1940
2044
enableRipple (selectedTheme .indexOf (' material' ) !== - 1 || ! selectedTheme );
1941
- loadTheme (selectedTheme );
1942
2045
};
1943
2046
1944
2047
</script >
0 commit comments