How to add custom button in draft.js editor toolbar for adding a horizontal rule and how to get inline styles using stateTOHTML - draftjs

I have a texteditor with a basic toolbar header
I want to add a text alignment options(right, left, and center indent) and a divider(horizontal line) option I am using react-draft-wysiwyg editor and no additional plugins.
<Editor
editorState={this.state.editorState}
onEditorStateChange={this.handleEditorChange}
wrapperClassName="wrapper-class"
editorClassName="editor-class"
toolbarClassName="toolbar-class"
toolbar={{
options: ['inline', 'blockType', 'list', 'emoji', textAlign]
}}
Please someone, guide me on how to add custom options in the draft.js toolbar.

I am using
import draftToHtml from 'draftjs-to-html';
import htmlToDraft from 'html-to-draftjs';
for applying inline styles and it is working like charm. Instead of using stateToHTML / covertToHTML use draftToHtml(convertToRaw( this.state.editorState.getCurrentContent() ));
and for converting HTML to raw data use: this.setState({ editorState: EditorState.createWithContent( ContentState.createFromBlockArray(htmlToDraft(data)) ), });

Related

How to search for and highlight a substring in Codemirror 6?

I'm building a simple code editor to help children learn HTML. One feature I'm trying to add is that when users mouseover their rendered code (in an iframe), the corresponding HTML code in the editor is highlighted. So, for example, if a user mouses-over an image of kittens, the actual code, , would be highlighted in the editor.
Mousing-over the iframe to get the html source for that element is the easy part, which I've done (using document.elementFromPoint(e.clientX, e.clientY in the iframe itself, and posting that up to the parent) - so that's not the part I need help with. The part I can't figure out is how to search for and highlight that string of selected code in the code editor.
I'm using Codemirror 6 for this project, as it seems as it will give me the most flexibility to create such a feature. However, as a Codemirror 6 novice, I'm struggling with the documentation to find out where I should start. It seems like the steps I need to complete to accomplish this are:
Search for a range in the editor's text that matches a string (ie.'<img src="kittens.gif"').
Highlight that range in the editor.
Can anyone out there give me some advice as to where in the Codemirror 6 API I should look to start implementing this? It seems like it should be easy, but my unfamiliarity with the Codemirror API and the terse documentation is making this difficult.
1. Search for a range in the editor's text that matches a string (ie.'<img src="kittens.gif"').
You can use SearchCursor class (iterator) to get the character's range where is located the DOM element in your editor.
// the import for SearchCursor class
import {SearchCursor} from "#codemirror/search"
// your editor's view
let main_view = new EditorView({ /* your code */ });
// will create a cursor based on the doc content and the DOM element as a string (outerHTML)
let cursor = new SearchCursor(main_view.state.doc, element.outerHTML);
// will search the first match of the string element.outerHTML in the editor view main_view.state.doc
cursor.next()
// display the range where is located your DOM element in your editor
console.log(cursor.value);
2. Highlight that range in the editor.
As described in the migration documentation here, marked text is replace by decoration. To highlight a range in the editor with codemirror 6, you need to create one decoration and apply it in a dispatch on your view. This decoration need to be provide by an extension that you add in the extensions of your editor view.
// the import for the 3 new classes
import {StateEffect, StateField} from "#codemirror/state"
import {Decoration} from "#codemirror/view"
// code mirror effect that you will use to define the effect you want (the decoration)
const highlight_effect = StateEffect.define();
// define a new field that will be attached to your view state as an extension, update will be called at each editor's change
const highlight_extension = StateField.define({
create() { return Decoration.none },
update(value, transaction) {
value = value.map(transaction.changes)
for (let effect of transaction.effects) {
if (effect.is(highlight_effect)) value = value.update({add: effect.value, sort: true})
}
return value
},
provide: f => EditorView.decorations.from(f)
});
// this is your decoration where you can define the change you want : a css class or directly css attributes
const highlight_decoration = Decoration.mark({
// attributes: {style: "background-color: red"}
class: 'red_back'
});
// your editor's view
let main_view = new EditorView({
extensions: [highlight_extension]
});
// this is where the change takes effect by the dispatch. The of method instanciate the effect. You need to put this code where you want the change to take place
main_view.dispatch({
effects: highlight_effect.of([highlight_decoration.range(cursor.value.from, cursor.value.to)])
});
Hope it will help you to implement what you want ;)
Have a look at #codemirror/search.
Specifically, the source code implementation of Selection Matching may be of use for you to adapt.
It uses Decoration.mark over a range of text.
You can use SearchCursor to iterate over ranges that match your pattern (or RegExpCursor)
Use getSearchCursor, something like this:
var cursor = cmEditor.getSearchCursor(keyword , CodeMirror.Pos(cmEditor.firstLine(), 0), {caseFold: true, multiline: true});
if(cursor.find(false)){ //move to that position.
cmEditor.setSelection(cursor.from(), cursor.to());
cmEditor.scrollIntoView({from: cursor.from(), to: cursor.to()}, 20);
}
Programmatically search and select a keyword
Take a look at getSearchCursor source code it it give some glow about how it works and its usage.
So use getSearchCursor for finding text and optionally use markText for highlighting text because you can mark text with setSelection method of editor.
Selection Marking Demo
var editor = CodeMirror.fromTextArea(document.getElementById("code"), {
lineNumbers: true,
styleSelectedText: true
});
editor.markText({line: 6, ch: 26}, {line: 6, ch: 42}, {className: "styled-background"});
And it seem this is what you are looking for:
codemirror: search and highlight multipule words without dialog
RegExpCursor is another option that you can use:
new RegExpCursor(
text: Text,
query: string,
options⁠?: {ignoreCase⁠?: boolean},
from⁠?: number = 0,
to⁠?: number = text.length
)
Sample usage at:
Replacing text between dollar signs for Mathml expression.

How do I make the MUI Dialog title an H1 element so the modal is accessible

I'm working on making my app more accessible and am struggling with the MUI Dialog component. I'm using the DialogTitle component, which creates an H2 element and am getting an issue of "page doesn't contain a level-one heading". Should I be creating my modal in some other way, or are MUI Dialogs just not accessible?
import { Dialog, DialogTitle } from '#mui/material';
const MyModal = () => {
return (
<Dialog open={true}>
<DialogTitle>
My Title
</DialogTitle>
</Dialog>
);
};
export default MyModal;
Updated for MUI v5:
The Dialog component API includes a helper DialogTitle component which, by default, renders its contents within an h2 element. To change this functionality, you can pass the component property to the DialogTitle to have the DialogTitle rendered using whatever elementType that you wish. For example:
<DialogTitle component="h1">
My Dialog Title
</DialogTitle>
This is currently an undocumented feature of DialogTitle, but it can been seen in the source code that properties that are passed to DialogTitle are spread onto the underlying Typography component -- By passing component, you are essentially overwriting the hardcoded component="h2" prop with your own value.
Working example: https://codesandbox.io/s/simpledialog-material-demo-forked-kpq9k?file=/demo.js
Original answer for MUI v4:
The Dialog component API includes a helper DialogTitle component which, by default, renders its contents within an h2 element. To disable this functionality, you can use the DialogTitle component with the disableTypography prop (to disable the h2 wrapping behavior) and then include your own Typography component set to h1. For example:
<DialogTitle disableTypography>
<Typography variant="h1">My Dialog Title</Typography>
</DialogTitle>
Working example: https://codesandbox.io/s/material-demo-forked-7pso2?file=/demo.js
Extra Credit: You may then come across the problem that the h1 is styled "too large" for your design. If so, and you prefer the h2 look, you can use the Typography prop named component in combination with the variant prop to visually style it back to an h2, while maintaining the underlying h1 element. For example:
<DialogTitle disableTypography>
<Typography variant="h2" component="h1">My Dialog Title</Typography>
</DialogTitle>
In the case of TypeScript there's no typing provided so you need to do a hack:
<DialogTitle {...{ component: 'div' } as any}>
<Typography variant="h1">My Dialog Title</Typography>
</DialogTitle>
This is a temporary hack until they add the typing for all the props there.
As #Steve mentionned, in Mui v5, the job is harder. Even if you specify a component prop (which won't work in TS, moreover it's a bad practice), it won't change the style as it renders a h2 with a h6-style.
The cleanest workaround would be to mess with CSS (in a styled component for eg)
// Styling
import { styled } from "#mui/system";
// UI
import { DialogTitle as MuiDialogTitle } from "#mui/material";
const DialogTitle = styled(MuiDialogTitle)(({ theme }) => ({
"&.MuiDialogTitle-root.MuiTypography-root": {
fontSize: 25,
fontWeight: "bold",
},
}));

CKeditor: How to build a custom plugin?

I am trying to create a custom plugin for CKeditor following this guide. I created the files as indicated (myplugin.png, myplugin.js, plugin.js) and added
CKEDITOR_CONFIGS = {
'default': {
'extraPlugins': ','.join( [ 'myplugin' ] ),
'allowedContent' : True,
}
}
to the settings.
This is the content of my plugin.js file:
CKEDITOR.plugins.add( 'myplugin', {
icons: 'myplugin',
init: function( editor ) {
// Plugin logic goes here...
editor.addCommand( 'myplugin', new CKEDITOR.dialogCommand( 'mypluginDialog' ) );
editor.ui.addButton( 'myplugin', {
label: 'My Plugin',
command: 'myplugin',
toolbar: 'insert'
});
}
});
Yet, the icon of the custom plugin still doesn't show. I can see in the browser's tools that the plugin.js file is retrieved. I made a test by removing the icon file and it didn't create any difference (no error message, no 404). I suppose then that the file is not even called or accessed. so the initialization does not even try to render the button.
Thank you for your help.
Finally, I found the answer to the problem. It comes from the way CKEditor displays the toolbars. In the guide, the custom plugin is added to the "insert" group of the toolbars. However, this one will not be visible until it is explicitely set to be displayed.
Adding the extra plugin to the default configuration is not enough, the toolbar setting has to be specified properly (if for some reason, your platform doesn't default to null). In my case, with django-ckeditor, I had to add
'toolbar': None,
to the CKEDITOR_CONFIGS.

How to add a custom control to the TinyMce 4 toolbar

I want to introduce a new Control to TinyMce that I can use in the toolbar. In my case I want to add an icon control that can be placed at the start of the toolbar to differentiate between editors.
However there is almost no information about how to properly do this.
Finally I managed to come up with a way to properly do this.
First I introduce a new plugin icon (in icon/plugin.js) that registers a new control Icon. It uses a setting iconClass.
tinymce.PluginManager.add('icon', function() {
tinymce.ui.Icon = tinymce.ui.Widget.extend({
renderHtml: function () {
return '<span class="icon icon-' + this.settings.iconClass + '"> </span>';
}
});
});
Next I add a button facebook to the toolbar in the following way:
editor.addButton('facebook', {
type: 'icon',
iconClass: 'facebook-share'
});
Now I can add it to the toolbar specification:
tinymce.init({
toolbar: "facebook"
})
That's it! The new custom control should not render. The plugin code is only ran once; even if used multiple times.

Display Views exposed form item label inside selects (Instead of the default '- Any -')?

How to display form item label in Views exposed form instead of '- Any -'? To be more specific I use this code to replace select's default value text with custom text and want that custom text to be the label of that element:
function THEMENAME_form_views_exposed_form_alter(&$form, &$form_state) {
//dpm($form);
if ($form['#id'] == 'views-exposed-form-FORMID') {
$form['ITEMNAME']['#options']['All'] = t('My custom translatable text');
}
}
This works for custom text. What I want is to display its label instead of My custom translatable text with the simple code like:
$form['ITEMNAME']['#options']['All'] = $form['ITEMNAME']['#name'];
but have no luck on such and similar codes to work. According fo $dpm($form) output '#name', '#title' elements seem not to exist at all.
The goal is to have similar functionality of https://drupal.org/project/compact_forms or https://drupal.org/project/In-Field-Labels without another Javascript library (prefer to use couple PHP lines, please no JS solutions)
Your above code will work in case of select field but not for text field. If you need it to work for text fields you can try this
$form['ITEMNAME']['#attributes'] = array('placeholder' => array('My custom translatable text'));
or
$form['ITEMNAME']['#attributes'] = array('placeholder' =>$form['ITEMNAME']['#name']);
hope this helps you