Skip to content

Commit

Permalink
Add hotkey for opening search dialog (#2633)
Browse files Browse the repository at this point in the history
  • Loading branch information
atyrin committed Aug 24, 2022
1 parent ce19982 commit 56beaa3
Show file tree
Hide file tree
Showing 7 changed files with 112 additions and 7 deletions.
@@ -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>
)
Expand Down
5 changes: 5 additions & 0 deletions plugins/base/frontend/src/main/components/search/search.scss
Expand Up @@ -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;

Expand Down
4 changes: 2 additions & 2 deletions plugins/base/frontend/src/main/components/search/search.tsx
Expand Up @@ -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';
Expand Down Expand Up @@ -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} />
}
/>
Expand Down
14 changes: 13 additions & 1 deletion 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;
Expand Down Expand Up @@ -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
}
58 changes: 58 additions & 0 deletions 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
}
}
}

14 changes: 14 additions & 0 deletions 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
}
1 change: 1 addition & 0 deletions plugins/base/frontend/src/main/types/@jetbrains/index.d.ts
@@ -1,4 +1,5 @@
declare module '@jetbrains/ring-ui' {
export const Tooltip: any;
export const Select: any;
export const List: any;
}

0 comments on commit 56beaa3

Please sign in to comment.