@cntrl-site/sdk-nextjs 1.0.17 → 1.0.19-alpha.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/.idea/codeStyles/codeStyleConfig.xml +5 -0
- package/.idea/inspectionProfiles/Project_Default.xml +15 -0
- package/README.md +4 -1
- package/jest.config.js +2 -2
- package/lib/components/Article.js +9 -8
- package/lib/components/Item.js +32 -14
- package/lib/components/items/CodeEmbedItem.js +9 -8
- package/lib/components/items/CustomItem.js +8 -7
- package/lib/components/items/GroupItem.js +9 -8
- package/lib/components/items/ImageItem.js +13 -12
- package/lib/components/items/RectangleItem.js +9 -8
- package/lib/components/items/RichTextItem.js +8 -7
- package/lib/components/items/VideoItem.js +13 -12
- package/lib/components/items/VimeoEmbed.js +16 -15
- package/lib/components/items/YoutubeEmbed.js +14 -13
- package/lib/components/useStatesClassNames.js +18 -0
- package/lib/provider/InteractionsContext.js +45 -0
- package/lib/utils/{HoverStyles/HoverStyles.js → StateStyles/StateStyles.js} +18 -16
- package/lib/utils/getStatesCSS.js +16 -0
- package/package.json +7 -4
- package/src/components/Article.tsx +31 -28
- package/src/components/ArticleWrapper.tsx +1 -2
- package/src/components/Item.tsx +36 -19
- package/src/components/items/CodeEmbedItem.tsx +10 -9
- package/src/components/items/CustomItem.tsx +10 -9
- package/src/components/items/GroupItem.tsx +10 -9
- package/src/components/items/ImageItem.tsx +16 -15
- package/src/components/items/RectangleItem.tsx +11 -10
- package/src/components/items/RichTextItem.tsx +15 -9
- package/src/components/items/VideoItem.tsx +16 -15
- package/src/components/items/VimeoEmbed.tsx +17 -16
- package/src/components/items/YoutubeEmbed.tsx +15 -15
- package/src/components/useStatesClassNames.ts +23 -0
- package/src/provider/InteractionsContext.test.tsx +97 -0
- package/src/provider/InteractionsContext.tsx +65 -0
- package/src/utils/{HoverStyles/HoverStyles.ts → StateStyles/StateStyles.ts} +22 -21
- package/src/utils/getStatesCSS.ts +21 -0
- package/cntrl-site-sdk-nextjs-1.0.17.tgz +0 -0
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import { render, waitFor } from '@testing-library/react';
|
|
3
|
+
import { InteractionsProvider, InteractionsContext } from './InteractionsContext';
|
|
4
|
+
import { Interaction } from '@cntrl-site/sdk';
|
|
5
|
+
|
|
6
|
+
describe('InteractionsProvider', () => {
|
|
7
|
+
const interactions: Interaction[] = [
|
|
8
|
+
{
|
|
9
|
+
id: 'interaction1',
|
|
10
|
+
startStateId: 'state1',
|
|
11
|
+
triggers: [
|
|
12
|
+
{ itemId: 'item1', type: 'click', from: 'state1', to: 'state2' }
|
|
13
|
+
],
|
|
14
|
+
states: [{ id: 'state1' }, { id: 'state2' }]
|
|
15
|
+
},
|
|
16
|
+
{
|
|
17
|
+
id: 'interaction2',
|
|
18
|
+
startStateId: 'state3',
|
|
19
|
+
triggers: [
|
|
20
|
+
{ itemId: 'item2', type: 'hover-in', from: 'state3', to: 'state4' }
|
|
21
|
+
],
|
|
22
|
+
states: [{ id: 'state3' }, { id: 'state4' }]
|
|
23
|
+
}
|
|
24
|
+
];
|
|
25
|
+
|
|
26
|
+
it('should generate correct default interactionsStatesMap', () => {
|
|
27
|
+
let contextValue;
|
|
28
|
+
|
|
29
|
+
render(
|
|
30
|
+
<InteractionsProvider interactions={interactions}>
|
|
31
|
+
<InteractionsContext.Consumer>
|
|
32
|
+
{value => {
|
|
33
|
+
contextValue = value;
|
|
34
|
+
return null;
|
|
35
|
+
}}
|
|
36
|
+
</InteractionsContext.Consumer>
|
|
37
|
+
</InteractionsProvider>
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
expect(contextValue!).toBeDefined();
|
|
41
|
+
expect(contextValue!.interactionsStatesMap).toEqual({
|
|
42
|
+
interaction1: 'state1',
|
|
43
|
+
interaction2: 'state3'
|
|
44
|
+
});
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it('should correctly update interactionsStatesMap when transitionTo is called', async () => {
|
|
48
|
+
let contextValue;
|
|
49
|
+
|
|
50
|
+
render(
|
|
51
|
+
<InteractionsProvider interactions={interactions}>
|
|
52
|
+
<InteractionsContext.Consumer>
|
|
53
|
+
{value => {
|
|
54
|
+
contextValue = value;
|
|
55
|
+
return null;
|
|
56
|
+
}}
|
|
57
|
+
</InteractionsContext.Consumer>
|
|
58
|
+
</InteractionsProvider>
|
|
59
|
+
);
|
|
60
|
+
|
|
61
|
+
expect(contextValue!).toBeDefined();
|
|
62
|
+
contextValue!.transitionTo('interaction1', 'state2');
|
|
63
|
+
|
|
64
|
+
await waitFor(() => {
|
|
65
|
+
expect(contextValue!.interactionsStatesMap['interaction1']).toBe('state2');
|
|
66
|
+
});
|
|
67
|
+
});
|
|
68
|
+
|
|
69
|
+
it('should return the correct trigger using getItemTrigger', () => {
|
|
70
|
+
let contextValue;
|
|
71
|
+
|
|
72
|
+
render(
|
|
73
|
+
<InteractionsProvider interactions={interactions}>
|
|
74
|
+
<InteractionsContext.Consumer>
|
|
75
|
+
{value => {
|
|
76
|
+
contextValue = value;
|
|
77
|
+
return null;
|
|
78
|
+
}}
|
|
79
|
+
</InteractionsContext.Consumer>
|
|
80
|
+
</InteractionsProvider>
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
expect(contextValue!).toBeDefined();
|
|
84
|
+
|
|
85
|
+
// Check the correct trigger is returned
|
|
86
|
+
const trigger = contextValue!.getItemTrigger('item1', 'click');
|
|
87
|
+
expect(trigger).toEqual({
|
|
88
|
+
id: 'interaction1',
|
|
89
|
+
from: 'state1',
|
|
90
|
+
to: 'state2',
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// Check that no trigger is returned when conditions don't match
|
|
94
|
+
const noTrigger = contextValue!.getItemTrigger('item1', 'hover-on');
|
|
95
|
+
expect(noTrigger).toBeNull();
|
|
96
|
+
});
|
|
97
|
+
});
|
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
import { createContext, FC, PropsWithChildren, useState } from 'react';
|
|
2
|
+
import { Interaction, InteractionTrigger } from '@cntrl-site/sdk';
|
|
3
|
+
|
|
4
|
+
const defaultState = {
|
|
5
|
+
interactionsStatesMap: {},
|
|
6
|
+
interactions: [],
|
|
7
|
+
transitionTo: () => {},
|
|
8
|
+
getItemTrigger: () => null
|
|
9
|
+
};
|
|
10
|
+
|
|
11
|
+
export const InteractionsContext = createContext<{
|
|
12
|
+
interactionsStatesMap: StatesMap,
|
|
13
|
+
interactions: Interaction[],
|
|
14
|
+
transitionTo: (interactionId: string, stateId: string) => void,
|
|
15
|
+
getItemTrigger: (itemId: string, triggerType: TriggerType) => Trigger | null
|
|
16
|
+
}>(defaultState);
|
|
17
|
+
|
|
18
|
+
interface Props {
|
|
19
|
+
interactions: Interaction[];
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
export const InteractionsProvider: FC<PropsWithChildren<Props>> = ({ interactions, children }) => {
|
|
23
|
+
const defaultStatesMap = interactions.reduce<Record<string, string>>((map, { id, startStateId }) => {
|
|
24
|
+
map[id] = startStateId;
|
|
25
|
+
return map;
|
|
26
|
+
}, {});
|
|
27
|
+
const [interactionsStatesMap, setInteractionsStatesMap] = useState(defaultStatesMap);
|
|
28
|
+
const transitionTo = (interactionId: string, stateId: string) => {
|
|
29
|
+
setInteractionsStatesMap((map) => ({ ...map, [interactionId]: stateId }));
|
|
30
|
+
};
|
|
31
|
+
const getItemTrigger = (itemId: string, triggerType: TriggerType): Trigger | null => {
|
|
32
|
+
for (const interaction of interactions) {
|
|
33
|
+
const activeStateId = interactionsStatesMap[interaction.id];
|
|
34
|
+
const matchingTrigger = interaction.triggers.find((trigger) =>
|
|
35
|
+
trigger.itemId === itemId &&
|
|
36
|
+
trigger.from === activeStateId &&
|
|
37
|
+
trigger.type === triggerType
|
|
38
|
+
);
|
|
39
|
+
if (matchingTrigger) {
|
|
40
|
+
return {
|
|
41
|
+
id: interaction.id,
|
|
42
|
+
from: matchingTrigger.from,
|
|
43
|
+
to: matchingTrigger.to,
|
|
44
|
+
};
|
|
45
|
+
}
|
|
46
|
+
}
|
|
47
|
+
return null;
|
|
48
|
+
};
|
|
49
|
+
return (
|
|
50
|
+
<InteractionsContext.Provider value={{
|
|
51
|
+
transitionTo,
|
|
52
|
+
interactionsStatesMap,
|
|
53
|
+
interactions,
|
|
54
|
+
getItemTrigger
|
|
55
|
+
}}>
|
|
56
|
+
{children}
|
|
57
|
+
</InteractionsContext.Provider>
|
|
58
|
+
);
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
type StatesMap = Record<InteractionId, StateId>;
|
|
62
|
+
type Trigger = { id: InteractionId, from: StateId, to: StateId };
|
|
63
|
+
type TriggerType = InteractionTrigger['type'];
|
|
64
|
+
type InteractionId = string;
|
|
65
|
+
type StateId = string;
|
|
@@ -1,12 +1,11 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { StateParams, ArticleItemType, AnchorSide, ItemState } from '@cntrl-site/sdk';
|
|
2
2
|
import { CntrlColor } from '@cntrl-site/color';
|
|
3
3
|
import { getItemTopStyle } from '../getItemTopStyle';
|
|
4
4
|
|
|
5
5
|
type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
|
|
6
|
-
type
|
|
7
|
-
type ItemHoverParams = Omit<UnionToIntersection<ItemHoverState>, 'autoplay'>;
|
|
6
|
+
type StateParamsGetter = (value: any, anchorSide?: AnchorSide) => string;
|
|
8
7
|
|
|
9
|
-
const
|
|
8
|
+
const stateTransformationMap: Record<keyof ItemState<ArticleItemType>, StateParamsGetter> = {
|
|
10
9
|
'width': (width: number) => `width: ${width * 100}vw !important;`,
|
|
11
10
|
'height': (height: number) => `height: ${height * 100}vw !important;`,
|
|
12
11
|
'top': (top: number, anchorSide?: AnchorSide) => `top: ${getItemTopStyle(top, anchorSide)} !important;`,
|
|
@@ -25,7 +24,7 @@ const hoverTransformationMap: Record<keyof ItemHoverParams, HoverParamsGetter> =
|
|
|
25
24
|
'wordSpacing': (wordSpacing: number) => `word-spacing: ${wordSpacing * 100}vw !important;`
|
|
26
25
|
};
|
|
27
26
|
|
|
28
|
-
const CSSPropertyNameMap: Record<keyof
|
|
27
|
+
const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
|
|
29
28
|
'width': 'width',
|
|
30
29
|
'height': 'height',
|
|
31
30
|
'top': 'top',
|
|
@@ -45,16 +44,17 @@ const CSSPropertyNameMap: Record<keyof ItemHoverParams, string> = {
|
|
|
45
44
|
};
|
|
46
45
|
|
|
47
46
|
export function getTransitions<T extends ArticleItemType>(
|
|
48
|
-
values: Array<keyof
|
|
49
|
-
|
|
47
|
+
values: Array<keyof ItemState<ArticleItemType>>,
|
|
48
|
+
state?: ItemState<ArticleItemType>
|
|
50
49
|
): string {
|
|
51
|
-
if (!
|
|
50
|
+
if (!state) return 'unset';
|
|
52
51
|
const transitionValues = values.reduce<string[]>((acc, valueName) => {
|
|
53
|
-
if (valueName in
|
|
54
|
-
|
|
52
|
+
if (valueName in state && state[valueName] !== undefined) {
|
|
53
|
+
// @ts-ignore TODO
|
|
54
|
+
const stateProperties = state[valueName] as StateParams<string | number>;
|
|
55
55
|
return [
|
|
56
56
|
...acc,
|
|
57
|
-
`${CSSPropertyNameMap[valueName]} ${
|
|
57
|
+
`${CSSPropertyNameMap[valueName]} ${stateProperties!.duration}ms ${stateProperties!.timing} ${stateProperties!.delay}ms`
|
|
58
58
|
];
|
|
59
59
|
}
|
|
60
60
|
return acc;
|
|
@@ -63,23 +63,24 @@ export function getTransitions<T extends ArticleItemType>(
|
|
|
63
63
|
return transitionValues.join(', ');
|
|
64
64
|
}
|
|
65
65
|
|
|
66
|
-
export function
|
|
67
|
-
values: Array<keyof
|
|
68
|
-
|
|
66
|
+
export function getStateStyles<T extends ArticleItemType>(
|
|
67
|
+
values: Array<keyof ItemState<ArticleItemType>>,
|
|
68
|
+
state?: ItemState<ArticleItemType>,
|
|
69
69
|
anchorSide?: AnchorSide
|
|
70
70
|
): string {
|
|
71
|
-
if (!
|
|
72
|
-
const
|
|
73
|
-
if (valueName in
|
|
74
|
-
|
|
71
|
+
if (!state) return '';
|
|
72
|
+
const stateValues = values.reduce<string[]>((acc, valueName) => {
|
|
73
|
+
if (valueName in state && state[valueName] !== undefined) {
|
|
74
|
+
// @ts-ignore TODO
|
|
75
|
+
const stateProperties = state[valueName] as StateParams<string | number>;
|
|
75
76
|
return [
|
|
76
77
|
...acc,
|
|
77
|
-
|
|
78
|
+
stateTransformationMap[valueName](stateProperties.value, anchorSide)
|
|
78
79
|
];
|
|
79
80
|
}
|
|
80
81
|
return acc;
|
|
81
82
|
}, []);
|
|
82
|
-
if (!
|
|
83
|
-
return
|
|
83
|
+
if (!stateValues.length) return '';
|
|
84
|
+
return stateValues.join('\n');
|
|
84
85
|
}
|
|
85
86
|
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
import { AnchorSide, ArticleItemType, ItemState } from '@cntrl-site/sdk';
|
|
2
|
+
import { getStateStyles } from './StateStyles/StateStyles';
|
|
3
|
+
|
|
4
|
+
export function getStatesCSS(
|
|
5
|
+
itemId: string,
|
|
6
|
+
classNamePrefix: string,
|
|
7
|
+
keys: Array<keyof ItemState<ArticleItemType>>,
|
|
8
|
+
states: Record<string, ItemState<ArticleItemType>> | undefined,
|
|
9
|
+
anchorSide?: AnchorSide
|
|
10
|
+
) {
|
|
11
|
+
return states
|
|
12
|
+
? Object.entries(states).map(([stateId, params]) => {
|
|
13
|
+
return `
|
|
14
|
+
.${classNamePrefix}-${itemId}-state-${stateId} {
|
|
15
|
+
${getStateStyles(keys, params, anchorSide)};
|
|
16
|
+
}
|
|
17
|
+
`;
|
|
18
|
+
}).join('\n')
|
|
19
|
+
: '';
|
|
20
|
+
}
|
|
21
|
+
|
|
Binary file
|