Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Update Placeholder after initialization #14627

Merged
merged 15 commits into from
Jul 26, 2023
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions docs/_snippets/features/update-placeholder.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
<div id="snippet-update-placeholder">
<p></p>
</div>
<button id="update-placeholder-button">Update placeholder</button>
36 changes: 36 additions & 0 deletions docs/_snippets/features/update-placeholder.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/**
* @license Copyright (c) 2003-2023, CKSource Holding sp. z o.o. All rights reserved.
* For licensing, see LICENSE.md or https://ckeditor.com/legal/ckeditor-oss-license
*/

/* globals console, window, document, ClassicEditor */

import { CS_CONFIG } from '@ckeditor/ckeditor5-cloud-services/tests/_utils/cloud-services-config';

ClassicEditor
.create( document.querySelector( '#snippet-update-placeholder' ), {
cloudServices: CS_CONFIG,
toolbar: [
'undo', 'redo', '|', 'heading',
'|', 'bold', 'italic',
'|', 'link', 'uploadImage', 'insertTable', 'mediaEmbed',
'|', 'bulletedList', 'numberedList', 'outdent', 'indent'
],
ui: {
viewportOffset: {
top: window.getViewportTopOffsetConfig()
}
},
placeholder: 'Type some content here!'
} )
.then( editor => {
const button = document.getElementById( 'update-placeholder-button' );
window.editor = editor;

button.addEventListener( 'click', () => {
editor.editing.view.document.getRoot( 'main' ).placeholder = 'New placeholder';
} );
} )
.catch( err => {
console.error( err.stack );
} );
10 changes: 10 additions & 0 deletions docs/features/editor-placeholder.md
Original file line number Diff line number Diff line change
Expand Up @@ -108,6 +108,16 @@ The editor placeholder text is displayed using a CSS pseudo–element (`::before

**Note**: The `.ck-placeholder` class is also used to display placeholders in other places, for instance, {@link features/images-captions image captions}. Make sure your custom styles apply to the right subset of placeholders.

## Updating the placeholder
mlewand marked this conversation as resolved.
Show resolved Hide resolved

The editor placeholder could be updated in runtime changing `placeholder` property in editing root. You can use it also in {@link installation/getting-started/predefined-builds#multi-root-editor `Multi-root editor`} and {@link installation/getting-started/predefined-builds#inline-editor `Inline editor`}.
mlewand marked this conversation as resolved.
Show resolved Hide resolved

```js
editor.editing.view.document.getRoot( 'main' ).placeholder = 'new placeholder'
mlewand marked this conversation as resolved.
Show resolved Hide resolved
```

{@snippet features/update-placeholder}

## Contribute

The source code of the feature is available on GitHub at [https://github.com/ckeditor/ckeditor5/tree/master/packages/ckeditor5-core](https://github.com/ckeditor/ckeditor5/tree/master/packages/ckeditor5-core).
26 changes: 12 additions & 14 deletions packages/ckeditor5-editor-balloon/src/ballooneditorui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
*/

import {
type Editor,
type ElementApi
type Editor
} from 'ckeditor5/src/core';

import {
Expand Down Expand Up @@ -110,22 +109,21 @@ export default class BalloonEditorUI extends EditorUI {
const editor = this.editor;
const editingView = editor.editing.view;
const editingRoot = editingView.document.getRoot()!;
const sourceElement = ( editor as Editor & ElementApi ).sourceElement;

const placeholder = editor.config.get( 'placeholder' );
let placeholderText;

if ( placeholder ) {
const placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];

if ( placeholderText ) {
enablePlaceholder( {
view: editingView,
element: editingRoot,
text: placeholderText,
isDirectHost: false,
keepOnFocus: true
} );
}
placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];
mlewand marked this conversation as resolved.
Show resolved Hide resolved
}

editingRoot.placeholder = placeholderText;

enablePlaceholder( {
view: editingView,
element: editingRoot,
isDirectHost: false,
keepOnFocus: true
} );
}
}
15 changes: 8 additions & 7 deletions packages/ckeditor5-editor-classic/src/classiceditorui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -162,14 +162,15 @@ export default class ClassicEditorUI extends EditorUI {
}

if ( placeholderText ) {
enablePlaceholder( {
view: editingView,
element: editingRoot,
text: placeholderText,
isDirectHost: false,
keepOnFocus: true
} );
editingRoot.placeholder = placeholderText;
}

enablePlaceholder( {
view: editingView,
element: editingRoot,
isDirectHost: false,
keepOnFocus: true
} );
}

/**
Expand Down
26 changes: 12 additions & 14 deletions packages/ckeditor5-editor-decoupled/src/decouplededitorui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@
*/

import {
type Editor,
type ElementApi
type Editor
} from 'ckeditor5/src/core';

import {
Expand Down Expand Up @@ -118,22 +117,21 @@ export default class DecoupledEditorUI extends EditorUI {
const editor = this.editor;
const editingView = editor.editing.view;
const editingRoot = editingView.document.getRoot()!;
const sourceElement = ( editor as Editor & ElementApi ).sourceElement;

const placeholder = editor.config.get( 'placeholder' );
let placeholderText;

if ( placeholder ) {
const placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];

if ( placeholderText ) {
enablePlaceholder( {
view: editingView,
element: editingRoot,
text: placeholderText,
isDirectHost: false,
keepOnFocus: true
} );
}
placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];
}

editingRoot.placeholder = placeholderText;

enablePlaceholder( {
view: editingView,
element: editingRoot,
isDirectHost: false,
keepOnFocus: true
} );
}
}
26 changes: 13 additions & 13 deletions packages/ckeditor5-editor-inline/src/inlineeditorui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,6 @@
*/

import {
type ElementApi,
type Editor
} from 'ckeditor5/src/core';

Expand Down Expand Up @@ -153,22 +152,23 @@ export default class InlineEditorUI extends EditorUI {
const editor = this.editor;
const editingView = editor.editing.view;
const editingRoot = editingView.document.getRoot()!;
const sourceElement = ( editor as Editor & ElementApi ).sourceElement;

const placeholder = editor.config.get( 'placeholder' );
let placeholderText;

if ( placeholder ) {
const placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];

if ( placeholderText ) {
enablePlaceholder( {
view: editingView,
element: editingRoot,
text: placeholderText,
isDirectHost: false,
keepOnFocus: true
} );
}
placeholderText = typeof placeholder === 'string' ? placeholder : placeholder[ editingRoot.rootName ];
}

editingRoot.placeholder = placeholderText;
illia-stv marked this conversation as resolved.
Show resolved Hide resolved

if ( placeholderText ) {
mlewand marked this conversation as resolved.
Show resolved Hide resolved
enablePlaceholder( {
view: editingView,
element: editingRoot,
isDirectHost: false,
keepOnFocus: true
} );
}
}
}
7 changes: 2 additions & 5 deletions packages/ckeditor5-editor-multi-root/src/multirooteditorui.ts
Original file line number Diff line number Diff line change
Expand Up @@ -204,17 +204,14 @@ export default class MultiRootEditorUI extends EditorUI {
}
}

if ( !placeholder ) {
return;
}

const editingView = this.editor.editing.view;
const editingRoot = editingView.document.getRoot( editable.name! )!;

editingRoot.placeholder = placeholder;

enablePlaceholder( {
view: editingView,
element: editingRoot,
text: placeholder,
isDirectHost: false,
keepOnFocus: true
} );
Expand Down
8 changes: 8 additions & 0 deletions packages/ckeditor5-engine/src/view/editableelement.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,13 @@ export default class EditableElement extends ObservableMixin( ContainerElement )
*/
declare public isFocused: boolean;

/**
* Placeholder of current editable element.
illia-stv marked this conversation as resolved.
Show resolved Hide resolved
*
* @observable
*/
declare public placeholder?: string;

/**
* Creates an editable element.
*
Expand All @@ -62,6 +69,7 @@ export default class EditableElement extends ObservableMixin( ContainerElement )

this.set( 'isReadOnly', false );
this.set( 'isFocused', false );
this.set( 'placeholder', undefined );

this.bind( 'isReadOnly' ).to( document );

Expand Down
47 changes: 35 additions & 12 deletions packages/ckeditor5-engine/src/view/placeholder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import '../../theme/placeholder.css';

import type Document from './document';
import type DowncastWriter from './downcastwriter';
import type EditableElement from './editableelement';
import type Element from './element';
import type View from './view';

Expand All @@ -37,10 +38,9 @@ const documentPlaceholders = new WeakMap<Document, Map<Element, PlaceholderConfi
* editable root elements.
* @param options.keepOnFocus If set `true`, the placeholder stay visible when the host element is focused.
*/
export function enablePlaceholder( { view, element, text, isDirectHost = true, keepOnFocus = false }: {
export function enablePlaceholder( { view, element, isDirectHost = true, keepOnFocus = false }: {
illia-stv marked this conversation as resolved.
Show resolved Hide resolved
view: View;
element: Element;
text: string;
element: PlaceholderableElement | EditableElement;
isDirectHost?: boolean;
keepOnFocus?: boolean;
} ): void {
Expand All @@ -60,16 +60,32 @@ export function enablePlaceholder( { view, element, text, isDirectHost = true, k
}, { priority: 'high' } );
}

// Store information about the element placeholder under its document.
documentPlaceholders.get( doc )!.set( element, {
text,
isDirectHost,
keepOnFocus,
hostElement: isDirectHost ? element : null
} );
if ( element.is( 'editableElement' ) ) {
element.on( 'change:placeholder', ( evtInfo, evt, text ) => {
// Store information about the element placeholder under its document.
documentPlaceholders.get( doc )!.set( element, {
text,
isDirectHost,
keepOnFocus,
hostElement: isDirectHost ? element : null
} );

// Update the placeholders right away.
view.change( writer => updateDocumentPlaceholders( doc, writer ) );
} );
}

if ( element.placeholder ) {
documentPlaceholders.get( doc )!.set( element, {
illia-stv marked this conversation as resolved.
Show resolved Hide resolved
text: element.placeholder,
isDirectHost,
keepOnFocus,
hostElement: isDirectHost ? element : null
} );

// Update the placeholders right away.
view.change( writer => updateDocumentPlaceholders( doc, writer ) );
// Update the placeholders right away.
view.change( writer => updateDocumentPlaceholders( doc, writer ) );
}
}

/**
Expand Down Expand Up @@ -297,3 +313,10 @@ interface PlaceholderConfig {
keepOnFocus: boolean;
hostElement: Element | null;
}

/**
* Element which could have placeholder.
*/
export type PlaceholderableElement = Element & {
placeholder?: string;
mlewand marked this conversation as resolved.
Show resolved Hide resolved
};
Loading