mirror of
https://github.com/urbit/shrub.git
synced 2024-12-18 15:55:00 +03:00
invite-search: allow keyboard nav + selection
Allows the user to navigate potential invitees with the arrow keys and with shift+tab and tab. Enter adds the currently selected invitee.
This commit is contained in:
parent
2bfcf20bf7
commit
3801056d3c
@ -1,4 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import Mousetrap from 'mousetrap';
|
||||||
import urbitOb from "urbit-ob";
|
import urbitOb from "urbit-ob";
|
||||||
import { Sigil } from "../lib/icons/sigil";
|
import { Sigil } from "../lib/icons/sigil";
|
||||||
|
|
||||||
@ -14,13 +16,17 @@ export class InviteSearch extends Component {
|
|||||||
groups: [],
|
groups: [],
|
||||||
ships: []
|
ships: []
|
||||||
},
|
},
|
||||||
|
selected: null,
|
||||||
inviteError: false
|
inviteError: false
|
||||||
};
|
};
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
|
|
||||||
|
this.textarea = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.peerUpdate();
|
this.peerUpdate();
|
||||||
|
this.bindShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@ -126,12 +132,91 @@ export class InviteSearch extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { selected } = this.state;
|
||||||
|
let groupIdx = groupMatches.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = shipMatches.findIndex(ship => ship === selected);
|
||||||
|
let staleSelection = groupIdx < 0 && shipIdx < 0;
|
||||||
|
if(!selected || staleSelection) {
|
||||||
|
const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
|
||||||
|
this.setState({ selected: newSelection })
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
searchResults: { groups: groupMatches, ships: shipMatches }
|
searchResults: { groups: groupMatches, ships: shipMatches }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindShortcuts() {
|
||||||
|
let mousetrap = Mousetrap(this.textarea.current);
|
||||||
|
mousetrap.bind(['down', 'tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind(['up', 'shift+tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection('backward');
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind('enter', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const { selected } = this.state;
|
||||||
|
if(selected.startsWith('/')) {
|
||||||
|
this.addGroup(selected)
|
||||||
|
} else {
|
||||||
|
this.addShip(selected);
|
||||||
|
}
|
||||||
|
this.setState({ selected: null })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nextSelection(backward = false) {
|
||||||
|
let { selected, searchResults } = this.state;
|
||||||
|
const { ships, groups } = searchResults;
|
||||||
|
if(!selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let groupIdx = groups.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = ships.findIndex(ship => ship === selected);
|
||||||
|
if(groupIdx >= 0) {
|
||||||
|
backward ? groupIdx-- : groupIdx++;
|
||||||
|
let selected = _.get(groups,[groupIdx], '[0]');
|
||||||
|
if(groupIdx === groups.length) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[0][0]
|
||||||
|
: ships[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
if(groupIdx < 0) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[groups.length - 1][0]
|
||||||
|
: ships[ships.length - 1];
|
||||||
|
}
|
||||||
|
this.setState({ selected });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(shipIdx >= 0) {
|
||||||
|
backward ? shipIdx-- : shipIdx++;
|
||||||
|
let selected = ships[shipIdx];
|
||||||
|
if(shipIdx === ships.length) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[0]
|
||||||
|
: groups[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shipIdx < 0) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[ships.length - 1]
|
||||||
|
: groups[groups.length - 1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ selected });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
deleteGroup() {
|
deleteGroup() {
|
||||||
let { ships } = this.props.invites;
|
let { ships } = this.props.invites;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -251,7 +336,8 @@ export class InviteSearch extends Component {
|
|||||||
className={
|
className={
|
||||||
"list white-d f8 pv2 ph3 pointer" +
|
"list white-d f8 pv2 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d " +
|
" hover-bg-gray4 hover-bg-gray1-d " +
|
||||||
((group[1]) ? "inter" : "mono")
|
((group[1]) ? "inter" : "mono") +
|
||||||
|
( group[0] === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={() => this.addGroup(group[0])}>
|
onClick={() => this.addGroup(group[0])}>
|
||||||
<span className="mix-blend-diff white">{(group[1]) ? group[1] : group[0]}</span>
|
<span className="mix-blend-diff white">{(group[1]) ? group[1] : group[0]}</span>
|
||||||
@ -270,7 +356,8 @@ export class InviteSearch extends Component {
|
|||||||
key={ship}
|
key={ship}
|
||||||
className={
|
className={
|
||||||
"list mono white-d f8 pv1 ph3 pointer" +
|
"list mono white-d f8 pv1 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d relative"
|
" hover-bg-gray4 hover-bg-gray1-d relative" +
|
||||||
|
( ship === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={e => this.addShip(ship)}>
|
onClick={e => this.addShip(ship)}>
|
||||||
<Sigil
|
<Sigil
|
||||||
@ -364,9 +451,7 @@ export class InviteSearch extends Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
ref={e => {
|
ref={this.textarea}
|
||||||
this.textarea = e;
|
|
||||||
}}
|
|
||||||
className={
|
className={
|
||||||
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
||||||
" db focus-b--black focus-b--white-d"
|
" db focus-b--black focus-b--white-d"
|
||||||
@ -379,12 +464,6 @@ export class InviteSearch extends Component {
|
|||||||
resize: "none",
|
resize: "none",
|
||||||
paddingLeft: 36
|
paddingLeft: 36
|
||||||
}}
|
}}
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === "Enter" || e.key === ",") {
|
|
||||||
e.preventDefault();
|
|
||||||
this.submitShipToAdd(this.state.searchValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onChange={this.search}
|
onChange={this.search}
|
||||||
value={state.searchValue}
|
value={state.searchValue}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import Mousetrap from 'mousetrap';
|
||||||
import urbitOb from "urbit-ob";
|
import urbitOb from "urbit-ob";
|
||||||
import { Sigil } from "../lib/icons/sigil";
|
import { Sigil } from "../lib/icons/sigil";
|
||||||
|
|
||||||
@ -14,13 +16,17 @@ export class InviteSearch extends Component {
|
|||||||
groups: [],
|
groups: [],
|
||||||
ships: []
|
ships: []
|
||||||
},
|
},
|
||||||
|
selected: null,
|
||||||
inviteError: false
|
inviteError: false
|
||||||
};
|
};
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
|
|
||||||
|
this.textarea = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.peerUpdate();
|
this.peerUpdate();
|
||||||
|
this.bindShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@ -127,12 +133,91 @@ export class InviteSearch extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { selected } = this.state;
|
||||||
|
let groupIdx = groupMatches.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = shipMatches.findIndex(ship => ship === selected);
|
||||||
|
let staleSelection = groupIdx < 0 && shipIdx < 0;
|
||||||
|
if(!selected || staleSelection) {
|
||||||
|
const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
|
||||||
|
this.setState({ selected: newSelection })
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
searchResults: { groups: groupMatches, ships: shipMatches }
|
searchResults: { groups: groupMatches, ships: shipMatches }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindShortcuts() {
|
||||||
|
let mousetrap = Mousetrap(this.textarea.current);
|
||||||
|
mousetrap.bind(['down', 'tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind(['up', 'shift+tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection('backward');
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind('enter', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const { selected } = this.state;
|
||||||
|
if(selected.startsWith('/')) {
|
||||||
|
this.addGroup(selected)
|
||||||
|
} else {
|
||||||
|
this.addShip(selected);
|
||||||
|
}
|
||||||
|
this.setState({ selected: null })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nextSelection(backward = false) {
|
||||||
|
let { selected, searchResults } = this.state;
|
||||||
|
const { ships, groups } = searchResults;
|
||||||
|
if(!selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let groupIdx = groups.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = ships.findIndex(ship => ship === selected);
|
||||||
|
if(groupIdx >= 0) {
|
||||||
|
backward ? groupIdx-- : groupIdx++;
|
||||||
|
let selected = _.get(groups,[groupIdx], '[0]');
|
||||||
|
if(groupIdx === groups.length) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[0][0]
|
||||||
|
: ships[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
if(groupIdx < 0) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[groups.length - 1][0]
|
||||||
|
: ships[ships.length - 1];
|
||||||
|
}
|
||||||
|
this.setState({ selected });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(shipIdx >= 0) {
|
||||||
|
backward ? shipIdx-- : shipIdx++;
|
||||||
|
let selected = ships[shipIdx];
|
||||||
|
if(shipIdx === ships.length) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[0]
|
||||||
|
: groups[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shipIdx < 0) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[ships.length - 1]
|
||||||
|
: groups[groups.length - 1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ selected });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
deleteGroup() {
|
deleteGroup() {
|
||||||
let { ships } = this.props.invites;
|
let { ships } = this.props.invites;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -252,7 +337,8 @@ export class InviteSearch extends Component {
|
|||||||
className={
|
className={
|
||||||
"list white-d f8 pv2 ph3 pointer" +
|
"list white-d f8 pv2 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d " +
|
" hover-bg-gray4 hover-bg-gray1-d " +
|
||||||
(group[1] ? "inter" : "mono")
|
(group[1] ? "inter" : "mono") +
|
||||||
|
( group[0] === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={() => this.addGroup(group[0])}>
|
onClick={() => this.addGroup(group[0])}>
|
||||||
<span className="mix-blend-diff white">
|
<span className="mix-blend-diff white">
|
||||||
@ -276,7 +362,8 @@ export class InviteSearch extends Component {
|
|||||||
key={ship}
|
key={ship}
|
||||||
className={
|
className={
|
||||||
"list mono white-d f8 pv1 ph3 pointer" +
|
"list mono white-d f8 pv1 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d relative"
|
" hover-bg-gray4 hover-bg-gray1-d relative" +
|
||||||
|
( ship === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={e => this.addShip(ship)}>
|
onClick={e => this.addShip(ship)}>
|
||||||
<Sigil
|
<Sigil
|
||||||
@ -374,9 +461,7 @@ export class InviteSearch extends Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
ref={e => {
|
ref={this.textarea}
|
||||||
this.textarea = e;
|
|
||||||
}}
|
|
||||||
className={
|
className={
|
||||||
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
||||||
" db focus-b--black focus-b--white-d"
|
" db focus-b--black focus-b--white-d"
|
||||||
@ -389,12 +474,6 @@ export class InviteSearch extends Component {
|
|||||||
resize: "none",
|
resize: "none",
|
||||||
paddingLeft: 36
|
paddingLeft: 36
|
||||||
}}
|
}}
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === "Enter" || e.key === ",") {
|
|
||||||
e.preventDefault();
|
|
||||||
this.submitShipToAdd(this.state.searchValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onChange={this.search}
|
onChange={this.search}
|
||||||
value={state.searchValue}
|
value={state.searchValue}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import Mousetrap from 'mousetrap';
|
||||||
import urbitOb from "urbit-ob";
|
import urbitOb from "urbit-ob";
|
||||||
import { Sigil } from "../lib/icons/sigil";
|
import { Sigil } from "../lib/icons/sigil";
|
||||||
|
|
||||||
@ -14,13 +16,17 @@ export class InviteSearch extends Component {
|
|||||||
groups: [],
|
groups: [],
|
||||||
ships: []
|
ships: []
|
||||||
},
|
},
|
||||||
|
selected: null,
|
||||||
inviteError: false
|
inviteError: false
|
||||||
};
|
};
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
|
|
||||||
|
this.textarea = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.peerUpdate();
|
this.peerUpdate();
|
||||||
|
this.bindShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@ -127,12 +133,91 @@ export class InviteSearch extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { selected } = this.state;
|
||||||
|
let groupIdx = groupMatches.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = shipMatches.findIndex(ship => ship === selected);
|
||||||
|
let staleSelection = groupIdx < 0 && shipIdx < 0;
|
||||||
|
if(!selected || staleSelection) {
|
||||||
|
const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
|
||||||
|
this.setState({ selected: newSelection })
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
searchResults: { groups: groupMatches, ships: shipMatches }
|
searchResults: { groups: groupMatches, ships: shipMatches }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindShortcuts() {
|
||||||
|
let mousetrap = Mousetrap(this.textarea.current);
|
||||||
|
mousetrap.bind(['down', 'tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind(['up', 'shift+tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection('backward');
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind('enter', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const { selected } = this.state;
|
||||||
|
if(selected.startsWith('/')) {
|
||||||
|
this.addGroup(selected)
|
||||||
|
} else {
|
||||||
|
this.addShip(selected);
|
||||||
|
}
|
||||||
|
this.setState({ selected: null })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nextSelection(backward = false) {
|
||||||
|
let { selected, searchResults } = this.state;
|
||||||
|
const { ships, groups } = searchResults;
|
||||||
|
if(!selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let groupIdx = groups.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = ships.findIndex(ship => ship === selected);
|
||||||
|
if(groupIdx >= 0) {
|
||||||
|
backward ? groupIdx-- : groupIdx++;
|
||||||
|
let selected = _.get(groups,[groupIdx], '[0]');
|
||||||
|
if(groupIdx === groups.length) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[0][0]
|
||||||
|
: ships[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
if(groupIdx < 0) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[groups.length - 1][0]
|
||||||
|
: ships[ships.length - 1];
|
||||||
|
}
|
||||||
|
this.setState({ selected });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(shipIdx >= 0) {
|
||||||
|
backward ? shipIdx-- : shipIdx++;
|
||||||
|
let selected = ships[shipIdx];
|
||||||
|
if(shipIdx === ships.length) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[0]
|
||||||
|
: groups[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shipIdx < 0) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[ships.length - 1]
|
||||||
|
: groups[groups.length - 1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ selected });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
deleteGroup() {
|
deleteGroup() {
|
||||||
let { ships } = this.props.invites;
|
let { ships } = this.props.invites;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -252,7 +337,8 @@ export class InviteSearch extends Component {
|
|||||||
className={
|
className={
|
||||||
"list white-d f8 pv2 ph3 pointer" +
|
"list white-d f8 pv2 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d " +
|
" hover-bg-gray4 hover-bg-gray1-d " +
|
||||||
(group[1] ? "inter" : "mono")
|
(group[1] ? "inter" : "mono") +
|
||||||
|
( group[0] === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={() => this.addGroup(group[0])}>
|
onClick={() => this.addGroup(group[0])}>
|
||||||
<span className="mix-blend-diff white">
|
<span className="mix-blend-diff white">
|
||||||
@ -276,7 +362,8 @@ export class InviteSearch extends Component {
|
|||||||
key={ship}
|
key={ship}
|
||||||
className={
|
className={
|
||||||
"list mono white-d f8 pv1 ph3 pointer" +
|
"list mono white-d f8 pv1 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d relative"
|
" hover-bg-gray4 hover-bg-gray1-d relative" +
|
||||||
|
( ship === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={e => this.addShip(ship)}>
|
onClick={e => this.addShip(ship)}>
|
||||||
<Sigil
|
<Sigil
|
||||||
@ -374,9 +461,7 @@ export class InviteSearch extends Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
ref={e => {
|
ref={this.textarea}
|
||||||
this.textarea = e;
|
|
||||||
}}
|
|
||||||
className={
|
className={
|
||||||
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
||||||
" db focus-b--black focus-b--white-d"
|
" db focus-b--black focus-b--white-d"
|
||||||
@ -389,12 +474,6 @@ export class InviteSearch extends Component {
|
|||||||
resize: "none",
|
resize: "none",
|
||||||
paddingLeft: 36
|
paddingLeft: 36
|
||||||
}}
|
}}
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === "Enter" || e.key === ",") {
|
|
||||||
e.preventDefault();
|
|
||||||
this.submitShipToAdd(this.state.searchValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onChange={this.search}
|
onChange={this.search}
|
||||||
value={state.searchValue}
|
value={state.searchValue}
|
||||||
/>
|
/>
|
||||||
|
@ -1,4 +1,6 @@
|
|||||||
import React, { Component } from "react";
|
import React, { Component } from "react";
|
||||||
|
import _ from 'lodash';
|
||||||
|
import Mousetrap from 'mousetrap';
|
||||||
import urbitOb from "urbit-ob";
|
import urbitOb from "urbit-ob";
|
||||||
import { Sigil } from "../lib/icons/sigil";
|
import { Sigil } from "../lib/icons/sigil";
|
||||||
|
|
||||||
@ -14,13 +16,17 @@ export class InviteSearch extends Component {
|
|||||||
groups: [],
|
groups: [],
|
||||||
ships: []
|
ships: []
|
||||||
},
|
},
|
||||||
|
selected: null,
|
||||||
inviteError: false
|
inviteError: false
|
||||||
};
|
};
|
||||||
this.search = this.search.bind(this);
|
this.search = this.search.bind(this);
|
||||||
|
|
||||||
|
this.textarea = React.createRef();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.peerUpdate();
|
this.peerUpdate();
|
||||||
|
this.bindShortcuts();
|
||||||
}
|
}
|
||||||
|
|
||||||
componentDidUpdate(prevProps) {
|
componentDidUpdate(prevProps) {
|
||||||
@ -127,12 +133,91 @@ export class InviteSearch extends Component {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let { selected } = this.state;
|
||||||
|
let groupIdx = groupMatches.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = shipMatches.findIndex(ship => ship === selected);
|
||||||
|
let staleSelection = groupIdx < 0 && shipIdx < 0;
|
||||||
|
if(!selected || staleSelection) {
|
||||||
|
const newSelection = _.get(groupMatches, '[0][0]') || shipMatches[0];
|
||||||
|
this.setState({ selected: newSelection })
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
searchResults: { groups: groupMatches, ships: shipMatches }
|
searchResults: { groups: groupMatches, ships: shipMatches }
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
bindShortcuts() {
|
||||||
|
let mousetrap = Mousetrap(this.textarea.current);
|
||||||
|
mousetrap.bind(['down', 'tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection();
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind(['up', 'shift+tab'], e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
this.nextSelection('backward');
|
||||||
|
});
|
||||||
|
|
||||||
|
mousetrap.bind('enter', e => {
|
||||||
|
e.preventDefault();
|
||||||
|
e.stopPropagation();
|
||||||
|
const { selected } = this.state;
|
||||||
|
if(selected.startsWith('/')) {
|
||||||
|
this.addGroup(selected)
|
||||||
|
} else {
|
||||||
|
this.addShip(selected);
|
||||||
|
}
|
||||||
|
this.setState({ selected: null })
|
||||||
|
})
|
||||||
|
}
|
||||||
|
nextSelection(backward = false) {
|
||||||
|
let { selected, searchResults } = this.state;
|
||||||
|
const { ships, groups } = searchResults;
|
||||||
|
if(!selected) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let groupIdx = groups.findIndex(([path]) => path === selected);
|
||||||
|
let shipIdx = ships.findIndex(ship => ship === selected);
|
||||||
|
if(groupIdx >= 0) {
|
||||||
|
backward ? groupIdx-- : groupIdx++;
|
||||||
|
let selected = _.get(groups,[groupIdx], '[0]');
|
||||||
|
if(groupIdx === groups.length) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[0][0]
|
||||||
|
: ships[0];
|
||||||
|
|
||||||
|
}
|
||||||
|
if(groupIdx < 0) {
|
||||||
|
selected = ships.length === 0
|
||||||
|
? groups[groups.length - 1][0]
|
||||||
|
: ships[ships.length - 1];
|
||||||
|
}
|
||||||
|
this.setState({ selected });
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
if(shipIdx >= 0) {
|
||||||
|
backward ? shipIdx-- : shipIdx++;
|
||||||
|
let selected = ships[shipIdx];
|
||||||
|
if(shipIdx === ships.length) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[0]
|
||||||
|
: groups[0][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
if(shipIdx < 0) {
|
||||||
|
selected = groups.length === 0
|
||||||
|
? ships[ships.length - 1]
|
||||||
|
: groups[groups.length - 1][0];
|
||||||
|
}
|
||||||
|
|
||||||
|
this.setState({ selected });
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
deleteGroup() {
|
deleteGroup() {
|
||||||
let { ships } = this.props.invites;
|
let { ships } = this.props.invites;
|
||||||
this.setState({
|
this.setState({
|
||||||
@ -252,7 +337,8 @@ export class InviteSearch extends Component {
|
|||||||
className={
|
className={
|
||||||
"list white-d f8 pv2 ph3 pointer" +
|
"list white-d f8 pv2 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d " +
|
" hover-bg-gray4 hover-bg-gray1-d " +
|
||||||
(group[1] ? "inter" : "mono")
|
(group[1] ? "inter" : "mono") +
|
||||||
|
( group[0] === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={() => this.addGroup(group[0])}>
|
onClick={() => this.addGroup(group[0])}>
|
||||||
<span className="mix-blend-diff white">
|
<span className="mix-blend-diff white">
|
||||||
@ -276,7 +362,8 @@ export class InviteSearch extends Component {
|
|||||||
key={ship}
|
key={ship}
|
||||||
className={
|
className={
|
||||||
"list mono white-d f8 pv1 ph3 pointer" +
|
"list mono white-d f8 pv1 ph3 pointer" +
|
||||||
" hover-bg-gray4 hover-bg-gray1-d relative"
|
" hover-bg-gray4 hover-bg-gray1-d relative" +
|
||||||
|
( ship === state.selected ? ' bg-gray1-d bg-gray4' : '')
|
||||||
}
|
}
|
||||||
onClick={e => this.addShip(ship)}>
|
onClick={e => this.addShip(ship)}>
|
||||||
<Sigil
|
<Sigil
|
||||||
@ -374,9 +461,7 @@ export class InviteSearch extends Component {
|
|||||||
}}
|
}}
|
||||||
/>
|
/>
|
||||||
<textarea
|
<textarea
|
||||||
ref={e => {
|
ref={this.textarea}
|
||||||
this.textarea = e;
|
|
||||||
}}
|
|
||||||
className={
|
className={
|
||||||
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
"f7 ba b--gray3 b--gray2-d bg-gray0-d white-d pa3 w-100" +
|
||||||
" db focus-b--black focus-b--white-d"
|
" db focus-b--black focus-b--white-d"
|
||||||
@ -389,12 +474,6 @@ export class InviteSearch extends Component {
|
|||||||
resize: "none",
|
resize: "none",
|
||||||
paddingLeft: 36
|
paddingLeft: 36
|
||||||
}}
|
}}
|
||||||
onKeyPress={e => {
|
|
||||||
if (e.key === "Enter" || e.key === ",") {
|
|
||||||
e.preventDefault();
|
|
||||||
this.submitShipToAdd(this.state.searchValue);
|
|
||||||
}
|
|
||||||
}}
|
|
||||||
onChange={this.search}
|
onChange={this.search}
|
||||||
value={state.searchValue}
|
value={state.searchValue}
|
||||||
/>
|
/>
|
||||||
|
Loading…
Reference in New Issue
Block a user