feat: add scoped shortcuts

This commit is contained in:
Max Koon
2025-12-12 18:44:18 -05:00
parent 27f6e627d4
commit c6dd174376
13 changed files with 451 additions and 138 deletions

View File

@@ -6,7 +6,7 @@ export interface DialogState {
close?: () => void;
}
export const Context = createContext<DialogState>({
close: () => { },
close: () => {},
});
interface ProviderProps {
@@ -21,7 +21,7 @@ export function Provider({ children, visible, close }: ProviderProps) {
{/* <Pressable onPress={() => close && close()} style={{ justifyContent: 'center', alignItems: 'center', flex: 1, backgroundColor: 'rgba(0,0,0,0.2)', }}> */}
<View
style={{
justifyContent: "center",
// justifyContent: "center",
alignItems: "center",
flex: 1,
backgroundColor: "rgba(0,0,0,0.2)",
@@ -39,12 +39,10 @@ interface ContentProps {
}
export function Content({ children }: ContentProps) {
const { close } = use(Context);
useShortcut("escape", () => close?.());
useShortcut("escape", () => close?.(), "dialog");
return (
<View
style={{ backgroundColor: "white", padding: 12, alignItems: "center" }}
>
<View style={{ backgroundColor: "white", alignItems: "center", top: 120 }}>
{children}
</View>
);

View File

@@ -1,19 +1,15 @@
import {
createContext,
use,
useEffect,
useRef,
useState,
type ReactNode,
} from "react";
import { createContext, use, useEffect, useState, type ReactNode } from "react";
import { View, Text } from "react-native";
import type { KeyEvent } from "@opentui/core";
import { useShortcut } from "../lib/shortcuts/hooks";
import type { Key } from "../lib/shortcuts";
const HEADER_COLOR = "#7158e2";
const TABLE_COLORS = ["#ddd", "#eee"];
const SELECTED_COLOR = "#f7b730";
const COLORS = {
focused: "#ddd",
selected: "#eaebf6",
focused_selected: "#d5d7ef",
};
const EXTRA = 5;
@@ -24,7 +20,7 @@ interface TableState {
columns: Column[];
columnMap: Map<string, number>;
idx: number;
selectedFrom: number | undefined;
selectedIdx: Set<number>;
}
const INITAL_STATE = {
@@ -32,7 +28,7 @@ const INITAL_STATE = {
columns: [],
columnMap: new Map(),
idx: 0,
selectedFrom: undefined,
selectedIdx: new Set(),
} satisfies TableState;
export const Context = createContext<TableState>(INITAL_STATE);
@@ -69,7 +65,7 @@ export function Provider<T extends ValidRecord>({
shortcuts,
}: ProviderProps<T>) {
const [idx, setIdx] = useState(0);
const [selectedFrom, setSelectedFrom] = useState<number>();
const [selectedIdx, setSelectedIdx] = useState(new Set<number>());
useShortcut("j", () => {
setIdx((prev) => Math.min(prev + 1, data.length - 1));
@@ -84,6 +80,17 @@ export function Provider<T extends ValidRecord>({
setIdx((prev) => Math.max(prev - 1, 0));
});
useShortcut("escape", () => {
setSelectedIdx(new Set());
});
useShortcut("x", () => {
setSelectedIdx((last) => {
const newSelected = new Set(last);
newSelected.add(idx);
return newSelected;
});
});
useEffect(() => {
setIdx((prev) => Math.max(Math.min(prev, data.length - 1), 0));
}, [data]);
@@ -91,9 +98,9 @@ export function Provider<T extends ValidRecord>({
if (shortcuts) {
for (const shortcut of shortcuts) {
useShortcut(shortcut.key, () => {
const from = selectedFrom ? Math.min(idx, selectedFrom) : idx;
const to = selectedFrom ? Math.max(idx, selectedFrom) : idx;
const selected = data.slice(from, to + 1);
const selected = data.filter(
(_, index) => idx == index || selectedIdx.has(index),
);
shortcut.handler({ selected, index: idx });
});
}
@@ -112,14 +119,14 @@ export function Provider<T extends ValidRecord>({
);
return (
<Context.Provider value={{ data, columns, columnMap, idx, selectedFrom }}>
<Context.Provider value={{ data, columns, columnMap, idx, selectedIdx }}>
{children}
</Context.Provider>
);
}
export function Body() {
const { columns, data, columnMap, idx, selectedFrom } = use(Context);
const { columns, data, columnMap, idx, selectedIdx } = use(Context);
return (
<View>
<View style={{ backgroundColor: HEADER_COLOR, flexDirection: "row" }}>
@@ -136,19 +143,21 @@ export function Body() {
))}
</View>
{data.map((row, index) => {
const isSelected =
index == idx ||
(selectedFrom != undefined &&
((selectedFrom <= index && index <= idx) ||
(idx <= index && index <= selectedFrom)));
const isSelected = selectedIdx.has(index);
const isFocused = index == idx;
return (
<View
key={index}
style={{
backgroundColor: isSelected
? SELECTED_COLOR
: TABLE_COLORS[index % 2],
backgroundColor:
isSelected && isFocused
? COLORS.focused_selected
: isFocused
? COLORS.focused
: isSelected
? COLORS.selected
: undefined,
}}
>
<TableRow