Skip to content

Commit

Permalink
feat: quill 2 final
Browse files Browse the repository at this point in the history
  • Loading branch information
KillerCodeMonkey committed May 16, 2024
1 parent d9cfa35 commit 14f9df8
Show file tree
Hide file tree
Showing 11 changed files with 503 additions and 541 deletions.
878 changes: 424 additions & 454 deletions package-lock.json

Large diffs are not rendered by default.

12 changes: 6 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "stencil-quill",
"version": "10.0.0",
"version": "11.0.0",
"description": "Native web component for quill editor",
"main": "dist/index.cjs.js",
"module": "dist/index.js",
Expand Down Expand Up @@ -37,19 +37,19 @@
"native"
],
"peerDependencies": {
"quill": "2.0.0-rc.0"
"quill": "^2.0.0"
},
"devDependencies": {
"@stencil/core": "^4.12.1",
"@stencil/core": "^4.18.1",
"@types/jest": "^29.5.12",
"canvas": "^2.11.2",
"gh-pages": "^6.1.1",
"jest": "^29.7.0",
"jest-cli": "^29.7.0",
"jsdom": "^24.0.0",
"prettier": "^3.2.5",
"puppeteer": "^22.0.0",
"quill": "2.0.0-rc.0"
"puppeteer": "^22.9.0",
"quill": "^2.0.2"
},
"repository": {
"type": "git",
Expand All @@ -62,6 +62,6 @@
},
"homepage": "https://github.com/KillerCodeMonkey/stencil-quill",
"volta": {
"node": "18.19.0"
"node": "20.13.1"
}
}
4 changes: 2 additions & 2 deletions readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@ PayPal: [PayPal.Me/bengtler](https://paypal.me/bengtler)

- customToolbarPosition - if you are working with a custom toolbar you can switch the position :). - default: `top`, possible values `top`, `bottom`
- debug - set log level `warn`, `error`, `log` or `false` to deactivate logging, default: `warn`
- preserveWhitespace - default: false - possbility to use a pre-tag instead of a div-tag for the contenteditable area to preserve duplicated whitespaces | caution if used with syntax plugin [Related issue](https://github.com/quilljs/quill/issues/1751)
- defaultEmptValue - set the default empty value, e.g. empty string, default `null`

[Full Quill Toolbar HTML](https://github.com/quilljs/quill/blob/f75ff2973f068c3db44f949915eb8a74faf162a8/docs/_includes/full-toolbar.html)

Expand Down Expand Up @@ -204,7 +204,7 @@ It renders a readOnly quilljs editor without a border and toolbar. Does not prov
- theme - bubble/snow, default is `snow`
- styles - set a styles object, e.g. `styles="{height: '250px'}"`
- debug - set log level `warn`, `error`, `log` or `false` to deactivate logging, default: `warn`
- preserveWhitespace - default: false - possbility to use a pre-tag instead of a div-tag for the contenteditable area to preserve duplicated whitespaces | caution if used with syntax plugin [Related issue](https://github.com/quilljs/quill/issues/1751)
- defaultEmptValue - set the default empty value, e.g. empty string, default `null`

## QuillViewHTML component

Expand Down
8 changes: 4 additions & 4 deletions src/components.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,22 +11,22 @@ export namespace Components {
"content": string;
"customToolbarPosition": 'top' | 'bottom';
"debug": string;
"defaultEmptyValue": any;
"format": 'html' | 'text' | 'json';
"formats": string[];
"modules"?: string;
"placeholder": string;
"preserveWhitespace": boolean;
"readOnly": boolean;
"styles": string;
"theme": string;
}
interface QuillView {
"content": string;
"debug": string;
"defaultEmptyValue": any;
"format": 'html' | 'text' | 'json';
"formats": string[];
"modules"?: string;
"preserveWhitespace": boolean;
"strict": boolean;
"styles": string;
"theme": string;
Expand Down Expand Up @@ -122,6 +122,7 @@ declare namespace LocalJSX {
"content"?: string;
"customToolbarPosition"?: 'top' | 'bottom';
"debug"?: string;
"defaultEmptyValue"?: any;
"format"?: 'html' | 'text' | 'json';
"formats"?: string[];
"modules"?: string;
Expand Down Expand Up @@ -167,18 +168,17 @@ declare namespace LocalJSX {
source: string;
}>) => void;
"placeholder"?: string;
"preserveWhitespace"?: boolean;
"readOnly"?: boolean;
"styles"?: string;
"theme"?: string;
}
interface QuillView {
"content"?: string;
"debug"?: string;
"defaultEmptyValue"?: any;
"format"?: 'html' | 'text' | 'json';
"formats"?: string[];
"modules"?: string;
"preserveWhitespace"?: boolean;
"strict"?: boolean;
"styles"?: string;
"theme"?: string;
Expand Down
35 changes: 11 additions & 24 deletions src/components/quill-editor/quill-editor.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,9 @@ class Quill {
};
enable() {}
setContents() {}
getSemanticHTML() {
return ''
}
setText() {}
getContents() {}
getText() {}
Expand Down Expand Up @@ -70,7 +73,7 @@ describe('QuillEditorComponent', () => {
expect(component.format).toEqual('html');
expect(component.debug).toEqual('warn');
expect(component.styles).toEqual('{"height": "200px"}');
expect(component.preserveWhitespace).toBe(false);
expect(component.defaultEmptyValue).toBe(null);
expect(component.modules).toEqual('{"toolbar":true}');

expect(component.quillEditor.options.modules).toEqual({ toolbar: true });
Expand Down Expand Up @@ -310,17 +313,18 @@ describe('QuillEditorComponent', () => {
describe('#getEditorContent', () => {
describe('format: html', () => {
it('p with br or div with br returns empty string', () => {
component.quillEditor.editorElement.children[0].innerHTML = '<div><br/></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><br></div>');

component.format = 'html';

expect(component.getEditorContent()).toEqual('');
expect(component.getEditorContent()).toEqual(null);
});

it('returns html', () => {
component.quillEditor.editorElement.children[0].innerHTML = '<div><p>asdf</p><br/></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><p>asdf</p><br/></div>');
component.format = 'html';

expect(component.getEditorContent()).toEqual('<div><p>asdf</p><br></div>');
expect(component.getEditorContent()).toEqual('<div><p>asdf</p><br/></div>');
});
});

Expand Down Expand Up @@ -413,23 +417,6 @@ describe('QuillEditorComponent', () => {
});
});

describe('preserve whitespaces', () => {
it('renders pre tag instead of div', async () => {
component.preserveWhitespace = true;

component.componentDidLoad();

expect(page.root).toEqualHtml(`
<quill-editor content="<p>Test</p>" modules="{&quot;toolbar&quot;:true}" styles="{&quot;height&quot;: &quot;200px&quot;}">
<div quill-editor="" style="height: 200px;">
<div></div>
</div>
<pre quill-editor="" style="height: 200px;"><div></div></pre>
</div>
`);
});
});

describe('custom toolbar', () => {
it('renders toolbar', async () => {
page = await newSpecPage({
Expand Down Expand Up @@ -656,7 +643,7 @@ describe('QuillEditorComponent', () => {
const emitSpy = jest.spyOn(component.editorChange, 'emit');
jest.spyOn(component.quillEditor, 'getText').mockReturnValue('test');
jest.spyOn(component.quillEditor, 'getContents').mockReturnValue([{ insert: 'test' }]);
component.editorElement.children[0].innerHTML = '<div><br></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><br></div>');

component.quillEditor.callbacks[0]('text-change', null, null, 'api');

Expand Down Expand Up @@ -725,7 +712,7 @@ describe('QuillEditorComponent', () => {
const emitSpy = jest.spyOn(component.editorContentChange, 'emit');
jest.spyOn(component.quillEditor, 'getText').mockReturnValue('test');
jest.spyOn(component.quillEditor, 'getContents').mockReturnValue([{ insert: 'test' }]);
component.editorElement.children[0].innerHTML = '<div><br></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><br></div>');

component.quillEditor.callbacks[2](null, null, 'api');

Expand Down
22 changes: 13 additions & 9 deletions src/components/quill-editor/quill-editor.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -65,7 +65,7 @@ export class QuillEditorComponent implements ComponentDidLoad {
@Prop() styles: string = '{}';
@Prop() theme: string = 'snow';
@Prop() customToolbarPosition: 'top' | 'bottom' = 'top';
@Prop() preserveWhitespace: boolean = false;
@Prop() defaultEmptyValue: any = null

quillEditor: any;
editorElement: HTMLDivElement | HTMLPreElement;
Expand Down Expand Up @@ -118,9 +118,9 @@ export class QuillEditorComponent implements ComponentDidLoad {
const text = this.quillEditor.getText();
const content = this.quillEditor.getContents();

let html: string | null = this.editorElement.children[0].innerHTML;
if (html === '<p><br></p>' || html === '<div><br></div>') {
html = '';
let html: string | null = this.quillEditor.getSemanticHTML();
if (this.isEmptyValue(html)) {
html = this.defaultEmptyValue;
}

if (this.format === 'html') {
Expand All @@ -139,7 +139,7 @@ export class QuillEditorComponent implements ComponentDidLoad {
}

componentDidLoad() {
this.editorElement = this.preserveWhitespace ? document.createElement('pre') : document.createElement('div');
this.editorElement = document.createElement('div');
this.editorElement.setAttribute('quill-editor', '');

let modules: any = this.modules ? JSON.parse(this.modules) : this.defaultModules;
Expand Down Expand Up @@ -188,8 +188,8 @@ export class QuillEditorComponent implements ComponentDidLoad {
const text = this.quillEditor.getText();
const content = this.quillEditor.getContents();

let html: string | null = this.editorElement.querySelector('.ql-editor')!.innerHTML;
if (html === '<p><br></p>' || html === '<div><br></div>') {
let html: string | null = this.quillEditor.getSemanticHTML();
if (this.isEmptyValue(html)) {
html = null;
}

Expand Down Expand Up @@ -240,8 +240,8 @@ export class QuillEditorComponent implements ComponentDidLoad {
const text = this.quillEditor.getText();
const content = this.quillEditor.getContents();

let html: string | null = this.editorElement.querySelector('.ql-editor').innerHTML;
if (html === '<p><br></p>' || html === '<div><br></div>') {
let html: string | null = this.quillEditor.getSemanticHTML();
if (this.isEmptyValue(html)) {
html = null;
}

Expand Down Expand Up @@ -344,4 +344,8 @@ export class QuillEditorComponent implements ComponentDidLoad {
<slot name="quill-toolbar" quill-toolbar="" />
</Host>;
}

private isEmptyValue(html: string | null) {
return html === '<p></p>' || html === '<div></div>' || html === '<p><br></p>' || html === '<div><br></div>'
}
}
2 changes: 1 addition & 1 deletion src/components/quill-editor/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,11 @@
| `content` | `content` | | `string` | `undefined` |
| `customToolbarPosition` | `custom-toolbar-position` | | `"bottom" \| "top"` | `'top'` |
| `debug` | `debug` | | `string` | `'warn'` |
| `defaultEmptyValue` | `default-empty-value` | | `any` | `null` |
| `format` | `format` | | `"html" \| "json" \| "text"` | `'html'` |
| `formats` | -- | | `string[]` | `undefined` |
| `modules` | `modules` | | `string` | `undefined` |
| `placeholder` | `placeholder` | | `string` | `'Insert text here ...'` |
| `preserveWhitespace` | `preserve-whitespace` | | `boolean` | `false` |
| `readOnly` | `read-only` | | `boolean` | `undefined` |
| `styles` | `styles` | | `string` | `'{}'` |
| `theme` | `theme` | | `string` | `'snow'` |
Expand Down
23 changes: 6 additions & 17 deletions src/components/quill-view/quill-view.spec.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ class Quill {
clipboard = {
convert: () => {},
};
getSemanticHTML() {}
setContents() {}
setText() {}
getContents() {}
Expand Down Expand Up @@ -55,24 +56,12 @@ describe('QuillViewComponent', () => {
expect(component.debug).toEqual('warn');
expect(component.styles).toEqual('{"height": "200px"}');
expect(component.strict).toBe(true);
expect(component.preserveWhitespace).toBe(false);
expect(component.defaultEmptyValue).toBe(null);
expect(component.modules).toEqual('{"toolbar":true}');

expect(component.quillEditor.options).toEqual({ debug: 'warn', formats: undefined, modules: { toolbar: false }, readOnly: true, strict: true, theme: 'snow' });
});

it('renders pre tag if preserve whitespace', async () => {
component.preserveWhitespace = true;

await page.waitForChanges();

expect(page.root).toEqualHtml(`
<quill-view content="<p>Test</p>" modules="{&quot;toolbar&quot;:true}" styles="{&quot;height&quot;: &quot;200px&quot;}">
<pre quill-element=""></pre>
</quill-view>
`);
});

it('renders styles changes', async () => {
component.styles = '{"height": "300px"}';

Expand Down Expand Up @@ -250,17 +239,17 @@ describe('QuillViewComponent', () => {
describe('#getEditorContent', () => {
describe('format: html', () => {
it('p with br or div with br returns empty string', () => {
component.quillEditor.editorElement.children[0].innerHTML = '<div><br/></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><br></div>');
component.format = 'html';

expect(component.getEditorContent()).toEqual('');
expect(component.getEditorContent()).toEqual(null);
});

it('returns html', () => {
component.quillEditor.editorElement.children[0].innerHTML = '<div><p>asdf</p><br/></div>';
jest.spyOn(component.quillEditor, 'getSemanticHTML').mockReturnValue('<div><p>asdf</p><br/></div>');
component.format = 'html';

expect(component.getEditorContent()).toEqual('<div><p>asdf</p><br></div>');
expect(component.getEditorContent()).toEqual('<div><p>asdf</p><br/></div>');
});
});

Expand Down
32 changes: 21 additions & 11 deletions src/components/quill-view/quill-view.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import { h, Component, ComponentDidLoad, Element, Prop, Watch } from '@stencil/core';
import { ComponentDidLoad } from '@stencil/core';
import { h, Component, Element, Prop, Watch } from '@stencil/core';

declare const Quill: any;

Expand All @@ -19,12 +20,15 @@ export class QuillViewComponent implements ComponentDidLoad {
@Prop() strict: boolean = true;
@Prop() styles: string = '{}';
@Prop() theme: string = 'snow';
@Prop() preserveWhitespace: boolean = false;
@Prop() defaultEmptyValue: any = null

quillEditor: any;
editorElement: HTMLDivElement | HTMLPreElement;
editorElement: HTMLDivElement;

setEditorContent(value: any) {
if (!this.quillEditor) {
return null
}
if (this.format === 'html') {
const contents = this.quillEditor.clipboard.convert(value);
this.quillEditor.setContents(contents, 'api');
Expand All @@ -42,12 +46,15 @@ export class QuillViewComponent implements ComponentDidLoad {
}

getEditorContent() {
if (!this.quillEditor) {
return null
}
const text = this.quillEditor.getText();
const content = this.quillEditor.getContents();

let html: string | null = this.editorElement.children[0].innerHTML;
if (html === '<p><br></p>' || html === '<div><br></div>') {
html = '';
let html: string | null = this.quillEditor.getSemanticHTML()
if (this.isEmptyValue(html)) {
html = this.defaultEmptyValue;
}

if (this.format === 'html') {
Expand Down Expand Up @@ -123,6 +130,9 @@ export class QuillViewComponent implements ComponentDidLoad {

@Watch('content')
updateContent(newValue: any): void {
if (!this.quillEditor) {
return null
}
const editorContents = this.getEditorContent();

if (['text', 'html', 'json'].indexOf(this.format) > -1 && newValue === editorContents) {
Expand All @@ -143,12 +153,12 @@ export class QuillViewComponent implements ComponentDidLoad {

this.setEditorContent(newValue);
}

private isEmptyValue(html: string | null) {
return html === '<p></p>' || html === '<div></div>' || html === '<p><br></p>' || html === '<div><br></div>'
}

render() {
return this.preserveWhitespace ? (
<pre quill-element ref={(el: HTMLPreElement) => (this.editorElement = el)}></pre>
) : (
<div quill-element ref={(el: HTMLDivElement) => (this.editorElement = el)}></div>
);
return (<div quill-element ref={(el: HTMLDivElement) => {this.editorElement = el;}}></div>)
}
}
Loading

0 comments on commit 14f9df8

Please sign in to comment.