Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
55 changes: 29 additions & 26 deletions src/components/main-ui.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ import { OrganiseUI } from "./organise";
import { HeaderLayout } from "./header-layout";
import { PDFPreviewPane } from "./pdf-preview";
import { useFileListContext } from "../provider/fileListProvider";
import { KeyboardNavProvider } from "../provider/keyboardNavProvider";
import { Button } from "./ui/button";

interface MainUIProps {
Expand Down Expand Up @@ -50,33 +51,35 @@ export function MainUI(props: MainUIProps) {

return (
<HeaderLayout toolName={props.toolName} onBack={props.onBack}>
<box flexDirection="row">
<box flexGrow={3}>
<Dynamic component={toolComponents[props.selectedTool]} />
</box>
<Show when={props.selectedTool !== "organise" && props.selectedTool !== "decrypt"}>
<box flexGrow={2} width={isPreviewOpen() && fl.fileCount() > 0 ? "30%" : 6}>
<Show
when={isPreviewOpen() && fl.fileCount() > 0}
fallback={
<box alignItems="flex-start" paddingTop={2}>
<Button
disabled={!fl.selectedFile()}
color="yellow"
label="◀"
onClick={() => setIsPreviewOpen(true)}
/>
</box>
}
>
<PDFPreviewPane
onOpen={() => setIsPreviewOpen(true)}
onClose={() => setIsPreviewOpen(false)}
/>
</Show>
<KeyboardNavProvider>
<box flexDirection="row">
<box flexGrow={3}>
<Dynamic component={toolComponents[props.selectedTool]} />
</box>
</Show>
</box>
<Show when={props.selectedTool !== "organise" && props.selectedTool !== "decrypt"}>
<box flexGrow={2} width={isPreviewOpen() && fl.fileCount() > 0 ? "30%" : 6}>
<Show
when={isPreviewOpen() && fl.fileCount() > 0}
fallback={
<box alignItems="flex-start" paddingTop={2}>
<Button
disabled={!fl.selectedFile()}
color="yellow"
label="◀"
onClick={() => setIsPreviewOpen(true)}
/>
</box>
}
>
<PDFPreviewPane
onOpen={() => setIsPreviewOpen(true)}
onClose={() => setIsPreviewOpen(false)}
/>
</Show>
</box>
</Show>
</box>
</KeyboardNavProvider>
</HeaderLayout>
);
}
15 changes: 10 additions & 5 deletions src/hooks/useKeyboardNav.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,10 @@
import { createSignal, createMemo } from "solid-js";
import { createSignal, createMemo, createContext, useContext } from "solid-js";
import { useBindings } from "@opentui/keymap/solid";
import type { FocusableElement } from "../model/models";

export type { FocusableElement };

export function useKeyboardNav() {
export function createKeyboardNav() {
const [focusIndex, setFocusIndex] = createSignal(0);
const [elements, setElements] = createSignal<FocusableElement[]>([]);
const [isInputMode, setIsInputMode] = createSignal(false);
Expand Down Expand Up @@ -62,19 +62,18 @@ export function useKeyboardNav() {
const getFocusedId = () => focusedElement()?.id ?? null;

const clearElements = () => {
setElements([]);
setElements((prev) => prev.filter((e) => e.persistent));
setFocusIndex(0);
};

useBindings(() => ({
priority: 100,
bindings: isInputMode()
? [
// Let escape bubble to the app-level double-esc handler in src/index.tsx
{
key: "escape",
cmd: () => setIsInputMode(false),
preventDefault: false,
preventDefault: false, // Let escape bubble to the app-level double-esc handler in src/index.tsx
},
{
key: "tab",
Expand Down Expand Up @@ -114,3 +113,9 @@ export function useKeyboardNav() {
setIsInputMode,
};
}

export const KeyboardNavContext = createContext<ReturnType<typeof createKeyboardNav>>();

export function useKeyboardNav() {
return useContext(KeyboardNavContext) ?? createKeyboardNav();
}
1 change: 1 addition & 0 deletions src/model/models.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,7 @@ export interface FocusableElement {
onEnter?: () => void;
canFocus?: () => boolean;
onMouseDown?: () => void;
persistent?: boolean; // keeps element registered across clearElements calls
}

export interface Status {
Expand Down
9 changes: 9 additions & 0 deletions src/provider/keyboardNavProvider.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
import { type ParentComponent } from "solid-js";
import { KeyboardNavContext, createKeyboardNav } from "../hooks/useKeyboardNav";

// Provides a single keyboard-nav instance so descendant panels (the active tool
// and the PDF preview) share one focus ring instead of competing rings.
export const KeyboardNavProvider: ParentComponent = (props) => {
const nav = createKeyboardNav();
return <KeyboardNavContext.Provider value={nav}>{props.children}</KeyboardNavContext.Provider>;
};
Loading