Skip to content
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

Use mobile UI in customiser blocks #17960

Closed
wants to merge 3 commits into from
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/block-editor/src/components/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -72,3 +72,4 @@ export { default as WritingFlow } from './writing-flow';
*/

export { default as BlockEditorProvider } from './provider';
export { default as __experimentalSimulateMediaQuery } from './simulate-media-query';
103 changes: 103 additions & 0 deletions packages/block-editor/src/components/simulate-media-query/index.js
Original file line number Diff line number Diff line change
@@ -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, value } ) {
useEffect(
() => {
const styleSheets = getStyleSheetsThatMatchPaths( partialPaths );
const originalStyles = [];
styleSheets.forEach( ( styleSheet, styleSheetIndex ) => {
for ( let ruleIndex = 0; ruleIndex < styleSheet.rules.length; ++ruleIndex ) {
const rule = styleSheet.rules[ ruleIndex ];
if ( ! isReplaceableMediaRule( rule ) ) {
continue;
}
const ruleText = rule.cssText;
if ( ! originalStyles[ styleSheetIndex ] ) {
originalStyles[ styleSheetIndex ] = [];
}
originalStyles[ styleSheetIndex ][ ruleIndex ] = ruleText;
replaceRule(
styleSheet,
replaceMediaQueryWithWidthEvaluation( ruleText, value ),
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, value ]
);
return null;
}

1 change: 1 addition & 0 deletions packages/edit-widgets/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@
"@wordpress/i18n": "file:../i18n",
"@wordpress/media-utils": "file:../media-utils",
"@wordpress/notices": "file:../notices",
"@wordpress/viewport": "file:../viewport",
"lodash": "^4.17.15",
"rememo": "^3.0.0"
},
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@ import {
navigateRegions,
} from '@wordpress/components';
import { __ } from '@wordpress/i18n';
import { ViewportMatchWidthProvider } from '@wordpress/viewport';
import { __experimentalSimulateMediaQuery } from '@wordpress/block-editor';

/**
* Internal dependencies
Expand All @@ -15,19 +17,35 @@ import WidgetAreas from '../widget-areas';

import './sync-customizer';

const PARTIAL_PATHS = [
'block-editor/style.css',
'block-library/style.css',
'block-library/theme.css',
'block-library/editor.css',
'format-library/style.css',
];

const CUSTOMIZER_PANEL_WIDTH = 479;

function CustomizerEditWidgetsInitializer( { settings } ) {
return (
<SlotFillProvider>
<div
className="edit-widgets-customizer-edit-widgets-initializer__content"
role="region"
aria-label={ __( 'Widgets screen content' ) }
tabIndex="-1"
>
<WidgetAreas blockEditorSettings={ settings } />
</div>
<Popover.Slot />
</SlotFillProvider>
<ViewportMatchWidthProvider width={ CUSTOMIZER_PANEL_WIDTH }>
<__experimentalSimulateMediaQuery
value={ CUSTOMIZER_PANEL_WIDTH }
partialPaths={ PARTIAL_PATHS }
/>
<SlotFillProvider>
<div
className="edit-widgets-customizer-edit-widgets-initializer__content"
role="region"
aria-label={ __( 'Widgets screen content' ) }
tabIndex="-1"
>
<WidgetAreas blockEditorSettings={ settings } />
</div>
<Popover.Slot />
</SlotFillProvider>
</ViewportMatchWidthProvider>
);
}

Expand Down
29 changes: 29 additions & 0 deletions packages/edit-widgets/src/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -46,6 +46,35 @@ body.gutenberg_page_gutenberg-widgets {
&.is-in-customizer {
min-height: initial;
position: initial;

.block-editor-inserter__main-area input {
width: auto;
}

@include break-medium {
.block-editor-inserter {
position: relative;
}

.block-editor-inserter__popover:not(.is-mobile) > .components-popover__content {
overflow-y: visible;
height: 432px;
}

.block-editor-inserter__menu,
.block-editor-inserter__main-area {
width: 400px;
position: relative;
}

.components-popover input[type="search"].block-editor-inserter__search {
font-size: $default-font-size;
}

.block-editor-inserter__results {
height: 394px;
}
}
}
}

Expand Down
5 changes: 5 additions & 0 deletions packages/viewport/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,11 @@ _Returns_

- `Function`: Higher-order component.

<a name="ViewportMatchWidthProvider" href="#ViewportMatchWidthProvider">#</a> **ViewportMatchWidthProvider**

Component that makes all withViewportMatch usages descendents of its children,
evaluate as if the viewport had the width specific as a prop.

<a name="withViewportMatch" href="#withViewportMatch">#</a> **withViewportMatch**

Higher-order component creator, creating a new component which renders with
Expand Down
1 change: 1 addition & 0 deletions packages/viewport/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,7 @@
"@babel/runtime": "^7.4.4",
"@wordpress/compose": "file:../compose",
"@wordpress/data": "file:../data",
"@wordpress/element": "file:../element",
"lodash": "^4.17.15"
},
"publishConfig": {
Expand Down
30 changes: 3 additions & 27 deletions packages/viewport/src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,33 +4,9 @@
import './store';
import addDimensionsEventListener from './listener';

export { default as ifViewportMatches } from './if-viewport-matches';
export { default as withViewportMatch } from './with-viewport-match';

/**
* Hash of breakpoint names with pixel width at which it becomes effective.
*
* @see _breakpoints.scss
*
* @type {Object}
*/
const BREAKPOINTS = {
huge: 1440,
wide: 1280,
large: 960,
medium: 782,
small: 600,
mobile: 480,
};
import { BREAKPOINTS, OPERATORS } from './with-viewport-match';

/**
* Hash of query operators with corresponding condition for media query.
*
* @type {Object}
*/
const OPERATORS = {
'<': 'max-width',
'>=': 'min-width',
};
export { default as ifViewportMatches } from './if-viewport-matches';
export { default as withViewportMatch, ViewportMatchWidthProvider } from './with-viewport-match';

addDimensionsEventListener( BREAKPOINTS, OPERATORS );
106 changes: 99 additions & 7 deletions packages/viewport/src/with-viewport-match.js
Original file line number Diff line number Diff line change
@@ -1,13 +1,83 @@
/**
* External dependencies
*/
import { mapValues } from 'lodash';
import { mapValues, reduce } from 'lodash';

/**
* WordPress dependencies
*/
import { createContext, useContext, useMemo } from '@wordpress/element';
import { createHigherOrderComponent } from '@wordpress/compose';
import { withSelect } from '@wordpress/data';
import { useSelect } from '@wordpress/data';

/**
* Hash of breakpoint names with pixel width at which it becomes effective.
*
* @see _breakpoints.scss
*
* @type {Object}
*/
export const BREAKPOINTS = {
huge: 1440,
wide: 1280,
large: 960,
medium: 782,
small: 600,
mobile: 480,
};

/**
* Hash of query operators with corresponding condition for media query.
*
* @type {Object}
*/
export const OPERATORS = {
'<': 'max-width',
'>=': 'min-width',
};

const OPERATOR_EVALUATORS = {
'<': ( breakpointValue, width ) => ( width < breakpointValue ),
'>=': ( breakpointValue, width ) => ( width >= breakpointValue ),
};

const ViewportMatchWidthContext = createContext( null );

/**
* Component that makes all withViewportMatch usages descendents of its children,
* evaluate as if the viewport had the width specific as a prop.
*/
export const ViewportMatchWidthProvider = ( { width, children } ) => {
const queriesResult = useMemo(
() => {
return reduce(
BREAKPOINTS,
( resultBreakpointsReduce, breakpointValue, breakpointName ) => {
return reduce(
OPERATORS,
( resultOperatorsReduce, operatorMediaQuery, operator ) => {
const operatorKey = `${ operator } ${ breakpointName }`;
const evaluationResult = OPERATOR_EVALUATORS[ operator ](
breakpointValue,
width
);
resultOperatorsReduce[ operatorKey ] = evaluationResult;
return resultOperatorsReduce;
},
resultBreakpointsReduce
);
},
{}
);
},
[ width ]
);
return (
<ViewportMatchWidthContext.Provider value={ queriesResult }>
{ children }
</ViewportMatchWidthContext.Provider>
);
};

/**
* Higher-order component creator, creating a new component which renders with
Expand All @@ -33,11 +103,33 @@ import { withSelect } from '@wordpress/data';
* @return {Function} Higher-order component.
*/
const withViewportMatch = ( queries ) => createHigherOrderComponent(
withSelect( ( select ) => {
return mapValues( queries, ( query ) => {
return select( 'core/viewport' ).isViewportMatch( query );
} );
} ),
( WrappedComponent ) => {
return ( props ) => {
const globalQueriesResult = useSelect( ( select ) => {
return mapValues( queries, ( query ) => {
return select( 'core/viewport' ).isViewportMatch( query );
} );
}, [ queries ] );
const localQueries = useContext( ViewportMatchWidthContext );
const localQueriesResult = useMemo(
() => {
if ( ! localQueries ) {
return null;
}
return mapValues( queries, ( query ) => {
return localQueries[ query ];
} );
},
[ localQueries, queries ]
);
return (
<WrappedComponent
{ ...props }
{ ...( localQueriesResult ? localQueriesResult : globalQueriesResult ) }
/>
);
};
},
'withViewportMatch'
);

Expand Down