feat(shortcut): Keyboard Navigation (#168)

* chore: remove sliding menu

* feat(ui): sheet

* feat: shortcut component

* chore: register new shortcut component to layout

* fix: react attr naming

* fix: set default to false for shortcut

* feat(store): keydown-manager

* feat(hooks): keyboard manager

* chore: use util from base for la-editor

* chore: use util from base for minimal-tiptap-editor

* chore(utils): keyboard

* chore: use new keyboard manager

* fix: uniqueness of certain component

* feat: global key handler

* chore: implement new key handler
This commit is contained in:
Aslam
2024-09-19 21:17:11 +07:00
committed by GitHub
parent 0df105f186
commit 8eed3f8cc2
23 changed files with 686 additions and 515 deletions

View File

@@ -1,6 +1,6 @@
import * as React from "react"
import { cn } from "@/lib/utils"
import { getShortcutKey } from "../../lib/utils"
import { getShortcutKey } from "@/lib/utils"
export interface ShortcutKeyWrapperProps extends React.HTMLAttributes<HTMLSpanElement> {
ariaLabel: string
@@ -32,7 +32,7 @@ const ShortcutKey = React.forwardRef<HTMLSpanElement, ShortcutKeyProps>(({ class
{...props}
ref={ref}
>
{getShortcutKey(shortcut)}
{getShortcutKey(shortcut).symbol}
</kbd>
)
})

View File

@@ -6,7 +6,7 @@ import { Button } from "@/components/ui/button"
import { Separator } from "@/components/ui/separator"
import { Command, MenuListProps } from "./types"
import { getShortcutKeys } from "../../lib/utils"
import { getShortcutKeys } from "@/lib/utils"
import { Icon } from "../../components/ui/icon"
import { PopoverWrapper } from "../../components/ui/popover-wrapper"
import { Shortcut } from "../../components/ui/shortcut"
@@ -136,7 +136,11 @@ export const MenuList = React.forwardRef((props: MenuListProps, ref) => {
<Icon name={command.iconName} />
<span className="truncate text-sm">{command.label}</span>
<div className="flex flex-auto flex-row"></div>
<Shortcut.Wrapper ariaLabel={getShortcutKeys(command.shortcuts)}>
<Shortcut.Wrapper
ariaLabel={getShortcutKeys(command.shortcuts)
.map(shortcut => shortcut.readable)
.join(" + ")}
>
{command.shortcuts.map(shortcut => (
<Shortcut.Key shortcut={shortcut} key={shortcut} />
))}

View File

@@ -8,7 +8,5 @@ export function getOutput(editor: Editor, output: LAEditorProps["output"]) {
return ""
}
export * from "./keyboard"
export * from "./platform"
export * from "./isCustomNodeSelected"
export * from "./isTextSelected"

View File

@@ -1,25 +0,0 @@
import { isMacOS } from "./platform"
export const getShortcutKey = (key: string) => {
const lowercaseKey = key.toLowerCase()
const macOS = isMacOS()
switch (lowercaseKey) {
case "mod":
return macOS ? "⌘" : "Ctrl"
case "alt":
return macOS ? "⌥" : "Alt"
case "shift":
return macOS ? "⇧" : "Shift"
default:
return key
}
}
export const getShortcutKeys = (keys: string | string[], separator: string = "") => {
const keyArray = Array.isArray(keys) ? keys : keys.split(/\s+/)
const shortcutKeys = keyArray.map(getShortcutKey)
return shortcutKeys.join(separator)
}
export default { getShortcutKey, getShortcutKeys }

View File

@@ -1,46 +0,0 @@
export interface NavigatorWithUserAgentData extends Navigator {
userAgentData?: {
brands: { brand: string; version: string }[]
mobile: boolean
platform: string
getHighEntropyValues: (hints: string[]) => Promise<{
platform: string
platformVersion: string
uaFullVersion: string
}>
}
}
let isMac: boolean | undefined
const getPlatform = () => {
const nav = navigator as NavigatorWithUserAgentData
if (nav.userAgentData) {
if (nav.userAgentData.platform) {
return nav.userAgentData.platform
}
nav.userAgentData
.getHighEntropyValues(["platform"])
.then(highEntropyValues => {
if (highEntropyValues.platform) {
return highEntropyValues.platform
}
})
.catch(() => {
return navigator.platform || ""
})
}
return navigator.platform || ""
}
export const isMacOS = () => {
if (isMac === undefined) {
isMac = getPlatform().toLowerCase().includes("mac")
}
return isMac
}
export default isMacOS