diff --git a/gulpfile.js b/gulpfile.js index 94b00421d..f00aec03d 100644 --- a/gulpfile.js +++ b/gulpfile.js @@ -571,15 +571,10 @@ exit; gulp.task( 'style-editor', function() { return gulp.src( [ path.resolve( __dirname, './src/**/editor.scss' ), '!' + path.resolve( __dirname, './src/deprecated/**/editor.scss' ) ] ) - // Override the breakpoints in the editor in - // src/styles/breakpoints.scss, we do it here because there are various - // files that use the breakpoints and it's easier to override it here. + // The active theme can change the editor preview widths at runtime. + // Use the editor's current device class rather than fixed Sass breakpoints. .pipe( sassVariables( { - // Match the Block Editor's fixed preview widths. getMediaQuery subtracts 1, - // so these default values target 781px tablet and 479px mobile in WordPress 7.0. - // https://github.com/WordPress/gutenberg/pull/74339 - '$desktop-width': 782, - '$tablet-width': 480, + '$use-editor-preview-classes': true, } ) ) .pipe( sass( sassOptions ).on( 'error', sass.logError ) ) .pipe( concat( 'editor_blocks.css' ) ) diff --git a/src/components/block-css/index.js b/src/components/block-css/index.js index 47956abd9..f43c24dfd 100644 --- a/src/components/block-css/index.js +++ b/src/components/block-css/index.js @@ -17,6 +17,7 @@ import { getBlockUniqueClassname, getDependencyAttrnamesFast, getMediaQuery, + getViewportMediaQuery, isVersionSupported, prependClass, } from './util' @@ -31,6 +32,7 @@ import { * External dependencies */ import { pick, kebabCase } from 'lodash' +import { settings } from 'stackable' /** * WordPress dependencies @@ -546,7 +548,12 @@ function createCssEdit( selector, rule, value, device = 'desktop', vendorPrefixe } ) - const mediaQuery = getMediaQuery( device, tabletBreakpoint, mobileBreakpoint ) + const editorBreakpoints = settings.stackable_editor_breakpoints || {} + const mediaQuery = getViewportMediaQuery( device, settings.stackable_editor_viewport_breakpoints ) || getMediaQuery( + device, + editorBreakpoints.tablet || tabletBreakpoint, + editorBreakpoints.mobile || mobileBreakpoint + ) if ( mediaQuery ) { css = `\n${ mediaQuery } {${ css }\n}` } diff --git a/src/components/block-css/util.js b/src/components/block-css/util.js index 475d8968f..43990508f 100644 --- a/src/components/block-css/util.js +++ b/src/components/block-css/util.js @@ -33,7 +33,41 @@ export const getMediaQuery = ( devices = 'desktop', breakDesktop = 1024, breakTa } else if ( devices === 'mobile' ) { return '@media screen and (max-width: ' + ( breakTablet - 1 ) + 'px)' } - return null + return null +} + +/** + * Forms a media query string from WordPress theme.json viewport settings. + * + * WordPress 7.1 allows themes to configure these values and uses the same + * ranges for responsive editor previews. Unlike getMediaQuery, these are + * maximum viewport widths rather than the start of the next device range. + * + * @param {string} devices A list of devices: desktop, tablet or mobile. + * @param {Object} viewports WordPress viewport settings. + * @param {string} viewports.tablet Maximum Tablet viewport width. + * @param {string} viewports.mobile Maximum Mobile viewport width. + * @return {string|null} A media query, or null for missing settings. + */ +export const getViewportMediaQuery = ( devices = 'desktop', viewports = {} ) => { + const { tablet, mobile } = viewports + if ( ! tablet || ! mobile ) { + return null + } + + if ( devices === 'desktopTablet' ) { + return `@media screen and (width > ${ mobile })` + } else if ( devices === 'desktopOnly' ) { + return `@media screen and (width > ${ tablet })` + } else if ( devices === 'tablet' ) { + return `@media screen and (width <= ${ tablet })` + } else if ( devices === 'tabletOnly' ) { + return `@media screen and (width > ${ mobile }) and (width <= ${ tablet })` + } else if ( devices === 'mobile' ) { + return `@media screen and (width <= ${ mobile })` + } + + return null } /** diff --git a/src/editor-settings.php b/src/editor-settings.php index c07d859db..2604409b9 100644 --- a/src/editor-settings.php +++ b/src/editor-settings.php @@ -321,6 +321,22 @@ public function add_settings( $settings ) { $settings['stackable_enable_heading_default_theme_margins_non_posts'] = get_option( 'stackable_enable_heading_default_theme_margins_non_posts' ); $settings['stackable_icon_list_block_default_icon'] = get_option( 'stackable_icon_list_block_default_icon' ); + // WordPress 7.1 allows themes define the Tablet and Mobile editor preview + // breakpoints in theme.json. Keep Stackable's generated editor CSS in sync + // with those previews when the active theme provides valid values. + $viewport_breakpoints = function_exists( 'wp_get_global_settings' ) ? wp_get_global_settings( array( 'viewport' ) ) : array(); + if ( + is_array( $viewport_breakpoints ) && + isset( $viewport_breakpoints['tablet'], $viewport_breakpoints['mobile'] ) && + is_string( $viewport_breakpoints['tablet'] ) && + is_string( $viewport_breakpoints['mobile'] ) + ) { + $settings['stackable_editor_viewport_breakpoints'] = array( + 'tablet' => $viewport_breakpoints['tablet'], + 'mobile' => $viewport_breakpoints['mobile'], + ); + } + // Inserter variations are registered before the block Edit component renders, // so provide the post type here. $current_screen = function_exists( 'get_current_screen' ) ? get_current_screen() : null; diff --git a/src/styles/breakpoints.scss b/src/styles/breakpoints.scss index 472d4608e..2620a55b3 100644 --- a/src/styles/breakpoints.scss +++ b/src/styles/breakpoints.scss @@ -1,32 +1,75 @@ -// These breakpoints are also overridden by gulpfile.js when building styles for -// the editor. We need to define these as integers here because in our -// definition in gulpfile.js, we cannot use px. +// The editor build uses its selected device class instead of viewport media +// queries. The active theme controls the preview width at runtime, so Sass +// cannot safely provide a fixed editor breakpoint. $desktop-width: 1024 !default; $tablet-width: 768 !default; +$use-editor-preview-classes: false !default; -@mixin desktop { - @media only screen and (min-width: #{$desktop-width + 0px}) { +@mixin editor-preview-device( $device ) { + @at-root .stk-preview-device-#{ $device } #{ & } { @content; } } + +@mixin desktop { + @if $use-editor-preview-classes { + @include editor-preview-device( desktop ) { + @content; + } + } @else { + @media only screen and (min-width: #{$desktop-width + 0px}) { + @content; + } + } +} @mixin desktop-tablet { - @media only screen and (min-width: #{$tablet-width + 0px}) { - @content; + @if $use-editor-preview-classes { + @include editor-preview-device( desktop ) { + @content; + } + @include editor-preview-device( tablet ) { + @content; + } + } @else { + @media only screen and (min-width: #{$tablet-width + 0px}) { + @content; + } } } @mixin tablet { - @media only screen and (min-width: #{$tablet-width + 0px}) and (max-width: #{$desktop-width - 1px}) { - @content; + @if $use-editor-preview-classes { + @include editor-preview-device( tablet ) { + @content; + } + } @else { + @media only screen and (min-width: #{$tablet-width + 0px}) and (max-width: #{$desktop-width - 1px}) { + @content; + } } } @mixin tablet-mobile { - @media only screen and (max-width: #{$desktop-width - 1px}) { - @content; + @if $use-editor-preview-classes { + @include editor-preview-device( tablet ) { + @content; + } + @include editor-preview-device( mobile ) { + @content; + } + } @else { + @media only screen and (max-width: #{$desktop-width - 1px}) { + @content; + } } } @mixin mobile { - @media only screen and (max-width: #{$tablet-width - 1px}) { - @content; + @if $use-editor-preview-classes { + @include editor-preview-device( mobile ) { + @content; + } + } @else { + @media only screen and (max-width: #{$tablet-width - 1px}) { + @content; + } } } @@ -41,28 +84,31 @@ $tablet-width: 768 !default; * These dummy styles are removed by gulpfile.js in the `style-editor` and * `style` tasks. */ -@include desktop { - .z { - opacity: 1; + +@if not $use-editor-preview-classes { + @include desktop { + .z { + opacity: 1; + } } -} -@include desktop-tablet { - .z { - opacity: 1; + @include desktop-tablet { + .z { + opacity: 1; + } } -} -@include tablet { - .z { - opacity: 1; + @include tablet { + .z { + opacity: 1; + } } -} -@include tablet-mobile { - .z { - opacity: 1; + @include tablet-mobile { + .z { + opacity: 1; + } } -} -@include mobile { - .z { - opacity: 1; + @include mobile { + .z { + opacity: 1; + } } } diff --git a/src/styles/cssvars.scss b/src/styles/cssvars.scss index 7af78941b..6d6954762 100644 --- a/src/styles/cssvars.scss +++ b/src/styles/cssvars.scss @@ -75,22 +75,38 @@ $_cssvars: (); } @if length( $tablet ) > 0 { - @include tablet { - :root { + @if $use-editor-preview-classes { + .stk-preview-device-tablet { @each $name, $value in $tablet { --stk-#{ $name }: #{ $value }; } } + } @else { + @include tablet { + :root { + @each $name, $value in $tablet { + --stk-#{ $name }: #{ $value }; + } + } + } } } @if length( $mobile ) > 0 { - @include mobile { - :root { + @if $use-editor-preview-classes { + .stk-preview-device-mobile { @each $name, $value in $mobile { --stk-#{ $name }: #{ $value }; } } + } @else { + @include mobile { + :root { + @each $name, $value in $mobile { + --stk-#{ $name }: #{ $value }; + } + } + } } } }