Generating TinyMCE drop-down menu dynamically - javascript

I am trying to create toolbar button in TinyMCE with options that are derived from the array. I've followed the examples on Tiny's website and the button is getting generated as expected. Here is the code:
var mergeFields = {one: "first", two: "second", three: "third"};
tinymce.init({
selector: 'textarea',
menubar: false,
toolbar: 'mergefields',
setup: function (editor) {
editor.ui.registry.addMenuButton('mergefields', {
text: 'Merge Fields',
fetch: function (callback) {
var items = [];
for (var fieldName in mergeFields) {
var menuItem = {
type: 'menuitem',
text: mergeFields[fieldName],
onAction: function() {
// The problem: this function always inserts the last element of the array
// instead of the expected fieldName associated with this menuItem
editor.insertContent(fieldName);
},
};
items.push(menuItem);
}
callback(items);
},
});
}
});
<script src="https://cloud.tinymce.com/5/tinymce.min.js?apiKey=XXXXX"></script>
<textarea>Editor</textarea>
The problem happens when one of the options is selected and the anonymous function assigned to onAction property is executed -- it always inserts "three" into the document (presumably because after running through the whole array, fieldName is set to "three"). How can I make the onAction handler insert the right value into the document?
This needs to work in TinyMCE 5.
I've found a similar question here: Adding custom dropdown menu to tinyMCE and insert dynamic contents, but it was referring to TinyMCE 4 and unfortunately the provided answer does not work for TinyMCE 5.
Thanks for your help!

I had the same problem.
I solved it using value+onSetup
https://jsfiddle.net/stvakis/tjh7k20v/8/
var mergeFields = {
one: "first",
two: "second",
three: "third"
};
tinymce.init({
selector: 'textarea',
menubar: false,
toolbar: 'mergefields',
setup: function(editor) {
editor.ui.registry.addMenuButton('mergefields', {
text: 'Merge Fields',
fetch: function(callback) {
var items = [];
for (var fieldName in mergeFields) {
var menuItem = {
type: 'menuitem',
text: mergeFields[fieldName],
value:fieldName,
onSetup: function(buttonApi) {
var $this = this;
this.onAction = function() {
editor.insertContent($this.data.value);
};
},
};
items.push(menuItem);
}
callback(items);
},
});
}
});

Related

Summernote custom button pass context and data

I am using summernote in angular I want to create a custom button.
I would like to pass the listHit in customButton as parameter like this 'testBtn': this.customButton(context, listHit) but I am not sure how to do this since the function looks like this 'testBtn': this.customButton any help would be appreciated thank you.
My custom button looks something like this.
customButton(context) {
var listHit = ['One', 'Two', 'Tree'];
const ui = ($ as any).summernote.ui;
var i;
var listHitHtml = "";
for (i = 0; i < listHit.length; i++) {
listHitHtml += "<li>" + listHit[i] + "</li>";
}
var button = ui.buttonGroup([
ui.button({
className: 'dropdown-toggle',
contents: '<i class="fa fa-comments"/><span class="caret"></span>',
tooltip: '#erp_colombia.Lang.Resource.conAvailableComments',
data: {
toggle: 'dropdown'
}
}),
ui.dropdown({
className: 'drop-default summernote-list',
contents: "<div id=\"container-comentario\"><div id=\"dialog\" title=\"Comentarios\" ><h1 class=\"header-comentario\">" + 'Comment' + "</h1><ul id=\"liste-comentarios\"><ul>" + listHitHtml + "</ul></div></div>",
callback: function ($dropdown) {
$dropdown.find('li').each(function () {
$(this).click(function () {
context.invoke("editor.insertText", $(this).html() + "\n");
});
});
}
})
]);
return button.render(); // return button as jquery object
}
Here is my pdfmaker config
this.config = {
placeholder: placeholder,
shortcuts: false,
disableDragAndDrop: true,
//tabsize: 2,
hint: {
mentions: this.quoteCommentsForSummerNote,
match: /\b(\w{1,})$/,
search: function (keyword, callback) {
callback($.grep(this.mentions, function (item: any) {
return item.indexOf(keyword) == 0;
}));
},
content: function (item) {
return item;
}
},
height: 200,
toolbar: [
['myotherbutton', ['testBtn']],
],
buttons: {
'testBtn': this.customButton
}
}
And this is my angular html
Here you can fiddle with a example I created a list that we will assume comes from a service I would like to pass this list to customButton
listStringFromDbService = ['one', 'two', 'three'];
https://stackblitz.com/edit/angular-summernote-demo-gdvvbn?file=src%2Fapp%2Fapp.component.ts
I believe I figured this out.
You can change your button declaration to a function that returns a button function. That way you can pass data to it before constructing the function that Evernote binds to testBtn.
Change the function declaration (expression or declaration will work, like you pointed out)
function customButtonGenerator(arr) {
return function (context) {
const ui = $.summernote.ui;
const button = ui.button({
contents: '<i class="note-icon-magic"></i> Hello',
tooltip: 'Custom button',
container: '.note-editor',
className: 'note-btn',
click: function () {
context.invoke('editor.insertText', 'Hello from test btn!!! ' + arr);
},
});
return button.render();
};
};
then when you create the ui config you can generate the button function instead:
buttons: {
testBtn: customButtonGenerator(this.listStringFromDbService),
},
Here's an updated stackblitz showing a working example.
I have modified the answer of MPawlak like this in it there is a dropdown where you can click the items you want to add. Also I have the passing of parameter to the custom button that still works. Thanks to MPawlak
https://stackblitz.com/edit/angular-summernote-demo-yq8t4t

Highlight selected jsGrid row

I found this example which highlights a row after it has been selected but the problem with it is that it keeps the previous row(s) highlighted after another one has been selected.
Here's part of the code
//js
rowClick: function(args) {
var $row = this.rowByItem(args.item);
$row.toggleClass("highlight");
},
//css
tr.highlight td.jsgrid-cell {
background-color: green;
}
I can't find a solution to unhighlight the previously selected row
A little late to the party on this one, however the accepted answer by #Narenda didn't completely solve my problem. This may help someone else that stumbles across this later.
If you need a single select only, here's a way of doing it:
Extend the jsGrid plugin with a method to find a row by index:
jsGrid.Grid.prototype.rowByIndex = function(arg){
//this._content.find("tr")[arg] returns a DOM element instead of a jQuery object
//Pass the DOM element to the find method to get a jQuery object representing it
return this._content.find(this._content.find("tr")[arg]);
};
Modify the rowClick function in #Narenda's answer:
rowClick: function ( args ) {
//Deselect all rows
for(var i = 0; i<this.data.length; i++){
this.rowByIndex(i).removeClass("jsgrid-highlight-row");
}
//Everything else as per the previous answer
var $row = this.rowByItem(args.item),
selectedRow = $("#jsGrid").find('table tr.jsgrid-highlight-row');
if (selectedRow.length) {
selectedRow.toggleClass('jsgrid-highlight-row');
};
$row.toggleClass("jsgrid-highlight-row");
//Any other code to run on item click
}
And add some CSS. This mimics the row hover in the default theme:
tr.jsgrid-highlight-row td.jsgrid-cell {
background:#c4e2ff;
border-color:#c4e2ff;
}
You can achieve by this following steps
First on row click you need to get selected row like this
var selectedRow = $("#jsGrid").find('table tr.highlight').
Then you can use
selectedRow.toggleClass('highlight') or selectedRow.removeClass('highlight')
DEMO
$("#jsGrid").jsGrid({
width: "100%",
height: "auto",
paging: false,
//for loadData method Need to set auto load true
autoload: true,
noDataContent: "Directory is empty",
controller: {
loadData: function(filter) {
var data = [{
nickname: "Test",
email: "t#gmail.com"
}, {
nickname: "Test 1",
email: "t1#gmail.com"
}, {
nickname: "Test 2",
email: "t2#gmail.com"
}, {
nickname: "Test 3",
email: "t3#gmail.com"
}];
return data;
}
},
rowClick: function(args) {
var $row = this.rowByItem(args.item),
selectedRow = $("#jsGrid").find('table tr.highlight');
if (selectedRow.length) {
selectedRow.toggleClass('highlight');
};
$row.toggleClass("highlight");
},
fields: [{
name: "nickname",
type: "text",
width: 80,
title: "Name"
}, {
name: "email",
type: "text",
width: 100,
title: "Email Address",
readOnly: false
}]
});
tr.highlight td.jsgrid-cell {
background-color: green;
}
<script src="//ajax.googleapis.com/ajax/libs/jquery/2.1.1/jquery.min.js"></script>
<link rel="stylesheet" href="//cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.css" />
<link rel="stylesheet" href="https://cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid-theme.min.css" />
<script type="text/javascript" src="//cdnjs.cloudflare.com/ajax/libs/jsgrid/1.5.3/jsgrid.min.js"></script>
<div id="jsGrid"></div>
If you came looking for a solution in which only 1 line is selected and which also deselects the same line, here is the solution:
selectedVal = null;
$(document).ready(function(){
jsGrid.Grid.prototype.rowByIndex = function(arg) {
//this._content.find("tr")[arg] returns a DOM element instead of a jQuery object
//Pass the DOM element to the find method to get a jQuery object representing it
return this._content.find(this._content.find("tr")[arg]);
};
});
rowClick: function (args) {
selectedVal = args.item;
let $row = this.rowByItem(args.item);
if ($row.hasClass("highlight") === false) {
//Deseleciona todas as linhas
for (let i = 0; i < this.data.length; i++) {
this.rowByIndex(i).removeClass("highlight");
}
$row.toggleClass("highlight");
} else {
selectedVal = null;
$row.toggleClass("highlight");
}
console.log(selectedVal);
}

Stateful selection in Ext JS

I have a regular tree with elements. What i want to do is following:
When I reload the page, the selected item must be the same as before ( I select only 1 item in the tree).
For example, when I click on 'Sue Potato' - it is selected and when I refresh the page, it must look the same (be also selected).
I've tried reading some Stateful, Provider, Manager on the Sencha Docs, but I didn't get it.
Controller code:
Ext.define('FirstApp.controller.Main', {
extend: 'Ext.app.Controller',
refs: [
{
ref: 'grid',
selector: 'lesson-grid'
},
{
ref: 'tree',
selector: 'school-tree'
}
],
init: function() {
Ext.state.Manager.setProvider(Ext.create('Ext.state.LocalStorageProvider'));
}
});
Tree code:
Ext.define('FirstApp.view.SchoolTree', {
extend: 'Ext.tree.Panel',
xtype: 'school-tree',
stateful: true,
stateId: 'stateGrid',
stateEvents:['selection'],
constructor: function() {
var that = this;
this.store = Ext.create('FirstApp.store.School');
this.store.on('load', function () {
that.getSelectionModel().select(1, true);
});
this.callParent(arguments);
this.getState = function() {
return that.getSelectionModel().getSelection();
};
this.applyState = function() {
};
}
});
Help would be much appreciated.
This is a working code of the requirement above.I had to get an id of the selected element and then pass it to applyState.
Ext.define('FirstApp.view.SchoolTree', {
extend: 'Ext.tree.Panel',
xtype: 'school-tree',
stateful: true,
stateId: 'stateTree',
stateEvents:['selectionchange'],
constructor: function() {
var that = this;
this.store = Ext.create('FirstApp.store.School');
this.store.on('load', function () {
that.getSelectionModel().select(1);
});
this.callParent(arguments);
},
getState: function() {
return {
'stateTree': this.getSelectionModel().getSelection()[0].getId()
};
},
applyState: function(state) {
var me = this;
this.store.on('load', function(record) {
record = this.getById(state.stateTree);
me.getSelectionModel().select(record);
});
}
});
You stored tree state and set it into State Manager
Ext.state.Manager.setProvider(Ext.create('Ext.state.LocalStorageProvider'));
So when you will refresh your page state will apply and an item will be also selected.
Is there any requirement to store state of tree? If yes and still you don't want to selected then you can forcefully clear selection on tree render.

WordPress Dynamic Tinymce listbox

what i am trying to do is dynamically generate WordPress tinymcs listbox values. but it seems my getValue function is not working well or its not possible to add getvalue() function to value parameter. this code is not working. please tell me how to do this. i need this for my new plugin development. sorry for bad english :(
here i have posted the code bellow
(function() {
tinymce.PluginManager.add('AP_tc_button', function( editor, url ) {
editor.addButton( 'AP_tc_button', {
text: 'My test button',
icon: 'wp_code',
onclick: function() {
editor.windowManager.open( {
title: 'Select Your AD',
body: [
{
type: 'listbox',
name: 'level',
label: 'Header level',
values: getValues()
}],
onsubmit: function( e ) {
editor.insertContent('dd');
}
});
}
});
});
})();
function getValues() {
//Set new values to myKeyValueList
tinyMCE.activeEditor.settings.myKeyValueList = [{text: 'newtext', value: 'newvalue'}];
return editor.settings.myKeyValueList;
}
Try changing return statement to
return tinyMCE.activeEditor.settings.myKeyValueList;
variable editor is probably undefined in global scope.

Add an "All" item to kendo ui listview populated by a remote datasource

I am building a website using MVC 4, Web API, and Kendo UI controls.
On my page I am using a Kendo UI Listview to filter my grid. I'm trying to add an "ALL" option as the first item in my listview.
Here is the listview:
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: filterDataSource,
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Here is my datasource:
var filterDataSource = new kendo.data.DataSource({
transport: {
read: {
url: "api/Filter"
}
},
schema: {
model: { id: "FilterId" }
}
});
I have tried a few different methods to make this happen:
I can make it work if I attach it to a button - but I need it there
when the data loads.
If I add it to the dataBound event of the listview, it causes the
databound event to go into a loop and adds the item a bunch (IE) or kills the browser (firefox). Adding preventDefault did nothing.
I've read up on adding a function to the Read paramter of the
datasource, but I think that is simply not the correct place to do
it.
Based on what I've read, I think that I should be able to do it in the dataBound event of the listview and that my implementation is incorrect. Here is the listview with dataBound event added that crashes my browser (Firefox) - or adds about 50 "All" items to the listview (IE).
var jobsfilter = $("#jobfilter").kendoListView({
selectable: "single",
loadOnDemand: false,
template: "<div class='pointercursor' id=${FilterId}>${FilterName}</div>",
dataSource: {
transport: {
read: {
url: "api/Filter"
}
}
},
dataBound: function (e) {
var dsource = $("#jobfilter").data("kendoListView").dataSource;
dsource.insert(0, { FilterId: 0, FilterName: "All" });
e.preventDefault();
},
change: function (e) {
var itm = this.select().index(), dataItem = this.dataSource.view()[itm];
if (dataItem.FilterId !== 0) {
var $filter = new Array();
$filter.push({ field: "JobStatusId", operator: "eq", value: dataItem.FilterId });
jgrid.dataSource.filter($filter);
} else {
jgrid.dataSource.filter({});
}
}
});
Any help would be greatly appreciated.
Why don't you add it server-side?
Anyway, if you want to do it in dataBound, just check whether it exists and only add if it doesn't:
dataBound: function (e) {
var dsource = this.dataSource;
if (dsource.at(0).FilterName !== "All") {
dsource.insert(0, {
FilterId: 0,
FilterName: "All"
});
}
},
As an explanation to the problem you're seeing: you're creating an infinite loop since inserting an element in the data source will trigger the change event and the list view will refresh and bind again (and thus trigger dataBound).
You could also encapsulate this in a custom widget:
(function ($, kendo) {
var ui = kendo.ui,
ListView = ui.ListView;
var CustomListView = ListView.extend({
init: function (element, options) {
// base call to widget initialization
ListView.fn.init.call(this, element, options);
this.dataSource.insert(0, {
FilterId: 0,
FilterName: "All"
});
},
options: {
name: "CustomListView"
}
});
ui.plugin(CustomListView);
})(window.jQuery, window.kendo);

Categories

Resources