From cbcdb14d9e9641a7dd2d3d81f50a1c1e33c3f4dd Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 9 Sep 2024 17:08:13 +1000 Subject: [PATCH 01/15] Initial commit - working out category model --- .../src/components/style-book/constants.js | 103 ++++++++++++++++++ .../style-book/useStyleBookCategories.js | 12 ++ 2 files changed, 115 insertions(+) create mode 100644 packages/edit-site/src/components/style-book/constants.js create mode 100644 packages/edit-site/src/components/style-book/useStyleBookCategories.js diff --git a/packages/edit-site/src/components/style-book/constants.js b/packages/edit-site/src/components/style-book/constants.js new file mode 100644 index 0000000000000..e6f6cb1d352eb --- /dev/null +++ b/packages/edit-site/src/components/style-book/constants.js @@ -0,0 +1,103 @@ +/** + * WordPress dependencies + */ +import { __ } from '@wordpress/i18n'; + +const STYLE_BOOK_CATEGORIES = [ + { + name: 'layout', + label: __( 'Layout' ), + blocks: [], + }, + { + name: 'text', + label: __( 'Text' ), + blocks: [ 'core/post-content', 'core/home-link' ], + }, + { + name: 'colors', + label: __( 'Colors' ), + blocks: [], + }, + { + name: 'theme', + label: __( 'Theme' ), + categories: [ + { + name: 'site-identity', + label: __( 'Site Identity' ), + blocks: [ + 'core/site-logo', + 'core/site-title', + 'core/site-tagline', + ], + }, + { + name: 'design', + label: __( 'Design' ), + blocks: [ + 'core/navigation', + 'core/buttons', + 'core/avatar', + 'core/time-to-read', + 'core/table-of-contents', + 'core/separator', + 'core/more', + 'core/page-break', + ], + }, + { + name: 'posts', + label: __( 'Posts' ), + blocks: [ + 'core/post-title', + 'core/post-excerpt', + 'core/post-author', + 'core/post-author-name', + 'core/post-author-biography', + 'core/post-date', + 'core/post-terms', + 'core/term-description', + 'core/query-title', + 'core/query-no-results', + 'core/query-pagination', + 'core/query-numbers', + ], + }, + { + name: 'comments', + label: __( 'Comments' ), + blocks: [ + 'core/comments-title', + 'core/comments-pagination', + 'core/comments-pagination-numbers', + 'core/comments', + 'core/comments-author-name', + 'core/comment-content', + 'core/comment-date', + 'core/comment-edit-link', + 'core/comment-reply-link', + 'core/comment-template', + 'core/post-comments-count', + 'core/post-comments-link', + ], + }, + ], + }, + { + name: 'media', + label: __( 'Media' ), + blocks: [ 'core/post-featured-image' ], + }, + { + name: 'widgets', + label: __( 'Widgets' ), + blocks: [], + }, + + { + name: 'embeds', + label: __( 'Embeds' ), + blocks: [], + }, +]; diff --git a/packages/edit-site/src/components/style-book/useStyleBookCategories.js b/packages/edit-site/src/components/style-book/useStyleBookCategories.js new file mode 100644 index 0000000000000..523238fab1c94 --- /dev/null +++ b/packages/edit-site/src/components/style-book/useStyleBookCategories.js @@ -0,0 +1,12 @@ +/** + * External dependencies + */ + +/** + * WordPress dependencies + */ + +export function useStyleBookCategories() { + + +} From e308cc743bc58936c300b4d13c836f147a40248f Mon Sep 17 00:00:00 2001 From: ramon Date: Fri, 13 Sep 2024 16:48:13 +1000 Subject: [PATCH 02/15] More stuff --- .../src/components/style-book/constants.js | 47 +++++++++++++------ .../style-book/useStyleBookCategories.js | 14 +++++- 2 files changed, 46 insertions(+), 15 deletions(-) diff --git a/packages/edit-site/src/components/style-book/constants.js b/packages/edit-site/src/components/style-book/constants.js index e6f6cb1d352eb..bec364da401b2 100644 --- a/packages/edit-site/src/components/style-book/constants.js +++ b/packages/edit-site/src/components/style-book/constants.js @@ -3,28 +3,47 @@ */ import { __ } from '@wordpress/i18n'; -const STYLE_BOOK_CATEGORIES = [ +/* + category: slug/identifier for the category + label: display name for the category + blocks: block to display in the category in addition to any registered blocks in that category. + onClick: function to call when the category's blocks are clicked. Overrides the default behavior. + */ +export const STYLE_BOOK_CATEGORIES = [ { - name: 'layout', - label: __( 'Layout' ), - blocks: [], + // Clicking examples on the landing tab + // will take you to the corresponding + // tab in the style book. E.g., image > media + // So the click events have to be handled separately. + category: 'overview', + label: __( 'Overview' ), + blocks: [ + // colors + 'core/heading', + 'core/paragraph', + 'core/image', + 'core/separator', + 'core/buttons', + 'core/pull-quote', + 'core/search', + ], }, { - name: 'text', + category: 'text', label: __( 'Text' ), blocks: [ 'core/post-content', 'core/home-link' ], }, { - name: 'colors', + category: 'colors', label: __( 'Colors' ), blocks: [], }, { - name: 'theme', + category: 'theme', label: __( 'Theme' ), categories: [ { - name: 'site-identity', + category: 'site-identity', label: __( 'Site Identity' ), blocks: [ 'core/site-logo', @@ -33,7 +52,7 @@ const STYLE_BOOK_CATEGORIES = [ ], }, { - name: 'design', + category: 'design', label: __( 'Design' ), blocks: [ 'core/navigation', @@ -47,7 +66,7 @@ const STYLE_BOOK_CATEGORIES = [ ], }, { - name: 'posts', + category: 'posts', label: __( 'Posts' ), blocks: [ 'core/post-title', @@ -65,7 +84,7 @@ const STYLE_BOOK_CATEGORIES = [ ], }, { - name: 'comments', + category: 'comments', label: __( 'Comments' ), blocks: [ 'core/comments-title', @@ -85,18 +104,18 @@ const STYLE_BOOK_CATEGORIES = [ ], }, { - name: 'media', + category: 'media', label: __( 'Media' ), blocks: [ 'core/post-featured-image' ], }, { - name: 'widgets', + category: 'widgets', label: __( 'Widgets' ), blocks: [], }, { - name: 'embeds', + category: 'embeds', label: __( 'Embeds' ), blocks: [], }, diff --git a/packages/edit-site/src/components/style-book/useStyleBookCategories.js b/packages/edit-site/src/components/style-book/useStyleBookCategories.js index 523238fab1c94..c987feb0bfa96 100644 --- a/packages/edit-site/src/components/style-book/useStyleBookCategories.js +++ b/packages/edit-site/src/components/style-book/useStyleBookCategories.js @@ -6,7 +6,19 @@ * WordPress dependencies */ -export function useStyleBookCategories() { +/** + * Internal dependencies + */ +import { STYLE_BOOK_CATEGORIES } from './constants'; + +export function useStyleBookExamples() { + // This hook will get all block examples, + // and group them by category. + /* + - get all registered block examples. See current getExamples() + - build any adhoc examples. + STYLE_BOOK_CATEGORIES + */ } From 74f0c3a64e2baf7956b637190c0b3016075ba177 Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 16 Sep 2024 14:24:38 +1000 Subject: [PATCH 03/15] Working out the category model --- .../src/components/style-book/constants.js | 45 +++--- .../style-book/useStyleBookCategories.js | 137 +++++++++++++++++- 2 files changed, 154 insertions(+), 28 deletions(-) diff --git a/packages/edit-site/src/components/style-book/constants.js b/packages/edit-site/src/components/style-book/constants.js index bec364da401b2..21b84a87f10e6 100644 --- a/packages/edit-site/src/components/style-book/constants.js +++ b/packages/edit-site/src/components/style-book/constants.js @@ -5,20 +5,23 @@ import { __ } from '@wordpress/i18n'; /* category: slug/identifier for the category + subCategories: array of subcategories label: display name for the category - blocks: block to display in the category in addition to any registered blocks in that category. + include: block to display in the category in addition to any registered blocks in that category. + exclude: block to exclude from the category. onClick: function to call when the category's blocks are clicked. Overrides the default behavior. */ export const STYLE_BOOK_CATEGORIES = [ - { +/* { // Clicking examples on the landing tab // will take you to the corresponding // tab in the style book. E.g., image > media // So the click events have to be handled separately. category: 'overview', label: __( 'Overview' ), - blocks: [ + include: [ // colors + 'custom/colors', 'core/heading', 'core/paragraph', 'core/image', @@ -27,25 +30,25 @@ export const STYLE_BOOK_CATEGORIES = [ 'core/pull-quote', 'core/search', ], - }, + },*/ { category: 'text', label: __( 'Text' ), - blocks: [ 'core/post-content', 'core/home-link' ], + include: [ 'core/post-content', 'core/home-link' ], }, { category: 'colors', label: __( 'Colors' ), - blocks: [], + include: [ 'custom/colors' ], }, { category: 'theme', label: __( 'Theme' ), - categories: [ + subCategories: [ { category: 'site-identity', label: __( 'Site Identity' ), - blocks: [ + include: [ 'core/site-logo', 'core/site-title', 'core/site-tagline', @@ -54,21 +57,17 @@ export const STYLE_BOOK_CATEGORIES = [ { category: 'design', label: __( 'Design' ), - blocks: [ + include: [ 'core/navigation', - 'core/buttons', 'core/avatar', - 'core/time-to-read', - 'core/table-of-contents', - 'core/separator', - 'core/more', - 'core/page-break', + 'core/post-time-to-read', ], + exclude: [ 'core/home-link' ], }, { category: 'posts', label: __( 'Posts' ), - blocks: [ + include: [ 'core/post-title', 'core/post-excerpt', 'core/post-author', @@ -86,7 +85,7 @@ export const STYLE_BOOK_CATEGORIES = [ { category: 'comments', label: __( 'Comments' ), - blocks: [ + include: [ 'core/comments-title', 'core/comments-pagination', 'core/comments-pagination-numbers', @@ -106,17 +105,17 @@ export const STYLE_BOOK_CATEGORIES = [ { category: 'media', label: __( 'Media' ), - blocks: [ 'core/post-featured-image' ], + include: [ 'core/post-featured-image' ], }, { category: 'widgets', label: __( 'Widgets' ), - blocks: [], + include: [], }, - { - category: 'embeds', +/* { + category: 'embed', label: __( 'Embeds' ), - blocks: [], - }, + include: [], + },*/ ]; diff --git a/packages/edit-site/src/components/style-book/useStyleBookCategories.js b/packages/edit-site/src/components/style-book/useStyleBookCategories.js index c987feb0bfa96..e548d51bf8eda 100644 --- a/packages/edit-site/src/components/style-book/useStyleBookCategories.js +++ b/packages/edit-site/src/components/style-book/useStyleBookCategories.js @@ -5,20 +5,147 @@ /** * WordPress dependencies */ +import { __, sprintf } from '@wordpress/i18n'; +import { + getCategories, + getBlockType, + getBlockTypes, + getBlockFromExample, + createBlock, +} from '@wordpress/blocks'; +import { privateApis as editorPrivateApis } from '@wordpress/editor'; +import { useMemo, useState, memo, useContext } from '@wordpress/element'; /** * Internal dependencies */ +import { unlock } from '../../lock-unlock'; import { STYLE_BOOK_CATEGORIES } from './constants'; +import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; -export function useStyleBookExamples() { +const { + ExperimentalBlockEditorProvider, + useGlobalStyle, + GlobalStylesContext, + useGlobalStylesOutputWithConfig, +} = unlock( blockEditorPrivateApis ); + +/* // This hook will get all block examples, // and group them by category. + - get all registered block examples. See current getExamples() + - build any adhoc examples. + STYLE_BOOK_CATEGORIES - /* - - get all registered block examples. See current getExamples() - - build any adhoc examples. - STYLE_BOOK_CATEGORIES + */ + +function getCustomExamples() { + const customExamples = [ + { + name: 'custom/colors', + title: __( 'Colors' ), + category: 'colors', + blocks: [ + createBlock( 'core/group', { + attributes: { + layout: { + type: 'grid', + columnCount: 3, + }, + }, + innerBlocks: [ + createBlock( 'core/group', { + style: { + layout: { + selfStretch: 'fill', + }, + }, + layout: { + type: 'constrained', + }, + } ), + ], + } ), + ], + }, + ]; +} +export function getExamples() { + const nonHeadingBlockExamples = getBlockTypes() + .filter( ( blockType ) => { + const { name, example, supports } = blockType; + return ( + name !== 'core/heading' && + !! example && + supports.inserter !== false + ); + } ) + .map( ( blockType ) => ( { + name: blockType.name, + title: blockType.title, + category: blockType.category, + blocks: getBlockFromExample( blockType.name, blockType.example ), + } ) ); + /* + * Use our own example for the Heading block so that we can show multiple + * heading levels. */ + const headingsExample = !! getBlockType( 'core/heading' ) + ? { + name: 'core/heading', + title: __( 'Headings' ), + category: 'text', + blocks: [ 1, 2, 3, 4, 5, 6 ].map( ( level ) => { + return createBlock( 'core/heading', { + content: sprintf( + // translators: %d: heading level e.g: "1", "2", "3" + __( 'Heading %d' ), + level + ), + level, + } ); + } ), + } + : []; + + return [ nonHeadingBlockExamples, ...headingsExample ]; +} + +function getCategoryExamples( category, examples ) { + if ( ! category || ! examples ) { + return []; + } + + if ( category?.subCategories ) { + return category.subCategories.reduce( + ( acc, subCategory ) => ( { + ...acc, + ...getCategoryExamples( subCategory.category, examples ), + } ), + {} + ); + } + + const { include, exclude } = category; + + return { + [ category.category ]: { + label: category.label, + examples: examples.filter( ( example ) => { + return ( + ! exclude.includes( example.name ) && + ( example.category === category || + include.includes( example.name ) ) + ); + } ), + }, + }; } + +/* + + + + + */ From 0e963d871ce01540ec149a9b9cdda6488c8f98da Mon Sep 17 00:00:00 2001 From: ramon Date: Mon, 16 Sep 2024 15:17:22 +1000 Subject: [PATCH 04/15] Adding test --- .../src/components/style-book/test/utils.js | 14 ++++++++++++++ .../{useStyleBookCategories.js => utils.js} | 2 +- 2 files changed, 15 insertions(+), 1 deletion(-) create mode 100644 packages/edit-site/src/components/style-book/test/utils.js rename packages/edit-site/src/components/style-book/{useStyleBookCategories.js => utils.js} (98%) diff --git a/packages/edit-site/src/components/style-book/test/utils.js b/packages/edit-site/src/components/style-book/test/utils.js new file mode 100644 index 0000000000000..4cc02531a4f7e --- /dev/null +++ b/packages/edit-site/src/components/style-book/test/utils.js @@ -0,0 +1,14 @@ +/** + * Internal dependencies + */ +import { getCategoryExamples } from './utils'; +import { STYLE_BOOK_CATEGORIES } from './constants'; + +const exampleBlocks = {}; + +describe( 'getCategoryExamples', () => { + it( 'returns category key value pairs', () => { + + //expect( ).toEqual( ); + } ); +} ); diff --git a/packages/edit-site/src/components/style-book/useStyleBookCategories.js b/packages/edit-site/src/components/style-book/utils.js similarity index 98% rename from packages/edit-site/src/components/style-book/useStyleBookCategories.js rename to packages/edit-site/src/components/style-book/utils.js index e548d51bf8eda..0726f4b43c29f 100644 --- a/packages/edit-site/src/components/style-book/useStyleBookCategories.js +++ b/packages/edit-site/src/components/style-book/utils.js @@ -112,7 +112,7 @@ export function getExamples() { return [ nonHeadingBlockExamples, ...headingsExample ]; } -function getCategoryExamples( category, examples ) { +export function getCategoryExamples( category, examples ) { if ( ! category || ! examples ) { return []; } From 3918f4019c9b023db543b2f00922d176e34b6098 Mon Sep 17 00:00:00 2001 From: ramon Date: Tue, 17 Sep 2024 14:23:27 +1000 Subject: [PATCH 05/15] Start integration --- .../src/components/style-book/constants.js | 153 +++++++++--------- .../src/components/style-book/index.js | 16 +- .../src/components/style-book/test/utils.js | 96 ++++++++++- .../src/components/style-book/utils.js | 53 ++++-- 4 files changed, 209 insertions(+), 109 deletions(-) diff --git a/packages/edit-site/src/components/style-book/constants.js b/packages/edit-site/src/components/style-book/constants.js index 21b84a87f10e6..c7509326bd03e 100644 --- a/packages/edit-site/src/components/style-book/constants.js +++ b/packages/edit-site/src/components/style-book/constants.js @@ -3,10 +3,60 @@ */ import { __ } from '@wordpress/i18n'; +export const STYLE_BOOK_THEME_SUBCATEGORIES = [ + { + name: 'site-identity', + title: __( 'Site Identity' ), + blocks: [ 'core/site-logo', 'core/site-title', 'core/site-tagline' ], + }, + { + name: 'design', + title: __( 'Design' ), + blocks: [ 'core/navigation', 'core/avatar', 'core/post-time-to-read' ], + exclude: [ 'core/home-link' ], + }, + { + name: 'posts', + title: __( 'Posts' ), + blocks: [ + 'core/post-title', + 'core/post-excerpt', + 'core/post-author', + 'core/post-author-name', + 'core/post-author-biography', + 'core/post-date', + 'core/post-terms', + 'core/term-description', + 'core/query-title', + 'core/query-no-results', + 'core/query-pagination', + 'core/query-numbers', + ], + }, + { + name: 'comments', + title: __( 'Comments' ), + blocks: [ + 'core/comments-title', + 'core/comments-pagination', + 'core/comments-pagination-numbers', + 'core/comments', + 'core/comments-author-name', + 'core/comment-content', + 'core/comment-date', + 'core/comment-edit-link', + 'core/comment-reply-link', + 'core/comment-template', + 'core/post-comments-count', + 'core/post-comments-link', + ], + }, +]; /* - category: slug/identifier for the category - subCategories: array of subcategories - label: display name for the category + + name: slug/identifier for the category // @TODO maybe rename to `slug` after the results of getCategories() + subcategories: array of subcategories + title: display name for the category include: block to display in the category in addition to any registered blocks in that category. exclude: block to exclude from the category. onClick: function to call when the category's blocks are clicked. Overrides the default behavior. @@ -17,9 +67,9 @@ export const STYLE_BOOK_CATEGORIES = [ // will take you to the corresponding // tab in the style book. E.g., image > media // So the click events have to be handled separately. - category: 'overview', - label: __( 'Overview' ), - include: [ + name: 'overview', + title: __( 'Overview' ), + blocks: [ // colors 'custom/colors', 'core/heading', @@ -32,90 +82,35 @@ export const STYLE_BOOK_CATEGORIES = [ ], },*/ { - category: 'text', - label: __( 'Text' ), - include: [ 'core/post-content', 'core/home-link' ], + name: 'text', + title: __( 'Text' ), + blocks: [ 'core/post-content', 'core/home-link' ], }, { - category: 'colors', - label: __( 'Colors' ), - include: [ 'custom/colors' ], + name: 'colors', + title: __( 'Colors' ), + blocks: [ 'custom/colors' ], }, { - category: 'theme', - label: __( 'Theme' ), - subCategories: [ - { - category: 'site-identity', - label: __( 'Site Identity' ), - include: [ - 'core/site-logo', - 'core/site-title', - 'core/site-tagline', - ], - }, - { - category: 'design', - label: __( 'Design' ), - include: [ - 'core/navigation', - 'core/avatar', - 'core/post-time-to-read', - ], - exclude: [ 'core/home-link' ], - }, - { - category: 'posts', - label: __( 'Posts' ), - include: [ - 'core/post-title', - 'core/post-excerpt', - 'core/post-author', - 'core/post-author-name', - 'core/post-author-biography', - 'core/post-date', - 'core/post-terms', - 'core/term-description', - 'core/query-title', - 'core/query-no-results', - 'core/query-pagination', - 'core/query-numbers', - ], - }, - { - category: 'comments', - label: __( 'Comments' ), - include: [ - 'core/comments-title', - 'core/comments-pagination', - 'core/comments-pagination-numbers', - 'core/comments', - 'core/comments-author-name', - 'core/comment-content', - 'core/comment-date', - 'core/comment-edit-link', - 'core/comment-reply-link', - 'core/comment-template', - 'core/post-comments-count', - 'core/post-comments-link', - ], - }, - ], + name: 'theme', + title: __( 'Theme' ), + subcategories: STYLE_BOOK_THEME_SUBCATEGORIES, }, { - category: 'media', - label: __( 'Media' ), - include: [ 'core/post-featured-image' ], + name: 'media', + title: __( 'Media' ), + blocks: [ 'core/post-featured-image' ], }, { - category: 'widgets', - label: __( 'Widgets' ), - include: [], + name: 'widgets', + title: __( 'Widgets' ), + blocks: [], }, /* { - category: 'embed', - label: __( 'Embeds' ), + name: 'embed', + title: __( 'Embeds' ), include: [], },*/ ]; + diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js index 64503dcf7a6db..49e296da911ab 100644 --- a/packages/edit-site/src/components/style-book/index.js +++ b/packages/edit-site/src/components/style-book/index.js @@ -37,6 +37,7 @@ import { ENTER, SPACE } from '@wordpress/keycodes'; */ import { unlock } from '../../lock-unlock'; import EditorCanvasContainer from '../editor-canvas-container'; +import { STYLE_BOOK_CATEGORIES } from './constants'; const { ExperimentalBlockEditorProvider, @@ -184,17 +185,11 @@ function StyleBook( { const [ examples ] = useState( getExamples ); const tabs = useMemo( () => - getCategories() - .filter( ( category ) => - examples.some( - ( example ) => example.category === category.slug - ) + STYLE_BOOK_CATEGORIES.filter( ( category ) => + examples.some( + ( example ) => example.category === category.name ) - .map( ( category ) => ( { - name: category.slug, - title: category.title, - icon: category.icon, - } ) ), + ), [ examples ] ); const { base: baseConfig } = useContext( GlobalStylesContext ); @@ -379,6 +374,7 @@ const StyleBookBody = ( { const Examples = memo( ( { className, examples, category, label, isSelected, onSelect } ) => { + return ( { - it( 'returns category key value pairs', () => { +// Fixtures +/*const exampleBlocks = [ + { + name: 'core/post-content', + title: 'Post Content', + category: 'theme', + }, + { + name: 'core/home-link', + title: 'Home Link', + category: 'design', + }, + { + name: 'custom/colors', + title: 'Colors', + category: 'colors', + }, + { + name: 'core/site-logo', + title: 'Site Logo', + category: 'theme', + }, + { + name: 'core/site-title', + title: 'Site Title', + category: 'theme', + }, + { + name: 'core/site-tagline', + title: 'Site Tagline', + category: 'theme', + }, + { + name: 'core/group', + title: 'Group', + category: 'design', + }, + { + name: 'core/comments-pagination-numbers', + title: 'Comments Page Numbers', + category: 'theme', + }, + { + name: 'core/post-featured-image', + title: 'Featured Image', + category: 'theme', + }, +];*/ + +describe( 'utils', () => { + beforeAll( () => { + // Register all core blocks + registerCoreBlocks(); + } ); - //expect( ).toEqual( ); + afterAll( () => { + // Clean up registered blocks + getBlockTypes().forEach( ( block ) => { + unregisterBlockType( block.name ); + } ); } ); + + describe( 'getCategoryExamples', () => { + it( 'returns category key value pairs', () => { + const themeCategory = STYLE_BOOK_CATEGORIES.find( + ( category ) => category.name === 'theme' + ); + const themeCategoryExamples = getCategoryExamples( + themeCategory, + getExamples() + ); + expect( + themeCategoryExamples[ 'site-identity' ].examples + ).toBeDefined(); + expect( themeCategoryExamples.design.examples ).toBeDefined(); + expect( themeCategoryExamples.posts.examples ).toBeDefined(); + expect( themeCategoryExamples.comments.examples ).toBeDefined(); + } ); + } ); + +/* describe( 'getBlockCategories', () => { + it( 'returns categories', () => { + expect( getBlockCategories( getExamples() ) ).toEqual( ' ' ); + } ); + } );*/ } ); diff --git a/packages/edit-site/src/components/style-book/utils.js b/packages/edit-site/src/components/style-book/utils.js index 0726f4b43c29f..d54948560ec47 100644 --- a/packages/edit-site/src/components/style-book/utils.js +++ b/packages/edit-site/src/components/style-book/utils.js @@ -13,6 +13,7 @@ import { getBlockFromExample, createBlock, } from '@wordpress/blocks'; +import { __experimentalGetCoreBlocks } from '@wordpress/block-library'; import { privateApis as editorPrivateApis } from '@wordpress/editor'; import { useMemo, useState, memo, useContext } from '@wordpress/element'; @@ -20,7 +21,7 @@ import { useMemo, useState, memo, useContext } from '@wordpress/element'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; -import { STYLE_BOOK_CATEGORIES } from './constants'; +import { STYLE_BOOK_CATEGORIES, STYLE_BOOK_THEME_SUBCATEGORIES } from './constants'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; const { @@ -71,6 +72,29 @@ function getCustomExamples() { ]; } +// @TODO get other registered categories (aside from what's in STYLE_BOOK_CATEGORIES) +// And their subcategories. +export function getBlockCategories( examples ) { + const reservedCategories = [ + ...STYLE_BOOK_THEME_SUBCATEGORIES, + ...STYLE_BOOK_CATEGORIES, + ].map( ( category ) => category.category ); + + return getCategories() + .filter( + ( category ) => + ! reservedCategories.includes( category.slug ) && + examples.some( + ( example ) => example.category === category.slug + ) + ) + .map( ( category ) => ( { + name: category.slug, + title: category.title, + } ) ); +} + +// Get all examples from every registered block. export function getExamples() { const nonHeadingBlockExamples = getBlockTypes() .filter( ( blockType ) => { @@ -107,36 +131,37 @@ export function getExamples() { } ); } ), } - : []; + : {}; - return [ nonHeadingBlockExamples, ...headingsExample ]; + return [ headingsExample, ...nonHeadingBlockExamples ]; } -export function getCategoryExamples( category, examples ) { - if ( ! category || ! examples ) { +export function getCategoryExamples( categoryDefinition, examples ) { + if ( ! categoryDefinition?.name || ! examples?.length ) { return []; } - if ( category?.subCategories ) { - return category.subCategories.reduce( + if ( categoryDefinition?.subcategories?.length ) { + return categoryDefinition.subcategories.reduce( ( acc, subCategory ) => ( { ...acc, - ...getCategoryExamples( subCategory.category, examples ), + ...getCategoryExamples( subCategory, examples ), } ), {} ); } - const { include, exclude } = category; + const blocksToInclude = categoryDefinition?.blocks || []; + const blocksToExclude = categoryDefinition?.exclude || []; return { - [ category.category ]: { - label: category.label, + [ categoryDefinition.name ]: { + title: categoryDefinition.title, examples: examples.filter( ( example ) => { return ( - ! exclude.includes( example.name ) && - ( example.category === category || - include.includes( example.name ) ) + ! blocksToExclude.includes( example.name ) && + ( example.category === categoryDefinition.name || + blocksToInclude.includes( example.name ) ) ); } ), }, From df4912884c8c2ee27fe097993b2847cdbd7d049d Mon Sep 17 00:00:00 2001 From: ramon Date: Tue, 17 Sep 2024 16:52:55 +1000 Subject: [PATCH 06/15] Groups --- .../src/components/style-book/index.js | 49 +++++++++++++++++-- .../src/components/style-book/utils.js | 43 ++++++++++------ 2 files changed, 71 insertions(+), 21 deletions(-) diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js index 49e296da911ab..75092a6ffbc08 100644 --- a/packages/edit-site/src/components/style-book/index.js +++ b/packages/edit-site/src/components/style-book/index.js @@ -38,6 +38,7 @@ import { ENTER, SPACE } from '@wordpress/keycodes'; import { unlock } from '../../lock-unlock'; import EditorCanvasContainer from '../editor-canvas-container'; import { STYLE_BOOK_CATEGORIES } from './constants'; +import { getCategoryExamples } from './utils'; const { ExperimentalBlockEditorProvider, @@ -374,7 +375,15 @@ const StyleBookBody = ( { const Examples = memo( ( { className, examples, category, label, isSelected, onSelect } ) => { + const categoryDefinition = STYLE_BOOK_CATEGORIES.find( + ( c ) => c.name === category + ); + const filteredExamples = getCategoryExamples( + categoryDefinition, + examples + ); + console.log( { filteredExamples } ); return ( - { examples - .filter( ( example ) => - category ? example.category === category : true - ) - .map( ( example ) => ( + { !! filteredExamples.examples?.length && + filteredExamples.examples.map( ( example ) => ( ) ) } + { !! filteredExamples.subcategories?.length && + filteredExamples.subcategories.map( ( subcategory ) => ( + + + { subcategory.title } + + + + ) ) } ); } ); +const Subcategory = ( { examples, isSelected, onSelect } ) => { + return ( + !! examples?.length && + examples.map( ( example ) => ( + { + onSelect?.( example.name ); + } } + /> + ) ) + ); +}; + const Example = ( { id, title, blocks, isSelected, onClick } ) => { const originalSettings = useSelect( ( select ) => select( blockEditorStore ).getSettings(), diff --git a/packages/edit-site/src/components/style-book/utils.js b/packages/edit-site/src/components/style-book/utils.js index d54948560ec47..f770f92c7c9e9 100644 --- a/packages/edit-site/src/components/style-book/utils.js +++ b/packages/edit-site/src/components/style-book/utils.js @@ -21,7 +21,10 @@ import { useMemo, useState, memo, useContext } from '@wordpress/element'; * Internal dependencies */ import { unlock } from '../../lock-unlock'; -import { STYLE_BOOK_CATEGORIES, STYLE_BOOK_THEME_SUBCATEGORIES } from './constants'; +import { + STYLE_BOOK_CATEGORIES, + STYLE_BOOK_THEME_SUBCATEGORIES, +} from './constants'; import { privateApis as blockEditorPrivateApis } from '@wordpress/block-editor'; const { @@ -143,28 +146,36 @@ export function getCategoryExamples( categoryDefinition, examples ) { if ( categoryDefinition?.subcategories?.length ) { return categoryDefinition.subcategories.reduce( - ( acc, subCategory ) => ( { - ...acc, - ...getCategoryExamples( subCategory, examples ), - } ), - {} + ( acc, subcategory ) => { + acc.subcategories.push( + getCategoryExamples( subcategory, examples ) + ); + return acc; + }, + { + title: categoryDefinition.title, + name: categoryDefinition.name, + subcategories: [], + } ); } const blocksToInclude = categoryDefinition?.blocks || []; const blocksToExclude = categoryDefinition?.exclude || []; + // @TODO maybe return an array. [ { label: string, examples: [] } ] + // With subcategories { subcategories: [ { title: string, examples: [] } ] } + // With single category examples { title: string, examples: [] } return { - [ categoryDefinition.name ]: { - title: categoryDefinition.title, - examples: examples.filter( ( example ) => { - return ( - ! blocksToExclude.includes( example.name ) && - ( example.category === categoryDefinition.name || - blocksToInclude.includes( example.name ) ) - ); - } ), - }, + title: categoryDefinition.title, + name: categoryDefinition.name, + examples: examples.filter( ( example ) => { + return ( + ! blocksToExclude.includes( example.name ) && + ( example.category === categoryDefinition.name || + blocksToInclude.includes( example.name ) ) + ); + } ), }; } From 55af6f393925ff690ec4bc3ab216a6f847900982 Mon Sep 17 00:00:00 2001 From: ramon Date: Wed, 18 Sep 2024 15:43:41 +1000 Subject: [PATCH 07/15] Clean up experiments and unused code for MVP --- .../src/components/style-book/constants.js | 95 +++++++++++- .../src/components/style-book/index.js | 146 +----------------- .../src/components/style-book/test/utils.js | 67 +------- .../src/components/style-book/utils.js | 137 +++------------- 4 files changed, 128 insertions(+), 317 deletions(-) diff --git a/packages/edit-site/src/components/style-book/constants.js b/packages/edit-site/src/components/style-book/constants.js index c7509326bd03e..99d6855f01a07 100644 --- a/packages/edit-site/src/components/style-book/constants.js +++ b/packages/edit-site/src/components/style-book/constants.js @@ -62,7 +62,7 @@ export const STYLE_BOOK_THEME_SUBCATEGORIES = [ onClick: function to call when the category's blocks are clicked. Overrides the default behavior. */ export const STYLE_BOOK_CATEGORIES = [ -/* { + /* { // Clicking examples on the landing tab // will take you to the corresponding // tab in the style book. E.g., image > media @@ -106,11 +106,98 @@ export const STYLE_BOOK_CATEGORIES = [ title: __( 'Widgets' ), blocks: [], }, - -/* { + { name: 'embed', title: __( 'Embeds' ), include: [], - },*/ + }, ]; +// The content area of the Style Book is rendered within an iframe so that global styles +// are applied to elements within the entire content area. To support elements that are +// not part of the block previews, such as headings and layout for the block previews, +// additional CSS rules need to be passed into the iframe. These are hard-coded below. +// Note that button styles are unset, and then focus rules from the `Button` component are +// applied to the `button` element, targeted via `.edit-site-style-book__example`. +// This is to ensure that browser default styles for buttons are not applied to the previews. +export const STYLE_BOOK_IFRAME_STYLES = ` + // Forming a "block formatting context" to prevent margin collapsing. + // @see https://developer.mozilla.org/en-US/docs/Web/Guide/CSS/Block_formatting_context + .is-root-container { + display: flow-root; + } + + body { + position: relative; + padding: 32px !important; + } + + .edit-site-style-book__examples { + max-width: 1200px; + margin: 0 auto; + } + + .edit-site-style-book__example { + max-width: 900px + border-radius: 2px; + cursor: pointer; + display: flex; + flex-direction: column; + gap: 40px; + margin-bottom: 40px; + padding: 16px; + width: 100%; + box-sizing: border-box; + scroll-margin-top: 32px; + scroll-margin-bottom: 32px; + } + + .edit-site-style-book__example.is-selected { + box-shadow: 0 0 0 1px var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); + } + + .edit-site-style-book__example:focus:not(:disabled) { + box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); + outline: 3px solid transparent; + } + + .edit-site-style-book__examples.is-wide .edit-site-style-book__example { + flex-direction: row; + } + + .edit-site-style-book__example-title { + font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; + font-size: 11px; + font-weight: 500; + line-height: normal; + margin: 0; + text-align: left; + text-transform: uppercase; + } + + .edit-site-style-book__examples.is-wide .edit-site-style-book__example-title { + text-align: right; + width: 120px; + } + + .edit-site-style-book__example-preview { + width: 100%; + } + + .edit-site-style-book__example-preview .block-editor-block-list__insertion-point, + .edit-site-style-book__example-preview .block-list-appender { + display: none; + } + + .edit-site-style-book__example-preview .is-root-container > .wp-block:first-child { + margin-top: 0; + } + .edit-site-style-book__example-preview .is-root-container > .wp-block:last-child { + margin-bottom: 0; + } + .edit-site-style-book__subcategory-label { + margin-bottom: 40px; + border-bottom: 1px solid #ddd; + padding-bottom: 8px; + } +`; diff --git a/packages/edit-site/src/components/style-book/index.js b/packages/edit-site/src/components/style-book/index.js index 75092a6ffbc08..05d0565e3bfce 100644 --- a/packages/edit-site/src/components/style-book/index.js +++ b/packages/edit-site/src/components/style-book/index.js @@ -12,13 +12,6 @@ import { privateApis as componentsPrivateApis, } from '@wordpress/components'; import { __, sprintf } from '@wordpress/i18n'; -import { - getCategories, - getBlockType, - getBlockTypes, - getBlockFromExample, - createBlock, -} from '@wordpress/blocks'; import { BlockList, privateApis as blockEditorPrivateApis, @@ -37,8 +30,8 @@ import { ENTER, SPACE } from '@wordpress/keycodes'; */ import { unlock } from '../../lock-unlock'; import EditorCanvasContainer from '../editor-canvas-container'; -import { STYLE_BOOK_CATEGORIES } from './constants'; -import { getCategoryExamples } from './utils'; +import { STYLE_BOOK_CATEGORIES, STYLE_BOOK_IFRAME_STYLES } from './constants'; +import { getCategoryExamples, getExamples } from './utils'; const { ExperimentalBlockEditorProvider, @@ -50,126 +43,10 @@ const { mergeBaseAndUserConfigs } = unlock( editorPrivateApis ); const { Tabs } = unlock( componentsPrivateApis ); -// The content area of the Style Book is rendered within an iframe so that global styles -// are applied to elements within the entire content area. To support elements that are -// not part of the block previews, such as headings and layout for the block previews, -// additional CSS rules need to be passed into the iframe. These are hard-coded below. -// Note that button styles are unset, and then focus rules from the `Button` component are -// applied to the `button` element, targeted via `.edit-site-style-book__example`. -// This is to ensure that browser default styles for buttons are not applied to the previews. -const STYLE_BOOK_IFRAME_STYLES = ` - .edit-site-style-book__examples { - max-width: 900px; - margin: 0 auto; - } - - .edit-site-style-book__example { - border-radius: 2px; - cursor: pointer; - display: flex; - flex-direction: column; - gap: 40px; - margin-bottom: 40px; - padding: 16px; - width: 100%; - box-sizing: border-box; - scroll-margin-top: 32px; - scroll-margin-bottom: 32px; - } - - .edit-site-style-book__example.is-selected { - box-shadow: 0 0 0 1px var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); - } - - .edit-site-style-book__example:focus:not(:disabled) { - box-shadow: 0 0 0 var(--wp-admin-border-width-focus) var(--wp-components-color-accent, var(--wp-admin-theme-color, #007cba)); - outline: 3px solid transparent; - } - - .edit-site-style-book__examples.is-wide .edit-site-style-book__example { - flex-direction: row; - } - - .edit-site-style-book__example-title { - font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Oxygen-Sans, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; - font-size: 11px; - font-weight: 500; - line-height: normal; - margin: 0; - text-align: left; - text-transform: uppercase; - } - - .edit-site-style-book__examples.is-wide .edit-site-style-book__example-title { - text-align: right; - width: 120px; - } - - .edit-site-style-book__example-preview { - width: 100%; - } - - .edit-site-style-book__example-preview .block-editor-block-list__insertion-point, - .edit-site-style-book__example-preview .block-list-appender { - display: none; - } - - .edit-site-style-book__example-preview .is-root-container > .wp-block:first-child { - margin-top: 0; - } - .edit-site-style-book__example-preview .is-root-container > .wp-block:last-child { - margin-bottom: 0; - } -`; - function isObjectEmpty( object ) { return ! object || Object.keys( object ).length === 0; } -function getExamples() { - const nonHeadingBlockExamples = getBlockTypes() - .filter( ( blockType ) => { - const { name, example, supports } = blockType; - return ( - name !== 'core/heading' && - !! example && - supports.inserter !== false - ); - } ) - .map( ( blockType ) => ( { - name: blockType.name, - title: blockType.title, - category: blockType.category, - blocks: getBlockFromExample( blockType.name, blockType.example ), - } ) ); - - const isHeadingBlockRegistered = !! getBlockType( 'core/heading' ); - - if ( ! isHeadingBlockRegistered ) { - return nonHeadingBlockExamples; - } - - // Use our own example for the Heading block so that we can show multiple - // heading levels. - const headingsExample = { - name: 'core/heading', - title: __( 'Headings' ), - category: 'text', - blocks: [ 1, 2, 3, 4, 5, 6 ].map( ( level ) => { - return createBlock( 'core/heading', { - content: sprintf( - // translators: %d: heading level e.g: "1", "2", "3" - __( 'Heading %d' ), - level - ), - level, - } ); - } ), - }; - - return [ headingsExample, ...nonHeadingBlockExamples ]; -} - function StyleBook( { enableResizing = true, isSelected, @@ -325,10 +202,6 @@ const StyleBookBody = ( { readonly: true, }; - const buttonModeStyles = onClick - ? 'body { cursor: pointer; } body * { pointer-events: none; }' - : ''; - return (