-
Notifications
You must be signed in to change notification settings - Fork 109
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add copy & expand buttons to syntax-highlighted code blocks (#2652)
* Extend the core code block to add copy & expand buttons * Remove unused variable
- Loading branch information
Showing
5 changed files
with
226 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,5 @@ | ||
{ | ||
"name": "wporg/code", | ||
"style": "file:./style-view.css", | ||
"viewScript": "file:./view.js" | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
<?php | ||
/** | ||
* Extends the core code block to add copy & expand buttons. | ||
*/ | ||
|
||
namespace WordPressdotorg\Theme\Learn_2024\Code_Block; | ||
|
||
defined( 'WPINC' ) || die(); | ||
|
||
/** | ||
* Actions and filters. | ||
*/ | ||
add_action( 'init', __NAMESPACE__ . '\init' ); | ||
|
||
/** | ||
* Add the scripts & styles to update the code block. | ||
* | ||
* The dependencies are autogenerated in block.json, and can be read with | ||
* `wp_json_file_decode` & `register_block_script_handle. | ||
*/ | ||
function init() { | ||
$metadata_file = dirname( dirname( __DIR__ ) ) . '/build/code/block.json'; | ||
$metadata = wp_json_file_decode( $metadata_file, array( 'associative' => true ) ); | ||
$metadata['file'] = $metadata_file; | ||
|
||
$style_handle = register_block_style_handle( $metadata, 'style', 0 ); | ||
|
||
$script_handle = register_block_script_handle( $metadata, 'viewScript', 0 ); | ||
wp_localize_script( | ||
$script_handle, | ||
'wporgCodeI18n', | ||
array( | ||
'copy' => __( 'Copy', 'wporg-learn' ), | ||
'copied' => __( 'Code copied', 'wporg-learn' ), | ||
'expand' => __( 'Expand code', 'wporg-learn' ), | ||
'collapse' => __( 'Collapse code', 'wporg-learn' ), | ||
) | ||
); | ||
|
||
// Enqueue the assets only when the code block is on the page. | ||
add_action( | ||
'render_block_core/code', | ||
function( $block_content ) use ( $script_handle, $style_handle ) { | ||
wp_enqueue_script( $script_handle ); | ||
wp_enqueue_style( $style_handle ); | ||
return $block_content; | ||
} | ||
); | ||
} |
49 changes: 49 additions & 0 deletions
49
wp-content/themes/pub/wporg-learn-2024/src/code/style.scss
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,49 @@ | ||
.wporg-code-block { | ||
$border_radius: 2px; | ||
|
||
.wp-code-block-button-container { | ||
padding: var(--wp--preset--spacing--10); | ||
background-color: var(--wp--preset--color--light-grey-2); | ||
border-radius: $border_radius $border_radius 0 0; | ||
border-width: 1px 1px 0; | ||
border-style: solid; | ||
border-color: var(--wp--preset--color--light-grey-1); | ||
|
||
.wp-block-buttons { | ||
justify-content: flex-end; | ||
} | ||
|
||
.wp-block-button button { | ||
padding-top: var(--wp--custom--button--spacing--padding--top) !important; | ||
padding-bottom: var(--wp--custom--button--spacing--padding--bottom) !important; | ||
padding-left: var(--wp--custom--button--spacing--padding--left) !important; | ||
padding-right: var(--wp--custom--button--spacing--padding--right) !important; | ||
background-color: var(--wp--preset--color--white); | ||
text-wrap: nowrap; | ||
font-size: var(--wp--custom--button--typography--font-size); | ||
transition: none; | ||
} | ||
} | ||
|
||
.wp-code-block-button-container + pre { | ||
margin-top: 0; | ||
background-color: var(--wp--preset--color--white); | ||
border-width: 0 1px 1px; | ||
border-style: solid; | ||
border-color: var(--wp--preset--color--light-grey-1); | ||
} | ||
|
||
.wp-block-code { | ||
border-radius: 0 0 $border_radius $border_radius; | ||
|
||
code { | ||
font-family: var(--wp--preset--font-family--monospace); | ||
background-color: unset; | ||
} | ||
} | ||
|
||
.line-numbers-rows > span::before { | ||
text-align: left; | ||
padding-inline-start: var(--wp--preset--spacing--10); | ||
} | ||
} |
122 changes: 122 additions & 0 deletions
122
wp-content/themes/pub/wporg-learn-2024/src/code/view.js
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,122 @@ | ||
/* global wporgCodeI18n */ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { speak } from '@wordpress/a11y'; | ||
|
||
/** | ||
* Internal dependencies | ||
*/ | ||
import './style.scss'; | ||
|
||
// Index for | ||
let _instanceID = 0; | ||
|
||
function init() { | ||
// 27px (line height) * 12.7 for 12 lines + 18px top padding. | ||
// The extra partial line is to show that there is more content. | ||
const MIN_HEIGHT = 27 * 12.7 + 18; | ||
|
||
function collapseCodeBlock( element, button ) { | ||
button.innerText = wporgCodeI18n.expand; | ||
button.setAttribute( 'aria-expanded', 'false' ); | ||
element.style.height = MIN_HEIGHT + 'px'; | ||
} | ||
|
||
function expandCodeBlock( element, button ) { | ||
button.innerText = wporgCodeI18n.collapse; | ||
button.setAttribute( 'aria-expanded', 'true' ); | ||
// Add 5px to ensure the vertical scrollbar is not displayed. | ||
const height = parseInt( element.dataset.height, 10 ) + 5; | ||
element.style.height = height + 'px'; | ||
} | ||
|
||
// Run over all code blocks that use the syntax highlighter. | ||
const codeBlocks = document.querySelectorAll( '.wp-block-code[class*=language]' ); | ||
|
||
codeBlocks.forEach( function ( element ) { | ||
let timeoutId; | ||
|
||
// Create a unique ID for the `pre` element, which can be used for aria later. | ||
const instanceId = 'wporg-source-code-' + _instanceID++; | ||
element.id = instanceId; | ||
|
||
// Create the top-level container. This will contain the buttons & sits above the `pre`. | ||
const container = document.createElement( 'div' ); | ||
container.classList.add( 'wp-code-block-button-container' ); | ||
|
||
const buttonContainer = document.createElement( 'div' ); | ||
buttonContainer.classList.add( 'wp-block-buttons' ); | ||
|
||
const copyButtonBlock = document.createElement( 'div' ); | ||
copyButtonBlock.classList.add( 'wp-block-button', 'is-style-outline', 'is-small' ); | ||
|
||
const copyButton = document.createElement( 'button' ); | ||
copyButton.classList.add( 'wp-block-button__link', 'wp-element-button' ); | ||
copyButton.innerText = wporgCodeI18n.copy; | ||
|
||
copyButton.addEventListener( 'click', function ( event ) { | ||
event.preventDefault(); | ||
clearTimeout( timeoutId ); | ||
const code = element.querySelector( 'code' ).innerText; | ||
if ( ! code ) { | ||
return; | ||
} | ||
|
||
// This returns a promise which will resolve if the copy suceeded, | ||
// and we can set the button text to tell the user it worked. | ||
// We don't do anything if it fails. | ||
window.navigator.clipboard.writeText( code ).then( function () { | ||
copyButton.innerText = wporgCodeI18n.copied; | ||
speak( wporgCodeI18n.copied ); | ||
|
||
// After 5 seconds, reset the button text. | ||
timeoutId = setTimeout( function () { | ||
copyButton.innerText = wporgCodeI18n.copy; | ||
}, 5000 ); | ||
} ); | ||
} ); | ||
|
||
copyButtonBlock.append( copyButton ); | ||
buttonContainer.append( copyButtonBlock ); | ||
|
||
// Check code block height. If it's too tall, add in the collapse button, | ||
// and shrink down the `pre` to MIN_HEIGHT. | ||
const originalHeight = element.clientHeight; | ||
if ( originalHeight > MIN_HEIGHT ) { | ||
element.dataset.height = originalHeight; | ||
|
||
const expandButtonBlock = document.createElement( 'div' ); | ||
expandButtonBlock.classList.add( 'wp-block-button', 'is-style-outline', 'is-small' ); | ||
|
||
const expandButton = document.createElement( 'button' ); | ||
expandButton.classList.add( 'wp-block-button__link', 'wp-element-button' ); | ||
expandButton.setAttribute( 'aria-controls', instanceId ); | ||
expandButton.innerText = wporgCodeI18n.expand; | ||
|
||
expandButton.addEventListener( 'click', function ( event ) { | ||
event.preventDefault(); | ||
if ( 'true' === expandButton.getAttribute( 'aria-expanded' ) ) { | ||
collapseCodeBlock( element, expandButton ); | ||
} else { | ||
expandCodeBlock( element, expandButton ); | ||
} | ||
} ); | ||
|
||
collapseCodeBlock( element, expandButton ); | ||
|
||
expandButtonBlock.append( expandButton ); | ||
buttonContainer.append( expandButtonBlock ); | ||
} | ||
|
||
container.append( buttonContainer ); | ||
|
||
const wrapper = document.createElement( 'div' ); | ||
wrapper.classList.add( 'wporg-code-block' ); | ||
|
||
element.replaceWith( wrapper ); | ||
wrapper.append( container, element ); | ||
} ); | ||
} | ||
|
||
document.addEventListener( 'DOMContentLoaded', init ); |