From ff8d01feaa60340dc8b75f5d04f3fd677cc5ac02 Mon Sep 17 00:00:00 2001 From: y1j2x34 Date: Mon, 1 Apr 2024 16:57:53 +0800 Subject: [PATCH] feat: shortcuts plugin for react --- packages/react/package.json | 37 ++++++++ packages/react/src/context/KeyboardContext.ts | 6 ++ .../src/context/KeyboardContextProvider.tsx | 45 ++++++++++ packages/react/src/hooks/index.ts | 3 + packages/react/src/hooks/useCommand.ts | 10 +++ packages/react/src/hooks/useKeyboard.ts | 6 ++ .../react/src/hooks/useShortcutKeyMatch.ts | 13 +++ packages/react/src/index.ts | 2 + packages/react/tsconfig.json | 7 ++ packages/react/yarn.lock | 86 +++++++++++++++++++ 10 files changed, 215 insertions(+) create mode 100644 packages/react/package.json create mode 100644 packages/react/src/context/KeyboardContext.ts create mode 100644 packages/react/src/context/KeyboardContextProvider.tsx create mode 100644 packages/react/src/hooks/index.ts create mode 100644 packages/react/src/hooks/useCommand.ts create mode 100644 packages/react/src/hooks/useKeyboard.ts create mode 100644 packages/react/src/hooks/useShortcutKeyMatch.ts create mode 100644 packages/react/src/index.ts create mode 100644 packages/react/tsconfig.json create mode 100644 packages/react/yarn.lock diff --git a/packages/react/package.json b/packages/react/package.json new file mode 100644 index 0000000..d83d0c4 --- /dev/null +++ b/packages/react/package.json @@ -0,0 +1,37 @@ +{ + "name": "@vgerbot/shortcuts-react", + "version": "1.0.0", + "description": "Shortcuts for React", + "author": "y1j2x34 ", + "homepage": "https://github.com/y1j2x34/shortcuts/tree/main/packages/react#readme", + "license": "MIT", + "main": "lib/index.cjs.js", + "directories": { + "lib": "lib", + "test": "__tests__" + }, + "files": [ + "lib" + ], + "publishConfig": { + "registry": "https://registry.npmmirror.com/" + }, + "repository": { + "type": "git", + "url": "git+https://github.com/y1j2x34/shortcuts.git" + }, + "scripts": {}, + "bugs": { + "url": "https://github.com/y1j2x34/shortcuts/issues" + }, + "dependencies": { + "@vgerbot/shortcuts": "^1.0.0", + "react": "^16.8.0" + }, + "devDependencies": { + "@types/react": "^16.8.0" + }, + "peerDependencies": { + "react": ">= 16.8.0" + } +} diff --git a/packages/react/src/context/KeyboardContext.ts b/packages/react/src/context/KeyboardContext.ts new file mode 100644 index 0000000..c11d44d --- /dev/null +++ b/packages/react/src/context/KeyboardContext.ts @@ -0,0 +1,6 @@ +import { Keyboard } from '@vgerbot/shortcuts'; +import React from 'react'; + +export const KeyboardContext = React.createContext( + null as unknown as Keyboard +); diff --git a/packages/react/src/context/KeyboardContextProvider.tsx b/packages/react/src/context/KeyboardContextProvider.tsx new file mode 100644 index 0000000..805320e --- /dev/null +++ b/packages/react/src/context/KeyboardContextProvider.tsx @@ -0,0 +1,45 @@ +import { Keyboard } from '@vgerbot/shortcuts'; +import { KeyboardConstructorOptions } from '@vgerbot/shortcuts/lib/foundation/KeyboardConstructorOptions'; +import React, { useEffect, useRef } from 'react'; +import { KeyboardContext } from './KeyboardContext'; +const KEYBOARD_PROPS = Symbol('keyboard-props'); + +type KeyboardProviderProps = + React.PropsWithChildren; + +interface ReactKeyboard extends Keyboard { + [KEYBOARD_PROPS]: KeyboardProviderProps; +} + +export function KeyboardProvider(props: KeyboardProviderProps) { + const keyboardRef = useRef(); + if (!keyboardRef.current) { + keyboardRef.current = new Keyboard(props) as ReactKeyboard; + keyboardRef.current[KEYBOARD_PROPS] = props; + } + useEffect(() => { + const anchor = keyboardRef.current?.getAnchor(); + if (!props.anchor || anchor === props.anchor) { + return; + } + keyboardRef.current?.setAnchor(props.anchor); + }, [props.anchor]); + useEffect(() => { + if ( + keyboardRef.current && + keyboardRef.current[KEYBOARD_PROPS] === props + ) { + return; + } + keyboardRef.current?.destroy(); + const keyboard = (keyboardRef.current = new Keyboard( + props + ) as ReactKeyboard); + keyboard[KEYBOARD_PROPS] = props; + }, [props.eventOptions, props.macroRegistry]); + return ( + + {props.children} + + ); +} diff --git a/packages/react/src/hooks/index.ts b/packages/react/src/hooks/index.ts new file mode 100644 index 0000000..36fe765 --- /dev/null +++ b/packages/react/src/hooks/index.ts @@ -0,0 +1,3 @@ +export * from './useCommand'; +export * from './useKeyboard'; +export * from './useShortcutKeyMatch'; diff --git a/packages/react/src/hooks/useCommand.ts b/packages/react/src/hooks/useCommand.ts new file mode 100644 index 0000000..b2fac78 --- /dev/null +++ b/packages/react/src/hooks/useCommand.ts @@ -0,0 +1,10 @@ +import { ShortcutEventHandler } from '@vgerbot/shortcuts'; +import { useKeyboard } from './useKeyboard'; +import { useEffect } from 'react'; + +export function useCommand(command: string, callback: ShortcutEventHandler) { + const keyboard = useKeyboard(); + useEffect(() => { + return keyboard.on(command, callback); + }, [keyboard, callback]); +} diff --git a/packages/react/src/hooks/useKeyboard.ts b/packages/react/src/hooks/useKeyboard.ts new file mode 100644 index 0000000..860b122 --- /dev/null +++ b/packages/react/src/hooks/useKeyboard.ts @@ -0,0 +1,6 @@ +import { useContext } from 'react'; +import { KeyboardContext } from '../context/KeyboardContext'; + +export function useKeyboard() { + return useContext(KeyboardContext); +} diff --git a/packages/react/src/hooks/useShortcutKeyMatch.ts b/packages/react/src/hooks/useShortcutKeyMatch.ts new file mode 100644 index 0000000..69407db --- /dev/null +++ b/packages/react/src/hooks/useShortcutKeyMatch.ts @@ -0,0 +1,13 @@ +import { useEffect } from 'react'; +import { useKeyboard } from './useKeyboard'; +import { Shortcut, ShortcutEventHandler } from '@vgerbot/shortcuts'; + +export function useShortcutKeyMatch( + shortcut: string | Shortcut, + callback: ShortcutEventHandler +) { + const keyboard = useKeyboard(); + useEffect(() => { + return keyboard.onShortcutKeyMatch(shortcut, callback); + }, [keyboard]); +} diff --git a/packages/react/src/index.ts b/packages/react/src/index.ts new file mode 100644 index 0000000..32d242c --- /dev/null +++ b/packages/react/src/index.ts @@ -0,0 +1,2 @@ +export * from './hooks/index'; +export * from './context/KeyboardContextProvider'; diff --git a/packages/react/tsconfig.json b/packages/react/tsconfig.json new file mode 100644 index 0000000..aa6814e --- /dev/null +++ b/packages/react/tsconfig.json @@ -0,0 +1,7 @@ +{ + "compilerOptions": { + "jsx": "react" + }, + "extends": "../../tsconfig.json", + "include": ["src"] +} \ No newline at end of file diff --git a/packages/react/yarn.lock b/packages/react/yarn.lock new file mode 100644 index 0000000..49b8d76 --- /dev/null +++ b/packages/react/yarn.lock @@ -0,0 +1,86 @@ +# THIS IS AN AUTOGENERATED FILE. DO NOT EDIT THIS FILE DIRECTLY. +# yarn lockfile v1 + + +"@types/prop-types@*": + version "15.7.12" + resolved "https://registry.yarnpkg.com/@types/prop-types/-/prop-types-15.7.12.tgz#12bb1e2be27293c1406acb6af1c3f3a1481d98c6" + integrity sha512-5zvhXYtRNRluoE/jAp4GVsSduVUzNWKkOZrCDBWYtE7biZywwdC2AcEzg+cSMLFRfVgeAFqpfNabiPjxFddV1Q== + +"@types/react@^16.8.0": + version "16.14.60" + resolved "https://registry.yarnpkg.com/@types/react/-/react-16.14.60.tgz#f7ab62a329b82826f12d02bc8031d4ef4b5e0d81" + integrity sha512-wIFmnczGsTcgwCBeIYOuy2mdXEiKZ5znU/jNOnMZPQyCcIxauMGWlX0TNG4lZ7NxRKj7YUIZRneJQSSdB2jKgg== + dependencies: + "@types/prop-types" "*" + "@types/scheduler" "^0.16" + csstype "^3.0.2" + +"@types/scheduler@^0.16": + version "0.16.8" + resolved "https://registry.yarnpkg.com/@types/scheduler/-/scheduler-0.16.8.tgz#ce5ace04cfeabe7ef87c0091e50752e36707deff" + integrity sha512-WZLiwShhwLRmeV6zH+GkbOFT6Z6VklCItrDioxUnv+u4Ll+8vKeFySoFyK/0ctcRpOmwAicELfmys1sDc/Rw+A== + +"@vgerbot/shortcuts@^1.0.0": + version "1.0.0" + resolved "https://registry.yarnpkg.com/@vgerbot/shortcuts/-/shortcuts-1.0.0.tgz#1d0341b3024f5d795d6cce9887040d072bc27f43" + integrity sha512-KqfEfkvi/mX+oeySNP55/mBQxM/rgA8dssx9UdiTN+TXqdaLkMr3af4bbzVVzioe5X6km5jJ1paJGXKdXgrVfA== + dependencies: + browser-detect "^0.2.28" + +browser-detect@^0.2.28: + version "0.2.28" + resolved "https://registry.yarnpkg.com/browser-detect/-/browser-detect-0.2.28.tgz#5688fc22f638390614ebea4646483403fb20ebfb" + integrity sha512-KeWGHqYQmHDkCFG2dIiX/2wFUgqevbw/rd6wNi9N6rZbaSJFtG5kel0HtprRwCGp8sqpQP79LzDJXf/WCx4WAw== + dependencies: + core-js "^2.5.7" + +core-js@^2.5.7: + version "2.6.12" + resolved "https://registry.yarnpkg.com/core-js/-/core-js-2.6.12.tgz#d9333dfa7b065e347cc5682219d6f690859cc2ec" + integrity sha512-Kb2wC0fvsWfQrgk8HU5lW6U/Lcs8+9aaYcy4ZFc6DDlo4nZ7n70dEgE5rtR0oG6ufKDUnrwfWL1mXR5ljDatrQ== + +csstype@^3.0.2: + version "3.1.3" + resolved "https://registry.yarnpkg.com/csstype/-/csstype-3.1.3.tgz#d80ff294d114fb0e6ac500fbf85b60137d7eff81" + integrity sha512-M1uQkMl8rQK/szD0LNhtqxIPLpimGm8sOBwU7lLnCpSbTyY3yeU1Vc7l4KT5zT4s/yOxHH5O7tIuuLOCnLADRw== + +"js-tokens@^3.0.0 || ^4.0.0": + version "4.0.0" + resolved "https://registry.yarnpkg.com/js-tokens/-/js-tokens-4.0.0.tgz#19203fb59991df98e3a287050d4647cdeaf32499" + integrity sha512-RdJUflcE3cUzKiMqQgsCu06FPu9UdIJO0beYbPhHN4k6apgJtifcoCtT9bcxOpYBtpD2kCM6Sbzg4CausW/PKQ== + +loose-envify@^1.1.0, loose-envify@^1.4.0: + version "1.4.0" + resolved "https://registry.yarnpkg.com/loose-envify/-/loose-envify-1.4.0.tgz#71ee51fa7be4caec1a63839f7e682d8132d30caf" + integrity sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q== + dependencies: + js-tokens "^3.0.0 || ^4.0.0" + +object-assign@^4.1.1: + version "4.1.1" + resolved "https://registry.yarnpkg.com/object-assign/-/object-assign-4.1.1.tgz#2109adc7965887cfc05cbbd442cac8bfbb360863" + integrity sha512-rJgTQnkUnH1sFw8yT6VSU3zD3sWmu6sZhIseY8VX+GRu3P6F7Fu+JNDoXfklElbLJSnc3FUQHVe4cU5hj+BcUg== + +prop-types@^15.6.2: + version "15.8.1" + resolved "https://registry.yarnpkg.com/prop-types/-/prop-types-15.8.1.tgz#67d87bf1a694f48435cf332c24af10214a3140b5" + integrity sha512-oj87CgZICdulUohogVAR7AjlC0327U4el4L6eAvOqCeudMDVU0NThNaV+b9Df4dXgSP1gXMTnPdhfe/2qDH5cg== + dependencies: + loose-envify "^1.4.0" + object-assign "^4.1.1" + react-is "^16.13.1" + +react-is@^16.13.1: + version "16.13.1" + resolved "https://registry.yarnpkg.com/react-is/-/react-is-16.13.1.tgz#789729a4dc36de2999dc156dd6c1d9c18cea56a4" + integrity sha512-24e6ynE2H+OKt4kqsOvNd8kBpV65zoxbA4BVsEOB3ARVWQki/DHzaUoC5KuON/BiccDaCCTZBuOcfZs70kR8bQ== + +react@^16.8.0: + version "16.14.0" + resolved "https://registry.yarnpkg.com/react/-/react-16.14.0.tgz#94d776ddd0aaa37da3eda8fc5b6b18a4c9a3114d" + integrity sha512-0X2CImDkJGApiAlcf0ODKIneSwBPhqJawOa5wCtKbu7ZECrmS26NvtSILynQ66cgkT/RJ4LidJOc3bUESwmU8g== + dependencies: + loose-envify "^1.1.0" + object-assign "^4.1.1" + prop-types "^15.6.2"