Skip to content

Commit cd3d71f

Browse files
feat(overmind): implement state charts
1 parent f1444cf commit cd3d71f

File tree

18 files changed

+1345
-492
lines changed

18 files changed

+1345
-492
lines changed

packages/node_modules/overmind-devtools-client/src/components/ActionPayload/index.tsx

Lines changed: 22 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,26 +1,44 @@
1-
import { createElement, FunctionComponent } from 'react'
2-
import * as styles from './styles'
1+
import { FunctionComponent, createElement, useEffect, useRef } from 'react'
2+
33
import { useOvermind } from '../../overmind'
44
import { isValidJson } from '../../overmind/utils'
55
import { colors } from '../../theme'
6+
import * as styles from './styles'
67

78
const ActionPayload: FunctionComponent = () => {
8-
const { state, actions } = useOvermind()
9+
const { state, actions, reaction } = useOvermind()
10+
const input = useRef(null)
11+
12+
useEffect(() => {
13+
reaction(
14+
() => state.currentApp.selectedActionQuery,
15+
() => setTimeout(() => input.current && input.current.focus())
16+
)
17+
}, [])
18+
919
const payload = state.currentApp.actionQueryPayload
1020

1121
return (
1222
<div className={styles.wrapper}>
1323
<input
24+
ref={input}
1425
style={{
1526
borderColor:
1627
!payload || isValidJson(payload) ? 'transparent' : colors.red,
1728
}}
18-
disabled={!state.currentApp.selectedActionQuery}
29+
disabled={
30+
!state.currentApp.selectedActionQuery || state.isExecutingAction
31+
}
1932
placeholder={
2033
state.currentApp.selectedActionQuery
2134
? 'Add some JSON payload...'
2235
: null
2336
}
37+
onKeyDown={(event) => {
38+
if (event.keyCode === 13) {
39+
actions.executeAction()
40+
}
41+
}}
2442
className={styles.input}
2543
value={payload}
2644
onChange={(event) =>

packages/node_modules/overmind-devtools-client/src/components/ActionsTools/index.tsx

Lines changed: 11 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,11 @@
1-
import { createElement, FunctionComponent } from 'react'
2-
import * as styles from './styles'
1+
import { FunctionComponent, createElement } from 'react'
2+
import { FaRocket } from 'react-icons/fa'
3+
34
import { useOvermind } from '../../overmind'
4-
import ActionSelector from '../ActionSelector'
5-
import ActionPayload from '../ActionPayload'
65
import { colors } from '../../theme'
7-
import { FaRocket } from 'react-icons/fa'
6+
import ActionPayload from '../ActionPayload'
7+
import ActionSelector from '../ActionSelector'
8+
import * as styles from './styles'
89

910
const ActionsTools: FunctionComponent = () => {
1011
const { state, actions } = useOvermind()
@@ -21,11 +22,12 @@ const ActionsTools: FunctionComponent = () => {
2122
<ActionPayload />
2223
<div
2324
className={styles.button}
24-
onClick={() => actions.executeAction()}
25+
onClick={state.isExecutingAction ? null : () => actions.executeAction()}
2526
style={{
26-
backgroundColor: state.currentApp.selectedActionQuery
27-
? colors.green
28-
: colors.highlight,
27+
backgroundColor:
28+
!state.isExecutingAction && state.currentApp.selectedActionQuery
29+
? colors.green
30+
: colors.highlight,
2931
}}
3032
>
3133
<FaRocket />

packages/node_modules/overmind-devtools-client/src/components/ActionsTools/styles.ts

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
11
import { css } from 'emotion'
2+
23
import { colors } from '../../theme'
34

45
export const wrapper = css({
Lines changed: 177 additions & 102 deletions
Original file line numberDiff line numberDiff line change
@@ -1,124 +1,199 @@
11
import { css } from 'emotion'
22
import { FunctionComponent, createElement } from 'react'
3-
import { nameToColor } from '../../overmind/utils'
3+
44
import { useOvermind } from '../../overmind'
5+
import { Chart, NestedChart } from '../../overmind/types'
6+
import { nameToColor } from '../../overmind/utils'
57
import * as textStyles from '../../styles/text'
68
import * as styles from './styles'
79

8-
const Chart: FunctionComponent = () => {
9-
const { state, actions } = useOvermind()
10+
type Props = {
11+
chart: Chart
12+
statePath: string[]
13+
nestedChart?: NestedChart
14+
}
1015

11-
const chart = state.currentChart
16+
const ChartComponent: FunctionComponent<Props> = ({
17+
chart,
18+
statePath,
19+
nestedChart = chart.chart,
20+
}) => {
21+
const { state, actions } = useOvermind()
1222

1323
return (
14-
<div className={styles.wrapper}>
15-
{Object.keys(chart.chart).map((key) => (
16-
<div key={key} className={styles.stateItem}>
17-
<div className={styles.stateNameCell}>
18-
<div
19-
className={styles.stateName}
20-
style={{
21-
backgroundColor:
22-
chart.state === key
23-
? nameToColor(chart.path.join('.'))
24-
: null,
25-
}}
26-
>
27-
<span className={textStyles.label}>{key}</span>
28-
</div>
29-
</div>
30-
<div className={styles.stateLineCell}>
31-
<div
32-
className={styles.selectedLine}
33-
style={{
34-
backgroundColor:
35-
chart.state === key
36-
? nameToColor(chart.path.join('.'))
37-
: null,
38-
}}
39-
/>
40-
</div>
41-
{chart.chart[key].entry ? (
42-
<div className={styles.stateNameCell}>
43-
<div className={styles.stateName}>
44-
<span className={textStyles.normal}>
45-
{chart.chart[key].entry}
46-
</span>
24+
<div className={styles.outerWrapper}>
25+
<div className={styles.wrapper}>
26+
{Object.keys(nestedChart.states).map((key) => {
27+
const isCurrentState = chart.state
28+
.join('.')
29+
.startsWith(statePath.concat(key).join('.'))
30+
return (
31+
<div key={key} className={styles.stateItem}>
32+
<div className={styles.stateNameCell}>
33+
<div
34+
className={styles.stateName}
35+
style={{
36+
backgroundColor: isCurrentState
37+
? nameToColor(chart.path.join('.'))
38+
: null,
39+
}}
40+
>
41+
<span className={textStyles.label}>{key}</span>
42+
</div>
4743
</div>
48-
</div>
49-
) : (
50-
<div className={styles.stateLineCell}>
51-
<div
52-
className={styles.selectedLine}
53-
style={{
54-
backgroundColor:
55-
chart.state === key
44+
<div className={styles.stateLineCell}>
45+
<div
46+
className={styles.selectedLine}
47+
style={{
48+
backgroundColor: isCurrentState
5649
? nameToColor(chart.path.join('.'))
5750
: null,
58-
}}
59-
/>
60-
</div>
61-
)}
62-
<div className={styles.stateLineCell}>
63-
<div
64-
className={styles.selectedLine}
65-
style={{
66-
backgroundColor:
67-
chart.state === key
68-
? nameToColor(chart.path.join('.'))
69-
: null,
70-
}}
71-
/>
72-
</div>
73-
<div
74-
className={styles.onCell}
75-
style={{
76-
borderColor:
77-
chart.state === key ? nameToColor(chart.path.join('.')) : null,
78-
}}
79-
>
80-
{Object.keys(chart.chart[key].on).map((onKey) => (
81-
<div
82-
key={onKey}
83-
className={css(
84-
styles.onName,
85-
chart.actions[onKey] && styles.activeAction
86-
)}
87-
>
88-
<span className={textStyles.normal}>{onKey}</span>
51+
}}
52+
/>
8953
</div>
90-
))}
91-
</div>
92-
{chart.chart[key].exit ? (
93-
<div className={styles.stateLineCell}>
94-
<div
95-
className={styles.selectedLine}
96-
style={{
97-
backgroundColor:
98-
chart.state === key
54+
{nestedChart.states[key].entry ? (
55+
<div className={styles.stateNameCell}>
56+
<div
57+
className={styles.stateAction}
58+
style={{
59+
borderColor: isCurrentState
60+
? nameToColor(chart.path.join('.'))
61+
: null,
62+
}}
63+
>
64+
<span className={textStyles.normal}>
65+
{nestedChart.states[key].entry}
66+
</span>
67+
</div>
68+
</div>
69+
) : (
70+
<div className={styles.stateLineCell}>
71+
<div
72+
className={styles.selectedLine}
73+
style={{
74+
backgroundColor: isCurrentState
75+
? nameToColor(chart.path.join('.'))
76+
: null,
77+
}}
78+
/>
79+
</div>
80+
)}
81+
<div className={styles.stateLineCell}>
82+
<div
83+
className={styles.selectedLine}
84+
style={{
85+
backgroundColor: isCurrentState
9986
? nameToColor(chart.path.join('.'))
10087
: null,
88+
}}
89+
/>
90+
</div>
91+
<div
92+
className={styles.onCell}
93+
style={{
94+
borderColor: isCurrentState
95+
? nameToColor(chart.path.join('.'))
96+
: null,
10197
}}
102-
/>
103-
</div>
104-
) : (
105-
<div className={styles.stateLineCell} />
106-
)}
107-
{chart.chart[key].exit ? (
108-
<div className={styles.stateNameCell}>
109-
<div className={styles.stateName}>
110-
<span className={textStyles.normal}>
111-
{chart.chart[key].exit}
112-
</span>
98+
>
99+
<div
100+
className={styles.wrapper}
101+
style={{ margin: 0, width: '100%' }}
102+
>
103+
{Object.keys(nestedChart.states[key].on).map((onKey) => {
104+
let target
105+
if (nestedChart.states[key].on[onKey]) {
106+
target =
107+
// @ts-ignore
108+
nestedChart.states[key].on[onKey].target ||
109+
nestedChart.states[key].on[onKey]
110+
}
111+
return (
112+
<div key={onKey} className={styles.stateItem}>
113+
<div
114+
onClick={
115+
chart.actions[onKey]
116+
? () =>
117+
actions.selectQueryAction(
118+
chart.path.concat(onKey).join('.')
119+
)
120+
: null
121+
}
122+
className={css(
123+
styles.stateNameCell,
124+
styles.onName,
125+
chart.actions[onKey] && styles.activeAction,
126+
chart.path.concat(onKey).join('.') ===
127+
state.currentApp.selectedActionQuery &&
128+
styles.selectedAction
129+
)}
130+
>
131+
<span className={textStyles.normal}>{onKey}</span>
132+
</div>
133+
<div className={styles.stateLineCell}>
134+
{target ? (
135+
<div className={styles.transitionLine} />
136+
) : null}
137+
</div>
138+
<div className={styles.transitionCell}>{target}</div>
139+
<div className={styles.nestedRoomCell} />
140+
</div>
141+
)
142+
})}
143+
{nestedChart.states[key].initial ? (
144+
<div className={styles.stateItem}>
145+
<div className={styles.nestedChart}>
146+
<ChartComponent
147+
chart={chart}
148+
statePath={statePath.concat(key)}
149+
nestedChart={{
150+
initial: nestedChart.states[key].initial,
151+
states: nestedChart.states[key].states,
152+
}}
153+
/>
154+
</div>
155+
</div>
156+
) : null}
157+
</div>
113158
</div>
159+
{nestedChart.states[key].exit ? (
160+
<div className={styles.stateLineCell}>
161+
<div
162+
className={styles.selectedLine}
163+
style={{
164+
backgroundColor: isCurrentState
165+
? nameToColor(chart.path.join('.'))
166+
: null,
167+
}}
168+
/>
169+
</div>
170+
) : (
171+
<div className={styles.stateLineCell} />
172+
)}
173+
{nestedChart.states[key].exit ? (
174+
<div className={styles.stateNameCell}>
175+
<div
176+
className={styles.stateAction}
177+
style={{
178+
borderColor: isCurrentState
179+
? nameToColor(chart.path.join('.'))
180+
: null,
181+
}}
182+
>
183+
<span className={textStyles.normal}>
184+
{nestedChart.states[key].exit}
185+
</span>
186+
</div>
187+
</div>
188+
) : (
189+
<div className={styles.stateLineCell} />
190+
)}
114191
</div>
115-
) : (
116-
<div className={styles.stateLineCell} />
117-
)}
118-
</div>
119-
))}
192+
)
193+
})}
194+
</div>
120195
</div>
121196
)
122197
}
123198

124-
export default Chart
199+
export default ChartComponent

0 commit comments

Comments
 (0)