I started creating my own CK5 Editor plugins, and am now stuck at the plugin for creating a custom call-to-action button.
What I want
I've created a button in the editor toolbar to create a new button. What I want to happen is that a button element is created, and the user is able to edit it's textual content.
<button> [this must be editable text] </button>
Where I got stuck
So I made it so far that a button element is created as soon as I click on the action in the toolbar. But as soon as I start typing, the button disappears and a paragraph is created. It looks like the cursor is inside the button element though. I've been reading through the API documentation for a while now, but I didn't get any further.
My code so far
First I register the callToAction schema
const schema = this.editor.model.schema
schema.register('callToAction', {
isObject: true,
allowWhere: '$block',
inline: true
})
Then I define the converters
conversion.for('upcast').elementToElement({
model: 'callToAction',
view: {
name: 'button',
classes: 'cta-button'
}
})
conversion.for('dataDowncast').elementToElement({
model: 'callToAction',
view: {
name: 'button',
classes: 'cta-button'
}
})
conversion.for('editingDowncast').elementToElement({
model: 'callToAction',
view: (modelElement, viewWriter) => {
/* The Button element is editable on selection */
const button = viewWriter.createContainerElement('button', {
class: 'cta-button',
})
/* Makes the element editable */
return toWidgetEditable(button, viewWriter)
}
})
I Also created a command to execute the creation of the button element
function createCallToAction(writer) {
const callToAction = writer.createElement('callToAction')
return callToAction
}
The actual question
How can I create the button with a standard placeholder text, let's say Enter text, and let the user edit this text inside the button? I've searched the internet for answers, looked at other plugins, but didn't get the solution I need.
Hope someone can help me, or at least send me in the right direction.
Thanks in advance
I believe, toWidgetEditable(button, viewWriter) does not change the 'editability' of an element, it just creates the widget. So when you create a container-element with:
const button = viewWriter.createContainerElement('button', {
class: 'cta-button',
})
This cannot later on 'become' editable.
Instead, create an editable element with:
const button = viewWriter.createEditableElement('button', {
class: 'cta-button',
})
Related
I am using PrimeFaces 7 and its TextEditor component, which uses internally the free and open-source editor Quill
I need to add a custom HTML button, which, when selected, inserts the word selected on the current position of the cursor in the TextEditor - (in the Quill)
It is possible to add custom buttons in the Quill Editor and to attach EventListeners to them, as shown here:
https://quilljs.com/docs/modules/toolbar/
(Please look at this part of the above page):
var customButton = document.querySelector('#custom-button');
customButton.addEventListener('click', function() {
console.log('Clicked!');
});
There is also an API provided by the Quill editor to insert text at a given position, see here:
https://quilljs.com/docs/api/#content
have a look at this mehod:
`quill.insertText(0, 'Hello', 'bold', true);`
However, I do miss a couple of things:
1.) The definition of the custom-button shall be done in the toolbar div, as specified in the following code:
`<div id="toolbar">
<!-- But you can also add your own -->
<button id="custom-button"></button>`
However: how could I do that, when using ready PrimeFaces Text Editor component?
I locally tried this:
jQuery(document).ready(function () {
jQuery(document).ready(function () {
var customButton = document.getElementById("resultsFormId:quillToolbarId_toolbar:custButId");
if (customButton!=null){
return;
}
customButton = document.createElement("button");
customButton.innerHTML = 'Params';
customButton.id="resultsFormId:quillToolbarId_toolbar:custButId";
var qlTollbar = document.getElementById("resultsFormId:quillToolbarId_toolbar");
qlTollbar.appendChild(customButton);
customButton.addEventListener('click', function() {
Quill.insertText(0, 'Hello', 'bold', true);
console.log('Clicked!');
});
});
});
and could say the following:
1.1) The custom button gets inserted on the Quill toolbar (is not nice and styled, but it is there)
1.2) When I click the custom button, first the EventListener gets executed. This is OK,but here:
Quill.insertText(0, 'Hello', 'bold', true);
instead of Quill.insertText()., I need a reference to the js-object representing the Quill editor. === > Could you help?
1.3) after the EventListener from Point (1.2) gets executed, my whole code under
jQuery(document).ready(function () {
jQuery(document).ready(function () {
gets executed once again, the id of the customButton is not found, and it is recreated once again. ===> Could I avoid that?
2.) In the code for the insertion of a text on a specific position, I need to get the last position the cursor was, before the user clicked (selected the option on the) custom button.
How can I do that?
I never heard of PrimeFaces, but maybe I can answer this part of you question:
Quill.insertText(0, 'Hello', 'bold', true);
instead of Quill.insertText()., I need a reference to the js-object representing the Quill editor. === > Could you help?
According to the code on GitHub it should be
PrimeFaces.widget.TextEditor.editor.insertText(0, 'Hello', 'bold', true)
can you confirm? Then you should also be able to get the cursor location and selection (if any) with
PrimeFaces.widget.TextEditor.editor.getSelection();
I am working on book creator in react and tinymce. User is allowed to create multiple textblock on canvas and fill them with text.
Everything works fine when button with onClick method for creating new textblock is called outside editor, like typical .
Problem started when i've tried adding a toolbar button:
setup: (editor) => {
editor.ui.registry.addButton("AddNew", {
text: "Add new",
onAction: (buttonApi) => addTextBlock()
});
},
and it works once. It creates textBlock object by updating the state object but consecutive clicks on this button reset state to initial value (empty list) and add again one object.
Same code executed from button that is placed outside editor correctly created clicked amount of textblocks.
I've no error or relevant warning in console.
I have same problem too then I resolved this with the following code:
setup: (editor) => {
let self = this;
editor.ui.registry.addButton('customButton', {
text: 'Button',
tooltip: 'Insert/edit image',
onAction: function(){
self.setState({click:true});
}
});
}
So I'm thoroughly confused as how to make a custom SummerNote button which will allow me to either add classes to an already inserted HTML element (i.e. links) or even wrap selected text in something like:
<button class="btn btn-primary"> **selected text** </button>
From the Summernote Deep Dive it shows how to create a custom button that inserts static text and I understand how to implement it. However, I can't seem to figure out how to make it dynamic for whatever text is selected. I've tried replacing the 'text' part of the example after context.invoke call but it doesn't convert the text to html, it just prints it.
The need is for creating links as buttons. Ideally I'd wish to access the Insert Link dialog already built into SummerNote to make buttons that will insert links with predefined classes but I'm not sure how complicated that would be.
Does anyone have any suggestions?
var bsButton = function (context) {
var ui = $.summernote.ui;
// create button
var button = ui.button({
contents: 'Btn',
tooltip: 'Create Button',
click: function () {
// invoke insertText method with 'hello' on editor module.
context.invoke('editor.insertText', '<button class="btn btn-primary"></button>');
}
});
return button.render(); // return button as jquery object
}
I've reviewed a ton of different posts but I haven't gotten much further than the example for what I'm trying to accomplish. Thanks in advance to anyone who takes the time to help.
As I was doing more research I stumbled upon Summernote's 'Awesome Summernote' GitHub page which led me to a plugin, summernote-addclass. While this doesn't necessarily answer the heart of the question (i.e. achieving this same thing without a plugin) but it does exactly what I need to do. Maybe this will help someone else in the future. Enjoy!
By using jQuery in summernote callback method onInit I have added custom class for custom button
Here is my custom button code:
var AddPage = function(context) {
var ui = $.summernote.ui;
var button = ui.button({
contents: '<i class="fa fa-plus"/> Add Page',
tooltip: "Set a New Page",
class: "btn-primary",
click: function() {
addPage();
}
});
return button.render();
};
And i use callback methods to add custom class btn-primary to the button
$("#editor").summernote({
airMode: false,
dialogsInBody: false,
toolbar: [["mybutton", ["customButton"]],
buttons: {
customButton: AddPage
},
callbacks: {
onInit: function(e) {
var o = e.toolbar[0];
jQuery(o)
.find("button:first")
.addClass("btn-primary");
}
}
});
});
I think this may be helpful for anyone, even if this is not gud answer.
I know I can add buttons in TinyMCE 4 using addButton . But when I use addButton and enter a title for it, only an empty button is shown in the editor with a tooltip that contains the content of title. How do I add a title, that is actually shown in the menubar, like for the save button?
// Create and render a button to the body element
tinymce.ui.Factory.create({
type: 'button',
text: 'My button'
}).renderTo(document.body);
I found the solution, thanks to Eslam.
You actually just set the 'text' value of addButton:
$('#site_content').tinymce({
setup : function(ed) {
ed.addButton('name', {
text : 'TEXT',
onclick : function() {
}
});
}
});
The documentation of TinyMCE is just awful at some parts, if you'd ask me
I'm using the YUI TabView widget for adding and removing tabs like described in yuilibrary-tabview-add-remove.
I've noticed a "bug" or maybe just a missing functionality: When you close all tabs and then add a new tab, the "add tab" button will get stuck on the left side of the tab bar, and all new tabs will be sorted on the right side. If you don't close all tabs, the button will always stay on the right side no matter what.
Now, I've added a workaround: When adding a new tab, the no-tabs state will be detected and the DOM li-item will be sorted with the jQuery after() method. Finally, the newly added tab will be selected:
onAddClick : function(e) {
e.stopPropagation();
var tabview = this.get('host'), input = this.getTabInput();
tabview.add(input, input.index);
// When previously no tabs present, move 'add button' to end after adding a new tab
if ( tabview.size() == 1) {
var addTabButton = $('#addTabButton');
addTabButton.next().after(addTabButton);
tabview.selectChild(0);
};
}
However, I'm not happy with this solution. Might there be a more elegant way to solve this issue?
Your solution is definitely valid. I'd just write it using YUI because loading YUI and jQuery is really expensive in kweight and maintenance cost (you and your coworkers need to master two libraries).
One clean option is to create a node in the initializer and keep a reference to it so that you can move it around later:
initializer: function (config) {
var tabview = this.get('host');
// create the node before rendering and keep a reference to it
this._addNode = Y.Node.create(this.ADD_TEMPLATE);
tabview.after('render', this.afterRender, this);
tabview.get('contentBox')
.delegate('click', this.onAddClick, '.yui3-tab-add', this);
},
_appendAddNode: function () {
var tabview = this.get('host');
tabview.get('contentBox').one('> ul').append(this._addNode);
},
afterRender: function (e) {
this._appendAddNode();
},
onAddClick: function (e) {
e.stopPropagation();
var tabview = this.get('host'), input = this.getTabInput();
tabview.add(input, input.index);
// When previously no tabs present, move 'add button' to end after adding a new tab
if ( tabview.size() == 1) {
// _addNode will already be present, but by using append() it'll be moved to the
// last place in the list
this._appendAddNode();
};
}
Here's a working version: http://jsbin.com/iLiM/2/