@tinkoff/angular-contenteditable-accessor
Version:
This is a ControlValueAccessor for using Angular forms with contenteditable elements
214 lines (204 loc) • 10.7 kB
JavaScript
(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