I'm implementing a new widget that will be very similar to the simplebox example, so I'm trying to make that work first.
In the tutorial, it is said that specifying allowedContent is enough to include additional elements, classes, styles that we need, so that it is not filtered out. In the main editor configuration I have
div(simplebox);
This successfully enables the widget but still, if I switch to source and back to wysiwyg, things inside like the class for the inner div - "simplebox-content" is deleted and the widget no longer works properly. I haven't changed the code for simplebox.
Am I doing something wrong here?
Take a look on widget definition in Simplebox plugin:
editor.widgets.add( 'simplebox', {
// Allow all HTML elements, classes, and styles that this widget requires.
// Read more about the Advanced Content Filter here:
// * http://docs.ckeditor.com/#!/guide/dev_advanced_content_filter
// * http://docs.ckeditor.com/#!/guide/plugin_sdk_integration_with_acf
allowedContent:
'div(!simplebox,align-left,align-right,align-center){width};' +
'div(!simplebox-content); h2(!simplebox-title)',
// Minimum HTML which is required by this widget to work.
requiredContent: 'div(simplebox)',
...
} );
It says that the minimal requiredContent for the widget to work is div(simplebox). By "minimal", it means that it will run but with a limited set of features.
And this is exactly what happens when you set config.allowedContent = 'div(simplebox)'. If you compare it with allowedContent property in widget definition, you'll notice that by default, the widget registers much more than that in editor's filter, like .align-left, .align-right, .align-center and .simplebox-content classes for <div> and .simplebox-title class for <h2>. Once you set config.allowedContent, you override the rules defined by the widget and if some elements/classes become disallowed because of that, the structure of the widget gets "flattened", just like described
[...] things inside like the class for the inner div - "simplebox-content" is deleted and the widget no longer works properly
So the long story short: When you define config.allowedContent on your own, you are responsible for the shape of editor features and you must understand the consequences. Only if you include all the rules from the definition of Simplebox, you'll enjoy the full functionality of the widget.
I had the same problem. What I did was I added the classes inside editables variable like this:
editables: {
content: {
selector: '.static-gray-box',
allowedContent: 'h5(first-child,last-child);h6(first-child,last-child);p(first-child,last-child)'
}
},
Related
I have a component A that should dynamically change the font size of some of it's contents. I currently use CSS variables to do that and the component will contribute some CSS String containing these CSS variables:
public void renderHead(IHeaderResponse response) {
String fontCss = // dynamically fetch CSS cariables
response.render(CssHeaderItem.forCSS(fontCss, "font-css"));
}
On the same page I have the possibility to change these font sizes using an AJAX update within another component B. This will add the component A to the AjaxRequestTarget, which will cause the renderHead method to be executed with updated values for the font CSS variables.
However, I don't see an updated font size in my browser as the old CSS variables still seem to be present. How can I enforce the new CSS to overwrite the old one?
So far I found 2 solutions, that seem like dirty workarounds to me:
Add the whole page to the AjaxRequestTarget, so the whole page will be refreshed.
Add JavaScript to the AJAX update to remove the old styling with:
var allStyles = document.getElementsByTagName("style");
for (var style of allStyles) {
if (style.getAttribute("id").includes("font-css")) {
style.remove();
}
}
Is there a cleaner solution to this problem?
You found the problem with workaround 2.
response.render(CssHeaderItem.forCSS(fontCss, "font-css"));
adds <style id="font-css"> ... </style> to the page. Later when the Ajax response contributes the new content the JavaScript logic finds that there is an HTML element with id font-css and assumes that there is nothing to do.
A simple solution is to use dynamic id, e.g. by using UUID in #renderHead().
Another solution is to make this <style> a proper Wicket Component, a Label, that could be added to the AjaxRequestTarget with an updated Model when needed.
I'm working a project that will dynamically allow the user to change themes, and uses reactstrap and styled-components under the hood. We want to configure all of the variables via SASS, which is working fine. In order to make those variables available to styled-components, we have been using sass-extract-loader to create theme objects.
This all works great when we statically choose one of the themes, but I haven't been able to get it working dynamically reliably. I have two issues:
1) In development, it works fine to switch the theme once. If I change it again, my non-styled-components (i.e., raw reactstrap components) are styled with the second theme. I believe this is because the second theme is loading and overriding the original CSS.
2) In production, I get the same mix as #1 by default (i.e., because all of the CSS files are being put together into a single bundle, reactstrap components are styled one way, while styled-components "honors" the theme).
I believe the best option for us is to have the themes in two separate CSS files, and to toggle "alternate" rels on them. I just don't know how to configure CRA not to put all of the CSS into a single main bundle, and let me manually add links to alternate stylesheets. If I can split them out into separate files, I believe I can just add tags and dynamically swap the rel="alternate" property.
There may well be better ways to accomplish this. My understanding is that the easiest way to control the Bootstrap themes is via SASS variables, and I'd like to make sure those variables don't have to be re-defined when using them in styled-components.
If you want to apply styles conditionally, you can import your stylesheets in your index.js file and make it available to all your components through the context API. First of all we import the CSS files into index.js.
import themeA from './css/themeA.css';
import themeB from './css/themeB.css';
However, by using it this way, you cannot have element selectors in both CSS files, because they would be globally applied from both files. However, you could import an extra stylesheet that complements the selected theme, in which you define the element selectors.
By using CSS modules, you avoid the need for element selectors. You may want to read this article if you are unfamiliar with CSS modules: https://javascriptplayground.com/css-modules-webpack-react/
If you still need to apply element selectors in one theme, you can do so, but they will also get applied in your other theme this way.
import './css/default.css';
This example below is a modified version from the React documentation: https://reactjs.org/docs/context.html
In this example, we draw a button that changes the global theme when
it its clicked. There are three parts in this example that are crucial
to understand if you want to use React its context API.
1. Creating a Context.
Creating a Context is being done by assigning the return value of React.createContext(yourValue) to a variable. This variable can then be used to use the Provider or Consumer component inside your React components.
const ThemeContext = React.createContext();
2. Providing a value by passing it to your Provider component as prop. Now your value is accessible to all your components.
class App extends React.Component {
swapTheme() {
this.setState({ withThemeA: !this.state.withThemeA });
}
render() {
const theme = this.state.withThemeA ? themeA : themeB;
return (
<ThemeContext.Provider value={theme}>
<ThemedButton onClick={this.swapTheme} />
</ThemeContext.Provider>
);
}
}
3. Listening to updates with the Consumer component.
To listen for changes, you need to use the Consumer component. The passed function receives the theme as an argument so it can be assigned to the className prop of the button element.
class ThemedButton extends React.Component {
render() {
return <ThemeContext.Consumer>
{theme => <button className={theme.Button}}>click me</button>}
</ThemeContext.Consumer>;
}
}
I am trying to set the default style applied to the P elements that are automatically created when a user enters the blank editing area. I've spent many hours searching for an answer but have not found anything that works. The requirements are:
Style has to be inline, no stylesheet
No user interaction, no format/style plugin to click
When the user clicks in the editing area and starts typing, I want the style to be applied and visible automatically. Surely there is a way to accomplish this?
The closest I have gotten is by using the htmlFilter, like this :
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
But the new style is not automatically visible.
It does become visible if the user goes into source editing mode and back to WYSIWYG but I want it to be automatic.
I tried using updateElement() in the filter function, but it does not work and creates infinite recursion:
p_rule = {
elements : {
p : function(element) {
if (element.attributes.style === undefined) {
element.attributes.style = "color: #0000ff;";
CKEDITOR.instances['editor1'].updateElement();
}
}
}
};
ev.editor.dataProcessor.htmlFilter.addRules(p_rule);
(I guess updateElement() triggers the filter)
If I use setData(getData()) from an event I can strangely get the textarea to update with the changes the filter applied, for example:
CKEDITOR.instances['editor1'].on('blur', function() {
CKEDITOR.instances['editor1'].setData(CKEDITOR.instances['editor1'].getData());
});
But that too requires user interaction. Using the "change" event creates recursion.
I am new at CKEditor and obviously I'm missing something on how the filter works in relation to what is currently being displayed in the textarea.
Any CKEditor guru out there? Help!
Thanks
I really advise not to go this way. You'll find yourself fighting with countless issues, like what if you copy&paste, what if you change format to h1 and then back, what if you create a list item and then convert that into a paragraph, etc. etc. There are really dozens of those. You'd need to rewrite half of the editor.
The way to handle this in CKEditor 4 is to rethink this:
Style has to be inline, no stylesheet
Inside CKEditor you clearly need to use a stylesheet. I presume though that you want the inline styles in the output. So what I would propose is to:
Write htmlFilter rule which adds this style to every paragraph.
Write dataFilter rule which removes this style from every paragraph.
The second rule is needed so if you save the data and then load it back to the editor, the styles do not pollute it.
PS. CKEditor 5 will separate data model from rendering (the view) so you'll be able to render paragraph as you wish without affecting how other features interact with it. Read more about CKEditor 5 in this article.
If the source of something I write in my CKEDITOR looks like this:
This is my text. <strong>This part is bold.</strong> This part isn't.
I can highlight the bolded part and unbold it by pressing CTRL+B. However, if I add a class to that strong tag (due to another plugin I'm working on), I can only unbold clean strong tags - no attributes, styles, or classes. For example, consider this scenario:
This is my text. <strong>This part is bold.</strong> This part isn't. <strong class="whatever">This part is bolded AND has a custom class.</strong>
Only the first bolded segmented will be unbolded - the 2nd is pretty much stuck as is until I remove the ".whatever" class. Is there any way to get it to ignore strong tags with classes, and just do them regardless of what other attributes they have? I'm guessing it has to do with that "Advanced Content Filter" or something, but I can't figure out what.
After much hairpulling, I (think) I have the answer. In the CKEDITOR style definition, an applied style (ex, a strong tag) needs to have all of its attributes parsed through the content filter. If an attribute that is not dealt with by this filter remains when it comes time to actually remove the textNode from the style tags and replace it back into the parent element, the tags (and thus the style) will NOT be removed if there are any attributes remaining on the element. There is a (very poorly) documented workaround to this: the alwaysRemoveElement property can be set to true on the style DEFINITION (why the definition, and not the style itself, I have no idea).
Long story short, a little snippet of code that will force the removal of all style tags, even if their attributes don't match exactly with the filter. Hopefully it doesn't cause bugs somewhere else...
//this = Your Editor Instance
this.data.editor.on( 'instanceReady', function(){
//Filter through the existing contentRules, looking for styleCommands
$.each(this.activeFilter.allowedContent, function(i,v) {
var name = v.featureName, command = this.commands[v.featureName];
if (name && command && command.contentForms && command.style) {
command.style._.definition.alwaysRemoveElement = true;
}
}.bind(this));
}.bind(this));
As the previous answer, just add this in the config:
CKEDITOR.config.coreStyles_bold : { element: 'strong', overrides: 'b' ,alwaysRemoveElement: true},
It was also difficult for me to find this workaround, in my case I was adding an id to the strong element.
Right now I have a class that I'm #extend-ing to all button elements. However, I would like to not extend it to one single button and would prefer not to resort to changing the HTML to make it a link.
Is there any way to remove the #extend for a particular rule in SASS?
No. Your options are:
Don't style all buttons (use classes or more specific selectors to avoid your unique element)
Override the styles of the unique element (depending on the styles used, it can be very difficult to return a button to its default appearance for every browser)
The least painful solution for you would be to use the :not selector:
// style all buttons, except for ones with the "default" class on them
button:not(.default) { %extend theStyles }