@cntrl-site/sdk-nextjs 1.0.19-alpha.3 → 1.0.19
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/jest.config.js +2 -2
- package/lib/components/Article.js +8 -9
- package/lib/components/Head.js +1 -1
- package/lib/components/Item.js +13 -33
- package/lib/components/items/CodeEmbedItem.js +8 -10
- package/lib/components/items/CustomItem.js +7 -9
- package/lib/components/items/GroupItem.js +8 -10
- package/lib/components/items/ImageItem.js +15 -19
- package/lib/components/items/RectangleItem.js +8 -10
- package/lib/components/items/RichTextItem.js +7 -9
- package/lib/components/items/VideoItem.js +12 -14
- package/lib/components/items/VimeoEmbed.js +15 -17
- package/lib/components/items/YoutubeEmbed.js +13 -16
- package/lib/utils/{StateStyles/StateStyles.js → HoverStyles/HoverStyles.js} +24 -36
- package/package.json +4 -7
- package/src/components/Article.tsx +28 -31
- package/src/components/ArticleWrapper.tsx +2 -1
- package/src/components/Head.tsx +1 -0
- package/src/components/Item.tsx +18 -38
- package/src/components/items/CodeEmbedItem.tsx +9 -11
- package/src/components/items/CustomItem.tsx +9 -11
- package/src/components/items/GroupItem.tsx +9 -11
- package/src/components/items/ImageItem.tsx +19 -24
- package/src/components/items/RectangleItem.tsx +10 -12
- package/src/components/items/RichTextItem.tsx +9 -16
- package/src/components/items/VideoItem.tsx +15 -17
- package/src/components/items/VimeoEmbed.tsx +16 -18
- package/src/components/items/YoutubeEmbed.tsx +16 -18
- package/src/utils/{StateStyles/StateStyles.ts → HoverStyles/HoverStyles.ts} +27 -41
- package/lib/components/useStatesClassNames.js +0 -18
- package/lib/components/useStatesTransitions.js +0 -89
- package/lib/provider/InteractionsContext.js +0 -45
- package/lib/utils/getStatesCSS.js +0 -16
- package/src/components/useStatesClassNames.ts +0 -23
- package/src/components/useStatesTransitions.ts +0 -95
- package/src/provider/InteractionsContext.test.tsx +0 -97
- package/src/provider/InteractionsContext.tsx +0 -65
- package/src/utils/getStatesCSS.ts +0 -24
|
@@ -1,12 +1,12 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { HoverParams, ArticleItemType, AnchorSide, ItemHoverState } from '@cntrl-site/sdk';
|
|
2
2
|
import { CntrlColor } from '@cntrl-site/color';
|
|
3
3
|
import { getItemTopStyle } from '../getItemTopStyle';
|
|
4
4
|
|
|
5
|
-
type
|
|
6
|
-
type
|
|
7
|
-
type
|
|
5
|
+
type UnionToIntersection<U> = (U extends any ? (arg: U) => void : never) extends (arg: infer I) => void ? I : never;
|
|
6
|
+
type HoverParamsGetter = (value: any, anchorSide?: AnchorSide) => string;
|
|
7
|
+
type ItemHoverParams = Omit<UnionToIntersection<ItemHoverState>, 'autoplay'>;
|
|
8
8
|
|
|
9
|
-
const
|
|
9
|
+
const hoverTransformationMap: Record<keyof ItemHoverParams, HoverParamsGetter> = {
|
|
10
10
|
'width': (width: number) => `width: ${width * 100}vw !important;`,
|
|
11
11
|
'height': (height: number) => `height: ${height * 100}vw !important;`,
|
|
12
12
|
'top': (top: number, anchorSide?: AnchorSide) => `top: ${getItemTopStyle(top, anchorSide)} !important;`,
|
|
@@ -25,7 +25,7 @@ const stateTransformationMap: Record<StatePropertyKey, StateParamsGetter> = {
|
|
|
25
25
|
'wordSpacing': (wordSpacing: number) => `word-spacing: ${wordSpacing * 100}vw !important;`
|
|
26
26
|
};
|
|
27
27
|
|
|
28
|
-
const CSSPropertyNameMap: Record<keyof
|
|
28
|
+
const CSSPropertyNameMap: Record<keyof ItemHoverParams, string> = {
|
|
29
29
|
'width': 'width',
|
|
30
30
|
'height': 'height',
|
|
31
31
|
'top': 'top',
|
|
@@ -44,56 +44,42 @@ const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
|
|
|
44
44
|
'color': 'color'
|
|
45
45
|
};
|
|
46
46
|
|
|
47
|
-
export function
|
|
48
|
-
values: Array<
|
|
49
|
-
|
|
50
|
-
anchorSide?: AnchorSide
|
|
47
|
+
export function getTransitions<T extends ArticleItemType>(
|
|
48
|
+
values: Array<keyof ItemHoverParams>,
|
|
49
|
+
hover?: ItemHoverParams
|
|
51
50
|
): string {
|
|
52
|
-
if (!
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
if (valueName in state && state[valueName] !== undefined) {
|
|
57
|
-
// @ts-ignore
|
|
58
|
-
const stateProperties = state[valueName] as StateParams<string | number>;
|
|
59
|
-
const getter = stateTransformationMap[valueName] as StateParamsGetter;
|
|
51
|
+
if (!hover) return 'unset';
|
|
52
|
+
const transitionValues = values.reduce<string[]>((acc, valueName) => {
|
|
53
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
54
|
+
const hoverProperties = hover[valueName] as HoverParams<string | number>;
|
|
60
55
|
return [
|
|
61
56
|
...acc,
|
|
62
|
-
|
|
57
|
+
`${CSSPropertyNameMap[valueName]} ${hoverProperties!.duration}ms ${hoverProperties!.timing} ${hoverProperties!.delay}ms`
|
|
63
58
|
];
|
|
64
59
|
}
|
|
65
60
|
return acc;
|
|
66
61
|
}, []);
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
// @ts-ignore
|
|
70
|
-
const transitionStr = getTransitions(values, state);
|
|
71
|
-
stateValues.push(`transition: ${transitionStr};`);
|
|
72
|
-
return stateValues.join('\n');
|
|
62
|
+
if (!transitionValues.length) return 'unset';
|
|
63
|
+
return transitionValues.join(', ');
|
|
73
64
|
}
|
|
74
65
|
|
|
75
|
-
export function
|
|
76
|
-
values: Array<
|
|
77
|
-
|
|
66
|
+
export function getHoverStyles<T extends ArticleItemType>(
|
|
67
|
+
values: Array<keyof ItemHoverParams>,
|
|
68
|
+
hover?: ItemHoverParams,
|
|
69
|
+
anchorSide?: AnchorSide
|
|
78
70
|
): string {
|
|
79
|
-
if (!
|
|
80
|
-
const
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
// @ts-ignore TODO
|
|
84
|
-
const stateProperties = state[valueName] as StateParams<string | number>;
|
|
71
|
+
if (!hover) return '';
|
|
72
|
+
const hoverValues = values.reduce<string[]>((acc, valueName) => {
|
|
73
|
+
if (valueName in hover && hover[valueName] !== undefined) {
|
|
74
|
+
const hoverProperties = hover[valueName] as HoverParams<string | number>;
|
|
85
75
|
return [
|
|
86
76
|
...acc,
|
|
87
|
-
|
|
88
|
-
`${CSSPropertyNameMap[valueName]} ${stateProperties!.duration}ms ${stateProperties!.timing} ${stateProperties!.delay}ms`
|
|
77
|
+
hoverTransformationMap[valueName](hoverProperties.value, anchorSide)
|
|
89
78
|
];
|
|
90
79
|
}
|
|
91
80
|
return acc;
|
|
92
81
|
}, []);
|
|
93
|
-
if (!
|
|
94
|
-
return
|
|
82
|
+
if (!hoverValues.length) return '';
|
|
83
|
+
return hoverValues.join('\n');
|
|
95
84
|
}
|
|
96
85
|
|
|
97
|
-
// export function getStateTransitions<T extends ArticleItemType>(
|
|
98
|
-
//
|
|
99
|
-
// )
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useStatesClassNames = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const InteractionsContext_1 = require("../provider/InteractionsContext");
|
|
6
|
-
const useCurrentLayout_1 = require("../common/useCurrentLayout");
|
|
7
|
-
function useStatesClassNames(itemId, itemStates, uniquePrefix) {
|
|
8
|
-
const { interactionsStatesMap } = (0, react_1.useContext)(InteractionsContext_1.InteractionsContext);
|
|
9
|
-
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
10
|
-
const activeStates = Object.values(interactionsStatesMap);
|
|
11
|
-
const statesForLayout = layoutId ? itemStates[layoutId] : {};
|
|
12
|
-
const stateClassNames = Object.keys(statesForLayout !== null && statesForLayout !== void 0 ? statesForLayout : {})
|
|
13
|
-
.filter((stateId) => activeStates.includes(stateId))
|
|
14
|
-
.map((stateId) => `${uniquePrefix}-${itemId}-state-${stateId}`)
|
|
15
|
-
.join(' ');
|
|
16
|
-
return stateClassNames;
|
|
17
|
-
}
|
|
18
|
-
exports.useStatesClassNames = useStatesClassNames;
|
|
@@ -1,89 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useStatesTransitions = void 0;
|
|
4
|
-
const react_1 = require("react");
|
|
5
|
-
const InteractionsContext_1 = require("../provider/InteractionsContext");
|
|
6
|
-
const useCurrentLayout_1 = require("../common/useCurrentLayout");
|
|
7
|
-
function useStatesTransitions(el, state, values) {
|
|
8
|
-
const { interactionsStatesMap } = (0, react_1.useContext)(InteractionsContext_1.InteractionsContext);
|
|
9
|
-
const { layoutId } = (0, useCurrentLayout_1.useCurrentLayout)();
|
|
10
|
-
const activeStates = Object.values(interactionsStatesMap);
|
|
11
|
-
const statesForLayout = (0, react_1.useMemo)(() => layoutId ? state[layoutId] : {}, [state, layoutId]);
|
|
12
|
-
const itemStatesIds = statesForLayout ? Object.keys(statesForLayout) : [];
|
|
13
|
-
const itemActiveStateId = activeStates.find((stateId) => itemStatesIds.includes(stateId));
|
|
14
|
-
(0, react_1.useEffect)(() => {
|
|
15
|
-
if (!itemActiveStateId || !el)
|
|
16
|
-
return;
|
|
17
|
-
const state = statesForLayout[itemActiveStateId];
|
|
18
|
-
const transitionStr = getTransition(state, 'in', values);
|
|
19
|
-
const slowestProp = getSlowestProperty(state, 'in', values);
|
|
20
|
-
if (!transitionStr)
|
|
21
|
-
return;
|
|
22
|
-
el.style.transition = transitionStr;
|
|
23
|
-
el.ontransitionend = (e) => {
|
|
24
|
-
e.stopPropagation();
|
|
25
|
-
if (e.target !== el || e.propertyName !== slowestProp)
|
|
26
|
-
return;
|
|
27
|
-
el.style.transition = 'none';
|
|
28
|
-
};
|
|
29
|
-
return () => {
|
|
30
|
-
const transitionStr = getTransition(state, 'out', values);
|
|
31
|
-
const slowestProp = getSlowestProperty(state, 'out', values);
|
|
32
|
-
if (!transitionStr)
|
|
33
|
-
return;
|
|
34
|
-
el.style.transition = transitionStr;
|
|
35
|
-
el.ontransitionend = (e) => {
|
|
36
|
-
e.stopPropagation();
|
|
37
|
-
if (e.target !== el || e.propertyName !== slowestProp)
|
|
38
|
-
return;
|
|
39
|
-
el.style.transition = 'none';
|
|
40
|
-
};
|
|
41
|
-
};
|
|
42
|
-
}, [itemActiveStateId, statesForLayout, el]);
|
|
43
|
-
}
|
|
44
|
-
exports.useStatesTransitions = useStatesTransitions;
|
|
45
|
-
const CSSPropertyNameMap = {
|
|
46
|
-
'width': 'width',
|
|
47
|
-
'height': 'height',
|
|
48
|
-
'top': 'top',
|
|
49
|
-
'left': 'left',
|
|
50
|
-
'scale': 'transform',
|
|
51
|
-
'angle': 'transform',
|
|
52
|
-
'opacity': 'opacity',
|
|
53
|
-
'radius': 'border-radius',
|
|
54
|
-
'strokeWidth': 'border-width',
|
|
55
|
-
'strokeColor': 'border-color',
|
|
56
|
-
'fillColor': 'background-color',
|
|
57
|
-
'blur': 'filter',
|
|
58
|
-
'backdropBlur': 'backdrop-filter',
|
|
59
|
-
'letterSpacing': 'letter-spacing',
|
|
60
|
-
'wordSpacing': 'word-spacing',
|
|
61
|
-
'color': 'color'
|
|
62
|
-
};
|
|
63
|
-
function getTransition(state, direction, values) {
|
|
64
|
-
return Object.entries(state)
|
|
65
|
-
.filter(([key]) => values.includes(key))
|
|
66
|
-
.map(([key, params]) => {
|
|
67
|
-
const cssKey = CSSPropertyNameMap[key];
|
|
68
|
-
if (!cssKey) {
|
|
69
|
-
throw new Error(`Cannot translate "${key}" to a CSS property.`);
|
|
70
|
-
}
|
|
71
|
-
return `${cssKey} ${params[direction].duration}ms ${params[direction].timing} ${params[direction].delay}ms`;
|
|
72
|
-
}, [])
|
|
73
|
-
.join(', ');
|
|
74
|
-
}
|
|
75
|
-
function getSlowestProperty(state, direction, values) {
|
|
76
|
-
const mappedEntries = Object.entries(state)
|
|
77
|
-
.filter(([key]) => values.includes(key))
|
|
78
|
-
.map(([key, params]) => {
|
|
79
|
-
const transitionParams = params[direction];
|
|
80
|
-
return {
|
|
81
|
-
key,
|
|
82
|
-
time: transitionParams.duration + transitionParams.delay
|
|
83
|
-
};
|
|
84
|
-
});
|
|
85
|
-
if (mappedEntries.length === 0)
|
|
86
|
-
return '';
|
|
87
|
-
const { key } = mappedEntries.reduce((slowest, current) => current.time > slowest.time ? current : slowest);
|
|
88
|
-
return CSSPropertyNameMap[key];
|
|
89
|
-
}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.InteractionsProvider = exports.InteractionsContext = void 0;
|
|
4
|
-
const jsx_runtime_1 = require("react/jsx-runtime");
|
|
5
|
-
const react_1 = require("react");
|
|
6
|
-
const defaultState = {
|
|
7
|
-
interactionsStatesMap: {},
|
|
8
|
-
interactions: [],
|
|
9
|
-
transitionTo: () => { },
|
|
10
|
-
getItemTrigger: () => null
|
|
11
|
-
};
|
|
12
|
-
exports.InteractionsContext = (0, react_1.createContext)(defaultState);
|
|
13
|
-
const InteractionsProvider = ({ interactions, children }) => {
|
|
14
|
-
const defaultStatesMap = interactions.reduce((map, { id, startStateId }) => {
|
|
15
|
-
map[id] = startStateId;
|
|
16
|
-
return map;
|
|
17
|
-
}, {});
|
|
18
|
-
const [interactionsStatesMap, setInteractionsStatesMap] = (0, react_1.useState)(defaultStatesMap);
|
|
19
|
-
const transitionTo = (interactionId, stateId) => {
|
|
20
|
-
setInteractionsStatesMap((map) => (Object.assign(Object.assign({}, map), { [interactionId]: stateId })));
|
|
21
|
-
};
|
|
22
|
-
const getItemTrigger = (itemId, triggerType) => {
|
|
23
|
-
for (const interaction of interactions) {
|
|
24
|
-
const activeStateId = interactionsStatesMap[interaction.id];
|
|
25
|
-
const matchingTrigger = interaction.triggers.find((trigger) => trigger.itemId === itemId &&
|
|
26
|
-
trigger.from === activeStateId &&
|
|
27
|
-
trigger.type === triggerType);
|
|
28
|
-
if (matchingTrigger) {
|
|
29
|
-
return {
|
|
30
|
-
id: interaction.id,
|
|
31
|
-
from: matchingTrigger.from,
|
|
32
|
-
to: matchingTrigger.to,
|
|
33
|
-
};
|
|
34
|
-
}
|
|
35
|
-
}
|
|
36
|
-
return null;
|
|
37
|
-
};
|
|
38
|
-
return ((0, jsx_runtime_1.jsx)(exports.InteractionsContext.Provider, { value: {
|
|
39
|
-
transitionTo,
|
|
40
|
-
interactionsStatesMap,
|
|
41
|
-
interactions,
|
|
42
|
-
getItemTrigger
|
|
43
|
-
}, children: children }));
|
|
44
|
-
};
|
|
45
|
-
exports.InteractionsProvider = InteractionsProvider;
|
|
@@ -1,16 +0,0 @@
|
|
|
1
|
-
"use strict";
|
|
2
|
-
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.getStatesCSS = void 0;
|
|
4
|
-
const StateStyles_1 = require("./StateStyles/StateStyles");
|
|
5
|
-
function getStatesCSS(itemId, classNamePrefix, keys, states, anchorSide) {
|
|
6
|
-
return states
|
|
7
|
-
? Object.entries(states).map(([stateId, params]) => {
|
|
8
|
-
return `
|
|
9
|
-
.${classNamePrefix}-${itemId}-state-${stateId} {
|
|
10
|
-
${(0, StateStyles_1.getStateStyles)(keys, params, anchorSide)};
|
|
11
|
-
}
|
|
12
|
-
`;
|
|
13
|
-
}).join('\n')
|
|
14
|
-
: '';
|
|
15
|
-
}
|
|
16
|
-
exports.getStatesCSS = getStatesCSS;
|
|
@@ -1,23 +0,0 @@
|
|
|
1
|
-
import { ItemStateParams } from '@cntrl-site/sdk';
|
|
2
|
-
import { useContext } from 'react';
|
|
3
|
-
import { InteractionsContext } from '../provider/InteractionsContext';
|
|
4
|
-
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
5
|
-
|
|
6
|
-
export function useStatesClassNames(
|
|
7
|
-
itemId: string,
|
|
8
|
-
itemStates: Record<LayoutId, Record<StateId, ItemStateParams>>,
|
|
9
|
-
uniquePrefix: string, // unique in terms of item type, ie. rectangle, code-embed, group etc
|
|
10
|
-
): string {
|
|
11
|
-
const { interactionsStatesMap } = useContext(InteractionsContext);
|
|
12
|
-
const { layoutId } = useCurrentLayout();
|
|
13
|
-
const activeStates = Object.values(interactionsStatesMap);
|
|
14
|
-
const statesForLayout = layoutId ? itemStates[layoutId] : {};
|
|
15
|
-
const stateClassNames = Object.keys(statesForLayout ?? {})
|
|
16
|
-
.filter((stateId) => activeStates.includes(stateId))
|
|
17
|
-
.map((stateId) => `${uniquePrefix}-${itemId}-state-${stateId}`)
|
|
18
|
-
.join(' ');
|
|
19
|
-
return stateClassNames;
|
|
20
|
-
}
|
|
21
|
-
|
|
22
|
-
type StateId = string;
|
|
23
|
-
type LayoutId = string;
|
|
@@ -1,95 +0,0 @@
|
|
|
1
|
-
import { ArticleItemType, ItemState, ItemStateParams, ItemStatesMap } from '@cntrl-site/sdk';
|
|
2
|
-
import { useContext, useEffect, useMemo } from 'react';
|
|
3
|
-
import { InteractionsContext } from '../provider/InteractionsContext';
|
|
4
|
-
import { useCurrentLayout } from '../common/useCurrentLayout';
|
|
5
|
-
|
|
6
|
-
type AllKeys<T> = T extends any ? keyof T : never;
|
|
7
|
-
type StatePropertyKey = AllKeys<ItemStatesMap[keyof ItemStatesMap]>;
|
|
8
|
-
|
|
9
|
-
export function useStatesTransitions(
|
|
10
|
-
el: HTMLElement | null,
|
|
11
|
-
state: ItemState<ArticleItemType>,
|
|
12
|
-
values: StatePropertyKey[]
|
|
13
|
-
) {
|
|
14
|
-
const { interactionsStatesMap } = useContext(InteractionsContext);
|
|
15
|
-
const { layoutId } = useCurrentLayout();
|
|
16
|
-
const activeStates = Object.values(interactionsStatesMap);
|
|
17
|
-
const statesForLayout = useMemo(() => layoutId ? state[layoutId] : {}, [state, layoutId]);
|
|
18
|
-
const itemStatesIds = statesForLayout ? Object.keys(statesForLayout) : [];
|
|
19
|
-
const itemActiveStateId = activeStates.find((stateId) => itemStatesIds.includes(stateId));
|
|
20
|
-
useEffect(() => {
|
|
21
|
-
if (!itemActiveStateId || !el) return;
|
|
22
|
-
const state = statesForLayout[itemActiveStateId];
|
|
23
|
-
const transitionStr = getTransition(state, 'in', values);
|
|
24
|
-
const slowestProp = getSlowestProperty(state, 'in', values);
|
|
25
|
-
if (!transitionStr) return;
|
|
26
|
-
el.style.transition = transitionStr;
|
|
27
|
-
el.ontransitionend = (e) => {
|
|
28
|
-
e.stopPropagation();
|
|
29
|
-
if (e.target !== el || e.propertyName !== slowestProp) return;
|
|
30
|
-
el.style.transition = 'none';
|
|
31
|
-
};
|
|
32
|
-
return () => {
|
|
33
|
-
const transitionStr = getTransition(state, 'out', values);
|
|
34
|
-
const slowestProp = getSlowestProperty(state, 'out', values);
|
|
35
|
-
if (!transitionStr) return;
|
|
36
|
-
el.style.transition = transitionStr;
|
|
37
|
-
el.ontransitionend = (e) => {
|
|
38
|
-
e.stopPropagation();
|
|
39
|
-
if (e.target !== el || e.propertyName !== slowestProp) return;
|
|
40
|
-
el.style.transition = 'none';
|
|
41
|
-
};
|
|
42
|
-
};
|
|
43
|
-
}, [itemActiveStateId, statesForLayout, el]);
|
|
44
|
-
}
|
|
45
|
-
|
|
46
|
-
const CSSPropertyNameMap: Record<keyof ItemState<ArticleItemType>, string> = {
|
|
47
|
-
'width': 'width',
|
|
48
|
-
'height': 'height',
|
|
49
|
-
'top': 'top',
|
|
50
|
-
'left': 'left',
|
|
51
|
-
'scale': 'transform',
|
|
52
|
-
'angle': 'transform',
|
|
53
|
-
'opacity': 'opacity',
|
|
54
|
-
'radius': 'border-radius',
|
|
55
|
-
'strokeWidth': 'border-width',
|
|
56
|
-
'strokeColor': 'border-color',
|
|
57
|
-
'fillColor': 'background-color',
|
|
58
|
-
'blur': 'filter',
|
|
59
|
-
'backdropBlur': 'backdrop-filter',
|
|
60
|
-
'letterSpacing': 'letter-spacing',
|
|
61
|
-
'wordSpacing': 'word-spacing',
|
|
62
|
-
'color': 'color'
|
|
63
|
-
};
|
|
64
|
-
|
|
65
|
-
function getTransition(
|
|
66
|
-
state: ItemStateParams,
|
|
67
|
-
direction: 'in' | 'out',
|
|
68
|
-
values: string[]
|
|
69
|
-
) {
|
|
70
|
-
return Object.entries(state)
|
|
71
|
-
.filter(([key]) => values.includes(key))
|
|
72
|
-
.map(([key, params]) => {
|
|
73
|
-
const cssKey = CSSPropertyNameMap[key];
|
|
74
|
-
if (!cssKey) {
|
|
75
|
-
throw new Error(`Cannot translate "${key}" to a CSS property.`);
|
|
76
|
-
}
|
|
77
|
-
return `${cssKey} ${params[direction].duration}ms ${params[direction].timing} ${params[direction].delay}ms`;
|
|
78
|
-
}, [])
|
|
79
|
-
.join(', ');
|
|
80
|
-
}
|
|
81
|
-
|
|
82
|
-
function getSlowestProperty(state: ItemStateParams, direction: 'in' | 'out', values: string[]): string {
|
|
83
|
-
const mappedEntries = Object.entries(state)
|
|
84
|
-
.filter(([key]) => values.includes(key))
|
|
85
|
-
.map(([key, params]) => {
|
|
86
|
-
const transitionParams = params[direction];
|
|
87
|
-
return {
|
|
88
|
-
key,
|
|
89
|
-
time: transitionParams.duration + transitionParams.delay
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
if (mappedEntries.length === 0) return '';
|
|
93
|
-
const { key } = mappedEntries.reduce((slowest, current) => current.time > slowest.time ? current : slowest);
|
|
94
|
-
return CSSPropertyNameMap[key];
|
|
95
|
-
}
|
|
@@ -1,97 +0,0 @@
|
|
|
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
|
-
});
|
|
@@ -1,65 +0,0 @@
|
|
|
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,24 +0,0 @@
|
|
|
1
|
-
import { AnchorSide, ArticleItemType, ItemStatesMap } from '@cntrl-site/sdk';
|
|
2
|
-
import { getStateStyles } from './StateStyles/StateStyles';
|
|
3
|
-
|
|
4
|
-
type AllKeys<T> = T extends any ? keyof T : never;
|
|
5
|
-
type StatePropertyKey = AllKeys<ItemStatesMap[keyof ItemStatesMap]>;
|
|
6
|
-
|
|
7
|
-
export function getStatesCSS<T extends ArticleItemType>(
|
|
8
|
-
itemId: string,
|
|
9
|
-
classNamePrefix: string,
|
|
10
|
-
keys: Array<StatePropertyKey>,
|
|
11
|
-
states: Record<string, ItemStatesMap[T]> | undefined,
|
|
12
|
-
anchorSide?: AnchorSide
|
|
13
|
-
) {
|
|
14
|
-
return states
|
|
15
|
-
? Object.entries(states).map(([stateId, params]) => {
|
|
16
|
-
return `
|
|
17
|
-
.${classNamePrefix}-${itemId}-state-${stateId} {
|
|
18
|
-
${getStateStyles(keys, params, anchorSide)};
|
|
19
|
-
}
|
|
20
|
-
`;
|
|
21
|
-
}).join('\n')
|
|
22
|
-
: '';
|
|
23
|
-
}
|
|
24
|
-
|