mirror of
https://codeberg.org/emersion/gamja.git
synced 2024-11-14 19:05:01 -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 RegisterForm from "./register-form.js";
|
||||||
import VerifyForm from "./verify-form.js";
|
import VerifyForm from "./verify-form.js";
|
||||||
import SettingsForm from "./settings-form.js";
|
import SettingsForm from "./settings-form.js";
|
||||||
|
import SwitcherForm from "./switcher-form.js";
|
||||||
import Composer from "./composer.js";
|
import Composer from "./composer.js";
|
||||||
import ScrollManager from "./scroll-manager.js";
|
import ScrollManager from "./scroll-manager.js";
|
||||||
import Dialog from "./dialog.js";
|
import Dialog from "./dialog.js";
|
||||||
|
@ -226,6 +227,7 @@ export default class App extends Component {
|
||||||
this.handleOpenSettingsClick = this.handleOpenSettingsClick.bind(this);
|
this.handleOpenSettingsClick = this.handleOpenSettingsClick.bind(this);
|
||||||
this.handleSettingsChange = this.handleSettingsChange.bind(this);
|
this.handleSettingsChange = this.handleSettingsChange.bind(this);
|
||||||
this.handleSettingsDisconnect = this.handleSettingsDisconnect.bind(this);
|
this.handleSettingsDisconnect = this.handleSettingsDisconnect.bind(this);
|
||||||
|
this.handleSwitchSubmit = this.handleSwitchSubmit.bind(this);
|
||||||
|
|
||||||
this.state.settings = {
|
this.state.settings = {
|
||||||
...this.state.settings,
|
...this.state.settings,
|
||||||
|
@ -1903,6 +1905,13 @@ export default class App extends Component {
|
||||||
this.disconnectAll();
|
this.disconnectAll();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
handleSwitchSubmit(buf) {
|
||||||
|
this.dismissDialog();
|
||||||
|
if (buf) {
|
||||||
|
this.switchBuffer(buf);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
componentDidMount() {
|
componentDidMount() {
|
||||||
this.baseTitle = document.title;
|
this.baseTitle = document.title;
|
||||||
setupKeybindings(this);
|
setupKeybindings(this);
|
||||||
|
@ -2090,6 +2099,17 @@ export default class App extends Component {
|
||||||
</>
|
</>
|
||||||
`;
|
`;
|
||||||
break;
|
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;
|
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) {
|
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="username"],
|
||||||
form input[type="password"],
|
form input[type="password"],
|
||||||
form input[type="url"],
|
form input[type="url"],
|
||||||
form input[type="email"] {
|
form input[type="email"],
|
||||||
|
form input[type="search"] {
|
||||||
box-sizing: border-box;
|
box-sizing: border-box;
|
||||||
width: 100%;
|
width: 100%;
|
||||||
font-family: inherit;
|
font-family: inherit;
|
||||||
|
@ -561,6 +562,29 @@ kbd {
|
||||||
border-radius: 3px;
|
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) {
|
@media (prefers-color-scheme: dark) {
|
||||||
html {
|
html {
|
||||||
scrollbar-color: var(--gray) transparent;
|
scrollbar-color: var(--gray) transparent;
|
||||||
|
@ -588,7 +612,8 @@ kbd {
|
||||||
form input[type="username"],
|
form input[type="username"],
|
||||||
form input[type="password"],
|
form input[type="password"],
|
||||||
form input[type="url"],
|
form input[type="url"],
|
||||||
form input[type="email"] {
|
form input[type="email"],
|
||||||
|
form input[type="search"] {
|
||||||
color: #ffffff;
|
color: #ffffff;
|
||||||
background: var(--sidebar-background);
|
background: var(--sidebar-background);
|
||||||
border: 1px solid #495057;
|
border: 1px solid #495057;
|
||||||
|
@ -598,7 +623,8 @@ kbd {
|
||||||
form input[type="username"]:focus,
|
form input[type="username"]:focus,
|
||||||
form input[type="password"]:focus,
|
form input[type="password"]:focus,
|
||||||
form input[type="url"]:focus,
|
form input[type="url"]:focus,
|
||||||
form input[type="email"]:focus {
|
form input[type="email"]:focus,
|
||||||
|
form input[type="search"]:focus {
|
||||||
outline: 0;
|
outline: 0;
|
||||||
border-color: #3897ff;
|
border-color: #3897ff;
|
||||||
}
|
}
|
||||||
|
@ -677,6 +703,10 @@ kbd {
|
||||||
border: 1px solid var(--outline-color);
|
border: 1px solid var(--outline-color);
|
||||||
box-shadow: inset 0 -1px 0 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) {
|
@media (max-width: 640px) {
|
||||||
|
|
Loading…
Reference in a new issue