new shortcuts; alt+arrow key skips fragments, shift+arrow key jumps to last slide in the given direction #1105

edit
Hakim El Hattab 2021-09-06 13:02:58 +02:00
parent a3e8162eb1
commit 90bbe8be4f
5 changed files with 84 additions and 47 deletions

2
dist/reveal.esm.js vendored

File diff suppressed because one or more lines are too long

2
dist/reveal.js vendored

File diff suppressed because one or more lines are too long

View File

@ -32,15 +32,15 @@ export default class Keyboard {
} }
else { else {
this.shortcuts['N , SPACE'] = 'Next slide'; this.shortcuts['N , SPACE'] = 'Next slide';
this.shortcuts['P'] = 'Previous slide'; this.shortcuts['P , Shift SPACE'] = 'Previous slide';
this.shortcuts['← , H'] = 'Navigate left'; this.shortcuts['← , H'] = 'Navigate left';
this.shortcuts['→ , L'] = 'Navigate right'; this.shortcuts['→ , L'] = 'Navigate right';
this.shortcuts['↑ , K'] = 'Navigate up'; this.shortcuts['↑ , K'] = 'Navigate up';
this.shortcuts['↓ , J'] = 'Navigate down'; this.shortcuts['↓ , J'] = 'Navigate down';
} }
this.shortcuts['Home , Shift ←'] = 'First slide'; this.shortcuts['Alt + ←/&#8593/→/↓'] = 'Navigate without fragments';
this.shortcuts['End , Shift →'] = 'Last slide'; this.shortcuts['Shift + ←/&#8593/→/↓'] = 'Jump to first/last slide';
this.shortcuts['B , .'] = 'Pause'; this.shortcuts['B , .'] = 'Pause';
this.shortcuts['F'] = 'Fullscreen'; this.shortcuts['F'] = 'Fullscreen';
this.shortcuts['ESC, O'] = 'Slide overview'; this.shortcuts['ESC, O'] = 'Slide overview';
@ -182,13 +182,11 @@ export default class Keyboard {
let activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName ); let activeElementIsInput = document.activeElement && document.activeElement.tagName && /input|textarea/i.test( document.activeElement.tagName );
let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className); let activeElementIsNotes = document.activeElement && document.activeElement.className && /speaker-notes/i.test( document.activeElement.className);
// Whitelist specific modified + keycode combinations // Whitelist certain modifiers for slide navigation shortcuts
let prevSlideShortcut = event.shiftKey && event.keyCode === 32; let isNavigationKey = [32, 37, 38, 39, 40, 78, 80].indexOf( event.keyCode ) !== -1;
let firstSlideShortcut = event.shiftKey && keyCode === 37;
let lastSlideShortcut = event.shiftKey && keyCode === 39;
// Prevent all other events when a modifier is pressed // Prevent all other events when a modifier is pressed
let unusedModifier = !prevSlideShortcut && !firstSlideShortcut && !lastSlideShortcut && let unusedModifier = !( isNavigationKey && event.shiftKey || event.altKey ) &&
( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey ); ( event.shiftKey || event.altKey || event.ctrlKey || event.metaKey );
// Disregard the event if there's a focused element or a // Disregard the event if there's a focused element or a
@ -277,52 +275,58 @@ export default class Keyboard {
// P, PAGE UP // P, PAGE UP
if( keyCode === 80 || keyCode === 33 ) { if( keyCode === 80 || keyCode === 33 ) {
this.Reveal.prev(); this.Reveal.prev({skipFragments: event.altKey});
} }
// N, PAGE DOWN // N, PAGE DOWN
else if( keyCode === 78 || keyCode === 34 ) { else if( keyCode === 78 || keyCode === 34 ) {
this.Reveal.next(); this.Reveal.next({skipFragments: event.altKey});
} }
// H, LEFT // H, LEFT
else if( keyCode === 72 || keyCode === 37 ) { else if( keyCode === 72 || keyCode === 37 ) {
if( firstSlideShortcut ) { if( event.shiftKey ) {
this.Reveal.slide( 0 ); this.Reveal.slide( 0 );
} }
else if( !this.Reveal.overview.isActive() && useLinearMode ) { else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.prev(); this.Reveal.prev({skipFragments: event.altKey});
} }
else { else {
this.Reveal.left(); this.Reveal.left({skipFragments: event.altKey});
} }
} }
// L, RIGHT // L, RIGHT
else if( keyCode === 76 || keyCode === 39 ) { else if( keyCode === 76 || keyCode === 39 ) {
if( lastSlideShortcut ) { if( event.shiftKey ) {
this.Reveal.slide( Number.MAX_VALUE ); this.Reveal.slide( Number.MAX_VALUE );
} }
else if( !this.Reveal.overview.isActive() && useLinearMode ) { else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.next(); this.Reveal.next({skipFragments: event.altKey});
} }
else { else {
this.Reveal.right(); this.Reveal.right({skipFragments: event.altKey});
} }
} }
// K, UP // K, UP
else if( keyCode === 75 || keyCode === 38 ) { else if( keyCode === 75 || keyCode === 38 ) {
if( !this.Reveal.overview.isActive() && useLinearMode ) { if( event.shiftKey ) {
this.Reveal.prev(); this.Reveal.slide( undefined, 0 );
}
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.prev({skipFragments: event.altKey});
} }
else { else {
this.Reveal.up(); this.Reveal.up({skipFragments: event.altKey});
} }
} }
// J, DOWN // J, DOWN
else if( keyCode === 74 || keyCode === 40 ) { else if( keyCode === 74 || keyCode === 40 ) {
if( !this.Reveal.overview.isActive() && useLinearMode ) { if( event.shiftKey ) {
this.Reveal.next(); this.Reveal.slide( undefined, Number.MAX_VALUE );
}
else if( !this.Reveal.overview.isActive() && useLinearMode ) {
this.Reveal.next({skipFragments: event.altKey});
} }
else { else {
this.Reveal.down(); this.Reveal.down({skipFragments: event.altKey});
} }
} }
// HOME // HOME
@ -339,10 +343,10 @@ export default class Keyboard {
this.Reveal.overview.deactivate(); this.Reveal.overview.deactivate();
} }
if( event.shiftKey ) { if( event.shiftKey ) {
this.Reveal.prev(); this.Reveal.prev({skipFragments: event.altKey});
} }
else { else {
this.Reveal.next(); this.Reveal.next({skipFragments: event.altKey});
} }
} }
// TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON // TWO-SPOT, SEMICOLON, B, V, PERIOD, LOGITECH PRESENTER TOOLS "BLACK SCREEN" BUTTON

View File

@ -2197,55 +2197,55 @@ export default function( revealElement, options ) {
} }
function navigateLeft() { function navigateLeft({skipFragments=false}={}) {
navigationHistory.hasNavigatedHorizontally = true; navigationHistory.hasNavigatedHorizontally = true;
// Reverse for RTL // Reverse for RTL
if( config.rtl ) { if( config.rtl ) {
if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().left ) { if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().left ) {
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
} }
} }
// Normal navigation // Normal navigation
else if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().left ) { else if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().left ) {
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
} }
} }
function navigateRight() { function navigateRight({skipFragments=false}={}) {
navigationHistory.hasNavigatedHorizontally = true; navigationHistory.hasNavigatedHorizontally = true;
// Reverse for RTL // Reverse for RTL
if( config.rtl ) { if( config.rtl ) {
if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().right ) { if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().right ) {
slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined ); slide( indexh - 1, config.navigationMode === 'grid' ? indexv : undefined );
} }
} }
// Normal navigation // Normal navigation
else if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().right ) { else if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().right ) {
slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined ); slide( indexh + 1, config.navigationMode === 'grid' ? indexv : undefined );
} }
} }
function navigateUp() { function navigateUp({skipFragments=false}={}) {
// Prioritize hiding fragments // Prioritize hiding fragments
if( ( overview.isActive() || fragments.prev() === false ) && availableRoutes().up ) { if( ( overview.isActive() || skipFragments || fragments.prev() === false ) && availableRoutes().up ) {
slide( indexh, indexv - 1 ); slide( indexh, indexv - 1 );
} }
} }
function navigateDown() { function navigateDown({skipFragments=false}={}) {
navigationHistory.hasNavigatedVertically = true; navigationHistory.hasNavigatedVertically = true;
// Prioritize revealing fragments // Prioritize revealing fragments
if( ( overview.isActive() || fragments.next() === false ) && availableRoutes().down ) { if( ( overview.isActive() || skipFragments || fragments.next() === false ) && availableRoutes().down ) {
slide( indexh, indexv + 1 ); slide( indexh, indexv + 1 );
} }
@ -2257,12 +2257,12 @@ export default function( revealElement, options ) {
* 2) Previous vertical slide * 2) Previous vertical slide
* 3) Previous horizontal slide * 3) Previous horizontal slide
*/ */
function navigatePrev() { function navigatePrev({skipFragments=false}={}) {
// Prioritize revealing fragments // Prioritize revealing fragments
if( fragments.prev() === false ) { if( skipFragments || fragments.prev() === false ) {
if( availableRoutes().up ) { if( availableRoutes().up ) {
navigateUp(); navigateUp({skipFragments});
} }
else { else {
// Fetch the previous horizontal slide, if there is one // Fetch the previous horizontal slide, if there is one
@ -2288,13 +2288,13 @@ export default function( revealElement, options ) {
/** /**
* The reverse of #navigatePrev(). * The reverse of #navigatePrev().
*/ */
function navigateNext() { function navigateNext({skipFragments=false}={}) {
navigationHistory.hasNavigatedHorizontally = true; navigationHistory.hasNavigatedHorizontally = true;
navigationHistory.hasNavigatedVertically = true; navigationHistory.hasNavigatedVertically = true;
// Prioritize revealing fragments // Prioritize revealing fragments
if( fragments.next() === false ) { if( skipFragments || fragments.next() === false ) {
let routes = availableRoutes(); let routes = availableRoutes();
@ -2306,13 +2306,13 @@ export default function( revealElement, options ) {
} }
if( routes.down ) { if( routes.down ) {
navigateDown(); navigateDown({skipFragments});
} }
else if( config.rtl ) { else if( config.rtl ) {
navigateLeft(); navigateLeft({skipFragments});
} }
else { else {
navigateRight(); navigateRight({skipFragments});
} }
} }

View File

@ -97,6 +97,11 @@
// 4 // 4
Reveal.initialize().then( function() { Reveal.initialize().then( function() {
// Helper methods
function triggerKeyboardEvent(config) {
document.dispatchEvent( new KeyboardEvent( 'keydown', config ) );
}
// --------------------------------------------------------------- // ---------------------------------------------------------------
// DOM TESTS // DOM TESTS
@ -407,13 +412,41 @@
assert.ok( /X\-SHORTCUT\-X/.test( document.body.innerHTML ), 'binding is added to help overlay' ); assert.ok( /X\-SHORTCUT\-X/.test( document.body.innerHTML ), 'binding is added to help overlay' );
Reveal.toggleHelp( false ); Reveal.toggleHelp( false );
let event = new KeyboardEvent( 'keydown', { 'keyCode':88 } ); triggerKeyboardEvent({ keyCode: 88 });
document.dispatchEvent( event );
Reveal.removeKeyBinding( 88 ); Reveal.removeKeyBinding( 88 );
// should do nothing // should do nothing
document.dispatchEvent( event ); triggerKeyboardEvent({ keyCode: 88 });
});
QUnit.test( 'Navigation bindings', function( assert ) {
Reveal.slide( 0 );
// right arrow
triggerKeyboardEvent({ keyCode: 39 });
assert.strictEqual( Reveal.getIndices().h, 1 );
// down arrow + shift
triggerKeyboardEvent({ keyCode: 40, shiftKey: true });
assert.strictEqual( Reveal.getIndices().v, 2, 'shift + down arrow goes to last vertical slide' );
// up arrow
triggerKeyboardEvent({ keyCode: 38 });
assert.strictEqual( Reveal.getIndices().v, 1 );
// right arrow + shift
triggerKeyboardEvent({ keyCode: 39, shiftKey: true });
assert.ok( Reveal.isLastSlide(), 'shift + right arrow goes to last horizontal slide' );
// right arrow on slide with fragments
Reveal.slide( 2, 0, -1 );
triggerKeyboardEvent({ keyCode: 39 });
assert.deepEqual( Reveal.getIndices(), { h: 2, v: 0, f: 0 }, 'right arrow shows fragment' );
triggerKeyboardEvent({ keyCode: 39, altKey: true });
assert.strictEqual( Reveal.getIndices().h, 3, 'right arrow skips fragments when alt key is pressed' );
}); });
// --------------------------------------------------------------- // ---------------------------------------------------------------