-
Notifications
You must be signed in to change notification settings - Fork 2.5k
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
Adding inline styles and attributes to widget with applyStyle #1681
Conversation
Initially I was supposed to work on adding/removing styles and attributes as separate tickets, but since doing them both looks almost the same I squashed it into one PR. |
plugins/widget/plugin.js
Outdated
if ( !updatedClasses[ cl ] ) | ||
changed = updatedClasses[ cl ] = 1; | ||
} else { | ||
if ( updatedClasses[ cl ] ) { |
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.
Not part of my code, although I see now that I can change it to else if {
with other changes requested by reviewer.
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.
Yes, I would take this opportunity to refactor it, as you mentioned it should look something like:
while ( ( cl = classes.pop() ) ) {
if ( apply ) {
if ( !updatedClasses[ cl ] ) {
changed = updatedClasses[ cl ] = 1;
}
} else if ( updatedClasses[ cl ] ) {
delete updatedClasses[ cl ];
changed = 1;
}
}
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.
This issue is a little more complex as modifications in applyRemoveStyle
method affects 3 other methods:
- Docs for CKEDITOR.widget.applyStyle and CKEDITOR.widget.removeStyle should be updated.
- There is also CKEDITOR.widget.checkStyleActive method which should be adjusted. Now it only checks if widget element contains classes from a style. It should check also for styles and attributes (not sure, but maybe some method from
CKEDITOR.style
could be useful, e.g.checkElementMatch
).
CHANGES.md
Outdated
|
||
New Features: | ||
|
||
* [#1674](https://github.com/ckeditor/ckeditor-dev/issues/1674): The [Widget](https://ckeditor.com/cke4/addon/widget) plugin accepts inline styles and HTML attributes from [`CKEDITOR.style.customHandlers.widget`](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_style_customHandlers_widget.html). |
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.
Wouldn't it be more clear to say that now widgets are fully compatible or fully apply CKEDITOR.style
when applied via applyStyle
/removeStyle
method? (Previously only classes
were added, now all attributes
and styles
are added)
plugins/widget/plugin.js
Outdated
if ( !updatedClasses[ cl ] ) | ||
changed = updatedClasses[ cl ] = 1; | ||
} else { | ||
if ( updatedClasses[ cl ] ) { |
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.
Yes, I would take this opportunity to refactor it, as you mentioned it should look something like:
while ( ( cl = classes.pop() ) ) {
if ( apply ) {
if ( !updatedClasses[ cl ] ) {
changed = updatedClasses[ cl ] = 1;
}
} else if ( updatedClasses[ cl ] ) {
delete updatedClasses[ cl ];
changed = 1;
}
}
plugins/widget/plugin.js
Outdated
|
||
// Ee... Something is wrong with this style. | ||
if ( !classes ) | ||
if ( !classes && !styles && !attributes ) |
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.
If you could also fix the codestyle here as we rather use if
wtih {
.
plugins/widget/plugin.js
Outdated
} | ||
} | ||
} | ||
if ( changed ) |
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.
And here (if { ... }
).
plugins/widget/plugin.js
Outdated
widget.setData( 'classes', updatedClasses ); | ||
} | ||
|
||
function toggleStyleAttribute ( element, property, StyleOrAttr, apply ) { |
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.
This function is defined/created every time applyRemoveStyle
is executed. I would move it outside as a helper same as getStyleClasses
.
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.
Also i don't see a reason why StyleOrAttr
starts with uppercase. It is a standard param like any other in this function.
plugins/widget/plugin.js
Outdated
function toggleStyleAttribute ( element, property, StyleOrAttr, apply ) { | ||
apply = apply ? 'set' : 'remove'; | ||
StyleOrAttr = StyleOrAttr ? 'Style' : 'Attribute'; | ||
StyleOrAttr = apply + StyleOrAttr; |
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.
You can just make one-liner out of it:
StyleOrAttr = apply + ( StyleOrAttr ? 'Style' : 'Attribute' );
plugins/widget/plugin.js
Outdated
StyleOrAttr = StyleOrAttr ? 'Style' : 'Attribute'; | ||
StyleOrAttr = apply + StyleOrAttr; | ||
|
||
for ( var key in property ) { |
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.
Instead of iterating you may use apply/removeStyles
and apply/removeAttributes
.
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.
- There is no method
removeStyles
onlyremoveStyle
. - Method
setAttributes
takesobject
containing pairs as attribute, whileremoveAttributes
takesarray
as argument.
Applying all that will make our logic more complicated than it is right now so I will stay with current approach.
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.
Remember to check if other unit tests are passing after modifying behaviour of existing methods. It is likely that some tests checked behaviour which was changed. I run some widget unit tests and see some are failing:
Also as checkStyleActive
was modified, we need new unit tests covering new behaviour.
CHANGES.md
Outdated
|
||
New Features: | ||
|
||
* [#1674](https://github.com/ckeditor/ckeditor-dev/issues/1674): The [Widget](https://ckeditor.com/cke4/addon/widget) plugin now fully applies [`CKEDITOR.style.customHandlers.widget`](https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_style_customHandlers_widget.html) excepts for `element` when using methods `applyStyle` or `removeStyle`. |
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.
The
Widget
plugin now fully supportsCKEDITOR.style
'sattibuttes
/styles
viaapplyStyle
,removeStyle
andcheckStyleActive
methods.
☝️ Maybe something like this?
Please remember about proper linking to docs (so also method names should be linked).
plugins/widget/plugin.js
Outdated
* | ||
* By default this method handles only classes defined in the style. It clones existing | ||
* classes which are stored in the {@link #property-data widget data}'s `classes` property, | ||
* Since 4.10 this method applies all attributes styles and classes from {@link CKEDITOR.style}. |
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.
all attributes and styles from {@link CKEDITOR.style}.
As class
is basically attribute maybe it may be skipped here (also CKEDITOR.style
does not have separate class
property so mentioning it may be misleading - in opposite to styles
which are fine here).
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.
Could you apply to all 3 methods descriptions.
plugins/widget/plugin.js
Outdated
* classes which are stored in the {@link #property-data widget data}'s `classes` property, | ||
* Since 4.10 this method applies all attributes styles and classes from {@link CKEDITOR.style}. | ||
* | ||
* Previously this method handled only classes defined in the style. |
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.
classes defined in {@link CKEDITOR.style} attributes.
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.
Could you apply to all 3 methods descriptions.
plugins/widget/plugin.js
Outdated
@@ -1026,20 +1026,19 @@ | |||
}, | |||
|
|||
/** | |||
* Applies the specified style to the widget. It is highly recommended to use the |
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.
Maybe we can preserve this info about recommended methods?
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.
Could you apply to all 3 methods descriptions.
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.
I second this: info about recommended methods should stay.
plugins/widget/plugin.js
Outdated
* | ||
* Previously this method handled only classes defined in the style. | ||
* | ||
* This method adds classes by cloning existing ones which are stored in the {@link #property-data widget data}'s `classes` property, |
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.
Now it sounds somehow misleading, that classes are added by cloning existing ones
. I would stay with previous version, like:
This method clones existing classes ( ... ), adds new classes, and calls...
plugins/widget/plugin.js
Outdated
return false; | ||
} | ||
return true; | ||
return CKEDITOR.style.prototype.checkElementMatch.call( style, this.element ); |
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.
Why not just return style.checkElementMatch( this.element );
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.
I'm using style.prototype
because using it like that would refer to style.customHandler.widget.checkElementMatch
instead. But after reworking my PR I won't call it.
plugins/widget/plugin.js
Outdated
return false; | ||
style = CKEDITOR.tools.copy( style ); | ||
style.element = this.element.getName(); | ||
style._.definition.ignoreReadonly = true; |
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.
plugins/widget/plugin.js
Outdated
|
||
if ( !classes ) | ||
return false; | ||
style = CKEDITOR.tools.copy( style ); |
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.
CKEDITOR.tools.copy
does a shallow copy so it is not completely safe to use it (as changing copy may change source object). There are two solutions here:
- Use
CKEDITOR.tools.clone
which does deep copy. - Create new style using definition from source one:
new CKEDITOR.style( CKEDITOR.tools.clone( style._.definition ) )
(still we need to clone the definition).
I'm for the second one as cloning whole CKEDITOR.style
object may be expensive. And if you look on CKEDITOR.style
constructor it doesn't perform anything fancy or computational heavy so it should be faster than cloning whole object.
plugins/widget/plugin.js
Outdated
toggleStyleAttribute( widget.element, attributes, 0, apply ); | ||
} | ||
|
||
function toggleStyleAttribute ( element, property, styleOrAttr, apply ) { |
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.
Having a Boolean
property with name like styleOrAttr
is misleading. Without looking at function code is not possible to assume what styleOrAttr=false
or styleOrAttr=true
really means. But, it is also quite difficult to come up with proper name in such cases 🤔
You may go same way as apply
param - if true
it means apply/set if not - remove. So here it would be attributes
- true
means apply attributes, false - styles (far from perfect IMHO).
The more readable way (but a little bit "inflated" too) would be something like:
function toogleAttributes( element, properties, apply ) {
toogleProperties( element, properties, apply, 'Attribute' );
}
function toogleStyles( element, properties, apply ) {
toogleProperties( element, properties, apply, 'Style' );
}
function toogleProperties( element, properties, apply, propertyType ) { ... }
or actually, you may just stay with toogleProperties
(skipping toogleAttributes
and toggleStyles
) and call it with proper params in the first place.
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.
One more thing, property
param, should be properties
as it is an object containing multiple properties.
}; | ||
|
||
bender.test( { | ||
'test apply remove inline style': function() { |
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.
I would add few tests here checking how it behaves when widget already has some attributes, styles defined. You may check if different attributes are preserved and same ones are replaced, if styles are merged with existing once and same values replaced, etc.
After F2F talk, we came up with two conclusions:
|
As there were some doubts if we should store all applied styles as a one, merged object or just as an array of all applied styles, after looking how First of all it works the same way as just applying styles to an element. If a style contains property which is already present in the element, the new value overwrites old one of existing property (same will happen when merging newly applied style object to cached style definition). Two short scenarios just to illustrate how it should work (again based on Example 1
Of course if we run Example 2Having styles with some common property, like:
It is possible to only remove one style:
For quick experiments with styles (especially removing) you may use this codpen. One more thing, after F2F talk with @engineering-this, we decide to not reuse |
Currently I'm working on getting styles from |
The problem @engineering-this mentioned in #1681 (comment) is quite tricky. We were thinking about storing somehow (in e.g. I see two solutions:
|
@@ -1055,9 +1054,10 @@ | |||
* {@link CKEDITOR.style#checkActive} method instead of using this method directly, | |||
* because unlike style's method, this one does not perform any checks. |
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.
Not sure if this part should stay or not. Now this method checks much more than just classes.
plugins/widget/plugin.js
Outdated
if ( attributes ) { | ||
for ( item in attributes ) { | ||
if ( item !== 'class' && item !== 'style' ) { | ||
if ( !( item in styleDefinition.attributes ) ) { |
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.
Could make two if
's as one, but it is easier to read this way.
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.
I wonder if it wouldn't be more readable to create helper function and make it one if
.
84bf62d
to
c817380
Compare
plugins/widget/plugin.js
Outdated
// If there are no styles remove it to make sure we don't have `styles=''` in our output. | ||
delete styleDefinition.styles; | ||
} | ||
wrapper.attributes[ 'data-style-definition' ] = JSON.stringify( styleDefinition ); |
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.
This might be considered as 'ugly' solution, but I couldn't find better way to store widget.styleDefinition
before widget is initialized.
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.
At least it should be internal CKE attribute, [data**-cke**-style-definition]
.
What's more, information about partial styles (classes) are inside widget's data. Maybe we should reuse this mechanism and include all styles there.
This could possibly close #1566 too. After review I'd like other devs to discuss if we need any further integration between |
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.
Additionally there are no tests for integration with Undo, which does not work correctly (part of styles are lost after undoing/redoing).
plugins/widget/plugin.js
Outdated
if ( attributes ) { | ||
for ( item in attributes ) { | ||
if ( item !== 'class' && item !== 'style' ) { | ||
if ( !( item in styleDefinition.attributes ) ) { |
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.
I wonder if it wouldn't be more readable to create helper function and make it one if
.
plugins/widget/plugin.js
Outdated
// @param {CKEDITOR.style} style Custom widget style. | ||
// @param {Boolean} apply Whether to apply or remove style. | ||
// @param {Boolean} whenever to apply or remove style. |
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.
These API docs are incorrect/incomplete.
} | ||
|
||
// function setTest( bot, testStyles, testAttributes, config ) { | ||
function setTest( bot, config ) { |
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.
Maybe it's just me, but I'll rewrite this function to replace if
s with action dictionary:
function setTest() {
var tests = {
apply: function() {
[…]
},
remove: function() {
[…]
}
};
while ( index < config.length ) {
var conf = config[ index ],
action = CKEDITOR.tools.objectKeys( conf )[ 0 ];
tests[ action ]( conf[ action ] );
}
}
More complex, but IMO also more readable.
plugins/widget/plugin.js
Outdated
var styleDefinition = JSON.parse( wrapper.$.getAttribute( 'data-style-definition' ) ); | ||
delete wrapper.$.attributes[ 'data-style-definition' ]; | ||
|
||
wrapper.removeAttribute( 'data-style-definition' ); |
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.
Why there are doubled removal of this attribute?
plugins/widget/plugin.js
Outdated
@@ -830,7 +834,7 @@ | |||
* @param [startupData] Initial widget data. This data object will overwrite the default data and | |||
* the data loaded from the DOM. | |||
*/ | |||
function Widget( widgetsRepo, id, element, widgetDef, startupData ) { | |||
function Widget( widgetsRepo, id, element, widgetDef, startupData, styleDefinition ) { |
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.
There is no API docs for the last parameter.
plugins/widget/plugin.js
Outdated
* | ||
* @readonly | ||
* @since: 4.10.0 | ||
* @property {CKEDITOR.plugins.widget.styleDefinition} |
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.
There is no such type.
plugins/widget/plugin.js
Outdated
// If there are no styles remove it to make sure we don't have `styles=''` in our output. | ||
delete styleDefinition.styles; | ||
} | ||
wrapper.attributes[ 'data-style-definition' ] = JSON.stringify( styleDefinition ); |
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.
At least it should be internal CKE attribute, [data**-cke**-style-definition]
.
What's more, information about partial styles (classes) are inside widget's data. Maybe we should reuse this mechanism and include all styles there.
plugins/image2/plugin.js
Outdated
@@ -932,6 +938,10 @@ | |||
|
|||
// Image can be wrapped in link <a><img/></a>. | |||
image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' ); | |||
|
|||
el.lockedStyle = el.lockedStyle || {}; |
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.
Shouldn't it be bound to the widget, not the element itself?
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.
At this point widget hasn't been initialized and there is no instance to which I could bind this attribute.
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.
But upcast method takes data
as a second parameter. These locked styles could be treated as a part of widget's data.
0b87074
to
9442085
Compare
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.
Additionally there are no unit tests for undo integration.
plugins/widget/plugin.js
Outdated
if ( CKEDITOR.tools.objectKeys( styles ).length ) { | ||
wrapper.setStyles( styleDefinition.styles ); | ||
} else { | ||
delete styleDefinition.styles |
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 semicolon.
plugins/widget/plugin.js
Outdated
var classes = widget.styleDefinition ? widget.styleDefinition.attributes[ 'class' ] : undefined; | ||
classes = classes && classes.split( /\s+/ ) ; | ||
if ( classes ) { | ||
if ( !!remove ) { |
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.
This logic seems a little bit reversed.
plugins/image2/plugin.js
Outdated
@@ -932,6 +938,10 @@ | |||
|
|||
// Image can be wrapped in link <a><img/></a>. | |||
image = el.getFirst( 'img' ) || el.getFirst( 'a' ).getFirst( 'img' ); | |||
|
|||
el.lockedStyle = el.lockedStyle || {}; |
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.
But upcast method takes data
as a second parameter. These locked styles could be treated as a part of widget's data.
|
||
var wrapper = widgetsRepo.wrapElement( toBeWrapped[ 0 ], toBeWrapped[ 1 ] ); | ||
handleStylesOnUpcast( toBeWrapped[ 0 ], wrapper ); |
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.
I wonder if it shouldn't be moved into upcast iterator, constructed in createUpcastIterator
. Thanks to that we'd be able to replace some magic properties (e.g. lockedStyles
) with reusing widget's data.
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.
I've moved lockedStyle
to data
and accessed it from here.
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.
But now parsing data is done manually. If we move handleStylesOnUpcast
to mentioned iterator, widget system would parse it for us
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.
I can't access widgets wrapper from there, because its not yet created. I could split this logic into two steps. First in iterator create styleDefinition
object and save it in data
. Then inside initOn
add styles to actual html elements.
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.
I wonder if styleDefinition
also couldn't be moved to widget data, similar to lockedStyle
, and whole style handling logic would then be placed inside initial data
handler. WDYT, @engineering-this and @mlewand?
plugins/widget/plugin.js
Outdated
var classes = widget.styleDefinition ? widget.styleDefinition.attributes[ 'class' ] : undefined; | ||
if ( classes ) { | ||
classes = classes && classes.split( /\s+/ ) ; | ||
if ( !!remove ) { |
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.
That logic is still reversed…
Doing so results in many tests failing |
22306a8
to
ddb8bad
Compare
plugins/widget/plugin.js
Outdated
|
||
if ( attributes ) { | ||
for ( item in attributes ) { | ||
if ( isNotInAttributes( item, styleDefinition.attributes ) ) { |
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.
It seems a little bit odd to have negative function. IMO !isInAttributes
is more readable.
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.
What's more: why only attrbitues have appropriate helper function and every other styles have just inline conditional?
plugins/widget/plugin.js
Outdated
* Removes the specified style from the widget. It is highly recommended to use the | ||
* {@link CKEDITOR.editor#removeStyle} or {@link CKEDITOR.style#remove} methods instead of | ||
* using this method directly, because unlike editor's and style's methods, this one | ||
* does not perform any checks. |
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.
IMO it's better to leave this comment as this method still does not perform any checks.
plugins/widget/plugin.js
Outdated
@@ -1026,20 +1026,19 @@ | |||
}, | |||
|
|||
/** | |||
* Applies the specified style to the widget. It is highly recommended to use the |
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.
I second this: info about recommended methods should stay.
plugins/widget/plugin.js
Outdated
@@ -1873,6 +1914,10 @@ | |||
// REPOSITORY helpers ----------------------------------------------------- | |||
// | |||
|
|||
function isNotInAttributes( item, attributes ) { | |||
return item !== 'class' && item !== 'style' && !( item in attributes ); |
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.
As I said, it's rather odd to have negative function.
} ); | ||
classes = classes.join( ' ' ); | ||
if ( classes ) { | ||
styleDefinition.attributes[ 'class' ] = classes; |
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.
Isn't it a duplication of the existing classes logic (storing them inside widget's data)?
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.
It is, but as styleDefinition
is meant to hold all widgets styles it should contain also classes. While we can't get rid of data.classes
it will be duplicated...
|
||
var wrapper = widgetsRepo.wrapElement( toBeWrapped[ 0 ], toBeWrapped[ 1 ] ); | ||
handleStylesOnUpcast( toBeWrapped[ 0 ], wrapper ); |
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.
But now parsing data is done manually. If we move handleStylesOnUpcast
to mentioned iterator, widget system would parse it for us
delete updatedClasses[ cl ]; | ||
changed = 1; | ||
} | ||
} | ||
if ( changed ) { | ||
widget.setData( 'classes', updatedClasses ); |
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.
Is it really needed now? We already store such information in styleDefinition
.
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.
I mean: we have now two sources of truth – our new styleDefinition
object and legacy classes data in widget's data.
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.
Getting rid of data.classes wouldn't break users custom plugins that extend widget? I'm not sure how to approach this.
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.
I'm quite sure that this data is used only internally by our plugin to know, which styles are applied to the widget. I don't even see any info about data.classes
in our docs. @mlewand, am I right or I'm missing something?
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.
https://docs.ckeditor.com/ckeditor4/latest/api/CKEDITOR_plugins_widget.html#method-setData
I've found only this references to data.classes
.
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.
@Comandeer @engineering-this We can't get rid of data.classes
it has been already exposed. Among all it's used by the image2
plugin.
Community had already a good time on picking it up, and relying on that. Because of that we should keep it for compatibility reasons.
plugins/widget/plugin.js
Outdated
// @param {CKEDITOR.plugins.widget} Widget instance. | ||
// @param {Object} Pairs of inline styles or attributes. | ||
// @param {Boolean} whenever to apply or remove style. | ||
function toggleStyleAttribute( widget, properties, attribute, apply ) { |
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.
Parameters names and use of this function is rather unclear. toggleStyleAttribute
should… well, toggle [style]
attribute. But it seems that this function is used to apply/remove attributes/styles.
505fb38
to
f5b6bde
Compare
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.
- Easy Image has the same issue as
image2
:data.classes
in newly created widget does not contain.easyimage
class, yetstyleDefinition
– does. Maybe such behavior is hardcoded intowidget
plugin itself? - This situation exists, so it means that we don't have tests for synchronisation between
data.classes
andstyleDefinition
:P - The fact that captioned image inserted via
image2
plugin is aligned couldn't be derived from inspectingstyleDefinition
. It does not contain any information about it. Yettext-align: <align>
is added to the widget wrapper anddata.align
is set. I'm not sure how to deal with this situation, because ingetData
such image is transformed into:
<div style="text-align:center">
<figure class="image" style="display:inline-block"><img alt="Comandeer" height="96" src="https://www.comandeer.pl/images/custom/comandeer.jpg" width="96" />
<figcaption>Podpis</figcaption>
</figure>
</div>
Therefore image is not aligned in output HTML. Yet I feel it still a mismatch between old style system and new one.
- What's even more interesting, for aligned (not captioned) inline
image2
,styleDefinition
contains styles forp
element. The output HTML for this widget is:
<p style="text-align:center"><img alt="Comandeer" height="96" src="https://www.comandeer.pl/images/custom/comandeer.jpg" width="96" /></p>
What's more interesting, styleableElements
for this widget are img figure
, which makes such styleDefinition
even more inexplicable.
- If it's not enough, after switching to source mode and back to WYSIWYG one,
styleDefiniton
still containsp
element, but without any attributes and styles. - I even wonder if we could make
styleDefiniton
a dictionary of style definitions, with styles dedicated to elements (parts?) of the widget and styles dedicated for widget's wrapper. In case of alignedimage2
it could look like:
{
wrapper: {
styles: {
'text-align': 'center'
}
},
figure: {
styles: {
'display': 'inline-block'
}
}
}
It would eliminate styleDefinition.lockedStyle
and carry far more information about actual styles. Downside: we'd need to rewrite nearly everything we already have…
- There is no manual test for undo integration.
- We could also add
styleDefinition
inspector to tests, because inspecting it in browser's console is a little bit troublesome.
plugins/widget/plugin.js
Outdated
styleDefinition.styles = CKEDITOR.tools.parseCssText( style ); | ||
} else { | ||
// If there are no styles remove it to make sure we don't have `styles=''` in our output. | ||
// delete styleDefinition.styles; |
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.
Some dead code here.
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.
Oh, I cheated myself here. I was looking for this line in my code and couldn't find it... It took me a minute to remember. I fixed this and amended, but didn't push 😁
I though about it, but then what in case of two elements with same html tag in widget. For that case I am thinking about something like that:
But that looks way too over complicated, and we are basically creating some kind copy of DOM tree. |
f5b6bde
to
0dce367
Compare
Changed applystyle tests for widget and easyimage, so there is visible output presenting current data and styleDefinition of focused widget.
I've updated manual test and added new test. So @mlewand you can take a look on this. In the meantime I will fix this one:
|
I see that this issue has been frozen. Does that mean that the issue of image tags not being able to take classes from the StylesCombo because IMG becomes IMAGE in the editor has been resolved? It has been an issue with the CKEditor that is bundled with Drupal 8 from day one and I'm keen about resolving it. Cheers, |
Hello! Unfortunately, the issue itself is still valid (#1674). We've just frozen this PR as the work done here wasn't finished two years ago and our codebase changed a lot since then, so it would take a lot of time to just rebase this PR and resolve all the conflicts. |
Hello @Dumluregn, Thanks for the information. I've been following the issue from the point of view coming from Drupal 8 where CKEditor is bundled. There is an issue in the Drupal Issue Queue (2642808) that goes back to 2015 and it has stalled in anticipation of a resolution from the CKEditor side. On Drupal we can preprocess the stylesSet array before rendering the editor so we are able to loop through the img classes and create a duplicate set for the image widget, formatted in such a way that CKEditor can work with them. The following example of this takes a class for an img tag and duplicates it for an image widget. Original stylesSet
StylesSet rendered in CKEditor. I'm not sure if this will help with the native CKEditor but this works within Drupal. |
What is the purpose of this pull request?
Feature
Does your PR contain necessary tests?
All patches which change the editor code must include tests. You can always read more
on PR testing,
how to set the testing environment and
how to create tests
in the official CKEditor documentation.
This PR contains
What changes did you make?
I have extended
widget.applyRemoveStyle
method so when passedCKEDITOR.style
instance has property 'style' or 'attribute' it will add or remove by those properties to widget.Closes #1674