mirror of
https://github.com/BookStackApp/BookStack.git
synced 2024-10-30 07:32:39 +01:00
Distributed shortcut actions to common ui elements
This commit is contained in:
parent
b4cb375a02
commit
78b6450031
@ -3,8 +3,31 @@
|
||||
* @type {Object<string, string>}
|
||||
*/
|
||||
const defaultMap = {
|
||||
"edit": "e",
|
||||
// Header actions
|
||||
"home": "1",
|
||||
"shelves_view": "2",
|
||||
"books_view": "3",
|
||||
"settings_view": "4",
|
||||
"favorites_view": "5",
|
||||
"profile_view": "6",
|
||||
"global_search": "/",
|
||||
"logout": "0",
|
||||
|
||||
// Generic actions
|
||||
"edit": "e",
|
||||
"new": "n",
|
||||
"copy": "c",
|
||||
"delete": "d",
|
||||
"favorite": "f",
|
||||
"export": "x",
|
||||
"sort": "s",
|
||||
"permissions": "p",
|
||||
"move": "m",
|
||||
"revisions": "r",
|
||||
|
||||
// Navigation
|
||||
"next": "ArrowRight",
|
||||
"prev": "ArrowLeft",
|
||||
};
|
||||
|
||||
function reverseMap(map) {
|
||||
@ -26,10 +49,10 @@ class Shortcuts {
|
||||
this.mapByShortcut = reverseMap(this.mapById);
|
||||
|
||||
this.hintsShowing = false;
|
||||
|
||||
this.hideHints = this.hideHints.bind(this);
|
||||
// TODO - Allow custom key maps
|
||||
// TODO - Allow turning off shortcuts
|
||||
// TODO - Roll out to interface elements
|
||||
// TODO - Hide hints on focus, scroll, click
|
||||
|
||||
this.setupListeners();
|
||||
}
|
||||
@ -53,7 +76,6 @@ class Shortcuts {
|
||||
window.addEventListener('keydown', event => {
|
||||
if (event.key === '?') {
|
||||
this.hintsShowing ? this.hideHints() : this.showHints();
|
||||
this.hintsShowing = !this.hintsShowing;
|
||||
}
|
||||
});
|
||||
}
|
||||
@ -81,6 +103,12 @@ class Shortcuts {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (el.matches('div[tabindex]')) {
|
||||
el.click();
|
||||
el.focus();
|
||||
return true;
|
||||
}
|
||||
|
||||
console.error(`Shortcut attempted to be ran for element type that does not have handling setup`, el);
|
||||
|
||||
return false;
|
||||
@ -88,11 +116,24 @@ class Shortcuts {
|
||||
|
||||
showHints() {
|
||||
const shortcutEls = this.container.querySelectorAll('[data-shortcut]');
|
||||
const displayedIds = new Set();
|
||||
for (const shortcutEl of shortcutEls) {
|
||||
const id = shortcutEl.getAttribute('data-shortcut');
|
||||
if (displayedIds.has(id)) {
|
||||
continue;
|
||||
}
|
||||
|
||||
const key = this.mapById[id];
|
||||
this.showHintLabel(shortcutEl, key);
|
||||
displayedIds.add(id);
|
||||
}
|
||||
|
||||
window.addEventListener('scroll', this.hideHints);
|
||||
window.addEventListener('focus', this.hideHints);
|
||||
window.addEventListener('blur', this.hideHints);
|
||||
window.addEventListener('click', this.hideHints);
|
||||
|
||||
this.hintsShowing = true;
|
||||
}
|
||||
|
||||
showHintLabel(targetEl, key) {
|
||||
@ -113,6 +154,13 @@ class Shortcuts {
|
||||
for (const hint of hints) {
|
||||
hint.remove();
|
||||
}
|
||||
|
||||
window.removeEventListener('scroll', this.hideHints);
|
||||
window.removeEventListener('focus', this.hideHints);
|
||||
window.removeEventListener('blur', this.hideHints);
|
||||
window.removeEventListener('click', this.hideHints);
|
||||
|
||||
this.hintsShowing = false;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -37,7 +37,7 @@
|
||||
<h5>{{ trans('common.actions') }}</h5>
|
||||
<div class="icon-list text-primary">
|
||||
@if(user()->can('book-create-all'))
|
||||
<a href="{{ url("/create-book") }}" class="icon-list-item">
|
||||
<a href="{{ url("/create-book") }}" data-shortcut="new" class="icon-list-item">
|
||||
<span>@icon('add')</span>
|
||||
<span>{{ trans('entities.books_create') }}</span>
|
||||
</a>
|
||||
|
@ -94,13 +94,13 @@
|
||||
<div class="icon-list text-primary">
|
||||
|
||||
@if(userCan('page-create', $book))
|
||||
<a href="{{ $book->getUrl('/create-page') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/create-page') }}" data-shortcut="new" class="icon-list-item">
|
||||
<span>@icon('add')</span>
|
||||
<span>{{ trans('entities.pages_new') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('chapter-create', $book))
|
||||
<a href="{{ $book->getUrl('/create-chapter') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/create-chapter') }}" data-shortcut="new" class="icon-list-item">
|
||||
<span>@icon('add')</span>
|
||||
<span>{{ trans('entities.chapters_new') }}</span>
|
||||
</a>
|
||||
@ -113,25 +113,25 @@
|
||||
<span>@icon('edit')</span>
|
||||
<span>{{ trans('common.edit') }}</span>
|
||||
</a>
|
||||
<a href="{{ $book->getUrl('/sort') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/sort') }}" data-shortcut="sort" class="icon-list-item">
|
||||
<span>@icon('sort')</span>
|
||||
<span>{{ trans('common.sort') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('book-create-all'))
|
||||
<a href="{{ $book->getUrl('/copy') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/copy') }}" data-shortcut="copy" class="icon-list-item">
|
||||
<span>@icon('copy')</span>
|
||||
<span>{{ trans('common.copy') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('restrictions-manage', $book))
|
||||
<a href="{{ $book->getUrl('/permissions') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/permissions') }}" data-shortcut="permissions" class="icon-list-item">
|
||||
<span>@icon('lock')</span>
|
||||
<span>{{ trans('entities.permissions') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('book-delete', $book))
|
||||
<a href="{{ $book->getUrl('/delete') }}" class="icon-list-item">
|
||||
<a href="{{ $book->getUrl('/delete') }}" data-shortcut="delete" class="icon-list-item">
|
||||
<span>@icon('delete')</span>
|
||||
<span>{{ trans('common.delete') }}</span>
|
||||
</a>
|
||||
|
@ -108,7 +108,7 @@
|
||||
<div class="icon-list text-primary">
|
||||
|
||||
@if(userCan('page-create', $chapter))
|
||||
<a href="{{ $chapter->getUrl('/create-page') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/create-page') }}" data-shortcut="new" class="icon-list-item">
|
||||
<span>@icon('add')</span>
|
||||
<span>{{ trans('entities.pages_new') }}</span>
|
||||
</a>
|
||||
@ -117,31 +117,31 @@
|
||||
<hr class="primary-background"/>
|
||||
|
||||
@if(userCan('chapter-update', $chapter))
|
||||
<a href="{{ $chapter->getUrl('/edit') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/edit') }}" data-shortcut="edit" class="icon-list-item">
|
||||
<span>@icon('edit')</span>
|
||||
<span>{{ trans('common.edit') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCanOnAny('create', \BookStack\Entities\Models\Book::class) || userCan('chapter-create-all') || userCan('chapter-create-own'))
|
||||
<a href="{{ $chapter->getUrl('/copy') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/copy') }}" data-shortcut="copy" class="icon-list-item">
|
||||
<span>@icon('copy')</span>
|
||||
<span>{{ trans('common.copy') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('chapter-update', $chapter) && userCan('chapter-delete', $chapter))
|
||||
<a href="{{ $chapter->getUrl('/move') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/move') }}" data-shortcut="move" class="icon-list-item">
|
||||
<span>@icon('folder')</span>
|
||||
<span>{{ trans('common.move') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('restrictions-manage', $chapter))
|
||||
<a href="{{ $chapter->getUrl('/permissions') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/permissions') }}" data-shortcut="permissions" class="icon-list-item">
|
||||
<span>@icon('lock')</span>
|
||||
<span>{{ trans('entities.permissions') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('chapter-delete', $chapter))
|
||||
<a href="{{ $chapter->getUrl('/delete') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->getUrl('/delete') }}" data-shortcut="delete" class="icon-list-item">
|
||||
<span>@icon('delete')</span>
|
||||
<span>{{ trans('common.delete') }}</span>
|
||||
</a>
|
||||
@ -149,7 +149,7 @@
|
||||
|
||||
@if($chapter->book && userCan('book-update', $chapter->book))
|
||||
<hr class="primary-background"/>
|
||||
<a href="{{ $chapter->book->getUrl('/sort') }}" class="icon-list-item">
|
||||
<a href="{{ $chapter->book->getUrl('/sort') }}" data-shortcut="sort" class="icon-list-item">
|
||||
<span>@icon('sort')</span>
|
||||
<span>{{ trans('entities.chapter_sort_book') }}</span>
|
||||
</a>
|
||||
|
@ -2,7 +2,7 @@
|
||||
<div class="grid mx-l">
|
||||
|
||||
<div>
|
||||
<a href="{{ url('/') }}" class="logo">
|
||||
<a href="{{ url('/') }}" data-shortcut="home" class="logo">
|
||||
@if(setting('app-logo', '') !== 'none')
|
||||
<img class="logo-image" src="{{ setting('app-logo', '') === '' ? url('/logo.png') : url(setting('app-logo', '')) }}" alt="Logo">
|
||||
@endif
|
||||
@ -34,14 +34,14 @@
|
||||
@if (hasAppAccess())
|
||||
<a class="hide-over-l" href="{{ url('/search') }}">@icon('search'){{ trans('common.search') }}</a>
|
||||
@if(userCanOnAny('view', \BookStack\Entities\Models\Bookshelf::class) || userCan('bookshelf-view-all') || userCan('bookshelf-view-own'))
|
||||
<a href="{{ url('/shelves') }}">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
|
||||
<a href="{{ url('/shelves') }}" data-shortcut="shelves_view">@icon('bookshelf'){{ trans('entities.shelves') }}</a>
|
||||
@endif
|
||||
<a href="{{ url('/books') }}">@icon('books'){{ trans('entities.books') }}</a>
|
||||
<a href="{{ url('/books') }}" data-shortcut="books_view">@icon('books'){{ trans('entities.books') }}</a>
|
||||
@if(signedInUser() && userCan('settings-manage'))
|
||||
<a href="{{ url('/settings') }}">@icon('settings'){{ trans('settings.settings') }}</a>
|
||||
<a href="{{ url('/settings') }}" data-shortcut="settings_view">@icon('settings'){{ trans('settings.settings') }}</a>
|
||||
@endif
|
||||
@if(signedInUser() && userCan('users-manage') && !userCan('settings-manage'))
|
||||
<a href="{{ url('/settings/users') }}">@icon('users'){{ trans('settings.users') }}</a>
|
||||
<a href="{{ url('/settings/users') }}" data-shortcut="settings_view">@icon('users'){{ trans('settings.users') }}</a>
|
||||
@endif
|
||||
@endif
|
||||
|
||||
@ -62,13 +62,13 @@
|
||||
</span>
|
||||
<ul refs="dropdown@menu" class="dropdown-menu" role="menu">
|
||||
<li>
|
||||
<a href="{{ url('/favourites') }}" class="icon-item">
|
||||
<a href="{{ url('/favourites') }}" data-shortcut="favorites_view" class="icon-item">
|
||||
@icon('star')
|
||||
<div>{{ trans('entities.my_favourites') }}</div>
|
||||
</a>
|
||||
</li>
|
||||
<li>
|
||||
<a href="{{ $currentUser->getProfileUrl() }}" class="icon-item">
|
||||
<a href="{{ $currentUser->getProfileUrl() }}" data-shortcut="profile_view" class="icon-item">
|
||||
@icon('user')
|
||||
<div>{{ trans('common.view_profile') }}</div>
|
||||
</a>
|
||||
@ -83,7 +83,7 @@
|
||||
<form action="{{ url(config('auth.method') === 'saml2' ? '/saml2/logout' : '/logout') }}"
|
||||
method="post">
|
||||
{{ csrf_field() }}
|
||||
<button class="icon-item">
|
||||
<button class="icon-item" data-shortcut="logout">
|
||||
@icon('logout')
|
||||
<div>{{ trans('auth.logout') }}</div>
|
||||
</button>
|
||||
|
@ -2,8 +2,13 @@
|
||||
class="dropdown-container"
|
||||
id="export-menu">
|
||||
|
||||
<div refs="dropdown@toggle" class="icon-list-item"
|
||||
aria-haspopup="true" aria-expanded="false" aria-label="{{ trans('entities.export') }}" tabindex="0">
|
||||
<div refs="dropdown@toggle"
|
||||
class="icon-list-item"
|
||||
aria-haspopup="true"
|
||||
aria-expanded="false"
|
||||
aria-label="{{ trans('entities.export') }}"
|
||||
data-shortcut="export"
|
||||
tabindex="0">
|
||||
<span>@icon('export')</span>
|
||||
<span>{{ trans('entities.export') }}</span>
|
||||
</div>
|
||||
|
@ -5,7 +5,7 @@
|
||||
{{ csrf_field() }}
|
||||
<input type="hidden" name="type" value="{{ get_class($entity) }}">
|
||||
<input type="hidden" name="id" value="{{ $entity->id }}">
|
||||
<button type="submit" class="icon-list-item text-primary">
|
||||
<button type="submit" data-shortcut="favorite" class="icon-list-item text-primary">
|
||||
<span>@icon($isFavourite ? 'star' : 'star-outline')</span>
|
||||
<span>{{ $isFavourite ? trans('common.unfavourite') : trans('common.favourite') }}</span>
|
||||
</button>
|
||||
|
@ -1,7 +1,7 @@
|
||||
<div id="sibling-navigation" class="grid half collapse-xs items-center mb-m px-m no-row-gap fade-in-when-active print-hidden">
|
||||
<div>
|
||||
@if($previous)
|
||||
<a href="{{ $previous->getUrl() }}" class="outline-hover no-link-style block rounded">
|
||||
<a href="{{ $previous->getUrl() }}" data-shortcut="prev" class="outline-hover no-link-style block rounded">
|
||||
<div class="px-m pt-xs text-muted">{{ trans('common.previous') }}</div>
|
||||
<div class="inline-block">
|
||||
<div class="icon-list-item no-hover">
|
||||
@ -14,7 +14,7 @@
|
||||
</div>
|
||||
<div>
|
||||
@if($next)
|
||||
<a href="{{ $next->getUrl() }}" class="outline-hover no-link-style block rounded text-xs-right">
|
||||
<a href="{{ $next->getUrl() }}" data-shortcut="next" class="outline-hover no-link-style block rounded text-xs-right">
|
||||
<div class="px-m pt-xs text-muted text-xs-right">{{ trans('common.next') }}</div>
|
||||
<div class="inline block">
|
||||
<div class="icon-list-item no-hover">
|
||||
|
@ -145,37 +145,37 @@
|
||||
|
||||
{{--User Actions--}}
|
||||
@if(userCan('page-update', $page))
|
||||
<a href="{{ $page->getUrl('/edit') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/edit') }}" data-shortcut="edit" class="icon-list-item">
|
||||
<span>@icon('edit')</span>
|
||||
<span>{{ trans('common.edit') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCanOnAny('create', \BookStack\Entities\Models\Book::class) || userCanOnAny('create', \BookStack\Entities\Models\Chapter::class) || userCan('page-create-all') || userCan('page-create-own'))
|
||||
<a href="{{ $page->getUrl('/copy') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/copy') }}" data-shortcut="copy" class="icon-list-item">
|
||||
<span>@icon('copy')</span>
|
||||
<span>{{ trans('common.copy') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('page-update', $page))
|
||||
@if(userCan('page-delete', $page))
|
||||
<a href="{{ $page->getUrl('/move') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/move') }}" data-shortcut="move" class="icon-list-item">
|
||||
<span>@icon('folder')</span>
|
||||
<span>{{ trans('common.move') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@endif
|
||||
<a href="{{ $page->getUrl('/revisions') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/revisions') }}" data-shortcut="revisions" class="icon-list-item">
|
||||
<span>@icon('history')</span>
|
||||
<span>{{ trans('entities.revisions') }}</span>
|
||||
</a>
|
||||
@if(userCan('restrictions-manage', $page))
|
||||
<a href="{{ $page->getUrl('/permissions') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/permissions') }}" data-shortcut="permissions" class="icon-list-item">
|
||||
<span>@icon('lock')</span>
|
||||
<span>{{ trans('entities.permissions') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
@if(userCan('page-delete', $page))
|
||||
<a href="{{ $page->getUrl('/delete') }}" class="icon-list-item">
|
||||
<a href="{{ $page->getUrl('/delete') }}" data-shortcut="delete" class="icon-list-item">
|
||||
<span>@icon('delete')</span>
|
||||
<span>{{ trans('common.delete') }}</span>
|
||||
</a>
|
||||
|
@ -10,7 +10,7 @@
|
||||
<h5>{{ trans('common.actions') }}</h5>
|
||||
<div class="icon-list text-primary">
|
||||
@if(userCan('bookshelf-create-all'))
|
||||
<a href="{{ url("/create-shelf") }}" class="icon-list-item">
|
||||
<a href="{{ url("/create-shelf") }}" data-shortcut="new" class="icon-list-item">
|
||||
<span>@icon('add')</span>
|
||||
<span>{{ trans('entities.shelves_new_action') }}</span>
|
||||
</a>
|
||||
|
@ -112,7 +112,7 @@
|
||||
<div class="icon-list text-primary">
|
||||
|
||||
@if(userCan('book-create-all') && userCan('bookshelf-update', $shelf))
|
||||
<a href="{{ $shelf->getUrl('/create-book') }}" class="icon-list-item">
|
||||
<a href="{{ $shelf->getUrl('/create-book') }}" data-shortcut="new" class="icon-list-item">
|
||||
<span class="icon">@icon('add')</span>
|
||||
<span>{{ trans('entities.books_new_action') }}</span>
|
||||
</a>
|
||||
@ -123,21 +123,21 @@
|
||||
<hr class="primary-background">
|
||||
|
||||
@if(userCan('bookshelf-update', $shelf))
|
||||
<a href="{{ $shelf->getUrl('/edit') }}" class="icon-list-item">
|
||||
<a href="{{ $shelf->getUrl('/edit') }}" data-shortcut="edit" class="icon-list-item">
|
||||
<span>@icon('edit')</span>
|
||||
<span>{{ trans('common.edit') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(userCan('restrictions-manage', $shelf))
|
||||
<a href="{{ $shelf->getUrl('/permissions') }}" class="icon-list-item">
|
||||
<a href="{{ $shelf->getUrl('/permissions') }}" data-shortcut="permissions" class="icon-list-item">
|
||||
<span>@icon('lock')</span>
|
||||
<span>{{ trans('entities.permissions') }}</span>
|
||||
</a>
|
||||
@endif
|
||||
|
||||
@if(userCan('bookshelf-delete', $shelf))
|
||||
<a href="{{ $shelf->getUrl('/delete') }}" class="icon-list-item">
|
||||
<a href="{{ $shelf->getUrl('/delete') }}" data-shortcut="delete" class="icon-list-item">
|
||||
<span>@icon('delete')</span>
|
||||
<span>{{ trans('common.delete') }}</span>
|
||||
</a>
|
||||
|
Loading…
Reference in New Issue
Block a user