feat: Keyboard shortcut reference inside editor
This commit is contained in:
@ -21,6 +21,8 @@ import Sidebar from 'components/Sidebar';
|
|||||||
import SettingsSidebar from 'components/Sidebar/Settings';
|
import SettingsSidebar from 'components/Sidebar/Settings';
|
||||||
import Modals from 'components/Modals';
|
import Modals from 'components/Modals';
|
||||||
import DocumentHistory from 'components/DocumentHistory';
|
import DocumentHistory from 'components/DocumentHistory';
|
||||||
|
import Modal from 'components/Modal';
|
||||||
|
import KeyboardShortcuts from 'scenes/KeyboardShortcuts';
|
||||||
import ErrorSuspended from 'scenes/ErrorSuspended';
|
import ErrorSuspended from 'scenes/ErrorSuspended';
|
||||||
import AuthStore from 'stores/AuthStore';
|
import AuthStore from 'stores/AuthStore';
|
||||||
import UiStore from 'stores/UiStore';
|
import UiStore from 'stores/UiStore';
|
||||||
@ -41,6 +43,7 @@ type Props = {
|
|||||||
class Layout extends React.Component<Props> {
|
class Layout extends React.Component<Props> {
|
||||||
scrollable: ?HTMLDivElement;
|
scrollable: ?HTMLDivElement;
|
||||||
@observable redirectTo: ?string;
|
@observable redirectTo: ?string;
|
||||||
|
@observable keyboardShortcutsOpen: boolean = false;
|
||||||
|
|
||||||
componentWillMount() {
|
componentWillMount() {
|
||||||
this.updateBackground();
|
this.updateBackground();
|
||||||
@ -54,6 +57,15 @@ class Layout extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@keydown('shift+/')
|
||||||
|
handleOpenKeyboardShortcuts() {
|
||||||
|
this.keyboardShortcutsOpen = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
handleCloseKeyboardShortcuts = () => {
|
||||||
|
this.keyboardShortcutsOpen = false;
|
||||||
|
};
|
||||||
|
|
||||||
updateBackground() {
|
updateBackground() {
|
||||||
// ensure the wider page color always matches the theme
|
// ensure the wider page color always matches the theme
|
||||||
window.document.body.style.background = this.props.theme.background;
|
window.document.body.style.background = this.props.theme.background;
|
||||||
@ -71,11 +83,6 @@ class Layout extends React.Component<Props> {
|
|||||||
this.redirectTo = homeUrl();
|
this.redirectTo = homeUrl();
|
||||||
}
|
}
|
||||||
|
|
||||||
@keydown('shift+/')
|
|
||||||
openKeyboardShortcuts() {
|
|
||||||
this.props.ui.setActiveModal('keyboard-shortcuts');
|
|
||||||
}
|
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { auth, ui } = this.props;
|
const { auth, ui } = this.props;
|
||||||
const { user, team } = auth;
|
const { user, team } = auth;
|
||||||
@ -118,6 +125,13 @@ class Layout extends React.Component<Props> {
|
|||||||
</Switch>
|
</Switch>
|
||||||
</Container>
|
</Container>
|
||||||
<Modals ui={ui} />
|
<Modals ui={ui} />
|
||||||
|
<Modal
|
||||||
|
isOpen={this.keyboardShortcutsOpen}
|
||||||
|
onRequestClose={this.handleCloseKeyboardShortcuts}
|
||||||
|
title="Keyboard shortcuts"
|
||||||
|
>
|
||||||
|
<KeyboardShortcuts />
|
||||||
|
</Modal>
|
||||||
<GlobalStyles />
|
<GlobalStyles />
|
||||||
</Container>
|
</Container>
|
||||||
);
|
);
|
||||||
|
@ -9,7 +9,6 @@ import CollectionDelete from 'scenes/CollectionDelete';
|
|||||||
import CollectionExport from 'scenes/CollectionExport';
|
import CollectionExport from 'scenes/CollectionExport';
|
||||||
import DocumentDelete from 'scenes/DocumentDelete';
|
import DocumentDelete from 'scenes/DocumentDelete';
|
||||||
import DocumentShare from 'scenes/DocumentShare';
|
import DocumentShare from 'scenes/DocumentShare';
|
||||||
import KeyboardShortcuts from 'scenes/KeyboardShortcuts';
|
|
||||||
|
|
||||||
type Props = {
|
type Props = {
|
||||||
ui: UiStore,
|
ui: UiStore,
|
||||||
@ -55,9 +54,6 @@ class Modals extends React.Component<Props> {
|
|||||||
<Modal name="document-delete" title="Delete document">
|
<Modal name="document-delete" title="Delete document">
|
||||||
<DocumentDelete onSubmit={this.handleClose} />
|
<DocumentDelete onSubmit={this.handleClose} />
|
||||||
</Modal>
|
</Modal>
|
||||||
<Modal name="keyboard-shortcuts" title="Keyboard shortcuts">
|
|
||||||
<KeyboardShortcuts />
|
|
||||||
</Modal>
|
|
||||||
</span>
|
</span>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -9,6 +9,7 @@ type Props = {
|
|||||||
children: React.Node,
|
children: React.Node,
|
||||||
delay?: number,
|
delay?: number,
|
||||||
className?: string,
|
className?: string,
|
||||||
|
block?: boolean,
|
||||||
};
|
};
|
||||||
|
|
||||||
class Tooltip extends React.Component<Props> {
|
class Tooltip extends React.Component<Props> {
|
||||||
@ -17,7 +18,15 @@ class Tooltip extends React.Component<Props> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { tooltip, delay = 50, children, className, ...rest } = this.props;
|
const {
|
||||||
|
tooltip,
|
||||||
|
delay = 50,
|
||||||
|
children,
|
||||||
|
block,
|
||||||
|
className,
|
||||||
|
...rest
|
||||||
|
} = this.props;
|
||||||
|
const Component = block ? 'div' : 'span';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<StyledTippy
|
<StyledTippy
|
||||||
@ -30,7 +39,7 @@ class Tooltip extends React.Component<Props> {
|
|||||||
inertia
|
inertia
|
||||||
{...rest}
|
{...rest}
|
||||||
>
|
>
|
||||||
<span className={className}>{children}</span>
|
<Component className={className}>{children}</Component>
|
||||||
</StyledTippy>
|
</StyledTippy>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -1,6 +1,7 @@
|
|||||||
// @flow
|
// @flow
|
||||||
import * as React from 'react';
|
import * as React from 'react';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
import { observable } from 'mobx';
|
||||||
import { inject, observer } from 'mobx-react';
|
import { inject, observer } from 'mobx-react';
|
||||||
import { MoonIcon } from 'outline-icons';
|
import { MoonIcon } from 'outline-icons';
|
||||||
import styled, { withTheme } from 'styled-components';
|
import styled, { withTheme } from 'styled-components';
|
||||||
@ -8,6 +9,8 @@ import UiStore from 'stores/UiStore';
|
|||||||
import AuthStore from 'stores/AuthStore';
|
import AuthStore from 'stores/AuthStore';
|
||||||
import Flex from 'shared/components/Flex';
|
import Flex from 'shared/components/Flex';
|
||||||
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
import { DropdownMenu, DropdownMenuItem } from 'components/DropdownMenu';
|
||||||
|
import Modal from 'components/Modal';
|
||||||
|
import KeyboardShortcuts from 'scenes/KeyboardShortcuts';
|
||||||
import {
|
import {
|
||||||
developers,
|
developers,
|
||||||
changelog,
|
changelog,
|
||||||
@ -26,57 +29,74 @@ type Props = {
|
|||||||
|
|
||||||
@observer
|
@observer
|
||||||
class AccountMenu extends React.Component<Props> {
|
class AccountMenu extends React.Component<Props> {
|
||||||
handleOpenKeyboardShortcuts = () => {
|
@observable keyboardShortcutsOpen: boolean = false;
|
||||||
this.props.ui.setActiveModal('keyboard-shortcuts');
|
|
||||||
};
|
|
||||||
|
|
||||||
handleLogout = () => {
|
handleLogout = () => {
|
||||||
this.props.auth.logout();
|
this.props.auth.logout();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
handleOpenKeyboardShortcuts = () => {
|
||||||
|
this.keyboardShortcutsOpen = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCloseKeyboardShortcuts = () => {
|
||||||
|
this.keyboardShortcutsOpen = false;
|
||||||
|
};
|
||||||
|
|
||||||
render() {
|
render() {
|
||||||
const { ui, theme } = this.props;
|
const { ui, theme } = this.props;
|
||||||
const isLightTheme = ui.theme === 'light';
|
const isLightTheme = ui.theme === 'light';
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<DropdownMenu
|
<React.Fragment>
|
||||||
style={{ marginRight: 10, marginTop: -10 }}
|
<Modal
|
||||||
label={this.props.label}
|
isOpen={this.keyboardShortcutsOpen}
|
||||||
>
|
onRequestClose={this.handleCloseKeyboardShortcuts}
|
||||||
<DropdownMenuItem as={Link} to={settings()}>
|
title="Keyboard shortcuts"
|
||||||
Settings
|
>
|
||||||
</DropdownMenuItem>
|
<KeyboardShortcuts />
|
||||||
<DropdownMenuItem onClick={this.handleOpenKeyboardShortcuts}>
|
</Modal>
|
||||||
Keyboard shortcuts
|
<DropdownMenu
|
||||||
</DropdownMenuItem>
|
style={{ marginRight: 10, marginTop: -10 }}
|
||||||
<DropdownMenuItem href={developers()} target="_blank">
|
label={this.props.label}
|
||||||
API documentation
|
>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem as={Link} to={settings()}>
|
||||||
<hr />
|
Settings
|
||||||
<DropdownMenuItem href={changelog()} target="_blank">
|
</DropdownMenuItem>
|
||||||
Changelog
|
<DropdownMenuItem onClick={this.handleOpenKeyboardShortcuts}>
|
||||||
</DropdownMenuItem>
|
Keyboard shortcuts
|
||||||
<DropdownMenuItem href={spectrumUrl()} target="_blank">
|
</DropdownMenuItem>
|
||||||
Community
|
<DropdownMenuItem href={developers()} target="_blank">
|
||||||
</DropdownMenuItem>
|
API documentation
|
||||||
<DropdownMenuItem href={mailToUrl()} target="_blank">
|
</DropdownMenuItem>
|
||||||
Send us feedback
|
<hr />
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem href={changelog()} target="_blank">
|
||||||
<DropdownMenuItem href={githubIssuesUrl()} target="_blank">
|
Changelog
|
||||||
Report a bug
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
<DropdownMenuItem href={spectrumUrl()} target="_blank">
|
||||||
<hr />
|
Community
|
||||||
<DropdownMenuItem onClick={ui.toggleDarkMode}>
|
</DropdownMenuItem>
|
||||||
<NightMode justify="space-between">
|
<DropdownMenuItem href={mailToUrl()} target="_blank">
|
||||||
Night Mode{' '}
|
Send us feedback
|
||||||
<MoonIcon
|
</DropdownMenuItem>
|
||||||
color={isLightTheme ? theme.textSecondary : theme.primary}
|
<DropdownMenuItem href={githubIssuesUrl()} target="_blank">
|
||||||
/>
|
Report a bug
|
||||||
</NightMode>
|
</DropdownMenuItem>
|
||||||
</DropdownMenuItem>
|
<hr />
|
||||||
<hr />
|
<DropdownMenuItem onClick={ui.toggleDarkMode}>
|
||||||
<DropdownMenuItem onClick={this.handleLogout}>Log out</DropdownMenuItem>
|
<NightMode justify="space-between">
|
||||||
</DropdownMenu>
|
Night Mode{' '}
|
||||||
|
<MoonIcon
|
||||||
|
color={isLightTheme ? theme.textSecondary : theme.primary}
|
||||||
|
/>
|
||||||
|
</NightMode>
|
||||||
|
</DropdownMenuItem>
|
||||||
|
<hr />
|
||||||
|
<DropdownMenuItem onClick={this.handleLogout}>
|
||||||
|
Log out
|
||||||
|
</DropdownMenuItem>
|
||||||
|
</DropdownMenu>
|
||||||
|
</React.Fragment>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -22,6 +22,7 @@ import { emojiToUrl } from 'utils/emoji';
|
|||||||
import Header from './components/Header';
|
import Header from './components/Header';
|
||||||
import DocumentMove from './components/DocumentMove';
|
import DocumentMove from './components/DocumentMove';
|
||||||
import Branding from './components/Branding';
|
import Branding from './components/Branding';
|
||||||
|
import KeyboardShortcuts from './components/KeyboardShortcuts';
|
||||||
import Backlinks from './components/Backlinks';
|
import Backlinks from './components/Backlinks';
|
||||||
import ErrorBoundary from 'components/ErrorBoundary';
|
import ErrorBoundary from 'components/ErrorBoundary';
|
||||||
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
import LoadingPlaceholder from 'components/LoadingPlaceholder';
|
||||||
@ -426,7 +427,7 @@ class DocumentScene extends React.Component<Props> {
|
|||||||
</MaxWidth>
|
</MaxWidth>
|
||||||
</Container>
|
</Container>
|
||||||
</Container>
|
</Container>
|
||||||
{isShare && <Branding />}
|
{isShare ? <Branding /> : <KeyboardShortcuts />}
|
||||||
</ErrorBoundary>
|
</ErrorBoundary>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
72
app/scenes/Document/components/KeyboardShortcuts.js
Normal file
72
app/scenes/Document/components/KeyboardShortcuts.js
Normal file
@ -0,0 +1,72 @@
|
|||||||
|
// @flow
|
||||||
|
import * as React from 'react';
|
||||||
|
import styled from 'styled-components';
|
||||||
|
import breakpoint from 'styled-components-breakpoint';
|
||||||
|
import { observable } from 'mobx';
|
||||||
|
import { observer } from 'mobx-react';
|
||||||
|
import { KeyboardIcon } from 'outline-icons';
|
||||||
|
import Modal from 'components/Modal';
|
||||||
|
import Tooltip from 'components/Tooltip';
|
||||||
|
import KeyboardShortcuts from 'scenes/KeyboardShortcuts';
|
||||||
|
|
||||||
|
type Props = {};
|
||||||
|
|
||||||
|
@observer
|
||||||
|
class KeyboardShortcutsButton extends React.Component<Props> {
|
||||||
|
@observable keyboardShortcutsOpen: boolean = false;
|
||||||
|
|
||||||
|
handleOpenKeyboardShortcuts = () => {
|
||||||
|
this.keyboardShortcutsOpen = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
handleCloseKeyboardShortcuts = () => {
|
||||||
|
this.keyboardShortcutsOpen = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
render() {
|
||||||
|
return (
|
||||||
|
<React.Fragment>
|
||||||
|
<Modal
|
||||||
|
isOpen={this.keyboardShortcutsOpen}
|
||||||
|
onRequestClose={this.handleCloseKeyboardShortcuts}
|
||||||
|
title="Keyboard shortcuts"
|
||||||
|
>
|
||||||
|
<KeyboardShortcuts />
|
||||||
|
</Modal>
|
||||||
|
<Button onClick={this.handleOpenKeyboardShortcuts}>
|
||||||
|
<Tooltip tooltip="Keyboard shortcuts" placement="left" block>
|
||||||
|
<KeyboardIcon />
|
||||||
|
</Tooltip>
|
||||||
|
</Button>
|
||||||
|
</React.Fragment>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Button = styled.button`
|
||||||
|
display: none;
|
||||||
|
position: fixed;
|
||||||
|
bottom: 0;
|
||||||
|
right: 0;
|
||||||
|
margin: 24px;
|
||||||
|
width: 24px;
|
||||||
|
height: 24px;
|
||||||
|
opacity: 0.8;
|
||||||
|
background: none;
|
||||||
|
line-height: 0;
|
||||||
|
border: 0;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
opacity: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
${breakpoint('tablet')`
|
||||||
|
display: block;
|
||||||
|
`};
|
||||||
|
|
||||||
|
@media print {
|
||||||
|
display: none;
|
||||||
|
}
|
||||||
|
`;
|
||||||
|
|
||||||
|
export default KeyboardShortcutsButton;
|
@ -10,10 +10,8 @@ function KeyboardShortcuts() {
|
|||||||
return (
|
return (
|
||||||
<Flex column>
|
<Flex column>
|
||||||
<HelpText>
|
<HelpText>
|
||||||
Outline is designed to be super fast and easy to use. All of your usual
|
Outline is designed to be fast and easy to use. All of your usual
|
||||||
keyboard shortcuts work here, and there
|
keyboard shortcuts work here, and there’s Markdown too.
|
||||||
{"'"}
|
|
||||||
s Markdown too.
|
|
||||||
</HelpText>
|
</HelpText>
|
||||||
|
|
||||||
<h2>Navigation</h2>
|
<h2>Navigation</h2>
|
||||||
|
Reference in New Issue
Block a user