aboutsummaryrefslogtreecommitdiff
path: root/plugins/base/frontend/src/main/components
diff options
context:
space:
mode:
authorAndrey Tyrin <andrei.tyrin@jetbrains.com>2022-08-25 00:44:21 +0200
committerGitHub <noreply@github.com>2022-08-25 01:44:21 +0300
commit56beaa318c603c0ada0cf97454f06fbb44175917 (patch)
tree07fe058a94346cbb5a4404659f31f4b02489f1bb /plugins/base/frontend/src/main/components
parentce1998247e0478a9d22860026efb9694e2916396 (diff)
downloaddokka-56beaa318c603c0ada0cf97454f06fbb44175917.tar.gz
dokka-56beaa318c603c0ada0cf97454f06fbb44175917.tar.bz2
dokka-56beaa318c603c0ada0cf97454f06fbb44175917.zip
Add hotkey for opening search dialog (#2633)
Diffstat (limited to 'plugins/base/frontend/src/main/components')
-rw-r--r--plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx23
-rw-r--r--plugins/base/frontend/src/main/components/search/search.scss5
-rw-r--r--plugins/base/frontend/src/main/components/search/search.tsx4
-rw-r--r--plugins/base/frontend/src/main/components/search/types.ts14
-rw-r--r--plugins/base/frontend/src/main/components/utils/hotkey.ts58
-rw-r--r--plugins/base/frontend/src/main/components/utils/os.ts14
6 files changed, 111 insertions, 7 deletions
diff --git a/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
index a502f589..a22dc77d 100644
--- a/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
+++ b/plugins/base/frontend/src/main/components/search/dokkaSearchAnchor.tsx
@@ -1,12 +1,27 @@
import React from "react";
+import Tooltip from '@jetbrains/ring-ui/components/tooltip/tooltip';
import SearchIcon from 'react-svg-loader!../assets/searchIcon.svg';
+import {CustomAnchorProps} from "./types";
+import {Hotkey} from "../utils/hotkey";
+
+const HOTKEY_LETTER = 'k'
+const HOTKEY_TOOLTIP_DISPLAY_DELAY = 0.5 * 1000 // seconds
+
+export const DokkaSearchAnchor = ({wrapperProps, buttonProps, popup}: CustomAnchorProps) => {
+ const hotkeys = new Hotkey()
+ hotkeys.registerHotkeyWithAccel(buttonProps.onClick, HOTKEY_LETTER)
-export const DokkaSearchAnchor = ({wrapperProps, buttonProps, popup}: any) => {
return (
<span {...wrapperProps}>
- <button type="button" {...buttonProps}>
- <SearchIcon />
- </button>
+ <Tooltip
+ title={`${hotkeys.getOsAccelKeyName()} + ${HOTKEY_LETTER.toUpperCase()}`}
+ delay={HOTKEY_TOOLTIP_DISPLAY_DELAY}
+ popupProps={{className: "search-hotkey-popup"}}
+ >
+ <button type="button" {...buttonProps}>
+ <SearchIcon/>
+ </button>
+ </Tooltip>
{popup}
</span>
)
diff --git a/plugins/base/frontend/src/main/components/search/search.scss b/plugins/base/frontend/src/main/components/search/search.scss
index 24ad509f..780ea6b3 100644
--- a/plugins/base/frontend/src/main/components/search/search.scss
+++ b/plugins/base/frontend/src/main/components/search/search.scss
@@ -18,6 +18,11 @@ $secondary-font-color: hsla(0, 0%, 100%, 0.6);
}
}
+.search-hotkey-popup{
+ background-color: var(--background-color) !important;
+ padding: 4px;
+}
+
.popup-wrapper {
min-width: calc(100% - 322px) !important;
diff --git a/plugins/base/frontend/src/main/components/search/search.tsx b/plugins/base/frontend/src/main/components/search/search.tsx
index dfdcd462..d4e406bf 100644
--- a/plugins/base/frontend/src/main/components/search/search.tsx
+++ b/plugins/base/frontend/src/main/components/search/search.tsx
@@ -3,7 +3,7 @@ import List from '@jetbrains/ring-ui/components/list/list';
import Select from '@jetbrains/ring-ui/components/select/select';
import '@jetbrains/ring-ui/components/input-size/input-size.css';
import './search.scss';
-import { IWindow, Option, Props } from "./types";
+import {CustomAnchorProps, IWindow, Option, Props} from "./types";
import { DokkaSearchAnchor } from "./dokkaSearchAnchor";
import { DokkaFuzzyFilterComponent } from "./dokkaFuzzyFilter";
import { relativizeUrlForRequest } from '../utils/requests';
@@ -34,7 +34,7 @@ const WithFuzzySearchFilterComponent: React.FC<Props> = ({ data }: Props) => {
data={data}
popupClassName={"popup-wrapper"}
onSelect={onChangeSelected}
- customAnchor={({ wrapperProps, buttonProps, popup }) =>
+ customAnchor={({ wrapperProps, buttonProps, popup }: CustomAnchorProps) =>
<DokkaSearchAnchor wrapperProps={wrapperProps} buttonProps={buttonProps} popup={popup} />
}
/>
diff --git a/plugins/base/frontend/src/main/components/search/types.ts b/plugins/base/frontend/src/main/components/search/types.ts
index 84e55399..57e7e169 100644
--- a/plugins/base/frontend/src/main/components/search/types.ts
+++ b/plugins/base/frontend/src/main/components/search/types.ts
@@ -1,4 +1,4 @@
-import React from "react";
+import React, {ButtonHTMLAttributes, HTMLAttributes, ReactNode, RefCallback} from "react";
export type Page = {
name: string;
@@ -37,3 +37,15 @@ export type OptionWithHighlightComponent = Option & {
export type SearchProps = {
searchResult: OptionWithSearchResult,
}
+
+export interface DataTestProps {
+ 'data-test'?: string | null | undefined
+}
+
+export interface CustomAnchorProps {
+ wrapperProps: HTMLAttributes<HTMLElement> & DataTestProps & {ref: RefCallback<HTMLElement>}
+ buttonProps: Pick<ButtonHTMLAttributes<HTMLButtonElement>, 'id' | 'disabled' | 'children'> &
+ {onClick: () => void} &
+ DataTestProps,
+ popup: ReactNode
+} \ No newline at end of file
diff --git a/plugins/base/frontend/src/main/components/utils/hotkey.ts b/plugins/base/frontend/src/main/components/utils/hotkey.ts
new file mode 100644
index 00000000..8ba47ab5
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/utils/hotkey.ts
@@ -0,0 +1,58 @@
+import {detectOsKind, OsKind} from "./os";
+
+type ModifierKey = {
+ name: string
+ keyArg: string
+}
+
+class ModifierKeys {
+ static metaKey: ModifierKey = {name: "Command", keyArg: "Meta"}
+ static ctrlKey: ModifierKey = {name: "Ctrl", keyArg: "Control"}
+ static altKey: ModifierKey = {name: "Alt", keyArg: "Alt"}
+ static shiftKey: ModifierKey = {name: "Shift", keyArg: "Shift"}
+}
+
+const setOfKeys = [ModifierKeys.altKey, ModifierKeys.shiftKey, ModifierKeys.ctrlKey, ModifierKeys.metaKey]
+
+export class Hotkey {
+ private readonly osKind: OsKind;
+
+ constructor() {
+ this.osKind = detectOsKind()
+ }
+
+ public getOsAccelKeyName() {
+ return this.getOsAccelKey().name
+ }
+
+ /**
+ * Register a hotkey of combination Accel key (Cmd/Ctrl depending on OS).
+ * The method also checks that other modifiers key is not pressed to avoid shortcuts intersection.
+ * E.g. don't trigger [Ctrl+K] if [Ctrl + Shift + K] pressed
+ */
+ public registerHotkeyWithAccel = (event: () => void, letter: string) => {
+ const osMetaKey = this.getOsAccelKey()
+ document.onkeydown = (keyDownEvent) => {
+ const isMetaKeyPressed = keyDownEvent.getModifierState(osMetaKey.keyArg)
+ const isOtherModifierKeyPressed = setOfKeys
+ .filter(key => key !== osMetaKey)
+ .map((otherKeys: ModifierKey) => keyDownEvent.getModifierState(otherKeys.keyArg))
+ .some(value => value)
+
+ if (isMetaKeyPressed && !isOtherModifierKeyPressed && keyDownEvent.key === letter) {
+ keyDownEvent.preventDefault()
+ event()
+ }
+ };
+ }
+
+ private getOsAccelKey(): ModifierKey {
+ switch (this.osKind) {
+ case OsKind.MACOS:
+ return ModifierKeys.metaKey
+ default:
+ return ModifierKeys.ctrlKey
+ }
+ }
+}
+
diff --git a/plugins/base/frontend/src/main/components/utils/os.ts b/plugins/base/frontend/src/main/components/utils/os.ts
new file mode 100644
index 00000000..3005245c
--- /dev/null
+++ b/plugins/base/frontend/src/main/components/utils/os.ts
@@ -0,0 +1,14 @@
+export enum OsKind{
+ WINDOWS,
+ MACOS,
+ LINUX,
+ OTHER
+}
+
+export const detectOsKind = (): OsKind => {
+ const userAgent = navigator.userAgent
+ if(userAgent.includes("Mac")) return OsKind.MACOS
+ else if (userAgent.includes("Win")) return OsKind.WINDOWS
+ else if (userAgent.includes("Linux")) return OsKind.LINUX
+ else return OsKind.OTHER
+} \ No newline at end of file