Skip to content

Commit

Permalink
Add VBE editor injector (#86123)
Browse files Browse the repository at this point in the history
Co-authored-by: escapemanuele <[email protected]>
Co-authored-by: Renan <[email protected]>
  • Loading branch information
3 people authored and TimBroddin committed Jan 18, 2024
1 parent 529f79e commit 4955117
Show file tree
Hide file tree
Showing 14 changed files with 243 additions and 195 deletions.
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ cached-requests.json
/packages/*/dist/
/build-tools/dist/
/apps/*/.cache/
/packages/*/.cache/
/desktop/.cache/
/apps/*/release-files/
/apps/**/*/build_meta.json
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,12 +24,16 @@ const CommentBlockEditor = ( {

useEffect( () => {
if ( siteId ) {
addApiMiddleware( siteId );
addApiMiddleware( ( url ) => ( {
path: `/sites/${ encodeURIComponent( siteId ) }/proxy`,
query: `url=${ encodeURIComponent( url ) }`,
apiNamespace: 'oembed/1.0',
} ) );
}
}, [ siteId ] );

return (
<div className="editor__wrapper">
<div className="verbum-editor-wrapper">
<Editor initialContent={ commentContent } onChange={ onChange } isRTL={ isRTL } />
</div>
);
Expand Down
34 changes: 27 additions & 7 deletions packages/verbum-block-editor/README.md
Original file line number Diff line number Diff line change
@@ -1,14 +1,34 @@
# Verbum Block Editor

Verbum Block Editor is a lightweight Gutenberg editor designed specifically for comments. It provides a simplified and intuitive interface for users to compose and format their comments effortlessly.
Verbum Block Editor is a lightweight Gutenberg editor, tailored specifically for enhancing the commenting experience. It offers a user-friendly interface, enabling effortless composition and formatting of comments.

## Features

- Autofocus on the last block when the empty white space is clicked.
- Autofocus on the first paragraph on load.
- Handles embeds by adding all the needed API middlewares.
- Uses and iframed editor to limit CSS collisions.
- Automatically focuses on the last block when clicking on any empty white space.
- Initial focus is set to the first paragraph upon loading.
- Efficiently handles embeds by integrating all necessary API middlewares.
- Incorporates an iframed editor to minimize CSS collisions.

## WIP
Soon, this will be published on NPM and used everywhere a user can edit comments (in Verbum and in Calypso's `/comments/all/` page).
## Development

This package can be utilized in two primary ways:

### Directly In Calypso
- The package is directly integrated into Calypso as a standard package.
- No separate build process is required after modifications.
- Direct file alterations are reflected immediately in Calypso.

### Via widgets.wp.com
- The package publishes a bundle on widgets.wp.com for broader accessibility.
- Development process:
1. Navigate to the package's directory: `cd package/verbum-block-editor`.
2. Execute `yarn dev --sync`.
3. Changes are synchronized to `/home/wpcom/public_html/widgets.wp.com/verbum-block-editor` on your sandbox.

### Deploying Changes

To deploy modifications to the package:
1. Ensure your sandbox is in a clean git state.
2. Run `yarn build --sync`.
3. Create a patch.
4. Deploy the patch.
8 changes: 5 additions & 3 deletions packages/verbum-block-editor/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -23,13 +23,15 @@
"bugs": "https://github.com/Automattic/wp-calypso/issues",
"types": "dist/types",
"scripts": {
"clean": "tsc --build ./tsconfig.json ./tsconfig-cjs.json --clean && rm -rf dist",
"build": "tsc --build ./tsconfig.json ./tsconfig-cjs.json && copy-assets",
"prepack": "yarn run clean && yarn run build",
"clean": "rm -rf dist",
"build": "NODE_ENV=production yarn dev",
"build:app": "calypso-build",
"dev": "yarn run calypso-apps-builder --localPath dist --remotePath /home/wpcom/public_html/widgets.wp.com/verbum-block-editor",
"watch": "tsc --build ./tsconfig.json --watch",
"prepare": "yarn build"
},
"dependencies": {
"@automattic/calypso-apps-builder": "workspace:^",
"@types/wordpress__block-editor": "^11.5.8",
"@wordpress/base-styles": "^4.39.0",
"@wordpress/block-editor": "^12.16.0",
Expand Down
19 changes: 13 additions & 6 deletions packages/verbum-block-editor/src/api/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,19 +18,26 @@ function createFallbackResponse( url: string ) {
};
}

export function addApiMiddleware( siteId: number ) {
export type EmbedRequestParams = {
path: string;
query: string;
apiNamespace: string;
};

export function addApiMiddleware(
requestParamsGenerator: ( embedURL: string ) => EmbedRequestParams
) {
apiFetch.setFetchHandler( ( options ) => {
const { path } = options;

if ( path?.startsWith( '/oembed/1.0/proxy' ) ) {
const url = new URL( 'https://wordpress.com' + path );
const embedUrl = url.searchParams.get( 'url' );

return wpcomRequest( {
path: `/sites/${ encodeURIComponent( siteId ) }/proxy`,
query: `url=${ embedUrl }`,
apiNamespace: 'oembed/1.0',
} );
if ( embedUrl ) {
return wpcomRequest( requestParamsGenerator( embedUrl ) );
}
return Promise.reject( new Error( 'Invalid embed URL' ) );
}

return defaultFetchHandler( options );
Expand Down
138 changes: 76 additions & 62 deletions packages/verbum-block-editor/src/editor/editor-style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -4,95 +4,109 @@
@import "@wordpress/block-editor/build-style/style";
@import "@wordpress/block-editor/build-style/content.css";

.editor__wrapper {
.verbum-editor-wrapper {
width: 100%;
padding: 0 10px;
box-sizing: border-box;
}

.editor__header {
top: 0;
overflow: hidden;
position: sticky;
display: grid;
grid-template-rows: 0fr;
border: 1px solid var(--color-neutral-0);
// Avoid double border.
border-bottom: none;

&.is-editing {
overflow: visible;
grid-template-rows: 1fr;
transition: all 0.5s 0.5s ease-in-out;
}

@at-root body.admin-bar & {
top: 32px;
}

.editor__header-wrapper {
display: flex;
.editor__header {
top: 0;
overflow: hidden;
justify-content: space-between;
align-items: center;
position: sticky;
display: grid;
border: 1px solid var(--color-neutral-0);
// Avoid double border.
border-bottom: none;

&.is-editing {
overflow: visible;
}

.editor__header-toolbar {
flex-grow: 1;
@at-root body.admin-bar & {
top: 32px;
}

.block-editor-block-toolbar {
.editor__header-wrapper {
display: flex;
overflow: hidden;
justify-content: space-between;
align-items: center;
min-height: 52px;
border-bottom: solid 1px #dcdcde;

.editor__header-toolbar {
flex-grow: 1;

.block-editor-block-settings-menu,
button:disabled {
display: none;
.block-editor-block-contextual-toolbar.components-accessible-toolbar {
border-bottom: none;
}

.block-editor-block-parent-selector {
background-color: transparent;
.block-editor-block-toolbar {

.block-editor-block-settings-menu,
button:disabled {
display: none;
}

.block-editor-block-parent-selector {
background-color: transparent;

button.block-editor-block-parent-selector__button {
border: none;
button.block-editor-block-parent-selector__button {
border: none;
}
}
}
}
}

.block-editor-inserter {
padding: 5px 8px;
.block-editor-inserter {
padding: 5px 8px;

.block-editor-inserter__toggle {
svg {
margin: auto;
.block-editor-inserter__toggle {
svg {
margin: auto;
}
}
}
}

.block-editor-media-placeholder__url-input-form {
display: flex;
box-sizing: border-box;
}
.block-editor-media-placeholder__url-input-form {
display: flex;
box-sizing: border-box;
}

.block-editor-media-flow__url-input {
border-top: 1px solid #1e1e1e;
margin-top: -9px;
padding: 8px 16px;
.block-editor-media-flow__url-input {
border-top: 1px solid #1e1e1e;
margin-top: -9px;
padding: 8px 16px;

.block-editor-media-replace-flow__image-url-label {
display: block;
margin-bottom: 8px;
font-size: 13px;
}
.block-editor-media-replace-flow__image-url-label {
display: block;
margin-bottom: 8px;
font-size: 13px;
}

.block-editor-link-control {
width: 300px;
.block-editor-link-control {
width: 300px;
}
}
}
}
}

.editor__main {
border: solid 1px var(--color-neutral-0);
margin-bottom: 10px;
.editor__main {
border: 1px solid var(--color-neutral-0);
margin-bottom: 10px;
&.loading-placeholder {
&.loading {
display: flex;
justify-content: center;
align-items: center;
}
}

&,
& iframe {
min-height: 140px;
}
}
}


Expand Down
45 changes: 24 additions & 21 deletions packages/verbum-block-editor/src/editor/index.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,17 @@ import {
store as blockEditorStore,
// @ts-expect-error - Typings missing
} from '@wordpress/block-editor';
import { parse } from '@wordpress/blocks';
import { createBlock, serialize, type BlockInstance } from '@wordpress/blocks';
import { Popover, SlotFillProvider, KeyboardShortcuts } from '@wordpress/components';
import { useStateWithHistory, useResizeObserver } from '@wordpress/compose';
import { useDispatch } from '@wordpress/data';
import { useState, useEffect, useCallback } from '@wordpress/element';
import React, { useState, useEffect, useCallback } from '@wordpress/element';
import { rawShortcut } from '@wordpress/keycodes';
import classNames from 'classnames';
import { safeParse } from '../utils';
import { editorSettings } from './editor-settings';
import { EditorProps, StateWithUndoManager } from './editor-types';
import type { MouseEvent, KeyboardEvent } from 'react';
import type { MouseEvent, KeyboardEvent, FC } from 'react';
import css from '!!css-loader!sass-loader!./inline-iframe-style.scss';
import './editor-style.scss';

Expand All @@ -28,14 +28,16 @@ const iframedCSS = css.reduce( ( css: string, [ , item ]: [ string, string ] ) =
/**
* Editor component
*/
export const Editor: React.FC< EditorProps > = ( { initialContent = '', onChange, isRTL } ) => {
export const Editor: FC< EditorProps > = ( { initialContent = '', onChange, isRTL } ) => {
// We keep the content in state so we can access the blocks in the editor.
const {
value: editorContent,
setValue,
undo,
redo,
} = useStateWithHistory( parse( initialContent ) ) as unknown as StateWithUndoManager;
} = useStateWithHistory(
initialContent !== '' ? safeParse( initialContent ) : [ createBlock( 'core/paragraph' ) ]
) as unknown as StateWithUndoManager;
const [ isEditing, setIsEditing ] = useState( false );

const handleContentUpdate = useCallback(
Expand All @@ -53,26 +55,27 @@ export const Editor: React.FC< EditorProps > = ( { initialContent = '', onChange

const selectLastBlock = ( event?: MouseEvent | KeyboardEvent ) => {
const lastBlock = editorContent[ editorContent.length - 1 ];
if ( lastBlock ) {
// If this is a click event only shift focus if the click is in the root.
// We don't want to shift focus if the click is in a block.
if ( event ) {
if ( ( event.target as HTMLDivElement ).dataset.isDropZone ) {
// If the last block isn't a paragraph, add a new one.
// This allows the user to add text after a non-text block without clicking the inserter.
if ( lastBlock.name !== 'core/paragraph' ) {
const newParagraph = createBlock( 'core/paragraph' );
handleContentUpdate( [ ...editorContent, newParagraph ] );
selectBlock( newParagraph.clientId );
}

// If this is a click event only shift focus if the click is in the root.
// We don't want to shift focus if the click is in a block.
if ( event ) {
if ( ( event.target as HTMLDivElement ).dataset.isDropZone ) {
// If the last block isn't a paragraph, add a new one.
// This allows the user to add text after a non-text block without clicking the inserter.
if ( lastBlock.name !== 'core/paragraph' ) {
const newParagraph = createBlock( 'core/paragraph' );
handleContentUpdate( [ ...editorContent, newParagraph ] );
selectBlock( newParagraph.clientId );
selectBlock( lastBlock.clientId );
} else {
return;
}

selectBlock( lastBlock.clientId );
} else {
return;
}
}

selectBlock( lastBlock.clientId );
selectBlock( lastBlock.clientId );
}
};

useEffect( () => {
Expand Down
1 change: 1 addition & 0 deletions packages/verbum-block-editor/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,3 +2,4 @@ export { Editor } from './editor';
export { loadTextFormatting } from './load-text-formatting';
export { loadBlocksWithCustomizations } from './load-blocks';
export { addApiMiddleware } from './api';
export { attachGutenberg } from './text-area-injector';
Loading

0 comments on commit 4955117

Please sign in to comment.