improve focus setting and keystrokes

This commit is contained in:
marlon-erler
2026-04-13 10:26:03 +00:00
parent 3cb771ed3a
commit 92cb02e113
10 changed files with 36 additions and 6 deletions
+12 -1
View File
@@ -2372,6 +2372,8 @@
window.location.reload(); window.location.reload();
} }
function setFocus() { function setFocus() {
const focusedInModal = document.querySelector(".modal[open] #focused");
if (focusedInModal) return focusedInModal.focus();
document.getElementById("focused")?.focus(); document.getElementById("focused")?.focus();
} }
function setFocusWithDelay() { function setFocusWithDelay() {
@@ -3499,6 +3501,7 @@
); );
this.chatViewModel.registerContext("tasks" /* Tasks */, this); this.chatViewModel.registerContext("tasks" /* Tasks */, this);
this.selectedBoardId.subscribeSilent(this.updateContexts); this.selectedBoardId.subscribeSilent(this.updateContexts);
this.registerKeyStroke("." /* Options */, this.toggleBoardList);
} }
// context // context
get isOpen() { get isOpen() {
@@ -4499,9 +4502,11 @@
[messagePageViewModel.reactionFilter], [messagePageViewModel.reactionFilter],
() => messagePageViewModel.reactionFilter.value == void 0 () => messagePageViewModel.reactionFilter.value == void 0
); );
messagePageViewModel.isFilterModalOpen.subscribe(setFocusWithDelay);
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": messagePageViewModel.isFilterModalOpen }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.chatPage.message.messageFilterHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": messagePageViewModel.isFilterModalOpen }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.chatPage.message.messageFilterHeadline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
id: "focused",
placeholder: coreViewModel2.translations.general.searchLabel, placeholder: coreViewModel2.translations.general.searchLabel,
"bind:value": messagePageViewModel.searchViewModel.searchInput, "bind:value": messagePageViewModel.searchViewModel.searchInput,
"on:enter": messagePageViewModel.searchViewModel.applySearch "on:enter": messagePageViewModel.searchViewModel.applySearch
@@ -4575,6 +4580,7 @@
scrollDownIfApplicable scrollDownIfApplicable
); );
setTimeout(() => scrollDown(true), 100); setTimeout(() => scrollDown(true), 100);
setFocusWithDelay();
return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, coreViewModel2.translations.chatPage.message.messagesHeadline), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { id: "message-page" }, /* @__PURE__ */ createElement("div", { class: "pane-wrapper" }, /* @__PURE__ */ createElement("div", { class: "pane" }, /* @__PURE__ */ createElement("div", { class: "toolbar" }, /* @__PURE__ */ createElement("span", { class: "title" }, coreViewModel2.translations.chatPage.message.messagesHeadline), /* @__PURE__ */ createElement("span", null, /* @__PURE__ */ createElement(
"button", "button",
{ {
@@ -4587,6 +4593,7 @@
))), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement( ))), /* @__PURE__ */ createElement("div", { class: "content" }, messageContainer, /* @__PURE__ */ createElement("div", { id: "composer" }, /* @__PURE__ */ createElement("div", { class: "content-width-constraint" }, /* @__PURE__ */ createElement("div", { class: "input-width-constraint" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
id: "focused",
"bind:value": messagePageViewModel.composingMessage, "bind:value": messagePageViewModel.composingMessage,
"on:enter": messagePageViewModel.sendMessage, "on:enter": messagePageViewModel.sendMessage,
placeholder: coreViewModel2.translations.chatPage.message.composerInputPlaceholder placeholder: coreViewModel2.translations.chatPage.message.composerInputPlaceholder
@@ -5130,9 +5137,11 @@
isOpen.value = false; isOpen.value = false;
} }
const suggestionId = v4_default(); const suggestionId = v4_default();
isOpen.subscribe(setFocusWithDelay);
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen, extended: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isOpen, extended: true }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, headline), /* @__PURE__ */ createElement("div", { class: "flex-row width-input" }, /* @__PURE__ */ createElement(
"input", "input",
{ {
id: "focused",
placeholder: coreViewModel2.translations.general.searchLabel, placeholder: coreViewModel2.translations.general.searchLabel,
"bind:value": searchViewModel.searchInput, "bind:value": searchViewModel.searchInput,
"on:enter": searchViewModel.applySearch, "on:enter": searchViewModel.applySearch,
@@ -6278,11 +6287,13 @@
return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.dataEntryInputDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement( return /* @__PURE__ */ createElement("div", { class: "modal", "toggle:open": isPresented }, /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("main", null, /* @__PURE__ */ createElement("h2", null, coreViewModel2.translations.dataTransferModal.transferDataHeadline), /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.dataEntryInputDescription), /* @__PURE__ */ createElement("hr", null), /* @__PURE__ */ createElement("div", { class: "flex-column gap content-margin-bottom" }, /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "forum"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferChannelHeadline), /* @__PURE__ */ createElement(
"input", "input",
{ {
"on:enter": fileTransferViewModel2.prepareReceivingData,
"bind:value": fileTransferViewModel2.receivingTransferChannel "bind:value": fileTransferViewModel2.receivingTransferChannel
} }
))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement( ))), /* @__PURE__ */ createElement("label", { class: "tile" }, /* @__PURE__ */ createElement("span", { class: "icon" }, "key"), /* @__PURE__ */ createElement("div", null, /* @__PURE__ */ createElement("span", { class: "secondary" }, coreViewModel2.translations.dataTransferModal.transferKeyHeadline), /* @__PURE__ */ createElement(
"input", "input",
{ {
"on:enter": fileTransferViewModel2.prepareReceivingData,
"bind:value": fileTransferViewModel2.receivingTransferKey "bind:value": fileTransferViewModel2.receivingTransferKey
} }
))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement( ))))), /* @__PURE__ */ createElement("div", { class: "flex-row width-100" }, /* @__PURE__ */ createElement(
@@ -7296,7 +7307,7 @@
this.settingsViewModel.showSettingsModal this.settingsViewModel.showSettingsModal
); );
this.registerKeyStroke( this.registerKeyStroke(
"d", "t",
this.fileTransferViewModel.showDirectionSelectionModal this.fileTransferViewModel.showDirectionSelectionModal
); );
this.registerKeyStroke("e", this.storageViewModel.showStorageModal); this.registerKeyStroke("e", this.storageViewModel.showStorageModal);
+5
View File
@@ -7,12 +7,14 @@ import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel"; import ChatMessageViewModel from "../../ViewModel/Chat/chatMessageViewModel";
import { ChatMessage } from "../Components/chatMessage"; import { ChatMessage } from "../Components/chatMessage";
import { MessageFilterModal } from "../Modals/messageFilterModal"; import { MessageFilterModal } from "../Modals/messageFilterModal";
import { setFocusWithDelay } from "../utility";
export function MessagePage( export function MessagePage(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
messagePageViewModel: MessagePageViewModel, messagePageViewModel: MessagePageViewModel,
) { ) {
messagePageViewModel.loadData(); messagePageViewModel.loadData();
const ChatMessageViewModelToView: React.StateItemConverter< const ChatMessageViewModelToView: React.StateItemConverter<
ChatMessageViewModel ChatMessageViewModel
> = (chatMessageViewModel: ChatMessageViewModel) => { > = (chatMessageViewModel: ChatMessageViewModel) => {
@@ -49,6 +51,8 @@ export function MessagePage(
); );
setTimeout(() => scrollDown(true), 100); setTimeout(() => scrollDown(true), 100);
setFocusWithDelay();
return ( return (
<div id="message-page"> <div id="message-page">
<div class="pane-wrapper"> <div class="pane-wrapper">
@@ -82,6 +86,7 @@ export function MessagePage(
<div class="content-width-constraint"> <div class="content-width-constraint">
<div class="input-width-constraint"> <div class="input-width-constraint">
<input <input
id="focused"
bind:value={ bind:value={
messagePageViewModel.composingMessage messagePageViewModel.composingMessage
} }
+2
View File
@@ -450,6 +450,7 @@ function TransferDataInputModal(
} }
</span> </span>
<input <input
on:enter={fileTransferViewModel.prepareReceivingData}
bind:value={ bind:value={
fileTransferViewModel.receivingTransferChannel fileTransferViewModel.receivingTransferChannel
} }
@@ -467,6 +468,7 @@ function TransferDataInputModal(
} }
</span> </span>
<input <input
on:enter={fileTransferViewModel.prepareReceivingData}
bind:value={ bind:value={
fileTransferViewModel.receivingTransferKey fileTransferViewModel.receivingTransferKey
} }
+4 -3
View File
@@ -1,12 +1,10 @@
import * as React from "bloatless-react"; import * as React from "bloatless-react";
import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
import { StringToOption } from "../Components/option";
import { v4 } from "uuid";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel"; import MessagePageViewModel from "../../ViewModel/Pages/messagePageViewModel";
import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton"; import { MessageReactionFilterButton } from "../Components/messageReactionFilterButton";
import { ReactionSymbols } from "../../Model/Chat/chatModel"; import { ReactionSymbols } from "../../Model/Chat/chatModel";
import { setFocusWithDelay } from "../utility";
export function MessageFilterModal<T>( export function MessageFilterModal<T>(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -18,6 +16,8 @@ export function MessageFilterModal<T>(
() => messagePageViewModel.reactionFilter.value == undefined, () => messagePageViewModel.reactionFilter.value == undefined,
); );
messagePageViewModel.isFilterModalOpen.subscribe(setFocusWithDelay);
return ( return (
<div class="modal" toggle:open={messagePageViewModel.isFilterModalOpen}> <div class="modal" toggle:open={messagePageViewModel.isFilterModalOpen}>
<div> <div>
@@ -30,6 +30,7 @@ export function MessageFilterModal<T>(
</h2> </h2>
<div class="flex-row width-input"> <div class="flex-row width-input">
<input <input
id="focused"
placeholder={ placeholder={
coreViewModel.translations.general.searchLabel coreViewModel.translations.general.searchLabel
} }
+4
View File
@@ -4,6 +4,7 @@ import SearchViewModel from "../../ViewModel/Utility/searchViewModel";
import { StringToOption } from "../Components/option"; import { StringToOption } from "../Components/option";
import { v4 } from "uuid"; import { v4 } from "uuid";
import CoreViewModel from "../../ViewModel/Global/coreViewModel"; import CoreViewModel from "../../ViewModel/Global/coreViewModel";
import { setFocusWithDelay } from "../utility";
export function SearchModal<T>( export function SearchModal<T>(
coreViewModel: CoreViewModel, coreViewModel: CoreViewModel,
@@ -18,6 +19,8 @@ export function SearchModal<T>(
const suggestionId = v4(); const suggestionId = v4();
isOpen.subscribe(setFocusWithDelay);
return ( return (
<div class="modal" toggle:open={isOpen} extended> <div class="modal" toggle:open={isOpen} extended>
<div> <div>
@@ -25,6 +28,7 @@ export function SearchModal<T>(
<h2>{headline}</h2> <h2>{headline}</h2>
<div class="flex-row width-input"> <div class="flex-row width-input">
<input <input
id="focused"
placeholder={ placeholder={
coreViewModel.translations.general.searchLabel coreViewModel.translations.general.searchLabel
} }
+1
View File
@@ -2,6 +2,7 @@ export enum CommonKeys {
Home = " ", Home = " ",
Filter = "=", Filter = "=",
Reset = "-", Reset = "-",
Options = ".",
CloseOrCancel = "backspace", CloseOrCancel = "backspace",
Apply = "enter", Apply = "enter",
Settings = ",", Settings = ",",
+2
View File
@@ -11,6 +11,8 @@ export function reload() {
} }
export function setFocus() { export function setFocus() {
const focusedInModal = document.querySelector(".modal[open] #focused") as HTMLElement;
if (focusedInModal) return focusedInModal.focus();
document.getElementById("focused")?.focus(); document.getElementById("focused")?.focus();
} }
@@ -155,6 +155,7 @@ export default class FileTransferViewModel extends Context {
}; };
prepareReceivingData = (): void => { prepareReceivingData = (): void => {
if (this.cannotPrepareToReceive.value == true) return;
this.presentedModal.value = FileTransferModals.ReceptionDisplay; this.presentedModal.value = FileTransferModals.ReceptionDisplay;
this.filePathsReceived.clear(); this.filePathsReceived.clear();
+1 -1
View File
@@ -24,7 +24,7 @@ export default class HomeViewModel extends Context {
this.settingsViewModel.showSettingsModal, this.settingsViewModel.showSettingsModal,
); );
this.registerKeyStroke( this.registerKeyStroke(
"d", "t",
this.fileTransferViewModel.showDirectionSelectionModal, this.fileTransferViewModel.showDirectionSelectionModal,
); );
this.registerKeyStroke("e", this.storageViewModel.showStorageModal); this.registerKeyStroke("e", this.storageViewModel.showStorageModal);
+4 -1
View File
@@ -8,6 +8,7 @@ import BoardViewModel, { BoardPageTypes } from "./boardViewModel";
import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel"; import ChatViewModel, { ChatPageTypes } from "../Chat/chatViewModel";
import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel"; import CoreViewModel, { Context, ContextHost } from "../Global/coreViewModel";
import { IndexManager } from "../../Model/Utility/utility"; import { IndexManager } from "../../Model/Utility/utility";
import { CommonKeys } from "../../View/keystrokes";
export default class TaskPageViewModel extends ContextHost<string> { export default class TaskPageViewModel extends ContextHost<string> {
contextDebugDescription = "task-page"; contextDebugDescription = "task-page";
@@ -181,8 +182,10 @@ export default class TaskPageViewModel extends ContextHost<string> {
// context // context
this.chatViewModel.registerContext(ChatPageTypes.Tasks, this); this.chatViewModel.registerContext(ChatPageTypes.Tasks, this);
this.selectedBoardId.subscribeSilent(this.updateContexts); this.selectedBoardId.subscribeSilent(this.updateContexts);
// keystrokes
this.registerKeyStroke(CommonKeys.Options, this.toggleBoardList);
} }
} }