UNPKG

@tinkoff/angular-contenteditable-accessor

Version:

This is a ControlValueAccessor for using Angular forms with contenteditable elements

214 lines (204 loc) 10.7 kB
(function (global, factory) { typeof exports === 'object' && typeof module !== 'undefined' ? factory(exports, require('@angular/core'), require('@angular/forms'), require('@angular/platform-browser')) : typeof define === 'function' && define.amd ? define('@tinkoff/angular-contenteditable-accessor', ['exports', '@angular/core', '@angular/forms', '@angular/platform-browser'], factory) : (global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory((global.tinkoff = global.tinkoff || {}, global.tinkoff["angular-contenteditable-accessor"] = {}), global.ng.core, global.ng.forms, global.ng.platformBrowser)); })(this, (function (exports, i0, forms, i1) { 'use strict'; function _interopNamespace(e) { if (e && e.__esModule) return e; var n = Object.create(null); if (e) { Object.keys(e).forEach(function (k) { if (k !== 'default') { var d = Object.getOwnPropertyDescriptor(e, k); Object.defineProperty(n, k, d.get ? d : { enumerable: true, get: function () { return e[k]; } }); } }); } n["default"] = e; return Object.freeze(n); } var i0__namespace = /*#__PURE__*/_interopNamespace(i0); var i1__namespace = /*#__PURE__*/_interopNamespace(i1); /* * This is a barebones contenteditable {@link ControlValueAccessor} allowing you to use * Angular forms with native contenteditable HTML. For security reasons you might want * to consider sanitizing pasted/dropped content before using it. Also make sure that * you do not set any dangerous content as control value yourself, because directive * just outputs control value as-is. */ var ContenteditableValueAccessor = /** @class */ (function () { function ContenteditableValueAccessor(elementRef, renderer, sanitizer) { var _this = this; this.elementRef = elementRef; this.renderer = renderer; this.sanitizer = sanitizer; /* * MutationObserver IE11 fallback (as opposed to input event for modern browsers). * When mutation removes a tag, i.e. delete is pressed on the last remaining character * inside a tag — callback is triggered before the DOM is actually changed, therefore * setTimeout is used */ this.observer = new MutationObserver(function () { setTimeout(function () { _this.onChange(_this.processValue(_this.elementRef.nativeElement.innerHTML)); }); }); /* * onTouch callback that marks control as touched and allows FormHooks use */ this.onTouched = function () { }; /* * onChange callback that writes value to control and allows FormHooks use */ this.onChange = function () { }; } /* * To support IE11 MutationObserver is used to monitor changes to the content */ ContenteditableValueAccessor.prototype.ngAfterViewInit = function () { this.observer.observe(this.elementRef.nativeElement, { characterData: true, childList: true, subtree: true, }); }; /* * Disconnect MutationObserver IE11 fallback on destroy */ ContenteditableValueAccessor.prototype.ngOnDestroy = function () { this.observer.disconnect(); }; /* * Listen to input events to write innerHTML value into control, * also disconnect MutationObserver as it is not needed if this * event works in current browser */ ContenteditableValueAccessor.prototype.onInput = function () { this.observer.disconnect(); this.onChange(this.processValue(this.elementRef.nativeElement.innerHTML)); }; /* * Listen to blur event to mark control as touched */ ContenteditableValueAccessor.prototype.onBlur = function () { this.onTouched(); }; /* * Reacts to external change * * @see {@link ControlValueAccessor#writeValue} */ ContenteditableValueAccessor.prototype.writeValue = function (value) { this.renderer.setProperty(this.elementRef.nativeElement, 'innerHTML', this.processValue(value)); }; /* * Registers onChange callback * * @see {@link ControlValueAccessor#registerOnChange} */ ContenteditableValueAccessor.prototype.registerOnChange = function (onChange) { this.onChange = onChange; }; /* * Registers onTouch callback * * @see {@link ControlValueAccessor#registerOnTouched} */ ContenteditableValueAccessor.prototype.registerOnTouched = function (onTouched) { this.onTouched = onTouched; }; /* * Sets disabled state by setting contenteditable attribute to true/false * * @see {@link ControlValueAccessor#setDisabledState} */ ContenteditableValueAccessor.prototype.setDisabledState = function (disabled) { this.renderer.setAttribute(this.elementRef.nativeElement, 'contenteditable', String(!disabled)); }; /* * null is treated as empty string to prevent IE11 outputting 'null', * also single <br> is replaced with empty string when passed to the control */ ContenteditableValueAccessor.prototype.processValue = function (value) { var _a; var processed = String(value === null || value === undefined ? '' : value); return ((_a = this.sanitizer.sanitize(i0.SecurityContext.HTML, processed.trim() === '<br>' ? '' : processed)) !== null && _a !== void 0 ? _a : ''); }; return ContenteditableValueAccessor; }()); /** @nocollapse */ ContenteditableValueAccessor.ɵfac = function ContenteditableValueAccessor_Factory(t) { return new (t || ContenteditableValueAccessor)(i0__namespace.ɵɵdirectiveInject(i0.ElementRef), i0__namespace.ɵɵdirectiveInject(i0.Renderer2), i0__namespace.ɵɵdirectiveInject(i1.DomSanitizer)); }; /** @nocollapse */ ContenteditableValueAccessor.ɵdir = /** @pureOrBreakMyCode */ i0__namespace.ɵɵdefineDirective({ type: ContenteditableValueAccessor, selectors: [["", "contenteditable", "", "formControlName", ""], ["", "contenteditable", "", "formControl", ""], ["", "contenteditable", "", "ngModel", ""]], hostBindings: function ContenteditableValueAccessor_HostBindings(rf, ctx) { if (rf & 1) { i0__namespace.ɵɵlistener("input", function ContenteditableValueAccessor_input_HostBindingHandler() { return ctx.onInput(); })("blur", function ContenteditableValueAccessor_blur_HostBindingHandler() { return ctx.onBlur(); }); } }, features: [i0__namespace.ɵɵProvidersFeature([ { provide: forms.NG_VALUE_ACCESSOR, useExisting: i0.forwardRef((function () { return ContenteditableValueAccessor; })), multi: true, }, ])] }); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(ContenteditableValueAccessor, [{ type: i0.Directive, args: [{ selector: '[contenteditable][formControlName], [contenteditable][formControl], [contenteditable][ngModel]', providers: [ { provide: forms.NG_VALUE_ACCESSOR, useExisting: i0.forwardRef((function () { return ContenteditableValueAccessor; })), multi: true, }, ], }] }], function () { return [{ type: i0__namespace.ElementRef, decorators: [{ type: i0.Inject, args: [i0.ElementRef] }] }, { type: i0__namespace.Renderer2, decorators: [{ type: i0.Inject, args: [i0.Renderer2] }] }, { type: i1__namespace.DomSanitizer, decorators: [{ type: i0.Inject, args: [i1.DomSanitizer] }] }]; }, { onInput: [{ type: i0.HostListener, args: ['input'] }], onBlur: [{ type: i0.HostListener, args: ['blur'] }] }); })(); var ContenteditableValueAccessorModule = /** @class */ (function () { function ContenteditableValueAccessorModule() { } return ContenteditableValueAccessorModule; }()); /** @nocollapse */ ContenteditableValueAccessorModule.ɵfac = function ContenteditableValueAccessorModule_Factory(t) { return new (t || ContenteditableValueAccessorModule)(); }; /** @nocollapse */ ContenteditableValueAccessorModule.ɵmod = /** @pureOrBreakMyCode */ i0__namespace.ɵɵdefineNgModule({ type: ContenteditableValueAccessorModule }); /** @nocollapse */ ContenteditableValueAccessorModule.ɵinj = /** @pureOrBreakMyCode */ i0__namespace.ɵɵdefineInjector({}); (function () { (typeof ngDevMode === "undefined" || ngDevMode) && i0__namespace.ɵsetClassMetadata(ContenteditableValueAccessorModule, [{ type: i0.NgModule, args: [{ declarations: [ContenteditableValueAccessor], exports: [ContenteditableValueAccessor], }] }], null, null); })(); (function () { (typeof ngJitMode === "undefined" || ngJitMode) && i0__namespace.ɵɵsetNgModuleScope(ContenteditableValueAccessorModule, { declarations: [ContenteditableValueAccessor], exports: [ContenteditableValueAccessor] }); })(); /* * Public API Surface of @tinkoff/angular-contenteditable-accessor */ /** * Generated bundle index. Do not edit. */ exports.ContenteditableValueAccessor = ContenteditableValueAccessor; exports.ContenteditableValueAccessorModule = ContenteditableValueAccessorModule; Object.defineProperty(exports, '__esModule', { value: true }); })); //# sourceMappingURL=tinkoff-angular-contenteditable-accessor.umd.js.map