Creating a custom class attributer in QuillJS - quill

I'm trying to create a custom class attributer in QuillJS.
I've got this far:
let config = {
scope: Parchment.Scope.BLOCK,
};
let MClass = new Parchment.Attributor.Class('mark', 'dom-mark', config);
Quill.register(MClass,true)
But when attempting:
this.quillEditor.format('mark', 'MarkThisHere');
I get:
ERROR TypeError: BlotClass.create is not a function
What am i doing wrong?

Works for me in this example.
Parchment = Quill.import('parchment');
let config = {
scope: Parchment.Scope.BLOCK,
};
let MClass = new Parchment.Attributor.Class('mark', 'dom-mark', config);
Quill.register(MClass,true)
var quill = new Quill('#editor-container', {
modules: {
toolbar: [
[{ header: [1, 2, false] }],
['bold', 'italic', 'underline'],
['image', 'code-block']
]
},
placeholder: 'Compose an epic...',
theme: 'snow' // or 'bubble'
});
quill.format('mark', 'MarkThisHere');

Related

React + ChartJS V3: Annoations don't work

I'm using react-chartjs-2 v4.1 with ChartJS v3.8 in typescript.
I'd like to draw a horizontal line through my bar graph as shown below:
I find many half-written examples of which I cannot create a functional one. I couldn't find any complete, working example on how to use annotations.
My Code
I've added the chartjs-plugin-annotation package to my project.
Below is the code for a react component showing the graph of the screenshot. The annotation, however, does not work.
Can anyone tell me what's wrong with the code?
import React from 'react';
import { Bar } from 'react-chartjs-2';
export const MyChart: React.FC = () => {
const options2 = {
plugins: {
legend: {
display: false,
},
annotation: {
annotations: [
{
id: 'a-line-1',
type: 'line',
mode: 'horizontal',
scaleID: 'y',
value: 1.0,
borderColor: 'red',
borderWidth: 4,
label: {
enabled: false,
content: 'Test label',
},
},
],
},
},
};
const data2 = {
labels: [ 'a', 'b'],
datasets: [ { data: [1, 2] } ],
};
return (<Bar options={options2} data={data2} height={150} />
);
};
You dont import and register the annotation plugin:
import { Chart } from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
Chart.register(annotationPlugin);
Based on LeeLenalee's answer here's a fully working example.
Changes to code in question:
import and register annotationPlugin
set annotation type to type: 'line' as const (not just type: 'line'). Otherwise typescript complains.
import React from 'react';
import { Bar } from 'react-chartjs-2';
import { Chart } from 'chart.js';
import annotationPlugin from 'chartjs-plugin-annotation';
Chart.register(annotationPlugin);
export const MyChart: React.FC = () => {
const options2 = {
plugins: {
legend: {
display: false,
},
annotation: {
annotations: [
{
id: 'a-line-1',
type: 'line' as const, // important, otherwise typescript complains
mode: 'horizontal',
scaleID: 'y',
value: 1.0,
borderColor: 'red',
borderWidth: 4,
label: {
enabled: false,
content: 'Test label',
},
},
],
},
},
};
const data2 = {
labels: [ 'a', 'b'],
datasets: [ { data: [1, 2] } ],
};
return (<Bar options={options2} data={data2} height={150} />
);
};

add a plugin from a library in tailwind

I am using react, nx, tailwind to create a monorepo with multiple config
What I would like to achieve is
one config for the whole repo
one config per project
one plugin that extend the project config on each lib.
I currently have this :
root::tailwind.config (base styles)
module.exports = {
content: [],
theme: {
extend: {
colors: {
'27313B': '#27313B',
},
},
},
plugins: [],
};
project::tailwdind.config (extends the root and call for a plugin)
const { createGlobPatternsForDependencies } = require('#nrwl/react/tailwind');
const { join } = require('path');
module.exports = {
content: [
join(__dirname, 'src/**/!(*.stories|*.spec).{ts,tsx,html}'),
...createGlobPatternsForDependencies(__dirname),
],
presets: [require(join(__dirname, '../../tailwind.config'))],
theme: {
extend: {},
},
plugins: [require(join(__dirname, '../../libs//tailwind.plugin'))],
};
lib::tailwind.plugin
const plugin = require('tailwindcss/plugin');
module.exports = {
plugins: [
plugin(
function ({ matchUtilities, theme }) {
matchUtilities(
{
colors: (value) => ({
colors: value,
}),
},
{ values: theme('colors') }
);
},
{
theme: {
extend: {
colors: {
F00: '#f00',
},
},
},
}
),
],
};
Sadly, the plugin never work,
doing
<div className={'text-27313B'}>asdasd</div>
show the good color but
<div className={'text-F00'}>asdasd</div>
dont
I am configuring it wrong ?
you try this
module.exports = {
content: [],
theme: {
extend: {
colors: {
27313B: '#27313B',
},
},
},
plugins: [],
};
ok I copy pasted wrong...
module.exports = plugin(
function ({ matchUtilities, theme }) {
matchUtilities(
{
colors: (value) => ({
colors: value,
}),
},
{ values: theme('colors') }
);
},
{
theme: {
extend: {
colors: {
F00: '#f00',
},
},
},
}
);
works.
I was exporting a plugin : plugin() instead of just plugin()

How to add "strinsert" custom dropdown plugin to ckeditor4-react?

I am working with ckeditor4-react plugin to use Text editor inside react app. Now I want to add a string dropdown inside my text editor, for this I have followed the "add custom plugin" documentation and added "strinsert" custom plugin.
Inside "node-modules/ckeditor4-react/" folder I have created a folder with name "plugins" and placed "strinsert" folder inside this.
Now my path of custom plugins is "node-modules/ckeditor4-react/plugins/strinsert/plugin.js"
Code of "plugin.js":
CKEDITOR.plugins.add('strinsert',
{
requires : ['richcombo'],
init : function( editor )
{
// array of strings to choose from that'll be inserted into the editor
var strings = [];
strings.push(['##FAQ::displayList()##', 'FAQs', 'FAQs']);
strings.push(['##Glossary::displayList()##', 'Glossary', 'Glossary']);
strings.push(['##CareerCourse::displayList()##', 'Career Courses', 'Career Courses']);
strings.push(['##CareerProfile::displayList()##', 'Career Profiles', 'Career Profiles']);
// add the menu to the editor
editor.ui.addRichCombo('strinsert',
{
label: 'Insert Content',
title: 'Insert Content',
voiceLabel: 'Insert Content',
className: 'cke_format',
multiSelect:false,
panel:
{
css: [ editor.config.contentsCss, CKEDITOR.skin.getPath('editor') ],
voiceLabel: editor.lang.panelVoiceLabel
},
init: function()
{
this.startGroup( "Insert Content" );
for (var i in strings)
{
this.add(strings[i][0], strings[i][1], strings[i][2]);
}
},
onClick: function( value )
{
editor.focus();
editor.fire( 'saveSnapshot' );
editor.insertHtml(value);
editor.fire( 'saveSnapshot' );
}
});
}
});
After adding this I have used this plugin inside Text editor by using "extraPlugins" config prop. This is a code of my TextEditor plugin file(which is inside the "src" folder of )
class TextEditor extends React.Component {
constructor(props) {
super(props);
this.state = {
editorData: this.props.data
}
}
/** lifecycle method */
componentDidMount() {
this.isMount = true;
this.setState({editorData: this.props.data})
}
componentWillUnmount() {
this.isMount = false;
}
/** function to detect the editor changes */
onEditorChange(event) {
let data = event.editor.getData()
this.props.onChange(data)
}
// main function
render() {
const { editorData } = this.state;
return (
<CKEditor
data={editorData}
onChange={(e) => this.onEditorChange(e)}
config={{
toolbar: [
{ name: 'basicstyles', items: ['Bold', 'Italic', 'Underline', 'Strike'] },
{ name: 'editing', items: ['SelectAll'] },
{ name: 'clipboard', items: ['Undo', 'Redo'] },
{ name: 'links', items: ['Link', 'Unlink', 'Anchor'] },
{ name: 'insert', items: [ 'Image', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar' ] },
{ name: 'document', items: [ 'Templates', 'Preview', '-', 'Source'] },
{ name: 'paragraph', items: ['NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', 'Blockquote', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl', 'Language'] },
{ name: 'styles', items: [ 'Styles', 'Format', 'Font', 'FontSize' ] },
{ name: 'colors', items: [ 'TextColor', 'BGColor' ] },
{ name: 'tools', items: [ 'Maximize', 'ShowBlocks' ] },
],
removePlugins: ['language'],
extraPlugins: "strinsert",
}}
/>
);
}
}
export { TextEditor };
After adding this when I am opening the text editor it shows me an error inside console::
Error::
ckeditor.js:98 GET https://cdn.ckeditor.com/4.15.1/standard-all/plugins/strinsert/plugin.js?t=KA9B
ckeditor.js:258 Uncaught Error: [CKEDITOR.resourceManager.load] Resource name "strinsert" was not found at "https://cdn.ckeditor.com/4.15.1/standard-all/plugins/strinsert/plugin.js?t=KA9B".
at window.CKEDITOR.window.CKEDITOR.dom.CKEDITOR.resourceManager.<anonymous> (ckeditor.js:258)
at n (ckeditor.js:253)
at Array.v (ckeditor.js:254)
at y (ckeditor.js:254)
at HTMLScriptElement.CKEDITOR.env.ie.g.$.onerror (ckeditor.js:255)
Please suggest how can I add "strinsert" custom plugin inside ckeditor4-react text editor.

React Quill custom image handler not working

Can someone help me to find out what is the issue in the code. I have created a custom image upload option but for some reason the variable "quillReact" is coming null when quillImageCallback function is invoked. I am using react-hooks. The image is uploaded properly when using API and proper response is also returned from the backend.
let quillReact: ReactQuill | null = null;
const updateIssueInfo = (value: string, delta: any, source: any, editor: any) => {
setIssueManagementInEdit({
...issueManagementInEdit,
description: value
});
};
const quillImageCallback = () => {
console.log(issueManagement);
const input = document.createElement("input");
input.setAttribute("type","file");
input.setAttribute("accept", "image/*");
input.click();
input.onchange = async () => {
const file: File | null = input.files ? input.files[0] : null;
if(file){
uploadImage(file).then(async (fileName: any) => {
const newFileName:string = await fileName.text();
console.log(quillReact);
let quill: any | null = quillReact?.getEditor();
console.log(quill);
const range : any | null = quill?.getSelection(true);
quill?.insertEmbed(range.index, 'image', `http://localhost:8080/uploads/${newFileName}`);
});
}
}
};
const module = React.useMemo(() => { return {
toolbar: {
container: [
['bold', 'italic', 'underline', 'strike'], // toggled buttons
['blockquote', 'code-block'],
[{ 'header': 1 }, { 'header': 2 }], // custom button values
[{ 'list': 'ordered'}, { 'list': 'bullet' }],
[{ 'script': 'sub'}, { 'script': 'super' }], // superscript/subscript
[{ 'indent': '-1'}, { 'indent': '+1' }], // outdent/indent
[{ 'direction': 'rtl' }], // text direction
[{ 'size': ['small', false, 'large', 'huge'] }], // custom dropdown
[{ 'header': [1, 2, 3, 4, 5, 6, false] }],
[{ 'color': [] }, { 'background': [] }], // dropdown with defaults from theme
[{ 'font': [] }],
[{ 'align': [] }],
['clean', 'image'] // remove formatting button
],
handlers: {
image: quillImageCallback
}
},
clipboard: {
// toggle to add extra line breaks when pasting HTML:
matchVisual: false,
}
}},[]);
<ReactQuill
value={issueManagementInEdit.description ? issueManagementInEdit.description : ""}
onChange={updateIssueInfo}
modules={module}
ref={(el: ReactQuill) => {
quillReact = el;
} }
style={{height: "250px"}}
id="description"
key="description"
/>
Thank You.
I suggest you try useRef:
const quillRef = React.useRef(null);
<ReactQuill ... ref={quillRef} />
And then access the editor in your callback:
const quill = quillRef.current.getEditor();

QuillJS - Anyone can supply customized code for full toolbar with headers applied only for selections and not the entire text? Coldfusion and QuillJS

Need a working QUILLJS code with has full toolbar and a working Header icons in the toolbar to be applied only to the selected text and not to the entire text.
I am trying to avoid lot of recoding moving from a paid CKEditor into free QUILLJS.
HTML:
<div id="editor-container" style="height: 350px;">#variables.valTextSettings[url.msg]#</div>
<textarea id="htmlMessage" name="htmlMessage" style="display:none;"></textarea>
JS:
the html is handled via a hidden variable
$(document).ready(function () {
var quillcontainter = '#editor-container';
var hiddenformfield = '#htmlMessage';
var quill = new Quill(quillcontainter, {
modules: {
toolbar: [
['bold', 'italic', 'underline', 'strike'],
[{ 'font': [] }],[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }, { 'indent': '-1' }, { 'indent': '+1' }],
[{ 'direction': 'rtl' }],
[{ 'header': 1 }, { 'header': 2 }],
['blockquote', 'code-block'],
[{ 'script': 'sub'}, { 'script': 'super' }],
[{ 'align': [] }],
['link'],
['clean'],
['showHtml']
]
},
placeholder: 'Transaction Notes..',
theme: 'snow' // or 'bubble'
});
/* Load Default values */
$(hiddenformfield).html(quill.root.innerHTML);
// This will produce a warning message in the console as we are attaching handlers separately, but we can ignore
var txtArea = document.createElement('textarea');
txtArea.style.cssText = "width: 100%;margin: 0px;background: rgb(29, 29, 29);box-sizing: border-box;color: rgb(204, 204, 204);font-size: 15px;outline: none;padding: 20px;line-height: 24px;font-family: Arial, Helvetica, sans-serif;position: absolute;top: 0;bottom: 0;border: none;display:none";
var htmlEditor = quill.addContainer('ql-custom'); htmlEditor.appendChild(txtArea);
var myEditor = document.querySelector(quillcontainter);
quill.on('text-change', function (delta, old, source) {
txtArea.value = quill.root.innerHTML;
$(hiddenformfield).html(quill.root.innerHTML);
});
var customButton = document.querySelector('.ql-showHtml');
customButton.addEventListener('click', function() {
if (txtArea.style.display === '') { var html = txtArea.value; quill.pasteHTML(html); }
// No text change but clicking the Source button
else { var html = quill.root.innerHTML; quill.pasteHTML(html); }
txtArea.style.display = txtArea.style.display === 'none' ? '' : 'none'
});
});

Resources