diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 212d42976088e..1d3791a85a945 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -2928,6 +2928,9 @@ importers: '@wordpress/date': specifier: 4.38.0 version: 4.38.0 + '@wordpress/dependency-extraction-webpack-plugin': + specifier: 4.17.0 + version: 4.17.0(webpack@5.76.0) '@wordpress/edit-post': specifier: 7.15.0 version: 7.15.0(@types/react@18.2.14)(react-dom@18.2.0)(react@18.2.0) @@ -10498,6 +10501,17 @@ packages: moment: 2.29.4 moment-timezone: 0.5.43 + /@wordpress/dependency-extraction-webpack-plugin@4.17.0(webpack@5.76.0): + resolution: {integrity: sha512-vSWYcjHwdQrnvw6XeYfxRFLGZBEKR3IU82ahfiUEb0doomlR/ulZbnIZdC22Xo0KuQkvTSHEx71yhnvyKYolBg==} + engines: {node: '>=14'} + peerDependencies: + webpack: ^4.8.3 || ^5.0.0 + dependencies: + json2php: 0.0.7 + webpack: 5.76.0(webpack-cli@4.9.1) + webpack-sources: 3.2.3 + dev: false + /@wordpress/dependency-extraction-webpack-plugin@4.21.0(webpack@5.76.0): resolution: {integrity: sha512-YLsopatpixhc0MlbH9Nt5Tutw8QA+zt8uAEy4MpHl6J9h4/xqiROh0QsA0qHrCD5Tn6gi3gLx9RdMTU/pLWQUQ==} engines: {node: '>=14'} diff --git a/projects/packages/blocks/changelog/update-extensions-build b/projects/packages/blocks/changelog/update-extensions-build new file mode 100644 index 0000000000000..fa33a5d0df454 --- /dev/null +++ b/projects/packages/blocks/changelog/update-extensions-build @@ -0,0 +1,4 @@ +Significance: patch +Type: added + +Build individual blocks diff --git a/projects/packages/blocks/src/class-blocks.php b/projects/packages/blocks/src/class-blocks.php index 9286bd4ebc36d..046f80c17d4bf 100644 --- a/projects/packages/blocks/src/class-blocks.php +++ b/projects/packages/blocks/src/class-blocks.php @@ -36,10 +36,11 @@ class Blocks { * @type array $version_requirements Array containing required Gutenberg version and, if known, the WordPress version that was released with this minimum version. * @type bool $plan_check Should we check for a specific plan before registering the block. * } + * @param string $metadata_dir Directory holding the block.json metadata file. * * @return WP_Block_Type|false The registered block type on success, or false on failure. */ - public static function jetpack_register_block( $slug, $args = array() ) { + public static function jetpack_register_block( $slug, $args = array(), $metadata_dir = '' ) { if ( 0 !== strpos( $slug, 'jetpack/' ) && ! strpos( $slug, '/' ) ) { _doing_it_wrong( 'jetpack_register_block', 'Prefix the block with jetpack/ ', 'Jetpack 9.0.0' ); $slug = 'jetpack/' . $slug; @@ -94,12 +95,12 @@ function () use ( $feature_name, $method_name ) { // Ensure editor styles are registered so that the site editor knows about the // editor style dependency when copying styles to the editor iframe. - if ( ! isset( $args['editor_style'] ) ) { + if ( ! isset( $args['editor_style'] ) && ! $metadata_dir ) { $args['editor_style'] = 'jetpack-blocks-editor'; } } - return register_block_type( $slug, $args ); + return register_block_type( $metadata_dir ? $metadata_dir : $slug, $args ); } /** diff --git a/projects/plugins/jetpack/changelog/update-extensions-build b/projects/plugins/jetpack/changelog/update-extensions-build new file mode 100644 index 0000000000000..05b55adf2a462 --- /dev/null +++ b/projects/plugins/jetpack/changelog/update-extensions-build @@ -0,0 +1,4 @@ +Significance: minor +Type: enhancement + +Build individual blocks diff --git a/projects/plugins/jetpack/class.jetpack-gutenberg.php b/projects/plugins/jetpack/class.jetpack-gutenberg.php index 0b2e3c09a461f..8c15a80b2a398 100644 --- a/projects/plugins/jetpack/class.jetpack-gutenberg.php +++ b/projects/plugins/jetpack/class.jetpack-gutenberg.php @@ -273,13 +273,15 @@ public static function preset_exists( $preset ) { * Decodes JSON loaded from a preset file in the blocks folder * * @param string $preset The name of the .json file to load. + * @param object $associative TODO: define. * * @return mixed Returns an object if the file is present, or false if a valid .json file is not present. */ - public static function get_preset( $preset ) { + public static function get_preset( $preset, $associative = null ) { return json_decode( // phpcs:ignore WordPress.WP.AlternativeFunctions.file_get_contents_file_get_contents - file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' ) + file_get_contents( JETPACK__PLUGIN_DIR . self::get_blocks_directory() . $preset . '.json' ), + $associative ); } @@ -297,6 +299,47 @@ public static function get_jetpack_gutenberg_extensions_allowed_list() { return self::get_extensions_preset_for_variation( $preset_extensions_manifest, $blocks_variation ); } + /** + * Returns a list of Jetpack Gutenberg extensions loading seaparately (blocks and plugins), based on index.json + * + * @return array A list of blocks: eg [ 'publicize', 'markdown' ] + */ + public static function get_jetpack_gutenberg_single_extensions() { + $preset_extensions_manifest = self::preset_exists( 'index' ) + ? self::get_preset( 'index' ) + : (object) array(); + + return isset( $preset_extensions_manifest->single ) + ? (array) $preset_extensions_manifest->single + : array(); + } + + /** + * Check for bundled extensions + * + * @return boolean true if has bundled extensions + */ + public static function has_bundled_extension() { + $preset_extensions_manifest = self::preset_exists( 'index' ) + ? self::get_preset( 'index', true ) + : array(); + + $production = isset( $preset_extensions_manifest['production'] ) + ? $preset_extensions_manifest['production'] + : array(); + $beta = isset( $preset_extensions_manifest['beta'] ) + ? $preset_extensions_manifest['beta'] + : array(); + $experimental = isset( $preset_extensions_manifest['experimental'] ) + ? $preset_extensions_manifest['experimental'] + : array(); + $no_post = isset( $preset_extensions_manifest['no-post-editor'] ) + ? $preset_extensions_manifest['no-post-editor'] + : array(); + + return count( $production ) > 0 || count( $beta ) > 0 || count( $experimental ) > 0 || count( $no_post ) > 0; + } + /** * Returns a diff from a combined list of allowed extensions and extensions determined to be excluded * @@ -621,6 +664,9 @@ public static function enqueue_block_editor_assets() { $blocks_dir = self::get_blocks_directory(); $blocks_variation = self::blocks_variation(); + $has_bundle = self::has_bundled_extension(); + $single_blocks = self::get_jetpack_gutenberg_single_extensions(); + $has_single = count( $single_blocks ) > 0; if ( 'production' !== $blocks_variation ) { $blocks_env = '-' . esc_attr( $blocks_variation ); @@ -628,28 +674,49 @@ public static function enqueue_block_editor_assets() { $blocks_env = ''; } - Assets::register_script( - 'jetpack-blocks-editor', - "{$blocks_dir}editor{$blocks_env}.js", - JETPACK__PLUGIN_FILE, - array( 'textdomain' => 'jetpack' ) - ); + if ( $has_single ) { + Assets::register_script( + 'editor-core', + "{$blocks_dir}editor-core.js", + JETPACK__PLUGIN_FILE, + array( 'textdomain' => 'jetpack' ) + ); - // Hack around #20357 (specifically, that the editor bundle depends on - // wp-edit-post but wp-edit-post's styles break the Widget Editor and - // Site Editor) until a real fix gets unblocked. - // @todo Remove this once #20357 is properly fixed. - wp_styles()->query( 'jetpack-blocks-editor', 'registered' )->deps = array(); + wp_enqueue_style( 'editor-core' ); - Assets::enqueue_script( 'jetpack-blocks-editor' ); + wp_localize_script( + 'editor-core', + 'Jetpack_Block_Assets_Base_Url', + array( + 'url' => plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE ), + ) + ); + } - wp_localize_script( - 'jetpack-blocks-editor', - 'Jetpack_Block_Assets_Base_Url', - array( - 'url' => plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE ), - ) - ); + if ( $has_bundle ) { + Assets::register_script( + 'jetpack-blocks-editor', + "{$blocks_dir}editor{$blocks_env}.js", + JETPACK__PLUGIN_FILE, + array( 'textdomain' => 'jetpack' ) + ); + + // Hack around #20357 (specifically, that the editor bundle depends on + // wp-edit-post but wp-edit-post's styles break the Widget Editor and + // Site Editor) until a real fix gets unblocked. + // @todo Remove this once #20357 is properly fixed. + wp_styles()->query( 'jetpack-blocks-editor', 'registered' )->deps = array(); + + Assets::enqueue_script( 'jetpack-blocks-editor' ); + + wp_localize_script( + 'jetpack-blocks-editor', + 'Jetpack_Block_Assets_Base_Url', + array( + 'url' => plugins_url( $blocks_dir . '/', JETPACK__PLUGIN_FILE ), + ) + ); + } if ( defined( 'IS_WPCOM' ) && IS_WPCOM ) { $user = wp_get_current_user(); @@ -736,14 +803,27 @@ public static function enqueue_block_editor_assets() { ); } - wp_localize_script( - 'jetpack-blocks-editor', - 'Jetpack_Editor_Initial_State', - $initial_state - ); + if ( $has_bundle ) { + wp_localize_script( + 'jetpack-blocks-editor', + 'Jetpack_Editor_Initial_State', + $initial_state + ); - // Adds Connection package initial state. - wp_add_inline_script( 'jetpack-blocks-editor', Connection_Initial_State::render(), 'before' ); + // Adds Connection package initial state. + wp_add_inline_script( 'jetpack-blocks-editor', Connection_Initial_State::render(), 'before' ); + } + + if ( $has_single && ! $has_bundle ) { + wp_localize_script( + 'editor-core', + 'Jetpack_Editor_Initial_State', + $initial_state + ); + + // Adds Connection package initial state. + wp_add_inline_script( 'editor-core', Connection_Initial_State::render(), 'before' ); + } } /** @@ -1019,6 +1099,8 @@ public static function get_extensions_preset_for_variation( $preset_extensions_m $preset_extensions = array_unique( array_merge( $preset_extensions, $production_extensions ) ); } + $preset_extensions = array_unique( array_merge( $preset_extensions, self::get_jetpack_gutenberg_single_extensions() ) ); + return $preset_extensions; } diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/block.json b/projects/plugins/jetpack/extensions/blocks/business-hours-single/block.json new file mode 100644 index 0000000000000..2363268afaf47 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/block.json @@ -0,0 +1,28 @@ +{ + "$schema": "https://schemas.wp.org/trunk/block.json", + "apiVersion": 3, + "name": "jetpack/business-hours-single", + "title": "Business Hours Single", + "description": "Display opening hours for your business.", + "keywords": [ "opening hours", "closing time", "schedule", "working day" ], + "version": "1.0.0", + "textdomain": "jetpack", + "category": "grow", + "supports": { + "html": true, + "color": { + "gradients": true + }, + "spacing": { + "margin": true, + "padding": true + }, + "typography": { + "fontSize": true, + "lineHeight": true + }, + "align": [ "wide", "full" ] + }, + "editorScript": "file:./editor.js", + "editorStyle": "file:./editor.css" +} diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/business-hours-single.php b/projects/plugins/jetpack/extensions/blocks/business-hours-single/business-hours-single.php new file mode 100644 index 0000000000000..66f027dcc2671 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/business-hours-single.php @@ -0,0 +1,176 @@ + __NAMESPACE__ . '\render', + ), + $json_dir + ); +} +add_action( 'init', __NAMESPACE__ . '\register_block' ); + +/** + * Get's default days / hours to render a business hour block with no data provided. + * + * @return array + */ +function get_default_days() { + return array( + array( + 'name' => 'Sun', + 'hours' => array(), + ), + array( + 'name' => 'Mon', + 'hours' => array( + array( + 'opening' => '09:00', + 'closing' => '17:00', + ), + ), + ), + array( + 'name' => 'Tue', + 'hours' => array( + array( + 'opening' => '09:00', + 'closing' => '17:00', + ), + ), + ), + array( + 'name' => 'Wed', + 'hours' => array( + array( + 'opening' => '09:00', + 'closing' => '17:00', + ), + ), + ), + array( + 'name' => 'Thu', + 'hours' => array( + array( + 'opening' => '09:00', + 'closing' => '17:00', + ), + ), + ), + array( + 'name' => 'Fri', + 'hours' => array( + array( + 'opening' => '09:00', + 'closing' => '17:00', + ), + ), + ), + array( + 'name' => 'Sat', + 'hours' => array(), + ), + ); +} + +/** + * Dynamic rendering of the block. + * + * @param array $attributes Array containing the business hours block attributes. + * + * @return string + */ +function render( $attributes ) { + global $wp_locale; + + if ( empty( $attributes['days'] ) || ! is_array( $attributes['days'] ) ) { + $attributes['days'] = get_default_days(); + } + + $wrapper_attributes = \WP_Block_Supports::get_instance()->apply_block_supports(); + + $start_of_week = (int) get_option( 'start_of_week', 0 ); + $time_format = get_option( 'time_format' ); + $content = sprintf( + '
', + ! empty( $attributes['className'] ) ? ' ' . esc_attr( $attributes['className'] ) : '', + ! empty( $wrapper_attributes['class'] ) ? ' ' . esc_attr( $wrapper_attributes['class'] ) : '', + ! empty( $wrapper_attributes['style'] ) ? ' style="' . esc_attr( $wrapper_attributes['style'] ) . '"' : '' + ); + + $days = array( 'Sun', 'Mon', 'Tue', 'Wed', 'Thu', 'Fri', 'Sat' ); + + if ( $start_of_week ) { + $chunk1 = array_slice( $attributes['days'], 0, $start_of_week ); + $chunk2 = array_slice( $attributes['days'], $start_of_week ); + $attributes['days'] = array_merge( $chunk2, $chunk1 ); + } + + foreach ( $attributes['days'] as $day ) { + $content .= '
' . + ucfirst( $wp_locale->get_weekday( array_search( $day['name'], $days, true ) ) ) . + '
'; + $content .= '
'; + $days_hours = ''; + + foreach ( $day['hours'] as $key => $hour ) { + $opening = strtotime( $hour['opening'] ); + $closing = strtotime( $hour['closing'] ); + if ( ! $opening || ! $closing ) { + continue; + } + $days_hours .= sprintf( + '%1$s - %2$s', + gmdate( $time_format, $opening ), + gmdate( $time_format, $closing ) + ); + if ( $key + 1 < count( $day['hours'] ) ) { + $days_hours .= ', '; + } + } + + if ( empty( $days_hours ) ) { + $days_hours = esc_html__( 'Closed', 'jetpack' ); + } + $content .= $days_hours; + $content .= '
'; + } + + $content .= '
'; + + Jetpack_Gutenberg::load_assets_as_required( FEATURE_NAME ); + + /** + * Allows folks to filter the HTML content for the Business Hours block + * + * @since 7.1.0 + * + * @param string $content The default HTML content set by `jetpack_business_hours_render` + * @param array $attributes Attributes generated in the block editor for the Business Hours block + */ + return apply_filters( 'jetpack_business_hours_content', $content, $attributes ); +} diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-edit.js b/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-edit.js new file mode 100644 index 0000000000000..3bbe9eae1f131 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-edit.js @@ -0,0 +1,198 @@ +import { Button, TextControl, ToggleControl } from '@wordpress/components'; +import { Component, Fragment } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import classNames from 'classnames'; +import { isEmpty } from 'lodash'; + +const defaultOpen = '09:00'; +const defaultClose = '17:00'; + +class DayEdit extends Component { + renderInterval = ( interval, intervalIndex ) => { + const { day } = this.props; + const { opening, closing } = interval; + return ( + +
+
+ { intervalIndex === 0 && this.renderDayToggle() } +
+
+ { + this.setHour( value, 'opening', intervalIndex ); + } } + /> + { + this.setHour( value, 'closing', intervalIndex ); + } } + /> +
+
+ { day.hours.length > 1 && ( +
+
+ { intervalIndex === day.hours.length - 1 && ( +
+
 
+
+ +
+
 
+
+ ) } +
+ ); + }; + + setHour = ( hourValue, hourType, hourIndex ) => { + const { day, attributes, setAttributes } = this.props; + const { days } = attributes; + setAttributes( { + days: days.map( value => { + if ( value.name === day.name ) { + return { + ...value, + hours: value.hours.map( ( hour, index ) => { + if ( index === hourIndex ) { + return { + ...hour, + [ hourType ]: hourValue, + }; + } + return hour; + } ), + }; + } + return value; + } ), + } ); + }; + + toggleClosed = nextValue => { + const { day, attributes, setAttributes } = this.props; + const { days } = attributes; + + setAttributes( { + days: days.map( value => { + if ( value.name === day.name ) { + const hours = nextValue + ? [ + { + opening: defaultOpen, + closing: defaultClose, + }, + ] + : []; + return { + ...value, + hours, + }; + } + return value; + } ), + } ); + }; + + addInterval = () => { + const { day, attributes, setAttributes } = this.props; + const { days } = attributes; + day.hours.push( { opening: '', closing: '' } ); + setAttributes( { + days: days.map( value => { + if ( value.name === day.name ) { + return { + ...value, + hours: day.hours, + }; + } + return value; + } ), + } ); + }; + + removeInterval = hourIndex => { + const { day, attributes, setAttributes } = this.props; + const { days } = attributes; + + setAttributes( { + days: days.map( value => { + if ( day.name === value.name ) { + return { + ...value, + hours: value.hours.filter( ( hour, index ) => { + return hourIndex !== index; + } ), + }; + } + return value; + } ), + } ); + }; + + isClosed() { + const { day } = this.props; + return isEmpty( day.hours ); + } + + renderDayToggle() { + const { day, localization } = this.props; + return ( + + { localization.days[ day.name ] } + + + ); + } + + renderClosed() { + const { day } = this.props; + return ( +
+
+ { this.renderDayToggle() } +
+
 
+
 
+
+ ); + } + + render() { + const { day } = this.props; + return this.isClosed() ? this.renderClosed() : day.hours.map( this.renderInterval ); + } +} + +export default DayEdit; diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-preview.js b/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-preview.js new file mode 100644 index 0000000000000..4c0c9c69ad9fe --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/components/day-preview.js @@ -0,0 +1,54 @@ +import { date } from '@wordpress/date'; +import { Component } from '@wordpress/element'; +import { _x, sprintf } from '@wordpress/i18n'; +import { isEmpty } from 'lodash'; + +class DayPreview extends Component { + formatTime( time ) { + const { timeFormat } = this.props; + const [ hours, minutes ] = time.split( ':' ); + const _date = new Date(); + if ( ! hours || ! minutes ) { + return false; + } + _date.setHours( hours ); + _date.setMinutes( minutes ); + return date( timeFormat, _date ); + } + + renderInterval = ( interval, key ) => { + const { day } = this.props; + const hours = day.hours; + return ( + + { sprintf( + '%1$s - %2$s', + this.formatTime( interval.opening ), + this.formatTime( interval.closing ) + ) } + { hours.length > 1 + key && , } + + ); + }; + + render() { + const { day, localization } = this.props; + const hours = day.hours.filter( + // remove any malformed or empty intervals + interval => this.formatTime( interval.opening ) && this.formatTime( interval.closing ) + ); + return ( +
+
{ localization.days[ day.name ] }
+
+ { isEmpty( hours ) + ? _x( 'Closed', 'business is closed on a full day', 'jetpack' ) + : hours.map( this.renderInterval ) } +
+
+
+ ); + } +} + +export default DayPreview; diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/edit.js b/projects/plugins/jetpack/extensions/blocks/business-hours-single/edit.js new file mode 100644 index 0000000000000..0c35aa9920378 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/edit.js @@ -0,0 +1,91 @@ +import apiFetch from '@wordpress/api-fetch'; +import { Placeholder } from '@wordpress/components'; +import { getSettings } from '@wordpress/date'; +import { Component } from '@wordpress/element'; +import { __ } from '@wordpress/i18n'; +import classNames from 'classnames'; +import DayEdit from './components/day-edit'; +import DayPreview from './components/day-preview'; +import { icon } from '.'; + +export const defaultLocalization = { + days: { + Sun: __( 'Sunday', 'jetpack' ), + Mon: __( 'Monday', 'jetpack' ), + Tue: __( 'Tuesday', 'jetpack' ), + Wed: __( 'Wednesday', 'jetpack' ), + Thu: __( 'Thursday', 'jetpack' ), + Fri: __( 'Friday', 'jetpack' ), + Sat: __( 'Saturday', 'jetpack' ), + }, + startOfWeek: 0, +}; + +class BusinessHours extends Component { + state = { + localization: defaultLocalization, + hasFetched: false, + }; + + componentDidMount() { + this.apiFetch(); + } + + apiFetch() { + this.setState( { data: defaultLocalization }, () => { + apiFetch( { path: '/wpcom/v2/business-hours/localized-week' } ).then( + data => { + this.setState( { localization: data, hasFetched: true } ); + }, + () => { + this.setState( { localization: defaultLocalization, hasFetched: true } ); + } + ); + } ); + } + + render() { + const { attributes, className, isSelected } = this.props; + const { days } = attributes; + const { localization, hasFetched } = this.state; + const { startOfWeek } = localization; + const localizedWeek = days.concat( days.slice( 0, startOfWeek ) ).slice( startOfWeek ); + + if ( ! hasFetched ) { + return ; + } + + if ( ! isSelected ) { + const settings = getSettings(); + const { + formats: { time }, + } = settings; + return ( +
+ { localizedWeek.map( ( day, key ) => { + return ( + + ); + } ) } +
+ ); + } + + return ( +
+ { localizedWeek.map( ( day, key ) => { + return ( + + ); + } ) } +
+ ); + } +} + +export default BusinessHours; diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.js b/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.js new file mode 100644 index 0000000000000..03fa36550695a --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.js @@ -0,0 +1,8 @@ +import 'editor-core'; // editor-core is an external dependency +import registerJetpackBlock from '../../shared/register-jetpack-block'; +import metadata from './block.json'; +import { settings } from '.'; + +import './editor.scss'; + +registerJetpackBlock( metadata.name.replace( 'jetpack/', '' ), settings ); diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.scss b/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.scss new file mode 100644 index 0000000000000..d596766155cf7 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/editor.scss @@ -0,0 +1,170 @@ +@import '@automattic/jetpack-base-styles/gutenberg-base-styles'; + +.wp-block-jetpack-business-hours { + overflow: hidden; + + dd, dt { + @media (min-width: 480px ) { + display: inline-block; + } + } + + dt { + min-width: 30%; + vertical-align: top; + } + + dd { + margin: 0; + @media (min-width: 480px ) { + max-width: calc( 70% - 0.5em ); + } + } + + .components-toggle-control__label, + .components-base-control__label { + // Sets labels to 13px for consistency in the Site Editor. + // The Site Editor iframe doesn't include common styles, + // from which the font size of the labels are inherited. + // https://github.com/WordPress/gutenberg/blob/3da717b8d0ac7d7821fc6d0475695ccf3ae2829f/packages/base-styles/_variables.scss#L16 + font-size: $default-font-size; + } + + .components-base-control__field { + margin-bottom: 0; + } + + .jetpack-business-hours__item { + margin-bottom: 0.5em; + } + + .business-hours__row { + display: flex; + line-height: normal; + margin-bottom: 4px; + + &.business-hours-row__add, + &.business-hours-row__closed { + margin-bottom: 20px; + } + + .business-hours__day { + width: 44%; + display: flex; + align-items: flex-start; + + .business-hours__day-name { + width: 60%; + font-weight: bold; + overflow-x: hidden; + text-overflow: ellipsis; + white-space: nowrap; + } + + .components-form-toggle { + margin-right: 4px; + margin-top: 4px; + } + } + + .business-hours__hours { + width: 44%; + margin: 0; + display: flex; + align-items: center; + flex-wrap: wrap; + + .components-button { + padding: 0; + } + + .components-base-control { + display: inline-block; + margin-bottom: 0; + width: 48%; + + &.business-hours__open { + margin-right: 4%; + } + + .components-base-control__label { + margin-bottom: 0; + } + } + } + } + + .business-hours__remove { + align-self: flex-end; + margin-bottom: 8px; + text-align: center; + width: 10%; + } + + .business-hours-row__add button:hover { + box-shadow: none !important; + } + + .business-hours__remove button { + display: block; + margin: 0 auto; + } + + .business-hours-row__add .components-button.is-default:hover, + .business-hours__remove .components-button.is-default:hover, + .business-hours-row__add .components-button.is-default:focus, + .business-hours__remove .components-button.is-default:focus, + .business-hours-row__add .components-button.is-default:active, + .business-hours__remove .components-button.is-default:active { + background: none; + box-shadow: none; + } +} + +/** + * We consider the editor area to be small when the business hours block is: + * - within a column block + * - in a screen < xlarge size with the sidebar open + * - in a screen < small size + * In these cases we'll apply small screen styles. + */ +@mixin editor-area-is-small { + @media ( max-width: $break-xlarge ) { + .is-sidebar-opened { + @content; + } + } + @media ( max-width: $break-small ) { + @content; + } + + .wp-block-columns { + @content; + } +} + +@include editor-area-is-small() { + .wp-block-jetpack-business-hours { + .business-hours__row { + flex-wrap: wrap; + + &.business-hours-row__add { + .business-hours__day, + .business-hours__remove { + display: none; + } + } + + .business-hours__day { + width: 100%; + } + + .business-hours__hours { + width: 78%; + } + .business-hours__remove { + width: 18%; + } + } + } +} diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/index.js b/projects/plugins/jetpack/extensions/blocks/business-hours-single/index.js new file mode 100644 index 0000000000000..1076c617d3dab --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/index.js @@ -0,0 +1,87 @@ +import { Path } from '@wordpress/components'; +import { getIconColor } from '../../shared/block-icons'; +import renderMaterialIcon from '../../shared/render-material-icon'; +import BusinessHours from './edit'; + +/** + * Block Registrations: + */ + +const defaultDays = [ + { + name: 'Sun', + hours: [], // Closed by default + }, + { + name: 'Mon', + hours: [ + { + opening: '09:00', + closing: '17:00', + }, + ], + }, + { + name: 'Tue', + hours: [ + { + opening: '09:00', + closing: '17:00', + }, + ], + }, + { + name: 'Wed', + hours: [ + { + opening: '09:00', + closing: '17:00', + }, + ], + }, + { + name: 'Thu', + hours: [ + { + opening: '09:00', + closing: '17:00', + }, + ], + }, + { + name: 'Fri', + hours: [ + { + opening: '09:00', + closing: '17:00', + }, + ], + }, + { + name: 'Sat', + hours: [], // Closed by default + }, +]; +export const icon = renderMaterialIcon( + +); + +export const settings = { + icon: { + src: icon, + foreground: getIconColor(), + }, + attributes: { + days: { + type: 'array', + default: defaultDays, + }, + }, + example: { + attributes: { + days: defaultDays, + }, + }, + edit: props => , + save: () => null, +}; diff --git a/projects/plugins/jetpack/extensions/blocks/business-hours-single/style.scss b/projects/plugins/jetpack/extensions/blocks/business-hours-single/style.scss new file mode 100644 index 0000000000000..1fc272a79e0d7 --- /dev/null +++ b/projects/plugins/jetpack/extensions/blocks/business-hours-single/style.scss @@ -0,0 +1,26 @@ +.jetpack-business-hours { + dt, + dd { + @media (min-width: 480px ) { + display: inline-block; + } + } + + dt { + font-weight: bold; + margin-right: 0.5em; + min-width: 30%; + vertical-align: top; + } + + dd { + margin: 0; + @media (min-width: 480px ) { + max-width: calc( 70% - 0.5em ); + } + } +} + +.jetpack-business-hours__item { + margin-bottom: 0.5em; +} diff --git a/projects/plugins/jetpack/extensions/index.json b/projects/plugins/jetpack/extensions/index.json index 727694b981ac9..c06bd89c254e2 100644 --- a/projects/plugins/jetpack/extensions/index.json +++ b/projects/plugins/jetpack/extensions/index.json @@ -94,5 +94,6 @@ "tock", "videopress", "wordads" - ] + ], + "single": [ "business-hours-single" ] } diff --git a/projects/plugins/jetpack/package.json b/projects/plugins/jetpack/package.json index 92cbf4a02c1f2..2b6890e5c8cff 100644 --- a/projects/plugins/jetpack/package.json +++ b/projects/plugins/jetpack/package.json @@ -68,6 +68,7 @@ "@wordpress/compose": "6.15.0", "@wordpress/data": "9.8.0", "@wordpress/date": "4.38.0", + "@wordpress/dependency-extraction-webpack-plugin": "4.17.0", "@wordpress/edit-post": "7.15.0", "@wordpress/element": "5.15.0", "@wordpress/hooks": "3.38.0", diff --git a/projects/plugins/jetpack/tools/webpack.config.extensions.js b/projects/plugins/jetpack/tools/webpack.config.extensions.js index 127621bbd07b7..7c68f86ef3eae 100644 --- a/projects/plugins/jetpack/tools/webpack.config.extensions.js +++ b/projects/plugins/jetpack/tools/webpack.config.extensions.js @@ -7,6 +7,9 @@ const path = require( 'path' ); const jetpackWebpackConfig = require( '@automattic/jetpack-webpack-config/webpack' ); const webpack = jetpackWebpackConfig.webpack; const RemoveAssetWebpackPlugin = require( '@automattic/remove-asset-webpack-plugin' ); +const { + defaultRequestToExternal, +} = require( '@wordpress/dependency-extraction-webpack-plugin/lib/util' ); const CopyWebpackPlugin = require( 'copy-webpack-plugin' ); const jsdom = require( 'jsdom' ); const CopyBlockEditorAssetsPlugin = require( './copy-block-editor-assets' ); @@ -40,6 +43,7 @@ const presetPath = path.join( __dirname, '../extensions', 'index.json' ); const presetIndex = require( presetPath ); const presetProductionBlocks = presetIndex.production || []; const presetNoPostEditorBlocks = presetIndex[ 'no-post-editor' ] || []; +const presetSingleBlocks = presetIndex.single || []; const presetExperimentalBlocks = [ ...presetProductionBlocks, @@ -96,6 +100,22 @@ const editorNoPostEditorScript = [ ), ]; +const editorSingleBlocksScripts = presetSingleBlocks.reduce( ( editorBlocks, block ) => { + const editorScriptPath = path.join( __dirname, '../extensions/blocks', block, 'editor.js' ); + if ( fs.existsSync( editorScriptPath ) ) { + editorBlocks[ block + '/editor' ] = [ editorScriptPath ]; + } + return editorBlocks; +}, {} ); + +const viewSingleBlocksScripts = presetSingleBlocks.reduce( ( viewBlocks, block ) => { + const viewScriptPath = path.join( __dirname, '../extensions/blocks', block, 'view.js' ); + if ( fs.existsSync( viewScriptPath ) ) { + viewBlocks[ block + '/view' ] = [ viewSetup, ...[ viewScriptPath ] ]; + } + return viewBlocks; +}, {} ); + const sharedWebpackConfig = { mode: jetpackWebpackConfig.mode, devtool: jetpackWebpackConfig.devtool, @@ -268,4 +288,34 @@ module.exports = [ } ), ], }, + { + ...sharedWebpackConfig, + entry: { + ...editorSingleBlocksScripts, + ...viewSingleBlocksScripts, + 'editor-core': path.join( __dirname, '../extensions/editor.js' ), + }, + plugins: [ + new CopyWebpackPlugin( { + patterns: [ + { + from: '**/block.json', + to: '[path][name][ext]', + context: path.join( __dirname, '../extensions/blocks' ), + }, + ], + } ), + ...jetpackWebpackConfig.StandardPlugins( { + DependencyExtractionPlugin: { + injectPolyfill: true, + requestToExternal( request ) { + if ( request === 'editor-core' ) { + return 'editor-core'; + } + return defaultRequestToExternal( request ); + }, + }, + } ), + ], + }, ]; diff --git a/tools/docker/jetpack-docker-config-default.yml b/tools/docker/jetpack-docker-config-default.yml index 63ac16823faa9..10ef36b238d4d 100644 --- a/tools/docker/jetpack-docker-config-default.yml +++ b/tools/docker/jetpack-docker-config-default.yml @@ -7,6 +7,7 @@ default: tools/docker/wordpress: /var/www/html .: /usr/local/src/jetpack-monorepo tools/docker/mu-plugins: /var/www/html/wp-content/mu-plugins + /Users/andrewlysenko/Automattic/gutenberg: /var/www/html/wp-content/plugins/gutenberg # Extra configuration (none by default). Anything set under this key will be written out as # an extra docker-compose configuration file.