Skip to content

Commit

Permalink
Merge pull request #8998 from ckeditor/i/8959
Browse files Browse the repository at this point in the history
Fix (engine): `DowncastWriter` should handle `UIElements` consistently while wrapping with and inserting them into attribute elements. Closes #8959.

Feature (engine): `ContainerElement` can be marked as `isAllowedInsideAttributeElement` in order to allow wrapping it with attribute elements. Useful for instance for inline widgets. Other element types (UI, Raw, Empty) have this flag on by default but it can be changed via `options.isAllowedInsideAttributeElement` to `false`. Read more in `DowncastWriter#create*()` methods documentation. Closes #1633.
  • Loading branch information
Reinmar committed Feb 22, 2021
2 parents 0545fe6 + f73f959 commit fcb166e
Show file tree
Hide file tree
Showing 20 changed files with 475 additions and 50 deletions.
13 changes: 11 additions & 2 deletions docs/_snippets/framework/tutorials/inline-widget.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,10 +24,14 @@ import Model from '@ckeditor/ckeditor5-ui/src/model';
class PlaceholderCommand extends Command {
execute( { value } ) {
const editor = this.editor;
const selection = editor.model.document.selection;

editor.model.change( writer => {
// Create a <placeholder> elment with the "name" attribute...
const placeholder = writer.createElement( 'placeholder', { name: value } );
// Create a <placeholder> element with the "name" attribute (and all the selection attributes)...
const placeholder = writer.createElement( 'placeholder', {
...Object.fromEntries( selection.getAttributes() ),
name: value
} );

// ... and insert it into the document.
editor.model.insertContent( placeholder );
Expand Down Expand Up @@ -145,6 +149,9 @@ class PlaceholderEditing extends Plugin {
// The inline widget is self-contained so it cannot be split by the caret and it can be selected:
isObject: true,

// The inline widget can have the same attributes as text (for example linkHref, bold).
allowAttributesOf: '$text',

// The placeholder can have many types, like date, name, surname, etc:
allowAttributes: [ 'name' ]
} );
Expand Down Expand Up @@ -187,6 +194,8 @@ class PlaceholderEditing extends Plugin {

const placeholderView = viewWriter.createContainerElement( 'span', {
class: 'placeholder'
}, {
isAllowedInsideAttributeElement: true
} );

// Insert the placeholder name (as a text).
Expand Down
25 changes: 21 additions & 4 deletions docs/framework/guides/tutorials/implementing-an-inline-widget.md
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,9 @@ export default class PlaceholderEditing extends Plugin {
// The inline widget is self-contained so it cannot be split by the caret and can be selected:
isObject: true,

// The inline widget can have the same attributes as text (for example linkHref, bold).
allowAttributesOf: '$text',

// The placeholder can have many types, like date, name, surname, etc:
allowAttributes: [ 'name' ]
} );
Expand Down Expand Up @@ -354,6 +357,8 @@ export default class PlaceholderEditing extends Plugin {

const placeholderView = viewWriter.createContainerElement( 'span', {
class: 'placeholder'
}, {
isAllowedInsideAttributeElement: true
} );

// Insert the placeholder name (as a text).
Expand Down Expand Up @@ -398,10 +403,14 @@ import Command from '@ckeditor/ckeditor5-core/src/command';
export default class PlaceholderCommand extends Command {
execute( { value } ) {
const editor = this.editor;
const selection = editor.model.document.selection;

editor.model.change( writer => {
// Create a <placeholder> elment with the "name" attribute...
const placeholder = writer.createElement( 'placeholder', { name: value } );
// Create a <placeholder> elment with the "name" attribute (and all the selection attributes)...
const placeholder = writer.createElement( 'placeholder', {
...Object.fromEntries( selection.getAttributes() ),
name: value
} );

// ... and insert it into the document.
editor.model.insertContent( placeholder );
Expand Down Expand Up @@ -769,8 +778,11 @@ class PlaceholderCommand extends Command {
const editor = this.editor;

editor.model.change( writer => {
// Create a <placeholder> elment with the "name" attribute...
const placeholder = writer.createElement( 'placeholder', { name: value } );
// Create a <placeholder> elment with the "name" attribute (and all the selection attributes)...
const placeholder = writer.createElement( 'placeholder', {
...Object.fromEntries( selection.getAttributes() ),
name: value
} );

// ... and insert it into the document.
editor.model.insertContent( placeholder );
Expand Down Expand Up @@ -882,6 +894,9 @@ class PlaceholderEditing extends Plugin {
// The inline widget is self-contained so it cannot be split by the caret and it can be selected:
isObject: true,

// The inline widget can have the same attributes as text (for example linkHref, bold).
allowAttributesOf: '$text',

// The placeholder can have many types, like date, name, surname, etc:
allowAttributes: [ 'name' ]
} );
Expand Down Expand Up @@ -924,6 +939,8 @@ class PlaceholderEditing extends Plugin {

const placeholderView = viewWriter.createContainerElement( 'span', {
class: 'placeholder'
}, {
isAllowedInsideAttributeElement: true
} );

// Insert the placeholder name (as a text).
Expand Down
10 changes: 10 additions & 0 deletions packages/ckeditor5-engine/src/view/attributeelement.js
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,16 @@ const DEFAULT_PRIORITY = 10;
* To create a new attribute element instance use the
* {@link module:engine/view/downcastwriter~DowncastWriter#createAttributeElement `DowncastWriter#createAttributeElement()`} method.
*
* **Note:** Attribute elements by default can wrap {@link module:engine/view/text~Text},
* {@link module:engine/view/emptyelement~EmptyElement}, {@link module:engine/view/uielement~UIElement},
* {@link module:engine/view/rawelement~RawElement} and other attribute elements with higher priority. Other elements while placed inside
* an attribute element will split it (or nest in case of an `AttributeElement`). This behavior can be modified by changing
* the `isAllowedInsideAttributeElement` option while creating
* {@link module:engine/view/downcastwriter~DowncastWriter#createContainerElement},
* {@link module:engine/view/downcastwriter~DowncastWriter#createEmptyElement},
* {@link module:engine/view/downcastwriter~DowncastWriter#createUIElement} or
* {@link module:engine/view/downcastwriter~DowncastWriter#createRawElement}.
*
* @extends module:engine/view/element~Element
*/
export default class AttributeElement extends Element {
Expand Down

0 comments on commit fcb166e

Please sign in to comment.