From d1f19bbbd47b790305116643e621dcd39256c9e3 Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Sun, 4 Oct 2026 18:29:35 +0530 Subject: [PATCH 1/3] Icon block: Show collection name in the "All" tab of the icon library --- .../components/custom-inserter/icon-grid.jsx | 38 ++++++++++++++----- .../icon/components/custom-inserter/index.jsx | 5 +++ packages/block-library/src/icon/editor.scss | 14 ++++++- 3 files changed, 46 insertions(+), 11 deletions(-) diff --git a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx index 5f145b57ba16f1..d7104a85ef08c4 100644 --- a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx +++ b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx @@ -1,4 +1,5 @@ -import { __ } from '@wordpress/i18n'; +import clsx from 'clsx'; +import { __, sprintf } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; import { useAsyncList } from '@wordpress/compose'; import { useRef, useLayoutEffect } from '@wordpress/element'; @@ -7,7 +8,7 @@ import HtmlRenderer from '../../../utils/html-renderer'; const BATCH_SIZE = 20; -export default function IconGrid( { icons, onChange, value } ) { +export default function IconGrid( { icons, onChange, value, collections } ) { const shownIcons = useAsyncList( icons, { step: BATCH_SIZE, } ); @@ -46,18 +47,30 @@ export default function IconGrid( { icons, onChange, value } ) { aria-label={ __( 'Icon library' ) } > { shownIcons.map( ( icon ) => { + const isSelected = icon.name === value; + + const collectionLabel = collections?.find( + ( { slug } ) => slug === icon.collection + )?.label; return ( ); } ) } diff --git a/packages/block-library/src/icon/components/custom-inserter/index.jsx b/packages/block-library/src/icon/components/custom-inserter/index.jsx index 86d74f834d4f99..3a3730d58b9031 100644 --- a/packages/block-library/src/icon/components/custom-inserter/index.jsx +++ b/packages/block-library/src/icon/components/custom-inserter/index.jsx @@ -128,6 +128,11 @@ export default function CustomInserterModal( { onClose, value, onChange } ) { icons={ filteredIcons } onChange={ onChange } value={ value } + collections={ + collectionSlug === '' + ? collections + : undefined + } /> ) } diff --git a/packages/block-library/src/icon/editor.scss b/packages/block-library/src/icon/editor.scss index da9f47c3096a70..0db6981b4e9408 100644 --- a/packages/block-library/src/icon/editor.scss +++ b/packages/block-library/src/icon/editor.scss @@ -65,10 +65,22 @@ .wp-block-icon__inserter-grid-icons-list-item-title { font-size: $font-size-small; - padding: $grid-unit-05 $grid-unit-05 $grid-unit-10; + padding: $grid-unit-05; overflow-wrap: break-word; } +.wp-block-icon__inserter-grid-icons-list-item-collection { + font-size: $font-size-small; + font-weight: var(--wpds-typography-font-weight-default); + padding: 0 $grid-unit-05 $grid-unit-10; + overflow-wrap: break-word; +} + + +.wp-block-icon__inserter-grid-icons-list-item:not(.is-selected) .wp-block-icon__inserter-grid-icons-list-item-collection { + color: var(--wpds-color-foreground-content-neutral-weak); +} + .wp-block-icon__toolbar-content { width: 250px; } From 3861417b145a9f3afb04f19ff9e5cfc8f52c4fb1 Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Wed, 7 Oct 2026 15:51:55 +0530 Subject: [PATCH 2/3] Icon block: Group the "All" tab of the icon library by collection --- packages/block-library/CHANGELOG.md | 1 + .../components/custom-inserter/icon-grid.jsx | 149 +++++++++++------- .../icon/components/custom-inserter/index.jsx | 3 +- .../icon/components/custom-inserter/utils.js | 21 +++ packages/block-library/src/icon/editor.scss | 20 +-- 5 files changed, 127 insertions(+), 67 deletions(-) create mode 100644 packages/block-library/src/icon/components/custom-inserter/utils.js diff --git a/packages/block-library/CHANGELOG.md b/packages/block-library/CHANGELOG.md index 46bea484577a0a..a43bc9f241feb5 100644 --- a/packages/block-library/CHANGELOG.md +++ b/packages/block-library/CHANGELOG.md @@ -4,6 +4,7 @@ ### Enhancements +- Icon: Group the "All" tab of the icon library by collection, under a heading that stays at the top while scrolling, when more than one collection is registered. - Navigation Link: Search every entity type from the link UI instead of only the link's own type; so a category, tag or post can be added without needing to add that specific type's navigation link block ([#83408](https://github.com/WordPress/gutenberg/pull/83408)). - Navigation Link: List the blocks a Navigation allows in the link UI search results, so typing "Home" finds the Home Link block ([#83652](https://github.com/WordPress/gutenberg/pull/83652)). - Cover: Don't autoplay an embedded background video on the front end when the visitor prefers reduced motion. A new view module swaps the iframe source for one without the autoplay parameters ([#83452](https://github.com/WordPress/gutenberg/pull/83452)). diff --git a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx index d7104a85ef08c4..39eece51610fa8 100644 --- a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx +++ b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx @@ -1,15 +1,31 @@ -import clsx from 'clsx'; -import { __, sprintf } from '@wordpress/i18n'; +import { __ } from '@wordpress/i18n'; import { Button } from '@wordpress/components'; -import { useAsyncList } from '@wordpress/compose'; -import { useRef, useLayoutEffect } from '@wordpress/element'; +import { useAsyncList, useInstanceId } from '@wordpress/compose'; +import { useRef, useLayoutEffect, useMemo } from '@wordpress/element'; import { getScrollContainer } from '@wordpress/dom'; import HtmlRenderer from '../../../utils/html-renderer'; +import { groupIconsByCollection } from './utils'; const BATCH_SIZE = 20; export default function IconGrid( { icons, onChange, value, collections } ) { - const shownIcons = useAsyncList( icons, { + const instanceId = useInstanceId( + IconGrid, + 'wp-block-icon__inserter-grid' + ); + + const { groups, orderedIcons } = useMemo( () => { + if ( ! icons?.length || ! collections ) { + return { groups: null, orderedIcons: icons }; + } + const iconGroups = groupIconsByCollection( icons, collections ); + return { + groups: iconGroups, + orderedIcons: iconGroups.flatMap( ( group ) => group.icons ), + }; + }, [ icons, collections ] ); + + const shownIcons = useAsyncList( orderedIcons, { step: BATCH_SIZE, } ); @@ -18,11 +34,11 @@ export default function IconGrid( { icons, onChange, value, collections } ) { // if the user has already scrolled the list. const selectedIconRef = useRef(); const selectedIndex = - icons?.findIndex( ( icon ) => icon.name === value ) ?? -1; + orderedIcons?.findIndex( ( icon ) => icon.name === value ) ?? -1; const isReadyToScroll = selectedIndex >= 0 && ( shownIcons.length >= selectedIndex + BATCH_SIZE || - shownIcons.length === icons.length ); + shownIcons.length === orderedIcons.length ); useLayoutEffect( () => { const node = selectedIconRef.current; @@ -35,60 +51,81 @@ export default function IconGrid( { icons, onChange, value, collections } ) { node.scrollIntoView( { block: 'center' } ); }, [ isReadyToScroll ] ); - return ( -
- { ! icons?.length ? ( + const renderIcons = ( iconsToRender, label ) => ( +
+ { iconsToRender.map( ( icon ) => ( + + ) ) } +
+ ); + + const renderGroups = () => { + let offset = 0; + return groups.map( ( group ) => { + const start = offset; + offset += group.icons.length; + // Show this group's icons, as many as have loaded so far. + const shownGroupIcons = group.icons.slice( + 0, + Math.max( 0, shownIcons.length - start ) + ); + if ( ! shownGroupIcons.length ) { + return null; + } + const headingId = `${ instanceId }-${ group.slug }`; + return ( +
+

+ { group.label } +

+ { renderIcons( shownGroupIcons ) } +
+ ); + } ); + }; + + if ( ! icons?.length ) { + return ( +

{ __( 'No results found.' ) }

- ) : ( -
- { shownIcons.map( ( icon ) => { - const isSelected = icon.name === value; +
+ ); + } - const collectionLabel = collections?.find( - ( { slug } ) => slug === icon.collection - )?.label; - return ( - - ); - } ) } + return ( +
+ { groups ? ( +
+ { renderGroups() }
+ ) : ( + renderIcons( shownIcons, __( 'Icon library' ) ) ) }
); diff --git a/packages/block-library/src/icon/components/custom-inserter/index.jsx b/packages/block-library/src/icon/components/custom-inserter/index.jsx index 4c10ae1ee08f27..61415f36f5bb37 100644 --- a/packages/block-library/src/icon/components/custom-inserter/index.jsx +++ b/packages/block-library/src/icon/components/custom-inserter/index.jsx @@ -133,7 +133,8 @@ export default function CustomInserterModal( { onClose, value, onChange } ) { onChange={ onChange } value={ value } collections={ - collectionSlug === '' + collectionSlug === '' && + collections?.length > 1 ? collections : undefined } diff --git a/packages/block-library/src/icon/components/custom-inserter/utils.js b/packages/block-library/src/icon/components/custom-inserter/utils.js new file mode 100644 index 00000000000000..71fcd017023e40 --- /dev/null +++ b/packages/block-library/src/icon/components/custom-inserter/utils.js @@ -0,0 +1,21 @@ +/** + * Groups icons by collection, in the order of `collections`. + * + * @param {Object[]} icons Icons, each with a `collection` slug. + * @param {Object[]} collections Icon collections, each with a `slug` and `label`. + * @return {Object[]} Groups, each with a `slug`, `label` and `icons`. + */ +export function groupIconsByCollection( icons, collections ) { + const groups = new Map( + collections.map( ( { slug, label } ) => [ + slug, + { slug, label, icons: [] }, + ] ) + ); + + icons.forEach( ( icon ) => { + groups.get( icon.collection )?.icons.push( icon ); + } ); + + return [ ...groups.values() ].filter( ( group ) => group.icons.length ); +} diff --git a/packages/block-library/src/icon/editor.scss b/packages/block-library/src/icon/editor.scss index 0db6981b4e9408..1e627edcbfc666 100644 --- a/packages/block-library/src/icon/editor.scss +++ b/packages/block-library/src/icon/editor.scss @@ -65,20 +65,20 @@ .wp-block-icon__inserter-grid-icons-list-item-title { font-size: $font-size-small; - padding: $grid-unit-05; + padding: $grid-unit-05 $grid-unit-05 $grid-unit-10; overflow-wrap: break-word; } -.wp-block-icon__inserter-grid-icons-list-item-collection { - font-size: $font-size-small; - font-weight: var(--wpds-typography-font-weight-default); - padding: 0 $grid-unit-05 $grid-unit-10; - overflow-wrap: break-word; -} - - -.wp-block-icon__inserter-grid-icons-list-item:not(.is-selected) .wp-block-icon__inserter-grid-icons-list-item-collection { +.wp-block-icon__inserter-modal .wp-block-icon__inserter-grid-group-title { + position: sticky; + top: -$grid-unit-05; + z-index: 1; + padding: $grid-unit-15 $grid-unit-05; + margin: 0; color: var(--wpds-color-foreground-content-neutral-weak); + text-transform: uppercase; + font-size: var(--wpds-typography-font-size-sm); + background-color: $white; } .wp-block-icon__toolbar-content { From eb88a30c40ecec4eeacf4222e612e4faa45b842d Mon Sep 17 00:00:00 2001 From: PRASAD KARMALKAR Date: Sat, 10 Oct 2026 13:57:56 +0530 Subject: [PATCH 3/3] Icon: Fix sticky group heading shift and remove ineffective aria-label --- .../icon/components/custom-inserter/icon-grid.jsx | 15 +++------------ packages/block-library/src/icon/editor.scss | 4 ++++ 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx index 39eece51610fa8..502b5272c99741 100644 --- a/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx +++ b/packages/block-library/src/icon/components/custom-inserter/icon-grid.jsx @@ -51,11 +51,8 @@ export default function IconGrid( { icons, onChange, value, collections } ) { node.scrollIntoView( { block: 'center' } ); }, [ isReadyToScroll ] ); - const renderIcons = ( iconsToRender, label ) => ( -
+ const renderIcons = ( iconsToRender ) => ( +
{ iconsToRender.map( ( icon ) => (
); } diff --git a/packages/block-library/src/icon/editor.scss b/packages/block-library/src/icon/editor.scss index 1e627edcbfc666..26585f1fbf5bb4 100644 --- a/packages/block-library/src/icon/editor.scss +++ b/packages/block-library/src/icon/editor.scss @@ -81,6 +81,10 @@ background-color: $white; } +.wp-block-icon__inserter-modal .wp-block-icon__inserter-grid > :first-child > .wp-block-icon__inserter-grid-group-title { + margin-top: -$grid-unit-05; +} + .wp-block-icon__toolbar-content { width: 250px; }