Skip to content
GitLab
Menu
Projects
Groups
Snippets
/
Help
Help
Support
Community forum
Keyboard shortcuts
?
Submit feedback
Sign in
Toggle navigation
Menu
Open sidebar
Andrew Seales
Kubernetes Presentation
Commits
bff9bfb1
Commit
bff9bfb1
authored
Mar 12, 2020
by
Hakim El Hattab
Browse files
add support for automatically scrolling code highlights into view
parent
5a5a5c9a
Changes
7
Hide whitespace changes
Inline
Side-by-side
css/reveal.scss
View file @
bff9bfb1
...
...
@@ -39,6 +39,7 @@ body {
opacity
:
0
;
visibility
:
hidden
;
transition
:
all
.2s
ease
;
will-change
:
opacity
;
&
.visible
{
opacity
:
1
;
...
...
@@ -1599,6 +1600,10 @@ $overlayHeaderPadding: 5px;
* CODE HIGHLGIHTING
*********************************************/
.reveal
.hljs
{
min-height
:
100%
;
}
.reveal
.hljs
table
{
margin
:
initial
;
}
...
...
demo.html
View file @
bff9bfb1
...
...
@@ -102,7 +102,7 @@
<section
data-auto-animate
>
<h2
data-id=
"code-title"
>
With animations
</h2>
<pre
data-id=
"code-animation"
><code
class=
"hljs"
data-trim
data-line-numbers=
"|4
|4
,8-11"
>
<pre
data-id=
"code-animation"
><code
class=
"hljs"
data-trim
data-line-numbers=
"|4,8-11
|17|22-24
"
>
import React, { useState } from 'react';
function Example() {
...
...
@@ -117,6 +117,19 @@
<
/div
>
);
}
function SecondExample() {
const [count, setCount] = useState(0);
return (
<
div
>
<
p
>
You clicked {count} times
<
/p
>
<
button onClick={() =
>
setCount(count + 1)}
>
Click me
<
/button
>
<
/div
>
);
}
</code></pre>
</section>
...
...
js/controllers/autoanimate.js
View file @
bff9bfb1
...
...
@@ -67,7 +67,14 @@ export default class AutoAnimate {
}
}
);
this
.
Reveal
.
dispatchEvent
(
'
autoanimate
'
,
{
fromSlide
:
fromSlide
,
toSlide
:
toSlide
,
sheet
:
this
.
autoAnimateStyleSheet
}
);
this
.
Reveal
.
dispatchEvent
({
type
:
'
autoanimate
'
,
data
:
{
fromSlide
,
toSlide
,
sheet
:
this
.
autoAnimateStyleSheet
}
});
}
...
...
js/controllers/fragments.js
View file @
bff9bfb1
...
...
@@ -180,7 +180,7 @@ export default class Fragments {
// Visible fragments
if
(
i
<=
index
)
{
if
(
!
el
.
classList
.
contains
(
'
visible
'
)
)
changedFragments
.
shown
.
push
(
el
);
let
wasVisible
=
el
.
classList
.
contains
(
'
visible
'
)
el
.
classList
.
add
(
'
visible
'
);
el
.
classList
.
remove
(
'
current-fragment
'
);
...
...
@@ -191,12 +191,30 @@ export default class Fragments {
el
.
classList
.
add
(
'
current-fragment
'
);
this
.
Reveal
.
slideContent
.
startEmbeddedContent
(
el
);
}
if
(
!
wasVisible
)
{
changedFragments
.
shown
.
push
(
el
)
this
.
Reveal
.
dispatchEvent
({
target
:
el
,
type
:
'
visible
'
,
bubbles
:
false
});
}
}
// Hidden fragments
else
{
if
(
el
.
classList
.
contains
(
'
visible
'
)
)
changedFragments
.
hidden
.
push
(
el
);
let
wasVisible
=
el
.
classList
.
contains
(
'
visible
'
)
el
.
classList
.
remove
(
'
visible
'
);
el
.
classList
.
remove
(
'
current-fragment
'
);
if
(
wasVisible
)
{
changedFragments
.
hidden
.
push
(
el
);
this
.
Reveal
.
dispatchEvent
({
target
:
el
,
type
:
'
hidden
'
,
bubbles
:
false
});
}
}
}
);
...
...
@@ -253,11 +271,23 @@ export default class Fragments {
let
changedFragments
=
this
.
update
(
index
,
fragments
);
if
(
changedFragments
.
hidden
.
length
)
{
this
.
Reveal
.
dispatchEvent
(
'
fragmenthidden
'
,
{
fragment
:
changedFragments
.
hidden
[
0
],
fragments
:
changedFragments
.
hidden
}
);
this
.
Reveal
.
dispatchEvent
({
type
:
'
fragmenthidden
'
,
data
:
{
fragment
:
changedFragments
.
hidden
[
0
],
fragments
:
changedFragments
.
hidden
}
});
}
if
(
changedFragments
.
shown
.
length
)
{
this
.
Reveal
.
dispatchEvent
(
'
fragmentshown
'
,
{
fragment
:
changedFragments
.
shown
[
0
],
fragments
:
changedFragments
.
shown
}
);
this
.
Reveal
.
dispatchEvent
({
type
:
'
fragmentshown
'
,
data
:
{
fragment
:
changedFragments
.
shown
[
0
],
fragments
:
changedFragments
.
shown
}
});
}
this
.
Reveal
.
updateControls
();
...
...
js/controllers/overview.js
View file @
bff9bfb1
...
...
@@ -65,11 +65,14 @@ export default class Overview {
const
indices
=
this
.
Reveal
.
getIndices
();
// Notify observers of the overview showing
this
.
Reveal
.
dispatchEvent
(
'
overviewshown
'
,
{
'
indexh
'
:
indices
.
h
,
'
indexv
'
:
indices
.
v
,
'
currentSlide
'
:
this
.
Reveal
.
getCurrentSlide
()
}
);
this
.
Reveal
.
dispatchEvent
({
type
:
'
overviewshown
'
,
data
:
{
'
indexh
'
:
indices
.
h
,
'
indexv
'
:
indices
.
v
,
'
currentSlide
'
:
this
.
Reveal
.
getCurrentSlide
()
}
});
}
...
...
@@ -175,11 +178,14 @@ export default class Overview {
this
.
Reveal
.
cueAutoSlide
();
// Notify observers of the overview hiding
this
.
Reveal
.
dispatchEvent
(
'
overviewhidden
'
,
{
'
indexh
'
:
indices
.
h
,
'
indexv
'
:
indices
.
v
,
'
currentSlide
'
:
this
.
Reveal
.
getCurrentSlide
()
}
);
this
.
Reveal
.
dispatchEvent
({
type
:
'
overviewhidden
'
,
data
:
{
'
indexh
'
:
indices
.
h
,
'
indexv
'
:
indices
.
v
,
'
currentSlide
'
:
this
.
Reveal
.
getCurrentSlide
()
}
});
}
}
...
...
js/reveal.js
View file @
bff9bfb1
...
...
@@ -194,11 +194,14 @@ export default function( revealElement, options ) {
dom
.
wrapper
.
classList
.
add
(
'
ready
'
);
dispatchEvent
(
'
ready
'
,
{
'
indexh
'
:
indexh
,
'
indexv
'
:
indexv
,
'
currentSlide
'
:
currentSlide
}
);
dispatchEvent
({
type
:
'
ready
'
,
data
:
{
indexh
,
indexv
,
currentSlide
}
});
},
1
);
// Special setup and config is required when printing to PDF
...
...
@@ -511,7 +514,7 @@ export default function( revealElement, options ) {
}
);
// Notify subscribers that the PDF layout is good to go
dispatchEvent
(
'
pdf-ready
'
);
dispatchEvent
(
{
type
:
'
pdf-ready
'
}
);
}
...
...
@@ -1058,16 +1061,18 @@ export default function( revealElement, options ) {
* Dispatches an event of the specified type from the
* reveal DOM element.
*/
function
dispatchEvent
(
type
,
args
)
{
function
dispatchEvent
(
{
target
=
dom
.
wrapper
,
type
,
data
,
bubbles
=
true
}
)
{
let
event
=
document
.
createEvent
(
'
HTMLEvents
'
,
1
,
2
);
event
.
initEvent
(
type
,
true
,
true
);
extend
(
event
,
args
);
dom
.
wrapper
.
dispatchEvent
(
event
);
event
.
initEvent
(
type
,
bubbles
,
true
);
extend
(
event
,
data
);
target
.
dispatchEvent
(
event
);
// If we're in an iframe, post each reveal.js event to the
// parent window. Used by the notes plugin
dispatchPostMessage
(
type
);
if
(
target
===
dom
.
wrapper
)
{
// If we're in an iframe, post each reveal.js event to the
// parent window. Used by the notes plugin
dispatchPostMessage
(
type
);
}
}
...
...
@@ -1347,11 +1352,14 @@ export default function( revealElement, options ) {
}
if
(
oldScale
!==
scale
)
{
dispatchEvent
(
'
resize
'
,
{
'
oldScale
'
:
oldScale
,
'
scale
'
:
scale
,
'
size
'
:
size
}
);
dispatchEvent
({
type
:
'
resize
'
,
data
:
{
oldScale
,
scale
,
size
}
});
}
}
...
...
@@ -1577,7 +1585,7 @@ export default function( revealElement, options ) {
dom
.
wrapper
.
classList
.
add
(
'
paused
'
);
if
(
wasPaused
===
false
)
{
dispatchEvent
(
'
paused
'
);
dispatchEvent
(
{
type
:
'
paused
'
}
);
}
}
...
...
@@ -1594,7 +1602,7 @@ export default function( revealElement, options ) {
cueAutoSlide
();
if
(
wasPaused
)
{
dispatchEvent
(
'
resumed
'
);
dispatchEvent
(
{
type
:
'
resumed
'
}
);
}
}
...
...
@@ -1763,7 +1771,7 @@ export default function( revealElement, options ) {
document
.
documentElement
.
classList
.
add
(
state
[
i
]
);
// Dispatch custom event matching the state's name
dispatchEvent
(
state
[
i
]
);
dispatchEvent
(
{
type
:
state
[
i
]
}
);
}
// Clean up the remains of the previous state
...
...
@@ -1772,13 +1780,16 @@ export default function( revealElement, options ) {
}
if
(
slideChanged
)
{
dispatchEvent
(
'
slidechanged
'
,
{
'
indexh
'
:
indexh
,
'
indexv
'
:
indexv
,
'
previousSlide
'
:
previousSlide
,
'
currentSlide
'
:
currentSlide
,
'
origin
'
:
o
}
);
dispatchEvent
({
type
:
'
slidechanged
'
,
data
:
{
indexh
,
indexv
,
previousSlide
,
currentSlide
,
origin
:
o
}
});
}
// Handle embedded content
...
...
@@ -2035,14 +2046,26 @@ export default function( revealElement, options ) {
}
}
let
slide
=
slides
[
index
];
let
wasPresent
=
slide
.
classList
.
contains
(
'
present
'
);
// Mark the current slide as present
slides
[
index
].
classList
.
add
(
'
present
'
);
slides
[
index
].
removeAttribute
(
'
hidden
'
);
slides
[
index
].
removeAttribute
(
'
aria-hidden
'
);
slide
.
classList
.
add
(
'
present
'
);
slide
.
removeAttribute
(
'
hidden
'
);
slide
.
removeAttribute
(
'
aria-hidden
'
);
if
(
!
wasPresent
)
{
// Dispatch an event indicating the slide is now visible
dispatchEvent
({
target
:
slide
,
type
:
'
visible
'
,
bubbles
:
false
});
}
// If this slide has a state associated with it, add it
// onto the current state of the deck
let
slideState
=
slide
s
[
index
]
.
getAttribute
(
'
data-state
'
);
let
slideState
=
slide
.
getAttribute
(
'
data-state
'
);
if
(
slideState
)
{
state
=
state
.
concat
(
slideState
.
split
(
'
'
)
);
}
...
...
@@ -2947,7 +2970,7 @@ export default function( revealElement, options ) {
if
(
autoSlide
&&
!
autoSlidePaused
)
{
autoSlidePaused
=
true
;
dispatchEvent
(
'
autoslidepaused
'
);
dispatchEvent
(
{
type
:
'
autoslidepaused
'
}
);
clearTimeout
(
autoSlideTimeout
);
if
(
autoSlidePlayer
)
{
...
...
@@ -2961,7 +2984,7 @@ export default function( revealElement, options ) {
if
(
autoSlide
&&
autoSlidePaused
)
{
autoSlidePaused
=
false
;
dispatchEvent
(
'
autoslideresumed
'
);
dispatchEvent
(
{
type
:
'
autoslideresumed
'
}
);
cueAutoSlide
();
}
...
...
plugin/highlight/highlight.js
View file @
bff9bfb1
...
...
@@ -100,6 +100,15 @@
if( config.highlightOnLoad ) {
RevealHighlight.highlightBlock( block );
}
} );
// If we're printing to PDF, scroll the code highlights of
// all blocks in the deck into view at once
Reveal.addEventListener( 'pdf-ready', function() {
[].slice.call( document.querySelectorAll( '.reveal pre code[data-line-numbers].current-fragment' ) ).forEach( function( block ) {
RevealHighlight.scrollHighlightedLineIntoView( block, {}, true );
} );
} );
},
...
...
@@ -122,6 +131,8 @@
if( block.hasAttribute( 'data-line-numbers' ) ) {
hljs.lineNumbersBlock( block, { singleLine: true } );
var scrollState = { currentBlock: block };
// If there is at least one highlight step, generate
// fragments
var highlightSteps = RevealHighlight.deserializeHighlightSteps( block.getAttribute( 'data-line-numbers' ) );
...
...
@@ -130,6 +141,7 @@
// If the original code block has a fragment-index,
// each clone should follow in an incremental sequence
var fragmentIndex = parseInt( block.getAttribute( 'data-fragment-index' ), 10 );
if( typeof fragmentIndex !== 'number' || isNaN( fragmentIndex ) ) {
fragmentIndex = null;
}
...
...
@@ -151,6 +163,10 @@
fragmentBlock.removeAttribute( 'data-fragment-index' );
}
// Scroll highlights into view as we step through them
fragmentBlock.addEventListener( 'visible', RevealHighlight.scrollHighlightedLineIntoView.bind( RevealHighlight, fragmentBlock, scrollState ) );
fragmentBlock.addEventListener( 'hidden', RevealHighlight.scrollHighlightedLineIntoView.bind( RevealHighlight, fragmentBlock.previousSibling, scrollState ) );
} );
block.removeAttribute( 'data-fragment-index' )
...
...
@@ -158,12 +174,116 @@
}
// Scroll the first highlight into view when the slide
// becomes visible. Note supported in IE11 since it lacks
// support for Element.closest.
var slide = typeof block.closest === 'function' ? block.closest( 'section:not(.stack)' ) : null;
if( slide ) {
var scrollFirstHighlightIntoView = function() {
RevealHighlight.scrollHighlightedLineIntoView( block, scrollState, true );
slide.removeEventListener( 'visible', scrollFirstHighlightIntoView );
}
slide.addEventListener( 'visible', scrollFirstHighlightIntoView );
}
RevealHighlight.highlightLines( block );
}
},
/**
* Animates scrolling to the first highlighted line
* in the given code block.
*/
scrollHighlightedLineIntoView: function( block, scrollState, skipAnimation ) {
cancelAnimationFrame( scrollState.animationFrameID );
// Match the scroll position of the currently visible
// code block
if( scrollState.currentBlock ) {
block.scrollTop = scrollState.currentBlock.scrollTop;
}
// Remember the current code block so that we can match
// its scroll position when showing/hiding fragments
scrollState.currentBlock = block;
var highlightBounds = this.getHighlightedLineBounds( block )
var viewportHeight = block.offsetHeight;
// Subtract padding from the viewport height
var blockStyles = getComputedStyle( block );
viewportHeight -= parseInt( blockStyles.paddingTop ) + parseInt( blockStyles.paddingBottom );
// Scroll position which centers all highlights
var startTop = block.scrollTop;
var targetTop = highlightBounds.top + ( Math.min( highlightBounds.bottom - highlightBounds.top, viewportHeight ) - viewportHeight ) / 2;
// Account for offsets in position applied to the
// <table> that holds our lines of code
var lineTable = block.querySelector( '.hljs-ln' );
if( lineTable ) targetTop += lineTable.offsetTop - parseInt( blockStyles.paddingTop );
// Make sure the scroll target is within bounds
targetTop = Math.max( Math.min( targetTop, block.scrollHeight - viewportHeight ), 0 );
if( skipAnimation === true || startTop === targetTop ) {
block.scrollTop = targetTop;
}
else {
// Don't attempt to scroll if there is no overflow
if( block.scrollHeight <= viewportHeight ) return;
var time = 0;
var animate = function() {
time = Math.min( time + 0.02, 1 );
// Update our eased scroll position
block.scrollTop = startTop + ( targetTop - startTop ) * RevealHighlight.easeInOutQuart( time );
// Keep animating unless we've reached the end
if( time < 1 ) {
scrollState.animationFrameID = requestAnimationFrame( animate );
}
};
animate();
}
},
/**
* The easing function used when scrolling.
*/
easeInOutQuart: function( t ) {
// easeInOutQuart
return t<.5 ? 8*t*t*t*t : 1-8*(--t)*t*t*t;
},
getHighlightedLineBounds: function( block ) {
var highlightedLines = block.querySelectorAll( '.highlight-line' );
if( highlightedLines.length === 0 ) {
return { top: 0, bottom: 0 };
}
else {
var firstHighlight = highlightedLines[0];
var lastHighlight = highlightedLines[ highlightedLines.length -1 ];
return {
top: firstHighlight.offsetTop,
bottom: lastHighlight.offsetTop + lastHighlight.offsetHeight
}
}
},
/**
* Visually emphasize specific lines within a code block.
* This only works on blocks with line numbering turned on.
...
...
Write
Preview
Supports
Markdown
0%
Try again
or
attach a new file
.
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment