State not updated when setState is called from tinyMCE onAction method - javascript

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});
}
});
}

Related

Add a custom button to the Quill's toolbar (and integrate it also on the PrimeFaces Text Editor)?

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();

Custom CTA button plugin CK5Editor

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',
})

manual click event only triggers on second click

I build a small color-picker module. But it only opens up (and then works) when pickColor is called a second time. I also tried to wrap the _openColorPicker into a setTimeout but that didn't work either. In fact, the color-picker didn't show up at all when I did that.
What I found interesting is that the binding to the change event works, so the $ selector must have found the element already.
So I have two questions:
1) why is the picker only showing after the second call to _openColorPicker?
2) why didn't the picker open at all when I wrapper the _openColorPicker call in a setTimeout?
Edit: The _openColorPicker functions gets executed after the user has right-clicked into the document and then clicked on context-menu which is now showing.
Complete Code:
const ColorUtils = {
_initialized: false,
_openColorPicker: function () {
$('#color-picker').click();
},
pickColor: function (onChangeCallback, context) {
if (!this._initialized) {
$('<input/>').attr({
type: 'color',
id: 'color-picker',
display: 'hidden',
value: '#ffffff'
}).appendTo('#centralRow');
this._initialized = true;
$('#color-picker').on('change', onChangeCallback.bind(context));
}
this._openColorPicker();
// version with timeOut
const scope = this;
setTimeout(function () {
scope._openColorPicker();
}, 1000);
}
};
export default ColorUtils;
Above code is used like ColorUtils.pickColor(onColorPicked, this);
Check out this post. Looks like you can't trigger a click on an invisible color picker. That answer suggests giving the element an absolute position and placing it off screen, like so:
position:absolute;
left:-9999px;
top:-9999px;
I tried to replicate your case (for what I understood) : JSFIddle
I made some changes.
I moved the $('<input/>') in a property of the object ColorUtils and appended it to the DOM with absolute position and outside the screen.
(And also commented display:'hidden' because it's either display:none or visibility:hidden and as a CSS property, not Html attribute)
On right clic on the document I instantiate the picker (and register the callback + context) then add a button to the DOM to trigger the picker again.
Does it fulfill your requirements ?

Pebble.JS card when "action" is set - "scrollable" fails

I am trying to create a card that would have action on clicking "select" button and still have scrollable text, something like:
var card = new UI.Card({
title: 'Title',
body: 'long text goes here...',
action: {
select: 'images/refresh.png',
backgroundColor: 'white'
},
scrollable: true
});
card.on('click', function(e) {
if (e.button == 'select') {
//some code
}
});
if executed like this - icon in action bar is visible and "click" event runs, but "scrollable: true" no longer has effect. If I comment "action" property - "click" event still runs and this time "scrollable: true" is working, but of course no icon is displayed. Is it a bug or is it by design? How can I have best of both worlds - display icon for "select" and keep card scrollable?
Thanks to this question, I've made a commit that will allow you to use the action bar layer and scroll layer at the same time: https://github.com/pebble/pebblejs/commit/04f926f137395a0ebd0faaab8b0722da9aa75a7d.
I'll update this answer when the commit is merged into CloudPebble.

YUI TabView: "add tab" button stuck on the left side when all tabs are closed

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/

Categories