@kitconcept/core 1.0.0-alpha.29 → 1.0.0-alpha.30
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/.changelog.draft +9 -1
- package/CHANGELOG.md +10 -0
- package/package.json +9 -3
- package/src/config/slots.ts +19 -0
- package/src/helpers/BlocksConfigMerger.test.ts +98 -0
- package/src/helpers/BlocksConfigMerger.ts +42 -0
- package/src/index.ts +15 -1
- package/src/slots/ConfigInjector/ConfigInjector.tsx +32 -0
- package/src/slots/TTWCustomCSS/TTWCustomCSS.tsx +28 -0
- package/src/types.d.ts +22 -0
package/.changelog.draft
CHANGED
|
@@ -1,3 +1,11 @@
|
|
|
1
|
-
## 1.0.0-alpha.
|
|
1
|
+
## 1.0.0-alpha.30 (2025-09-18)
|
|
2
|
+
|
|
3
|
+
### Feature
|
|
4
|
+
|
|
5
|
+
- Transfer core features from intranet distribution to here: TTWCustomCSS and TTWBlocksConfig. @sneridagh [#53](https://github.com/kitconcept/kitconcept-core/issue/53)
|
|
6
|
+
|
|
7
|
+
### Bugfix
|
|
8
|
+
|
|
9
|
+
- Better buttons in slider add item. Refresh content button in slider. @sneridagh
|
|
2
10
|
|
|
3
11
|
|
package/CHANGELOG.md
CHANGED
|
@@ -8,6 +8,16 @@
|
|
|
8
8
|
|
|
9
9
|
<!-- towncrier release notes start -->
|
|
10
10
|
|
|
11
|
+
## 1.0.0-alpha.30 (2025-09-18)
|
|
12
|
+
|
|
13
|
+
### Feature
|
|
14
|
+
|
|
15
|
+
- Transfer core features from intranet distribution to here: TTWCustomCSS and TTWBlocksConfig. @sneridagh [#53](https://github.com/kitconcept/kitconcept-core/issue/53)
|
|
16
|
+
|
|
17
|
+
### Bugfix
|
|
18
|
+
|
|
19
|
+
- Better buttons in slider add item. Refresh content button in slider. @sneridagh
|
|
20
|
+
|
|
11
21
|
## 1.0.0-alpha.29 (2025-09-17)
|
|
12
22
|
|
|
13
23
|
## 1.0.0-alpha.28 (2025-09-16)
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@kitconcept/core",
|
|
3
|
-
"version": "1.0.0-alpha.
|
|
3
|
+
"version": "1.0.0-alpha.30",
|
|
4
4
|
"description": "Core setup for kitconcept GmbH distributions built on top of Plone",
|
|
5
5
|
"main": "src/index.ts",
|
|
6
6
|
"license": "MIT",
|
|
@@ -37,20 +37,26 @@
|
|
|
37
37
|
],
|
|
38
38
|
"dependencies": {
|
|
39
39
|
"@plone-collective/volto-authomatic": "3.0.0-alpha.4",
|
|
40
|
-
"@kitconcept/volto-light-theme": "7.0.0-beta.
|
|
40
|
+
"@kitconcept/volto-light-theme": "7.0.0-beta.5",
|
|
41
41
|
"@mbarde/volto-image-crop-widget": "^0.5.1",
|
|
42
42
|
"@plone/components": "3.0.2"
|
|
43
43
|
},
|
|
44
44
|
"peerDependencies": {
|
|
45
|
+
"@plonegovbr/volto-social-media": "^2.0.0-alpha.8",
|
|
45
46
|
"react": "18.2.0",
|
|
46
47
|
"react-dom": "18.2.0",
|
|
47
|
-
"
|
|
48
|
+
"react-intl": "^3.12.1",
|
|
49
|
+
"react-redux": "^8.1.2"
|
|
48
50
|
},
|
|
49
51
|
"devDependencies": {
|
|
50
52
|
"@plone/scripts": "^3.6.1",
|
|
53
|
+
"@types/lodash": "^4.14.201",
|
|
51
54
|
"@types/react": "^18.3.12",
|
|
52
55
|
"@types/react-dom": "^18.3.1",
|
|
56
|
+
"lodash": "4.17.21",
|
|
53
57
|
"release-it": "^19.0.4",
|
|
58
|
+
"typescript": "^5.7.3",
|
|
59
|
+
"vitest": "^3.1.2",
|
|
54
60
|
"@plone/types": "1.4.5"
|
|
55
61
|
},
|
|
56
62
|
"scripts": {
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import type { ConfigType } from '@plone/registry';
|
|
2
|
+
import TTWCustomCSS from '../slots/TTWCustomCSS/TTWCustomCSS';
|
|
3
|
+
import ConfigInjector from '../slots/ConfigInjector/ConfigInjector';
|
|
4
|
+
|
|
5
|
+
export default function install(config: ConfigType) {
|
|
6
|
+
config.registerSlotComponent({
|
|
7
|
+
slot: 'aboveHeader',
|
|
8
|
+
name: 'ConfigInjector',
|
|
9
|
+
component: ConfigInjector,
|
|
10
|
+
});
|
|
11
|
+
|
|
12
|
+
config.registerSlotComponent({
|
|
13
|
+
slot: 'aboveHeader',
|
|
14
|
+
name: 'TTWCustomCSS',
|
|
15
|
+
component: TTWCustomCSS,
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
return config;
|
|
19
|
+
}
|
|
@@ -0,0 +1,98 @@
|
|
|
1
|
+
import { BlocksConfigMerger } from './BlocksConfigMerger';
|
|
2
|
+
import { describe, it, expect } from 'vitest';
|
|
3
|
+
|
|
4
|
+
const baseBlocksConfig = {
|
|
5
|
+
teaser: {
|
|
6
|
+
restricted: false,
|
|
7
|
+
variations: [
|
|
8
|
+
{ id: 'variation1', label: 'Variation 1' },
|
|
9
|
+
{ id: 'variation2', label: 'Variation 2' },
|
|
10
|
+
{ id: 'variation3', label: 'Variation 3' },
|
|
11
|
+
],
|
|
12
|
+
themes: [],
|
|
13
|
+
},
|
|
14
|
+
gridBlock: {
|
|
15
|
+
restricted: false,
|
|
16
|
+
variations: [
|
|
17
|
+
{ id: 'variationA', label: 'Variation A' },
|
|
18
|
+
{ id: 'variationB', label: 'Variation B' },
|
|
19
|
+
],
|
|
20
|
+
themes: [],
|
|
21
|
+
},
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
const mutator = {
|
|
25
|
+
teaser: {
|
|
26
|
+
disable: true,
|
|
27
|
+
variations: ['variation1', 'variation2'],
|
|
28
|
+
themes: [
|
|
29
|
+
{
|
|
30
|
+
style: {
|
|
31
|
+
'--theme-color': '#fff',
|
|
32
|
+
'--theme-high-contrast-color': '#ecebeb',
|
|
33
|
+
'--theme-foreground-color': '#000',
|
|
34
|
+
'--theme-low-contrast-foreground-color': '#555555',
|
|
35
|
+
},
|
|
36
|
+
name: 'default',
|
|
37
|
+
label: 'Default',
|
|
38
|
+
},
|
|
39
|
+
],
|
|
40
|
+
},
|
|
41
|
+
gridBlock: {
|
|
42
|
+
variations: ['variationB'],
|
|
43
|
+
},
|
|
44
|
+
description: {
|
|
45
|
+
disable: true,
|
|
46
|
+
},
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
describe('BlocksConfigMerger', () => {
|
|
50
|
+
it('disables the block if disable is true', () => {
|
|
51
|
+
const result = BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
52
|
+
expect(result.teaser.restricted).toBe(true);
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
it('filters variations according to mutator', () => {
|
|
56
|
+
const result = BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
57
|
+
expect(result.teaser.variations.map((v) => v.id)).toEqual([
|
|
58
|
+
'variation1',
|
|
59
|
+
'variation2',
|
|
60
|
+
]);
|
|
61
|
+
expect(result.gridBlock.variations.map((v) => v.id)).toEqual([
|
|
62
|
+
'variationB',
|
|
63
|
+
]);
|
|
64
|
+
});
|
|
65
|
+
|
|
66
|
+
it('assigns themes from mutator', () => {
|
|
67
|
+
const result = BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
68
|
+
expect(result.teaser.themes).toEqual([
|
|
69
|
+
{
|
|
70
|
+
style: {
|
|
71
|
+
'--theme-color': '#fff',
|
|
72
|
+
'--theme-high-contrast-color': '#ecebeb',
|
|
73
|
+
'--theme-foreground-color': '#000',
|
|
74
|
+
'--theme-low-contrast-foreground-color': '#555555',
|
|
75
|
+
},
|
|
76
|
+
name: 'default',
|
|
77
|
+
label: 'Default',
|
|
78
|
+
},
|
|
79
|
+
]);
|
|
80
|
+
});
|
|
81
|
+
|
|
82
|
+
it('does not modify blocks not present in mutator', () => {
|
|
83
|
+
const result = BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
84
|
+
expect(result.teaser.variations.length).toBe(2);
|
|
85
|
+
expect(result.gridBlock.restricted).toBe(false);
|
|
86
|
+
});
|
|
87
|
+
|
|
88
|
+
it('ignores blocks in mutator that do not exist in blocksConfig', () => {
|
|
89
|
+
const result = BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
90
|
+
expect(result.description).toBeUndefined();
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
it('does not mutate the original blocksConfig', () => {
|
|
94
|
+
const original = JSON.parse(JSON.stringify(baseBlocksConfig));
|
|
95
|
+
BlocksConfigMerger(baseBlocksConfig, mutator);
|
|
96
|
+
expect(baseBlocksConfig).toEqual(original);
|
|
97
|
+
});
|
|
98
|
+
});
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
import type { BlocksConfig } from '@plone/types';
|
|
2
|
+
import cloneDeep from 'lodash/cloneDeep';
|
|
3
|
+
import type { MutatorDSL } from '../types';
|
|
4
|
+
|
|
5
|
+
// Utility type for deep recursive Partial
|
|
6
|
+
type DeepPartial<T> = {
|
|
7
|
+
[P in keyof T]?: T[P] extends object
|
|
8
|
+
? T[P] extends Array<infer U>
|
|
9
|
+
? Array<DeepPartial<U>>
|
|
10
|
+
: DeepPartial<T[P]>
|
|
11
|
+
: T[P];
|
|
12
|
+
};
|
|
13
|
+
|
|
14
|
+
export function BlocksConfigMerger(
|
|
15
|
+
blocksConfig: DeepPartial<BlocksConfig['blocksConfig']>,
|
|
16
|
+
merger: MutatorDSL,
|
|
17
|
+
): BlocksConfig['blocksConfig'] {
|
|
18
|
+
const mergedConfig = cloneDeep(blocksConfig);
|
|
19
|
+
|
|
20
|
+
Object.entries(merger).forEach(([blockId, dsl]) => {
|
|
21
|
+
if (!mergedConfig[blockId]) return;
|
|
22
|
+
|
|
23
|
+
// 1. Disable block
|
|
24
|
+
if (dsl.disable) {
|
|
25
|
+
mergedConfig[blockId]!.restricted = true;
|
|
26
|
+
}
|
|
27
|
+
|
|
28
|
+
// 2. Filter variations
|
|
29
|
+
if (Array.isArray(dsl.variations) && mergedConfig[blockId]!.variations) {
|
|
30
|
+
mergedConfig[blockId]!.variations = mergedConfig[
|
|
31
|
+
blockId
|
|
32
|
+
]!.variations!.filter((v) => dsl.variations!.includes(v.id));
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
// 3. Assign themes
|
|
36
|
+
if (Array.isArray(dsl.themes)) {
|
|
37
|
+
mergedConfig[blockId]!.themes = dsl.themes;
|
|
38
|
+
}
|
|
39
|
+
});
|
|
40
|
+
|
|
41
|
+
return mergedConfig as BlocksConfig['blocksConfig'];
|
|
42
|
+
}
|
package/src/index.ts
CHANGED
|
@@ -1,9 +1,23 @@
|
|
|
1
1
|
import type { ConfigType } from '@plone/registry';
|
|
2
|
-
|
|
2
|
+
import type { CustomInheritBehavior, BlocksConfigSettings } from './types';
|
|
3
3
|
import installSettings from './config/settings';
|
|
4
|
+
import installSlots from './config/slots';
|
|
5
|
+
|
|
6
|
+
declare module '@plone/types' {
|
|
7
|
+
export interface GetSiteResponse {
|
|
8
|
+
'kitconcept.intranet.custom_css': string;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export interface Expanders {
|
|
12
|
+
inherit: {
|
|
13
|
+
'kitconcept.blocks.config': CustomInheritBehavior<BlocksConfigSettings>;
|
|
14
|
+
};
|
|
15
|
+
}
|
|
16
|
+
}
|
|
4
17
|
|
|
5
18
|
const applyConfig = (config: ConfigType) => {
|
|
6
19
|
installSettings(config);
|
|
20
|
+
installSlots(config);
|
|
7
21
|
return config;
|
|
8
22
|
};
|
|
9
23
|
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import config from '@plone/volto/registry';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import { BlocksConfigMerger } from '../../helpers/BlocksConfigMerger';
|
|
4
|
+
import type { Content } from '@plone/types';
|
|
5
|
+
import type { MutatorDSL } from '../../types';
|
|
6
|
+
|
|
7
|
+
type FormState = {
|
|
8
|
+
content: {
|
|
9
|
+
data: Content;
|
|
10
|
+
};
|
|
11
|
+
};
|
|
12
|
+
|
|
13
|
+
const ConfigInjector = () => {
|
|
14
|
+
const blockConfigData = useSelector<FormState, MutatorDSL>(
|
|
15
|
+
(state) =>
|
|
16
|
+
state.content.data?.['@components']?.inherit?.['kitconcept.blocks.config']
|
|
17
|
+
?.data?.blocks_config_mutator,
|
|
18
|
+
);
|
|
19
|
+
|
|
20
|
+
if (blockConfigData) {
|
|
21
|
+
config.blocks.blocksConfig = BlocksConfigMerger(
|
|
22
|
+
config.blocks.blocksConfig,
|
|
23
|
+
blockConfigData,
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
// This component does not render anything, it just injects config from the Redux
|
|
28
|
+
// store in the global config
|
|
29
|
+
return null;
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
export default ConfigInjector;
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
import Helmet from '@plone/volto/helpers/Helmet/Helmet';
|
|
2
|
+
import { useSelector } from 'react-redux';
|
|
3
|
+
import type { GetSiteResponse } from '@plone/types';
|
|
4
|
+
|
|
5
|
+
type FormState = {
|
|
6
|
+
site: { data: GetSiteResponse };
|
|
7
|
+
};
|
|
8
|
+
|
|
9
|
+
const TTWCustomCSS = () => {
|
|
10
|
+
const site = useSelector<FormState, GetSiteResponse>(
|
|
11
|
+
(state) => state.site.data,
|
|
12
|
+
);
|
|
13
|
+
const customCSS = site['kitconcept.custom_css'];
|
|
14
|
+
|
|
15
|
+
return (
|
|
16
|
+
<>
|
|
17
|
+
{customCSS ? (
|
|
18
|
+
<>
|
|
19
|
+
<Helmet>
|
|
20
|
+
<style>{customCSS}</style>
|
|
21
|
+
</Helmet>
|
|
22
|
+
</>
|
|
23
|
+
) : null}
|
|
24
|
+
</>
|
|
25
|
+
);
|
|
26
|
+
};
|
|
27
|
+
|
|
28
|
+
export default TTWCustomCSS;
|
package/src/types.d.ts
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
import type { StyleDefinition } from '@plone/types';
|
|
2
|
+
|
|
3
|
+
export type MutatorDSL = Record<
|
|
4
|
+
string,
|
|
5
|
+
{
|
|
6
|
+
disable?: boolean;
|
|
7
|
+
variations?: string[];
|
|
8
|
+
themes?: StyleDefinition[];
|
|
9
|
+
}
|
|
10
|
+
>;
|
|
11
|
+
|
|
12
|
+
export type BlocksConfigSettings = {
|
|
13
|
+
blocks_config_mutator: MutatorDSL;
|
|
14
|
+
};
|
|
15
|
+
|
|
16
|
+
export type CustomInheritBehavior<T> = {
|
|
17
|
+
data: T;
|
|
18
|
+
from: {
|
|
19
|
+
'@id': string;
|
|
20
|
+
title: string;
|
|
21
|
+
};
|
|
22
|
+
};
|