feature: keyboard shortcuts and tips (#258)

* feature: draft of the feature

* fix: default state of the modal

* feature: shortcuts and tips

* feature: typo

* refactor: deletes "Can be used to"

* refactor: default state to close

* style: consisten keyboard layout

* fix: - third scroll bar

* fix: styling correction alligh all text to baseline

* refactor: rewrite on maner of MS Teams

* fix: initial state - false

Co-authored-by: kunzheng <58841788+kunzms@users.noreply.github.com>
This commit is contained in:
alex-krasn 2020-05-19 16:15:59 -07:00 коммит произвёл GitHub
Родитель 2a3383d4a0
Коммит 37aa859a80
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
9 изменённых файлов: 463 добавлений и 11 удалений

Просмотреть файл

@ -12,6 +12,7 @@ import IAppErrorActions, * as appErrorActions from "./redux/actions/appErrorActi
import { ErrorHandler } from "./react/components/common/errorHandler/errorHandler";
import { KeyboardManager } from "./react/components/common/keyboardManager/keyboardManager";
import { HelpMenu } from "./react/components/shell/helpMenu";
import { KeyboardShortcuts } from "./react/components/shell/keyboardShortcuts";
import { MainContentRouter } from "./react/components/shell/mainContentRouter";
import { Sidebar } from "./react/components/shell/sidebar";
import { StatusBar } from "./react/components/shell/statusBar";
@ -77,7 +78,12 @@ export default class App extends React.Component<IAppProps> {
<BrowserRouter>
<div className={`app-shell platform-${platform}`}>
<TitleBar icon="TagGroup">
<div className="app-help-menu-icon"><HelpMenu/></div>
<div className="app-hotkeys-menu-icon">
<KeyboardShortcuts />
</div>
<div className="app-help-menu-icon">
<HelpMenu />
</div>
</TitleBar>
<div className="app-main">
<Sidebar project={this.props.currentProject} />

Различия файлов скрыты, потому что одна или несколько строк слишком длинны

Просмотреть файл

@ -322,6 +322,68 @@ export const english: IAppStrings = {
profile: {
settings: "Profile Settings",
},
shortcuts: {
squareBrackets: {
keys: {
leftBracket: "[",
rightBracket: "]",
},
description: {
prevWord: "Move the selection to previous word",
nextWord: "Move the selection to next word",
},
},
greaterAndLessThan: {
keys: {
lessThan: "<",
greaterThan: ">",
},
description: {
prevPage: "Go to the previous page in multi-pages documents",
nextPage: "Go to the next page in multi-pages documents",
},
},
zoomKeys: {
keys: {
minus: "-",
plus: "=",
slash: "/",
},
description: {
in: "Zoom in",
out: "Zoom out",
reset: "Reset zoom",
},
},
deleteAndBackspace: {
keys: {
delete: "Delete",
backSpace: "Backspace",
},
description: {
delete: "Delete selection from document map or selection key from a label",
backSpace: "Delete selection from document map or selection key from a label",
},
},
tips: {
quickLabeling: {
name: "Quick labeling",
description: "Hotkeys of 1 through 0 and all letters are assigned to first 36 tags, after you selected one or multiple words from the highlighted text elements, by pressing these hotkeys, you can label the selected words.",
},
renameTag: {
name: "Rename Tag",
description: "Hold Alt key and click on tag name, user can change the tag's name.",
},
multipleWordSelection: {
name: "Multiple words selection",
description: "Click and hold on word, than hover over other words to do multiple words selection at a time.",
},
},
headers: {
keyboardShortcuts: "Keyboard shortcuts",
otherTips: "Other tips",
},
},
errors: {
unknown: {
title: "Unknown Error",

Просмотреть файл

@ -325,6 +325,68 @@ export const spanish: IAppStrings = {
profile: {
settings: "Configuración de Perfíl",
},
shortcuts: {
squareBrackets: {
keys: {
leftBracket: "[",
rightBracket: "]",
},
description: {
prevWord: "Mover la selección a la palabra anterior",
nextWord: "Mover la selección a la siguiente palabra",
},
},
greaterAndLessThan: {
keys: {
lessThan: "<",
greaterThan: ">",
},
description: {
prevPage: "Ir a la página anterior en documentos de varias páginas",
nextPage: "Ir a la página siguiente en documentos de varias páginas",
},
},
zoomKeys: {
keys: {
minus: "-",
plus: "=",
slash: "/",
},
description: {
in: "Acercarse",
out: "Disminuir el zoom",
reset: "Restablecer zoom",
},
},
deleteAndBackspace: {
keys: {
delete: "Delete",
backSpace: "Backspace",
},
description: {
delete: "Eliminar selección del mapa del documento o clave de selección de una etiqueta",
backSpace: "Eliminar selección del mapa del documento o clave de selección de una etiqueta",
},
},
tips: {
quickLabeling: {
name: "Etiquetado rápido",
description: "Las teclas de acceso rápido de 1 a 0 y todas las letras se asignan a las primeras 36 etiquetas, después de seleccionar una o varias palabras de los elementos de texto resaltados, al presionar estas teclas de acceso rápido, puede etiquetar las palabras seleccionadas.",
},
renameTag: {
name: "Rename Tag",
description: "Mantenga presionada la tecla Alt y haga clic en el nombre de la etiqueta, el usuario puede cambiar el nombre de la etiqueta.",
},
multipleWordSelection: {
name: "Selección de palabras múltiples",
description: "Haga clic y mantenga presionada la palabra, luego desplace el cursor sobre otras palabras para seleccionar varias palabras a la vez.",
},
},
headers: {
keyboardShortcuts: "Atajos de teclado",
otherTips: "Otros consejos",
},
},
errors: {
unknown: {
title: "Error desconocido",

Просмотреть файл

@ -317,6 +317,68 @@ export interface IAppStrings {
profile: {
settings: string;
};
shortcuts: {
squareBrackets: {
keys: {
leftBracket: string,
rightBracket: string,
},
description: {
prevWord: string,
nextWord: string,
},
},
greaterAndLessThan: {
keys: {
lessThan: string,
greaterThan: string,
},
description: {
prevPage: string,
nextPage: string,
},
},
zoomKeys: {
keys: {
minus: string,
plus: string,
slash: string,
},
description: {
in: string,
out: string,
reset: string,
},
},
deleteAndBackspace: {
keys: {
delete: string,
backSpace: string,
},
description: {
delete: string,
backSpace: string,
},
},
tips: {
quickLabeling: {
name: string,
description: string,
},
renameTag: {
name: string,
description: string,
},
multipleWordSelection: {
name: string,
description: string,
},
},
headers: {
keyboardShortcuts: string,
otherTips: string,
},
};
errors: {
unknown: IErrorMetadata,
projectInvalidJson: IErrorMetadata,

Просмотреть файл

@ -34,6 +34,10 @@
"name": "Add",
"unicode": "E710"
},
{
"name": "Cancel",
"unicode": "E711"
},
{
"name": "Settings",
"unicode": "E713"
@ -58,10 +62,6 @@
"name": "CheckboxComposite",
"unicode": "E73A"
},
{
"name": "LabelComposite",
"unicode": "E932"
},
{
"name": "CheckMark",
"unicode": "E73E"
@ -126,6 +126,10 @@
"name": "Tag",
"unicode": "E8EC"
},
{
"name": "Label",
"unicode": "E932"
},
{
"name": "Info",
"unicode": "E946"
@ -177,6 +181,10 @@
{
"name": "WarningSolid",
"unicode": "F736"
},
{
"name": "BookAnswers",
"unicode": "F8A4"
}
]
}

Просмотреть файл

@ -0,0 +1,96 @@
@import "../../../assets/sass/theme.scss";
@import "../common/common.scss";
.container {
display: flex;
margin: 2rem;
padding: 4rem 1rem 2rem 2rem;
h3, h4, h6 {
font-weight: 600;
}
.header {
display: flex;
position: absolute;
top: 0;
left: 0;
height: 4rem;
width: calc(100% - 1.5rem);
background-color: rgb(30, 32, 36);
h3 {
display: flex;
width: 90%;
align-self: center;
margin-top: 1.5rem;
margin-left: 2rem;
}
.close-modal {
margin: 1.25rem 0rem auto auto;
color: #fff;
&:hover {
color: $lighter-5;
cursor: pointer;
}
}
}
.content {
.description {
color: rgba(255, 255, 255, 0.6);
}
li {
list-style: none;
}
ul {
padding: 0;
}
h6 {
margin-bottom: 0;
}
.shortcuts-list {
overflow-y: auto;
.shortcut {
display: flex;
border-bottom: 1px solid rgba(255, 255, 255, 0.05);
margin: 0.5rem;
margin-left: 0;
&-description {
align-self: center;
margin-bottom: 0.25rem;
margin-right: 0.125rem;
width: 70%;
}
&-keys {
display: flex;
justify-content: flex-end;
width: 30%;
.keyboard-key {
display: inline-flex;
align-items: center;
color: rgba(255, 255, 255, 0.7);
background: rgba(49, 52, 58, 0.79);
font-weight: 600;
height: 2rem;
margin-bottom: 0.5rem;
padding: 0 1rem;
-moz-border-radius: 4px;
border-radius: 4px;
border-bottom: 1px solid rgba(255, 255, 255, 0.4);
}
}
}
}
}
}
// modal icon-button
.shortcuts-modal-button {
padding: 6px 8px;
color: #ccc;
display: inline-block;
&:hover {
color: #fff;
background-color: $lighter-2;
cursor: pointer;
}
}

Просмотреть файл

@ -0,0 +1,149 @@
// Copyright (c) Microsoft Corporation.
// Licensed under the MIT license.
import React, { useState } from "react";
import { Modal } from "office-ui-fabric-react/lib/Modal";
import { FontIcon, IFontIconProps } from "office-ui-fabric-react";
import { ICustomizations, Customizer } from "office-ui-fabric-react/lib/Utilities";
import { useId } from "@uifabric/react-hooks";
import { getDarkGreyTheme } from "../../../common/themes";
import { strings } from "../../../common/strings";
import "./keyboardShortcuts.scss";
export interface IHotKeysModalState {
showModal: boolean;
}
interface IKey {
key: string;
description: string;
}
export const KeyboardShortcuts: React.FC = () => {
const dark: ICustomizations = {
settings: {
theme: getDarkGreyTheme(),
},
scopedSettings: {},
};
const uniqueId: string = useId("shortcuts");
const [showModal, setShowModal] = useState(false);
const closeModal = () => setShowModal(false);
const shortcutsItems: IKey[] = [
{
key: strings.shortcuts.squareBrackets.keys.leftBracket,
description: strings.shortcuts.squareBrackets.description.prevWord,
},
{
key: strings.shortcuts.squareBrackets.keys.rightBracket,
description: strings.shortcuts.squareBrackets.description.nextWord,
},
{
key: strings.shortcuts.greaterAndLessThan.keys.lessThan,
description: strings.shortcuts.greaterAndLessThan.description.prevPage,
},
{
key: strings.shortcuts.greaterAndLessThan.keys.greaterThan,
description: strings.shortcuts.greaterAndLessThan.description.nextPage,
},
{
key: strings.shortcuts.zoomKeys.keys.minus,
description: strings.shortcuts.zoomKeys.description.out,
},
{
key: strings.shortcuts.zoomKeys.keys.plus,
description: strings.shortcuts.zoomKeys.description.in,
},
{
key: strings.shortcuts.zoomKeys.keys.slash,
description: strings.shortcuts.zoomKeys.description.reset,
},
{
key: strings.shortcuts.deleteAndBackspace.keys.delete,
description: strings.shortcuts.deleteAndBackspace.description.delete,
},
{
key: strings.shortcuts.deleteAndBackspace.keys.backSpace,
description: strings.shortcuts.deleteAndBackspace.description.backSpace,
},
];
const tipsItems = [
{
name: strings.shortcuts.tips.quickLabeling.name,
description: strings.shortcuts.tips.quickLabeling.description,
},
{
name: strings.shortcuts.tips.renameTag.name,
description: strings.shortcuts.tips.renameTag.description,
},
{
name: strings.shortcuts.tips.multipleWordSelection.name,
description: strings.shortcuts.tips.multipleWordSelection.description,
},
];
const ShortcutsListItem = ({ item }): JSX.Element => {
return (
<li key={uniqueId} className="shortcut">
<div className="shortcut-description description">{item.description}</div>
<div className="shortcut-keys">
<span
key={uniqueId}
className="keyboard-key"
aria-label={`keyboard key - ${item.key}`}
>{item.key}</span>
</div>
</li>);
};
const TipsListItem = ({ item }): JSX.Element => (
<li key={uniqueId}>
<h6>{item.name}</h6>
<p className="description">{item.description}</p>
</li>
);
return (
<Customizer {...dark}>
<FontIcon
className="shortcuts-modal-button"
iconName="BookAnswers"
role="button"
onClick={() => setShowModal(true)}
/>
<Modal
titleAriaId={"Hot Keys Modal"}
isOpen={showModal}
onDismiss={closeModal}
isBlocking={false}
containerClassName="container"
// className="container"
>
<div className="header">
<h3>{strings.shortcuts.headers.keyboardShortcuts}</h3>
<FontIcon className="close-modal" role="button" onClick={closeModal} iconName="Cancel" />
</div>
<div className="content">
<div className="shortcuts-list-container">
<ul className="shortcuts-list">
{
shortcutsItems.map((item) => <ShortcutsListItem item={item} />)
}
</ul>
</div>
<div className="tips-list-container">
<h4 >{strings.shortcuts.headers.otherTips}</h4>
<ul className="tips-list">
{
tipsItems.map((item) => <TipsListItem item={item} />)
}
</ul>
</div>
</div>
</Modal>
</Customizer>
);
};

Просмотреть файл

@ -52,6 +52,9 @@ export function registerIcons() {
Filter: "\uE71C",
Table: "\uED86",
MapLayers: "\uE81E",
BookAnswers: "\uF8A4",
Cancel: "\uE711",
},
});
}