Skip to content

Commit

Permalink
tgui-next: useBackend edition (tgstation#48010)
Browse files Browse the repository at this point in the history
* Initial useBackend implementation

* Lots of random ui shit

* Bulldozer

* Fix an infinite recursion

* Quotes

* Rebuild tgui
  • Loading branch information
stylemistake authored and actioninja committed Nov 30, 2019
1 parent c2e89e8 commit 085b30b
Show file tree
Hide file tree
Showing 89 changed files with 1,474 additions and 1,473 deletions.
2 changes: 1 addition & 1 deletion code/datums/components/uplink.dm
Original file line number Diff line number Diff line change
Expand Up @@ -129,7 +129,7 @@ GLOBAL_LIST_EMPTY(uplinks)
active = TRUE
ui = SStgui.try_update_ui(user, src, ui_key, ui, force_open)
if(!ui)
ui = new(user, src, ui_key, "uplink", name, 720, 480, master_ui, state)
ui = new(user, src, ui_key, "uplink", name, 620, 580, master_ui, state)
ui.set_autoupdate(FALSE) // This UI is only ever opened by one person, and never is updated outside of user input.
ui.set_style("syndicate")
ui.open()
Expand Down
7 changes: 5 additions & 2 deletions tgui-next/packages/tgui-dev-server/link/client.js
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,8 @@ const ensureConnection = () => {
return;
}
if (!socket || socket.readyState === WebSocket.CLOSED) {
socket = new WebSocket('ws://127.0.0.1:3000');
const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1';
socket = new WebSocket(`ws://${DEV_SERVER_IP}:3000`);
socket.onopen = () => {
// Empty the message queue
while (queue.length !== 0) {
Expand Down Expand Up @@ -79,8 +80,10 @@ const sendRawMessage = msg => {
}
// Send message using plain HTTP request.
else {
const DEV_SERVER_IP = process.env.DEV_SERVER_IP || '127.0.0.1';
const req = new XMLHttpRequest();
req.open('POST', 'http://127.0.0.1:3001', true);
req.open('POST', `http://${DEV_SERVER_IP}:3001`);
req.timeout = 500;
req.send(json);
}
}
Expand Down
1 change: 1 addition & 0 deletions tgui-next/packages/tgui-dev-server/link/server.js
Original file line number Diff line number Diff line change
Expand Up @@ -107,6 +107,7 @@ const setupHttpLink = () => {
});
return;
}
res.write('Hello');
res.end();
});

Expand Down
44 changes: 43 additions & 1 deletion tgui-next/packages/tgui/backend.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import { UI_DISABLED, UI_INTERACTIVE } from './constants';
import { tridentVersion } from './byond';
import { tridentVersion, act as _act } from './byond';

/**
* This file provides a clear separation layer between backend updates
Expand Down Expand Up @@ -55,3 +55,45 @@ export const backendReducer = (state, action) => {

return state;
};

/**
* @typedef BackendState
* @type {{
* config: {
* title: string,
* status: number,
* screen: string,
* style: string,
* interface: string,
* fancy: number,
* locked: number,
* observer: number,
* window: string,
* ref: string,
* },
* data: any,
* visible: boolean,
* interactive: boolean,
* }}
*/

/**
* A React hook (sort of) for getting tgui state and related functions.
*
* This is supposed to be replaced with a real React Hook, which can only
* be used in functional components. DO NOT use it in class-based components!
*
* @return {BackendState & {
* act: (action: string, params?: object) => void,
* }}
*/
export const useBackend = props => {
// TODO: Dispatch "act" calls as Redux actions
const { state, dispatch } = props;
const ref = state.config.ref;
const act = (action, params = {}) => _act(ref, action, params);
return {
...state,
act,
};
};
13 changes: 6 additions & 7 deletions tgui-next/packages/tgui/components/BlockQuote.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
import { classes } from 'common/react';
import { Box } from './Box';

export const BlockQuote = props => {
const { style, ...rest } = props;
const { className, ...rest } = props;
return (
<Box
style={{
'color': 'rgba(255, 255, 255, 0.5)',
'border-left': '2px solid rgba(255, 255, 255, 0.5)',
'padding-left': '6px',
...style,
}}
className={classes([
'BlockQuote',
className,
])}
{...rest} />
);
};
83 changes: 29 additions & 54 deletions tgui-next/packages/tgui/components/Collapsible.js
Original file line number Diff line number Diff line change
@@ -1,15 +1,13 @@
import { classes } from 'common/react';
import { Component, Fragment } from 'inferno';
import { Component } from 'inferno';
import { Box } from './Box';
import { Icon } from './Icon';
import { Button } from './Button';

export class Collapsible extends Component {

constructor(props) {
super(props);
const { open = false } = props;
const { open } = props;
this.state = {
open,
open: open || false,
};
}

Expand All @@ -21,56 +19,33 @@ export class Collapsible extends Component {
color = "default",
title,
buttons,
onClick,
...boxProps
} = props;
// Box props
const {
className,
...rest
} = boxProps;
} = props;
return (
<Fragment>
<table
style={{
"width": "100%",
}}
>
<tr>
<td>
<Box
mb={open ? 0 : 1}
className={classes([
"Button",
"Button--fluid",
"Button--color--" + color,
className,
])}
onClick={e => this.setState({open: !open})}
{...rest}
>
<Box
inline
mr={1}
>
<Icon name={this.state.open ? "chevron-down" : "chevron-right"} />
</Box>
{title}
</Box>
</td>
{buttons && (
<td
style={{
"width": "0.01%",
}}
>
{buttons}
</td>
)}
</tr>
</table>
{open && children}
</Fragment>
<Box mb={1}>
<div className="Table">
<div className="Table__cell">
<Button
fluid
color={color}
icon={open ? 'chevron-down' : 'chevron-right'}
onClick={() => this.setState({ open: !open })}
{...rest}>
{title}
</Button>
</div>
{buttons && (
<div className="Table__cell Table__cell--collapsing">
{buttons}
</div>
)}
</div>
{open && (
<Box mt={1}>
{children}
</Box>
)}
</Box>
);
}
}
91 changes: 51 additions & 40 deletions tgui-next/packages/tgui/components/Input.js
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
import { classes, pureComponentHooks } from 'common/react';
import { classes } from 'common/react';
import { Component, createRef } from 'inferno';
import { Box } from './Box';

Expand All @@ -9,6 +9,52 @@ export class Input extends Component {
this.state = {
editing: false,
};
this.handleInput = e => {
const { editing } = this.state;
const { onInput } = this.props;
if (!editing) {
this.setEditing(true);
}
if (onInput) {
onInput(e, e.target.value);
}
};
this.handleFocus = e => {
const { editing } = this.state;
if (!editing) {
this.setEditing(true);
}
};
this.handleBlur = e => {
const { editing } = this.state;
const { onChange } = this.props;
if (editing) {
this.setEditing(false);
if (onChange) {
onChange(e, e.target.value);
}
}
};
this.handleKeyDown = e => {
const { onInput, onChange } = this.props;
if (e.keyCode === 13) {
this.setEditing(false);
if (onChange) {
onChange(e, e.target.value);
}
if (onInput) {
onInput(e, e.target.value);
}
e.target.blur();
return;
}
if (e.keyCode === 27) {
this.setEditing(false);
e.target.value = this.props.value;
e.target.blur();
return;
}
};
}

componentDidMount() {
Expand Down Expand Up @@ -62,46 +108,11 @@ export class Input extends Component {
<input
ref={this.inputRef}
className="Input__input"
onInput={e => {
this.setEditing(true);
if (onInput) {
onInput(e, e.target.value);
}
}}
onFocus={e => {
this.setEditing(true);
}}
onBlur={e => {
const { editing } = this.state;
if (editing) {
this.setEditing(false);
if (onChange) {
onChange(e, e.target.value);
}
}
}}
onKeyDown={e => {
if (e.keyCode === 13) {
this.setEditing(false);
if (onChange) {
onChange(e, e.target.value);
}
if (onInput) {
onInput(e, e.target.value);
}
e.target.blur();
return;
}
if (e.keyCode === 27) {
this.setEditing(false);
e.target.value = props.value;
e.target.blur();
return;
}
}} />
onInput={this.handleInput}
onFocus={this.handleFocus}
onBlur={this.handleBlur}
onKeyDown={this.handleKeyDown} />
</Box>
);
}
}

Input.defaultHooks = pureComponentHooks;
14 changes: 9 additions & 5 deletions tgui-next/packages/tgui/hotkeys.js
Original file line number Diff line number Diff line change
Expand Up @@ -219,13 +219,17 @@ const subscribeToKeyPresses = listenerFn => {
// Middleware
export const hotKeyMiddleware = store => {
const { dispatch } = store;
// Subscribe to key events
subscribeToKeyPresses((e, eventType) => {
// IE8: Can't determine the focused element, so by extension it passes
// keypresses when inputs are focused.
if (tridentVersion > 4) {
handlePassthrough(e, eventType);
}
handleHotKey(e, eventType, dispatch);
});
// IE8: focusin/focusout only available on IE9+
if (tridentVersion > 4) {
// Subscribe to key events
subscribeToKeyPresses((e, eventType) => {
handlePassthrough(e, eventType);
handleHotKey(e, eventType, dispatch);
});
// Clean up when browser window completely loses focus
subscribeToLossOfFocus(() => {
releaseHeldKeys();
Expand Down
Loading

0 comments on commit 085b30b

Please sign in to comment.