I'm trying to add a new module in the editor to show image size and can resize it.
I found this module for the Quill editor but I am not able to add it in ngx-quill when it returns the editor in onEditorCreated ()
Component.js
onEditorCreated(quill) {
this.editor = quill;
this.editor.register('modules/ImageResize',ImageResize);
console.log('quill is ready! this is current quill instance object', quill);
}
`/**
* Custom module for quilljs to allow user to resize <img> elements
* (Works on Chrome, Edge, Safari and replaces Firefox's native resize behavior)
* @see https://quilljs.com/blog/building-a-custom-module/
*/
export class ImageResize {
constructor(quill, options = {}) {
// save the quill reference and options
this.quill = quill;
this.options = options;
// bind handlers to this instance
this.handleClick = this.handleClick.bind(this);
this.handleMousedown = this.handleMousedown.bind(this);
this.handleMouseup = this.handleMouseup.bind(this);
this.handleDrag = this.handleDrag.bind(this);
this.checkImage = this.checkImage.bind(this);
// track resize handles
this.boxes = [];
// disable native image resizing on firefox
document.execCommand('enableObjectResizing', false, 'false');
// respond to clicks inside the editor
this.quill.root.addEventListener('click', this.handleClick, false);
}
handleClick(evt) {
if (evt.target && evt.target.tagName && evt.target.tagName.toUpperCase() == 'IMG') {
if (this.img === evt.target) {
// we are already focused on this image
return;
}
if (this.img) {
// we were just focused on another image
this.hide();
}
// clicked on an image inside the editor
this.show(evt.target);
}
else if (this.img) {
// clicked on a non image
this.hide();
}
}
show(img) {
// keep track of this img element
this.img = img;
this.showResizers();
this.showSizeDisplay();
// position the resize handles at the corners
const rect = this.img.getBoundingClientRect();
this.positionBoxes(rect);
this.positionSizeDisplay(rect);
}
hide() {
this.hideResizers();
this.hideSizeDisplay();
this.img = undefined;
}
showResizers() {
// prevent spurious text selection
this.setUserSelect('none');
// add 4 resize handles
this.addBox('nwse-resize'); // top left
this.addBox('nesw-resize'); // top right
this.addBox('nwse-resize'); // bottom right
this.addBox('nesw-resize'); // bottom left
// listen for the image being deleted or moved
document.addEventListener('keyup', this.checkImage, true);
this.quill.root.addEventListener('input', this.checkImage, true);
}
hideResizers() {
// stop listening for image deletion or movement
document.removeEventListener('keyup', this.checkImage);
this.quill.root.removeEventListener('input', this.checkImage);
// reset user-select
this.setUserSelect('');
this.setCursor('');
// remove boxes
this.boxes.forEach(box => document.body.removeChild(box));
// release memory
this.dragBox = undefined;
this.dragStartX = undefined;
this.preDragWidth = undefined;
this.boxes = [];
}
addBox(cursor) {
// create div element for resize handle
const box = document.createElement('div');
// apply styles
const styles = {
position: 'absolute',
height: '12px',
width: '12px',
backgroundColor: 'white',
border: '1px solid #777',
boxSizing: 'border-box',
opacity: '0.80',
cursor: cursor,
};
this.extend(box.style, styles, this.options.handleStyles || {});
// listen for mousedown on each box
box.addEventListener('mousedown', this.handleMousedown, false);
// add drag handle to document
document.body.appendChild(box);
// keep track of drag handle
this.boxes.push(box);
}
extend(destination, ...sources) {
sources.forEach(source => {
for (let prop in source) {
if (source.hasOwnProperty(prop)) {
destination[prop] = source[prop];
}
}
});
return destination;
}
positionBoxes(rect) {
// set the top and left for each drag handle
[
{left: rect.left - 6, top: rect.top - 6}, // top left
{left: rect.left + rect.width - 6, top: rect.top - 6}, // top right
{left: rect.left + rect.width - 6, top: rect.top + rect.height - 6}, // bottom right
{left: rect.left - 6, top: rect.top + rect.height - 6}, // bottom left
].forEach((pos, idx) => {
this.extend(this.boxes[idx].style, {
top: Math.round(pos.top + window.pageYOffset) + 'px',
left: Math.round(pos.left + window.pageXOffset) + 'px',
});
});
}
handleMousedown(evt) {
// note which box
this.dragBox = evt.target;
// note starting mousedown position
this.dragStartX = evt.clientX;
// store the width before the drag
this.preDragWidth = this.img.width || this.img.naturalWidth;
// set the proper cursor everywhere
this.setCursor(this.dragBox.style.cursor);
// listen for movement and mouseup
document.addEventListener('mousemove', this.handleDrag, false);
document.addEventListener('mouseup', this.handleMouseup, false);
}
handleMouseup() {
// reset cursor everywhere
this.setCursor('');
// stop listening for movement and mouseup
document.removeEventListener('mousemove', this.handleDrag);
document.removeEventListener('mouseup', this.handleMouseup);
}
handleDrag(evt) {
if (!this.img) {
// image not set yet
return;
}
// update image size
if (this.dragBox == this.boxes[0] || this.dragBox == this.boxes[3]) {
// left-side resize handler; draging right shrinks image
this.img.width = Math.round(this.preDragWidth - evt.clientX - this.dragStartX);
}
else {
// right-side resize handler; draging right enlarges image
this.img.width = Math.round(this.preDragWidth + evt.clientX - this.dragStartX);
}
// reposition the drag handles around the image
const rect = this.img.getBoundingClientRect();
this.positionBoxes(rect);
this.positionSizeDisplay(rect);
}
setUserSelect(value) {
[
'userSelect',
'mozUserSelect',
'webkitUserSelect',
'msUserSelect'
].forEach(prop => {
// set on contenteditable element and <html>
this.quill.root.style[prop] = value;
document.documentElement.style[prop] = value;
});
}
setCursor(value) {
[
document.body,
this.img,
this.quill.root
].forEach(el => el.style.cursor = value);
}
checkImage() {
if (this.img) {
this.hide();
}
}
showSizeDisplay() {
if (!this.options.displaySize) {
return;
}
this.display = document.createElement('div');
// apply styles
const styles = {
position: 'absolute',
font: '12px/1.0 Arial, Helvetica, sans-serif',
padding: '4px 8px',
textAlign: 'center',
backgroundColor: 'white',
color: '#333',
border: '1px solid #777',
boxSizing: 'border-box',
opacity: '0.80',
cursor: 'default',
};
this.extend(this.display.style, styles, this.options.displayStyles || {});
document.body.appendChild(this.display);
}
hideSizeDisplay() {
document.body.removeChild(this.display);
this.display = undefined;
}
positionSizeDisplay(rect) {
if (!this.display || !this.img) {
return;
}
const size = this.getCurrentSize();
this.display.innerHTML = size.join(' × ');
if (size[0] > 120 && size[1] > 30) {
// position on top of image
const dispRect = this.display.getBoundingClientRect();
this.extend(this.display.style, {
left: Math.round(rect.left + rect.width + window.pageXOffset - dispRect.width - 8) + 'px',
top: Math.round(rect.top + rect.height + window.pageYOffset - dispRect.height - 8) + 'px',
});
}
else {
// position off bottom right
this.extend(this.display.style, {
left: Math.round(rect.left + rect.width + window.pageXOffset + 8) + 'px',
top: Math.round(rect.top + rect.height + window.pageYOffset + 8) + 'px',
});
}
}
getCurrentSize() {
return [
this.img.width,
Math.round(this.img.width / this.img.naturalWidth * this.img.naturalHeight),
];
}
}
`
Someone know how to ?
Could you tell us, what is not working?
But if you follow this guide:
https://quilljs.com/blog/building-a-custom-module/
you see that you need to register the custom module to the global Quill and not to the instance.
Like:
import * as Quill from 'quill';
import ImageResize from 'xxx';
// register globally for QuillJS
Quill.registerModule('imageResize', ImageResize);
....
@Component({ ... })
class YourComponent {
imageResizeInstance: ImageResize;
...
onEditorCreated(quill) {
// add custom module to quill editor instance --> it should be working if you pass 'imageResize' to the ngx-quill modules property binding as well ;)
this.imageResizeInstance = quill.addModule('imageResize');
...
}
}
i try this
import * as Quill from "quill";
import {ImageResize} from "../cabir/cabir-editor/ImageResize";
Quill.registerModule('imageResize', ImageResize);
quill.registerModule is not a function
quill.addModule is not a function
in index.d.ts of quilljs, has interface of Quill, dont see registerModule or addModule
export interface Quill {
new (container: string | Element, options?: QuillOptionsStatic): Quill;
deleteText(index: number, length: number, source?: Sources): void;
disable(): void;
enable(enabled?: boolean): void;
getContents(index?: number, length?: number): DeltaStatic;
getLength(): number;
getText(index?: number, length?: number): string;
insertEmbed(index: number, type: string, value: any, source?: Sources): void;
insertText(index: number, text: string, source?: Sources): DeltaStatic;
insertText(index: number, text: string, format: string, value: any, source?: Sources): DeltaStatic;
insertText(index: number, text: string, formats: Formats, source?: Sources): DeltaStatic;
/**
* @deprecated Use clipboard.dangerouslyPasteHTML(index: number, html: string, source: Sources)
*/
pasteHTML(index: number, html: string, source?: Sources): string;
/**
* @deprecated Use dangerouslyPasteHTML(html: string, source: Sources): void;
*/
pasteHTML(html: string, source?: Sources): string;
setContents(delta: DeltaStatic, source?: Sources): DeltaStatic;
setText(text: string, source?: Sources): DeltaStatic;
update(source?: string): void;
updateContents(delta: DeltaStatic, source?: Sources): DeltaStatic;
format(name: string, value: any, source?: Sources): DeltaStatic;
formatLine(index: number, length: number, source?: Sources): DeltaStatic;
formatLine(index: number, length: number, format: string, value: any, source?: Sources): DeltaStatic;
formatLine(index: number, length: number, formats: Formats, source?: Sources): DeltaStatic;
formatText(index: number, length: number, source?: Sources): DeltaStatic;
formatText(index: number, length: number, format: string, value: any, source?: Sources): DeltaStatic;
formatText(index: number, length: number, formats: Formats, source?: Sources): DeltaStatic;
getFormat(range?: RangeStatic): Formats;
getFormat(index: number, length?: number): Formats;
removeFormat(index: number, length: number, source?: Sources): void;
blur(): void;
focus(): void;
getBounds(index: number, length?: number): BoundsStatic;
getSelection(focus?: boolean): RangeStatic;
hasFocus(): boolean;
setSelection(index: number, length: number, source?: Sources): void;
setSelection(range: RangeStatic, source?: Sources): void;
on(eventName: string, callback: (<T>(delta: T, oldContents: T, source: string) => void) |
((name: string, ...args: any[]) => void)): Quill;
once(eventName: string, callback: (delta: DeltaStatic, source: string) => void): Quill;
off(eventName: string, callback: (delta: DeltaStatic, source: string) => void): Quill;
debug(level: string): void;
import(path: string): any;
register(path: string, def: any, suppressWarning?: boolean): void;
register(defs: Formats, suppressWarning?: boolean): void;
addContainer(className: string, refNode?: any): any;
addContainer(domNode: any, refNode?: any): any;
getModule(name: string): any
clipboard: ClipboardStatic;
}
okay then follow this:
https://quilljs.com/docs/modules/#extending
use the register function and thats it. Hope this helps
https://quilljs.com/docs/api/#register
EDIT: btw. i think you will get some typescript errors during compilation, because this class is not that clean ;)
Congratulation.
with this Guide for ES6
https://quilljs.com/guides/building-a-custom-module/
import * as Quill from "quill";
import {ImageResize} from "../cabir/cabir-editor/ImageResize";
Quill.register('modules/imageResize', ImageResize);
public options: any;
this.options = {
toolbar: [
['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'], // remove formatting button
['link', 'image', 'video'] // link and image, video
],
imageResize: true
}
html
<quill-editor [placeholder]="'Digite aqui todo o conteudo do post!'"
[modules]="options"
(onContentChanged)="onContentChanged($event)"
(onEditorCreated)="onEditorCreated($event)"
(onSelectionChanged)="onSelectionChanged($event)"
[(ngModel)]="editorContent">
</quill-editor>
its work
but thanks for letting me know. i will add the "counter" example as typescript to ngx-examples.
for your interest:
checkout the last editor with word counter :)
https://killercodemonkey.github.io/ngx-quill-example/
People who are having difficulty using the quill image resize module with ANGULAR-CLI e ANGULAR 2+
Here's a way to not have to tinker with the webpack.config.ts file
terminal
npm install quill --save
npm install quill-image-resize-module --save
angular-cli.json
"scripts": [
...,
"../node_modules/quill/dist/quill.min.js"
]
Componente.ts
import * as QuillNamespace from 'quill';
let Quill: any = QuillNamespace;
import ImageResize from 'quill-image-resize-module';
Quill.register('modules/imageResize', ImageResize);
this.editor_modules = {
toolbar: {
container: [
[{ 'font': [] }],
[{ 'size': ['small', false, 'large', 'huge'] }],
['bold', 'italic', 'underline', 'strike'],
[{ 'header': 1 }, { 'header': 2 }],
[{ 'color': [] }, { 'background': [] }],
[{ 'list': 'ordered' }, { 'list': 'bullet' }],
[{ 'align': [] }],
['link', 'image']
]
},
imageResize: true
};
Componente.html
<quill-editor [modules]="editor_modules" [(ngModel)]="content"></quill-editor>
@viniciusaugutis
Do you have a .d.ts file about quill-image-resize-module ? or did you made it?
@KillerCodeMonkey works like expected, thx
app.module.ts
QuillModule.forRoot({
customModules: [
{
implementation: PlainClipboard,
path: 'modules/clipboard'
}
],
})
plain-clipboard.ts
import Quill from 'quill';
const Delta = Quill.import('delta');
const Clipboard = Quill.import('modules/clipboard');
export default class PlainClipboard extends Clipboard {
onPaste (e) {
e.preventDefault();
const range = this.quill.getSelection();
const text = e.clipboardData.getData('text/plain');
const delta = new Delta()
.retain(range.index)
.delete(range.length)
.insert(text);
const index = text.length + range.index;
const length = 0;
this.quill.updateContents(delta, 'silent');
this.quill.setSelection(index, length, 'silent');
this.quill.scrollIntoView();
}
}
Most helpful comment
People who are having difficulty using the quill image resize module with ANGULAR-CLI e ANGULAR 2+
Here's a way to not have to tinker with the webpack.config.ts file
terminal
angular-cli.json
Componente.ts
Componente.html
<quill-editor [modules]="editor_modules" [(ngModel)]="content"></quill-editor>