@genesislcap/blank-app-seed 5.7.0-prerelease.2 → 5.7.0-prerelease.20
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 +114 -0
- package/.genx/package.json +5 -2
- package/.genx/prompts/api.js +25 -0
- package/.genx/prompts/ui.js +12 -0
- package/.genx/prompts.js +8 -2
- package/.genx/static.js +1 -0
- package/.genx/templates/angular/component/component.hbs +31 -3
- package/.genx/templates/angular/entityManager.hbs +17 -6
- package/.genx/templates/angular/grid.hbs +5 -2
- package/.genx/templates/angular/slices/eventing.slice.hbs +41 -0
- package/.genx/templates/angular/store.hbs +16 -0
- package/.genx/templates/react/component/component.hbs +84 -15
- package/.genx/templates/react/entityManager.hbs +11 -17
- package/.genx/templates/react/grid.hbs +5 -9
- package/.genx/templates/react/gridLayout.hbs +4 -4
- package/.genx/templates/react/horizontalLayout.hbs +1 -1
- package/.genx/templates/react/route.hbs +12 -1
- package/.genx/templates/react/slices/eventing.slice.hbs +39 -0
- package/.genx/templates/react/store.hbs +24 -0
- package/.genx/templates/react/tabsLayout.hbs +1 -1
- package/.genx/templates/web-components/component/component.hbs +26 -0
- package/.genx/templates/web-components/entityManager.hbs +17 -9
- package/.genx/templates/web-components/grid.hbs +13 -5
- package/.genx/templates/web-components/slices/eventing.slice.hbs +44 -0
- package/.genx/templates/web-components/store.hbs +18 -0
- package/.genx/utils/fontUtils.js +75 -0
- package/.genx/utils/formatRouteData.js +5 -0
- package/.genx/utils/generateRoute.js +1 -1
- package/.genx/utils/generateStore.js +98 -0
- package/.genx/utils/index.js +4 -0
- package/.genx/versions.json +3 -3
- package/.github/CODEOWNERS +1 -1
- package/.github/pull_request_template.md +6 -0
- package/.github/workflows/upgrade.yml +0 -1
- package/CHANGELOG.md +160 -0
- package/client-tmp/angular/package.json +5 -0
- package/client-tmp/angular/src/app/app.component.ts +2 -31
- package/client-tmp/angular/src/app/app.module.ts +3 -0
- package/client-tmp/angular/src/app/layouts/default/default.layout.css +8 -0
- package/client-tmp/angular/src/app/layouts/default/default.layout.html +3 -0
- package/client-tmp/angular/src/styles/design-tokens.json +1 -1
- package/client-tmp/angular/src/styles/styles.css +15 -1
- package/client-tmp/react/index.html +0 -1
- package/client-tmp/react/package.json +16 -28
- package/client-tmp/react/public/index.html +3 -1
- package/client-tmp/react/src/App.tsx +12 -29
- package/client-tmp/react/src/components/routes/AppRoutes.tsx +4 -4
- package/client-tmp/react/src/config.ts +1 -1
- package/client-tmp/react/src/layouts/blank/BlankLayout.tsx +1 -1
- package/client-tmp/react/src/layouts/default/DefaultLayout.module.css +8 -0
- package/client-tmp/react/src/layouts/default/DefaultLayout.tsx +7 -4
- package/client-tmp/react/src/main.tsx +1 -1
- package/client-tmp/react/src/pages/AuthPage/AuthPage.tsx +1 -1
- package/client-tmp/react/src/pages/NotFoundPage/NotFoundPage.tsx +1 -1
- package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.test.tsx +1 -1
- package/client-tmp/react/src/pages/NotPermittedPage/NotPermittedPage.tsx +1 -1
- package/client-tmp/react/src/pbc/container.tsx +1 -1
- package/client-tmp/react/src/share/foundation-login.ts +2 -2
- package/client-tmp/react/src/store/RoutesContext.tsx +6 -6
- package/client-tmp/react/src/styles/design-tokens.json +1 -1
- package/client-tmp/react/src/styles/styles.css +15 -1
- package/client-tmp/react/src/utils/getLayoutNameByRoute.ts +1 -1
- package/client-tmp/react/src/utils/goldenLayout.helper.ts +59 -0
- package/client-tmp/react/test/e2e/fixture.ts +1 -1
- package/client-tmp/web-components/package.json +4 -0
- package/client-tmp/web-components/src/layouts/default.ts +12 -2
- package/client-tmp/web-components/src/main/main.css +12 -0
- package/client-tmp/web-components/src/main/main.template.ts +0 -1
- package/client-tmp/web-components/src/main/main.ts +1 -34
- package/client-tmp/web-components/src/styles/design-tokens.json +1 -1
- package/client-tmp/web-components/src/styles/typography.ts +6 -4
- package/package.json +1 -1
- package/client-tmp/angular/src/app/store/store.ts +0 -34
- package/client-tmp/react/lint-css.ts +0 -19
- package/client-tmp/react/src/services/store.service.ts +0 -48
- package/client-tmp/react/vite.config.ts +0 -70
- package/client-tmp/web-components/src/store/store.ts +0 -29
|
@@ -3,7 +3,7 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
|
|
|
3
3
|
{{/if}}
|
|
4
4
|
<rapid-grid-pro
|
|
5
5
|
header-case-type="capitalCase"
|
|
6
|
-
{{#if config.useOnlyTemplateCols
|
|
6
|
+
{{#if config.useOnlyTemplateCols}}
|
|
7
7
|
only-template-col-defs
|
|
8
8
|
{{/if}}
|
|
9
9
|
enable-row-flashing
|
|
@@ -11,16 +11,12 @@ hasUserPermission('{{config.permissions.viewRight}}') ? (
|
|
|
11
11
|
>
|
|
12
12
|
<grid-pro-genesis-datasource
|
|
13
13
|
resource-name="{{config.resourceName}}"
|
|
14
|
-
{{#if config.snapshot
|
|
14
|
+
{{#if config.snapshot}}
|
|
15
15
|
isSnapshot="{{config.snapshot}}"
|
|
16
16
|
{{/if}}
|
|
17
|
-
{{#
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
{{/if}}
|
|
21
|
-
{{#if config.gridOptions~}}
|
|
22
|
-
deferredGridOptions={deferredGridOptions}
|
|
23
|
-
{{/if}}
|
|
17
|
+
{{#ifAny config.gridOptions config.eventing.publishEventName}}
|
|
18
|
+
deferredGridOptions={gridOptions}
|
|
19
|
+
{{/ifAny}}
|
|
24
20
|
></grid-pro-genesis-datasource>
|
|
25
21
|
{{#if config.gridOptions}}
|
|
26
22
|
{gridOptionsTile?.columnDefs?.map((columnDef, index) => (
|
|
@@ -2,12 +2,12 @@
|
|
|
2
2
|
<rapid-layout-region type="vertical">
|
|
3
3
|
{{#each route.tiles}}
|
|
4
4
|
{{#ifEquals @index 0}}
|
|
5
|
-
<rapid-layout-item title="{{this.title}}">
|
|
5
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
6
6
|
<{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
|
|
7
7
|
</rapid-layout-item>
|
|
8
8
|
{{/ifEquals}}
|
|
9
9
|
{{#ifEquals @index 1}}
|
|
10
|
-
<rapid-layout-item title="{{this.title}}">
|
|
10
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
11
11
|
<{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
|
|
12
12
|
</rapid-layout-item>
|
|
13
13
|
{{/ifEquals}}
|
|
@@ -16,12 +16,12 @@
|
|
|
16
16
|
<rapid-layout-region type="vertical">
|
|
17
17
|
{{#each route.tiles}}
|
|
18
18
|
{{#ifEquals @index 2}}
|
|
19
|
-
<rapid-layout-item title="{{this.title}}">
|
|
19
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
20
20
|
<{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
|
|
21
21
|
</rapid-layout-item>
|
|
22
22
|
{{/ifEquals}}
|
|
23
23
|
{{#ifEquals @index 3}}
|
|
24
|
-
<rapid-layout-item title="{{this.title}}">
|
|
24
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
25
25
|
<{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
|
|
26
26
|
</rapid-layout-item>
|
|
27
27
|
{{/ifEquals}}
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<rapid-layout-region>
|
|
2
2
|
{{#each route.tiles}}
|
|
3
|
-
<rapid-layout-item title="{{this.title}}">
|
|
3
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
4
4
|
<{{pascalCase this.componentName}}></{{pascalCase this.componentName}}>
|
|
5
5
|
</rapid-layout-item>
|
|
6
6
|
{{/each}}
|
|
@@ -1,14 +1,25 @@
|
|
|
1
1
|
import './{{pascalCase route.name}}.css';
|
|
2
2
|
import { persistLayout } from '../../utils';
|
|
3
|
+
import { setComponentItemsMap } from '../../utils/goldenLayout.helper';
|
|
4
|
+
import { LayoutComponentNames, layoutComponentsMap } from '../../store/store';
|
|
5
|
+
import { useRef, useEffect } from 'react';
|
|
3
6
|
{{#each route.tiles}}
|
|
4
7
|
import { {{pascalCase this.componentName}} } from './{{pascalCase this.title}}{{pascalCase this.componentType}}';
|
|
5
8
|
{{/each}}
|
|
6
9
|
|
|
7
10
|
const {{pascalCase route.name}} = () => {
|
|
11
|
+
const layoutRef = useRef<any>(null);
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (layoutRef.current) {
|
|
15
|
+
setComponentItemsMap(layoutRef.current, layoutComponentsMap);
|
|
16
|
+
}
|
|
17
|
+
}, []);
|
|
18
|
+
|
|
8
19
|
return (
|
|
9
20
|
<section className="{{kebabCase route.name}}-page">
|
|
10
21
|
{{#if route.tiles}}
|
|
11
|
-
<rapid-layout auto-save-key={persistLayout('{{route.layoutKey}}')}>
|
|
22
|
+
<rapid-layout ref={layoutRef} auto-save-key={persistLayout('{{route.layoutKey}}')}>
|
|
12
23
|
{{> (lookup ./route 'layoutType') }}
|
|
13
24
|
</rapid-layout>
|
|
14
25
|
{{else}}
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { CriteriaBuilder, ExpressionBuilder, Serialisers } from '@genesislcap/foundation-criteria';
|
|
2
|
+
import { createSlice } from '@genesislcap/foundation-redux';
|
|
3
|
+
|
|
4
|
+
export const eventingSlice = createSlice({
|
|
5
|
+
name: 'eventing',
|
|
6
|
+
initialState: {
|
|
7
|
+
{{#each events}}
|
|
8
|
+
{{this}}: null{{#unless @last}},{{/unless}}
|
|
9
|
+
{{/each}}
|
|
10
|
+
},
|
|
11
|
+
reducers: {
|
|
12
|
+
{{#each events}}
|
|
13
|
+
publish{{pascalCase this}}: (state, action) => {
|
|
14
|
+
state.{{this}} = action.payload;
|
|
15
|
+
}{{#unless @last}},{{/unless}}
|
|
16
|
+
{{/each}}
|
|
17
|
+
},
|
|
18
|
+
selectors: {
|
|
19
|
+
{{#each listeners}}
|
|
20
|
+
getCriteriaFor{{pascalCase tileName}}: (state) => {
|
|
21
|
+
const criteriaBuilder = new CriteriaBuilder();
|
|
22
|
+
const data = state.{{eventName}};
|
|
23
|
+
if (!data) return '';
|
|
24
|
+
|
|
25
|
+
{{#each mappings}}
|
|
26
|
+
if (data.{{sourceField}}) {
|
|
27
|
+
criteriaBuilder.And(new ExpressionBuilder()
|
|
28
|
+
.withField('{{targetField}}')
|
|
29
|
+
.withValue(data.{{sourceField}})
|
|
30
|
+
.withSerialiser(Serialisers.EQ)
|
|
31
|
+
.build());
|
|
32
|
+
}
|
|
33
|
+
{{/each}}
|
|
34
|
+
|
|
35
|
+
return criteriaBuilder.build();
|
|
36
|
+
}{{#unless @last}},{{/unless}}
|
|
37
|
+
{{/each}}
|
|
38
|
+
},
|
|
39
|
+
});
|
|
@@ -0,0 +1,24 @@
|
|
|
1
|
+
import { createStore } from '@genesislcap/foundation-redux';
|
|
2
|
+
import { eventingSlice } from './slices/eventing.slice';
|
|
3
|
+
|
|
4
|
+
export const layoutComponentsMap: Map<string, any> = new Map();
|
|
5
|
+
|
|
6
|
+
export const LayoutComponentNames = {
|
|
7
|
+
{{#each layoutComponents}}
|
|
8
|
+
{{constantCase componentName}}: '{{componentName}}',
|
|
9
|
+
{{/each}}
|
|
10
|
+
} as const;
|
|
11
|
+
|
|
12
|
+
export const reduxStore = createStore([eventingSlice], {
|
|
13
|
+
eventing: {
|
|
14
|
+
{{#each events}}
|
|
15
|
+
{{this}}: null{{#unless @last}},{{/unless}}
|
|
16
|
+
{{/each}}
|
|
17
|
+
},
|
|
18
|
+
});
|
|
19
|
+
|
|
20
|
+
export const { store, actions, selectors } = reduxStore;
|
|
21
|
+
|
|
22
|
+
export type Store = typeof store;
|
|
23
|
+
export type Actions = typeof actions;
|
|
24
|
+
export type Selectors = typeof selectors;
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
<rapid-layout-region type="tabs">
|
|
2
2
|
{{#each route.tiles}}
|
|
3
|
-
<rapid-layout-item title="{{this.title}}">
|
|
3
|
+
<rapid-layout-item title="{{this.title}}" registration={LayoutComponentNames["{{constantCase this.componentName}}"]}>
|
|
4
4
|
{{> (lookup . 'type') }}
|
|
5
5
|
</rapid-layout-item>
|
|
6
6
|
{{/each}}
|
|
@@ -1,6 +1,15 @@
|
|
|
1
1
|
import { Connect } from '@genesislcap/foundation-comms';
|
|
2
2
|
import { User } from '@genesislcap/foundation-user';
|
|
3
3
|
import { customElement, GenesisElement, observable } from '@genesislcap/web-core';
|
|
4
|
+
{{#if tile.config.gridOptions}}
|
|
5
|
+
import { gridOptions as baseGridOptions } from './{{kebabCase tile.title}}.gridOptions';
|
|
6
|
+
{{/if}}
|
|
7
|
+
{{#if tile.config.eventing.publishEventName}}
|
|
8
|
+
import { actions } from '../../../store/store';
|
|
9
|
+
{{/if}}
|
|
10
|
+
{{#if tile.config.eventing.listener}}
|
|
11
|
+
import { selectors } from '../../../store/store';
|
|
12
|
+
{{/if}}
|
|
4
13
|
import { {{pascalCase tile.title}}Styles as styles } from './{{kebabCase tile.title}}.styles';
|
|
5
14
|
import { {{pascalCase tile.title}}Template as template } from './{{kebabCase tile.title}}.template';
|
|
6
15
|
{{#if tile.config.columns}}
|
|
@@ -31,4 +40,21 @@ export class {{pascalCase tile.componentName}} extends GenesisElement {
|
|
|
31
40
|
...columnDefs,
|
|
32
41
|
];
|
|
33
42
|
{{/if}}
|
|
43
|
+
{{#if tile.config.eventing.publishEventName}}
|
|
44
|
+
public handleRowSelection = (e: any) => {
|
|
45
|
+
const selectedRows = e.api.getSelectedRows();
|
|
46
|
+
if (!selectedRows || !selectedRows.length) {
|
|
47
|
+
actions.eventing.publish{{pascalCase tile.config.eventing.publishEventName}}(null);
|
|
48
|
+
return;
|
|
49
|
+
}
|
|
50
|
+
const { TIMESTAMP, RECORD_ID, ROW_REF, ...data } = selectedRows[0];
|
|
51
|
+
actions.eventing.publish{{pascalCase tile.config.eventing.publishEventName}}(data);
|
|
52
|
+
};
|
|
53
|
+
{{/if}}
|
|
54
|
+
{{#if tile.config.eventing.listener}}
|
|
55
|
+
getCriteria() {
|
|
56
|
+
const c = selectors.eventing.getCriteriaFor{{pascalCase tile.title}}();
|
|
57
|
+
return c || '';
|
|
58
|
+
}
|
|
59
|
+
{{/if}}
|
|
34
60
|
}
|
|
@@ -25,15 +25,23 @@ ${whenElse(
|
|
|
25
25
|
{{#if config.deleteEvent}}
|
|
26
26
|
deleteEvent="${(x) => getViewUpdateRightComponent(x.user, '{{config.permissions.updateRight}}', '{{ config.deleteEvent }}')}"
|
|
27
27
|
{{/if}}
|
|
28
|
-
{{#
|
|
29
|
-
:gridOptions=${() =>
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
{{
|
|
28
|
+
{{#ifAny config.gridOptions config.eventing.publishEventName}}
|
|
29
|
+
:gridOptions=${(x) => ({
|
|
30
|
+
{{#if config.gridOptions}}
|
|
31
|
+
...gridOptions,{{/if}}{{#if config.eventing.publishEventName}}
|
|
32
|
+
onSelectionChanged: x.handleRowSelection,
|
|
33
|
+
{{/if}}
|
|
34
|
+
})}
|
|
35
|
+
{{/ifAny}}
|
|
36
|
+
{{#ifAny config.reqrep config.eventing.listener}}
|
|
37
|
+
:datasourceConfig=${(x) => ({
|
|
38
|
+
{{#if config.reqrep}}
|
|
39
|
+
pollingInterval: 5000,
|
|
40
|
+
requestAutoSetup: false,{{/if}}{{#if config.eventing.listener}}
|
|
41
|
+
criteria: x.getCriteria(),
|
|
42
|
+
{{/if}}
|
|
43
|
+
})}
|
|
44
|
+
{{/ifAny}}
|
|
37
45
|
{{#if config.entityName}}
|
|
38
46
|
entityLabel="{{ config.entityName }}"
|
|
39
47
|
{{/if}}
|
|
@@ -9,16 +9,24 @@ ${whenElse(
|
|
|
9
9
|
>
|
|
10
10
|
<grid-pro-genesis-datasource
|
|
11
11
|
resource-name="{{config.resourceName}}"
|
|
12
|
-
{{#if config.snapshot}}
|
|
13
|
-
isSnapshot="{{config.snapshot}}"
|
|
14
|
-
{{/if}}
|
|
15
12
|
{{#if config.reqrep}}
|
|
16
13
|
request-auto-setup="false"
|
|
17
14
|
polling-interval="5000"
|
|
18
15
|
{{/if}}
|
|
19
|
-
{{#if config.
|
|
20
|
-
|
|
16
|
+
{{#if config.eventing.listener}}
|
|
17
|
+
criteria=${(x) => x.getCriteria()}
|
|
21
18
|
{{/if}}
|
|
19
|
+
{{#ifAny config.gridOptions config.eventing.publishEventName}}
|
|
20
|
+
:deferredGridOptions=${(x) => ({
|
|
21
|
+
{{#if config.gridOptions}}
|
|
22
|
+
onRowClicked: gridOptions?.onRowClicked,
|
|
23
|
+
{{/if}}
|
|
24
|
+
{{#if config.eventing.publishEventName}}
|
|
25
|
+
onSelectionChanged: x.handleRowSelection,
|
|
26
|
+
rowSelection: 'single',
|
|
27
|
+
{{/if}}
|
|
28
|
+
})}
|
|
29
|
+
{{/ifAny}}
|
|
22
30
|
></grid-pro-genesis-datasource>
|
|
23
31
|
{{#if config.gridOptions}}
|
|
24
32
|
${repeat(
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { CriteriaBuilder, ExpressionBuilder, Serialisers } from '@genesislcap/foundation-criteria';
|
|
2
|
+
import { createSlice } from '@genesislcap/foundation-redux';
|
|
3
|
+
|
|
4
|
+
export const eventingSlice = createSlice({
|
|
5
|
+
name: 'eventing',
|
|
6
|
+
{{#if events}}
|
|
7
|
+
initialState: {
|
|
8
|
+
{{#each events}}
|
|
9
|
+
{{this}}: null{{#unless @last}},{{/unless}}
|
|
10
|
+
{{/each}} },
|
|
11
|
+
reducers: {
|
|
12
|
+
{{#each events}}
|
|
13
|
+
publish{{pascalCase this}}: (state, action) => {
|
|
14
|
+
state.{{this}} = action.payload;
|
|
15
|
+
}{{#unless @last}},{{/unless}}
|
|
16
|
+
{{/each}}
|
|
17
|
+
},
|
|
18
|
+
selectors: {
|
|
19
|
+
{{#each listeners}}
|
|
20
|
+
getCriteriaFor{{pascalCase tileName}}: (state) => {
|
|
21
|
+
const criteriaBuilder = new CriteriaBuilder();
|
|
22
|
+
const data = state.{{eventName}};
|
|
23
|
+
if (!data) return '';
|
|
24
|
+
|
|
25
|
+
{{#each mappings}}
|
|
26
|
+
if (data.{{sourceField}}) {
|
|
27
|
+
criteriaBuilder.And(new ExpressionBuilder()
|
|
28
|
+
.withField('{{targetField}}')
|
|
29
|
+
.withValue(data.{{sourceField}})
|
|
30
|
+
.withSerialiser(Serialisers.EQ)
|
|
31
|
+
.build());
|
|
32
|
+
}
|
|
33
|
+
{{/each}}
|
|
34
|
+
|
|
35
|
+
return criteriaBuilder.build();
|
|
36
|
+
}{{#unless @last}},{{/unless}}
|
|
37
|
+
{{/each}}
|
|
38
|
+
},
|
|
39
|
+
{{else}}
|
|
40
|
+
initialState: {},
|
|
41
|
+
reducers: {},
|
|
42
|
+
selectors: {},
|
|
43
|
+
{{/if}}
|
|
44
|
+
});
|
|
@@ -0,0 +1,18 @@
|
|
|
1
|
+
import { createStore } from '@genesislcap/foundation-redux';
|
|
2
|
+
import { eventingSlice } from './slices/eventing.slice';
|
|
3
|
+
|
|
4
|
+
export const { store, actions, selectors } = createStore([eventingSlice], {
|
|
5
|
+
{{#if events}}
|
|
6
|
+
eventing: {
|
|
7
|
+
{{#each events}}
|
|
8
|
+
{{this}}: null{{#unless @last}},{{/unless}}
|
|
9
|
+
{{/each}}
|
|
10
|
+
},
|
|
11
|
+
{{else}}
|
|
12
|
+
eventing: {},
|
|
13
|
+
{{/if}}
|
|
14
|
+
});
|
|
15
|
+
|
|
16
|
+
export type Store = typeof store;
|
|
17
|
+
export type Actions = typeof actions;
|
|
18
|
+
export type Selectors = typeof selectors;
|
|
@@ -0,0 +1,75 @@
|
|
|
1
|
+
const path = require('path');
|
|
2
|
+
|
|
3
|
+
// Extract font family from filename
|
|
4
|
+
// "Overpass-Regular.ttf" -> "Overpass"
|
|
5
|
+
// "Open-Sans-Bold.woff2" -> "Open Sans"
|
|
6
|
+
// "RobotoMono-Regular.ttf" -> "RobotoMono"
|
|
7
|
+
function extractFontFamily(filename) {
|
|
8
|
+
const baseName = path.basename(filename, path.extname(filename));
|
|
9
|
+
|
|
10
|
+
// Remove common weight/style keywords
|
|
11
|
+
const keywords = /(Regular|Bold|Light|Medium|Thin|Black|Heavy|Italic|Oblique|SemiBold|ExtraBold|UltraLight|DemiBold|ExtraLight|UltraBold)$/i;
|
|
12
|
+
const withoutKeywords = baseName.replace(keywords, '').replace(/[-_]$/, '');
|
|
13
|
+
|
|
14
|
+
// Convert hyphens between capitalized words to spaces (Open-Sans -> Open Sans)
|
|
15
|
+
// But keep single-word fonts intact (RobotoMono stays RobotoMono)
|
|
16
|
+
const parts = withoutKeywords.split(/[-_]/);
|
|
17
|
+
if (parts.length > 1 && parts.every(p => p && p[0] === p[0].toUpperCase())) {
|
|
18
|
+
return parts.join(' ').trim();
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
return withoutKeywords.trim() || baseName;
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
// Infer font weight from filename
|
|
25
|
+
function inferFontWeight(filename) {
|
|
26
|
+
const lower = filename.toLowerCase();
|
|
27
|
+
if (lower.includes('thin')) return '100';
|
|
28
|
+
if (lower.includes('extralight') || lower.includes('ultralight')) return '200';
|
|
29
|
+
if (lower.includes('light')) return '300';
|
|
30
|
+
if (lower.includes('regular') || lower.includes('normal')) return '400';
|
|
31
|
+
if (lower.includes('medium')) return '500';
|
|
32
|
+
if (lower.includes('semibold') || lower.includes('demibold')) return '600';
|
|
33
|
+
if (lower.includes('bold') && !lower.includes('semi') && !lower.includes('extra')) return '700';
|
|
34
|
+
if (lower.includes('extrabold') || lower.includes('ultrabold')) return '800';
|
|
35
|
+
if (lower.includes('black') || lower.includes('heavy')) return '900';
|
|
36
|
+
return '400'; // default
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
// Get font format from extension
|
|
40
|
+
function getFontFormat(filename) {
|
|
41
|
+
const ext = path.extname(filename).toLowerCase();
|
|
42
|
+
const formatMap = {
|
|
43
|
+
'.ttf': 'truetype',
|
|
44
|
+
'.otf': 'opentype',
|
|
45
|
+
'.woff': 'woff',
|
|
46
|
+
'.woff2': 'woff2',
|
|
47
|
+
'.eot': 'embedded-opentype'
|
|
48
|
+
};
|
|
49
|
+
return formatMap[ext] || 'truetype';
|
|
50
|
+
}
|
|
51
|
+
|
|
52
|
+
// Process font files and prepare data
|
|
53
|
+
function processFontFiles(customFonts) {
|
|
54
|
+
if (!customFonts || !customFonts.files || !customFonts.files.length) {
|
|
55
|
+
return null;
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
const files = Array.isArray(customFonts.files) ? customFonts.files : [customFonts.files];
|
|
59
|
+
const fontFamily = customFonts.family || extractFontFamily(files[0]);
|
|
60
|
+
|
|
61
|
+
const fontData = files.map(file => ({
|
|
62
|
+
filename: path.basename(file),
|
|
63
|
+
weight: inferFontWeight(file),
|
|
64
|
+
format: getFontFormat(file)
|
|
65
|
+
}));
|
|
66
|
+
|
|
67
|
+
return { fontFamily, fontData };
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
module.exports = {
|
|
71
|
+
extractFontFamily,
|
|
72
|
+
inferFontWeight,
|
|
73
|
+
getFontFormat,
|
|
74
|
+
processFontFiles,
|
|
75
|
+
};
|
|
@@ -43,6 +43,7 @@ const formatRouteData = (framework, route) => {
|
|
|
43
43
|
uischema,
|
|
44
44
|
columns,
|
|
45
45
|
customEvents,
|
|
46
|
+
eventing,
|
|
46
47
|
} = config;
|
|
47
48
|
|
|
48
49
|
return {
|
|
@@ -59,6 +60,10 @@ const formatRouteData = (framework, route) => {
|
|
|
59
60
|
uischema: formatJSONValue(uischema),
|
|
60
61
|
columns: gridColumnsSerializer(columns),
|
|
61
62
|
customEvents: formatCustomEvents(customEvents),
|
|
63
|
+
eventing: {
|
|
64
|
+
publishEventName: eventing?.publishEventName || null,
|
|
65
|
+
listener: eventing?.listener || null,
|
|
66
|
+
},
|
|
62
67
|
},
|
|
63
68
|
metadata: {
|
|
64
69
|
...metadata,
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
const { resolve } = require('node:path');
|
|
2
|
+
const makeDirectory = require('./makeDirectory');
|
|
3
|
+
const {
|
|
4
|
+
FRAMEWORK_WEB_COMPONENTS_ALIAS,
|
|
5
|
+
FRAMEWORK_ANGULAR_ALIAS,
|
|
6
|
+
FRAMEWORK_REACT_ALIAS,
|
|
7
|
+
DIR_TEMPLATE_BY_FRAMEWORK,
|
|
8
|
+
} = require('../static');
|
|
9
|
+
|
|
10
|
+
const getStorePathByFramework = (framework) => {
|
|
11
|
+
if (framework === FRAMEWORK_WEB_COMPONENTS_ALIAS) {
|
|
12
|
+
return `../../client/src/store/store.ts`;
|
|
13
|
+
}
|
|
14
|
+
if (framework === FRAMEWORK_REACT_ALIAS) {
|
|
15
|
+
return `../../client/src/store/store.ts`;
|
|
16
|
+
}
|
|
17
|
+
if (framework === FRAMEWORK_ANGULAR_ALIAS) {
|
|
18
|
+
return `../../client/src/app/store/store.ts`;
|
|
19
|
+
}
|
|
20
|
+
return null;
|
|
21
|
+
};
|
|
22
|
+
|
|
23
|
+
const aggregateFromRoutes = (routes = []) => {
|
|
24
|
+
const aggregation = routes.reduce(
|
|
25
|
+
(acc, route) => {
|
|
26
|
+
const routeEvents = Array.from(
|
|
27
|
+
new Set(
|
|
28
|
+
(route.tiles || [])
|
|
29
|
+
.map((t) => t.config?.eventing?.publishEventName)
|
|
30
|
+
.filter(Boolean)
|
|
31
|
+
.concat(
|
|
32
|
+
(route.tiles || [])
|
|
33
|
+
.map((t) => t.config?.eventing?.listener?.eventName)
|
|
34
|
+
.filter(Boolean),
|
|
35
|
+
),
|
|
36
|
+
),
|
|
37
|
+
);
|
|
38
|
+
|
|
39
|
+
const routeListeners = (route.tiles || [])
|
|
40
|
+
.filter((t) => t.config?.eventing?.listener)
|
|
41
|
+
.map((t) => ({
|
|
42
|
+
tileName: t.title,
|
|
43
|
+
eventName: t.config.eventing.listener.eventName,
|
|
44
|
+
mappings: t.config.eventing.listener.mappings || [],
|
|
45
|
+
}));
|
|
46
|
+
|
|
47
|
+
const routeLayoutComponents = (route.tiles || [])
|
|
48
|
+
.map((t) => ({
|
|
49
|
+
componentName: `${route.name}-${t.title.replace(/[^0-9a-z]/gi, '')}-${t.componentType}`,
|
|
50
|
+
}));
|
|
51
|
+
|
|
52
|
+
acc.events = Array.from(new Set(acc.events.concat(routeEvents)));
|
|
53
|
+
acc.listeners = acc.listeners.concat(routeListeners);
|
|
54
|
+
acc.layoutComponents = acc.layoutComponents.concat(routeLayoutComponents);
|
|
55
|
+
return acc;
|
|
56
|
+
},
|
|
57
|
+
{ events: [], listeners: [], layoutComponents: [] },
|
|
58
|
+
);
|
|
59
|
+
|
|
60
|
+
return aggregation;
|
|
61
|
+
};
|
|
62
|
+
|
|
63
|
+
const generateStore = (routesOrAggregation, { writeFileWithData }, framework) => {
|
|
64
|
+
const { events = [], listeners = [], layoutComponents = [] } = Array.isArray(routesOrAggregation)
|
|
65
|
+
? aggregateFromRoutes(routesOrAggregation)
|
|
66
|
+
: (routesOrAggregation || {});
|
|
67
|
+
const sourceTemplateDir = `../${DIR_TEMPLATE_BY_FRAMEWORK[framework]}`;
|
|
68
|
+
const storeTemplate = `${sourceTemplateDir}/store.hbs`;
|
|
69
|
+
const sliceTemplate = `${sourceTemplateDir}/slices/eventing.slice.hbs`;
|
|
70
|
+
|
|
71
|
+
const storeTargetRelative = getStorePathByFramework(framework);
|
|
72
|
+
if (!storeTargetRelative) return;
|
|
73
|
+
|
|
74
|
+
const storeTarget = resolve(__dirname, storeTargetRelative);
|
|
75
|
+
|
|
76
|
+
// Ensure target directories exist
|
|
77
|
+
makeDirectory(resolve(__dirname, storeTarget.replace(/\/store\.ts$/, '')));
|
|
78
|
+
|
|
79
|
+
// Write store.ts
|
|
80
|
+
writeFileWithData(
|
|
81
|
+
storeTarget,
|
|
82
|
+
{ events, listeners, layoutComponents },
|
|
83
|
+
resolve(__dirname, storeTemplate),
|
|
84
|
+
);
|
|
85
|
+
|
|
86
|
+
// Write slices/eventing.slice.ts next to store for all frameworks
|
|
87
|
+
const sliceTarget = storeTarget.replace(/store\.ts$/, 'slices/eventing.slice.ts');
|
|
88
|
+
makeDirectory(resolve(__dirname, sliceTarget.replace(/\/slices\/eventing\.slice\.ts$/, '/slices')));
|
|
89
|
+
writeFileWithData(
|
|
90
|
+
sliceTarget,
|
|
91
|
+
{ events, listeners },
|
|
92
|
+
resolve(__dirname, sliceTemplate),
|
|
93
|
+
);
|
|
94
|
+
};
|
|
95
|
+
|
|
96
|
+
module.exports = generateStore;
|
|
97
|
+
|
|
98
|
+
|
package/.genx/utils/index.js
CHANGED
|
@@ -4,12 +4,14 @@ const formatRouteData = require('./formatRouteData');
|
|
|
4
4
|
const generateRoute = require('./generateRoute');
|
|
5
5
|
const generateCsv = require('./generateCsv');
|
|
6
6
|
const getCombinedCsvData = require('./getCombinedCsvData');
|
|
7
|
+
const generateStore = require('./generateStore');
|
|
7
8
|
const makeDirectory = require('./makeDirectory');
|
|
8
9
|
const normalizeFrameworkAlias = require('./normalizeFrameworkAlias');
|
|
9
10
|
const parseJSONArgument = require('./parseJSONArgument');
|
|
10
11
|
const registerPartials = require('./registerPartials');
|
|
11
12
|
const validateRoute = require('./validateRoute');
|
|
12
13
|
const validateFrameworkAlias = require('./validateFrameworkAlias');
|
|
14
|
+
const fontUtils = require('./fontUtils');
|
|
13
15
|
|
|
14
16
|
module.exports = {
|
|
15
17
|
deleteGradleWrappers,
|
|
@@ -18,10 +20,12 @@ module.exports = {
|
|
|
18
20
|
generateRoute,
|
|
19
21
|
generateCsv,
|
|
20
22
|
getCombinedCsvData,
|
|
23
|
+
generateStore,
|
|
21
24
|
makeDirectory,
|
|
22
25
|
normalizeFrameworkAlias,
|
|
23
26
|
parseJSONArgument,
|
|
24
27
|
registerPartials,
|
|
25
28
|
validateFrameworkAlias,
|
|
26
29
|
validateRoute,
|
|
30
|
+
fontUtils,
|
|
27
31
|
};
|
package/.genx/versions.json
CHANGED
package/.github/CODEOWNERS
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
# for reference see https://docs.github.com/en/github/creating-cloning-and-archiving-repositories/creating-a-repository-on-github/about-code-owners#example-of-a-codeowners-file
|
|
2
2
|
|
|
3
|
-
* @
|
|
3
|
+
* @skawian @kievitsp @jacinpoz @khouari1 @matteematt @SzymonZur @ArturKrasinski @jay-taylerson @Gareth-Spencer-Genesis @patrickoneill-genesis
|
|
@@ -33,6 +33,12 @@ Add file / directory pointers.
|
|
|
33
33
|
rm -rf blankappseedtest && npx -y @genesislcap/genx@latest init blankappseedtest -x --ref %YOUR-BRANCH-NAME% --no-npm
|
|
34
34
|
```
|
|
35
35
|
|
|
36
|
+
### Design tokens and header logo parameter handling test
|
|
37
|
+
|
|
38
|
+
```
|
|
39
|
+
rm -rf blankappseedtest && npx -y @genesislcap/genx@latest init blankappseedtest -x --ref %YOUR-BRANCH-NAME% --no-npm --designTokens '{"design_tokens":{"color":{"accent":{"$value":"#0EAFE2","$type":"color"},"neutral":{"$value":"#7C909B","$type":"color"}},"fontFamily":{"bodyFont":{"$value":"Roboto, \"Segoe UI\", Arial, Helvetica, sans-serif","$type":"fontFamily"}},"typography":{"baseFontSize":{"$value":"14px","$type":"dimension"},"baseLineHeight":{"$value":"20px","$type":"dimension"}},"mode":{"luminance":{"$value":1,"$type":"number"}},"style":{"density":{"$value":1,"$type":"number"},"borderRadius":{"$value":4,"$type":"number"},"strokeWidth":{"$value":1,"$type":"number"}},"space":{"designUnit":{"$value":4,"$type":"number"}}}}' --headerLogo ./logo.png
|
|
40
|
+
```
|
|
41
|
+
|
|
36
42
|
### Route and CSV parameter handling test
|
|
37
43
|
|
|
38
44
|
```
|