Ver código fonte

Add demo for inline widget tutorial.

Mateusz Samsel 6 anos atrás
pai
commit
b024ede888

+ 17 - 0
docs/_snippets/framework/tutorials/inline-widget.html

@@ -0,0 +1,17 @@
+<style>
+	.placeholder {
+		background: #ffff00;
+		padding: 4px 2px;
+		outline-offset: -2px;
+		line-height: 1em;
+		margin: 0 1px;
+	}
+
+	.placeholder::selection {
+		display: none;
+	}
+</style>
+
+<div id="snippet-inline-widget">
+	<p>Hello <span class="placeholder">{first name}</span> <span class="placeholder">{surname}</span>!</p>
+</div>

+ 213 - 0
docs/_snippets/framework/tutorials/inline-widget.js

@@ -0,0 +1,213 @@
+/**
+ * @license Copyright (c) 2003-2019, CKSource - Frederico Knabben. All rights reserved.
+ * For licensing, see LICENSE.md.
+ */
+/* globals console, window, document */
+
+import ClassicEditor from '@ckeditor/ckeditor5-editor-classic/src/classiceditor';
+import Essentials from '@ckeditor/ckeditor5-essentials/src/essentials';
+import Paragraph from '@ckeditor/ckeditor5-paragraph/src/paragraph';
+import Heading from '@ckeditor/ckeditor5-heading/src/heading';
+import List from '@ckeditor/ckeditor5-list/src/list';
+import Bold from '@ckeditor/ckeditor5-basic-styles/src/bold';
+import Italic from '@ckeditor/ckeditor5-basic-styles/src/italic';
+
+import Plugin from '@ckeditor/ckeditor5-core/src/plugin';
+import { toWidget, viewToModelPositionOutsideModelElement } from '@ckeditor/ckeditor5-widget/src/utils';
+import Widget from '@ckeditor/ckeditor5-widget/src/widget';
+import Command from '@ckeditor/ckeditor5-core/src/command';
+
+import { addListToDropdown, createDropdown } from '@ckeditor/ckeditor5-ui/src/dropdown/utils';
+import Collection from '@ckeditor/ckeditor5-utils/src/collection';
+import Model from '@ckeditor/ckeditor5-ui/src/model';
+
+class PlaceholderCommand extends Command {
+	execute( { value } ) {
+		const editor = this.editor;
+
+		editor.model.change( writer => {
+			// Create <placeholder> elment with name attribute...
+			const placeholder = writer.createElement( 'placeholder', { name: value } );
+
+			// ... and insert it into the document.
+			editor.model.insertContent( placeholder );
+
+			// Put the selection on inserted element.
+			writer.setSelection( placeholder, 'on' );
+		} );
+	}
+
+	refresh() {
+		const model = this.editor.model;
+		const selection = model.document.selection;
+
+		const isAllowed = model.schema.checkChild( selection.focus.parent, 'placeholder' );
+
+		this.isEnabled = isAllowed;
+	}
+}
+
+class Placeholder extends Plugin {
+	static get requires() {
+		return [ PlaceholderEditing, PlaceholderUI ];
+	}
+}
+
+class PlaceholderUI extends Plugin {
+	init() {
+		const editor = this.editor;
+		const t = editor.t;
+		const placeholderNames = editor.config.get( 'placeholderConfig.types' );
+
+		// The "placeholder" dropdown must be registered among UI components of the editor
+		// to be displayed in the toolbar.
+		editor.ui.componentFactory.add( 'placeholder', locale => {
+			const dropdownView = createDropdown( locale );
+
+			// Populate the list in the dropdown with items.
+			addListToDropdown( dropdownView, getDropdownItemsDefinitions( placeholderNames ) );
+
+			dropdownView.buttonView.set( {
+				// The t() function helps localize the editor. All strings enclosed in t() can be
+				// translated and change when the language of the editor changes.
+				label: t( 'Placeholder' ),
+				tooltip: true,
+				withText: true
+			} );
+
+			// Execute the command when the dropdown items is clicked (executed).
+			this.listenTo( dropdownView, 'execute', evt => {
+				editor.execute( 'placeholder', { value: evt.source.commandParam } );
+				editor.editing.view.focus();
+			} );
+
+			return dropdownView;
+		} );
+	}
+}
+
+function getDropdownItemsDefinitions( placeholderNames ) {
+	const itemDefinitions = new Collection();
+
+	for ( const name of placeholderNames ) {
+		const definition = {
+			type: 'button',
+			model: new Model( {
+				commandParam: name,
+				label: name,
+				withText: true
+			} )
+		};
+
+		// Add the item definition to the collection.
+		itemDefinitions.add( definition );
+	}
+
+	return itemDefinitions;
+}
+
+class PlaceholderEditing extends Plugin {
+	static get requires() {
+		return [ Widget ];
+	}
+
+	init() {
+		console.log( 'PlaceholderEditing#init() got called' );
+
+		this._defineSchema();
+		this._defineConverters();
+
+		this.editor.commands.add( 'placeholder', new PlaceholderCommand( this.editor ) );
+
+		this.editor.editing.mapper.on(
+			'viewToModelPosition',
+			viewToModelPositionOutsideModelElement( this.editor.model, viewElement => viewElement.hasClass( 'placeholder' ) )
+		);
+		this.editor.config.define( 'placeholderConfig', {
+			types: [ 'date', 'first name', 'surname' ]
+		} );
+	}
+
+	_defineSchema() {
+		const schema = this.editor.model.schema;
+
+		schema.register( 'placeholder', {
+			// Allow wherever text is allowed:
+			allowWhere: '$text',
+
+			// The placeholder will acts as an inline node:
+			isInline: true,
+
+			// The inline-widget is self-contained so cannot be split by the caret and can be selected:
+			isObject: true,
+
+			// The placeholder can have many types, like date, name, surname, etc:
+			allowAttributes: [ 'name' ]
+		} );
+	}
+
+	_defineConverters() {
+		const conversion = this.editor.conversion;
+
+		conversion.for( 'upcast' ).elementToElement( {
+			view: {
+				name: 'span',
+				classes: [ 'placeholder' ]
+			},
+			model: ( viewElement, modelWriter ) => {
+				// Extract the "name" from "{name}".
+				const name = viewElement.getChild( 0 ).data.slice( 1, -1 );
+
+				return modelWriter.createElement( 'placeholder', { name } );
+			}
+		} );
+
+		conversion.for( 'editingDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: ( modelItem, viewWriter ) => {
+				const widgetElement = createPlaceholderView( modelItem, viewWriter );
+
+				// Enable widget handling on placeholder element inside editing view.
+				return toWidget( widgetElement, viewWriter );
+			}
+		} );
+
+		conversion.for( 'dataDowncast' ).elementToElement( {
+			model: 'placeholder',
+			view: createPlaceholderView
+		} );
+
+		// Helper method for both downcast converters.
+		function createPlaceholderView( modelItem, viewWriter ) {
+			const name = modelItem.getAttribute( 'name' );
+
+			const placeholderView = viewWriter.createContainerElement( 'span', {
+				class: 'placeholder'
+			} );
+
+			// Insert the placeholder name (as a text).
+			const innerText = viewWriter.createText( '{' + name + '}' );
+			viewWriter.insert( viewWriter.createPositionAt( placeholderView, 0 ), innerText );
+
+			return placeholderView;
+		}
+	}
+}
+
+ClassicEditor
+	.create( document.querySelector( '#snippet-inline-widget' ), {
+		plugins: [ Essentials, Paragraph, Heading, List, Bold, Italic, Placeholder ],
+		toolbar: [ 'heading', '|', 'bold', 'italic', 'numberedList', 'bulletedList', '|', 'placeholder' ],
+		placeholderConfig: {
+			types: [ 'date', 'color', 'first name', 'surname' ]
+		}
+	} )
+	.then( editor => {
+		console.log( 'Editor was initialized', editor );
+
+		// Expose for playing in the console.
+		window.editor = editor;
+	} )
+	.catch( error => {
+		console.error( error.stack );
+	} );

+ 4 - 0
docs/framework/guides/tutorials/implementing-an-inline-widget.md

@@ -716,3 +716,7 @@ ClassicEditor
 Now if you open the dropdown in the toolbar you'll see new list of placeholders to insert.
 
 {@img assets/img/tutorial-implementing-an-inline-widget-2.png Screenshot of the placeholder widgets being inserted using the dropdown.}
+
+## Demo
+
+{@snippet framework/tutorials/inline-widget}