79 lines
2 KiB
TypeScript
79 lines
2 KiB
TypeScript
import type { ClipboardService } from "../context/clipboard"
|
|
|
|
type Toast = {
|
|
show: (input: { message: string; variant: "info" | "success" | "warning" | "error" }) => void
|
|
error: (err: unknown) => void
|
|
}
|
|
|
|
type FocusableSelectionTarget = {
|
|
hasSelection: () => boolean
|
|
getClipboardText?: (text: string) => string
|
|
}
|
|
|
|
type Renderer = {
|
|
getSelection: () => { getSelectedText: () => string; selectedRenderables: FocusableSelectionTarget[] } | null
|
|
clearSelection: () => void
|
|
currentFocusedRenderable?: FocusableSelectionTarget | null
|
|
}
|
|
|
|
type SelectionKeyEvent = {
|
|
ctrl?: boolean
|
|
name: string
|
|
preventDefault: () => void
|
|
stopPropagation: () => void
|
|
}
|
|
|
|
export function copy(renderer: Renderer, toast: Toast, clipboard: ClipboardService): boolean {
|
|
const selection = renderer.getSelection()
|
|
if (!selection) return false
|
|
|
|
const text = selection.getSelectedText()
|
|
if (!text) return false
|
|
|
|
const focus = renderer.currentFocusedRenderable
|
|
const clipboardText =
|
|
focus?.getClipboardText && selection.selectedRenderables.includes(focus) ? focus.getClipboardText(text) : text
|
|
|
|
clipboard
|
|
?.write?.(clipboardText)
|
|
.then(() => toast.show({ message: "Copied to clipboard", variant: "info" }))
|
|
.catch(toast.error)
|
|
|
|
renderer.clearSelection()
|
|
return true
|
|
}
|
|
|
|
export function handleSelectionKey(
|
|
renderer: Renderer,
|
|
toast: Toast,
|
|
event: SelectionKeyEvent,
|
|
clipboard: ClipboardService,
|
|
) {
|
|
const selection = renderer.getSelection()
|
|
if (!selection) return
|
|
|
|
if (event.ctrl && event.name === "c") {
|
|
if (!copy(renderer, toast, clipboard)) {
|
|
renderer.clearSelection()
|
|
return
|
|
}
|
|
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
return
|
|
}
|
|
|
|
if (event.name === "escape") {
|
|
renderer.clearSelection()
|
|
event.preventDefault()
|
|
event.stopPropagation()
|
|
return
|
|
}
|
|
|
|
const focus = renderer.currentFocusedRenderable
|
|
if (focus?.hasSelection() && selection.selectedRenderables.includes(focus)) return
|
|
|
|
renderer.clearSelection()
|
|
}
|
|
|
|
export * as Selection from "./selection"
|