diff --git a/lib/experiments-page.php b/lib/experiments-page.php index d6263b93ff6f8..2be55174d5e54 100644 --- a/lib/experiments-page.php +++ b/lib/experiments-page.php @@ -165,12 +165,12 @@ function gutenberg_initialize_experiments_settings() { add_settings_field( 'gutenberg-block-comment', - __( 'Enable multi-user commenting on blocks', 'gutenberg' ), + __( 'Comments', 'gutenberg' ), 'gutenberg_display_experiment_field', 'gutenberg-experiments', 'gutenberg_experiments_section', array( - 'label' => __( 'Comments', 'gutenberg' ), + 'label' => __( 'Enable multi-user commenting on blocks', 'gutenberg' ), 'id' => 'gutenberg-block-comment', ) ); diff --git a/packages/block-editor/src/components/block-toolbar/index.js b/packages/block-editor/src/components/block-toolbar/index.js index d53cbd74939df..754e8946e3b0a 100644 --- a/packages/block-editor/src/components/block-toolbar/index.js +++ b/packages/block-editor/src/components/block-toolbar/index.js @@ -99,10 +99,10 @@ export function PrivateBlockToolbar( { !! getBlockAttributes( clientId )?.metadata?.bindings ); - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const commentID = select( 'core/block-editor' ).getBlock( - selectedBlockClientId - )?.attributes?.blockCommentId; + const commentID = + // eslint-disable-next-line @wordpress/data-no-store-string-literals + select( 'core/block-editor' ).getBlock( selectedBlockClientId ) + ?.attributes?.blockCommentId || null; return { blockClientId: selectedBlockClientId, diff --git a/packages/block-editor/src/components/collab/block-comment-menu-item.js b/packages/block-editor/src/components/collab/block-comment-menu-item.js index 8ff965675db6a..7a661cd64f74c 100644 --- a/packages/block-editor/src/components/collab/block-comment-menu-item.js +++ b/packages/block-editor/src/components/collab/block-comment-menu-item.js @@ -6,14 +6,14 @@ import { MenuItem } from '@wordpress/components'; import { collabComment } from '@wordpress/icons'; import { useDispatch } from '@wordpress/data'; -export default function BlockCommentMenuItem( { clientId, onClose } ) { - +export default function BlockCommentMenuItem( { onClose } ) { + // eslint-disable-next-line @wordpress/data-no-store-string-literals const { openGeneralSidebar } = useDispatch( 'core/edit-post' ); const openCollabBoard = () => { onClose(); - openGeneralSidebar("edit-post/collab-sidebar"); - } + openGeneralSidebar( 'edit-post/collab-sidebar' ); + }; return ( { __( 'Add Comment' ) } - + ); } diff --git a/packages/block-editor/src/components/collab/test/__snapshots__/index.js.snap b/packages/block-editor/src/components/collab/test/__snapshots__/index.js.snap deleted file mode 100644 index f2915ead7417b..0000000000000 --- a/packages/block-editor/src/components/collab/test/__snapshots__/index.js.snap +++ /dev/null @@ -1,169 +0,0 @@ -// Jest Snapshot v1, https://goo.gl/fbAQLP - -exports[`AlignmentUI should allow custom alignment controls to be specified 1`] = ` -
-
-
- -
-
- -
-
-
-`; - -exports[`AlignmentUI should match snapshot when controls are hidden 1`] = ` -
-
- -
-
-`; - -exports[`AlignmentUI should match snapshot when controls are visible 1`] = ` -
-
-
- -
-
- -
-
- -
-
-
-`; diff --git a/packages/block-editor/src/components/collab/test/index.js b/packages/block-editor/src/components/collab/test/index.js deleted file mode 100644 index 178ba294127c3..0000000000000 --- a/packages/block-editor/src/components/collab/test/index.js +++ /dev/null @@ -1,183 +0,0 @@ -/** - * External dependencies - */ -import { render, screen } from '@testing-library/react'; -import userEvent from '@testing-library/user-event'; - -/** - * WordPress dependencies - */ -import { alignLeft, alignCenter } from '@wordpress/icons'; - -/** - * Internal dependencies - */ -import AlignmentUI from '../ui'; - -describe( 'AlignmentUI', () => { - const alignment = 'left'; - const onChangeSpy = jest.fn(); - - afterEach( () => { - onChangeSpy.mockClear(); - } ); - - test( 'should match snapshot when controls are hidden', () => { - const { container } = render( - - ); - - expect( container ).toMatchSnapshot(); - } ); - - test( 'should match snapshot when controls are visible', () => { - const { container } = render( - - ); - - expect( container ).toMatchSnapshot(); - } ); - - test( 'should expand controls when toggled', async () => { - const user = userEvent.setup(); - - const { unmount } = render( - - ); - - expect( - screen.queryByRole( 'menuitemradio', { - name: /^Align text \w+$/, - } ) - ).not.toBeInTheDocument(); - - await user.click( - screen.getByRole( 'button', { - name: 'Align text', - } ) - ); - - expect( - screen.getAllByRole( 'menuitemradio', { - name: /^Align text \w+$/, - } ) - ).toHaveLength( 3 ); - - // Cancel running effects, like delayed dropdown menu popover positioning. - unmount(); - } ); - - test( 'should call on change with undefined when a control is already active', async () => { - const user = userEvent.setup(); - - render( - - ); - - const activeControl = screen.getByRole( 'button', { - name: /^Align text \w+$/, - pressed: true, - } ); - - await user.click( activeControl ); - - expect( activeControl ).toHaveAttribute( 'align', alignment ); - expect( onChangeSpy ).toHaveBeenCalledTimes( 1 ); - expect( onChangeSpy ).toHaveBeenCalledWith( undefined ); - } ); - - test( 'should call on change a new value when the control is not active', async () => { - const user = userEvent.setup(); - - render( - - ); - - const inactiveControl = screen.getByRole( 'button', { - name: 'Align text center', - pressed: false, - } ); - - await user.click( inactiveControl ); - - expect( onChangeSpy ).toHaveBeenCalledTimes( 1 ); - expect( onChangeSpy ).toHaveBeenCalledWith( 'center' ); - } ); - - test( 'should allow custom alignment controls to be specified', async () => { - const user = userEvent.setup(); - - const { container } = render( - - ); - - expect( container ).toMatchSnapshot(); - - expect( - screen.getAllByRole( 'button', { - name: /^My custom \w+$/, - } ) - ).toHaveLength( 2 ); - - // Should correctly call on change when right alignment is pressed (active alignment) - const rightControl = screen.getByRole( 'button', { - name: 'My custom right', - } ); - - await user.click( rightControl ); - - expect( onChangeSpy ).toHaveBeenCalledTimes( 1 ); - expect( onChangeSpy ).toHaveBeenCalledWith( undefined ); - onChangeSpy.mockClear(); - - // Should correctly call on change when right alignment is pressed (inactive alignment) - const leftControl = screen.getByRole( 'button', { - name: 'My custom left', - } ); - - await user.click( leftControl ); - - expect( onChangeSpy ).toHaveBeenCalledTimes( 1 ); - expect( onChangeSpy ).toHaveBeenCalledWith( 'custom-left' ); - } ); -} ); diff --git a/packages/block-editor/src/components/collab/collab-board/index.js b/packages/editor/src/components/collab-sidebar/add-comment.js similarity index 72% rename from packages/block-editor/src/components/collab/collab-board/index.js rename to packages/editor/src/components/collab-sidebar/add-comment.js index 065c1f1002e9c..af4ec5cf0d1f2 100644 --- a/packages/block-editor/src/components/collab/collab-board/index.js +++ b/packages/editor/src/components/collab-sidebar/add-comment.js @@ -4,42 +4,61 @@ // eslint-disable-next-line no-restricted-imports import apiFetch from '@wordpress/api-fetch'; import { __ } from '@wordpress/i18n'; +import { useSelect, useDispatch } from '@wordpress/data'; import { useState } from '@wordpress/element'; import { - TextControl, - Button, - CheckboxControl, __experimentalHStack as HStack, __experimentalVStack as VStack, - Modal, + Button, + TextControl, + CheckboxControl, } from '@wordpress/components'; -import { dateI18n, format, getSettings } from '@wordpress/date'; +import { dateI18n, format } from '@wordpress/date'; import { - commentAuthorAvatar as userIcon, Icon, - trash as deleteIcon, edit as editIcon, + trash as deleteIcon, + commentAuthorAvatar as userIcon, } from '@wordpress/icons'; -import { useSelect, useDispatch } from '@wordpress/data'; -//import { store as coreStore } from '@wordpress/core-data'; -//import { useEntityProp } from '@wordpress/core-data'; +/** + * Renders the new comment form. + * + * @param {Object} root0 The component props. + * @param {Function} root0.setReloadComments Function to reload comments. + */ +export function AddComment( { setReloadComments } ) { + const generateNewComment = () => ( { + commentId: Date.now(), + createdBy: currentUser, + comment: inputComment, + createdAt: new Date().toISOString(), + } ); + + // Get the date time format from WordPress settings. + const dateTimeFormat = 'h:i A'; -export default function BlockCommentModal( { clientId, onClose, threadId } ) { // State to manage the comment thread. const [ inputComment, setInputComment ] = useState( '' ); - const [ isResolved, setIsResolved ] = useState( false ); const [ isEditing, setIsEditing ] = useState( null ); - const [ showConfirmation, setShowConfirmation ] = useState( false ); + const curruntUserData = useSelect( ( select ) => { // eslint-disable-next-line @wordpress/data-no-store-string-literals return select( 'core' ).getCurrentUser(); }, [] ); - const userAvatar = curruntUserData.avatar_urls[ 48 ] || null; + let userAvatar = + 'https://secure.gravatar.com/avatar/92929292929292929292929292929292?s=48&d=mm&r=g'; + if ( curruntUserData?.avatar_urls ) { + userAvatar = curruntUserData?.avatar_urls[ 48 ]; + } + + //const userAvatar = curruntUserData?.avatar_urls[ 48 ] ? curruntUserData?.avatar_urls[ 48 ] : null; + const currentUser = curruntUserData?.name || null; const allThreads = []; + let threadId = 0; const currentThread = allThreads[ threadId ] ?? {}; const isCurrentThreadResolved = currentThread.threadIsResolved || false; const commentsCount = isCurrentThreadResolved @@ -51,16 +70,24 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { return select( 'core/editor' ).getCurrentPostId(); }, [] ); + const clientId = useSelect( ( select ) => { + // eslint-disable-next-line @wordpress/data-no-store-string-literals + const { getSelectedBlockClientId } = select( 'core/block-editor' ); + return getSelectedBlockClientId(); + }, [] ); + + const blockCommentId = useSelect( ( select ) => { + const clientID = + // eslint-disable-next-line @wordpress/data-no-store-string-literals + select( 'core/block-editor' ).getSelectedBlockClientId(); + // eslint-disable-next-line @wordpress/data-no-store-string-literals + return select( 'core/block-editor' ).getBlock( clientID )?.attributes + ?.blockCommentId; + }, [] ); + // Get the dispatch functions to save the comment and update the block attributes. // eslint-disable-next-line @wordpress/data-no-store-string-literals const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); - // Helper function to generate a new comment. - const generateNewComment = () => ( { - commentId: Date.now(), - createdBy: currentUser, - comment: inputComment, - createdAt: new Date().toISOString(), - } ); // // Function to add a border class to the content reference. const setAttributes = () => { @@ -69,26 +96,10 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { } ); }; - // Helper function to get updated comments structure - const getUpdatedComments = ( newComment, threadKey ) => ( { - ...allThreads, - [ threadKey ]: { - isResolved, - createdAt: - allThreads?.threadKey?.createdAt || new Date().toISOString(), - createdBy: currentUser, - comments: [ - ...( allThreads[ threadKey ]?.comments || [] ), - newComment, - ], - }, - } ); - // Function to save the comment. const saveComment = () => { const newComment = generateNewComment(); threadId = newComment?.commentId; - const updatedComments = getUpdatedComments( newComment, threadId ); apiFetch( { path: '/wp/v2/comments', @@ -101,12 +112,11 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { comment_author: currentUser, comment_approved: 0, }, - } ).then( (response) => { - threadId = response?.id; - setAttributes( clientId, threadId ); - onClose(); + } ).then( ( response ) => { + setAttributes( clientId, response?.id ); + setInputComment( '' ); + setReloadComments( true ); } ); - }; // Function to edit the comment. @@ -130,22 +140,6 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { setIsEditing( null ); }; - // Function to mark thread as resolved - const markThreadAsResolved = () => { - setIsResolved( true ); - - const updatedComments = { ...allThreads }; - - updatedComments[ threadId ] = { - ...updatedComments[ threadId ], - isResolved: true, - resolvedBy: currentUser, - resolvedAt: new Date().toISOString(), - }; - - onClose(); - }; - // Function to delete a comment. const deleteComment = ( commentId ) => { // Filter out the comment to be deleted. @@ -166,28 +160,11 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { } }; - // Function to show the confirmation overlay. - const showConfirmationOverlay = () => setShowConfirmation( true ); - - // Function to hide the confirmation overlay. - const hideConfirmationOverlay = () => setShowConfirmation( false ); - - // Function to confirm and mark thread as resolved. - const confirmAndMarkThreadAsResolved = () => { - markThreadAsResolved(); - hideConfirmationOverlay(); - }; + const handleCancel = () => {}; - // On cancel, remove the border if no comments are present. - const handleCancel = () => { - onClose(); - }; - - // Get the date time format from WordPress settings. - const dateTimeFormat = getSettings().formats.datetime; return ( <> - + { null !== clientId && 0 === blockCommentId && ( { 0 < commentsCount && ! isCurrentThreadResolved && ( <> @@ -203,17 +180,22 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { ) => ( <> { isEditing === commentId && ( - + - + { currentUser } @@ -232,7 +214,7 @@ export default function BlockCommentModal( { clientId, onClose, threadId } ) { spacing="2" > - - ) } ); diff --git a/packages/editor/src/components/collab-sidebar/comments.js b/packages/editor/src/components/collab-sidebar/comments.js new file mode 100644 index 0000000000000..dfbd497a9b0d4 --- /dev/null +++ b/packages/editor/src/components/collab-sidebar/comments.js @@ -0,0 +1,1005 @@ +/** + * WordPress dependencies + */ +// eslint-disable-next-line no-restricted-imports +import apiFetch from '@wordpress/api-fetch'; +import { useState, RawHTML } from '@wordpress/element'; +import { + __experimentalHStack as HStack, + __experimentalVStack as VStack, + __experimentalText as Text, + Button, + DropdownMenu, + TextareaControl, + Tooltip, +} from '@wordpress/components'; +import { dateI18n, format } from '@wordpress/date'; +import { Icon, check, published, moreVertical } from '@wordpress/icons'; +import { __ } from '@wordpress/i18n'; +import { useSelect } from '@wordpress/data'; + +export function Comments( { threads } ) { + const [ showConfirmation, setShowConfirmation ] = useState( false ); + const [ showConfirmationTabId, setShowConfirmationTabId ] = useState( 0 ); + const [ commentConfirmation, setCommentConfirmation ] = useState( false ); + const [ deleteCommentShowConfirmation, setDeleteCommentShowConfirmation ] = + useState( false ); + const [ commentDeleteMessage, setCommentDeleteMessage ] = useState( false ); + const [ commentEdit, setCommentEdit ] = useState( false ); + const [ newEditedComment, setNewEditedComment ] = useState( '' ); + const [ commentEditedMessage, setCommentEditedMessage ] = useState( false ); + const [ hasCommentReply, setHasCommentReply ] = useState( false ); + const [ commentReply, setCommentReply ] = useState( '' ); + const [ replyMessage, setReplyMessage ] = useState( false ); + + // Get the date time format from WordPress settings. + const dateTimeFormat = 'h:i A'; + const currentUserData = useSelect( ( select ) => { + // eslint-disable-next-line @wordpress/data-no-store-string-literals + return select( 'core' ).getCurrentUser(); + }, [] ); + const currentUser = currentUserData?.name || null; + const postId = useSelect( ( select ) => { + // eslint-disable-next-line @wordpress/data-no-store-string-literals + return select( 'core/editor' ).getCurrentPostId(); + }, [] ); + + const confirmAndMarkThreadAsResolved = ( threadID ) => { + setCommentConfirmation( false ); + if ( threadID ) { + apiFetch( { + path: '/wp/v2/comments/' + threadID, + method: 'POST', + data: { + status: 'approved', + }, + } ).then( ( response ) => { + if ( 'approved' === response.status ) { + setShowConfirmation( false ); + setCommentConfirmation( true ); + } + } ); + } + }; + + const onEditComment = ( threadID ) => { + if ( threadID ) { + setHasCommentReply( false ); + setCommentEdit( true ); + } + }; + + const confirmEditComment = ( threadID ) => { + if ( threadID ) { + const editedComment = newEditedComment.replace( + /^

|<\/p>$/g, + '' + ); + + apiFetch( { + path: '/wp/v2/comments/' + threadID, + method: 'POST', + data: { + content: editedComment, + }, + } ).then( ( response ) => { + if ( 'trash' !== response.status && '' !== response.id ) { + setCommentEdit( false ); + setCommentEditedMessage( true ); + } + } ); + } + }; + + const confirmDeleteComment = ( threadID ) => { + setDeleteCommentShowConfirmation( false ); + if ( threadID ) { + apiFetch( { + path: '/wp/v2/comments/' + threadID, + method: 'DELETE', + } ).then( ( response ) => { + if ( 'trash' === response.status && '' !== response.id ) { + setCommentDeleteMessage( true ); + } + } ); + } + }; + + const onReplyComment = ( threadID ) => { + if ( threadID ) { + setCommentEdit( false ); + setHasCommentReply( true ); + } + }; + + const generateReplyComment = ( comment ) => ( { + commentId: Date.now(), + createdBy: currentUser, + comment, + createdAt: new Date().toISOString(), + } ); + + const confirmReplyComment = ( threadID ) => { + if ( threadID ) { + const newComment = generateReplyComment( commentReply ); + apiFetch( { + path: '/wp/v2/comments/', + method: 'POST', + data: { + parent: threadID, + post: postId, + content: newComment.comment, + comment_date: newComment.createdAt, + comment_type: 'block_comment', + comment_author: currentUser, + comment_approved: 0, + }, + } ).then( ( response ) => { + if ( 'trash' !== response.status && '' !== response.id ) { + setReplyMessage( true ); + } + } ); + } + }; + + return ( + <> + { + // If there are no threads, show a message indicating no threads are available. + ( ! Array.isArray( threads ) || threads.length === 0 ) && ( + + + { __( 'No comments available' ) } + + + ) + } + + { Array.isArray( threads ) && + threads.length > 0 && + threads.reverse().map( ( thread ) => ( + + + { + + + { thread.author_name } + + + + + { thread.status !== 'approved' && ( + + + + + { 0 === thread.parent ? ( + { + setShowConfirmationTabId( + thread.id + ); + onEditComment( + thread.id + ); + }, + }, + { + title: __( 'Delete' ), + onClick: () => { + setCommentEdit( + false + ); + setShowConfirmationTabId( + thread.id + ); + setDeleteCommentShowConfirmation( + true + ); + }, + }, + { + title: __( 'Reply' ), + onClick: () => { + setShowConfirmationTabId( + thread.id + ); + onReplyComment( + thread.id + ); + }, + }, + ] } + /> + ) : ( + { + setShowConfirmationTabId( + thread.id + ); + onEditComment( + thread.id + ); + }, + }, + { + title: __( 'Delete' ), + onClick: () => { + setCommentEdit( + false + ); + setShowConfirmationTabId( + thread.id + ); + setDeleteCommentShowConfirmation( + true + ); + }, + }, + ] } + /> + ) } + + ) } + { thread.status === 'approved' && ( + + + + ) } + + + + + { commentEdit && + thread.id === showConfirmationTabId && ( + <> + /g, + '' + ) + : thread.content.rendered.replace( + /<\/?p>/g, + '' + ) + } + onChange={ ( value ) => { + setNewEditedComment( + value + ); + } } + /> + + setCommentEdit( false ) + } + > + + + + + + + ) } + { thread.content.rendered } + + + + { hasCommentReply && + thread.id === showConfirmationTabId && ( + + + { + setCommentReply( value ); + } } + /> + + setHasCommentReply( false ) + } + > + + + + + + + + ) } + + { commentConfirmation && + thread.id === showConfirmationTabId && ( + + { __( 'Thread marked as resolved.' ) } + + ) } + { commentEditedMessage && + thread.id === showConfirmationTabId && ( + + { __( 'Thread edited successfully.' ) } + + ) } + { commentDeleteMessage && + thread.id === showConfirmationTabId && ( + + { __( 'Thread deleted successfully.' ) } + + ) } + { replyMessage && + thread.id === showConfirmationTabId && ( + + { __( 'Reply added successfully.' ) } + + ) } + + { showConfirmation && + thread.id === showConfirmationTabId && ( + + setShowConfirmation( false ) + } + className="editor-collab-sidebar__useroverlay confirmation-overlay" + spacing="0" + justify="space-between" + > +

+ { __( + 'Are you sure you want to mark this thread as resolved?' + ) } +

+ + + + + + ) } + + { deleteCommentShowConfirmation && + thread.id === showConfirmationTabId && ( + + setDeleteCommentShowConfirmation( + false + ) + } + className="editor-collab-sidebar__useroverlay confirmation-overlay" + spacing="0" + justify="space-between" + > +

+ { __( + 'Are you sure you want to delete this thread?' + ) } +

+ + + + +
+ ) } + + { 0 < thread?.reply?.length && + thread.reply.map( ( reply ) => ( + + + { + + + { reply.author_name } + + + + + { reply.status !== 'approved' && ( + + { 0 === reply.parent && ( + { + setShowConfirmationTabId( + reply.id + ); + onEditComment( + reply.id + ); + }, + }, + { + title: __( + 'Delete' + ), + onClick: + () => { + setCommentEdit( + false + ); + setShowConfirmationTabId( + reply.id + ); + setDeleteCommentShowConfirmation( + true + ); + }, + }, + { + title: __( + 'Reply' + ), + onClick: + () => { + setShowConfirmationTabId( + reply.id + ); + onReplyComment( + reply.id + ); + }, + }, + ] } + /> + ) } + + { 0 !== reply.parent && + thread.status !== + 'approved' && ( + { + setShowConfirmationTabId( + reply.id + ); + onEditComment( + reply.id + ); + }, + }, + { + title: __( + 'Delete' + ), + onClick: + () => { + setCommentEdit( + false + ); + setShowConfirmationTabId( + reply.id + ); + setDeleteCommentShowConfirmation( + true + ); + }, + }, + ] } + /> + ) } + + ) } + + + + + { commentEdit && + reply.id === + showConfirmationTabId && ( + <> + /g, + '' + ) + : reply.content.rendered.replace( + /<\/?p>/g, + '' + ) + } + onChange={ ( + value + ) => { + setNewEditedComment( + value + ); + } } + /> + + setCommentEdit( + false + ) + } + > + + + + + + + ) } + + { reply.content.rendered } + + + + + { hasCommentReply && + reply.id === showConfirmationTabId && ( + + + { + setCommentReply( + value + ); + } } + /> + + setHasCommentReply( + false + ) + } + > + + + + + + + + ) } + + { commentConfirmation && + reply.id === showConfirmationTabId && ( + + { __( + 'Thread marked as resolved.' + ) } + + ) } + { commentEditedMessage && + reply.id === showConfirmationTabId && ( + + { __( + 'Thread edited successfully.' + ) } + + ) } + { commentDeleteMessage && + reply.id === showConfirmationTabId && ( + + { __( + 'Thread deleted successfully.' + ) } + + ) } + { replyMessage && + reply.id === showConfirmationTabId && ( + + { __( + 'Reply added successfully.' + ) } + + ) } + + { showConfirmation && + reply.id === showConfirmationTabId && ( + + setShowConfirmation( false ) + } + className="editor-collab-sidebar__useroverlay confirmation-overlay" + spacing="0" + justify="space-between" + > +

+ { __( + 'Are you sure you want to mark this thread as resolved?' + ) } +

+ + + + +
+ ) } + + { deleteCommentShowConfirmation && + reply.id === showConfirmationTabId && ( + + setDeleteCommentShowConfirmation( + false + ) + } + className="editor-collab-sidebar__useroverlay confirmation-overlay" + spacing="0" + justify="space-between" + > +

+ { __( + 'Are you sure you want to delete this thread?' + ) } +

+ + + + +
+ ) } +
+ ) ) } + + ) ) } + + ); +} diff --git a/packages/editor/src/components/collab-sidebar/index.js b/packages/editor/src/components/collab-sidebar/index.js index c8b7d3d42c95b..215eb6c582a20 100644 --- a/packages/editor/src/components/collab-sidebar/index.js +++ b/packages/editor/src/components/collab-sidebar/index.js @@ -4,29 +4,10 @@ // eslint-disable-next-line no-restricted-imports import apiFetch from '@wordpress/api-fetch'; import { __ } from '@wordpress/i18n'; -import { useSelect, useDispatch, withDispatch, withSelect } from '@wordpress/data'; -import { useState, useEffect, RawHTML } from '@wordpress/element'; -import { - __experimentalHStack as HStack, - __experimentalVStack as VStack, - __experimentalText as Text, - Button, - DropdownMenu, - TextareaControl, - Tooltip, - TextControl, - CheckboxControl, -} from '@wordpress/components'; -import { dateI18n, format, getSettings } from '@wordpress/date'; -import { - comment as commentIcon, - Icon, - check, - published, - moreVertical -} from '@wordpress/icons'; +import { useSelect } from '@wordpress/data'; +import { useState, useEffect } from '@wordpress/element'; +import { comment as commentIcon } from '@wordpress/icons'; -import { registerBlockType } from '@wordpress/blocks'; import { addFilter } from '@wordpress/hooks'; /** @@ -34,14 +15,18 @@ import { addFilter } from '@wordpress/hooks'; */ import PluginSidebar from '../plugin-sidebar'; import { collabSidebarName } from './constants'; +import { Comments } from './comments'; +import { AddComment } from './add-comment'; -import { store as blockEditorStore } from '../../store'; +const isBlockCommentExperimentEnabled = + window?.__experimentalEnableBlockComment; -const isBlockCommentExperimentEnabled = window?.__experimentalEnableBlockComment; - -const modifyBlockCommentAttributes = (settings, name) => { - if ( name && name?.includes( 'core/' ) ) { - if( undefined === settings.attributes.blockCommentId || null === settings.attributes.blockCommentId ) { +const modifyBlockCommentAttributes = ( settings, name ) => { + if ( name && name?.includes( 'core/' ) ) { + if ( + undefined === settings.attributes.blockCommentId || + null === settings.attributes.blockCommentId + ) { settings.attributes = { ...settings.attributes, blockCommentId: { @@ -50,364 +35,82 @@ const modifyBlockCommentAttributes = (settings, name) => { }, }; } - - } - return settings; + } + return settings; }; // Apply the filter to all core blocks addFilter( - 'blocks.registerBlockType', - 'block-comment/modify-core-block-attributes', - modifyBlockCommentAttributes + 'blocks.registerBlockType', + 'block-comment/modify-core-block-attributes', + modifyBlockCommentAttributes ); /** * Renders the Collab sidebar. */ export default function CollabSidebar() { + const [ threads, setThreads ] = useState( [] ); + const [ reloadComments, setReloadComments ] = useState( false ); + const postId = useSelect( ( select ) => { + // eslint-disable-next-line @wordpress/data-no-store-string-literals + return select( 'core/editor' ).getCurrentPostId(); + }, [] ); - const postId = useSelect((select) => { - return select('core/editor').getCurrentPostId(); - }, []); - - const [threads, setThreads] = useState([]); - const [ showConfirmation, setShowConfirmation ] = useState( false ); - const [ showConfirmationTabId, setShowConfirmationTabId ] = useState( 0 ); - const [ commentConfirmation, setCommentConfirmation ] = useState( false ); - const [ deleteCommentShowConfirmation, setDeleteCommentShowConfirmation ] = useState( false ); - const [ commentDeleteMessage, setCommentDeleteMessage ] = useState( false ); - const [ commentEdit, setCommentEdit ] = useState( false ); - const [ newEditedComment, setNewEditedComment ] = useState( '' ); - const [ commentEditedMessage, setCommentEditedMessage ] = useState( false ); - const [ hasCommentReply, setHasCommentReply ] = useState( false ); - const [ commentReply, setCommentReply ] = useState( '' ); - const [ replyMessage, setReplyMessage ] = useState( false ); - - useEffect(() => { - if (postId) { - apiFetch({ - path: '/wp/v2/comments?post=' + postId + '&type=block_comment' + '&status=any&per_page=100', + useEffect( () => { + if ( postId ) { + apiFetch( { + path: + '/wp/v2/comments?post=' + + postId + + '&type=block_comment' + + '&status=any&per_page=100', method: 'GET', - }) - .then((data) => { - + } ).then( ( data ) => { // Create a compare to store the references to all objects by id const compare = {}; const result = []; - + // Initialize each object with an empty `reply` array - data.forEach(item => { - compare[item.id] = { ...item, reply: [] }; - }); - + data.forEach( ( item ) => { + compare[ item.id ] = { ...item, reply: [] }; + } ); + // Iterate over the data to build the tree structure - data.forEach(item => { - if (item.parent === 0) { + data.forEach( ( item ) => { + if ( item.parent === 0 ) { // If parent is 0, it's a root item, push it to the result array - result.push(compare[item.id]); - } else { + result.push( compare[ item.id ] ); + } else if ( + compare[ item.parent ] && + 'trash' !== item.status + ) { // Otherwise, find its parent and push it to the parent's `reply` array - if (compare[item.parent]) { - if( 'trash' !== item.status ) { - compare[item.parent].reply.push(compare[item.id]); - } - } + compare[ item.parent ].reply.push( compare[ item.id ] ); } - }); - const filteredComments = result.filter(comment => comment.status !== 'trash'); - setThreads(Array.isArray(filteredComments) ? filteredComments : []); - - }) + } ); + const filteredComments = result.filter( + ( comment ) => comment.status !== 'trash' + ); + setThreads( + Array.isArray( filteredComments ) ? filteredComments : [] + ); + } ); } - }, [postId]); + }, [ postId, reloadComments ] ); - const { threads: selectedThreads } = useSelect(() => { + const { threads: selectedThreads } = useSelect( () => { return { - threads: threads, + threads, }; - }, [threads]); + }, [ threads ] ); // Check if the experimental flag is enabled. if ( ! isBlockCommentExperimentEnabled ) { return null; // or maybe return some message indicating no threads are available. } - const confirmAndMarkThreadAsResolved = ( threadID ) => { - setCommentConfirmation( false ); - if (threadID) { - apiFetch({ - path: '/wp/v2/comments/' + threadID, - method: 'POST', - data: { - status: 'approved', - } - }) - .then((response) => { - if ( 'approved' === response.status ) { - setShowConfirmation( false ); - setCommentConfirmation( true ); - } - }) - } - }; - - const onEditComment = ( threadID ) => { - if ( threadID ) { - setHasCommentReply( false ); - setCommentEdit( true ); - } - }; - - const confirmEditComment = ( threadID ) => { - if (threadID) { - const editedComment = newEditedComment.replace(/^

|<\/p>$/g, ''); - - apiFetch({ - path: '/wp/v2/comments/' + threadID, - method: 'POST', - data: { - content: editedComment, - } - }) - .then((response) => { - if ( 'trash' !== response.status && '' !== response.id ) { - setCommentEdit( false ); - setCommentEditedMessage( true ); - } - }) - } - }; - - const confirmDeleteComment = ( threadID ) => { - setDeleteCommentShowConfirmation( false ); - if (threadID) { - apiFetch({ - path: '/wp/v2/comments/' + threadID, - method: 'DELETE', - }) - .then((response) => { - if ( 'trash' === response.status && '' !== response.id ) { - setCommentDeleteMessage( true ); - } - }) - } - }; - - const onReplyComment = ( threadID ) => { - if ( threadID ) { - setCommentEdit( false ); - setHasCommentReply( true ); - } - }; - - const confirmReplyComment = ( threadID ) => { - if (threadID) { - - const newComment = generateReplyComment( commentReply ); - const commentId = newComment?.commentId; - const updatedComments = getUpdatedComments( newComment, commentId ); - - apiFetch({ - path: '/wp/v2/comments/', - method: 'POST', - data: { - parent: threadID, - post: postID, - content: newComment.comment, - comment_date: newComment.createdAt, - comment_type: 'block_comment', - comment_author: currentUser, - comment_approved: 0, - } - }) - .then((response) => { - if ( 'trash' !== response.status && '' !== response.id ) { - setReplyMessage( true ); - } - }) - } - } - - // Helper function to get updated comments structure - const getUpdatedComments = ( newComment, threadKey ) => ( { - ...threads, - [ threadKey ]: { - isResolved: false, - createdAt: - threads?.threadKey?.createdAt || new Date().toISOString(), - createdBy: currentUser, - comments: { - 0: newComment, - }, - }, - } ); - - const generateReplyComment = (comment) => ( { - commentId: Date.now(), - createdBy: currentUser, - comment, - createdAt: new Date().toISOString(), - } ); - - const generateNewComment = () => ( { - commentId: Date.now(), - createdBy: currentUser, - comment: inputComment, - createdAt: new Date().toISOString(), - } ); - - // Get the date time format from WordPress settings. - const dateTimeFormat = 'h:i A'; - const resultThreads = selectedThreads.map(thread => thread).reverse(); - - // State to manage the comment thread. - const [ inputComment, setInputComment ] = useState( '' ); - const [ isResolved, setIsResolved ] = useState( false ); - const [ isEditing, setIsEditing ] = useState( null ); - const curruntUserData = useSelect( ( select ) => { - // eslint-disable-next-line @wordpress/data-no-store-string-literals - return select( 'core' ).getCurrentUser(); - }, [] ); - - let userAvatar = 'https://secure.gravatar.com/avatar/92929292929292929292929292929292?s=48&d=mm&r=g'; - if( curruntUserData?.avatar_urls ) { - userAvatar = curruntUserData?.avatar_urls[ 48 ]; - } - - //const userAvatar = curruntUserData?.avatar_urls[ 48 ] ? curruntUserData?.avatar_urls[ 48 ] : null; - - const currentUser = curruntUserData?.name || null; - const allThreads = []; - const threadId = 0; - const currentThread = allThreads[ threadId ] ?? {}; - const isCurrentThreadResolved = currentThread.threadIsResolved || false; - const commentsCount = isCurrentThreadResolved - ? 0 - : currentThread.comments?.length || 0; - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const postID = useSelect( ( select ) => { - // eslint-disable-next-line @wordpress/data-no-store-string-literals - return select( 'core/editor' ).getCurrentPostId(); - }, [] ); - - const clientId = useSelect((select) => { - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const { getSelectedBlockClientId } = select('core/block-editor'); - return getSelectedBlockClientId(); - }, []); - - const blockCommentId = useSelect((select) => { - // eslint-disable-next-line @wordpress/data-no-store-string-literals - return select( 'core/block-editor' ).getBlock( select('core/block-editor').getSelectedBlockClientId() )?.attributes?.blockCommentId; - }, []); - - // Get the dispatch functions to save the comment and update the block attributes. - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const { updateBlockAttributes } = useDispatch( 'core/block-editor' ); - - // eslint-disable-next-line @wordpress/data-no-store-string-literals - const { closeGeneralSidebar } = useDispatch( 'core/edit-post' ); - - // // Function to add a border class to the content reference. - const setAttributes = ( clientId, threadId ) => { - updateBlockAttributes( clientId, { - blockCommentId: threadId, - } ); - - }; - - // Function to save the comment. - const saveComment = () => { - const newComment = generateNewComment(); - apiFetch( { - path: '/wp/v2/comments', - method: 'POST', - data: { - post: postID, - content: newComment.comment, - comment_date: newComment.createdAt, - comment_type: 'block_comment', - comment_author: currentUser, - comment_approved: 0, - }, - } ).then( (response) => { - const threadId = response?.id; - setAttributes( clientId, threadId ); - setInputComment(''); - //onClose(); - } ); - }; - - // Function to edit the comment. - const editComment = ( commentId ) => { - const editedComments = { ...allThreads }; - - if ( - editedComments[ threadId ] && - editedComments[ threadId ].comments - ) { - editedComments[ threadId ].comments.map( ( comment ) => { - if ( comment.commentId === commentId ) { - comment.comment = inputComment; - comment.date = new Date().toISOString(); - } - return comment; - } ); - } - - setInputComment( '' ); - setIsEditing( null ); - }; - - // Function to mark thread as resolved - const markThreadAsResolved = () => { - setIsResolved( true ); - - const updatedComments = { ...allThreads }; - - updatedComments[ threadId ] = { - ...updatedComments[ threadId ], - isResolved: true, - resolvedBy: currentUser, - resolvedAt: new Date().toISOString(), - }; - - onClose(); - }; - - // Function to delete a comment. - const deleteComment = ( commentId ) => { - // Filter out the comment to be deleted. - const currentComments = allThreads[ threadId ].comments.filter( - ( comment ) => comment.commentId !== commentId - ); - - const updatedComments = { ...allThreads }; - - // If there are no comments, delete the thread. - if ( currentComments.length === 0 ) { - delete updatedComments[ threadId ]; - } else { - updatedComments[ threadId ] = { - ...allThreads[ threadId ], - comments: currentComments, - }; - } - }; - - // Function to show the confirmation overlay. - const showConfirmationOverlay = () => setShowConfirmation( true ); - - // Function to hide the confirmation overlay. - const hideConfirmationOverlay = () => setShowConfirmation( false ); - - - // On cancel, remove the border if no comments are present. - const handleCancel = () => { - closeGeneralSidebar("edit-post/collab-sidebar"); - }; - + const resultThreads = selectedThreads.map( ( thread ) => thread ).reverse(); return (

- - { null !== clientId && 0 === blockCommentId && ( - - { 0 < commentsCount && ! isCurrentThreadResolved && ( - <> - { currentThread.comments.map( - ( - { - createdBy, - comment, - timestamp, - commentId, - }, - index - ) => ( - <> - { isEditing === commentId && ( - - - - - { currentUser } - - - - setInputComment( val ) - } - placeholder={ __( - 'Add comment' - ) } - className="block-editor-format-toolbar__comment-input" - /> - - - - { 0 === thread.parent ? ( - { - setShowConfirmationTabId( thread.id ); - onEditComment( thread.id ); - } - }, - { - title: __( 'Delete' ), - onClick: () => { - setCommentEdit( false ); - setShowConfirmationTabId( thread.id ); - setDeleteCommentShowConfirmation( true ); - } - }, - { - title: __( 'Reply' ), - onClick: () => { - setShowConfirmationTabId( thread.id ); - onReplyComment( thread.id ); - } - }, - ] } - /> - ) : ( - { - setShowConfirmationTabId( thread.id ); - onEditComment( thread.id ); - } - }, - { - title: __( 'Delete' ), - onClick: () => { - setCommentEdit( false ); - setShowConfirmationTabId( thread.id ); - setDeleteCommentShowConfirmation( true ); - } - }, - ] } - /> - ) } - - - } - { thread.status === 'approved' && - - - - } - - - - - { commentEdit && thread.id === showConfirmationTabId && ( - <> - /g, '') : thread.content.rendered.replace(/<\/?p>/g, '') } - onChange={ ( value ) => { - setNewEditedComment( value ); - } } - /> - setCommentEdit( false ) } - > - - - - - - - ) } - - { thread.content.rendered } - - - - - { hasCommentReply && thread.id === showConfirmationTabId && ( - - - { - setCommentReply( value ); - } } - /> - setHasCommentReply( false ) } - > - - - - - - - - ) } - - { commentConfirmation && thread.id === showConfirmationTabId && ( - - { __( 'Thread marked as resolved.' ) } - - ) } - { commentEditedMessage && thread.id === showConfirmationTabId && ( - - { __( 'Thread edited successfully.' ) } - - ) } - { commentDeleteMessage && thread.id === showConfirmationTabId && ( - - { __( 'Thread deleted successfully.' ) } - - ) } - { replyMessage && thread.id === showConfirmationTabId && ( - - { __( 'Reply added successfully.' ) } - - ) } - - { showConfirmation && thread.id === showConfirmationTabId && ( - setShowConfirmation( false ) } - className="editor-collab-sidebar__useroverlay confirmation-overlay" - spacing="0" - justify="space-between" - > -

- { __( - 'Are you sure you want to mark this thread as resolved?' - ) } -

- - - - -
- ) } - - { deleteCommentShowConfirmation && thread.id === showConfirmationTabId && ( - setDeleteCommentShowConfirmation( false ) } - className="editor-collab-sidebar__useroverlay confirmation-overlay" - spacing="0" - justify="space-between" - > -

- { __( - 'Are you sure you want to delete this thread?' - ) } -

- - - - -
- ) } - - { 0 < thread.reply.length && ( - thread.reply.map((reply) => ( - - - { - - - { reply.author_name } - - - - - { reply.status !== 'approved' && - - { 0 === reply.parent && ( - { - setShowConfirmationTabId( reply.id ); - onEditComment( reply.id ); - } - }, - { - title: __( 'Delete' ), - onClick: () => { - setCommentEdit( false ); - setShowConfirmationTabId( reply.id ); - setDeleteCommentShowConfirmation( true ); - } - }, - { - title: __( 'Reply' ), - onClick: () => { - setShowConfirmationTabId( reply.id ); - onReplyComment( reply.id ); - } - }, - ] } - /> - ) } - - { 0 !== reply.parent && thread.status !== 'approved' && ( - { - setShowConfirmationTabId( reply.id ); - onEditComment( reply.id ); - } - }, - { - title: __( 'Delete' ), - onClick: () => { - setCommentEdit( false ); - setShowConfirmationTabId( reply.id ); - setDeleteCommentShowConfirmation( true ); - } - }, - ] } - /> - ) } - - - } - - - - - { commentEdit && reply.id === showConfirmationTabId && ( - <> - /g, '') : reply.content.rendered.replace(/<\/?p>/g, '') } - onChange={ ( value ) => { - setNewEditedComment( value ); - } } - /> - setCommentEdit( false ) } - > - - - - - - - ) } - - { reply.content.rendered } - - - - - { hasCommentReply && reply.id === showConfirmationTabId && ( - - - { - setCommentReply( value ); - } } - /> - setHasCommentReply( false ) } - > - - - - - - - - ) } - - { commentConfirmation && reply.id === showConfirmationTabId && ( - - { __( 'Thread marked as resolved.' ) } - - ) } - { commentEditedMessage && reply.id === showConfirmationTabId && ( - - { __( 'Thread edited successfully.' ) } - - ) } - { commentDeleteMessage && reply.id === showConfirmationTabId && ( - - { __( 'Thread deleted successfully.' ) } - - ) } - { replyMessage && reply.id === showConfirmationTabId && ( - - { __( 'Reply added successfully.' ) } - - ) } - - { showConfirmation && reply.id === showConfirmationTabId && ( - setShowConfirmation( false ) } - className="editor-collab-sidebar__useroverlay confirmation-overlay" - spacing="0" - justify="space-between" - > -

- { __( - 'Are you sure you want to mark this thread as resolved?' - ) } -

- - - - -
- ) } - - { deleteCommentShowConfirmation && reply.id === showConfirmationTabId && ( - setDeleteCommentShowConfirmation( false ) } - className="editor-collab-sidebar__useroverlay confirmation-overlay" - spacing="0" - justify="space-between" - > -

- { __( - 'Are you sure you want to delete this thread?' - ) } -

- - - - -
- ) } - -
- - )) - ) } - -
- )) - } - + +
); diff --git a/packages/editor/src/components/collab-sidebar/style.scss b/packages/editor/src/components/collab-sidebar/style.scss index bb4c8fde25893..6c8b582ba0731 100644 --- a/packages/editor/src/components/collab-sidebar/style.scss +++ b/packages/editor/src/components/collab-sidebar/style.scss @@ -1,174 +1,174 @@ .editor-collab-sidebar { - &__activities { - padding: 16px; - background-color: #f9f9f9; - } - - &__thread { - position: relative; - padding: 16px; - border-radius: 8px; - border: 1px solid $gray-300; - background-color: $gray-100; - margin-bottom: 16px; - - .components-base-control__field { - margin-bottom: 0 - } - - input[type=text] { - @include input-control; - border-radius: 6px; - padding: 8px 15px; - } - } - - &__activethread { - border: 1.5px solid #3858E9; - background-color: $white; - box-shadow: 0px 5.5px 7.8px -0.3px rgba(0, 0, 0, 0.102); - } - - - &__editarea, - &__replyComment { - flex: 1; - - &__textarea{ - width: 100%; - } - - .components-textarea-control__input { - border: 1px solid $gray-300; - border-radius: 8px; - padding: 10px 15px; - background-color: $gray-100; - } - } - - &__childThread { - margin-top: 15px; - } - - &__userName { - font-size: 12px; - font-weight: 400; - line-height: 16px; - text-align: left; - color: $gray-700; - text-transform: capitalize; - } - - &__usertime { - font-size: 12px; - font-weight: 400; - line-height: 16px; - text-align: left; - color: $gray-700; - } - - &__usercomment { - font-size: 13px; - font-weight: 400; - line-height: 20px; - text-align: left; - color: $gray-900; - - p { - margin-bottom: 0; - } - } - - &__userIcon { - border-radius: 50%; - flex-shrink: 0; - } - - &__userstatus { - button { - font-size: 12px; - font-weight: 400; - line-height: 16px; - padding: 0; - height: auto; - box-shadow: none; - outline: none; - } - } - - &__useroverlay { - background-color: rgba(0, 0, 0, 0.7); - width: 100%; - height: 100%; - text-align: center; - position: absolute; - top: 0; - left: 0; - z-index: 1; - padding: 15px; - border-radius: 8px; - color: $white; - - p { - margin-bottom: 15px; - } - - button { - height: 24px; - padding: 6px; - color: $white; - } - } - - &__resolvedIcon { - fill: #008000; - border-radius: 50%; - width: 18px; - height: 18px; - border: 1px solid #008000; - margin-right: 2px; - margin-top: 5px; - } - - &__commentUpdate { - margin-left: auto; - - .components-button { - &.is-compact { - &.has-icon:not(.has-text) { - min-width: 24px; - padding: 0; - width: 24px; - height: 24px; - flex-shrink: 0; - } - } - } - - .components-dropdown { - &.is-compact { - flex-shrink: 0; - - .components-button { - min-width: 24px; - padding: 0; - width: 24px; - height: 24px; - } - } - } - } - - &__resolvedText { - font-style: italic; - font-weight: 500 !important; - color: #808080 !important; - margin-top: 5px !important; - } + &__activities { + padding: 16px; + background-color: #f9f9f9; + } + + &__thread { + position: relative; + padding: 16px; + border-radius: 8px; + border: 1px solid $gray-300; + background-color: $gray-100; + margin-bottom: 16px; + + .components-base-control__field { + margin-bottom: 0; + } + + input[type="text"] { + @include input-control; + border-radius: 6px; + padding: 8px 15px; + } + } + + &__activethread { + border: 1.5px solid #3858e9; + background-color: $white; + box-shadow: 0 5.5px 7.8px -0.3px rgba(0, 0, 0, 0.102); + } + + + &__editarea, + &__replyComment { + flex: 1; + + &__textarea { + width: 100%; + } + + .components-textarea-control__input { + border: 1px solid $gray-300; + border-radius: 8px; + padding: 10px 15px; + background-color: $gray-100; + } + } + + &__childThread { + margin-top: 15px; + } + + &__userName { + font-size: 12px; + font-weight: 400; + line-height: 16px; + text-align: left; + color: $gray-700; + text-transform: capitalize; + } + + &__usertime { + font-size: 12px; + font-weight: 400; + line-height: 16px; + text-align: left; + color: $gray-700; + } + + &__usercomment { + font-size: 13px; + font-weight: 400; + line-height: 20px; + text-align: left; + color: $gray-900; + + p { + margin-bottom: 0; + } + } + + &__userIcon { + border-radius: 50%; + flex-shrink: 0; + } + + &__userstatus { + button { + font-size: 12px; + font-weight: 400; + line-height: 16px; + padding: 0; + height: auto; + box-shadow: none; + outline: none; + } + } + + &__useroverlay { + background-color: rgba(0, 0, 0, 0.7); + width: 100%; + height: 100%; + text-align: center; + position: absolute; + top: 0; + left: 0; + z-index: 1; + padding: 15px; + border-radius: 8px; + color: $white; + + p { + margin-bottom: 15px; + } + + button { + height: 24px; + padding: 6px; + color: $white; + } + } + + &__resolvedIcon { + fill: #008000; + border-radius: 50%; + width: 18px; + height: 18px; + border: 1px solid #008000; + margin-right: 2px; + margin-top: 5px; + } + + &__commentUpdate { + margin-left: auto; + + .components-button { + &.is-compact { + &.has-icon:not(.has-text) { + min-width: 24px; + padding: 0; + width: 24px; + height: 24px; + flex-shrink: 0; + } + } + } + + .components-dropdown { + &.is-compact { + flex-shrink: 0; + + .components-button { + min-width: 24px; + padding: 0; + width: 24px; + height: 24px; + } + } + } + } + + &__resolvedText { + font-style: italic; + font-weight: 500 !important; + color: #808080 !important; + margin-top: 5px !important; + } } .is-collab-block-selected { - background-color: color-mix( in srgb, currentColor 5%, #182F97 20% ); + background-color: color-mix(in srgb, currentColor 5%, #182f97 20%); } .block-editor-format-toolbar__comment-board { diff --git a/packages/format-library/package.json b/packages/format-library/package.json index 7ea25a917549d..531ec71224ce7 100644 --- a/packages/format-library/package.json +++ b/packages/format-library/package.json @@ -1,6 +1,6 @@ { "name": "@wordpress/format-library", - "version": "5.1.0", + "version": "5.6.0", "description": "Format library for the WordPress editor.", "author": "The WordPress Contributors", "license": "GPL-2.0-or-later", @@ -31,10 +31,7 @@ "@wordpress/block-editor": "file:../block-editor", "@wordpress/components": "file:../components", "@wordpress/compose": "file:../compose", - "@wordpress/core-data": "file:../core-data", "@wordpress/data": "file:../data", - "@wordpress/date": "file:../date", - "@wordpress/editor": "file:../editor", "@wordpress/element": "file:../element", "@wordpress/html-entities": "file:../html-entities", "@wordpress/i18n": "file:../i18n",