From f18f02e740e93e093b1c89b42ad09b36976b0305 Mon Sep 17 00:00:00 2001 From: Rinkal Pagdar <92097119+rinkalpagdar@users.noreply.github.com> Date: Fri, 13 Dec 2024 16:32:50 +0530 Subject: [PATCH 1/5] Refactor Settings panel of Navigation Item block --- package-lock.json | 1 - .../block-library/src/navigation-link/edit.js | 537 +++++++++--------- 2 files changed, 283 insertions(+), 255 deletions(-) diff --git a/package-lock.json b/package-lock.json index 32ff2db4986512..c3005e83127e96 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45192,7 +45192,6 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, - "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 39073b848d3ca8..c1219401643d33 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -9,12 +9,13 @@ import clsx from 'clsx'; import { createBlock } from '@wordpress/blocks'; import { useSelect, useDispatch } from '@wordpress/data'; import { - PanelBody, TextControl, TextareaControl, ToolbarButton, Tooltip, ToolbarGroup, + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { displayShortcut, isKeyboardEvent } from '@wordpress/keycodes'; import { __ } from '@wordpress/i18n'; @@ -53,61 +54,61 @@ const DEFAULT_BLOCK = { name: 'core/navigation-link' }; * * @return {boolean} Is dragging within the target element. */ -const useIsDraggingWithin = ( elementRef ) => { - const [ isDraggingWithin, setIsDraggingWithin ] = useState( false ); +const useIsDraggingWithin = (elementRef) => { + const [isDraggingWithin, setIsDraggingWithin] = useState(false); - useEffect( () => { + useEffect(() => { const { ownerDocument } = elementRef.current; - function handleDragStart( event ) { + function handleDragStart(event) { // Check the first time when the dragging starts. - handleDragEnter( event ); + handleDragEnter(event); } // Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape. function handleDragEnd() { - setIsDraggingWithin( false ); + setIsDraggingWithin(false); } - function handleDragEnter( event ) { + function handleDragEnter(event) { // Check if the current target is inside the item element. - if ( elementRef.current.contains( event.target ) ) { - setIsDraggingWithin( true ); + if (elementRef.current.contains(event.target)) { + setIsDraggingWithin(true); } else { - setIsDraggingWithin( false ); + setIsDraggingWithin(false); } } // Bind these events to the document to catch all drag events. // Ideally, we can also use `event.relatedTarget`, but sadly that // doesn't work in Safari. - ownerDocument.addEventListener( 'dragstart', handleDragStart ); - ownerDocument.addEventListener( 'dragend', handleDragEnd ); - ownerDocument.addEventListener( 'dragenter', handleDragEnter ); + ownerDocument.addEventListener('dragstart', handleDragStart); + ownerDocument.addEventListener('dragend', handleDragEnd); + ownerDocument.addEventListener('dragenter', handleDragEnter); return () => { - ownerDocument.removeEventListener( 'dragstart', handleDragStart ); - ownerDocument.removeEventListener( 'dragend', handleDragEnd ); - ownerDocument.removeEventListener( 'dragenter', handleDragEnter ); + ownerDocument.removeEventListener('dragstart', handleDragStart); + ownerDocument.removeEventListener('dragend', handleDragEnd); + ownerDocument.removeEventListener('dragenter', handleDragEnter); }; - }, [ elementRef ] ); + }, [elementRef]); return isDraggingWithin; }; -const useIsInvalidLink = ( kind, type, id ) => { +const useIsInvalidLink = (kind, type, id) => { const isPostType = kind === 'post-type' || type === 'post' || type === 'page'; - const hasId = Number.isInteger( id ); + const hasId = Number.isInteger(id); const postStatus = useSelect( - ( select ) => { - if ( ! isPostType ) { + (select) => { + if (!isPostType) { return null; } - const { getEntityRecord } = select( coreStore ); - return getEntityRecord( 'postType', type, id )?.status; + const { getEntityRecord } = select(coreStore); + return getEntityRecord('postType', type, id)?.status; }, - [ isPostType, type, id ] + [isPostType, type, id] ); // Check Navigation Link validity if: @@ -122,32 +123,32 @@ const useIsInvalidLink = ( kind, type, id ) => { isPostType && hasId && postStatus && 'trash' === postStatus; const isDraft = 'draft' === postStatus; - return [ isInvalid, isDraft ]; + return [isInvalid, isDraft]; }; -function getMissingText( type ) { +function getMissingText(type) { let missingText = ''; - switch ( type ) { + switch (type) { case 'post': /* translators: label for missing post in navigation link block */ - missingText = __( 'Select post' ); + missingText = __('Select post'); break; case 'page': /* translators: label for missing page in navigation link block */ - missingText = __( 'Select page' ); + missingText = __('Select page'); break; case 'category': /* translators: label for missing category in navigation link block */ - missingText = __( 'Select category' ); + missingText = __('Select category'); break; case 'tag': /* translators: label for missing tag in navigation link block */ - missingText = __( 'Select tag' ); + missingText = __('Select tag'); break; default: /* translators: label for missing values in navigation link block */ - missingText = __( 'Add link' ); + missingText = __('Add link'); } return missingText; @@ -158,78 +159,108 @@ function getMissingText( type ) { * packages/block-library/src/navigation-submenu/edit.js * Consider reuseing this components for both blocks. */ -function Controls( { attributes, setAttributes, setIsLabelFieldFocused } ) { +function Controls({ attributes, setAttributes, setIsLabelFieldFocused }) { const { label, url, description, title, rel } = attributes; return ( - - { - setAttributes( { label: labelValue } ); - } } - label={ __( 'Text' ) } - autoComplete="off" - onFocus={ () => setIsLabelFieldFocused( true ) } - onBlur={ () => setIsLabelFieldFocused( false ) } - /> - { - updateAttributes( - { url: urlValue }, - setAttributes, - attributes - ); - } } - label={ __( 'Link' ) } - autoComplete="off" - /> - { - setAttributes( { description: descriptionValue } ); - } } - label={ __( 'Description' ) } - help={ __( - 'The description will be displayed in the menu if the current theme supports it.' - ) } - /> - { - setAttributes( { title: titleValue } ); - } } - label={ __( 'Title attribute' ) } - autoComplete="off" - help={ __( - 'Additional information to help clarify the purpose of the link.' - ) } - /> - { - setAttributes( { rel: relValue } ); - } } - label={ __( 'Rel attribute' ) } - autoComplete="off" - help={ __( - 'The relationship of the linked URL as space-separated link types.' - ) } - /> - + + !!label} + label={__('Text')} + onDeselect={() => setAttributes({ label: '' })} + > + { + setAttributes({ label: labelValue }); + }} + autoComplete="off" + onFocus={() => setIsLabelFieldFocused(true)} + onBlur={() => setIsLabelFieldFocused(false)} + /> + + + !!url} + label={__('Link')} + onDeselect={() => setAttributes({ url: '' })} + > + { + updateAttributes({ url: urlValue }, setAttributes, attributes); + }} + autoComplete="off" + /> + + + !!description} + label={__('Description')} + onDeselect={() => setAttributes({ description: '' })} + > + { + setAttributes({ description: descriptionValue }); + }} + help={__( + 'The description will be displayed in the menu if the current theme supports it.' + )} + /> + + + !!title} + label={__('Title attribute')} + onDeselect={() => setAttributes({ title: '' })} + > + { + setAttributes({ title: titleValue }); + }} + autoComplete="off" + help={__( + 'Additional information to help clarify the purpose of the link.' + )} + /> + + + !!rel} + label={__('Rel attribute')} + onDeselect={() => setAttributes({ rel: '' })} + > + { + setAttributes({ rel: relValue }); + }} + autoComplete="off" + help={__( + 'The relationship of the linked URL as space-separated link types.' + )} + /> + + ); } -export default function NavigationLinkEdit( { +export default function NavigationLinkEdit({ attributes, isSelected, setAttributes, @@ -238,10 +269,10 @@ export default function NavigationLinkEdit( { onReplace, context, clientId, -} ) { +}) { const { id, label, type, url, description, kind } = attributes; - const [ isInvalid, isDraft ] = useIsInvalidLink( kind, type, id ); + const [isInvalid, isDraft] = useIsInvalidLink(kind, type, id); const { maxNestingLevel } = context; const { @@ -249,24 +280,24 @@ export default function NavigationLinkEdit( { __unstableMarkNextChangeAsNotPersistent, selectBlock, selectPreviousBlock, - } = useDispatch( blockEditorStore ); + } = useDispatch(blockEditorStore); // Have the link editing ui open on mount when lacking a url and selected. - const [ isLinkOpen, setIsLinkOpen ] = useState( isSelected && ! url ); + const [isLinkOpen, setIsLinkOpen] = useState(isSelected && !url); // Store what element opened the popover, so we know where to return focus to (toolbar button vs navigation link text) - const [ openedBy, setOpenedBy ] = useState( null ); + const [openedBy, setOpenedBy] = useState(null); // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. - const [ popoverAnchor, setPopoverAnchor ] = useState( null ); - const listItemRef = useRef( null ); - const isDraggingWithin = useIsDraggingWithin( listItemRef ); - const itemLabelPlaceholder = __( 'Add label…' ); + const [popoverAnchor, setPopoverAnchor] = useState(null); + const listItemRef = useRef(null); + const isDraggingWithin = useIsDraggingWithin(listItemRef); + const itemLabelPlaceholder = __('Add label…'); const ref = useRef(); const linkUIref = useRef(); - const prevUrl = usePrevious( url ); + const prevUrl = usePrevious(url); // Change the label using inspector causes rich text to change focus on firefox. // This is a workaround to keep the focus on the label field when label filed is focused we don't render the rich text. - const [ isLabelFieldFocused, setIsLabelFieldFocused ] = useState( false ); + const [isLabelFieldFocused, setIsLabelFieldFocused] = useState(false); const { isAtMaxNesting, @@ -274,76 +305,76 @@ export default function NavigationLinkEdit( { isParentOfSelectedBlock, hasChildren, } = useSelect( - ( select ) => { + (select) => { const { getBlockCount, getBlockName, getBlockRootClientId, hasSelectedInnerBlock, getBlockParentsByBlockName, - } = select( blockEditorStore ); + } = select(blockEditorStore); return { isAtMaxNesting: - getBlockParentsByBlockName( clientId, [ + getBlockParentsByBlockName(clientId, [ 'core/navigation-link', 'core/navigation-submenu', - ] ).length >= maxNestingLevel, + ]).length >= maxNestingLevel, isTopLevelLink: - getBlockName( getBlockRootClientId( clientId ) ) === + getBlockName(getBlockRootClientId(clientId)) === 'core/navigation', isParentOfSelectedBlock: hasSelectedInnerBlock( clientId, true ), - hasChildren: !! getBlockCount( clientId ), + hasChildren: !!getBlockCount(clientId), }; }, - [ clientId, maxNestingLevel ] + [clientId, maxNestingLevel] ); - const { getBlocks } = useSelect( blockEditorStore ); + const { getBlocks } = useSelect(blockEditorStore); /** * Transform to submenu block. */ const transformToSubmenu = () => { - let innerBlocks = getBlocks( clientId ); - if ( innerBlocks.length === 0 ) { - innerBlocks = [ createBlock( 'core/navigation-link' ) ]; - selectBlock( innerBlocks[ 0 ].clientId ); + let innerBlocks = getBlocks(clientId); + if (innerBlocks.length === 0) { + innerBlocks = [createBlock('core/navigation-link')]; + selectBlock(innerBlocks[0].clientId); } const newSubmenu = createBlock( 'core/navigation-submenu', attributes, innerBlocks ); - replaceBlock( clientId, newSubmenu ); + replaceBlock(clientId, newSubmenu); }; - useEffect( () => { + useEffect(() => { // If block has inner blocks, transform to Submenu. - if ( hasChildren ) { + if (hasChildren) { // This side-effect should not create an undo level as those should // only be created via user interactions. __unstableMarkNextChangeAsNotPersistent(); transformToSubmenu(); } - }, [ hasChildren ] ); + }, [hasChildren]); // If the LinkControl popover is open and the URL has changed, close the LinkControl and focus the label text. - useEffect( () => { + useEffect(() => { // We only want to do this when the URL has gone from nothing to a new URL AND the label looks like a URL if ( - ! prevUrl && + !prevUrl && url && isLinkOpen && - isURL( prependHTTP( label ) ) && - /^.+\.[a-z]+/.test( label ) + isURL(prependHTTP(label)) && + /^.+\.[a-z]+/.test(label) ) { // Focus and select the label text. selectLabelText(); } - }, [ prevUrl, url, isLinkOpen, label ] ); + }, [prevUrl, url, isLinkOpen, label]); /** * Focus the Link label text and select it. @@ -355,9 +386,9 @@ export default function NavigationLinkEdit( { const selection = defaultView.getSelection(); const range = ownerDocument.createRange(); // Get the range of the current ref contents so we can add this range to the selection. - range.selectNodeContents( ref.current ); + range.selectNodeContents(ref.current); selection.removeAllRanges(); - selection.addRange( range ); + selection.addRange(range); } /** @@ -369,17 +400,17 @@ export default function NavigationLinkEdit( { // to their default values otherwise this may // in advertently trigger side effects because // the values will have "changed". - setAttributes( { + setAttributes({ url: undefined, label: undefined, id: undefined, kind: undefined, type: undefined, opensInNewTab: false, - } ); + }); // Close the link editing UI. - setIsLinkOpen( false ); + setIsLinkOpen(false); } const { @@ -387,40 +418,40 @@ export default function NavigationLinkEdit( { customTextColor, backgroundColor, customBackgroundColor, - } = getColors( context, ! isTopLevelLink ); + } = getColors(context, !isTopLevelLink); - function onKeyDown( event ) { - if ( isKeyboardEvent.primary( event, 'k' ) ) { + function onKeyDown(event) { + if (isKeyboardEvent.primary(event, 'k')) { // Required to prevent the command center from opening, // as it shares the CMD+K shortcut. // See https://github.com/WordPress/gutenberg/pull/59845. event.preventDefault(); // If this link is a child of a parent submenu item, the parent submenu item event will also open, closing this popover event.stopPropagation(); - setIsLinkOpen( true ); - setOpenedBy( ref.current ); + setIsLinkOpen(true); + setOpenedBy(ref.current); } } - const blockProps = useBlockProps( { - ref: useMergeRefs( [ setPopoverAnchor, listItemRef ] ), - className: clsx( 'wp-block-navigation-item', { + const blockProps = useBlockProps({ + ref: useMergeRefs([setPopoverAnchor, listItemRef]), + className: clsx('wp-block-navigation-item', { 'is-editing': isSelected || isParentOfSelectedBlock, 'is-dragging-within': isDraggingWithin, - 'has-link': !! url, + 'has-link': !!url, 'has-child': hasChildren, - 'has-text-color': !! textColor || !! customTextColor, - [ getColorClassName( 'color', textColor ) ]: !! textColor, - 'has-background': !! backgroundColor || customBackgroundColor, - [ getColorClassName( 'background-color', backgroundColor ) ]: - !! backgroundColor, - } ), + 'has-text-color': !!textColor || !!customTextColor, + [getColorClassName('color', textColor)]: !!textColor, + 'has-background': !!backgroundColor || customBackgroundColor, + [getColorClassName('background-color', backgroundColor)]: + !!backgroundColor, + }), style: { - color: ! textColor && customTextColor, - backgroundColor: ! backgroundColor && customBackgroundColor, + color: !textColor && customTextColor, + backgroundColor: !backgroundColor && customBackgroundColor, }, onKeyDown, - } ); + }); const innerBlocksProps = useInnerBlocksProps( { @@ -434,26 +465,25 @@ export default function NavigationLinkEdit( { } ); - if ( ! url || isInvalid || isDraft ) { + if (!url || isInvalid || isDraft) { blockProps.onClick = () => { - setIsLinkOpen( true ); - setOpenedBy( ref.current ); + setIsLinkOpen(true); + setOpenedBy(ref.current); }; } - const classes = clsx( 'wp-block-navigation-item__content', { - 'wp-block-navigation-link__placeholder': ! url || isInvalid || isDraft, - } ); + const classes = clsx('wp-block-navigation-item__content', { + 'wp-block-navigation-link__placeholder': !url || isInvalid || isDraft, + }); - const missingText = getMissingText( type ); + const missingText = getMissingText(type); /* translators: Whether the navigation link is Invalid or a Draft. */ - const placeholderText = `(${ - isInvalid ? __( 'Invalid' ) : __( 'Draft' ) - })`; + const placeholderText = `(${isInvalid ? __('Invalid') : __('Draft') + })`; const tooltipText = isInvalid || isDraft - ? __( 'This item has been deleted, or is a draft' ) - : __( 'This item is missing a link' ); + ? __('This item has been deleted, or is a draft') + : __('This item is missing a link'); return ( <> @@ -461,117 +491,116 @@ export default function NavigationLinkEdit( { { - setIsLinkOpen( true ); - setOpenedBy( event.currentTarget ); - } } + icon={linkIcon} + title={__('Link')} + shortcut={displayShortcut.primary('k')} + onClick={(event) => { + setIsLinkOpen(true); + setOpenedBy(event.currentTarget); + }} /> - { ! isAtMaxNesting && ( + {!isAtMaxNesting && ( - ) } + )} - { /* Warning, this duplicated in packages/block-library/src/navigation-submenu/edit.js */ } + { /* Warning, this duplicated in packages/block-library/src/navigation-submenu/edit.js */} -
- { /* eslint-disable jsx-a11y/anchor-is-valid */ } - - { /* eslint-enable */ } - { ! url ? ( +
+ { /* eslint-disable jsx-a11y/anchor-is-valid */} + + { /* eslint-enable */} + {!url ? (
- - { missingText } + + {missingText}
) : ( <> - { ! isInvalid && - ! isDraft && - ! isLabelFieldFocused && ( + {!isInvalid && + !isDraft && + !isLabelFieldFocused && ( <> - setAttributes( { + value={label} + onChange={(labelValue) => + setAttributes({ label: labelValue, - } ) + }) } - onMerge={ mergeBlocks } - onReplace={ onReplace } - __unstableOnSplitAtEnd={ () => + onMerge={mergeBlocks} + onReplace={onReplace} + __unstableOnSplitAtEnd={() => insertBlocksAfter( createBlock( 'core/navigation-link' ) ) } - aria-label={ __( + aria-label={__( 'Navigation link text' - ) } - placeholder={ itemLabelPlaceholder } + )} + placeholder={itemLabelPlaceholder} withoutInteractiveFormatting /> - { description && ( + {description && ( - { description } + {description} - ) } + )} - ) } - { ( isInvalid || + )} + {(isInvalid || isDraft || - isLabelFieldFocused ) && ( -
- - - { - // Some attributes are stored in an escaped form. It's a legacy issue. - // Ideally they would be stored in a raw, unescaped form. - // Unescape is used here to "recover" the escaped characters - // so they display without encoding. - // See `updateAttributes` for more details. - `${ decodeEntities( label ) } ${ - isInvalid || isDraft - ? placeholderText - : '' - }`.trim() - } - - -
- ) } + isLabelFieldFocused) && ( +
+ + + { + // Some attributes are stored in an escaped form. It's a legacy issue. + // Ideally they would be stored in a raw, unescaped form. + // Unescape is used here to "recover" the escaped characters + // so they display without encoding. + // See `updateAttributes` for more details. + `${decodeEntities(label)} ${isInvalid || isDraft + ? placeholderText + : '' + }`.trim() + } + + +
+ )} - ) } - { isLinkOpen && ( + )} + {isLinkOpen && ( { + ref={linkUIref} + clientId={clientId} + link={attributes} + onClose={() => { // If there is no link then remove the auto-inserted block. // This avoids empty blocks which can provided a poor UX. - if ( ! url ) { + if (!url) { // Fixes https://github.com/WordPress/gutenberg/issues/61361 // There's a chance we're closing due to the user selecting the browse all button. // Only move focus if the focus is still within the popover ui. If it's not within @@ -583,39 +612,39 @@ export default function NavigationLinkEdit( { ) ) { // Select the previous block to keep focus nearby - selectPreviousBlock( clientId, true ); + selectPreviousBlock(clientId, true); } // Remove the link. - onReplace( [] ); + onReplace([]); return; } - setIsLinkOpen( false ); - if ( openedBy ) { + setIsLinkOpen(false); + if (openedBy) { openedBy.focus(); - setOpenedBy( null ); - } else if ( ref.current ) { + setOpenedBy(null); + } else if (ref.current) { // select the ref when adding a new link ref.current.focus(); } else { // Fallback - selectPreviousBlock( clientId, true ); + selectPreviousBlock(clientId, true); } - } } - anchor={ popoverAnchor } - onRemove={ removeLink } - onChange={ ( updatedValue ) => { + }} + anchor={popoverAnchor} + onRemove={removeLink} + onChange={(updatedValue) => { updateAttributes( updatedValue, setAttributes, attributes ); - } } + }} /> - ) } + )}
-
+
); From 1f2dab0495ff37ff8ba18d5e8a7b972f8fb168bf Mon Sep 17 00:00:00 2001 From: Rinkal Pagdar <92097119+rinkalpagdar@users.noreply.github.com> Date: Mon, 16 Dec 2024 10:30:59 +0530 Subject: [PATCH 2/5] fix package.json --- package-lock.json | 1 + 1 file changed, 1 insertion(+) diff --git a/package-lock.json b/package-lock.json index c3005e83127e96..32ff2db4986512 100644 --- a/package-lock.json +++ b/package-lock.json @@ -45192,6 +45192,7 @@ "resolved": "https://registry.npmjs.org/typescript/-/typescript-5.7.2.tgz", "integrity": "sha512-i5t66RHxDvVN40HfDd1PsEThGNnlMCMT3jMUuoh9/0TaqWevNontacunWyN02LA9/fIbEWlcHZcgTKb9QoaLfg==", "dev": true, + "license": "Apache-2.0", "bin": { "tsc": "bin/tsc", "tsserver": "bin/tsserver" From 624eac3ee9f677f0841a82577ec6b618ee0ec976 Mon Sep 17 00:00:00 2001 From: Rinkal Pagdar <92097119+rinkalpagdar@users.noreply.github.com> Date: Mon, 16 Dec 2024 11:40:32 +0530 Subject: [PATCH 3/5] property added --- packages/block-library/src/navigation-link/edit.js | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index c1219401643d33..9b4f6ae3cbb468 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -167,6 +167,7 @@ function Controls({ attributes, setAttributes, setIsLabelFieldFocused }) { hasValue={() => !!label} label={__('Text')} onDeselect={() => setAttributes({ label: '' })} + isShownByDefault > !!url} label={__('Link')} onDeselect={() => setAttributes({ url: '' })} + isShownByDefault > !!description} label={__('Description')} onDeselect={() => setAttributes({ description: '' })} + isShownByDefault > !!title} label={__('Title attribute')} onDeselect={() => setAttributes({ title: '' })} + isShownByDefault > !!rel} label={__('Rel attribute')} onDeselect={() => setAttributes({ rel: '' })} + isShownByDefault > Date: Mon, 16 Dec 2024 17:41:31 +0530 Subject: [PATCH 4/5] spacing fixes --- .../block-library/src/navigation-link/edit.js | 378 +++++++++--------- 1 file changed, 190 insertions(+), 188 deletions(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 9b4f6ae3cbb468..8fdc32e7eafde8 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -9,13 +9,13 @@ import clsx from 'clsx'; import { createBlock } from '@wordpress/blocks'; import { useSelect, useDispatch } from '@wordpress/data'; import { + __experimentalToolsPanel as ToolsPanel, + __experimentalToolsPanelItem as ToolsPanelItem, TextControl, TextareaControl, ToolbarButton, Tooltip, ToolbarGroup, - __experimentalToolsPanel as ToolsPanel, - __experimentalToolsPanelItem as ToolsPanelItem, } from '@wordpress/components'; import { displayShortcut, isKeyboardEvent } from '@wordpress/keycodes'; import { __ } from '@wordpress/i18n'; @@ -54,61 +54,61 @@ const DEFAULT_BLOCK = { name: 'core/navigation-link' }; * * @return {boolean} Is dragging within the target element. */ -const useIsDraggingWithin = (elementRef) => { - const [isDraggingWithin, setIsDraggingWithin] = useState(false); +const useIsDraggingWithin = ( elementRef ) => { + const [ isDraggingWithin, setIsDraggingWithin ] = useState( false ); - useEffect(() => { + useEffect( () => { const { ownerDocument } = elementRef.current; - function handleDragStart(event) { + function handleDragStart( event ) { // Check the first time when the dragging starts. - handleDragEnter(event); + handleDragEnter( event ); } // Set to false whenever the user cancel the drag event by either releasing the mouse or press Escape. function handleDragEnd() { - setIsDraggingWithin(false); + setIsDraggingWithin( false ); } - function handleDragEnter(event) { + function handleDragEnter( event ) { // Check if the current target is inside the item element. - if (elementRef.current.contains(event.target)) { - setIsDraggingWithin(true); + if ( elementRef.current.contains( event.target ) ) { + setIsDraggingWithin( true ); } else { - setIsDraggingWithin(false); + setIsDraggingWithin( false ); } } // Bind these events to the document to catch all drag events. // Ideally, we can also use `event.relatedTarget`, but sadly that // doesn't work in Safari. - ownerDocument.addEventListener('dragstart', handleDragStart); - ownerDocument.addEventListener('dragend', handleDragEnd); - ownerDocument.addEventListener('dragenter', handleDragEnter); + ownerDocument.addEventListener( 'dragstart', handleDragStart ); + ownerDocument.addEventListener( 'dragend', handleDragEnd ); + ownerDocument.addEventListener( 'dragenter', handleDragEnter ); return () => { - ownerDocument.removeEventListener('dragstart', handleDragStart); - ownerDocument.removeEventListener('dragend', handleDragEnd); - ownerDocument.removeEventListener('dragenter', handleDragEnter); + ownerDocument.removeEventListener( 'dragstart', handleDragStart ); + ownerDocument.removeEventListener( 'dragend', handleDragEnd ); + ownerDocument.removeEventListener( 'dragenter', handleDragEnter ); }; - }, [elementRef]); + }, [ elementRef ] ); return isDraggingWithin; }; -const useIsInvalidLink = (kind, type, id) => { +const useIsInvalidLink = ( kind, type, id ) => { const isPostType = kind === 'post-type' || type === 'post' || type === 'page'; - const hasId = Number.isInteger(id); + const hasId = Number.isInteger( id ); const postStatus = useSelect( - (select) => { - if (!isPostType) { + ( select ) => { + if ( ! isPostType ) { return null; } - const { getEntityRecord } = select(coreStore); - return getEntityRecord('postType', type, id)?.status; + const { getEntityRecord } = select( coreStore ); + return getEntityRecord( 'postType', type, id )?.status; }, - [isPostType, type, id] + [ isPostType, type, id ] ); // Check Navigation Link validity if: @@ -123,32 +123,32 @@ const useIsInvalidLink = (kind, type, id) => { isPostType && hasId && postStatus && 'trash' === postStatus; const isDraft = 'draft' === postStatus; - return [isInvalid, isDraft]; + return [ isInvalid, isDraft ]; }; -function getMissingText(type) { +function getMissingText( type ) { let missingText = ''; - switch (type) { + switch ( type ) { case 'post': /* translators: label for missing post in navigation link block */ - missingText = __('Select post'); + missingText = __( 'Select post' ); break; case 'page': /* translators: label for missing page in navigation link block */ - missingText = __('Select page'); + missingText = __( 'Select page' ); break; case 'category': /* translators: label for missing category in navigation link block */ - missingText = __('Select category'); + missingText = __( 'Select category' ); break; case 'tag': /* translators: label for missing tag in navigation link block */ - missingText = __('Select tag'); + missingText = __( 'Select tag' ); break; default: /* translators: label for missing values in navigation link block */ - missingText = __('Add link'); + missingText = __( 'Add link' ); } return missingText; @@ -159,7 +159,7 @@ function getMissingText(type) { * packages/block-library/src/navigation-submenu/edit.js * Consider reuseing this components for both blocks. */ -function Controls({ attributes, setAttributes, setIsLabelFieldFocused }) { +function Controls( { attributes, setAttributes, setIsLabelFieldFocused } ) { const { label, url, description, title, rel } = attributes; return ( @@ -265,7 +265,7 @@ function Controls({ attributes, setAttributes, setIsLabelFieldFocused }) { ); } -export default function NavigationLinkEdit({ +export default function NavigationLinkEdit( { attributes, isSelected, setAttributes, @@ -274,10 +274,10 @@ export default function NavigationLinkEdit({ onReplace, context, clientId, -}) { +} ) { const { id, label, type, url, description, kind } = attributes; - const [isInvalid, isDraft] = useIsInvalidLink(kind, type, id); + const [ isInvalid, isDraft ] = useIsInvalidLink( kind, type, id ); const { maxNestingLevel } = context; const { @@ -285,24 +285,24 @@ export default function NavigationLinkEdit({ __unstableMarkNextChangeAsNotPersistent, selectBlock, selectPreviousBlock, - } = useDispatch(blockEditorStore); + } = useDispatch( blockEditorStore ); // Have the link editing ui open on mount when lacking a url and selected. - const [isLinkOpen, setIsLinkOpen] = useState(isSelected && !url); + const [ isLinkOpen, setIsLinkOpen ] = useState( isSelected && ! url ); // Store what element opened the popover, so we know where to return focus to (toolbar button vs navigation link text) - const [openedBy, setOpenedBy] = useState(null); + const [ openedBy, setOpenedBy ] = useState( null ); // Use internal state instead of a ref to make sure that the component // re-renders when the popover's anchor updates. - const [popoverAnchor, setPopoverAnchor] = useState(null); - const listItemRef = useRef(null); - const isDraggingWithin = useIsDraggingWithin(listItemRef); - const itemLabelPlaceholder = __('Add label…'); + const [ popoverAnchor, setPopoverAnchor ] = useState( null ); + const listItemRef = useRef( null ); + const isDraggingWithin = useIsDraggingWithin( listItemRef ); + const itemLabelPlaceholder = __( 'Add label…' ); const ref = useRef(); const linkUIref = useRef(); - const prevUrl = usePrevious(url); + const prevUrl = usePrevious( url ); // Change the label using inspector causes rich text to change focus on firefox. // This is a workaround to keep the focus on the label field when label filed is focused we don't render the rich text. - const [isLabelFieldFocused, setIsLabelFieldFocused] = useState(false); + const [ isLabelFieldFocused, setIsLabelFieldFocused ] = useState( false ); const { isAtMaxNesting, @@ -310,76 +310,76 @@ export default function NavigationLinkEdit({ isParentOfSelectedBlock, hasChildren, } = useSelect( - (select) => { + ( select ) => { const { getBlockCount, getBlockName, getBlockRootClientId, hasSelectedInnerBlock, getBlockParentsByBlockName, - } = select(blockEditorStore); + } = select( blockEditorStore ); return { isAtMaxNesting: - getBlockParentsByBlockName(clientId, [ + getBlockParentsByBlockName( clientId, [ 'core/navigation-link', 'core/navigation-submenu', - ]).length >= maxNestingLevel, + ] ).length >= maxNestingLevel, isTopLevelLink: - getBlockName(getBlockRootClientId(clientId)) === + getBlockName( getBlockRootClientId( clientId ) ) === 'core/navigation', isParentOfSelectedBlock: hasSelectedInnerBlock( clientId, true ), - hasChildren: !!getBlockCount(clientId), + hasChildren: !! getBlockCount( clientId ), }; }, - [clientId, maxNestingLevel] + [ clientId, maxNestingLevel ] ); - const { getBlocks } = useSelect(blockEditorStore); + const { getBlocks } = useSelect( blockEditorStore ); /** * Transform to submenu block. */ const transformToSubmenu = () => { - let innerBlocks = getBlocks(clientId); - if (innerBlocks.length === 0) { - innerBlocks = [createBlock('core/navigation-link')]; - selectBlock(innerBlocks[0].clientId); + let innerBlocks = getBlocks( clientId ); + if ( innerBlocks.length === 0 ) { + innerBlocks = [ createBlock( 'core/navigation-link' ) ]; + selectBlock( innerBlocks[ 0 ].clientId ); } const newSubmenu = createBlock( 'core/navigation-submenu', attributes, innerBlocks ); - replaceBlock(clientId, newSubmenu); + replaceBlock( clientId, newSubmenu ); }; - useEffect(() => { + useEffect( () => { // If block has inner blocks, transform to Submenu. - if (hasChildren) { + if ( hasChildren ) { // This side-effect should not create an undo level as those should // only be created via user interactions. __unstableMarkNextChangeAsNotPersistent(); transformToSubmenu(); } - }, [hasChildren]); + }, [ hasChildren ] ); // If the LinkControl popover is open and the URL has changed, close the LinkControl and focus the label text. - useEffect(() => { + useEffect( () => { // We only want to do this when the URL has gone from nothing to a new URL AND the label looks like a URL if ( - !prevUrl && + ! prevUrl && url && isLinkOpen && - isURL(prependHTTP(label)) && - /^.+\.[a-z]+/.test(label) + isURL( prependHTTP( label ) ) && + /^.+\.[a-z]+/.test( label ) ) { // Focus and select the label text. selectLabelText(); } - }, [prevUrl, url, isLinkOpen, label]); + }, [ prevUrl, url, isLinkOpen, label ] ); /** * Focus the Link label text and select it. @@ -391,9 +391,9 @@ export default function NavigationLinkEdit({ const selection = defaultView.getSelection(); const range = ownerDocument.createRange(); // Get the range of the current ref contents so we can add this range to the selection. - range.selectNodeContents(ref.current); + range.selectNodeContents( ref.current ); selection.removeAllRanges(); - selection.addRange(range); + selection.addRange( range ); } /** @@ -405,17 +405,17 @@ export default function NavigationLinkEdit({ // to their default values otherwise this may // in advertently trigger side effects because // the values will have "changed". - setAttributes({ + setAttributes( { url: undefined, label: undefined, id: undefined, kind: undefined, type: undefined, opensInNewTab: false, - }); + } ); // Close the link editing UI. - setIsLinkOpen(false); + setIsLinkOpen( false ); } const { @@ -423,40 +423,40 @@ export default function NavigationLinkEdit({ customTextColor, backgroundColor, customBackgroundColor, - } = getColors(context, !isTopLevelLink); + } = getColors( context, ! isTopLevelLink ); - function onKeyDown(event) { - if (isKeyboardEvent.primary(event, 'k')) { + function onKeyDown( event ) { + if ( isKeyboardEvent.primary( event, 'k' ) ) { // Required to prevent the command center from opening, // as it shares the CMD+K shortcut. // See https://github.com/WordPress/gutenberg/pull/59845. event.preventDefault(); // If this link is a child of a parent submenu item, the parent submenu item event will also open, closing this popover event.stopPropagation(); - setIsLinkOpen(true); - setOpenedBy(ref.current); + setIsLinkOpen( true ); + setOpenedBy( ref.current ); } } - const blockProps = useBlockProps({ - ref: useMergeRefs([setPopoverAnchor, listItemRef]), - className: clsx('wp-block-navigation-item', { + const blockProps = useBlockProps( { + ref: useMergeRefs( [ setPopoverAnchor, listItemRef ] ), + className: clsx( 'wp-block-navigation-item', { 'is-editing': isSelected || isParentOfSelectedBlock, 'is-dragging-within': isDraggingWithin, - 'has-link': !!url, + 'has-link': !! url, 'has-child': hasChildren, - 'has-text-color': !!textColor || !!customTextColor, - [getColorClassName('color', textColor)]: !!textColor, - 'has-background': !!backgroundColor || customBackgroundColor, - [getColorClassName('background-color', backgroundColor)]: - !!backgroundColor, - }), + 'has-text-color': !! textColor || !! customTextColor, + [ getColorClassName( 'color', textColor ) ]: !! textColor, + 'has-background': !! backgroundColor || customBackgroundColor, + [ getColorClassName( 'background-color', backgroundColor ) ]: + !! backgroundColor, + } ), style: { - color: !textColor && customTextColor, - backgroundColor: !backgroundColor && customBackgroundColor, + color: ! textColor && customTextColor, + backgroundColor: ! backgroundColor && customBackgroundColor, }, onKeyDown, - }); + } ); const innerBlocksProps = useInnerBlocksProps( { @@ -470,25 +470,26 @@ export default function NavigationLinkEdit({ } ); - if (!url || isInvalid || isDraft) { + if ( ! url || isInvalid || isDraft ) { blockProps.onClick = () => { - setIsLinkOpen(true); - setOpenedBy(ref.current); + setIsLinkOpen( true ); + setOpenedBy( ref.current ); }; } - const classes = clsx('wp-block-navigation-item__content', { - 'wp-block-navigation-link__placeholder': !url || isInvalid || isDraft, - }); + const classes = clsx( 'wp-block-navigation-item__content', { + 'wp-block-navigation-link__placeholder': ! url || isInvalid || isDraft, + } ); - const missingText = getMissingText(type); + const missingText = getMissingText( type ); /* translators: Whether the navigation link is Invalid or a Draft. */ - const placeholderText = `(${isInvalid ? __('Invalid') : __('Draft') - })`; + const placeholderText = `(${ + isInvalid ? __( 'Invalid' ) : __( 'Draft' ) + })`; const tooltipText = isInvalid || isDraft - ? __('This item has been deleted, or is a draft') - : __('This item is missing a link'); + ? __( 'This item has been deleted, or is a draft' ) + : __( 'This item is missing a link' ); return ( <> @@ -496,116 +497,117 @@ export default function NavigationLinkEdit({ { - setIsLinkOpen(true); - setOpenedBy(event.currentTarget); - }} + icon={ linkIcon } + title={ __( 'Link' ) } + shortcut={ displayShortcut.primary( 'k' ) } + onClick={ ( event ) => { + setIsLinkOpen( true ); + setOpenedBy( event.currentTarget ); + } } /> - {!isAtMaxNesting && ( + { ! isAtMaxNesting && ( - )} + ) } - { /* Warning, this duplicated in packages/block-library/src/navigation-submenu/edit.js */} + { /* Warning, this duplicated in packages/block-library/src/navigation-submenu/edit.js */ } -
- { /* eslint-disable jsx-a11y/anchor-is-valid */} - - { /* eslint-enable */} - {!url ? ( +
+ { /* eslint-disable jsx-a11y/anchor-is-valid */ } + + { /* eslint-enable */ } + { ! url ? (
- - {missingText} + + { missingText }
) : ( <> - {!isInvalid && - !isDraft && - !isLabelFieldFocused && ( + { ! isInvalid && + ! isDraft && + ! isLabelFieldFocused && ( <> - setAttributes({ + value={ label } + onChange={ ( labelValue ) => + setAttributes( { label: labelValue, - }) + } ) } - onMerge={mergeBlocks} - onReplace={onReplace} - __unstableOnSplitAtEnd={() => + onMerge={ mergeBlocks } + onReplace={ onReplace } + __unstableOnSplitAtEnd={ () => insertBlocksAfter( createBlock( 'core/navigation-link' ) ) } - aria-label={__( + aria-label={ __( 'Navigation link text' - )} - placeholder={itemLabelPlaceholder} + ) } + placeholder={ itemLabelPlaceholder } withoutInteractiveFormatting /> - {description && ( + { description && ( - {description} + { description } - )} + ) } - )} - {(isInvalid || + ) } + { ( isInvalid || isDraft || - isLabelFieldFocused) && ( -
- - - { - // Some attributes are stored in an escaped form. It's a legacy issue. - // Ideally they would be stored in a raw, unescaped form. - // Unescape is used here to "recover" the escaped characters - // so they display without encoding. - // See `updateAttributes` for more details. - `${decodeEntities(label)} ${isInvalid || isDraft - ? placeholderText - : '' - }`.trim() - } - - -
- )} + isLabelFieldFocused ) && ( +
+ + + { + // Some attributes are stored in an escaped form. It's a legacy issue. + // Ideally they would be stored in a raw, unescaped form. + // Unescape is used here to "recover" the escaped characters + // so they display without encoding. + // See `updateAttributes` for more details. + `${ decodeEntities( label ) } ${ + isInvalid || isDraft + ? placeholderText + : '' + }`.trim() + } + + +
+ ) } - )} - {isLinkOpen && ( + ) } + { isLinkOpen && ( { + ref={ linkUIref } + clientId={ clientId } + link={ attributes } + onClose={ () => { // If there is no link then remove the auto-inserted block. // This avoids empty blocks which can provided a poor UX. - if (!url) { + if ( ! url ) { // Fixes https://github.com/WordPress/gutenberg/issues/61361 // There's a chance we're closing due to the user selecting the browse all button. // Only move focus if the focus is still within the popover ui. If it's not within @@ -617,39 +619,39 @@ export default function NavigationLinkEdit({ ) ) { // Select the previous block to keep focus nearby - selectPreviousBlock(clientId, true); + selectPreviousBlock( clientId, true ); } // Remove the link. - onReplace([]); + onReplace( [] ); return; } - setIsLinkOpen(false); - if (openedBy) { + setIsLinkOpen( false ); + if ( openedBy ) { openedBy.focus(); - setOpenedBy(null); - } else if (ref.current) { + setOpenedBy( null ); + } else if ( ref.current ) { // select the ref when adding a new link ref.current.focus(); } else { // Fallback - selectPreviousBlock(clientId, true); + selectPreviousBlock( clientId, true ); } - }} - anchor={popoverAnchor} - onRemove={removeLink} - onChange={(updatedValue) => { + } } + anchor={ popoverAnchor } + onRemove={ removeLink } + onChange={ ( updatedValue ) => { updateAttributes( updatedValue, setAttributes, attributes ); - }} + } } /> - )} + ) }
-
+
); From bb7e683dbb2bfb1c4c80d98070650b00472e3eb7 Mon Sep 17 00:00:00 2001 From: Rinkal Pagdar <92097119+rinkalpagdar@users.noreply.github.com> Date: Tue, 17 Dec 2024 11:44:19 +0530 Subject: [PATCH 5/5] lint fix --- .../block-library/src/navigation-link/edit.js | 102 +++++++++--------- 1 file changed, 53 insertions(+), 49 deletions(-) diff --git a/packages/block-library/src/navigation-link/edit.js b/packages/block-library/src/navigation-link/edit.js index 8fdc32e7eafde8..5966739aa61a61 100644 --- a/packages/block-library/src/navigation-link/edit.js +++ b/packages/block-library/src/navigation-link/edit.js @@ -162,103 +162,107 @@ function getMissingText( type ) { function Controls( { attributes, setAttributes, setIsLabelFieldFocused } ) { const { label, url, description, title, rel } = attributes; return ( - + !!label} - label={__('Text')} - onDeselect={() => setAttributes({ label: '' })} + hasValue={ () => !! label } + label={ __( 'Text' ) } + onDeselect={ () => setAttributes( { label: '' } ) } isShownByDefault > { - setAttributes({ label: labelValue }); - }} + label={ __( 'Text' ) } + value={ label ? stripHTML( label ) : '' } + onChange={ ( labelValue ) => { + setAttributes( { label: labelValue } ); + } } autoComplete="off" - onFocus={() => setIsLabelFieldFocused(true)} - onBlur={() => setIsLabelFieldFocused(false)} + onFocus={ () => setIsLabelFieldFocused( true ) } + onBlur={ () => setIsLabelFieldFocused( false ) } /> !!url} - label={__('Link')} - onDeselect={() => setAttributes({ url: '' })} + hasValue={ () => !! url } + label={ __( 'Link' ) } + onDeselect={ () => setAttributes( { url: '' } ) } isShownByDefault > { - updateAttributes({ url: urlValue }, setAttributes, attributes); - }} + label={ __( 'Link' ) } + value={ url ? safeDecodeURI( url ) : '' } + onChange={ ( urlValue ) => { + updateAttributes( + { url: urlValue }, + setAttributes, + attributes + ); + } } autoComplete="off" /> !!description} - label={__('Description')} - onDeselect={() => setAttributes({ description: '' })} + hasValue={ () => !! description } + label={ __( 'Description' ) } + onDeselect={ () => setAttributes( { description: '' } ) } isShownByDefault > { - setAttributes({ description: descriptionValue }); - }} - help={__( + label={ __( 'Description' ) } + value={ description || '' } + onChange={ ( descriptionValue ) => { + setAttributes( { description: descriptionValue } ); + } } + help={ __( 'The description will be displayed in the menu if the current theme supports it.' - )} + ) } /> !!title} - label={__('Title attribute')} - onDeselect={() => setAttributes({ title: '' })} + hasValue={ () => !! title } + label={ __( 'Title attribute' ) } + onDeselect={ () => setAttributes( { title: '' } ) } isShownByDefault > { - setAttributes({ title: titleValue }); - }} + label={ __( 'Title attribute' ) } + value={ title || '' } + onChange={ ( titleValue ) => { + setAttributes( { title: titleValue } ); + } } autoComplete="off" - help={__( + help={ __( 'Additional information to help clarify the purpose of the link.' - )} + ) } /> !!rel} - label={__('Rel attribute')} - onDeselect={() => setAttributes({ rel: '' })} + hasValue={ () => !! rel } + label={ __( 'Rel attribute' ) } + onDeselect={ () => setAttributes( { rel: '' } ) } isShownByDefault > { - setAttributes({ rel: relValue }); - }} + label={ __( 'Rel attribute' ) } + value={ rel || '' } + onChange={ ( relValue ) => { + setAttributes( { rel: relValue } ); + } } autoComplete="off" - help={__( + help={ __( 'The relationship of the linked URL as space-separated link types.' - )} + ) } />