@cntrl-site/sdk-nextjs 1.0.19-alpha.2 → 1.0.19-alpha.4

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.
Files changed (51) hide show
  1. package/lib/components/Article.js +1 -1
  2. package/lib/components/Item.js +31 -36
  3. package/lib/components/items/CodeEmbedItem.js +11 -15
  4. package/lib/components/items/CustomItem.js +7 -12
  5. package/lib/components/items/GroupItem.js +11 -15
  6. package/lib/components/items/ImageItem.js +21 -24
  7. package/lib/components/items/RectangleItem.js +24 -18
  8. package/lib/components/items/RichTextItem.js +19 -14
  9. package/lib/components/items/VideoItem.js +22 -23
  10. package/lib/components/items/VimeoEmbed.js +16 -22
  11. package/lib/components/items/YoutubeEmbed.js +16 -23
  12. package/lib/components/useItemPosition.js +10 -5
  13. package/lib/interactions/CSSPropertyNameMap.js +38 -0
  14. package/lib/interactions/InteractionsRegistry.js +158 -0
  15. package/lib/interactions/ItemInteractionCtrl.js +55 -0
  16. package/lib/interactions/getTransition.js +21 -0
  17. package/lib/interactions/types.js +2 -0
  18. package/lib/interactions/useItemInteractionCtrl.js +17 -0
  19. package/lib/provider/InteractionsContext.js +17 -39
  20. package/lib/utils/getStyleFromItemStateAndParams.js +9 -0
  21. package/package.json +1 -1
  22. package/src/components/Article.tsx +2 -2
  23. package/src/components/Item.tsx +40 -34
  24. package/src/components/items/CodeEmbedItem.tsx +11 -14
  25. package/src/components/items/CustomItem.tsx +8 -13
  26. package/src/components/items/GroupItem.tsx +12 -14
  27. package/src/components/items/ImageItem.tsx +29 -25
  28. package/src/components/items/RectangleItem.tsx +30 -16
  29. package/src/components/items/RichTextItem.tsx +26 -20
  30. package/src/components/items/VideoItem.tsx +31 -25
  31. package/src/components/items/VimeoEmbed.tsx +21 -21
  32. package/src/components/items/YoutubeEmbed.tsx +17 -23
  33. package/src/components/useItemPosition.ts +12 -5
  34. package/src/interactions/CSSPropertyNameMap.ts +38 -0
  35. package/src/interactions/InteractionsRegistry.ts +193 -0
  36. package/src/interactions/ItemInteractionCtrl.ts +56 -0
  37. package/src/interactions/getTransition.ts +27 -0
  38. package/src/interactions/types.ts +32 -0
  39. package/src/interactions/useItemInteractionCtrl.ts +18 -0
  40. package/src/provider/InteractionsContext.old.tsx +65 -0
  41. package/src/provider/InteractionsContext.test.tsx +7 -7
  42. package/src/provider/InteractionsContext.tsx +17 -54
  43. package/src/utils/getStyleFromItemStateAndParams.ts +8 -0
  44. package/lib/components/useStatesClassNames.js +0 -18
  45. package/lib/components/useStatesTransitions.js +0 -89
  46. package/lib/utils/StateStyles/StateStyles.js +0 -89
  47. package/lib/utils/getStatesCSS.js +0 -16
  48. package/src/components/useStatesClassNames.ts +0 -23
  49. package/src/components/useStatesTransitions.ts +0 -95
  50. package/src/utils/StateStyles/StateStyles.ts +0 -99
  51. package/src/utils/getStatesCSS.ts +0 -24
@@ -0,0 +1,193 @@
1
+ import { InteractionsRegistryPort, ItemInteractionCtrl } from './types';
2
+ import {
3
+ Article,
4
+ ArticleItemType,
5
+ Interaction,
6
+ InteractionTrigger,
7
+ ItemAny,
8
+ ItemState,
9
+ StateParams
10
+ } from '@cntrl-site/sdk';
11
+
12
+ export class InteractionsRegistry implements InteractionsRegistryPort {
13
+ private ctrls: Map<ItemId, ItemInteractionCtrl> = new Map();
14
+ private items: ItemAny[];
15
+ private interactions: Interaction[];
16
+ private stateItemsIdsMap: StateItemsIdsMap;
17
+ private interactionStateMap: InteractionStateMap;
18
+ private itemStageMap: ItemStageMap;
19
+
20
+ constructor(article: Article, private layoutId: string) {
21
+ const { interactions } = article;
22
+ const items = article.sections.flatMap((section) => section.items);
23
+ const activeStatesIds = interactions.reduce<StateId[]>((map, inter) => {
24
+ const activeStateId = inter.states.find((state) => state.id !== inter.startStateId)?.id!;
25
+ map.push(activeStateId);
26
+ return map;
27
+ }, []);
28
+ const interactionStateMap = interactions.reduce<InteractionStateMap>((map, { id, startStateId }) => {
29
+ map[id] = startStateId;
30
+ return map;
31
+ }, {});
32
+ const itemStageMap = items.reduce<ItemStageMap>((map, item) => {
33
+ map[item.id] = { type: 'active', isStartState: true };
34
+ return map;
35
+ }, {});
36
+ const stateItemsIdsMap = activeStatesIds.reduce<StateItemsIdsMap>((map, stateId) => {
37
+ map[stateId] = items
38
+ .filter((item) => {
39
+ const layoutStates = item.state[layoutId] ?? {};
40
+ const state = layoutStates?.[stateId] ?? {};
41
+ const hasKeys = Object.keys(state).length !== 0;
42
+ return hasKeys;
43
+ })
44
+ .map((item) => item.id);
45
+ return map;
46
+ }, {});
47
+ this.items = items;
48
+ this.interactions = interactions;
49
+ this.itemStageMap = itemStageMap;
50
+ this.stateItemsIdsMap = stateItemsIdsMap;
51
+ this.interactionStateMap = interactionStateMap;
52
+ }
53
+
54
+ register(itemId: ItemId, ctrl: ItemInteractionCtrl) {
55
+ this.ctrls.set(itemId, ctrl);
56
+ }
57
+
58
+ getStatePropsForItem(itemId: string) {
59
+ const { items, layoutId } = this;
60
+ const item = items.find((item) => item.id === itemId)!;
61
+ const stage = this.itemStageMap[itemId];
62
+ if (!stage) return {};
63
+ // handle "active" stage
64
+ if (stage.type === 'active') {
65
+ if (stage.isStartState) return {};
66
+ const params = item.state[layoutId]?.[stage.stateId!];
67
+ const stateProps = Object.entries(params).reduce<StateProps>((map, [key, stateDetails]) => {
68
+ const style = key as keyof ItemState<ArticleItemType>;
69
+ map[style] = {
70
+ value: stateDetails.value
71
+ };
72
+ return map;
73
+ }, {});
74
+ return stateProps;
75
+ }
76
+ // handle "transitioning" stage
77
+ const activeStateId = stage.direction === 'in' ? stage.to : stage.from;
78
+ const params = item.state[layoutId]?.[activeStateId];
79
+ return Object.entries(params).reduce<StateProps>((map, [key, stateDetails]) => {
80
+ const style = key as keyof ItemState<ArticleItemType>;
81
+ const details = stateDetails as StateParams<string | number>;
82
+ map[style] = {
83
+ value: stage.direction === 'in' ? details.value : undefined,
84
+ transition: {
85
+ timing: details[stage.direction].timing,
86
+ duration: details[stage.direction].duration,
87
+ delay: details[stage.direction].delay
88
+ }
89
+ };
90
+ return map;
91
+ }, {});
92
+ }
93
+
94
+ notifyTrigger(itemId: string, triggerType: TriggerType): void {
95
+ for (const interaction of this.interactions) {
96
+ const currentStateId = this.getCurrentStateByInteractionId(interaction.id);
97
+ const matchingTrigger = interaction.triggers.find((trigger) =>
98
+ trigger.itemId === itemId
99
+ && trigger.from === currentStateId
100
+ && trigger.type === triggerType
101
+ );
102
+ if (!matchingTrigger) continue;
103
+ const activeStateId = this.getActiveInteractionState(interaction.id);
104
+ const isNewStateActive = matchingTrigger.to === activeStateId;
105
+ this.setCurrentStateForInteraction(interaction.id, matchingTrigger.to);
106
+ const transitioningItems = this.stateItemsIdsMap[activeStateId] ?? [];
107
+ for (const [itemId, stage] of Object.entries(this.itemStageMap)) {
108
+ if (!transitioningItems.includes(itemId)) continue;
109
+ this.itemStageMap[itemId] = {
110
+ type: 'transitioning',
111
+ from: stage.type === 'transitioning' ? stage.to : stage.stateId!,
112
+ to: matchingTrigger.to,
113
+ direction: isNewStateActive ? 'in' : 'out'
114
+ };
115
+ }
116
+ this.notifyItemCtrlsChange(transitioningItems);
117
+ this.notifyTransitionStartForItems(transitioningItems, activeStateId);
118
+ }
119
+ }
120
+
121
+ notifyTransitionStartForItems(itemsIds: string[], activeStateId: string) {
122
+ for (const itemId of itemsIds) {
123
+ const ctrl = this.ctrls.get(itemId);
124
+ const item = this.items.find((item) => item.id === itemId)!;
125
+ const keys = Object.keys(item.state[this.layoutId]?.[activeStateId] ?? {});
126
+ ctrl?.handleTransitionStart?.(keys);
127
+ }
128
+ }
129
+
130
+ notifyTransitionEnd(itemId: string): void {
131
+ const prevState = this.itemStageMap[itemId];
132
+ if (prevState.type !== 'transitioning') return; // throw?
133
+ this.itemStageMap[itemId] = {
134
+ type: 'active',
135
+ stateId: prevState.to,
136
+ isStartState: prevState.direction === 'out'
137
+ };
138
+ this.ctrls.get(itemId)?.receiveChange();
139
+ }
140
+
141
+
142
+ private getCurrentStateByInteractionId(id: InteractionId): string {
143
+ let state;
144
+ for (const interactionId of Object.keys(this.interactionStateMap)) {
145
+ if (id !== interactionId) continue;
146
+ state = this.interactionStateMap[interactionId];
147
+ }
148
+ if (!state) throw new Error(`Failed to find current state for interaction w/ id="${id}"`);
149
+ return state;
150
+ }
151
+
152
+ private setCurrentStateForInteraction(interactionId: InteractionId, stateId: StateId) {
153
+ this.interactionStateMap = {
154
+ ...this.interactionStateMap,
155
+ [interactionId]: stateId
156
+ };
157
+ }
158
+
159
+ private getActiveInteractionState(interactionId: InteractionId): string {
160
+ const { interactions } = this;
161
+ const interaction = interactions.find((interaction) => interaction.id === interactionId)!;
162
+ return interaction.states.find(state => state.id !== interaction.startStateId)?.id!;
163
+ }
164
+
165
+ private notifyItemCtrlsChange(itemsIds: string[]) {
166
+ for (const itemId of itemsIds) {
167
+ this.ctrls.get(itemId)?.receiveChange();
168
+ }
169
+ }
170
+ }
171
+
172
+ type ItemStageMap = Record<ItemId, TransitioningStage | ActiveStage>;
173
+ type TransitioningStage = {
174
+ type: 'transitioning';
175
+ from: StateId;
176
+ to: StateId;
177
+ direction: 'in' | 'out';
178
+ };
179
+ type ActiveStage = { type: 'active'; stateId?: string; isStartState: boolean; };
180
+ type InteractionStateMap = Record<InteractionId, StateId>;
181
+ type StateItemsIdsMap = Record<StateId, ItemId[]>;
182
+ type TriggerType = InteractionTrigger['type'];
183
+ type InteractionId = string;
184
+ type StateId = string;
185
+ type ItemId = string;
186
+ type StateProps = Record<string, {
187
+ value?: string | number;
188
+ transition?: {
189
+ timing: string;
190
+ duration: number;
191
+ delay: number;
192
+ };
193
+ }>;
@@ -0,0 +1,56 @@
1
+ import { InteractionsRegistryPort, ItemInteractionCtrl } from './types';
2
+ import { getTransition } from './getTransition';
3
+ import { getStyleKeysFromCSSProperty } from './CSSPropertyNameMap';
4
+
5
+ export class ItemInteractionController implements ItemInteractionCtrl {
6
+ private transitionsInProgress: Set<string> = new Set();
7
+ constructor(
8
+ private itemId: string,
9
+ private registry: InteractionsRegistryPort,
10
+ private onChange: () => void
11
+ ) {
12
+ this.registry.register(itemId, this);
13
+ }
14
+
15
+ getState(keys: string[]) {
16
+ const stateProps = this.registry.getStatePropsForItem(this.itemId);
17
+ const styles = keys.reduce<Record<string, string | number>>((map, styleKey) => {
18
+ const prop = stateProps[styleKey];
19
+ if (prop?.value === undefined) return map;
20
+ map[styleKey] = prop.value;
21
+ return map;
22
+ }, {});
23
+ const transition = getTransition(stateProps, keys);
24
+ return {
25
+ styles,
26
+ transition
27
+ };
28
+ }
29
+
30
+ sendTrigger(type: 'click' | 'hover-in' | 'hover-out') {
31
+ this.registry.notifyTrigger(this.itemId, type);
32
+ }
33
+
34
+ handleTransitionStart = (types: string[]) => {
35
+ this.transitionsInProgress.clear();
36
+ for (const type of types) {
37
+ this.transitionsInProgress.add(type);
38
+ }
39
+ };
40
+
41
+ handleTransitionEnd = (cssPropKey: string) => {
42
+ const styleKeys = getStyleKeysFromCSSProperty(cssPropKey);
43
+ for (const key of styleKeys) {
44
+ const found = this.transitionsInProgress.has(key);
45
+ if (!found) continue;
46
+ this.transitionsInProgress.delete(key);
47
+ break;
48
+ }
49
+ if (this.transitionsInProgress.size !== 0) return;
50
+ this.registry.notifyTransitionEnd(this.itemId);
51
+ };
52
+
53
+ receiveChange() {
54
+ this.onChange();
55
+ }
56
+ }
@@ -0,0 +1,27 @@
1
+ import { CSSPropertyNameMap } from './CSSPropertyNameMap';
2
+
3
+ export function getTransition(
4
+ state: Record<string, StateInfo>,
5
+ keys: string[]
6
+ ): string {
7
+ if (Object.keys(state).length === 0) return 'none';
8
+ const transitions = [];
9
+ for (const [key, params] of Object.entries(state)) {
10
+ if (!keys.includes(key) || !params.transition) continue;
11
+ const { transition: { duration, delay, timing } } = params;
12
+ const cssKey = CSSPropertyNameMap[key]!;
13
+ const nonZeroDuration = Math.max(duration, 0.01);
14
+ transitions.push(`${cssKey} ${nonZeroDuration}ms ${timing} ${delay}ms`);
15
+ }
16
+ return transitions.length > 0
17
+ ? transitions.join(', ')
18
+ : 'none';
19
+ }
20
+
21
+ type StateInfo = {
22
+ transition?: {
23
+ duration: number;
24
+ delay: number;
25
+ timing: string;
26
+ };
27
+ };
@@ -0,0 +1,32 @@
1
+ import { ArticleItemType, ItemState } from '@cntrl-site/sdk';
2
+
3
+ export interface ItemInteractionCtrl {
4
+ getState(keys: string[]): StateCSSInfo;
5
+ sendTrigger(type: 'click' | 'hover-in' | 'hover-out'): void;
6
+ handleTransitionEnd?: (styleKey: string) => void;
7
+ handleTransitionStart?: (styleKeys: string[]) => void;
8
+ receiveChange: () => void;
9
+ }
10
+
11
+ export interface InteractionsRegistryPort {
12
+ register(itemId: string, ctrl: ItemInteractionCtrl): void;
13
+ getStatePropsForItem(itemId: string): StateProps;
14
+ notifyTrigger(itemId: string, type: TriggerType): void;
15
+ notifyTransitionEnd(itemId: string): void;
16
+ }
17
+
18
+ type StateCSSInfo = {
19
+ styles: Partial<Record<string, string | number>>;
20
+ transition?: string;
21
+ };
22
+
23
+ type StateProps = Record<keyof ItemState<ArticleItemType>, {
24
+ value?: string | number;
25
+ transition?: {
26
+ timing: string;
27
+ duration: number;
28
+ delay: number;
29
+ };
30
+ }>;
31
+
32
+ type TriggerType = 'click' | 'hover-in' | 'hover-out';
@@ -0,0 +1,18 @@
1
+ import { useMemo, useState } from 'react';
2
+ import { ItemInteractionController } from './ItemInteractionCtrl';
3
+ import { ItemInteractionCtrl } from './types';
4
+ import { useInteractionsRegistry } from '../provider/InteractionsContext';
5
+
6
+ export function useItemInteractionCtrl(itemId: string): ItemInteractionCtrl | undefined {
7
+ const [_, triggerRender] = useState(0);
8
+ const registry = useInteractionsRegistry();
9
+ const ctrl = useMemo(() => {
10
+ if (!registry) return;
11
+ return new ItemInteractionController(
12
+ itemId,
13
+ registry,
14
+ () => triggerRender(prev => prev + 1)
15
+ );
16
+ }, [itemId, registry]);
17
+ return ctrl;
18
+ }
@@ -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 InteractionsContextOld = 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
+ <InteractionsContextOld.Provider value={{
51
+ transitionTo,
52
+ interactionsStatesMap,
53
+ interactions,
54
+ getItemTrigger
55
+ }}>
56
+ {children}
57
+ </InteractionsContextOld.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,6 +1,6 @@
1
1
  import React from 'react';
2
2
  import { render, waitFor } from '@testing-library/react';
3
- import { InteractionsProvider, InteractionsContext } from './InteractionsContext';
3
+ import { InteractionsProvider, InteractionsContextOld } from './InteractionsContext.old';
4
4
  import { Interaction } from '@cntrl-site/sdk';
5
5
 
6
6
  describe('InteractionsProvider', () => {
@@ -28,12 +28,12 @@ describe('InteractionsProvider', () => {
28
28
 
29
29
  render(
30
30
  <InteractionsProvider interactions={interactions}>
31
- <InteractionsContext.Consumer>
31
+ <InteractionsContextOld.Consumer>
32
32
  {value => {
33
33
  contextValue = value;
34
34
  return null;
35
35
  }}
36
- </InteractionsContext.Consumer>
36
+ </InteractionsContextOld.Consumer>
37
37
  </InteractionsProvider>
38
38
  );
39
39
 
@@ -49,12 +49,12 @@ describe('InteractionsProvider', () => {
49
49
 
50
50
  render(
51
51
  <InteractionsProvider interactions={interactions}>
52
- <InteractionsContext.Consumer>
52
+ <InteractionsContextOld.Consumer>
53
53
  {value => {
54
54
  contextValue = value;
55
55
  return null;
56
56
  }}
57
- </InteractionsContext.Consumer>
57
+ </InteractionsContextOld.Consumer>
58
58
  </InteractionsProvider>
59
59
  );
60
60
 
@@ -71,12 +71,12 @@ describe('InteractionsProvider', () => {
71
71
 
72
72
  render(
73
73
  <InteractionsProvider interactions={interactions}>
74
- <InteractionsContext.Consumer>
74
+ <InteractionsContextOld.Consumer>
75
75
  {value => {
76
76
  contextValue = value;
77
77
  return null;
78
78
  }}
79
- </InteractionsContext.Consumer>
79
+ </InteractionsContextOld.Consumer>
80
80
  </InteractionsProvider>
81
81
  );
82
82
 
@@ -1,65 +1,28 @@
1
- import { createContext, FC, PropsWithChildren, useState } from 'react';
2
- import { Interaction, InteractionTrigger } from '@cntrl-site/sdk';
1
+ import { createContext, FC, PropsWithChildren, useContext, useMemo } from 'react';
2
+ import { InteractionsRegistry } from '../interactions/InteractionsRegistry';
3
+ import { Article } from '@cntrl-site/sdk';
4
+ import { useCurrentLayout } from '../common/useCurrentLayout';
3
5
 
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);
6
+ export const InteractionsContext = createContext<InteractionsRegistry | undefined>(undefined);
17
7
 
18
8
  interface Props {
19
- interactions: Interaction[];
9
+ article: Article;
20
10
  }
21
11
 
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
- };
12
+ export const InteractionsProvider: FC<PropsWithChildren<Props>> = ({ article, children }) => {
13
+ const { layoutId } = useCurrentLayout();
14
+ const registry = useMemo(() => {
15
+ if (!layoutId) return;
16
+ return new InteractionsRegistry(article, layoutId);
17
+ }, [layoutId]);
49
18
  return (
50
- <InteractionsContext.Provider value={{
51
- transitionTo,
52
- interactionsStatesMap,
53
- interactions,
54
- getItemTrigger
55
- }}>
19
+ <InteractionsContext.Provider value={registry}>
56
20
  {children}
57
21
  </InteractionsContext.Provider>
58
22
  );
59
23
  };
60
24
 
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;
25
+ export function useInteractionsRegistry(): InteractionsRegistry | undefined {
26
+ const registry = useContext(InteractionsContext);
27
+ return registry;
28
+ }
@@ -0,0 +1,8 @@
1
+ export function getStyleFromItemStateAndParams<T extends string | number>(
2
+ stateValue: string | number | undefined,
3
+ paramsValue: T | undefined
4
+ ): T | undefined {
5
+ return (stateValue as T) !== undefined
6
+ ? stateValue as T
7
+ : paramsValue;
8
+ }
@@ -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
- }