diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 1fb22e42008057..1dff5262aed9e4 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -87,3 +87,4 @@ export { default as WritingFlow } from './writing-flow'; */ export { default as BlockEditorProvider } from './provider'; +export { default as __experimentalSimulateMediaQuery } from './simulate-media-query'; diff --git a/packages/block-editor/src/components/simulate-media-query/index.js b/packages/block-editor/src/components/simulate-media-query/index.js new file mode 100644 index 00000000000000..4c6b68b01200be --- /dev/null +++ b/packages/block-editor/src/components/simulate-media-query/index.js @@ -0,0 +1,103 @@ +/** + * External dependencies + */ +import { + filter, + get, + some, +} from 'lodash'; + +/** + * WordPress dependencies + */ +import { useEffect } from '@wordpress/element'; + +const ENABLED_MEDIA_QUERY = '(min-width:0px)'; +const DISABLED_MEDIA_QUERY = '(min-width:999999px)'; + +const VALID_MEDIA_QUERY_REGEX = /\((min|max)-width:([^\(]*?)px\)/g; + +function getStyleSheetsThatMatchPaths( partialPaths ) { + return filter( + get( window, [ 'document', 'styleSheets' ], [] ), + ( styleSheet ) => { + return ( + styleSheet.href && + some( + partialPaths, + ( partialPath ) => { + return styleSheet.href.includes( partialPath ); + } + ) + ); + } + ); +} + +function isReplaceableMediaRule( rule ) { + if ( ! rule.media ) { + return false; + } + return !! rule.conditionText.match( VALID_MEDIA_QUERY_REGEX ); +} + +function replaceRule( styleSheet, newRuleText, index ) { + styleSheet.removeRule( index ); + styleSheet.insertRule( newRuleText, index ); +} + +function replaceMediaQueryWithWidthEvaluation( ruleText, widthValue ) { + return ruleText.replace( VALID_MEDIA_QUERY_REGEX, ( match, minOrMax, value ) => { + const integerValue = parseInt( value ); + if ( + ( minOrMax === 'min' && widthValue >= integerValue ) || + ( minOrMax === 'max' && widthValue <= integerValue ) + ) { + return ENABLED_MEDIA_QUERY; + } + return DISABLED_MEDIA_QUERY; + } ); +} + +export default function SimulateMediaQuery( { partialPaths, width } ) { + useEffect( + () => { + const styleSheets = getStyleSheetsThatMatchPaths( partialPaths ); + const originalStyles = []; + styleSheets.forEach( ( styleSheet, styleSheetIndex ) => { + for ( let ruleIndex = 0; ruleIndex < styleSheet.cssRules.length; ++ruleIndex ) { + const rule = styleSheet.cssRules[ ruleIndex ]; + if ( ! isReplaceableMediaRule( rule ) ) { + continue; + } + const ruleText = rule.cssText; + if ( ! originalStyles[ styleSheetIndex ] ) { + originalStyles[ styleSheetIndex ] = []; + } + originalStyles[ styleSheetIndex ][ ruleIndex ] = ruleText; + replaceRule( + styleSheet, + replaceMediaQueryWithWidthEvaluation( ruleText, width ), + ruleIndex + ); + } + } ); + return () => { + originalStyles.forEach( ( rulesCollection, styleSheetIndex ) => { + if ( ! rulesCollection ) { + return; + } + for ( let ruleIndex = 0; ruleIndex < rulesCollection.length; ++ruleIndex ) { + const originalRuleText = rulesCollection[ ruleIndex ]; + if ( originalRuleText ) { + replaceRule( styleSheets[ styleSheetIndex ], originalRuleText, ruleIndex ); + } + } + } ); + }; + }, + [ partialPaths, width ] + ); + return null; +} + diff --git a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js index b1d3b8d417ac41..28a1ebec60f397 100644 --- a/packages/edit-post/src/components/sidebar/settings-sidebar/index.js +++ b/packages/edit-post/src/components/sidebar/settings-sidebar/index.js @@ -1,10 +1,12 @@ /** * WordPress dependencies */ -import { Panel } from '@wordpress/components'; +import { Panel, ToggleControl, RangeControl } from '@wordpress/components'; import { compose, ifCondition } from '@wordpress/compose'; import { withSelect } from '@wordpress/data'; -import { BlockInspector } from '@wordpress/block-editor'; +import { BlockInspector, __experimentalSimulateMediaQuery } from '@wordpress/block-editor'; +import { __ } from '@wordpress/i18n'; +import { useState } from '@wordpress/element'; /** * Internal dependencies @@ -22,6 +24,43 @@ import PageAttributes from '../page-attributes'; import MetaBoxes from '../../meta-boxes'; import PluginDocumentSettingPanel from '../plugin-document-setting-panel'; +const PARTIAL_PATHS = [ + 'block-library/style.css', + 'block-library/theme.css', + 'block-library/editor.css', +]; +function TestSimulateMediaQuery() { + const [ simulationWidth, setSimulationWidth ] = useState( 400 ); + const [ isSimulationEnabled, setIsSimulationEnabled ] = useState( true ); + const toggleUI = ( + setIsSimulationEnabled( newValue ) } + /> + ); + if ( ! isSimulationEnabled ) { + return toggleUI; + } + return ( + <> + { toggleUI } + ( setSimulationWidth( newValue ) ) } + /> + <__experimentalSimulateMediaQuery + width={ simulationWidth } + partialPaths={ PARTIAL_PATHS } + /> + + ); +} + const SettingsSidebar = ( { sidebarName } ) => ( @@ -43,6 +82,7 @@ const SettingsSidebar = ( { sidebarName } ) => ( { sidebarName === 'edit-post/block' && ( ) } + );