mirror of
https://git.sr.ht/~emersion/gamja
synced 2024-11-14 19:25:26 -05:00
Add buffer switcher
This commit is contained in:
parent
fe016807da
commit
44a064274d
4 changed files with 202 additions and 3 deletions
|
@ -13,6 +13,7 @@ import AuthForm from "./auth-form.js";
|
|||
import RegisterForm from "./register-form.js";
|
||||
import VerifyForm from "./verify-form.js";
|
||||
import SettingsForm from "./settings-form.js";
|
||||
import SwitcherForm from "./switcher-form.js";
|
||||
import Composer from "./composer.js";
|
||||
import ScrollManager from "./scroll-manager.js";
|
||||
import Dialog from "./dialog.js";
|
||||
|
@ -226,6 +227,7 @@ export default class App extends Component {
|
|||
this.handleOpenSettingsClick = this.handleOpenSettingsClick.bind(this);
|
||||
this.handleSettingsChange = this.handleSettingsChange.bind(this);
|
||||
this.handleSettingsDisconnect = this.handleSettingsDisconnect.bind(this);
|
||||
this.handleSwitchSubmit = this.handleSwitchSubmit.bind(this);
|
||||
|
||||
this.state.settings = {
|
||||
...this.state.settings,
|
||||
|
@ -1903,6 +1905,13 @@ export default class App extends Component {
|
|||
this.disconnectAll();
|
||||
}
|
||||
|
||||
handleSwitchSubmit(buf) {
|
||||
this.dismissDialog();
|
||||
if (buf) {
|
||||
this.switchBuffer(buf);
|
||||
}
|
||||
}
|
||||
|
||||
componentDidMount() {
|
||||
this.baseTitle = document.title;
|
||||
setupKeybindings(this);
|
||||
|
@ -2090,6 +2099,17 @@ export default class App extends Component {
|
|||
</>
|
||||
`;
|
||||
break;
|
||||
case "switch":
|
||||
dialog = html`
|
||||
<${Dialog} title="Switch to a channel or user" onDismiss=${this.dismissDialog}>
|
||||
<${SwitcherForm}
|
||||
buffers=${this.state.buffers}
|
||||
servers=${this.state.servers}
|
||||
bouncerNetworks=${this.state.bouncerNetworks}
|
||||
onSubmit=${this.handleSwitchSubmit}/>
|
||||
</>
|
||||
`;
|
||||
break;
|
||||
}
|
||||
|
||||
let error = null;
|
||||
|
|
141
components/switcher-form.js
Normal file
141
components/switcher-form.js
Normal file
|
@ -0,0 +1,141 @@
|
|||
import { html, Component } from "../lib/index.js";
|
||||
import { BufferType, getBufferURL, getServerName } from "../state.js";
|
||||
|
||||
class SwitcherItem extends Component {
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleClick = this.handleClick.bind(this);
|
||||
}
|
||||
|
||||
handleClick(event) {
|
||||
event.preventDefault();
|
||||
this.props.onClick();
|
||||
}
|
||||
|
||||
render() {
|
||||
let class_ = this.props.selected ? "selected" : "";
|
||||
|
||||
return html`
|
||||
<li>
|
||||
<a
|
||||
href=${getBufferURL(this.props.buffer)}
|
||||
class=${class_}
|
||||
onClick=${this.handleClick}
|
||||
>
|
||||
<span class="server">
|
||||
${getServerName(this.props.server, this.props.bouncerNetwork)}
|
||||
</span>
|
||||
${this.props.buffer.name}
|
||||
</a>
|
||||
</li>
|
||||
`;
|
||||
}
|
||||
}
|
||||
|
||||
export default class SwitcherForm extends Component {
|
||||
state = {
|
||||
query: "",
|
||||
selected: 0,
|
||||
};
|
||||
|
||||
constructor(props) {
|
||||
super(props);
|
||||
|
||||
this.handleInput = this.handleInput.bind(this);
|
||||
this.handleSubmit = this.handleSubmit.bind(this);
|
||||
this.handleKeyUp = this.handleKeyUp.bind(this);
|
||||
}
|
||||
|
||||
getSuggestions() {
|
||||
let query = this.state.query.toLowerCase();
|
||||
|
||||
let l = [];
|
||||
for (let buf of this.props.buffers.values()) {
|
||||
if (buf.type === BufferType.SERVER) {
|
||||
continue;
|
||||
}
|
||||
if (query !== "" && !buf.name.toLowerCase().includes(query)) {
|
||||
continue;
|
||||
}
|
||||
l.push(buf);
|
||||
if (l.length >= 20) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
return l;
|
||||
}
|
||||
|
||||
handleInput(event) {
|
||||
let target = event.target;
|
||||
this.setState({ [target.name]: target.value });
|
||||
}
|
||||
|
||||
handleSubmit(event) {
|
||||
event.preventDefault();
|
||||
this.props.onSubmit(this.getSuggestions()[this.state.selected]);
|
||||
}
|
||||
|
||||
handleKeyUp(event) {
|
||||
switch (event.key) {
|
||||
case "ArrowUp":
|
||||
event.stopPropagation();
|
||||
this.move(-1);
|
||||
break;
|
||||
case "ArrowDown":
|
||||
event.stopPropagation();
|
||||
this.move(1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
move(delta) {
|
||||
let numSuggestions = this.getSuggestions().length;
|
||||
this.setState((state) => {
|
||||
return {
|
||||
selected: (state.selected + delta + numSuggestions) % numSuggestions,
|
||||
};
|
||||
});
|
||||
}
|
||||
|
||||
render() {
|
||||
let items = this.getSuggestions().map((buf, i) => {
|
||||
let server = this.props.servers.get(buf.server);
|
||||
|
||||
let bouncerNetwork = null;
|
||||
if (server.bouncerNetID) {
|
||||
bouncerNetwork = this.props.bouncerNetworks.get(server.bouncerNetID);
|
||||
}
|
||||
|
||||
return html`
|
||||
<${SwitcherItem}
|
||||
buffer=${buf}
|
||||
server=${server}
|
||||
bouncerNetwork=${bouncerNetwork}
|
||||
selected=${this.state.selected === i}
|
||||
onClick=${() => this.props.onSubmit(buf)}
|
||||
/>
|
||||
`;
|
||||
});
|
||||
|
||||
return html`
|
||||
<form
|
||||
onInput=${this.handleInput}
|
||||
onSubmit=${this.handleSubmit}
|
||||
onKeyUp=${this.handleKeyUp}
|
||||
>
|
||||
<input
|
||||
type="search"
|
||||
name="query"
|
||||
value=${this.state.query}
|
||||
placeholder="Filter"
|
||||
autocomplete="off"
|
||||
autofocus
|
||||
/>
|
||||
<ul class="switcher-list">
|
||||
${items}
|
||||
</ul>
|
||||
</form>
|
||||
`;
|
||||
}
|
||||
}
|
|
@ -94,6 +94,14 @@ export const keybindings = [
|
|||
}
|
||||
},
|
||||
},
|
||||
{
|
||||
key: "k",
|
||||
ctrlKey: true,
|
||||
description: "Switch to a buffer",
|
||||
execute: (app) => {
|
||||
app.openDialog("switch");
|
||||
},
|
||||
},
|
||||
];
|
||||
|
||||
export function setup(app) {
|
||||
|
|
36
style.css
36
style.css
|
@ -352,7 +352,8 @@ form input[type="text"],
|
|||
form input[type="username"],
|
||||
form input[type="password"],
|
||||
form input[type="url"],
|
||||
form input[type="email"] {
|
||||
form input[type="email"],
|
||||
form input[type="search"] {
|
||||
box-sizing: border-box;
|
||||
width: 100%;
|
||||
font-family: inherit;
|
||||
|
@ -561,6 +562,29 @@ kbd {
|
|||
border-radius: 3px;
|
||||
}
|
||||
|
||||
ul.switcher-list {
|
||||
list-style-type: none;
|
||||
margin: 0;
|
||||
padding: 0;
|
||||
margin-top: 10px;
|
||||
}
|
||||
ul.switcher-list li a {
|
||||
display: inline-block;
|
||||
width: 100%;
|
||||
padding: 5px 10px;
|
||||
margin: 4px 0;
|
||||
box-sizing: border-box;
|
||||
text-decoration: none;
|
||||
color: inherit;
|
||||
}
|
||||
ul.switcher-list li a.selected {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
ul.switcher-list .server {
|
||||
float: right;
|
||||
opacity: 0.8;
|
||||
}
|
||||
|
||||
@media (prefers-color-scheme: dark) {
|
||||
html {
|
||||
scrollbar-color: var(--gray) transparent;
|
||||
|
@ -588,7 +612,8 @@ kbd {
|
|||
form input[type="username"],
|
||||
form input[type="password"],
|
||||
form input[type="url"],
|
||||
form input[type="email"] {
|
||||
form input[type="email"],
|
||||
form input[type="search"] {
|
||||
color: #ffffff;
|
||||
background: var(--sidebar-background);
|
||||
border: 1px solid #495057;
|
||||
|
@ -598,7 +623,8 @@ kbd {
|
|||
form input[type="username"]:focus,
|
||||
form input[type="password"]:focus,
|
||||
form input[type="url"]:focus,
|
||||
form input[type="email"]:focus {
|
||||
form input[type="email"]:focus,
|
||||
form input[type="search"]:focus {
|
||||
outline: 0;
|
||||
border-color: #3897ff;
|
||||
}
|
||||
|
@ -677,6 +703,10 @@ kbd {
|
|||
border: 1px solid var(--outline-color);
|
||||
box-shadow: inset 0 -1px 0 var(--outline-color);
|
||||
}
|
||||
|
||||
ul.switcher-list li a.selected {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
}
|
||||
|
||||
@media (max-width: 640px) {
|
||||
|
|
Loading…
Reference in a new issue