@genesislcap/blank-app-seed 5.18.0 → 5.19.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.genx/configure.js +4 -2
- package/.genx/package.json +2 -2
- package/.genx/utils/index.js +2 -0
- package/.genx/utils/toModalThemeFormat.js +74 -0
- package/.genx/versions.json +1 -1
- package/CHANGELOG.md +14 -0
- package/client-tmp/angular/README.md +9 -1
- package/client-tmp/angular/package.json +0 -1
- package/client-tmp/angular/src/app/layouts/blank/blank.layout.ts +12 -3
- package/client-tmp/angular/src/app/layouts/default/default.layout.html +1 -1
- package/client-tmp/angular/src/app/layouts/default/default.layout.ts +17 -13
- package/client-tmp/angular/src/jest.setup.ts +20 -0
- package/client-tmp/angular/src/styles/active-theme.ts +6 -0
- package/client-tmp/angular/src/styles/default.theme.json +37 -0
- package/client-tmp/react/README.md +9 -1
- package/client-tmp/react/package.json +0 -1
- package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +9 -4
- package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +15 -12
- package/client-tmp/react/src/styles/active-theme.ts +6 -0
- package/client-tmp/react/src/styles/default.theme.json +37 -0
- package/client-tmp/web-components/README.md +8 -7
- package/client-tmp/web-components/package.json +0 -1
- package/client-tmp/web-components/src/layouts/default.ts +2 -1
- package/client-tmp/web-components/src/main/main.ts +14 -11
- package/client-tmp/web-components/src/styles/active-theme.ts +6 -0
- package/client-tmp/web-components/src/styles/default.theme.json +37 -0
- package/package.json +1 -1
- package/client-tmp/angular/src/styles/design-tokens.json +0 -60
- package/client-tmp/react/src/styles/design-tokens.json +0 -60
- package/client-tmp/web-components/src/styles/design-tokens.json +0 -60
package/.genx/configure.js
CHANGED
|
@@ -18,6 +18,7 @@ const {
|
|
|
18
18
|
deleteGradleWrappers,
|
|
19
19
|
generateStore,
|
|
20
20
|
fontUtils,
|
|
21
|
+
toModalThemeFormat,
|
|
21
22
|
} = require('./utils');
|
|
22
23
|
|
|
23
24
|
/**
|
|
@@ -52,8 +53,9 @@ module.exports = async (data, utils) => {
|
|
|
52
53
|
if (data.designTokens && Object.keys(data.designTokens).length > 0) {
|
|
53
54
|
try {
|
|
54
55
|
const frameworkDir = FRAMEWORKS_DIR_MAP.get(data.framework);
|
|
55
|
-
const templateFile = path.join(__dirname, '..', DIR_CLIENT_TEMP_ALIAS, frameworkDir, 'src/styles/
|
|
56
|
-
|
|
56
|
+
const templateFile = path.join(__dirname, '..', DIR_CLIENT_TEMP_ALIAS, frameworkDir, 'src/styles/default.theme.json');
|
|
57
|
+
// Legacy designTokens payloads are converted to the modal theme format; modal payloads pass through verbatim
|
|
58
|
+
const jsonContent = JSON.stringify(toModalThemeFormat(data.designTokens), null, 2);
|
|
57
59
|
const templateDir = path.dirname(templateFile);
|
|
58
60
|
if (!fs.existsSync(templateDir)) {
|
|
59
61
|
fs.mkdirSync(templateDir, { recursive: true });
|
package/.genx/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@genesislcap/blank-app-seed-config",
|
|
3
3
|
"description": "Genesis Blank App Seed Configuration",
|
|
4
|
-
"version": "5.
|
|
4
|
+
"version": "5.19.0",
|
|
5
5
|
"license": "Apache-2.0",
|
|
6
6
|
"scripts": {
|
|
7
7
|
"lint": "eslint .",
|
|
@@ -51,7 +51,7 @@
|
|
|
51
51
|
".iml"
|
|
52
52
|
],
|
|
53
53
|
"exclude": [
|
|
54
|
-
"
|
|
54
|
+
"default.theme.json"
|
|
55
55
|
]
|
|
56
56
|
}
|
|
57
57
|
}
|
package/.genx/utils/index.js
CHANGED
|
@@ -9,6 +9,7 @@ const makeDirectory = require('./makeDirectory');
|
|
|
9
9
|
const normalizeFrameworkAlias = require('./normalizeFrameworkAlias');
|
|
10
10
|
const parseJSONArgument = require('./parseJSONArgument');
|
|
11
11
|
const registerPartials = require('./registerPartials');
|
|
12
|
+
const toModalThemeFormat = require('./toModalThemeFormat');
|
|
12
13
|
const validateRoute = require('./validateRoute');
|
|
13
14
|
const validateFrameworkAlias = require('./validateFrameworkAlias');
|
|
14
15
|
const fontUtils = require('./fontUtils');
|
|
@@ -25,6 +26,7 @@ module.exports = {
|
|
|
25
26
|
normalizeFrameworkAlias,
|
|
26
27
|
parseJSONArgument,
|
|
27
28
|
registerPartials,
|
|
29
|
+
toModalThemeFormat,
|
|
28
30
|
validateFrameworkAlias,
|
|
29
31
|
validateRoute,
|
|
30
32
|
fontUtils,
|
|
@@ -0,0 +1,74 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Flattens legacy nested customTokens (`{ ag: { row: { height: { $value: '28px' } } } }`)
|
|
3
|
+
* to the modal theme's flat CSS custom property map (`{ '--ag-row-height': '28px' }`).
|
|
4
|
+
* A leaf is an object carrying `$value`, or a plain scalar.
|
|
5
|
+
*/
|
|
6
|
+
const flattenCustomTokens = (customTokens, prefix = '') => {
|
|
7
|
+
const flat = {};
|
|
8
|
+
Object.entries(customTokens).forEach(([key, value]) => {
|
|
9
|
+
const name = prefix ? `${prefix}-${key}` : key;
|
|
10
|
+
if (value && typeof value === 'object') {
|
|
11
|
+
if ('$value' in value) {
|
|
12
|
+
flat[`--${name}`] = String(value.$value);
|
|
13
|
+
} else {
|
|
14
|
+
Object.assign(flat, flattenCustomTokens(value, name));
|
|
15
|
+
}
|
|
16
|
+
} else if (value !== undefined && value !== null) {
|
|
17
|
+
flat[`--${name}`] = String(value);
|
|
18
|
+
}
|
|
19
|
+
});
|
|
20
|
+
return flat;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Converts a designTokens payload to the modal theme FORMAT.
|
|
25
|
+
* - Already modal (has `modes`): returned verbatim.
|
|
26
|
+
* - Legacy (`design_tokens` at the top level): converted to an adaptive modal theme —
|
|
27
|
+
* everything except the `mode` group becomes shared, both light and dark modes are
|
|
28
|
+
* defined, nested customTokens are flattened, and `header_logo`/`custom_fonts` are
|
|
29
|
+
* preserved as top-level keys.
|
|
30
|
+
* - Anything else: returned verbatim.
|
|
31
|
+
*
|
|
32
|
+
* Not to be confused with `toModalTheme` from `@genesislcap/rapid-design-system`, which
|
|
33
|
+
* VALIDATES an already-modal theme at app startup (and throws on legacy shapes); this
|
|
34
|
+
* utility is the generation-time converter that produces that modal shape from legacy input.
|
|
35
|
+
*/
|
|
36
|
+
const toModalThemeFormat = (designTokens) => {
|
|
37
|
+
if (designTokens.modes || !designTokens.design_tokens) {
|
|
38
|
+
return designTokens;
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
const { mode, ...sharedDesignTokens } = designTokens.design_tokens;
|
|
42
|
+
const theme = {
|
|
43
|
+
id: 'default',
|
|
44
|
+
colorStrategy: 'adaptive',
|
|
45
|
+
defaultMode: mode?.luminance?.$value < 0.5 ? 'dark' : 'light',
|
|
46
|
+
};
|
|
47
|
+
if (designTokens.header_logo !== undefined) {
|
|
48
|
+
theme.header_logo = designTokens.header_logo;
|
|
49
|
+
}
|
|
50
|
+
if (designTokens.custom_fonts !== undefined) {
|
|
51
|
+
theme.custom_fonts = designTokens.custom_fonts;
|
|
52
|
+
}
|
|
53
|
+
theme.shared = {
|
|
54
|
+
design_tokens: sharedDesignTokens,
|
|
55
|
+
};
|
|
56
|
+
if (designTokens.customTokens) {
|
|
57
|
+
theme.shared.customTokens = flattenCustomTokens(designTokens.customTokens);
|
|
58
|
+
}
|
|
59
|
+
theme.modes = {
|
|
60
|
+
light: {
|
|
61
|
+
design_tokens: {
|
|
62
|
+
mode: { luminance: { $value: 1, $type: 'number' } },
|
|
63
|
+
},
|
|
64
|
+
},
|
|
65
|
+
dark: {
|
|
66
|
+
design_tokens: {
|
|
67
|
+
mode: { luminance: { $value: 0.23, $type: 'number' } },
|
|
68
|
+
},
|
|
69
|
+
},
|
|
70
|
+
};
|
|
71
|
+
return theme;
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
module.exports = toModalThemeFormat;
|
package/.genx/versions.json
CHANGED
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,19 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [5.19.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.18.0...v5.19.0) (2026-07-10)
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
### Features
|
|
7
|
+
|
|
8
|
+
* adopt FUI modal theme API for theme selection GENC-0 (#602) 8ee122a
|
|
9
|
+
* apply UX default theme to the seed default GENC-0 e6e68de
|
|
10
|
+
* migrate theming to the FUI modal theme API ([FUI-2575](https://github.com/genesiscommunitysuccess/blank-app-seed/issues/2575)) GENC-0 60fd54a
|
|
11
|
+
|
|
12
|
+
|
|
13
|
+
### Bug Fixes
|
|
14
|
+
|
|
15
|
+
* align modal theming with FUI review-fixes PR 2384 GENC-0 f326bca
|
|
16
|
+
|
|
3
17
|
## [5.18.0](https://github.com/genesiscommunitysuccess/blank-app-seed/compare/v5.17.3...v5.18.0) (2026-07-08)
|
|
4
18
|
|
|
5
19
|
|
|
@@ -27,6 +27,15 @@ Run `npm run test:e2e` to execute the end-to-end tests via a platform of your ch
|
|
|
27
27
|
|
|
28
28
|
Run `npm run lint` to lint the project.
|
|
29
29
|
|
|
30
|
+
## Styling
|
|
31
|
+
|
|
32
|
+
The design system is driven by the theme file `src/styles/default.theme.json`: tokens under `shared` (colours,
|
|
33
|
+
typography, sizing) apply to every mode, while the `modes` section declares the available modes (`light` and `dark` by
|
|
34
|
+
default). `src/styles/active-theme.ts` loads the theme and is used by the layout components to inject the theme styles,
|
|
35
|
+
apply the initial mode, and decide whether the header shows the light/dark toggle. The toggle appears when the theme
|
|
36
|
+
declares more than one mode (set `showModeToggle` in the theme file to override this), cycles through the declared
|
|
37
|
+
modes, and the selected mode is remembered across reloads.
|
|
38
|
+
|
|
30
39
|
## NPM Scripts
|
|
31
40
|
|
|
32
41
|
- `npm run baseline`: Cleans the project and installs dependencies.
|
|
@@ -41,7 +50,6 @@ Run `npm run lint` to lint the project.
|
|
|
41
50
|
- `npm run dev:intellij`: Serves the project in development mode (specific for IntelliJ).
|
|
42
51
|
- `npm run dev:https`: Serves the project in development mode with HTTPS.
|
|
43
52
|
- `npm run dev:webpack`: Serves the project in development mode (specific for webpack configuration).
|
|
44
|
-
- `npm run dsconfig`: Configures design system based on `src/styles/design-tokens.json`.
|
|
45
53
|
- `npm run lint`: Lints the project using the specified profile.
|
|
46
54
|
- `npm run lint:fix`: Fixes linting errors in the project.
|
|
47
55
|
- `npm run lint:eslint`: Lints the project using ESLint and the specified profile.
|
|
@@ -22,7 +22,6 @@
|
|
|
22
22
|
"dev:intellij": "npm run dev",
|
|
23
23
|
"dev:https": "npm run dev -- --https",
|
|
24
24
|
"dev:webpack": "npm run dev",
|
|
25
|
-
"dsconfig": "dsconfig --path src/styles/design-tokens.json",
|
|
26
25
|
"lint": "genx lint --profile",
|
|
27
26
|
"lint:fix": "genx lint --fix",
|
|
28
27
|
"lint:eslint": "genx lint -l eslint --profile",
|
|
@@ -1,6 +1,10 @@
|
|
|
1
1
|
import { Component, ElementRef, ViewChild, AfterViewInit } from '@angular/core';
|
|
2
|
-
import {
|
|
3
|
-
|
|
2
|
+
import {
|
|
3
|
+
applyMode,
|
|
4
|
+
injectThemeStyles,
|
|
5
|
+
resolveInitialMode,
|
|
6
|
+
} from '@genesislcap/rapid-design-system';
|
|
7
|
+
import { activeTheme } from '../../../styles/active-theme';
|
|
4
8
|
|
|
5
9
|
@Component({
|
|
6
10
|
selector: 'app-blank-layout',
|
|
@@ -11,6 +15,11 @@ export class BlankLayoutComponent implements AfterViewInit {
|
|
|
11
15
|
@ViewChild('designSystemProvider') designSystemProviderElement!: ElementRef;
|
|
12
16
|
|
|
13
17
|
ngAfterViewInit() {
|
|
14
|
-
|
|
18
|
+
injectThemeStyles(this.designSystemProviderElement.nativeElement, activeTheme);
|
|
19
|
+
applyMode(
|
|
20
|
+
this.designSystemProviderElement.nativeElement,
|
|
21
|
+
activeTheme,
|
|
22
|
+
resolveInitialMode(activeTheme),
|
|
23
|
+
);
|
|
15
24
|
}
|
|
16
25
|
}
|
|
@@ -1,8 +1,13 @@
|
|
|
1
1
|
import { Component, ViewChild, ElementRef, AfterViewInit } from '@angular/core';
|
|
2
2
|
import { Router } from '@angular/router';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
|
|
3
|
+
import { FoundationRouteNavItem } from '@genesislcap/foundation-ui';
|
|
4
|
+
import {
|
|
5
|
+
applyMode,
|
|
6
|
+
injectThemeStyles,
|
|
7
|
+
nextMode,
|
|
8
|
+
resolveInitialMode,
|
|
9
|
+
} from '@genesislcap/rapid-design-system';
|
|
10
|
+
import { activeTheme, modeToggleEnabled } from '../../../styles/active-theme';
|
|
6
11
|
import { RouteService } from '../../services/route.service';
|
|
7
12
|
import BaseLayout from '../base.layout';
|
|
8
13
|
import { registerStylesTarget } from '../../../pbc/utils';
|
|
@@ -15,6 +20,8 @@ import { registerStylesTarget } from '../../../pbc/utils';
|
|
|
15
20
|
export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit {
|
|
16
21
|
@ViewChild('designSystemProvider') designSystemProviderElement!: ElementRef;
|
|
17
22
|
navItems: FoundationRouteNavItem[] = [];
|
|
23
|
+
modeToggleEnabled = modeToggleEnabled;
|
|
24
|
+
private themeMode = resolveInitialMode(activeTheme);
|
|
18
25
|
|
|
19
26
|
constructor(
|
|
20
27
|
private el: ElementRef,
|
|
@@ -26,10 +33,12 @@ export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit
|
|
|
26
33
|
}
|
|
27
34
|
|
|
28
35
|
ngAfterViewInit() {
|
|
29
|
-
|
|
36
|
+
injectThemeStyles(this.designSystemProviderElement.nativeElement, activeTheme);
|
|
37
|
+
this.themeMode = resolveInitialMode(activeTheme);
|
|
38
|
+
applyMode(this.designSystemProviderElement.nativeElement, activeTheme, this.themeMode);
|
|
30
39
|
registerStylesTarget(this.el.nativeElement, 'layout');
|
|
31
40
|
}
|
|
32
|
-
|
|
41
|
+
|
|
33
42
|
navigateAngular = (path: string) => {
|
|
34
43
|
this.router.navigate([path]);
|
|
35
44
|
};
|
|
@@ -37,14 +46,9 @@ export class DefaultLayoutComponent extends BaseLayout implements AfterViewInit
|
|
|
37
46
|
onLogout = () => {
|
|
38
47
|
this.router.navigate(['/login']);
|
|
39
48
|
};
|
|
40
|
-
|
|
49
|
+
|
|
41
50
|
onLuminanceToogle = (): void => {
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
baseLayerLuminance.getValueFor(this.designSystemProviderElement.nativeElement) ===
|
|
45
|
-
StandardLuminance.DarkMode
|
|
46
|
-
? StandardLuminance.LightMode
|
|
47
|
-
: StandardLuminance.DarkMode,
|
|
48
|
-
);
|
|
51
|
+
this.themeMode = nextMode(activeTheme, this.themeMode);
|
|
52
|
+
applyMode(this.designSystemProviderElement.nativeElement, activeTheme, this.themeMode);
|
|
49
53
|
};
|
|
50
54
|
}
|
|
@@ -44,3 +44,23 @@ Object.defineProperty(window, 'BroadcastChannel', {
|
|
|
44
44
|
writable: true,
|
|
45
45
|
value: MockBroadcastChannel,
|
|
46
46
|
});
|
|
47
|
+
|
|
48
|
+
// jsdom lacks the constructable-stylesheet API used by the modal-theme runtime
|
|
49
|
+
// (CSSStyleSheet.replaceSync + Document.adoptedStyleSheets)
|
|
50
|
+
if (typeof CSSStyleSheet === 'undefined') {
|
|
51
|
+
Object.defineProperty(window, 'CSSStyleSheet', {
|
|
52
|
+
writable: true,
|
|
53
|
+
value: class {
|
|
54
|
+
replaceSync() {}
|
|
55
|
+
},
|
|
56
|
+
});
|
|
57
|
+
} else if (!CSSStyleSheet.prototype.replaceSync) {
|
|
58
|
+
CSSStyleSheet.prototype.replaceSync = () => {};
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
if (!('adoptedStyleSheets' in document)) {
|
|
62
|
+
Object.defineProperty(document, 'adoptedStyleSheets', {
|
|
63
|
+
writable: true,
|
|
64
|
+
value: [],
|
|
65
|
+
});
|
|
66
|
+
}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "default",
|
|
3
|
+
"colorStrategy": "adaptive",
|
|
4
|
+
"defaultMode": "dark",
|
|
5
|
+
"shared": {
|
|
6
|
+
"design_tokens": {
|
|
7
|
+
"fontFamily": {
|
|
8
|
+
"bodyFont": {
|
|
9
|
+
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
10
|
+
"$type": "fontFamily"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"modes": {
|
|
16
|
+
"light": {
|
|
17
|
+
"design_tokens": {
|
|
18
|
+
"mode": {
|
|
19
|
+
"luminance": {
|
|
20
|
+
"$value": 1,
|
|
21
|
+
"$type": "number"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dark": {
|
|
27
|
+
"design_tokens": {
|
|
28
|
+
"mode": {
|
|
29
|
+
"luminance": {
|
|
30
|
+
"$value": 0.23,
|
|
31
|
+
"$type": "number"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -29,6 +29,15 @@ Run `npm run test:e2e` to execute the end-to-end tests.
|
|
|
29
29
|
|
|
30
30
|
Run `npm run lint` to lint the project.
|
|
31
31
|
|
|
32
|
+
## Styling
|
|
33
|
+
|
|
34
|
+
The design system is driven by the theme file `src/styles/default.theme.json`: tokens under `shared` (colours,
|
|
35
|
+
typography, sizing) apply to every mode, while the `modes` section declares the available modes (`light` and `dark` by
|
|
36
|
+
default). `src/styles/active-theme.ts` loads the theme and is used by the layout components to inject the theme styles,
|
|
37
|
+
apply the initial mode, and decide whether the header shows the light/dark toggle. The toggle appears when the theme
|
|
38
|
+
declares more than one mode (set `showModeToggle` in the theme file to override this), cycles through the declared
|
|
39
|
+
modes, and the selected mode is remembered across reloads.
|
|
40
|
+
|
|
32
41
|
## NPM Scripts
|
|
33
42
|
|
|
34
43
|
- `npm run baseline`: Cleans the project and installs dependencies.
|
|
@@ -43,7 +52,6 @@ Run `npm run lint` to lint the project.
|
|
|
43
52
|
- `npm run dev:no-open`: Serves the project in development mode without automatically opening the browser.
|
|
44
53
|
- `npm run dev:intellij`: Serves the project in development mode (specific for IntelliJ).
|
|
45
54
|
- `npm run dev:https`: Serves the project in development mode with HTTPS.
|
|
46
|
-
- `npm run dsconfig`: Configures design system based on `src/styles/design-tokens.json`.
|
|
47
55
|
- `npm run lint`: Lints the project using the specified profile.
|
|
48
56
|
- `npm run lint:fix`: Fixes linting errors in the project.
|
|
49
57
|
- `npm run lint:eslint`: Lints the project using ESLint and the specified profile.
|
|
@@ -20,7 +20,6 @@
|
|
|
20
20
|
"dev:no-open": "NO_OPEN=true npm run dev",
|
|
21
21
|
"dev:intellij": "npm run dev",
|
|
22
22
|
"dev:https": "HTTPS=true npm run dev",
|
|
23
|
-
"dsconfig": "dsconfig --path src/styles/design-tokens.json",
|
|
24
23
|
"lint": "genx lint --profile",
|
|
25
24
|
"lint:fix": "genx lint --fix",
|
|
26
25
|
"lint:eslint": "genx lint -l eslint --profile",
|
|
@@ -1,7 +1,11 @@
|
|
|
1
1
|
import React, { ReactNode, useEffect, useRef } from 'react';
|
|
2
|
-
import {
|
|
2
|
+
import {
|
|
3
|
+
applyMode,
|
|
4
|
+
injectThemeStyles,
|
|
5
|
+
resolveInitialMode,
|
|
6
|
+
} from '@genesislcap/rapid-design-system';
|
|
3
7
|
import styles from './BlankLayout.module.css';
|
|
4
|
-
import
|
|
8
|
+
import { activeTheme } from '../../styles/active-theme';
|
|
5
9
|
|
|
6
10
|
interface BlankLayoutProps {
|
|
7
11
|
children: ReactNode;
|
|
@@ -12,12 +16,13 @@ const BlankLayout: React.FC<BlankLayoutProps> = ({ children }) =>{
|
|
|
12
16
|
|
|
13
17
|
useEffect(() => {
|
|
14
18
|
if (designSystemProviderRef.current) {
|
|
15
|
-
|
|
19
|
+
injectThemeStyles(designSystemProviderRef.current, activeTheme);
|
|
20
|
+
applyMode(designSystemProviderRef.current, activeTheme, resolveInitialMode(activeTheme));
|
|
16
21
|
}
|
|
17
22
|
}, []);
|
|
18
23
|
|
|
19
24
|
return (
|
|
20
|
-
<rapid-design-system-provider className={styles['blank-layout']}>
|
|
25
|
+
<rapid-design-system-provider ref={designSystemProviderRef} className={styles['blank-layout']}>
|
|
21
26
|
<section className={styles.content}>{children}</section>
|
|
22
27
|
</rapid-design-system-provider>
|
|
23
28
|
);
|
|
@@ -1,11 +1,16 @@
|
|
|
1
1
|
import React, { useEffect, useRef, useSyncExternalStore } from 'react';
|
|
2
2
|
import { RouteObject, useNavigate, Outlet } from 'react-router-dom';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
3
|
+
import { getNavItems } from '@genesislcap/foundation-ui';
|
|
4
|
+
import {
|
|
5
|
+
applyMode,
|
|
6
|
+
injectThemeStyles,
|
|
7
|
+
nextMode,
|
|
8
|
+
resolveInitialMode,
|
|
9
|
+
} from '@genesislcap/rapid-design-system';
|
|
5
10
|
import styles from './DefaultLayout.module.css';
|
|
6
11
|
import PBCElementsRenderer from '../../pbc/elementsRenderer';
|
|
7
12
|
import { registerStylesTarget } from '../../pbc/utils';
|
|
8
|
-
import
|
|
13
|
+
import { activeTheme, modeToggleEnabled } from '../../styles/active-theme';
|
|
9
14
|
import { useRoutesContext } from '../../store/RoutesContext';
|
|
10
15
|
import { useDocumentTitle } from '../../utils/useDocumentTitle';
|
|
11
16
|
import { LOGOUT_URL } from '@genesislcap/foundation-utils';
|
|
@@ -41,6 +46,7 @@ const getSnapshot = () => connect.isConnected;
|
|
|
41
46
|
const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
|
|
42
47
|
const navigate = useNavigate();
|
|
43
48
|
const designSystemProviderRef = useRef<HTMLElement>(null);
|
|
49
|
+
const themeModeRef = useRef<string>(resolveInitialMode(activeTheme));
|
|
44
50
|
const routes = useRoutesContext() as ExtendedRouteObject[];
|
|
45
51
|
const navItems = getNavItems(
|
|
46
52
|
routes.flatMap((route) => ({
|
|
@@ -53,13 +59,8 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
|
|
|
53
59
|
|
|
54
60
|
const onLuminanceToggle = (): void => {
|
|
55
61
|
if (designSystemProviderRef.current) {
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
baseLayerLuminance.getValueFor(designSystemProviderRef.current) ===
|
|
59
|
-
StandardLuminance.DarkMode
|
|
60
|
-
? StandardLuminance.LightMode
|
|
61
|
-
: StandardLuminance.DarkMode,
|
|
62
|
-
);
|
|
62
|
+
themeModeRef.current = nextMode(activeTheme, themeModeRef.current);
|
|
63
|
+
applyMode(designSystemProviderRef.current, activeTheme, themeModeRef.current);
|
|
63
64
|
}
|
|
64
65
|
};
|
|
65
66
|
|
|
@@ -67,7 +68,9 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
|
|
|
67
68
|
|
|
68
69
|
useEffect(() => {
|
|
69
70
|
if (designSystemProviderRef.current) {
|
|
70
|
-
|
|
71
|
+
injectThemeStyles(designSystemProviderRef.current, activeTheme);
|
|
72
|
+
themeModeRef.current = resolveInitialMode(activeTheme);
|
|
73
|
+
applyMode(designSystemProviderRef.current, activeTheme, themeModeRef.current);
|
|
71
74
|
registerStylesTarget(document.body, 'layout');
|
|
72
75
|
registerStylesTarget(document.body, 'header');
|
|
73
76
|
registerStylesTarget(document.body, 'content');
|
|
@@ -90,7 +93,7 @@ const DefaultLayout: React.FC<DefaultLayoutProps> = () => {
|
|
|
90
93
|
await fetch(LOGOUT_URL);
|
|
91
94
|
window.location.reload();
|
|
92
95
|
}}
|
|
93
|
-
show-luminance-toggle-button
|
|
96
|
+
show-luminance-toggle-button={modeToggleEnabled}
|
|
94
97
|
show-misc-toggle-button
|
|
95
98
|
routeNavItems={navItems}
|
|
96
99
|
navigateTo={(path: string) => navigate(path)}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "default",
|
|
3
|
+
"colorStrategy": "adaptive",
|
|
4
|
+
"defaultMode": "dark",
|
|
5
|
+
"shared": {
|
|
6
|
+
"design_tokens": {
|
|
7
|
+
"fontFamily": {
|
|
8
|
+
"bodyFont": {
|
|
9
|
+
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
10
|
+
"$type": "fontFamily"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"modes": {
|
|
16
|
+
"light": {
|
|
17
|
+
"design_tokens": {
|
|
18
|
+
"mode": {
|
|
19
|
+
"luminance": {
|
|
20
|
+
"$value": 1,
|
|
21
|
+
"$type": "number"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dark": {
|
|
27
|
+
"design_tokens": {
|
|
28
|
+
"mode": {
|
|
29
|
+
"luminance": {
|
|
30
|
+
"$value": 0.23,
|
|
31
|
+
"$type": "number"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
|
@@ -47,15 +47,16 @@ npm run baseline
|
|
|
47
47
|
## Styling
|
|
48
48
|
|
|
49
49
|
Genesis components are registered with a Design System,
|
|
50
|
-
and the default system is named `rapid`.
|
|
51
|
-
|
|
52
|
-
|
|
50
|
+
and the default system is named `rapid`. The design system is driven by the theme file `src/styles/default.theme.json`:
|
|
51
|
+
tokens under `shared` (colours, typography, sizing) apply to every mode, while the `modes` section declares the
|
|
52
|
+
available modes (`light` and `dark` by default) with their per-mode tokens.
|
|
53
53
|
|
|
54
|
-
|
|
54
|
+
`src/styles/active-theme.ts` loads the theme file and exposes it as the app's active theme. It is the single source of
|
|
55
|
+
truth used to inject the theme styles and apply the initial mode at startup (`src/main/main.ts`), and to decide whether
|
|
56
|
+
the header shows the light/dark toggle (`src/layouts/default.ts`).
|
|
55
57
|
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
```
|
|
58
|
+
The toggle appears automatically when the theme declares more than one mode (set `showModeToggle` in the theme file to
|
|
59
|
+
override this). Clicking it cycles through the declared modes, and the selected mode is remembered across reloads.
|
|
59
60
|
|
|
60
61
|
## Custom Elements IntelliSense
|
|
61
62
|
|
|
@@ -35,7 +35,6 @@
|
|
|
35
35
|
"dev:https": "npm run dev -- -- --https",
|
|
36
36
|
"dev:vite": "genx dev -e API_HOST,ENABLE_SSO -b vite",
|
|
37
37
|
"dev:webpack": "genx dev -e API_HOST,ENABLE_SSO -b webpack",
|
|
38
|
-
"dsconfig": "dsconfig --path src/styles/design-tokens.json",
|
|
39
38
|
"git:setup": "cd .. && npx --yes husky install",
|
|
40
39
|
"lint": "genx lint --profile",
|
|
41
40
|
"lint:fix": "genx lint --fix",
|
|
@@ -2,6 +2,7 @@ import { getApp } from '@genesislcap/foundation-shell/app';
|
|
|
2
2
|
import type { FoundationRouter } from '@genesislcap/foundation-ui';
|
|
3
3
|
import { css, GenesisElementLayout, html } from '@genesislcap/web-core';
|
|
4
4
|
import type { Store } from '../store/foundation-store';
|
|
5
|
+
import { modeToggleEnabled } from '../styles/active-theme';
|
|
5
6
|
|
|
6
7
|
type ClientAppRouter = FoundationRouter & { store: Store };
|
|
7
8
|
|
|
@@ -43,7 +44,7 @@ export const defaultLayout = new GenesisElementLayout(
|
|
|
43
44
|
{{#if headerLogoSrc}}
|
|
44
45
|
logo-src="{{headerLogoSrc}}"
|
|
45
46
|
{{/if}}
|
|
46
|
-
show-luminance-toggle-button
|
|
47
|
+
?show-luminance-toggle-button=${() => modeToggleEnabled}
|
|
47
48
|
show-misc-toggle-button
|
|
48
49
|
:routeNavItems=${(x) => x.config.getNavItems()}
|
|
49
50
|
></foundation-header>
|
|
@@ -2,9 +2,13 @@ import { Connect, ConnectConfig, defaultConnectConfig } from '@genesislcap/found
|
|
|
2
2
|
import { EventEmitter } from '@genesislcap/foundation-events';
|
|
3
3
|
import { App } from '@genesislcap/foundation-shell/app';
|
|
4
4
|
import { importPBCAssets } from '@genesislcap/foundation-shell/pbc';
|
|
5
|
-
import { configureDesignSystem } from '@genesislcap/foundation-ui';
|
|
6
5
|
import {
|
|
7
|
-
|
|
6
|
+
applyMode,
|
|
7
|
+
injectThemeStyles,
|
|
8
|
+
nextMode,
|
|
9
|
+
resolveInitialMode,
|
|
10
|
+
} from '@genesislcap/rapid-design-system';
|
|
11
|
+
import {
|
|
8
12
|
customElement,
|
|
9
13
|
Container,
|
|
10
14
|
DefaultRouteRecognizer,
|
|
@@ -13,12 +17,11 @@ import {
|
|
|
13
17
|
inject,
|
|
14
18
|
observable,
|
|
15
19
|
Registration,
|
|
16
|
-
StandardLuminance,
|
|
17
20
|
} from '@genesislcap/web-core';
|
|
18
21
|
import * as Components from '../components';
|
|
19
22
|
import { MainRouterConfig } from '../routes';
|
|
20
23
|
import { Store, StoreEventDetailMap } from '../store/foundation-store';
|
|
21
|
-
import
|
|
24
|
+
import { activeTheme } from '../styles/active-theme';
|
|
22
25
|
{{#if FDC3.channels.length}}
|
|
23
26
|
import { listenToChannel, onFDC3Ready } from '../utils';
|
|
24
27
|
{{/if}}
|
|
@@ -48,6 +51,8 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
|
|
|
48
51
|
@observable ready: boolean = false;
|
|
49
52
|
@observable data: any = null;
|
|
50
53
|
|
|
54
|
+
private themeMode: string = resolveInitialMode(activeTheme);
|
|
55
|
+
|
|
51
56
|
async connectedCallback() {
|
|
52
57
|
this.registerDIDependencies();
|
|
53
58
|
super.connectedCallback();
|
|
@@ -59,7 +64,9 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
|
|
|
59
64
|
onFDC3Ready(this.FDC3ReadyHandler);
|
|
60
65
|
{{/if}}
|
|
61
66
|
DOM.queueUpdate(() => {
|
|
62
|
-
|
|
67
|
+
injectThemeStyles(this.provider, activeTheme);
|
|
68
|
+
this.themeMode = resolveInitialMode(activeTheme);
|
|
69
|
+
applyMode(this.provider, activeTheme, this.themeMode);
|
|
63
70
|
});
|
|
64
71
|
}
|
|
65
72
|
|
|
@@ -70,12 +77,8 @@ export class MainApplication extends EventEmitter<StoreEventDetailMap>(GenesisEl
|
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
onDarkModeToggle() {
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
baseLayerLuminance.getValueFor(this.provider) === StandardLuminance.DarkMode
|
|
76
|
-
? StandardLuminance.LightMode
|
|
77
|
-
: StandardLuminance.DarkMode,
|
|
78
|
-
);
|
|
80
|
+
this.themeMode = nextMode(activeTheme, this.themeMode);
|
|
81
|
+
applyMode(this.provider, activeTheme, this.themeMode);
|
|
79
82
|
}
|
|
80
83
|
|
|
81
84
|
async loadPBCs() {
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
{
|
|
2
|
+
"id": "default",
|
|
3
|
+
"colorStrategy": "adaptive",
|
|
4
|
+
"defaultMode": "dark",
|
|
5
|
+
"shared": {
|
|
6
|
+
"design_tokens": {
|
|
7
|
+
"fontFamily": {
|
|
8
|
+
"bodyFont": {
|
|
9
|
+
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
10
|
+
"$type": "fontFamily"
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
},
|
|
15
|
+
"modes": {
|
|
16
|
+
"light": {
|
|
17
|
+
"design_tokens": {
|
|
18
|
+
"mode": {
|
|
19
|
+
"luminance": {
|
|
20
|
+
"$value": 1,
|
|
21
|
+
"$type": "number"
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
},
|
|
26
|
+
"dark": {
|
|
27
|
+
"design_tokens": {
|
|
28
|
+
"mode": {
|
|
29
|
+
"luminance": {
|
|
30
|
+
"$value": 0.23,
|
|
31
|
+
"$type": "number"
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
}
|
package/package.json
CHANGED
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"design_tokens": {
|
|
3
|
-
"color": {
|
|
4
|
-
"accent": {
|
|
5
|
-
"$value": "#0EAFE2",
|
|
6
|
-
"$type": "color"
|
|
7
|
-
},
|
|
8
|
-
"neutral": {
|
|
9
|
-
"$value": "#7C909B",
|
|
10
|
-
"$type": "color"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"fontFamily": {
|
|
14
|
-
"bodyFont": {
|
|
15
|
-
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
16
|
-
"$type": "fontFamily"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"typography": {
|
|
20
|
-
"baseFontSize": {
|
|
21
|
-
"$value": "14px",
|
|
22
|
-
"$type": "dimension"
|
|
23
|
-
},
|
|
24
|
-
"baseLineHeight": {
|
|
25
|
-
"$value": "20px",
|
|
26
|
-
"$type": "dimension"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"mode": {
|
|
30
|
-
"luminance": {
|
|
31
|
-
"$value": 0.23,
|
|
32
|
-
"$type": "number"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"style": {
|
|
36
|
-
"density": {
|
|
37
|
-
"$value": 0,
|
|
38
|
-
"$type": "number"
|
|
39
|
-
},
|
|
40
|
-
"baseHeightMultiplier": {
|
|
41
|
-
"$value": 10,
|
|
42
|
-
"$type": "number"
|
|
43
|
-
},
|
|
44
|
-
"borderRadius": {
|
|
45
|
-
"$value": 4,
|
|
46
|
-
"$type": "number"
|
|
47
|
-
},
|
|
48
|
-
"strokeWidth": {
|
|
49
|
-
"$value": 1,
|
|
50
|
-
"$type": "number"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"space": {
|
|
54
|
-
"designUnit": {
|
|
55
|
-
"$value": 4,
|
|
56
|
-
"$type": "number"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"design_tokens": {
|
|
3
|
-
"color": {
|
|
4
|
-
"accent": {
|
|
5
|
-
"$value": "#0EAFE2",
|
|
6
|
-
"$type": "color"
|
|
7
|
-
},
|
|
8
|
-
"neutral": {
|
|
9
|
-
"$value": "#7C909B",
|
|
10
|
-
"$type": "color"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"fontFamily": {
|
|
14
|
-
"bodyFont": {
|
|
15
|
-
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
16
|
-
"$type": "fontFamily"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"typography": {
|
|
20
|
-
"baseFontSize": {
|
|
21
|
-
"$value": "14px",
|
|
22
|
-
"$type": "dimension"
|
|
23
|
-
},
|
|
24
|
-
"baseLineHeight": {
|
|
25
|
-
"$value": "20px",
|
|
26
|
-
"$type": "dimension"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"mode": {
|
|
30
|
-
"luminance": {
|
|
31
|
-
"$value": 0.23,
|
|
32
|
-
"$type": "number"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"style": {
|
|
36
|
-
"density": {
|
|
37
|
-
"$value": 0,
|
|
38
|
-
"$type": "number"
|
|
39
|
-
},
|
|
40
|
-
"baseHeightMultiplier": {
|
|
41
|
-
"$value": 10,
|
|
42
|
-
"$type": "number"
|
|
43
|
-
},
|
|
44
|
-
"borderRadius": {
|
|
45
|
-
"$value": 4,
|
|
46
|
-
"$type": "number"
|
|
47
|
-
},
|
|
48
|
-
"strokeWidth": {
|
|
49
|
-
"$value": 1,
|
|
50
|
-
"$type": "number"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"space": {
|
|
54
|
-
"designUnit": {
|
|
55
|
-
"$value": 4,
|
|
56
|
-
"$type": "number"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|
|
@@ -1,60 +0,0 @@
|
|
|
1
|
-
{
|
|
2
|
-
"design_tokens": {
|
|
3
|
-
"color": {
|
|
4
|
-
"accent": {
|
|
5
|
-
"$value": "#0EAFE2",
|
|
6
|
-
"$type": "color"
|
|
7
|
-
},
|
|
8
|
-
"neutral": {
|
|
9
|
-
"$value": "#7C909B",
|
|
10
|
-
"$type": "color"
|
|
11
|
-
}
|
|
12
|
-
},
|
|
13
|
-
"fontFamily": {
|
|
14
|
-
"bodyFont": {
|
|
15
|
-
"$value": "{{#if fontFamily}}{{fontFamily}}, {{/if}}Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif",
|
|
16
|
-
"$type": "fontFamily"
|
|
17
|
-
}
|
|
18
|
-
},
|
|
19
|
-
"typography": {
|
|
20
|
-
"baseFontSize": {
|
|
21
|
-
"$value": "14px",
|
|
22
|
-
"$type": "dimension"
|
|
23
|
-
},
|
|
24
|
-
"baseLineHeight": {
|
|
25
|
-
"$value": "20px",
|
|
26
|
-
"$type": "dimension"
|
|
27
|
-
}
|
|
28
|
-
},
|
|
29
|
-
"mode": {
|
|
30
|
-
"luminance": {
|
|
31
|
-
"$value": 0.23,
|
|
32
|
-
"$type": "number"
|
|
33
|
-
}
|
|
34
|
-
},
|
|
35
|
-
"style": {
|
|
36
|
-
"density": {
|
|
37
|
-
"$value": 0,
|
|
38
|
-
"$type": "number"
|
|
39
|
-
},
|
|
40
|
-
"baseHeightMultiplier": {
|
|
41
|
-
"$value": 10,
|
|
42
|
-
"$type": "number"
|
|
43
|
-
},
|
|
44
|
-
"borderRadius": {
|
|
45
|
-
"$value": 4,
|
|
46
|
-
"$type": "number"
|
|
47
|
-
},
|
|
48
|
-
"strokeWidth": {
|
|
49
|
-
"$value": 1,
|
|
50
|
-
"$type": "number"
|
|
51
|
-
}
|
|
52
|
-
},
|
|
53
|
-
"space": {
|
|
54
|
-
"designUnit": {
|
|
55
|
-
"$value": 4,
|
|
56
|
-
"$type": "number"
|
|
57
|
-
}
|
|
58
|
-
}
|
|
59
|
-
}
|
|
60
|
-
}
|