mirrored from git://develop.git.wordpress.org/
-
Notifications
You must be signed in to change notification settings - Fork 2.7k
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Replace Twenty Twenty-One custom JS frontend code with Interactivity API #5795
Draft
felixarntz
wants to merge
8
commits into
WordPress:trunk
Choose a base branch
from
felixarntz:experiment/tt1-interactivity-api
base: trunk
Could not load branches
Branch not found: {{ refName }}
Loading
Could not load tags
Nothing to show
Loading
Are you sure you want to change the base?
Some commits from the old base branch may be removed from the timeline,
and old review comments may become outdated.
Draft
Changes from all commits
Commits
Show all changes
8 commits
Select commit
Hold shift + click to select a range
3b84e7d
Replace TT1 custom JS frontend code with Interactivity API (if availa…
felixarntz 3e603d7
Merge branch 'trunk' into experiment/tt1-interactivity-api
felixarntz 50adcfa
Rely on WP 6.5 functions instead of Gutenberg plugin.
felixarntz 44e4977
Use new data-wp-on-window and data-wp-on-document.
felixarntz 0d749e3
Merge branch 'trunk' into experiment/tt1-interactivity-api
felixarntz b3af9e0
Use a watch callback to update the isDarkMode local storage cache.
felixarntz 5ccb56a
Merge branch 'trunk' into experiment/tt1-interactivity-api
felixarntz 4a35f3f
Fix local storage to only store dark mode value if manually overwritten.
felixarntz File filter
Filter by extension
Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
There are no files selected for viewing
208 changes: 208 additions & 0 deletions
208
src/wp-content/themes/twentytwentyone/assets/js/interactivity.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,208 @@ | ||
/** | ||
* WordPress dependencies | ||
*/ | ||
import { store, getContext, getElement } from '@wordpress/interactivity'; | ||
|
||
function checkClass( element, className ) { | ||
if ( element.classList.contains( className ) ) { | ||
return element; | ||
} | ||
if ( element.parentElement && element.parentElement.classList.contains( className ) ) { | ||
return element.parentElement; | ||
} | ||
if ( element.parentElement.parentElement && element.parentElement.parentElement.classList.contains( className ) ) { | ||
return element.parentElement.parentElement; | ||
} | ||
return null; | ||
} | ||
|
||
const { state, actions } = store( 'twentytwentyone', { | ||
state: { | ||
isPrimaryMenuOpen: false, | ||
prevScroll: 0, | ||
isDarkMode: false, | ||
isDarkModeManuallyOverwritten: false, | ||
isDarkModeTogglerHidden: false, | ||
}, | ||
actions: { | ||
togglePrimaryMenu: () => { | ||
state.isPrimaryMenuOpen = ! state.isPrimaryMenuOpen; | ||
}, | ||
|
||
openPrimaryMenu: () => { | ||
state.isPrimaryMenuOpen = true; | ||
}, | ||
|
||
closePrimaryMenu: () => { | ||
state.isPrimaryMenuOpen = false; | ||
}, | ||
|
||
toggleDarkMode: () => { | ||
state.isDarkMode = ! state.isDarkMode; | ||
state.isDarkModeManuallyOverwritten = true; | ||
}, | ||
|
||
trapFocusInModal: ( event ) => { | ||
if ( ! state.isPrimaryMenuOpen ) { | ||
return; | ||
} | ||
|
||
const ctx = getContext(); | ||
|
||
const escKey = event.keyCode === 27; | ||
if ( escKey ) { | ||
event.preventDefault(); | ||
actions.closePrimaryMenu(); | ||
if ( ctx.firstFocusable ) { | ||
ctx.firstFocusable.focus(); | ||
} | ||
return; | ||
} | ||
|
||
const tabKey = event.keyCode === 9; | ||
const shiftKey = event.shiftKey; | ||
const activeEl = document.activeElement; // eslint-disable-line @wordpress/no-global-active-element | ||
|
||
if ( ! shiftKey && tabKey && ctx.lastFocusable === activeEl ) { | ||
event.preventDefault(); | ||
if ( ctx.firstFocusable ) { | ||
ctx.firstFocusable.focus(); | ||
} | ||
return; | ||
} | ||
|
||
if ( shiftKey && tabKey && ctx.firstFocusable === activeEl ) { | ||
event.preventDefault(); | ||
if ( ctx.lastFocusable ) { | ||
ctx.lastFocusable.focus(); | ||
} | ||
return; | ||
} | ||
|
||
// If there are no elements in the menu, don't move the focus | ||
if ( tabKey && ctx.firstFocusable === ctx.lastFocusable ) { | ||
event.preventDefault(); | ||
} | ||
}, | ||
|
||
listenToSpecialClicks: ( event ) => { | ||
const ctx = getContext(); | ||
|
||
// Check if this was a `.sub-menu-toggle` click. | ||
const subMenuToggle = checkClass( event.target, 'sub-menu-toggle' ); | ||
if ( subMenuToggle ) { | ||
if ( ctx.activeSubmenu === subMenuToggle ) { | ||
ctx.activeSubmenu = null; | ||
} else { | ||
ctx.activeSubmenu = subMenuToggle; | ||
} | ||
return; | ||
} | ||
|
||
// Otherwise, check if this was an anchor link click. | ||
if ( ! event.target.hash ) { | ||
return; | ||
} | ||
|
||
actions.closePrimaryMenu(); | ||
|
||
// Wait 550 and scroll to the anchor. | ||
setTimeout( () => { | ||
var anchor = document.getElementById( event.target.hash.slice( 1 ) ); | ||
if ( anchor ) { | ||
anchor.scrollIntoView(); | ||
} | ||
}, 550 ); | ||
}, | ||
}, | ||
callbacks: { | ||
determineFocusableElements: () => { | ||
if ( ! state.isPrimaryMenuOpen ) { | ||
return; | ||
} | ||
|
||
const ctx = getContext(); | ||
const { ref } = getElement(); | ||
const elements = ref.querySelectorAll( 'input, a, button' ); | ||
|
||
ctx.firstFocusable = elements[ 0 ]; | ||
ctx.lastFocusable = elements[ elements.length - 1 ]; | ||
}, | ||
|
||
refreshSubmenus: () => { | ||
const ctx = getContext(); | ||
const { ref } = getElement(); | ||
const elements = ref.querySelectorAll( '.sub-menu-toggle' ); | ||
elements.forEach( ( subMenuToggle ) => { | ||
if ( ctx.activeSubmenu === subMenuToggle ) { | ||
subMenuToggle.setAttribute( 'aria-expanded', 'true' ); | ||
} else { | ||
subMenuToggle.setAttribute( 'aria-expanded', 'false' ); | ||
} | ||
} ); | ||
}, | ||
|
||
makeIframesResponsive: () => { | ||
const { ref } = getElement(); | ||
|
||
ref.querySelectorAll( 'iframe' ).forEach( function( iframe ) { | ||
// Only continue if the iframe has a width & height defined. | ||
if ( iframe.width && iframe.height ) { | ||
// Calculate the proportion/ratio based on the width & height. | ||
proportion = parseFloat( iframe.width ) / parseFloat( iframe.height ); | ||
// Get the parent element's width. | ||
parentWidth = parseFloat( window.getComputedStyle( iframe.parentElement, null ).width.replace( 'px', '' ) ); | ||
// Set the max-width & height. | ||
iframe.style.maxWidth = '100%'; | ||
iframe.style.maxHeight = Math.round( parentWidth / proportion ).toString() + 'px'; | ||
} | ||
} ); | ||
}, | ||
|
||
initDarkMode: () => { | ||
let isDarkMode = window.matchMedia( '(prefers-color-scheme: dark)' ).matches; | ||
let isDarkModeManuallyOverwritten = false; | ||
|
||
const cachedValue = window.localStorage.getItem( 'twentytwentyoneDarkMode' ); | ||
if ( 'yes' === cachedValue ) { | ||
isDarkMode = true; | ||
isDarkModeManuallyOverwritten = true; | ||
} else if ( 'no' === cachedValue ) { | ||
isDarkMode = false; | ||
isDarkModeManuallyOverwritten = true; | ||
} | ||
|
||
state.isDarkMode = isDarkMode; | ||
state.isDarkModeManuallyOverwritten = isDarkModeManuallyOverwritten; | ||
}, | ||
|
||
storeDarkMode: () => { | ||
// Store dark mode preference in local storage only if it was explicitly set via the website toggle. | ||
if ( state.isDarkModeManuallyOverwritten ) { | ||
window.localStorage.setItem( 'twentytwentyoneDarkMode', state.isDarkMode ? 'yes' : 'no' ); | ||
} | ||
}, | ||
|
||
refreshHtmlElementDarkMode: () => { | ||
// This hack may be needed since the HTML element cannot be controlled with the API attributes? | ||
if ( state.isDarkMode ) { | ||
document.documentElement.classList.add( 'is-dark-theme' ); | ||
} else { | ||
document.documentElement.classList.remove( 'is-dark-theme' ); | ||
} | ||
}, | ||
|
||
refreshDarkModeToggler: () => { | ||
const currentScroll = window.scrollY || document.documentElement.scrollTop; | ||
if ( | ||
currentScroll + ( window.innerHeight * 1.5 ) > document.body.clientHeight || | ||
currentScroll < state.prevScroll | ||
) { | ||
state.isDarkModeTogglerHidden = false; | ||
} else if ( currentScroll > state.prevScroll && 250 < currentScroll ) { | ||
state.isDarkModeTogglerHidden = true; | ||
} | ||
state.prevScroll = currentScroll; | ||
}, | ||
}, | ||
} ); |
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
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 |
---|---|---|
|
@@ -27,8 +27,15 @@ | |
function twenty_twenty_one_add_sub_menu_toggle( $output, $item, $depth, $args ) { | ||
if ( 0 === $depth && in_array( 'menu-item-has-children', $item->classes, true ) ) { | ||
|
||
// Extra attributes depending on whether or not the Interactivity API is being used. | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Nice side effect: avoiding |
||
if ( function_exists( 'wp_register_script_module' ) ) { | ||
$extra_attr = ''; | ||
} else { | ||
$extra_attr = ' onClick="twentytwentyoneExpandSubMenu(this)"'; | ||
} | ||
|
||
// Add toggle button. | ||
$output .= '<button class="sub-menu-toggle" aria-expanded="false" onClick="twentytwentyoneExpandSubMenu(this)">'; | ||
$output .= '<button class="sub-menu-toggle" aria-expanded="false"' . $extra_attr . '>'; | ||
$output .= '<span class="icon-plus">' . twenty_twenty_one_get_icon_svg( 'ui', 'plus', 18 ) . '</span>'; | ||
$output .= '<span class="icon-minus">' . twenty_twenty_one_get_icon_svg( 'ui', 'minus', 18 ) . '</span>'; | ||
/* translators: Hidden accessibility text. */ | ||
|
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
Oops, something went wrong.
Add this suggestion to a batch that can be applied as a single commit.
This suggestion is invalid because no changes were made to the code.
Suggestions cannot be applied while the pull request is closed.
Suggestions cannot be applied while viewing a subset of changes.
Only one suggestion per line can be applied in a batch.
Add this suggestion to a batch that can be applied as a single commit.
Applying suggestions on deleted lines is not supported.
You must change the existing code in this line in order to create a valid suggestion.
Outdated suggestions cannot be applied.
This suggestion has been applied or marked resolved.
Suggestions cannot be applied from pending reviews.
Suggestions cannot be applied on multi-line comments.
Suggestions cannot be applied while the pull request is queued to merge.
Suggestion cannot be applied right now. Please check back later.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
<html>
is not supported as an interactivity target, so this is the exception where mutating the DOM manually is correct.Oh, and doing so reactively with a
data-wp-watch
like you're doing here is the best possible way to do so because it will always be in sync, no matter wherestate.isDarkMode
is changed 🙂