Skip to content

Commit 128df01

Browse files
committed
port framebuffertabs to typescript
1 parent e6e27ea commit 128df01

File tree

3 files changed

+119
-53
lines changed

3 files changed

+119
-53
lines changed

src/containers/ContextMenuArea.tsx

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -7,7 +7,7 @@ const { Menu } = remote;
77

88
interface ContextMenuAreaProps {
99
menuItems: any[];
10-
style: React.CSSProperties;
10+
style?: React.CSSProperties;
1111
};
1212

1313
// Copied from https://github.com/johot/react-electron-contextmenu (with some

src/containers/FramebufferTabs.js renamed to src/containers/FramebufferTabs.tsx

Lines changed: 116 additions & 51 deletions
Original file line numberDiff line numberDiff line change
@@ -1,17 +1,17 @@
11

22
import React, { Component, PureComponent } from 'react';
33
import { connect } from 'react-redux'
4-
import { bindActionCreators } from 'redux'
4+
import { Dispatch, bindActionCreators } from 'redux'
55
import { SortableContainer, SortableElement, arrayMove } from '../external/react-sortable-hoc'
66

77
import classnames from 'classnames'
88

99
import ContextMenuArea from './ContextMenuArea'
1010

1111
import CharGrid from '../components/CharGrid'
12-
import { Framebuffer } from '../redux/editor'
13-
import { Toolbar } from '../redux/toolbar'
14-
import * as Screens from '../redux/screens'
12+
import * as framebuf from '../redux/editor'
13+
import * as toolbar from '../redux/toolbar'
14+
import * as screens from '../redux/screens'
1515
import * as selectors from '../redux/selectors'
1616
import * as screensSelectors from '../redux/screensSelectors'
1717
import { getSettingsCurrentColorPalette } from '../redux/settingsSelectors'
@@ -23,11 +23,28 @@ import { faPlus } from '@fortawesome/free-solid-svg-icons'
2323
import { FontAwesomeIcon } from '@fortawesome/react-fontawesome'
2424

2525
import styles from './FramebufferTabs.module.css'
26+
import { Framebuf, Rgb, Font, RootState } from '../redux/types';
27+
28+
interface NameInputDispatchProps {
29+
Toolbar: toolbar.PropsFromDispatch;
30+
}
31+
32+
interface NameInputProps {
33+
name: string;
34+
35+
onSubmit: (name: string) => void;
36+
onCancel: () => void;
37+
onBlur: () => void;
38+
}
39+
40+
interface NameInputState {
41+
name: string;
42+
}
2643

2744
// This class is a bit funky with how it disables/enables keyboard shortcuts
2845
// globally for the app while the input element has focus. Maybe there'd be a
2946
// better way to do this, but this seems to work.
30-
class NameInput_ extends Component {
47+
class NameInput_ extends Component<NameInputProps & NameInputDispatchProps, NameInputState> {
3148
state = {
3249
name: this.props.name
3350
}
@@ -36,32 +53,34 @@ class NameInput_ extends Component {
3653
this.props.Toolbar.setShortcutsActive(true)
3754
}
3855

39-
handleSubmit = (e) => {
56+
handleSubmit = (e: React.FormEvent<HTMLFormElement>) => {
4057
e.preventDefault()
4158
this.props.onSubmit(this.state.name)
4259
this.props.Toolbar.setShortcutsActive(true)
4360
}
4461

45-
handleChange = (e) => {
46-
this.setState({ name: e.target.value })
62+
handleChange = (e: React.FormEvent<EventTarget>) => {
63+
let target = e.target as HTMLInputElement;
64+
this.setState({ name: target.value })
4765
}
4866

49-
handleKeyDown = (e) => {
67+
handleKeyDown = (e: React.KeyboardEvent) => {
5068
if (e.key === 'Escape') {
5169
e.preventDefault()
5270
this.props.onCancel()
5371
this.props.Toolbar.setShortcutsActive(true)
5472
}
5573
}
5674

57-
handleBlur = (e) => {
75+
handleBlur = (_e: React.FormEvent<HTMLInputElement>) => {
5876
this.props.onBlur()
5977
this.props.Toolbar.setShortcutsActive(true)
6078
}
6179

62-
handleFocus = (e) => {
80+
handleFocus = (e: React.FormEvent<HTMLInputElement>) => {
81+
let target = e.target as HTMLInputElement;
6382
this.props.Toolbar.setShortcutsActive(false)
64-
e.target.select()
83+
target.select()
6584
}
6685

6786
render () {
@@ -90,17 +109,27 @@ class NameInput_ extends Component {
90109

91110
const NameInput = connect(
92111
null,
93-
(dispatch, ownProps) => {
112+
(dispatch) => {
94113
return {
95-
Toolbar: Toolbar.bindDispatch(dispatch)
114+
Toolbar: bindActionCreators(toolbar.Toolbar.actions, dispatch)
96115
}
97116
}
98117
)(NameInput_)
99118

100119

101-
class NameEditor extends Component {
120+
interface NameEditorProps {
121+
name: string;
122+
123+
onNameSave: (name: string) => void;
124+
}
125+
126+
interface NameEditorState {
127+
editing: boolean;
128+
}
129+
130+
class NameEditor extends Component<NameEditorProps, NameEditorState> {
102131
state = {
103-
editing: false,
132+
editing: false
104133
}
105134

106135
handleEditingClick = () => {
@@ -111,7 +140,7 @@ class NameEditor extends Component {
111140
this.setState({ editing: false})
112141
}
113142

114-
handleSubmit = (name) => {
143+
handleSubmit = (name: string) => {
115144
this.setState({ editing: false})
116145
this.props.onNameSave(name)
117146
}
@@ -139,7 +168,21 @@ class NameEditor extends Component {
139168
}
140169
}
141170

142-
class FramebufTab extends PureComponent {
171+
interface FramebufTabProps {
172+
id: number;
173+
active: boolean;
174+
framebufId: number;
175+
framebuf: Framebuf;
176+
colorPalette: Rgb[];
177+
font: Font;
178+
179+
setName: (name: string, framebufId: number) => void;
180+
onSetActiveTab: (id: number) => void;
181+
onDuplicateTab: (id: number) => void;
182+
onRemoveTab: (id: number) => void;
183+
};
184+
185+
class FramebufTab extends PureComponent<FramebufTabProps> {
143186
handleSelect = () => {
144187
this.props.onSetActiveTab(this.props.id)
145188
}
@@ -152,7 +195,7 @@ class FramebufTab extends PureComponent {
152195
this.props.onRemoveTab(this.props.id)
153196
}
154197

155-
handleNameSave = (name) => {
198+
handleNameSave = (name: string) => {
156199
if (name !== '') {
157200
this.props.setName(name, this.props.framebufId)
158201
}
@@ -223,28 +266,43 @@ class FramebufTab extends PureComponent {
223266
</div>
224267
</ContextMenuArea>
225268
<NameEditor
226-
name={fp.maybeDefault(this.props.framebuf.name, 'Untitled')}
269+
name={fp.maybeDefault(this.props.framebuf.name, 'Untitled' as string)}
227270
onNameSave={this.handleNameSave}
228271
/>
229272
</div>
230273
)
231274
}
232275
}
233276

234-
const SortableFramebufTab = SortableElement((props) =>
277+
const SortableFramebufTab = SortableElement((props: FramebufTabProps) =>
235278
<FramebufTab {...props} />
236279
)
237280

238-
const SortableTabList = SortableContainer(({children}) => {
281+
const SortableTabList = SortableContainer((props: {children: any}) => {
239282
return (
240283
<div className={styles.tabs}>
241-
{children}
284+
{props.children}
242285
</div>
243286
)
244287
})
245288

246-
class FramebufferTabs_ extends Component {
247-
handleActiveClick = (idx) => {
289+
interface FramebufferTabsDispatch {
290+
Screens: screens.PropsFromDispatch;
291+
Toolbar: toolbar.PropsFromDispatch;
292+
}
293+
294+
interface FramebufferTabsProps {
295+
screens: number[];
296+
activeScreen: number;
297+
colorPalette: Rgb[];
298+
299+
getFramebufByIndex: (framebufId: number) => Framebuf | null;
300+
getFont: (framebuf: Framebuf) => Font;
301+
setFramebufName: (name: string) => void;
302+
}
303+
304+
class FramebufferTabs_ extends Component<FramebufferTabsProps & FramebufferTabsDispatch> {
305+
handleActiveClick = (idx: number) => {
248306
this.props.Screens.setCurrentScreenIndex(idx)
249307
}
250308

@@ -254,25 +312,25 @@ class FramebufferTabs_ extends Component {
254312
this.props.Toolbar.setCtrlKey(false)
255313
}
256314

257-
handleRemoveTab = (idx) => {
315+
handleRemoveTab = (idx: number) => {
258316
this.props.Screens.removeScreen(idx)
259317
// Context menu eats the ctrl key up event, so force it to false
260318
this.props.Toolbar.setCtrlKey(false)
261319
}
262320

263-
handleDuplicateTab = (idx) => {
321+
handleDuplicateTab = (idx: number) => {
264322
this.props.Screens.cloneScreen(idx)
265323
// Context menu eats the ctrl key up event, so force it to false
266324
this.props.Toolbar.setCtrlKey(false)
267325
}
268326

269-
onSortEnd = ({oldIndex, newIndex}) => {
270-
this.props.Screens.setScreenOrder(arrayMove(this.props.screens, oldIndex, newIndex))
327+
onSortEnd = (args: {oldIndex: number, newIndex: number}) => {
328+
this.props.Screens.setScreenOrder(arrayMove(this.props.screens, args.oldIndex, args.newIndex))
271329
}
272330

273331
render () {
274332
const lis = this.props.screens.map((framebufId, i) => {
275-
const framebuf = this.props.getFramebufByIndex(framebufId)
333+
const framebuf = this.props.getFramebufByIndex(framebufId)!
276334
return (
277335
<SortableFramebufTab
278336
key={framebufId}
@@ -286,10 +344,13 @@ class FramebufferTabs_ extends Component {
286344
active={i === this.props.activeScreen}
287345
font={this.props.getFont(framebuf)}
288346
colorPalette={this.props.colorPalette}
289-
setName={this.props.Framebuffer.setName}
347+
setName={this.props.setFramebufName}
290348
/>
291349
)
292350
})
351+
// onClick is not in FontAwesomeIcon props and don't know how to pass
352+
// it otherwise.
353+
const typingWorkaround = { onClick: this.handleNewTab };
293354
return (
294355
<div className={styles.tabHeadings}>
295356
<SortableTabList
@@ -300,32 +361,36 @@ class FramebufferTabs_ extends Component {
300361
>
301362
{lis}
302363
<div className={classnames(styles.tab, styles.newScreen)}>
303-
<FontAwesomeIcon onClick={this.handleNewTab} icon={faPlus} />
364+
<FontAwesomeIcon {...typingWorkaround} icon={faPlus} />
304365
</div>
305366
</SortableTabList>
306367
</div>
307368
)
308369
}
309370
}
310371

311-
const mapDispatchToProps = (dispatch, ownProps) => {
312-
return {
313-
Toolbar: Toolbar.bindDispatch(dispatch),
314-
Framebuffer: Framebuffer.bindDispatch(dispatch),
315-
Screens: bindActionCreators(Screens.actions, dispatch)
316-
}
317-
}
318-
319-
const mapStateToProps = state => {
320-
return {
321-
activeScreen: screensSelectors.getCurrentScreenIndex(state),
322-
screens: screensSelectors.getScreens(state),
323-
getFramebufByIndex: (idx) => selectors.getFramebufByIndex(state, idx),
324-
getFont: (fb) => selectors.getFramebufFont(state, fb),
325-
colorPalette: getSettingsCurrentColorPalette(state)
326-
}
327-
}
328372
export default connect(
329-
mapStateToProps,
330-
mapDispatchToProps,
373+
(state: RootState) => {
374+
return {
375+
activeScreen: screensSelectors.getCurrentScreenIndex(state),
376+
screens: screensSelectors.getScreens(state),
377+
getFramebufByIndex: (idx: number) => selectors.getFramebufByIndex(state, idx),
378+
getFont: (fb: Framebuf) => selectors.getFramebufFont(state, fb),
379+
colorPalette: getSettingsCurrentColorPalette(state)
380+
}
381+
},
382+
(dispatch) => {
383+
return {
384+
Toolbar: toolbar.Toolbar.bindDispatch(dispatch),
385+
Screens: bindActionCreators(screens.actions, dispatch),
386+
setFramebufName: (name: string) => {
387+
return (dispatch: Dispatch, getState: () => RootState) => {
388+
const framebufIndex = screensSelectors.getCurrentScreenFramebufIndex(getState());
389+
if (framebufIndex != null) {
390+
dispatch(framebuf.actions.setName(name, framebufIndex));
391+
}
392+
}
393+
}
394+
}
395+
}
331396
)(FramebufferTabs_)

src/redux/screens.ts

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,7 +19,7 @@ import {
1919
RootState,
2020
Screens
2121
} from './types'
22-
import { ActionsUnion, createAction } from './typeUtils'
22+
import { ActionsUnion, createAction, DispatchPropsFromActions } from './typeUtils'
2323

2424
import { makeScreenName } from './utils'
2525

@@ -118,6 +118,7 @@ export const actions = {
118118
}
119119

120120
export type Actions = ActionsUnion<typeof actionCreators>;
121+
export type PropsFromDispatch = DispatchPropsFromActions<typeof actions>;
121122

122123
export function reducer(state: Screens = {current: 0, list: []}, action: Actions): Screens {
123124
switch (action.type) {

0 commit comments

Comments
 (0)