urbit/pkg/interface/src/views/apps/dojo/components/input.js

120 lines
3.7 KiB
JavaScript
Raw Normal View History

2020-05-01 05:54:12 +03:00
import React, { Component } from 'react';
import { cite } from '~/logic/lib/util';
import { Spinner } from '~/views/components/Spinner';
2020-05-01 05:54:12 +03:00
export class Input extends Component {
constructor(props) {
super(props);
this.state = {
awaiting: false,
type: 'Sending to Dojo'
};
this.keyPress = this.keyPress.bind(this);
this.inputRef = React.createRef();
}
componentDidUpdate() {
2020-08-18 02:15:35 +03:00
if (
!document.activeElement == document.body
|| document.activeElement == this.inputRef.current
) {
this.inputRef.current.focus();
2020-05-01 05:54:12 +03:00
this.inputRef.current.setSelectionRange(this.props.cursor, this.props.cursor);
}
2020-08-18 02:15:35 +03:00
}
2020-05-01 05:54:12 +03:00
2020-08-18 02:15:35 +03:00
keyPress(e) {
2020-05-01 05:54:12 +03:00
if ((e.getModifierState('Control') || event.getModifierState('Meta'))
&& e.key === 'v') {
return;
}
e.preventDefault();
const allowedKeys = [
'Enter', 'Backspace', 'ArrowLeft', 'ArrowRight', 'Tab'
];
if ((e.key.length > 1) && (!(allowedKeys.includes(e.key)))) {
return;
}
2020-08-18 02:15:35 +03:00
// submit on enter
if (e.key === 'Enter') {
this.setState({ awaiting: true, type: 'Sending to Dojo' });
this.props.api.soto('ret').then(() => {
this.setState({ awaiting: false });
});
} else if ((e.key === 'Backspace') && (this.props.cursor > 0)) {
this.props.store.doEdit({ del: this.props.cursor - 1 });
return this.props.store.setState({ cursor: this.props.cursor - 1 });
} else if (e.key === 'Backspace') {
return;
} else if (e.key.startsWith('Arrow')) {
if (e.key === 'ArrowLeft') {
if (this.props.cursor > 0) {
this.props.store.setState({ cursor: this.props.cursor - 1 });
}
} else if (e.key === 'ArrowRight') {
if (this.props.cursor < this.props.input.length) {
this.props.store.setState({ cursor: this.props.cursor + 1 });
}
2020-05-01 05:54:12 +03:00
}
}
2020-08-18 02:15:35 +03:00
// tab completion
else if (e.key === 'Tab') {
this.setState({ awaiting: true, type: 'Getting suggestions' });
this.props.api.soto({ tab: this.props.cursor }).then(() => {
this.setState({ awaiting: false });
});
}
2020-05-01 05:54:12 +03:00
2020-08-18 02:15:35 +03:00
// capture and transmit most characters
else {
this.props.store.doEdit({ ins: { cha: e.key, at: this.props.cursor } });
this.props.store.setState({ cursor: this.props.cursor + 1 });
}
2020-05-01 05:54:12 +03:00
}
2020-08-18 02:15:35 +03:00
render() {
return (
<div className="flex flex-row flex-grow-1 relative">
2020-08-19 06:10:36 +03:00
<div className="flex-shrink-0"><span class="dn-s">{cite(this.props.ship)}:</span>dojo
2020-08-18 02:15:35 +03:00
</div>
<span id="prompt">
{this.props.prompt}
</span>
<input
autoFocus
autocorrect="off"
autocapitalize="off"
spellcheck="false"
tabindex="0"
wrap="off"
className="mono ml1 flex-auto dib w-100"
id="dojo"
cursor={this.props.cursor}
onClick={e => this.props.store.setState({ cursor: e.target.selectionEnd })}
onKeyDown={this.keyPress}
onPaste={(e) => {
const clipboardData = e.clipboardData || window.clipboardData;
const paste = Array.from(clipboardData.getData('Text'));
paste.reduce(async (previous, next) => {
await previous;
this.setState({ cursor: this.props.cursor + 1 });
return this.props.store.doEdit({ ins: { cha: next, at: this.props.cursor } });
}, Promise.resolve());
e.preventDefault();
}}
ref={this.inputRef}
defaultValue={this.props.input}
/>
<Spinner awaiting={this.state.awaiting} text={`${this.state.type}...`} classes="absolute right-0 bottom-0 inter pa ba pa2 b--gray1-d" />
2020-05-01 05:54:12 +03:00
</div>
);
}
}
export default Input;