-
Notifications
You must be signed in to change notification settings - Fork 37
ImageStyle #23
Changes from all commits
74ec5d3
777827a
af6541d
9b7a556
26578db
0f96c7b
8069e19
b8a6178
98e4051
a005ff7
6e15b81
7004581
18b2819
8267f6f
b90621f
bb60e25
7ec5d65
21c86c7
12fbc44
3f09d99
cd305e3
7a2b3a0
c6948e5
8493080
091a128
1f511d5
d953a7b
40f1907
7327f57
12d75d9
c350b10
9ba4a3d
cc7370f
a174bdc
0b39383
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,135 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/** | ||
* @module image/imagestyle/converters | ||
*/ | ||
|
||
import { isImage } from '../utils'; | ||
|
||
/** | ||
* Returns converter for the `imageStyle` attribute. It can be used for adding, changing and removing the attribute. | ||
* | ||
* @param {Object} styles Object containing available styles. See {@link module:image/imagestyle/imagestyleengine~ImageStyleFormat} | ||
* for more details. | ||
* @returns {Function} Model to view attribute converter. | ||
*/ | ||
export function modelToViewStyleAttribute( styles ) { | ||
return ( evt, data, consumable, conversionApi ) => { | ||
const eventType = evt.name.split( ':' )[ 0 ]; | ||
const consumableType = eventType + ':imageStyle'; | ||
|
||
if ( !consumable.test( data.item, consumableType ) ) { | ||
return; | ||
} | ||
|
||
// Check if there is class name associated with given value. | ||
const newStyle = getStyleByValue( data.attributeNewValue, styles ); | ||
const oldStyle = getStyleByValue( data.attributeOldValue, styles ); | ||
const viewElement = conversionApi.mapper.toViewElement( data.item ); | ||
|
||
if ( handleRemoval( eventType, oldStyle, viewElement ) || handleAddition( eventType, newStyle, viewElement ) ) { | ||
consumable.consume( data.item, consumableType ); | ||
} | ||
}; | ||
} | ||
|
||
/** | ||
* Returns view to model converter converting image CSS classes to proper value in the model. | ||
* | ||
* @param {Array.<module:image/imagestyle/imagestyleengine~ImageStyleFormat>} styles Styles for which converter is created. | ||
* @returns {Function} View to model converter. | ||
*/ | ||
export function viewToModelStyleAttribute( styles ) { | ||
// Convert only styles without `null` value. | ||
const filteredStyles = styles.filter( style => style.value !== null ); | ||
|
||
return ( evt, data, consumable, conversionApi ) => { | ||
for ( let style of filteredStyles ) { | ||
viewToModelImageStyle( style, data, consumable, conversionApi ); | ||
} | ||
}; | ||
} | ||
|
||
// Converter from view to model converting single style. | ||
// For more information see {@link module:engine/conversion/viewconversiondispatcher~ViewConversionDispatcher}; | ||
// | ||
// @param {module:image/imagestyle/imagestyleengine~ImageStyleFormat} style | ||
// @param {Object} data | ||
// @param {module:engine/conversion/viewconsumable~ViewConsumable} consumable | ||
// @param {Object} conversionApi | ||
function viewToModelImageStyle( style, data, consumable, conversionApi ) { | ||
const viewFigureElement = data.input; | ||
const modelImageElement = data.output; | ||
|
||
// *** Step 1: Validate conversion. | ||
// Check if view element has proper class to consume. | ||
if ( !consumable.test( viewFigureElement, { class: style.className } ) ) { | ||
return; | ||
} | ||
|
||
// Check if figure is converted to image. | ||
if ( !isImage( modelImageElement ) ) { | ||
return; | ||
} | ||
|
||
// Check if image element can be placed in current context wit additional attribute. | ||
const attributes = [ ...modelImageElement.getAttributeKeys(), 'imageStyle' ]; | ||
|
||
if ( !conversionApi.schema.check( { name: 'image', inside: data.context, attributes } ) ) { | ||
return; | ||
} | ||
|
||
// *** Step2: Convert to model. | ||
consumable.consume( viewFigureElement, { class: style.className } ); | ||
modelImageElement.setAttribute( 'imageStyle', style.value ); | ||
} | ||
|
||
// Returns style with given `value` from array of styles. | ||
// | ||
// @param {String} value | ||
// @param {Array.<module:image/imagestyle/imagestyleengine~ImageStyleFormat> } styles | ||
// @return {module:image/imagestyle/imagestyleengine~ImageStyleFormat|undefined} | ||
function getStyleByValue( value, styles ) { | ||
for ( let style of styles ) { | ||
if ( style.value === value ) { | ||
return style; | ||
} | ||
} | ||
} | ||
|
||
// Handles converting removal of the attribute. | ||
// Returns `true` when handling was processed correctly and further conversion can be performed. | ||
// | ||
// @param {String} eventType Type of the event. | ||
// @param {module:image/imagestyle/imagestyleengine~ImageStyleFormat} style | ||
// @param {module:engine/view/element~Element} viewElement | ||
// @returns {Boolean} Whether the change was handled. | ||
function handleRemoval( eventType, style, viewElement ) { | ||
if ( style && ( eventType == 'changeAttribute' || eventType == 'removeAttribute' ) ) { | ||
viewElement.removeClass( style.className ); | ||
|
||
return true; | ||
} | ||
|
||
return false; | ||
} | ||
|
||
// Handles converting addition of the attribute. | ||
// Returns `true` when handling was processed correctly and further conversion can be performed. | ||
// | ||
// @param {String} eventType Type of the event. | ||
// @param {module:image/imagestyle/imagestyleengine~ImageStyleFormat} style | ||
// @param {module:engine/view/element~Element} viewElement | ||
// @returns {Boolean} Whether the change was handled. | ||
function handleAddition( evenType, style, viewElement ) { | ||
if ( style && ( evenType == 'addAttribute' || evenType == 'changeAttribute' ) ) { | ||
viewElement.addClass( style.className ); | ||
|
||
return true; | ||
} | ||
|
||
return false; | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,74 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/** | ||
* @module image/imagestyle/imagestyle | ||
*/ | ||
|
||
import Plugin from 'ckeditor5-core/src/plugin'; | ||
import ImageStyleEngine from './imagestyleengine'; | ||
import ButtonView from 'ckeditor5-ui/src/button/buttonview'; | ||
|
||
/** | ||
* The image style plugin. | ||
* | ||
* Uses {@link module:image/imagestyle/imagestyleengine~ImageStyleEngine}. | ||
* | ||
* @extends module:core/plugin~Plugin | ||
*/ | ||
export default class ImageStyle extends Plugin { | ||
/** | ||
* @inheritDoc | ||
*/ | ||
static get requires() { | ||
return [ ImageStyleEngine ]; | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
init() { | ||
const styles = this.editor.config.get( 'image.styles' ); | ||
|
||
for ( let style of styles ) { | ||
this._createButton( style ); | ||
} | ||
|
||
// Push buttons to default image toolbar if one exists. | ||
const defaultImageToolbarConfig = this.editor.config.get( 'image.defaultToolbar' ); | ||
|
||
if ( defaultImageToolbarConfig ) { | ||
styles.forEach( style => defaultImageToolbarConfig.push( style.name ) ); | ||
} | ||
} | ||
|
||
/** | ||
* Creates button for each style and stores it in editor's {@link module:ui/componentfactory~ComponentFactory ComponentFactory}. | ||
* | ||
* @private | ||
* @param {module:image/imagestyle/imagestyleengine~ImageStyleFormat} style | ||
*/ | ||
_createButton( style ) { | ||
const editor = this.editor; | ||
const command = editor.commands.get( style.name ); | ||
|
||
editor.ui.componentFactory.add( style.name, ( locale ) => { | ||
const view = new ButtonView( locale ); | ||
|
||
view.set( { | ||
label: style.title, | ||
icon: style.icon, | ||
tooltip: true | ||
} ); | ||
|
||
view.bind( 'isEnabled' ).to( command, 'isEnabled' ); | ||
view.bind( 'isOn' ).to( command, 'value' ); | ||
|
||
this.listenTo( view, 'execute', () => editor.execute( style.name ) ); | ||
|
||
return view; | ||
} ); | ||
} | ||
} |
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,109 @@ | ||
/** | ||
* @license Copyright (c) 2003-2016, CKSource - Frederico Knabben. All rights reserved. | ||
* For licensing, see LICENSE.md. | ||
*/ | ||
|
||
/** | ||
* @module image/imagestyle/imagestylecommand | ||
*/ | ||
|
||
import Command from 'ckeditor5-core/src/command/command'; | ||
import { isImage } from '../utils'; | ||
|
||
/** | ||
* The image style command. It is used to apply different image styles. | ||
* | ||
* @extends module:core/command/command~Command | ||
*/ | ||
export default class ImageStyleCommand extends Command { | ||
/** | ||
* Creates instance of the image style command. Each command instance is handling one style. | ||
* | ||
* @param {module:core/editor/editor~Editor} editor Editor instance. | ||
* @param {module:image/imagestyle/imagestyleengine~ImageStyleFormat} styles Style to apply by this command. | ||
*/ | ||
constructor( editor, style ) { | ||
super( editor ); | ||
|
||
/** | ||
* The current command value - `true` if style handled by the command is applied on currently selected image, | ||
* `false` otherwise. | ||
* | ||
* @readonly | ||
* @observable | ||
* @member {Boolean} #value | ||
*/ | ||
this.set( 'value', false ); | ||
|
||
/** | ||
* Style handled by this command. | ||
* | ||
* @readonly | ||
* @member {module:image/imagestyle/imagestyleengine~ImageStyleFormat} #style | ||
*/ | ||
this.style = style; | ||
|
||
// Update current value and refresh state each time something change in model document. | ||
this.listenTo( editor.document, 'changesDone', () => { | ||
this._updateValue(); | ||
this.refreshState(); | ||
} ); | ||
} | ||
|
||
/** | ||
* Updates command's value. | ||
* | ||
* @private | ||
*/ | ||
_updateValue() { | ||
const doc = this.editor.document; | ||
const element = doc.selection.getSelectedElement(); | ||
|
||
if ( !element ) { | ||
this.value = false; | ||
|
||
return; | ||
} | ||
|
||
if ( this.style.value === null ) { | ||
this.value = !element.hasAttribute( 'imageStyle' ); | ||
} else { | ||
this.value = ( element.getAttribute( 'imageStyle' ) == this.style.value ); | ||
} | ||
} | ||
|
||
/** | ||
* @inheritDoc | ||
*/ | ||
_checkEnabled() { | ||
const element = this.editor.document.selection.getSelectedElement(); | ||
|
||
return isImage( element ); | ||
} | ||
|
||
/** | ||
* Executes command. | ||
* | ||
* @protected | ||
* @param {Object} options | ||
* @param {module:engine/model/batch~Batch} [options.batch] Batch to collect all the change steps. New batch will be | ||
* created if this option is not set. | ||
*/ | ||
_doExecute( options = {} ) { | ||
// Stop if style is already applied. | ||
if ( this.value ) { | ||
return; | ||
} | ||
|
||
const editor = this.editor; | ||
const doc = editor.document; | ||
const selection = doc.selection; | ||
const imageElement = selection.getSelectedElement(); | ||
|
||
doc.enqueueChanges( () => { | ||
const batch = options.batch || doc.batch(); | ||
|
||
batch.setAttribute( imageElement, 'imageStyle', this.style.value ); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. The attribute should be called There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I wanted to call it There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. That's a very bad reason for choosing this name :P. Let's see if we can do something to fix that bug. It's a very serious problem. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Note: This is quite important what attribute name we use because this is kind of a public API of this feature. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I will fix other issues with this PR and then look into that issue. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I've created separate issue for that: https://github.com/ckeditor/ckeditor5-image/issues/25. |
||
} ); | ||
} | ||
} |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Missing new line.