diff --git a/docs/manifest.json b/docs/manifest.json index a44e36d2ee77a..f9cadda442375 100644 --- a/docs/manifest.json +++ b/docs/manifest.json @@ -1319,6 +1319,12 @@ "markdown_source": "../packages/format-library/README.md", "parent": "packages" }, + { + "title": "@wordpress/global-styles", + "slug": "packages-global-styles", + "markdown_source": "../packages/global-styles/README.md", + "parent": "packages" + }, { "title": "@wordpress/hooks", "slug": "packages-hooks", diff --git a/lib/demo-block-templates/front-page.html b/lib/demo-block-templates/front-page.html index 627b7ad76aecf..dc47f8b3a1ab8 100644 --- a/lib/demo-block-templates/front-page.html +++ b/lib/demo-block-templates/front-page.html @@ -1,3 +1,43 @@ + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit

+ + + +

Lorem ipsum dolor sit amet, consectetur adipiscing elit

+ + + +
Lorem ipsum dolor sit amet, consectetur adipiscing elit
+ + + +
Lorem ipsum dolor sit amet, consectetur adipiscing elit
+ + + +

Mauris consequat augue quis risus sollicitudin commodo. In hac habitasse platea dictumst. Pellentesque massa sem, + ultrices non tempus ac, aliquet ac nunc. Sed porttitor ac mi a malesuada. Donec blandit vel arcu blandit + scelerisque. Etiam euismod blandit leo a maximus.

+ + + +
+

Lorem ipsum dolor sit amet, consectetur adipiscing elit

+
+ + + +
Hello
+ +
@@ -9,12 +49,12 @@
- - - - - - + + + + + +
@@ -22,7 +62,7 @@ -
+
@@ -34,7 +74,8 @@
-

With full-site editing you can modify all visual aspects of the site using the block editor.

+

With full-site editing you can modify all + visual aspects of the site using the block editor.

@@ -47,19 +88,19 @@
-
- - -
+
+ + +
-
- -

Footer

- -
+
+ +

Footer

+ +
diff --git a/package-lock.json b/package-lock.json index bb70539a83817..616a613b70c65 100644 --- a/package-lock.json +++ b/package-lock.json @@ -10121,6 +10121,7 @@ "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", "@wordpress/escape-html": "file:packages/escape-html", + "@wordpress/global-styles": "file:packages/global-styles", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", @@ -10414,6 +10415,7 @@ "@wordpress/data": "file:packages/data", "@wordpress/editor": "file:packages/editor", "@wordpress/element": "file:packages/element", + "@wordpress/global-styles": "file:packages/global-styles", "@wordpress/hooks": "file:packages/hooks", "@wordpress/i18n": "file:packages/i18n", "@wordpress/media-utils": "file:packages/media-utils", @@ -10547,6 +10549,16 @@ "lodash": "^4.17.15" } }, + "@wordpress/global-styles": { + "version": "file:packages/global-styles", + "requires": { + "@babel/runtime": "^7.8.3", + "@wordpress/block-editor": "file:packages/block-editor", + "@wordpress/components": "file:packages/components", + "@wordpress/element": "file:packages/element", + "@wordpress/i18n": "file:packages/i18n" + } + }, "@wordpress/hooks": { "version": "file:packages/hooks", "requires": { diff --git a/package.json b/package.json index 9da8cdf2b75b6..ce68707a66cf4 100644 --- a/package.json +++ b/package.json @@ -47,6 +47,7 @@ "@wordpress/format-library": "file:packages/format-library", "@wordpress/hooks": "file:packages/hooks", "@wordpress/html-entities": "file:packages/html-entities", + "@wordpress/global-styles": "file:packages/global-styles", "@wordpress/i18n": "file:packages/i18n", "@wordpress/icons": "file:packages/icons", "@wordpress/is-shallow-equal": "file:packages/is-shallow-equal", diff --git a/packages/block-editor/README.md b/packages/block-editor/README.md index 1cbc41293ca13..e52a6f00cdfdc 100644 --- a/packages/block-editor/README.md +++ b/packages/block-editor/README.md @@ -303,6 +303,10 @@ _Returns_ - `string`: String with the class corresponding to the fontSize passed. The class is generated by appending 'has-' followed by fontSizeSlug in kebabCase and ending with '-font-size'. +# **ifBlockEditSelected** + +Undocumented declaration. + # **InnerBlocks** _Related_ diff --git a/packages/block-editor/src/components/block-edit/index.js b/packages/block-editor/src/components/block-edit/index.js index 3057ff3b4cf72..e1552e2c8ad7b 100644 --- a/packages/block-editor/src/components/block-edit/index.js +++ b/packages/block-editor/src/components/block-edit/index.js @@ -12,7 +12,11 @@ import { Component } from '@wordpress/element'; * Internal dependencies */ import Edit from './edit'; -import { BlockEditContextProvider, useBlockEditContext } from './context'; +import { + BlockEditContextProvider, + useBlockEditContext, + ifBlockEditSelected, +} from './context'; class BlockEdit extends Component { constructor() { @@ -67,4 +71,4 @@ class BlockEdit extends Component { } export default BlockEdit; -export { useBlockEditContext }; +export { useBlockEditContext, ifBlockEditSelected }; diff --git a/packages/block-editor/src/components/index.js b/packages/block-editor/src/components/index.js index 044c46a75e96d..6d96a70d4636a 100644 --- a/packages/block-editor/src/components/index.js +++ b/packages/block-editor/src/components/index.js @@ -10,7 +10,11 @@ export { default as Autocomplete } from './autocomplete'; export { default as BlockAlignmentToolbar } from './block-alignment-toolbar'; export { default as BlockBreadcrumb } from './block-breadcrumb'; export { default as BlockControls } from './block-controls'; -export { default as BlockEdit, useBlockEditContext } from './block-edit'; +export { + default as BlockEdit, + useBlockEditContext, + ifBlockEditSelected, +} from './block-edit'; export { default as BlockFormatControls } from './block-format-controls'; export { default as BlockIcon } from './block-icon'; export { default as BlockNavigationDropdown } from './block-navigation/dropdown'; diff --git a/packages/block-library/package.json b/packages/block-library/package.json index 23613d3fb6202..c0d56a7dd8d16 100644 --- a/packages/block-library/package.json +++ b/packages/block-library/package.json @@ -41,6 +41,7 @@ "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/escape-html": "file:../escape-html", + "@wordpress/global-styles": "file:../global-styles", "@wordpress/i18n": "file:../i18n", "@wordpress/icons": "file:../icons", "@wordpress/is-shallow-equal": "file:../is-shallow-equal", diff --git a/packages/block-library/src/heading/edit.js b/packages/block-library/src/heading/edit.js index c1811738ea2e7..0de30e361c7b9 100644 --- a/packages/block-library/src/heading/edit.js +++ b/packages/block-library/src/heading/edit.js @@ -12,7 +12,7 @@ import HeadingToolbar from './heading-toolbar'; * WordPress dependencies */ import { __ } from '@wordpress/i18n'; -import { PanelBody } from '@wordpress/components'; +import { PanelBody, RangeControl } from '@wordpress/components'; import { createBlock } from '@wordpress/blocks'; import { AlignmentToolbar, @@ -23,6 +23,12 @@ import { } from '@wordpress/block-editor'; import { useRef } from '@wordpress/element'; +import { + GlobalStylesControls, + GlobalStylesPanelBody, + useGlobalStylesState, +} from '@wordpress/global-styles'; + function HeadingEdit( { attributes, setAttributes, @@ -31,6 +37,7 @@ function HeadingEdit( { className, } ) { const ref = useRef(); + const { headingFontWeight, setStyles } = useGlobalStylesState(); const { TextColor, InspectorControlsColorPanel } = __experimentalUseColors( [ { name: 'textColor', property: 'color' } ], { @@ -61,6 +68,20 @@ function HeadingEdit( { } } /> + + + + setStyles( { headingFontWeight: nextValue } ) + } + min={ 100 } + max={ 900 } + step={ 100 } + /> + +

{ __( 'Level' ) }

diff --git a/packages/block-library/src/heading/style.scss b/packages/block-library/src/heading/style.scss new file mode 100644 index 0000000000000..ef5b9012edc78 --- /dev/null +++ b/packages/block-library/src/heading/style.scss @@ -0,0 +1,31 @@ +.wp-gs { + h1, + h2, + h3, + h4, + h5, + h6 { + color: var(--wp-color--text); + font-weight: var(--wp-heading--fontWeight); + line-height: var(--wp-typography--titleLineHeight); + } +} + +.wp-gs h1 { + font-size: var(--wp-typography--fontSizeH1); +} +.wp-gs h2 { + font-size: var(--wp-typography--fontSizeH2); +} +.wp-gs h3 { + font-size: var(--wp-typography--fontSizeH3); +} +.wp-gs h4 { + font-size: var(--wp-typography--fontSizeH4); +} +.wp-gs h5 { + font-size: var(--wp-typography--fontSizeH5); +} +.wp-gs h6 { + font-size: var(--wp-typography--fontSizeH6); +} diff --git a/packages/block-library/src/paragraph/edit.js b/packages/block-library/src/paragraph/edit.js index 944a3952dd616..400b65dc290f2 100644 --- a/packages/block-library/src/paragraph/edit.js +++ b/packages/block-library/src/paragraph/edit.js @@ -7,7 +7,13 @@ import classnames from 'classnames'; * WordPress dependencies */ import { __, _x } from '@wordpress/i18n'; -import { PanelBody, ToggleControl, ToolbarGroup } from '@wordpress/components'; +import { + ColorControl, + PanelBody, + RangeControl, + ToggleControl, + ToolbarGroup, +} from '@wordpress/components'; import { AlignmentToolbar, BlockControls, @@ -22,6 +28,12 @@ import { compose } from '@wordpress/compose'; import { useSelect } from '@wordpress/data'; import { useEffect, useState, useRef } from '@wordpress/element'; +import { + GlobalStylesControls, + GlobalStylesPanelBody, + useGlobalStylesState, +} from '@wordpress/global-styles'; + /** * Browser dependencies */ @@ -81,6 +93,11 @@ function ParagraphBlock( { setFontSize, } ) { const { align, content, dropCap, placeholder, direction } = attributes; + const { + paragraphColor, + paragraphLineHeight, + setStyles, + } = useGlobalStylesState(); const ref = useRef(); const dropCapMinimumHeight = useDropCapMinimumHeight( dropCap, [ @@ -124,6 +141,27 @@ function ParagraphBlock( { } /> + + + + setStyles( { paragraphColor: nextValue } ) + } + /> + + setStyles( { paragraphLineHeight: value } ) + } + /> + + @@ -90,6 +98,20 @@ export default function QuoteEdit( { /> ) } + + + + setStyles( { quoteFontSize: nextValue } ) + } + min={ 10 } + max={ 50 } + step={ 1 } + /> + + ); } diff --git a/packages/block-library/src/quote/style.scss b/packages/block-library/src/quote/style.scss index c1206d68958f7..9ff7209112f78 100644 --- a/packages/block-library/src/quote/style.scss +++ b/packages/block-library/src/quote/style.scss @@ -17,3 +17,9 @@ } } } + +.wp-gs .wp-block-quote { + p { + font-size: var(--wp-quote--fontSize); + } +} diff --git a/packages/block-library/src/style.scss b/packages/block-library/src/style.scss index bd8ab8a093bb5..07e35a726f861 100644 --- a/packages/block-library/src/style.scss +++ b/packages/block-library/src/style.scss @@ -11,6 +11,7 @@ @import "./columns/style.scss"; @import "./cover/style.scss"; @import "./embed/style.scss"; +@import "./heading/style.scss"; @import "./file/style.scss"; @import "./gallery/style.scss"; @import "./image/style.scss"; diff --git a/packages/components/src/color-control/index.js b/packages/components/src/color-control/index.js new file mode 100644 index 0000000000000..a9c62562d3a87 --- /dev/null +++ b/packages/components/src/color-control/index.js @@ -0,0 +1,106 @@ +/** + * External dependencies + */ +import colorize from 'tinycolor2'; +import classnames from 'classnames'; +import { noop } from 'lodash'; +/** + * WordPress dependencies + */ +import { useState, useCallback } from '@wordpress/element'; +import { compose, withInstanceId } from '@wordpress/compose'; + +/** + * Internal dependencies + */ +import BaseControl from '../base-control'; +import ColorPicker from '../color-picker'; +import Dropdown from '../dropdown'; + +import { + ControlContainer, + ControlWrapper, + ColorSwatch, + ColorLabel, +} from './styles/color-control-styles'; + +function BaseColorControl( { + className, + instanceId, + label, + value = 'black', + onChange = noop, + ...props +} ) { + const [ isFocused, setIsFocused ] = useState( false ); + const [ isOpen, setIsOpen ] = useState( false ); + + // TODO: Add derived prop/controlled hook to manage state + const [ color, setColor ] = useState( toColor( value ) ); + + const handleOnChange = ( nextColor ) => { + setColor( nextColor ); + onChange( nextColor ); + }; + + const renderToggle = useCallback( + ( { isOpen: isOpenProp, onToggle } ) => { + setIsOpen( isOpenProp ); + return ( + setIsFocused( false ) } + onFocus={ () => setIsFocused( true ) } + style={ { backgroundColor: color } } + onClick={ onToggle } + /> + ); + }, + [ color ] + ); + + const renderContent = useCallback( + () => ( + + handleOnChange( nextColor.hex ) + } + disableAlpha + /> + ), + [ color ] + ); + + const classes = classnames( 'components-color-control', className ); + const id = `inspector-color-control-${ instanceId }`; + const isFocusedOrOpen = isFocused || isOpen; + + return ( + + + + + + { color } + + + + + ); +} + +function toColor( color ) { + return colorize( color ).toHexString(); +} + +export default compose( [ withInstanceId ] )( BaseColorControl ); diff --git a/packages/components/src/color-control/stories/index.js b/packages/components/src/color-control/stories/index.js new file mode 100644 index 0000000000000..0e93187751fc9 --- /dev/null +++ b/packages/components/src/color-control/stories/index.js @@ -0,0 +1,25 @@ +/** + * External dependencies + */ +import styled from '@emotion/styled'; + +/** + * Internal dependencies + */ +import ColorControl from '../'; + +export default { title: 'Components/ColorControl', component: ColorControl }; + +export const _default = () => { + return ( + + + + ); +}; + +const Wrapper = styled.div` + padding: 40px; + margin-left: auto; + width: 250px; +`; diff --git a/packages/components/src/color-control/styles/color-control-styles.js b/packages/components/src/color-control/styles/color-control-styles.js new file mode 100644 index 0000000000000..ddbae7db2cee9 --- /dev/null +++ b/packages/components/src/color-control/styles/color-control-styles.js @@ -0,0 +1,57 @@ +/** + * External dependencies + */ +import { css } from '@emotion/core'; +import styled from '@emotion/styled'; + +/** + * Internal dependencies + */ +import { color } from '../../utils/colors'; + +export const ControlWrapper = styled.div``; + +const containerFocus = ( { isFocused } ) => { + if ( ! isFocused ) return ''; + + return css` + border: 1px solid ${color( 'blue.medium.focus' )}; + box-shadow: 0 0 0 1px ${color( 'blue.medium.focus' )}; + `; +}; + +export const ControlContainer = styled.div` + align-items: center; + border-radius: 2px; + border: 1px solid ${color( 'lightGray.600' )}; + box-sizing: border-box; + display: flex; + height: 36px; + overflow: hidden; + max-width: 110px; + + ${containerFocus}; +`; + +export const ColorSwatch = styled.button` + appearance: none; + border: none; + border-right: 1px solid ${color( 'lightGray.600' )}; + box-sizing: border-box; + cursor: pointer; + display: block; + height: 36px; + outline: none; + width: 36px; + + &:focus { + outline: none; + } +`; + +export const ColorLabel = styled.div` + box-sizing: border-box; + padding: 4px 8px; + width: 72px; + font-size: 12px; +`; diff --git a/packages/components/src/index.js b/packages/components/src/index.js index 5a78b26d4733e..216bc7edbee28 100644 --- a/packages/components/src/index.js +++ b/packages/components/src/index.js @@ -26,6 +26,7 @@ export { default as CardMedia } from './card/media'; export { default as CheckboxControl } from './checkbox-control'; export { default as ClipboardButton } from './clipboard-button'; export { default as ColorIndicator } from './color-indicator'; +export { default as ColorControl } from './color-control'; export { default as ColorPalette } from './color-palette'; export { default as ColorPicker } from './color-picker'; export { default as CustomSelectControl } from './custom-select-control'; diff --git a/packages/edit-site/package.json b/packages/edit-site/package.json index 8d25b17abbaa3..db954d80ebb88 100644 --- a/packages/edit-site/package.json +++ b/packages/edit-site/package.json @@ -29,6 +29,7 @@ "@wordpress/data": "file:../data", "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", + "@wordpress/global-styles": "file:../global-styles", "@wordpress/hooks": "file:../hooks", "@wordpress/i18n": "file:../i18n", "@wordpress/media-utils": "file:../media-utils", diff --git a/packages/edit-site/src/components/editor/index.js b/packages/edit-site/src/components/editor/index.js index 83a0f498a35fc..1b3e92e51339c 100644 --- a/packages/edit-site/src/components/editor/index.js +++ b/packages/edit-site/src/components/editor/index.js @@ -9,6 +9,7 @@ import { navigateRegions, } from '@wordpress/components'; import { EntityProvider } from '@wordpress/core-data'; +import { GlobalStylesStateProvider } from '@wordpress/global-styles'; /** * Internal dependencies @@ -29,21 +30,23 @@ function Editor( { settings } ) { [] ); return template ? ( - - - - -
- - - - - - + + + + + +
+ + + + + + + ) : null; } diff --git a/packages/edit-site/src/components/sidebar/index.js b/packages/edit-site/src/components/sidebar/index.js index 8b310e80d0231..dcf54be757faa 100644 --- a/packages/edit-site/src/components/sidebar/index.js +++ b/packages/edit-site/src/components/sidebar/index.js @@ -3,6 +3,11 @@ */ import { createSlotFill, Panel } from '@wordpress/components'; import { __ } from '@wordpress/i18n'; +import { GlobalStylesPanel } from '@wordpress/global-styles'; + +/** + * Internal dependencies + */ const { Slot: InspectorSlot, Fill: InspectorFill } = createSlotFill( 'EditSiteSidebarInspector' @@ -16,6 +21,9 @@ function Sidebar() { aria-label={ __( 'Site editor advanced settings.' ) } tabIndex="-1" > + + + diff --git a/packages/global-styles/README.md b/packages/global-styles/README.md new file mode 100644 index 0000000000000..5d765ae85542e --- /dev/null +++ b/packages/global-styles/README.md @@ -0,0 +1,3 @@ +# Global Styles + +(Experimental) Global Styles Library diff --git a/packages/global-styles/package.json b/packages/global-styles/package.json new file mode 100644 index 0000000000000..fbfe8bb34a2bf --- /dev/null +++ b/packages/global-styles/package.json @@ -0,0 +1,33 @@ +{ + "name": "@wordpress/global-styles", + "version": "0.0.1", + "private": true, + "description": "Global Styles module for WordPress.", + "author": "The WordPress Contributors", + "license": "GPL-2.0-or-later", + "keywords": [ + "wordpress" + ], + "homepage": "https://github.com/WordPress/gutenberg/tree/master/packages/global-styles/README.md", + "repository": { + "type": "git", + "url": "https://github.com/WordPress/gutenberg.git", + "directory": "packages/global-styles" + }, + "bugs": { + "url": "https://github.com/WordPress/gutenberg/issues" + }, + "main": "build/index.js", + "module": "build-module/index.js", + "react-native": "src/index", + "dependencies": { + "@babel/runtime": "^7.8.3", + "@wordpress/block-editor": "../block-editor", + "@wordpress/components": "../components", + "@wordpress/element": "../element", + "@wordpress/i18n": "../i18n" + }, + "publishConfig": { + "access": "public" + } +} diff --git a/packages/global-styles/src/controls/color-controls.js b/packages/global-styles/src/controls/color-controls.js new file mode 100644 index 0000000000000..9b23a423737e9 --- /dev/null +++ b/packages/global-styles/src/controls/color-controls.js @@ -0,0 +1,42 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { ColorControl } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { GlobalStylesPanelBody } from '../global-styles-panel-body'; +import { useGlobalStylesState } from '../store'; + +export default function ColorControls() { + const { + textColor, + backgroundColor, + primaryColor, + setStyles, + } = useGlobalStylesState(); + + return ( + + setStyles( { textColor: value } ) } + /> + + setStyles( { backgroundColor: value } ) + } + /> + setStyles( { primaryColor: value } ) } + /> + + ); +} diff --git a/packages/global-styles/src/controls/index.js b/packages/global-styles/src/controls/index.js new file mode 100644 index 0000000000000..b09df4b6b9842 --- /dev/null +++ b/packages/global-styles/src/controls/index.js @@ -0,0 +1,2 @@ +export { default as ColorControls } from './color-controls'; +export { default as TypographyControls } from './typography-controls'; diff --git a/packages/global-styles/src/controls/typography-controls.js b/packages/global-styles/src/controls/typography-controls.js new file mode 100644 index 0000000000000..25ee5ad2033c6 --- /dev/null +++ b/packages/global-styles/src/controls/typography-controls.js @@ -0,0 +1,61 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; +import { RangeControl } from '@wordpress/components'; + +/** + * Internal dependencies + */ +import { GlobalStylesPanelBody } from '../global-styles-panel-body'; +import { useGlobalStylesState } from '../store'; + +export default function TypographyControls() { + const { + fontSize, + fontScale, + lineHeight, + fontWeight, + setStyles, + } = useGlobalStylesState(); + + return ( + + setStyles( { fontSize: value } ) } + /> + setStyles( { fontScale: value } ) } + /> + setStyles( { lineHeight: value } ) } + /> + setStyles( { fontWeight: value } ) } + /> + + ); +} diff --git a/packages/global-styles/src/global-styles-controls.js b/packages/global-styles/src/global-styles-controls.js new file mode 100644 index 0000000000000..3d6c36b5eb44c --- /dev/null +++ b/packages/global-styles/src/global-styles-controls.js @@ -0,0 +1,11 @@ +/** + * Internal dependencies + */ +import { Fill } from './slot'; +import { isEditSite } from './utils'; + +export function GlobalStylesControls( { children } ) { + if ( ! isEditSite() ) return null; + + return { children }; +} diff --git a/packages/global-styles/src/global-styles-panel-body.js b/packages/global-styles/src/global-styles-panel-body.js new file mode 100644 index 0000000000000..78963132a4536 --- /dev/null +++ b/packages/global-styles/src/global-styles-panel-body.js @@ -0,0 +1,14 @@ +/** + * WordPress dependencies + */ +import { PanelBody } from '@wordpress/components'; +/** + * Internal dependencies + */ +import { isEditSite } from './utils'; + +export function GlobalStylesPanelBody( props ) { + if ( ! isEditSite() ) return null; + + return ; +} diff --git a/packages/global-styles/src/global-styles-panel.js b/packages/global-styles/src/global-styles-panel.js new file mode 100644 index 0000000000000..d5d8c19ccf39f --- /dev/null +++ b/packages/global-styles/src/global-styles-panel.js @@ -0,0 +1,17 @@ +/** + * Internal dependencies + */ + +import { Slot } from './slot'; + +import { ColorControls, TypographyControls } from './controls'; + +export function GlobalStylesPanel() { + return ( + <> + + + + + ); +} diff --git a/packages/global-styles/src/index.js b/packages/global-styles/src/index.js new file mode 100644 index 0000000000000..2db07eb4412bd --- /dev/null +++ b/packages/global-styles/src/index.js @@ -0,0 +1,5 @@ +export * from './store'; +export * from './global-styles-controls'; +export * from './global-styles-panel'; +export * from './global-styles-panel-body'; +export * from './utils'; diff --git a/packages/global-styles/src/renderer.js b/packages/global-styles/src/renderer.js new file mode 100644 index 0000000000000..d9135b98abc6f --- /dev/null +++ b/packages/global-styles/src/renderer.js @@ -0,0 +1,87 @@ +/** + * WordPress dependencies + */ +import { useEffect, useLayoutEffect } from '@wordpress/element'; + +/** + * TODO: Replace everything below with client-side style rendering mechanism + */ + +export function useRenderedGlobalStyles( styles = {} ) { + useGlobalStylesEnvironment(); + const generatedStyles = compileStyles( styles ); + + useEffect( () => { + const styleNodeId = 'wp-global-styles-tag'; + let styleNode = document.getElementById( styleNodeId ); + + if ( ! styleNode ) { + styleNode = document.createElement( 'style' ); + styleNode.id = styleNodeId; + document + .getElementsByTagName( 'head' )[ 0 ] + .appendChild( styleNode ); + } + + styleNode.innerText = generatedStyles; + }, [ generatedStyles ] ); +} + +function useGlobalStylesEnvironment() { + useLayoutEffect( () => { + // Adding a slight async delay to give the Gutenberg editor time to render. + window.requestAnimationFrame( () => { + // Getting the Gutenberg editor content wrapper DOM node. + const editorNode = document.getElementsByClassName( + 'editor-styles-wrapper' + )[ 0 ]; + + const targetNode = editorNode || document.documentElement; + + if ( ! targetNode.classList.contains( 'wp-gs' ) ) { + targetNode.classList.add( 'wp-gs' ); + } + } ); + }, [] ); +} + +function flattenObject( ob ) { + const toReturn = {}; + + for ( const i in ob ) { + if ( ! ob.hasOwnProperty( i ) ) continue; + + if ( typeof ob[ i ] === 'object' ) { + const flatObject = flattenObject( ob[ i ] ); + for ( const x in flatObject ) { + if ( ! flatObject.hasOwnProperty( x ) ) continue; + + toReturn[ i + '.' + x ] = flatObject[ x ]; + } + } else { + toReturn[ i ] = ob[ i ]; + } + } + return toReturn; +} + +function compileStyles( styles = {} ) { + const flattenedStyles = { ...flattenObject( styles ) }; + const html = []; + html.push( ':root {' ); + + for ( const key in flattenedStyles ) { + const value = flattenedStyles[ key ]; + if ( value ) { + const style = `--wp-${ key.replace( /\./g, '--' ) }: ${ value };`; + html.push( style ); + } + } + html.push( '}' ); + + html.push( + '.editor-styles-wrapper { background-color: var(--wp-color--background); }' + ); + + return html.join( '\n' ); +} diff --git a/packages/global-styles/src/slot.js b/packages/global-styles/src/slot.js new file mode 100644 index 0000000000000..83d2bd48b44db --- /dev/null +++ b/packages/global-styles/src/slot.js @@ -0,0 +1,10 @@ +/** + * WordPress dependencies + */ +import { createSlotFill } from '@wordpress/components'; +import { ifBlockEditSelected } from '@wordpress/block-editor'; + +export const GlobalStylesSlot = createSlotFill( '__GLOBAL_STYLES_SLOT__' ); +export const { Slot, Fill: BaseFill } = GlobalStylesSlot; + +export const Fill = ifBlockEditSelected( BaseFill ); diff --git a/packages/global-styles/src/store.js b/packages/global-styles/src/store.js new file mode 100644 index 0000000000000..764ef9ec4e7b7 --- /dev/null +++ b/packages/global-styles/src/store.js @@ -0,0 +1,132 @@ +/** + * WordPress dependencies + */ +import { useState, useContext, createContext } from '@wordpress/element'; + +/** + * Internal dependencies + */ +import { useRenderedGlobalStyles } from './renderer'; + +/** + * TODO: Replace everything below with wp.data store mechanism + */ + +export const GlobalStylesContext = createContext( {} ); +export const useGlobalStylesState = () => useContext( GlobalStylesContext ); + +export function GlobalStylesStateProvider( { children } ) { + const state = useGlobalStylesStore(); + + return ( + + { children } + + ); +} + +export function useGlobalStylesDataState() { + const initialState = { + fontSize: 16, + fontWeight: 400, + headingFontWeight: 600, + fontScale: 1.2, + lineHeight: 1.5, + quoteFontSize: 24, + textColor: '#000000', + backgroundColor: '#ffffff', + primaryColor: '#0000ff', + paragraphColor: null, + paragraphLineHeight: null, + }; + + const [ state, _setState ] = useState( initialState ); + + const setState = ( nextState = {} ) => { + const mergedState = { ...state, ...nextState }; + _setState( mergedState ); + }; + + return [ state, setState ]; +} + +export function useGlobalStylesStore() { + // TODO: Replace with data/actions from wp.data + const [ styleState, setStyles ] = useGlobalStylesDataState(); + const { + fontSize, + fontScale, + lineHeight, + fontWeight, + headingFontWeight, + paragraphColor, + paragraphLineHeight, + quoteFontSize, + textColor, + backgroundColor, + primaryColor, + } = styleState; + + const styles = { + color: { + text: textColor, + background: backgroundColor, + primary: primaryColor, + }, + typography: { + ...generateFontSizes( { fontSize, fontScale } ), + ...generateLineHeight( { lineHeight } ), + fontScale, + fontWeight, + }, + heading: { + fontWeight: headingFontWeight, + }, + quote: { + fontSize: toPx( quoteFontSize ), + }, + paragraph: { + color: paragraphColor, + lineHeight: paragraphLineHeight, + }, + }; + + useRenderedGlobalStyles( styles ); + + return { + ...styleState, + setStyles, + }; +} + +/** + * NOTE: Generators for extra computed values. + */ + +function generateLineHeight( { lineHeight = 1.5 } ) { + return { + lineHeight, + titleLineHeight: ( lineHeight * 0.8 ).toFixed( 2 ), + }; +} + +function generateFontSizes( { fontSize = 16, fontScale = 1.2 } ) { + const toScaledPx = ( size ) => { + const value = ( Math.pow( fontScale, size ) * fontSize ).toFixed( 2 ); + return toPx( value ); + }; + + return { + fontSize: `${ fontSize }px`, + fontSizeH1: toScaledPx( 5 ), + fontSizeH2: toScaledPx( 4 ), + fontSizeH3: toScaledPx( 3 ), + fontSizeH4: toScaledPx( 2 ), + fontSizeH5: toScaledPx( 1 ), + fontSizeH6: toScaledPx( 0.5 ), + }; +} + +function toPx( value ) { + return `${ value }px`; +} diff --git a/packages/global-styles/src/utils.js b/packages/global-styles/src/utils.js new file mode 100644 index 0000000000000..b7edfd6161dee --- /dev/null +++ b/packages/global-styles/src/utils.js @@ -0,0 +1,3 @@ +export function isEditSite() { + return window.location.search.indexOf( 'page=gutenberg-edit-site' ) >= 0; +} diff --git a/storybook/test/__snapshots__/index.js.snap b/storybook/test/__snapshots__/index.js.snap index e0afcc5aa3495..41a5c3f50d53c 100644 --- a/storybook/test/__snapshots__/index.js.snap +++ b/storybook/test/__snapshots__/index.js.snap @@ -1933,6 +1933,98 @@ exports[`Storyshots Components/ClipboardButton Default 1`] = ` `; +exports[`Storyshots Components/ColorControl Default 1`] = ` +.emotion-8 { + padding: 40px; + margin-left: auto; + width: 250px; +} + +.emotion-4 { + -webkit-align-items: center; + -webkit-box-align: center; + -ms-flex-align: center; + align-items: center; + border-radius: 2px; + border: 1px solid #d7dade; + box-sizing: border-box; + display: -webkit-box; + display: -webkit-flex; + display: -ms-flexbox; + display: flex; + height: 36px; + overflow: hidden; + max-width: 110px; +} + +.emotion-0 { + -webkit-appearance: none; + -moz-appearance: none; + appearance: none; + border: none; + border-right: 1px solid #d7dade; + box-sizing: border-box; + cursor: pointer; + display: block; + height: 36px; + outline: none; + width: 36px; +} + +.emotion-0:focus { + outline: none; +} + +.emotion-2 { + box-sizing: border-box; + padding: 4px 8px; + width: 72px; + font-size: 12px; +} + +
+
+
+
+
+
+
+
+ #000000 +
+
+
+
+
+
+`; + exports[`Storyshots Components/ColorIndicator Default 1`] = `