From a3ab60cef2a932445fe62ff611debaae671220d8 Mon Sep 17 00:00:00 2001 From: sibiraj-s Date: Sun, 31 Jan 2021 22:50:31 +0530 Subject: [PATCH] refactor: image view as angular element --- demo/src/app/doc.ts | 17 +++ demo/src/app/plugins/index.ts | 4 - docs/_sidebar.md | 2 - docs/full-featured-editor.md | 24 +-- docs/images.md | 12 -- docs/plugins.md | 10 -- package-lock.json | 8 + package.json | 1 + .../image-view/image-view.component.html | 9 ++ .../image-view/image-view.component.scss | 59 ++++++++ .../image-view/image-view.component.spec.ts | 25 ++++ .../image-view/image-view.component.ts | 66 +++++++++ src/lib/editor.component.html | 4 +- src/lib/editor.component.scss | 59 -------- src/lib/editor.component.ts | 21 ++- src/lib/editor.module.ts | 2 + src/lib/plugins/image.ts | 135 ++++++----------- src/lib/plugins/index.ts | 1 + src/plugins/image.ts | 139 ------------------ src/plugins/package.json | 12 -- src/plugins/public_api.ts | 1 - tsconfig.json | 4 - 22 files changed, 250 insertions(+), 365 deletions(-) delete mode 100644 docs/images.md delete mode 100644 docs/plugins.md create mode 100644 src/lib/components/image-view/image-view.component.html create mode 100644 src/lib/components/image-view/image-view.component.scss create mode 100644 src/lib/components/image-view/image-view.component.spec.ts create mode 100644 src/lib/components/image-view/image-view.component.ts delete mode 100644 src/plugins/image.ts delete mode 100644 src/plugins/package.json delete mode 100644 src/plugins/public_api.ts diff --git a/demo/src/app/doc.ts b/demo/src/app/doc.ts index 24715d5d..3d17b48b 100644 --- a/demo/src/app/doc.ts +++ b/demo/src/app/doc.ts @@ -257,6 +257,23 @@ export default { text: 'The content of the code editor is kept in sync with the content of the code block in the rich text editor, so that it is as if you\'re directly editing the outer document, using a more convenient interface.' } ] + }, + { + type: 'paragraph', + attrs: { + align: 'center' + }, + content: [ + { + type: 'image', + attrs: { + src: 'https://gameranx.com/wp-content/uploads/2016/03/Rise-of-the-Tomb-Raider-4K-Wallpaper-3.jpg', + alt: null, + title: null, + width: '578px' + } + } + ] } ] }; diff --git a/demo/src/app/plugins/index.ts b/demo/src/app/plugins/index.ts index ef3b911c..bdf3ee22 100644 --- a/demo/src/app/plugins/index.ts +++ b/demo/src/app/plugins/index.ts @@ -2,7 +2,6 @@ import { EditorState, Plugin, Selection, Transaction } from 'prosemirror-state'; import { EditorView } from 'prosemirror-view'; import { keymap } from 'prosemirror-keymap'; -import { image } from 'ngx-editor/plugins'; import { Command } from 'prosemirror-commands'; type Dir = 'left' | 'right' | 'up' | 'down'; @@ -34,9 +33,6 @@ const arrowHandlers = keymap({ const getPlugins = (): Plugin[] => { const plugins = [ - image({ - resize: true, - }), arrowHandlers ]; diff --git a/docs/_sidebar.md b/docs/_sidebar.md index 244bb044..cce0a359 100644 --- a/docs/_sidebar.md +++ b/docs/_sidebar.md @@ -3,7 +3,6 @@ - [Quick start](quickstart.md) - [Editor](editor.md) - [Configuration](configuration.md) - - [Plugins](plugins.md) - [Schema](schema.md) - [Commands](commands.md) - [Convert JSON doc to HTML](doc-html-doc.md) @@ -13,7 +12,6 @@ - [Full Featured Editor](full-featured-editor.md) - [Menu](menu.md) - [History](history.md) - - [Images](images.md) - [InputRules](input-rules.md) - [Shortcuts](shortcuts.md) - [Reactive Forms](reactive-forms.md) diff --git a/docs/full-featured-editor.md b/docs/full-featured-editor.md index 649f3535..df908d57 100644 --- a/docs/full-featured-editor.md +++ b/docs/full-featured-editor.md @@ -4,22 +4,6 @@ Use the following config to created a full featured editor -### plugin.ts - -```ts -import { Plugin } from 'prosemirror-state'; -import { image } from 'ngx-editor/plugins'; - -const plugins: Plugin[] = [ - image({ - // enables image resizing - resize: true, - }), -]; - -export default plugins; -``` - ### app.module.ts ```ts @@ -29,8 +13,6 @@ import { FormsModule } from '@angular/forms'; import { NgxEditorModule, schema } from 'ngx-editor'; import { schema } from 'ngx-editor/schema'; -import plugins from './plugins'; - @NgModule({ imports: [ FormsModule, @@ -81,8 +63,6 @@ import { Component, OnInit, OnDestroy, ViewEncapsulation } from '@angular/core'; import { AbstractControl, FormControl, FormGroup } from '@angular/forms'; import { Validators, Editor, Toolbar } from 'ngx-editor'; -import plugins from './plugins'; - @Component({ selector: 'app-root', templateUrl: 'app.component.html', @@ -107,9 +87,7 @@ export class AppComponent implements OnInit, OnDestroy { }); ngOnInit(): void { - this.editor = new Editor({ - plugins, - }); + this.editor = new Editor(); } ngOnDestroy(): void { diff --git a/docs/images.md b/docs/images.md deleted file mode 100644 index 9728cb19..00000000 --- a/docs/images.md +++ /dev/null @@ -1,12 +0,0 @@ -# Images - -```ts -import { Editor } from 'ngx-editor'; -import { image } from 'ngx-editor/plugins'; - -new Editor({ - image({ - resize: true - }) -}) -``` diff --git a/docs/plugins.md b/docs/plugins.md deleted file mode 100644 index a84c2d61..00000000 --- a/docs/plugins.md +++ /dev/null @@ -1,10 +0,0 @@ -# Plugins - -List of available plugins - -- image - - allows images to be resized. - -```js -import { image } from 'ngx-editor/plugins'; -``` diff --git a/package-lock.json b/package-lock.json index 755a5ca8..5a40b7b9 100644 --- a/package-lock.json +++ b/package-lock.json @@ -342,6 +342,14 @@ "tslib": "^2.0.0" } }, + "@angular/elements": { + "version": "11.1.1", + "resolved": "https://registry.npmjs.org/@angular/elements/-/elements-11.1.1.tgz", + "integrity": "sha512-cqTc5Y1GQC36b3H1esUfTqR/fWJAcpBYQyVomHrEo0utHTZgYW1/QBRz2DVRDBf0aNk1A5iJW17+qpZ/NDl6nw==", + "requires": { + "tslib": "^2.0.0" + } + }, "@angular/forms": { "version": "11.0.9", "resolved": "https://registry.npmjs.org/@angular/forms/-/forms-11.0.9.tgz", diff --git a/package.json b/package.json index 2290439f..8902228c 100644 --- a/package.json +++ b/package.json @@ -34,6 +34,7 @@ "@angular/common": "~11.0.1", "@angular/compiler": "~11.0.1", "@angular/core": "~11.0.1", + "@angular/elements": "^11.0.1", "@angular/forms": "~11.0.1", "@angular/platform-browser": "~11.0.1", "@angular/platform-browser-dynamic": "~11.0.1", diff --git a/src/lib/components/image-view/image-view.component.html b/src/lib/components/image-view/image-view.component.html new file mode 100644 index 00000000..ee0f32fb --- /dev/null +++ b/src/lib/components/image-view/image-view.component.html @@ -0,0 +1,9 @@ + + + + + + + + + diff --git a/src/lib/components/image-view/image-view.component.scss b/src/lib/components/image-view/image-view.component.scss new file mode 100644 index 00000000..508645ff --- /dev/null +++ b/src/lib/components/image-view/image-view.component.scss @@ -0,0 +1,59 @@ +$primary: #1a73e8; + +img { + width: 100%; + height: 100%; +} + +.NgxEditor__ImageWrapper { + position: relative; + display: inline-block; + line-height: 0; + padding: 2px; + + &.NgxEditor__Resizer--Active { + padding: 1px; + border: 1px solid $primary; + } + + .NgxEditor__ResizeHandle { + position: absolute; + height: 100%; + width: 100%; + + .NgxEditor__ResizeHandle--TL, + .NgxEditor__ResizeHandle--BL, + .NgxEditor__ResizeHandle--TR, + .NgxEditor__ResizeHandle--BR { + position: absolute; + width: 7px; + height: 7px; + background-color: $primary; + border: 1px solid white; + } + + .NgxEditor__ResizeHandle--BR { + bottom: -5px; + right: -5px; + cursor: se-resize; + } + + .NgxEditor__ResizeHandle--TR { + top: -5px; + right: -5px; + cursor: ne-resize; + } + + .NgxEditor__ResizeHandle--TL { + top: -5px; + left: -5px; + cursor: nw-resize; + } + + .NgxEditor__ResizeHandle--BL { + bottom: -5px; + left: -5px; + cursor: sw-resize; + } + } +} diff --git a/src/lib/components/image-view/image-view.component.spec.ts b/src/lib/components/image-view/image-view.component.spec.ts new file mode 100644 index 00000000..dd462278 --- /dev/null +++ b/src/lib/components/image-view/image-view.component.spec.ts @@ -0,0 +1,25 @@ +import { ComponentFixture, TestBed } from '@angular/core/testing'; + +import { ImageViewComponent } from './image-view.component'; + +describe('ImageComponent', () => { + let component: ImageViewComponent; + let fixture: ComponentFixture; + + beforeEach(async () => { + await TestBed.configureTestingModule({ + declarations: [ ImageViewComponent ] + }) + .compileComponents(); + }); + + beforeEach(() => { + fixture = TestBed.createComponent(ImageViewComponent); + component = fixture.componentInstance; + fixture.detectChanges(); + }); + + it('should create', () => { + expect(component).toBeTruthy(); + }); +}); diff --git a/src/lib/components/image-view/image-view.component.ts b/src/lib/components/image-view/image-view.component.ts new file mode 100644 index 00000000..f161c5dd --- /dev/null +++ b/src/lib/components/image-view/image-view.component.ts @@ -0,0 +1,66 @@ +import { + Component, ElementRef, EventEmitter, + Input, Output, ViewChild +} from '@angular/core'; +import { EditorView } from 'prosemirror-view'; + +@Component({ + selector: 'ngx-image-view', + templateUrl: './image-view.component.html', + styleUrls: ['./image-view.component.scss'] +}) + +export class ImageViewComponent { + private resizing = false; + @Input() src: string; + @Input() alt = ''; + @Input() title = ''; + @Input() outerWidth = ''; + @Input() selected = false; + @Input() view: EditorView; + + @Output() imageResize = new EventEmitter(); + + @ViewChild('imgEl', { static: true }) imgEl: ElementRef; + + constructor() { } + + startResizing(e: MouseEvent): void { + e.preventDefault(); + this.resizeImage(e); + } + + resizeImage(evt: MouseEvent): void { + const startX = evt.pageX; + const startWidth = this.imgEl.nativeElement.clientWidth; + + const { width } = window.getComputedStyle(this.view.dom); + const editorWidth = parseInt(width, 10); + + const onMouseMove = (e: MouseEvent) => { + const currentX = e.pageX; + const diffInPx = currentX - startX; + const computedWidth = startWidth + diffInPx; + + // prevent image overflow the editor + // prevent resizng below 20px + if (computedWidth > editorWidth || computedWidth < 20) { + return; + } + + this.outerWidth = `${computedWidth}px`; + }; + + const onMouseUp = (e: MouseEvent) => { + e.preventDefault(); + + document.removeEventListener('mousemove', onMouseMove); + document.removeEventListener('mouseup', onMouseUp); + + this.imageResize.emit(); + }; + + document.addEventListener('mousemove', onMouseMove); + document.addEventListener('mouseup', onMouseUp); + } +} diff --git a/src/lib/editor.component.html b/src/lib/editor.component.html index a9f36707..28fb0a45 100644 --- a/src/lib/editor.component.html +++ b/src/lib/editor.component.html @@ -1,3 +1 @@ -
- -
+
diff --git a/src/lib/editor.component.scss b/src/lib/editor.component.scss index 0366fd55..588b258f 100644 --- a/src/lib/editor.component.scss +++ b/src/lib/editor.component.scss @@ -1,8 +1,3 @@ -$primary: #1a73e8; -$light-gray: #f1f1f1; - -// prosemirror - .NgxEditor { background: white; color: black; @@ -51,60 +46,6 @@ $light-gray: #f1f1f1; pointer-events: none; } -.NgxEditor__ImageWrapper { - position: relative; - display: inline-block; - line-height: 0; - padding: 2px; - - &.NgxEditor__Resizer--Active { - padding: 1px; - border: 1px solid $primary; - } - - .NgxEditor__ResizeHandle { - position: absolute; - display: none; - height: 100%; - width: 100%; - - .NgxEditor__ResizeHandle--TL, - .NgxEditor__ResizeHandle--BL, - .NgxEditor__ResizeHandle--TR, - .NgxEditor__ResizeHandle--BR { - position: absolute; - width: 7px; - height: 7px; - background-color: $primary; - border: 1px solid white; - } - - .NgxEditor__ResizeHandle--BR { - bottom: -5px; - right: -5px; - cursor: se-resize; - } - - .NgxEditor__ResizeHandle--TR { - top: -5px; - right: -5px; - cursor: ne-resize; - } - - .NgxEditor__ResizeHandle--TL { - top: -5px; - left: -5px; - cursor: nw-resize; - } - - .NgxEditor__ResizeHandle--BL { - bottom: -5px; - left: -5px; - cursor: sw-resize; - } - } -} - .NgxEditor__Wrapper { border: 1px solid rgba(0, 0, 0, 0.4); border-radius: 4px; diff --git a/src/lib/editor.component.ts b/src/lib/editor.component.ts index ad82dccd..61ae5b0c 100644 --- a/src/lib/editor.component.ts +++ b/src/lib/editor.component.ts @@ -2,14 +2,16 @@ import { Component, ViewChild, ElementRef, forwardRef, OnDestroy, ViewEncapsulation, OnInit, Output, EventEmitter, - Input, Renderer2, SimpleChanges, OnChanges, + Input, Renderer2, SimpleChanges, OnChanges, Injector, } from '@angular/core'; import { NG_VALUE_ACCESSOR, ControlValueAccessor } from '@angular/forms'; +import { createCustomElement } from '@angular/elements'; import { Subscription } from 'rxjs'; import * as plugins from './plugins'; import { toHTML } from './parsers'; import Editor from './Editor'; +import { ImageViewComponent } from './components/image-view/image-view.component'; @Component({ selector: 'ngx-editor', @@ -24,7 +26,10 @@ import Editor from './Editor'; }) export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChanges, OnDestroy { - constructor(private renderer: Renderer2) { } + constructor( + private renderer: Renderer2, + private injector: Injector + ) { } @ViewChild('ngxEditor', { static: true }) ngxEditor: ElementRef; @@ -83,6 +88,15 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang this.setMeta('UPDATE_PLACEHOLDER', placeholder); } + private registerCustomElements(): void { + const imgViewExists = customElements.get('ngx-image-view'); + + if (!imgViewExists) { + const ImageViewElement = createCustomElement(ImageViewComponent, { injector: this.injector }); + customElements.define('ngx-image-view', ImageViewElement); + } + } + private registerPlugins(): void { this.editor.registerPlugin(plugins.editable(this.enabled)); this.editor.registerPlugin(plugins.placeholder(this.placeholder)); @@ -103,6 +117,8 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang this.focusOut.emit(); this.onTouched(); })); + + this.editor.registerPlugin(plugins.image(this.injector)); } ngOnInit(): void { @@ -110,6 +126,7 @@ export class NgxEditorComponent implements ControlValueAccessor, OnInit, OnChang throw new Error('NgxEditor: Required editor instance'); } + this.registerCustomElements(); this.registerPlugins(); this.renderer.appendChild(this.ngxEditor.nativeElement, this.editor.el); diff --git a/src/lib/editor.module.ts b/src/lib/editor.module.ts index 5e387275..74488253 100644 --- a/src/lib/editor.module.ts +++ b/src/lib/editor.module.ts @@ -9,6 +9,7 @@ import { MenuModule } from './modules/menu/menu.module'; import { BubbleComponent } from './components/bubble/bubble.component'; import { MenuComponent } from './modules/menu/menu.component'; +import { ImageViewComponent } from './components/image-view/image-view.component'; const NGX_EDITOR_CONFIG_TOKEN = new InjectionToken('NgxEditorConfig'); @@ -21,6 +22,7 @@ const NGX_EDITOR_CONFIG_TOKEN = new InjectionToken('NgxEditorCo declarations: [ NgxEditorComponent, BubbleComponent, + ImageViewComponent, ], exports: [ NgxEditorComponent, diff --git a/src/lib/plugins/image.ts b/src/lib/plugins/image.ts index cd51dd6f..d7ced257 100644 --- a/src/lib/plugins/image.ts +++ b/src/lib/plugins/image.ts @@ -1,6 +1,10 @@ +import { Injector, Renderer2 } from '@angular/core'; +import { NgElement, WithProperties } from '@angular/elements'; import { Node as ProseMirrorNode } from 'prosemirror-model'; import { NodeSelection, Plugin, PluginKey } from 'prosemirror-state'; -import { EditorView } from 'prosemirror-view'; +import { EditorView, NodeView } from 'prosemirror-view'; + +import { ImageViewComponent } from '../components/image-view/image-view.component'; const WRAPPER_CLASSNAME = 'NgxEditor__ImageWrapper'; const WRAPPER_RESIZE_ACTIVE_CLASSNAME = 'NgxEditor__Resizer--Active'; @@ -12,124 +16,67 @@ const createHandle = (direction: string): HTMLElement => { return handle; }; -class ImageRezieView { +class ImageRezieView implements NodeView { img: HTMLElement; - dom: HTMLElement; + dom: NgElement & WithProperties; handle: HTMLElement; + view: EditorView; + getPos: () => number; - constructor(node: ProseMirrorNode, view: EditorView, getPos: () => number) { - const outer = document.createElement('span'); - outer.className = WRAPPER_CLASSNAME; - outer.style.width = node.attrs.width; - - const handle = document.createElement('span'); - handle.className = RESIZE_HANDLE_CLASSNAME; - - const img = document.createElement('img'); - img.setAttribute('src', node.attrs.src); - img.setAttribute('alt', node.attrs.alt ?? ''); - img.setAttribute('title', node.attrs.title ?? ''); - img.style.width = '100%'; - img.style.height = '100%'; - - const handleBottomRight = createHandle('BR'); - const handleTopRight = createHandle('TL'); - const handleTopLeft = createHandle('TR'); - const handleBottomLeft = createHandle('BL'); - - const resizePropoptionally = (evt: MouseEvent) => { - evt.preventDefault(); - - const { state, dispatch } = view; - const { tr } = state; - - const startX = evt.pageX; - const startWidth = img.clientWidth; - - const { width } = window.getComputedStyle(view.dom); - const editorWidth = parseInt(width, 10); - - const onMouseMove = (e: MouseEvent) => { - const currentX = e.pageX; - const diffInPx = currentX - startX; - const computedWidth = startWidth + diffInPx; - - // prevent image overflow the editor - // prevent resizng below 20px - if (computedWidth > editorWidth || computedWidth < 20) { - return; - } + constructor(node: ProseMirrorNode, view: EditorView, getPos: () => number, injector: Injector) { + const renderer = injector.get(Renderer2); - outer.style.width = `${computedWidth}px`; - }; + const dom = renderer.createElement('ngx-image-view') as NgElement & WithProperties; + dom.src = node.attrs.src; + dom.alt = node.attrs.alt; + dom.title = node.attrs.title; + dom.outerWidth = node.attrs.width; + dom.view = view; - const onMouseUp = (e: MouseEvent) => { - e.preventDefault(); + this.dom = dom; + this.view = view; + this.getPos = getPos; - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - - const transaction = tr.setNodeMarkup(getPos(), undefined, { - src: node.attrs.src, - width: outer.style.width - }); - - const resolvedPos = transaction.doc.resolve(getPos()); - const newSelection = new NodeSelection(resolvedPos); - - transaction.setSelection(newSelection); - dispatch(transaction); - }; - - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - }; + this.dom.addEventListener('imageResize', this.handleResize); + } - handleBottomRight.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleTopRight.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleTopLeft.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleBottomLeft.addEventListener('mousedown', resizePropoptionally, { once: true }); + handleResize = (): void => { + const { state, dispatch } = this.view; + const { tr } = state; - handle.appendChild(handleBottomRight); - handle.appendChild(handleTopRight); - handle.appendChild(handleTopLeft); - handle.appendChild(handleBottomLeft); + const transaction = tr.setNodeMarkup(this.getPos(), undefined, { + src: this.dom.src, + width: this.dom.outerWidth + }); - outer.appendChild(handle); - outer.appendChild(img); + const resolvedPos = transaction.doc.resolve(this.getPos()); + const newSelection = new NodeSelection(resolvedPos); - this.dom = outer; - this.img = img; - this.handle = handle; + transaction.setSelection(newSelection); + dispatch(transaction); } selectNode(): void { - this.dom.classList.add(WRAPPER_RESIZE_ACTIVE_CLASSNAME); - this.handle.style.display = 'block'; + this.dom.selected = true; } deselectNode(): void { - this.dom.classList.remove(WRAPPER_RESIZE_ACTIVE_CLASSNAME); - this.handle.style.display = 'none'; + this.dom.selected = false; } -} -const defaultOptions = { - resize: true, -}; + destroy(): void { + this.dom.removeEventListener('imageResize', this.handleResize); + } +} -const imagePlugin = (opts = defaultOptions): Plugin => { - const options = { ...defaultOptions, ...opts }; +const imagePlugin = (injector: Injector): Plugin => { return new Plugin({ key: new PluginKey('link'), props: { nodeViews: { image: (node: ProseMirrorNode, view: EditorView, getPos: () => number) => { - if (!options.resize) { - return null; - } - return new ImageRezieView(node, view, getPos); + return new ImageRezieView(node, view, getPos, injector); }, } } diff --git a/src/lib/plugins/index.ts b/src/lib/plugins/index.ts index 0b4f9471..7eda34bb 100644 --- a/src/lib/plugins/index.ts +++ b/src/lib/plugins/index.ts @@ -3,3 +3,4 @@ export { default as placeholder } from './placeholder'; export { default as attributes } from './attributes'; export { default as focus } from './focus'; export { default as blur } from './blur'; +export { default as image } from './image'; diff --git a/src/plugins/image.ts b/src/plugins/image.ts deleted file mode 100644 index cd51dd6f..00000000 --- a/src/plugins/image.ts +++ /dev/null @@ -1,139 +0,0 @@ -import { Node as ProseMirrorNode } from 'prosemirror-model'; -import { NodeSelection, Plugin, PluginKey } from 'prosemirror-state'; -import { EditorView } from 'prosemirror-view'; - -const WRAPPER_CLASSNAME = 'NgxEditor__ImageWrapper'; -const WRAPPER_RESIZE_ACTIVE_CLASSNAME = 'NgxEditor__Resizer--Active'; -const RESIZE_HANDLE_CLASSNAME = 'NgxEditor__ResizeHandle'; - -const createHandle = (direction: string): HTMLElement => { - const handle = document.createElement('span'); - handle.className = `${RESIZE_HANDLE_CLASSNAME}--${direction}`; - return handle; -}; - -class ImageRezieView { - img: HTMLElement; - dom: HTMLElement; - handle: HTMLElement; - - constructor(node: ProseMirrorNode, view: EditorView, getPos: () => number) { - const outer = document.createElement('span'); - outer.className = WRAPPER_CLASSNAME; - outer.style.width = node.attrs.width; - - const handle = document.createElement('span'); - handle.className = RESIZE_HANDLE_CLASSNAME; - - const img = document.createElement('img'); - img.setAttribute('src', node.attrs.src); - img.setAttribute('alt', node.attrs.alt ?? ''); - img.setAttribute('title', node.attrs.title ?? ''); - img.style.width = '100%'; - img.style.height = '100%'; - - const handleBottomRight = createHandle('BR'); - const handleTopRight = createHandle('TL'); - const handleTopLeft = createHandle('TR'); - const handleBottomLeft = createHandle('BL'); - - const resizePropoptionally = (evt: MouseEvent) => { - evt.preventDefault(); - - const { state, dispatch } = view; - const { tr } = state; - - const startX = evt.pageX; - const startWidth = img.clientWidth; - - const { width } = window.getComputedStyle(view.dom); - const editorWidth = parseInt(width, 10); - - const onMouseMove = (e: MouseEvent) => { - const currentX = e.pageX; - const diffInPx = currentX - startX; - const computedWidth = startWidth + diffInPx; - - // prevent image overflow the editor - // prevent resizng below 20px - if (computedWidth > editorWidth || computedWidth < 20) { - return; - } - - outer.style.width = `${computedWidth}px`; - }; - - const onMouseUp = (e: MouseEvent) => { - e.preventDefault(); - - document.removeEventListener('mousemove', onMouseMove); - document.removeEventListener('mouseup', onMouseUp); - - const transaction = tr.setNodeMarkup(getPos(), undefined, { - src: node.attrs.src, - width: outer.style.width - }); - - const resolvedPos = transaction.doc.resolve(getPos()); - const newSelection = new NodeSelection(resolvedPos); - - transaction.setSelection(newSelection); - dispatch(transaction); - }; - - document.addEventListener('mousemove', onMouseMove); - document.addEventListener('mouseup', onMouseUp); - }; - - handleBottomRight.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleTopRight.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleTopLeft.addEventListener('mousedown', resizePropoptionally, { once: true }); - handleBottomLeft.addEventListener('mousedown', resizePropoptionally, { once: true }); - - handle.appendChild(handleBottomRight); - handle.appendChild(handleTopRight); - handle.appendChild(handleTopLeft); - handle.appendChild(handleBottomLeft); - - outer.appendChild(handle); - outer.appendChild(img); - - this.dom = outer; - this.img = img; - this.handle = handle; - } - - selectNode(): void { - this.dom.classList.add(WRAPPER_RESIZE_ACTIVE_CLASSNAME); - this.handle.style.display = 'block'; - } - - deselectNode(): void { - this.dom.classList.remove(WRAPPER_RESIZE_ACTIVE_CLASSNAME); - this.handle.style.display = 'none'; - } -} - -const defaultOptions = { - resize: true, -}; - -const imagePlugin = (opts = defaultOptions): Plugin => { - const options = { ...defaultOptions, ...opts }; - - return new Plugin({ - key: new PluginKey('link'), - props: { - nodeViews: { - image: (node: ProseMirrorNode, view: EditorView, getPos: () => number) => { - if (!options.resize) { - return null; - } - return new ImageRezieView(node, view, getPos); - }, - } - } - }); -}; - -export default imagePlugin; diff --git a/src/plugins/package.json b/src/plugins/package.json deleted file mode 100644 index 2f9e20af..00000000 --- a/src/plugins/package.json +++ /dev/null @@ -1,12 +0,0 @@ -{ - "ngPackage": { - "lib": { - "entryFile": "./public_api.ts", - "umdModuleIds": { - "prosemirror-state": "prosemirrorState", - "prosemirror-view": "prosemirrorView", - "prosemirror-commands": "prosemirrorCommands" - } - } - } -} diff --git a/src/plugins/public_api.ts b/src/plugins/public_api.ts deleted file mode 100644 index e1fb9888..00000000 --- a/src/plugins/public_api.ts +++ /dev/null @@ -1 +0,0 @@ -export { default as image } from './image'; diff --git a/tsconfig.json b/tsconfig.json index a3b1ddc3..ac1ac12a 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -39,10 +39,6 @@ "ngx-editor/commands": [ "dist/ngx-editor/ngx-editor/commands", "dist/ngx-editor/commands" - ], - "ngx-editor/plugins": [ - "dist/ngx-editor/ngx-editor/plugins", - "dist/ngx-editor/plugins" ] } },