diff --git a/copyfile.js b/copyfile.js deleted file mode 100644 index aac2d318..00000000 --- a/copyfile.js +++ /dev/null @@ -1,4 +0,0 @@ -'use strict'; - -const { copyFileSync } = require('fs'); // eslint-disable-line -copyFileSync('dist/devextreme-themebuilder-app/index.html', 'dist/devextreme-themebuilder-app/404.html'); diff --git a/generate-route-pages.js b/generate-route-pages.js new file mode 100644 index 00000000..de1e76b3 --- /dev/null +++ b/generate-route-pages.js @@ -0,0 +1,49 @@ +'use strict'; + +// GitHub Pages only serves real files, so deep links fall back to 404.html (HTTP 404). +// Emit a copy of index.html for every known route so they are served with HTTP 200. +// 404.html stays as the fallback for anything not listed here. + +const { copyFileSync, mkdirSync, readFileSync } = require('fs'); +const { join } = require('path'); +const themes = require('devextreme-themebuilder/modules/themes').default; + +const distDir = 'dist/devextreme-themebuilder-app'; +const indexFile = join(distDir, 'index.html'); + +const menuRoutes = [...readFileSync('src/app/left-menu/left-menu.aliases.ts', 'utf8') + .matchAll(/route:\s*'([^']+)'/g)].map((m) => m[1]); +if(!menuRoutes.length) throw new Error('No routes found in left-menu.aliases.ts'); + +const themeNames = [...new Set(themes.map((t) => t.name))]; + +const routes = [ + 'master', + 'import', + 'import/bootstrap', + 'import/meta', + 'advanced', + ...themeNames.flatMap((name) => [`preview/${name}`, `wizard/${name}`]), + ...themes.flatMap(({ name, colorScheme }) => { + const advanced = `advanced/${name}/${colorScheme}`; + return [ + `master/${name}/${colorScheme}`, + advanced, + `${advanced}/datagrid`, + `${advanced}/treelist`, + `${advanced}/grids/datagrid`, + `${advanced}/grids/treelist`, + ...menuRoutes.map((route) => `${advanced}/${route}`) + ]; + }) +]; + +routes.forEach((route) => { + const dir = join(distDir, route); + mkdirSync(dir, { recursive: true }); + copyFileSync(indexFile, join(dir, 'index.html')); +}); + +copyFileSync(indexFile, join(distDir, '404.html')); + +console.log(`Generated ${routes.length} route pages`); diff --git a/package.json b/package.json index 58e014f0..a46e4106 100644 --- a/package.json +++ b/package.json @@ -5,7 +5,7 @@ "scripts": { "ng": "ng", "start": "ng serve", - "build": "ng build --configuration=production --base-href https://devexpress.github.io/ThemeBuilder/ && node ./copyfile.js", + "build": "ng build --configuration=production --base-href https://devexpress.github.io/ThemeBuilder/ && node ./generate-route-pages.js", "lint-fix": "eslint --fix \"src/**/*.ts\"", "lint": "eslint \"src/**/*.ts\"", "test": "pnpm run lint && pnpm run build" diff --git a/src/app/iframe/iframe.component.ts b/src/app/iframe/iframe.component.ts index 7f51fbe9..6d2565eb 100644 --- a/src/app/iframe/iframe.component.ts +++ b/src/app/iframe/iframe.component.ts @@ -33,7 +33,7 @@ export class IframeComponent implements AfterViewInit, OnDestroy, OnInit { if(this.theme !== params['theme']) { this.loading.show(); this.theme = params['theme']; - this.url = document.getElementsByTagName('base')[0].href + (widget ? 'preview' : 'wizard') + '/' + this.theme; + this.url = document.getElementsByTagName('base')[0].href + (widget ? 'preview' : 'wizard') + '/' + this.theme + '/'; if(this.iframe) { this.iframe.nativeElement.src = this.url; } diff --git a/src/app/layouts/header/header.component.css b/src/app/layouts/header/header.component.css index 3c73d6b0..41b22258 100644 --- a/src/app/layouts/header/header.component.css +++ b/src/app/layouts/header/header.component.css @@ -99,8 +99,10 @@ display: flex; } -.switcher-template .switcher-icon.editor { +.switcher-icon.editor { margin-left: 11px; + padding: 3px; + height: 100%; display: flex; align-items: center; } diff --git a/src/app/layouts/header/header.component.html b/src/app/layouts/header/header.component.html index d98cef2a..cb504b8f 100644 --- a/src/app/layouts/header/header.component.html +++ b/src/app/layouts/header/header.component.html @@ -17,9 +17,9 @@ stylingMode="filled" [grouped]="true" groupTemplate="group" - displayExpr="text" + [displayExpr]="getDisplayText" valueExpr="themeId" - fieldTemplate="field" + [fieldAddons]="{ beforeTemplate: 'fieldIcon' }" [(value)]="currentThemeId" (onValueChanged)="themeChanged($event)" > @@ -43,14 +43,10 @@ -
-
+
+ @if (data) { - -
+ }
diff --git a/src/app/layouts/header/header.component.ts b/src/app/layouts/header/header.component.ts index d61e4001..b8ce7220 100644 --- a/src/app/layouts/header/header.component.ts +++ b/src/app/layouts/header/header.component.ts @@ -29,6 +29,12 @@ export class HeaderComponent implements OnInit, OnDestroy { subscription: Subscription; currentThemeId: number; + getDisplayText = (item: { name: string; text: string } | null): string => { + if(!item) return ''; + const name = item.name.toLowerCase().replace(/^\w/, (c) => c.toUpperCase()); + return `${name} ${item.text}`; + }; + constructor(private metadataService: MetadataRepositoryService, private route: Router) {} themeChanged(e): void { diff --git a/src/app/meta-repository.service.ts b/src/app/meta-repository.service.ts index 586c070e..afee813a 100644 --- a/src/app/meta-repository.service.ts +++ b/src/app/meta-repository.service.ts @@ -13,6 +13,7 @@ import { Metadata } from './types/metadata'; export class MetadataRepositoryService { private modifiedMetaCollection: ExportedItem[] = []; private metadata: Metadata; + private metadataPromise: Promise | null = null; theme: Theme = { name: 'generic', colorScheme: 'light' }; css = new BehaviorSubject(''); @@ -57,14 +58,17 @@ export class MetadataRepositoryService { } private getMetadata(): Promise { - if(this.metadata) { - return Promise.resolve(this.metadata); + if(this.metadataPromise === null) { + this.metadataPromise = this.themeBuilder.getMetadata().then((metadata) => { + this.metadata = metadata; + return metadata; + }, (error) => { + this.metadataPromise = null; + throw error; + }); } - return this.themeBuilder.getMetadata().then((metadata) => { - if(!this.metadata) this.metadata = metadata; - return metadata; - }); + return this.metadataPromise; } getData(): Promise { @@ -194,24 +198,10 @@ export class MetadataRepositoryService { } getVersion(): Promise { - if(this.metadata) { - return Promise.resolve(this.metadata.version); - } - - return this.getMetadata().then((metadata) => { - if(!this.metadata) this.metadata = metadata; - return metadata.version; - }); + return this.getMetadata().then((metadata) => metadata.version); } getThemes(): Promise { - if(this.metadata) { - return Promise.resolve(this.metadata.themes); - } - - return this.getMetadata().then((metadata) => { - if(!this.metadata) this.metadata = metadata; - return metadata.themes; - }); + return this.getMetadata().then((metadata) => metadata.themes); } }