Skip to content

Commit

Permalink
Improved rich text placeholder (#16733)
Browse files Browse the repository at this point in the history
* Improved rich text placeholder

* Rerender if placeholder changes

* Adjust placeholder padding for paragraph

* Use is-selected instead of :focus
  • Loading branch information
ellatrix authored Jul 26, 2019
1 parent 32b7b34 commit b51e44f
Show file tree
Hide file tree
Showing 18 changed files with 48 additions and 104 deletions.
2 changes: 0 additions & 2 deletions docs/designers-developers/developers/richtext.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ wp.blocks.registerBlockType( /* ... */, {
props.setAttributes( { content: content } ); // Store updated content as a block attribute
},
placeholder: __( 'Heading...' ), // Display this text before any content has been added by the user
keepPlaceholderOnFocus: true // Keep the placeholder text showing even when the field is focused (leave this property off to remove placeholder content on focus)
} );
},

Expand Down Expand Up @@ -85,7 +84,6 @@ registerBlockType( /* ... */, {
formattingControls={ [ 'bold', 'italic' ] } // Allow the content to be made bold or italic, but do not allow other formatting options
onChange={ ( content ) => setAttributes( { content } ) } // Store updated content as a block attribute
placeholder={ __( 'Heading...' ) } // Display this text before any content has been added by the user
keepPlaceholderOnFocus // Keep the placeholder text showing even when the field is focused (leave this property off to remove placeholder content on focus)
/>
);
},
Expand Down
4 changes: 0 additions & 4 deletions packages/block-editor/src/components/rich-text/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -53,10 +53,6 @@ Render a rich [`contenteditable` input](https://developer.mozilla.org/en-US/docs

*Optional.* Whether to show the input is selected or not in order to show the formatting controls. By default it renders the controls when the block is selected.

### `keepPlaceholderOnFocus: Boolean`

*Optional.* By default, the placeholder will hide as soon as the editable field receives focus. With this setting it can be be kept while the field is focussed and empty.

### `autocompleters: Array<Completer>`

*Optional.* A list of autocompleters to use instead of the default.
Expand Down
4 changes: 1 addition & 3 deletions packages/block-editor/src/components/rich-text/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -293,7 +293,6 @@ class RichTextWrapper extends Component {
isSelected: originalIsSelected,
onCreateUndoLevel,
placeholder,
keepPlaceholderOnFocus,
// eslint-disable-next-line no-unused-vars
allowedFormats,
withoutInteractiveFormatting,
Expand Down Expand Up @@ -338,9 +337,8 @@ class RichTextWrapper extends Component {
onSelectionChange={ onSelectionChange }
tagName={ tagName }
wrapperClassName={ classnames( wrapperClasses, wrapperClassName ) }
className={ classnames( classes, className ) }
className={ classnames( classes, className, { 'is-selected': originalIsSelected } ) }
placeholder={ placeholder }
keepPlaceholderOnFocus={ keepPlaceholderOnFocus }
allowedFormats={ adjustedAllowedFormats }
withoutInteractiveFormatting={ withoutInteractiveFormatting }
onEnter={ this.onEnter }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -49,7 +49,6 @@ function RichTextWraper( {
isSelected: originalIsSelected,
onCreateUndoLevel,
placeholder,
keepPlaceholderOnFocus,
// From experimental filter.
...experimentalProps
} ) {
Expand All @@ -68,7 +67,6 @@ function RichTextWraper( {
wrapperClassName={ classnames( wrapperClasses, wrapperClassName ) }
className={ classnames( classes, className ) }
placeholder={ placeholder }
keepPlaceholderOnFocus={ keepPlaceholderOnFocus }
__unstableIsSelected={ originalIsSelected }
//__unstablePatterns={ getPatterns() }
//__unstableEnterPatterns={ getEnterPatterns() }
Expand Down
37 changes: 12 additions & 25 deletions packages/block-editor/src/components/rich-text/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -34,39 +34,26 @@
}
}

&[data-is-placeholder-visible="true"] {
position: absolute;
top: 0;
width: 100%;
margin-top: 0;

& > p {
margin-top: 0;
}

// Ensure that if placeholder wraps (mobile/nested contexts) the clickable area is full-height.
height: 100%;
}

// Placeholder text.
& + .block-editor-rich-text__editable {
[data-rich-text-placeholder]::after {
content: attr(data-rich-text-placeholder);
pointer-events: none;

// Use opacity to work in various editor styles.
// We don't specify the color here, because blocks or editor styles might provide their own.
&,
p {
opacity: 0.62;
}
opacity: 0.62;
}

// Captions may have lighter (gray) text, or be shown on a range of different background luminosites.
// To ensure legibility, we increase the default placeholder opacity to ensure contrast.
&[data-is-placeholder-visible="true"] + figcaption.block-editor-rich-text__editable {
opacity: 0.8;
// Could be unset for individual rich text instances.
&.is-selected [data-rich-text-placeholder]::after {
display: none;
}
}

// Captions may have lighter (gray) text, or be shown on a range of different background luminosites.
// To ensure legibility, we increase the default placeholder opacity to ensure contrast.
figcaption.block-editor-rich-text__editable [data-rich-text-placeholder]::before {
opacity: 0.8;
}

.block-editor-rich-text__inline-toolbar {
display: flex;
justify-content: center;
Expand Down
1 change: 0 additions & 1 deletion packages/block-library/src/button/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,6 @@ class ButtonEdit extends Component {
backgroundColor: backgroundColor.color,
color: textColor.color,
} }
keepPlaceholderOnFocus
/>
<BaseControl
label={ __( 'Link' ) }
Expand Down
24 changes: 2 additions & 22 deletions packages/block-library/src/button/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@
}

// Make placeholder text white unless custom colors or outline versions are chosen.
&:not(.has-text-color):not(.is-style-outline) .block-editor-rich-text__editable[data-is-placeholder-visible="true"] + .block-editor-rich-text__editable {
&:not(.has-text-color):not(.is-style-outline) [data-rich-text-placeholder]::after {
color: $white;
}

Expand All @@ -36,29 +36,14 @@
}

// Increase placeholder opacity to meet contrast ratios.
.block-editor-rich-text__editable[data-is-placeholder-visible="true"] + .block-editor-rich-text__editable {
[data-rich-text-placeholder]::after {
opacity: 0.8;
}

// Make placeholder disappear on focus to make focus-state visible.
.block-editor-rich-text__editable[data-is-placeholder-visible="true"]:focus + .block-editor-rich-text__editable {
opacity: 0;
}

// Align cursor to left when placeholder is active.
.block-editor-rich-text__editable[data-is-placeholder-visible="true"] {
text-align: left;
}

// Don't let the placeholder text wrap in the variation preview.
.block-editor-block-preview__content & {
max-width: 100%;

// Polish the empty placeholder text for the button in variation previews.
.block-editor-rich-text__editable[data-is-placeholder-visible="true"] {
height: auto;
}

.wp-block-button__link {
max-width: 100%;
overflow: hidden;
Expand All @@ -70,11 +55,6 @@
text-overflow: ellipsis;
}
}

// Limit width of the text field if empty
.wp-block-button__link[data-is-placeholder-visible="true"] {
max-width: 150px;
}
}

.wp-block-button__inline-link {
Expand Down
2 changes: 0 additions & 2 deletions packages/block-library/src/file/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -212,7 +212,6 @@ class FileEdit extends Component {
tagName="div" // must be block-level or else cursor disappears
value={ fileName }
placeholder={ __( 'Write file name…' ) }
keepPlaceholderOnFocus
withoutInteractiveFormatting
onChange={ ( text ) => setAttributes( { fileName: text } ) }
/>
Expand All @@ -225,7 +224,6 @@ class FileEdit extends Component {
value={ downloadButtonText }
withoutInteractiveFormatting
placeholder={ __( 'Add text…' ) }
keepPlaceholderOnFocus
onChange={ ( text ) => setAttributes( { downloadButtonText: text } ) }
/>
</div>
Expand Down
8 changes: 3 additions & 5 deletions packages/block-library/src/gallery/editor.scss
Original file line number Diff line number Diff line change
Expand Up @@ -35,11 +35,6 @@ ul.wp-block-gallery {
overflow-y: auto;
}

.block-editor-rich-text figcaption:not([data-is-placeholder-visible="true"]) {
position: relative;
overflow: hidden;
}

.is-selected .block-editor-rich-text {
// IE calculates this incorrectly, so leave it to modern browsers.
@supports (position: sticky) {
Expand Down Expand Up @@ -74,6 +69,9 @@ ul.wp-block-gallery {
}

.block-editor-rich-text figcaption {
position: relative;
overflow: hidden;

a {
color: $white;
}
Expand Down
3 changes: 2 additions & 1 deletion packages/block-library/src/paragraph/editor.scss
Original file line number Diff line number Diff line change
@@ -1,7 +1,8 @@
// Specific to the empty paragraph placeholder:
// when shown on mobile and in nested contexts, one or more icons show up on the right.
// This padding makes sure it doesn't overlap text.
.block-editor-rich-text__editable[data-is-placeholder-visible="true"] + .block-editor-rich-text__editable.wp-block-paragraph {
.block-editor-rich-text__editable.wp-block-paragraph:not(.is-selected) [data-rich-text-placeholder]::after {
display: inline-block;
padding-right: $icon-button-size * 3;

// In nested contexts only one icon shows up.
Expand Down
2 changes: 0 additions & 2 deletions packages/block-library/src/search/edit.js
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,6 @@ export default function SearchEdit( { className, attributes, setAttributes } ) {
wrapperClassName="wp-block-search__label"
aria-label={ __( 'Label text' ) }
placeholder={ __( 'Add label…' ) }
keepPlaceholderOnFocus
withoutInteractiveFormatting
value={ label }
onChange={ ( html ) => setAttributes( { label: html } ) }
Expand All @@ -33,7 +32,6 @@ export default function SearchEdit( { className, attributes, setAttributes } ) {
className="wp-block-search__button-rich-text"
aria-label={ __( 'Button text' ) }
placeholder={ __( 'Add button text…' ) }
keepPlaceholderOnFocus
withoutInteractiveFormatting
value={ buttonText }
onChange={ ( html ) => setAttributes( { buttonText: html } ) }
Expand Down
2 changes: 1 addition & 1 deletion packages/edit-post/src/components/visual-editor/style.scss
Original file line number Diff line number Diff line change
Expand Up @@ -105,7 +105,7 @@
}

// Ensure that the height of the first appender, and the one between blocks, is the same as text.
.block-editor-block-list__block[data-type="core/paragraph"] p[data-is-placeholder-visible="true"] + p,
.block-editor-block-list__block[data-type="core/paragraph"] p,
.block-editor-default-block-appender__content {
min-height: $empty-paragraph-height / 2;
line-height: $editor-line-height;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,4 @@
.block-editor-block-list__empty-block-inserter {
left: -18px;
}

.block-editor-rich-text__editable[data-is-placeholder-visible="true"] + .block-editor-rich-text__editable.wp-block-paragraph {
padding: 0;
}
}
13 changes: 0 additions & 13 deletions packages/rich-text/src/component/editable.js
Original file line number Diff line number Diff line change
Expand Up @@ -81,8 +81,6 @@ function applyInternetExplorerInputFix( editorNode ) {
};
}

const IS_PLACEHOLDER_VISIBLE_ATTR_NAME = 'data-is-placeholder-visible';

/**
* Whether or not the user agent is Internet Explorer.
*
Expand All @@ -106,8 +104,6 @@ export default class Editable extends Component {
// update the attributes on the wrapper nodes here. `componentDidUpdate`
// will never be called.
shouldComponentUpdate( nextProps ) {
this.configureIsPlaceholderVisible( nextProps.isPlaceholderVisible );

if ( ! isEqual( this.props.style, nextProps.style ) ) {
this.editorNode.setAttribute( 'style', '' );
Object.assign( this.editorNode.style, {
Expand All @@ -129,13 +125,6 @@ export default class Editable extends Component {
return false;
}

configureIsPlaceholderVisible( isPlaceholderVisible ) {
const isPlaceholderVisibleString = String( !! isPlaceholderVisible );
if ( this.editorNode.getAttribute( IS_PLACEHOLDER_VISIBLE_ATTR_NAME ) !== isPlaceholderVisibleString ) {
this.editorNode.setAttribute( IS_PLACEHOLDER_VISIBLE_ATTR_NAME, isPlaceholderVisibleString );
}
}

bindEditorNode( editorNode ) {
this.editorNode = editorNode;
this.props.setRef( editorNode );
Expand All @@ -158,7 +147,6 @@ export default class Editable extends Component {
record,
valueToEditableHTML,
className,
isPlaceholderVisible,
...remainingProps
} = this.props;

Expand Down Expand Up @@ -190,7 +178,6 @@ export default class Editable extends Component {
'aria-multiline': true,
className,
contentEditable: true,
[ IS_PLACEHOLDER_VISIBLE_ATTR_NAME ]: isPlaceholderVisible,
ref: this.bindEditorNode,
style: {
...style,
Expand Down
19 changes: 7 additions & 12 deletions packages/rich-text/src/component/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -181,6 +181,7 @@ class RichText extends Component {
multilineWrapperTags: this.multilineWrapperTags,
prepareEditableTree: createPrepareEditableTree( this.props, 'format_prepare_functions' ),
__unstableDomOnly: domOnly,
placeholder: this.props.placeholder,
} );
}

Expand Down Expand Up @@ -725,6 +726,7 @@ class RichText extends Component {
value,
selectionStart,
selectionEnd,
placeholder,
__unstableIsSelected: isSelected,
} = this.props;

Expand Down Expand Up @@ -752,6 +754,10 @@ class RichText extends Component {
shouldReapply = shouldReapply ||
! isShallowEqual( prepareProps, prevPrepareProps );

// Rerender if the placeholder changed.
shouldReapply = shouldReapply ||
placeholder !== prevProps.placeholder;

const { activeFormats = [] } = this.record;

if ( shouldReapply ) {
Expand Down Expand Up @@ -808,6 +814,7 @@ class RichText extends Component {
value,
multilineTag: this.multilineTag,
prepareEditableTree: createPrepareEditableTree( this.props, 'format_prepare_functions' ),
placeholder: this.props.placeholder,
} ).body.innerHTML;
}

Expand Down Expand Up @@ -857,7 +864,6 @@ class RichText extends Component {
wrapperClassName,
className,
placeholder,
keepPlaceholderOnFocus = false,
__unstableIsSelected: isSelected,
children,
// To do: move autocompletion logic to rich-text.
Expand All @@ -872,10 +878,8 @@ class RichText extends Component {
// changes, we replace the relevant element. This is needed because we
// prevent Editable component updates.
const key = Tagname;
const MultilineTag = this.multilineTag;
const ariaProps = pickAriaProps( this.props );
const record = this.getRecord();
const isPlaceholderVisible = placeholder && ( ! isSelected || keepPlaceholderOnFocus ) && isEmpty( record );

const autoCompleteContent = ( { listBoxId, activeId } ) => (
<>
Expand All @@ -884,7 +888,6 @@ class RichText extends Component {
style={ style }
record={ record }
valueToEditableHTML={ this.valueToEditableHTML }
isPlaceholderVisible={ isPlaceholderVisible }
aria-label={ placeholder }
aria-autocomplete={ listBoxId ? 'list' : undefined }
aria-owns={ listBoxId }
Expand All @@ -902,14 +905,6 @@ class RichText extends Component {
onTouchStart={ this.onPointerDown }
setRef={ this.setRef }
/>
{ isPlaceholderVisible &&
<Tagname
className={ classnames( 'rich-text', className ) }
style={ style }
>
{ MultilineTag ? <MultilineTag>{ placeholder }</MultilineTag> : placeholder }
</Tagname>
}
{ isSelected && <FormatEdit
allowedFormats={ allowedFormats }
withoutInteractiveFormatting={ withoutInteractiveFormatting }
Expand Down
11 changes: 6 additions & 5 deletions packages/rich-text/src/create.js
Original file line number Diff line number Diff line change
Expand Up @@ -333,11 +333,12 @@ function createFromElement( {
continue;
}

if (
isEditableTree &&
type === 'br' &&
! node.getAttribute( 'data-rich-text-line-break' )
) {
if ( isEditableTree && (
// Ignore any placeholders.
node.getAttribute( 'data-rich-text-placeholder' ) ||
// Ignore any line breaks that are not inserted by us.
( type === 'br' && ! node.getAttribute( 'data-rich-text-line-break' ) )
) ) {
accumulateSelection( accumulator, node, range, createEmptyValue() );
continue;
}
Expand Down
Loading

0 comments on commit b51e44f

Please sign in to comment.