nexheal-lib 0.0.10 → 0.0.12
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/fesm2022/nexheal-lib.mjs +126 -222
- package/fesm2022/nexheal-lib.mjs.map +1 -1
- package/index.d.ts +38 -20
- package/package.json +1 -1
package/fesm2022/nexheal-lib.mjs
CHANGED
|
@@ -386,7 +386,21 @@ class CalendarControl {
|
|
|
386
386
|
timeOnly = false;
|
|
387
387
|
dateFormat = "dd/MM/yyyy";
|
|
388
388
|
placeholder = "dd-mm-yyyy";
|
|
389
|
-
|
|
389
|
+
_disabled = false;
|
|
390
|
+
get disabled() {
|
|
391
|
+
return this._disabled;
|
|
392
|
+
}
|
|
393
|
+
set disabled(value) {
|
|
394
|
+
this._disabled = value;
|
|
395
|
+
if (this.inputControl) {
|
|
396
|
+
if (value) {
|
|
397
|
+
this.inputControl.disable({ emitEvent: false });
|
|
398
|
+
}
|
|
399
|
+
else {
|
|
400
|
+
this.inputControl.enable({ emitEvent: false });
|
|
401
|
+
}
|
|
402
|
+
}
|
|
403
|
+
}
|
|
390
404
|
readonly = false;
|
|
391
405
|
submitted = false;
|
|
392
406
|
inputPlaceholder = false;
|
|
@@ -2458,24 +2472,41 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
2458
2472
|
args: ['switchContainer']
|
|
2459
2473
|
}] } });
|
|
2460
2474
|
|
|
2475
|
+
/**
|
|
2476
|
+
* Lightweight rich-text editor built on a `contenteditable` element and the
|
|
2477
|
+
* browser's `document.execCommand` rich-editing API (the de-facto standard for
|
|
2478
|
+
* simple in-browser editors).
|
|
2479
|
+
*
|
|
2480
|
+
* Implemented as a `ControlValueAccessor`, so it works with `[(ngModel)]`,
|
|
2481
|
+
* `formControlName` and `[formControl]`. The emitted value is the editor's
|
|
2482
|
+
* inner HTML string.
|
|
2483
|
+
*/
|
|
2461
2484
|
class TextEditor {
|
|
2462
2485
|
header = true;
|
|
2463
2486
|
media = true;
|
|
2464
2487
|
link = true;
|
|
2465
2488
|
placeholder = 'Type here...';
|
|
2466
2489
|
readonly = false;
|
|
2490
|
+
/** Disabled state, set by Reactive Forms through `setDisabledState`. */
|
|
2491
|
+
disabled = false;
|
|
2467
2492
|
editorRef;
|
|
2468
2493
|
onChange = () => { };
|
|
2469
2494
|
onTouch = () => { };
|
|
2470
|
-
|
|
2471
|
-
|
|
2472
|
-
|
|
2495
|
+
/** HTML received via `writeValue` before the view (and `editorRef`) is ready. */
|
|
2496
|
+
pendingValue = '';
|
|
2497
|
+
viewInitialized = false;
|
|
2498
|
+
/** Last selection range observed inside the editor (restored before commands). */
|
|
2499
|
+
savedRange = null;
|
|
2473
2500
|
ngAfterViewInit() {
|
|
2474
|
-
this.
|
|
2501
|
+
this.viewInitialized = true;
|
|
2502
|
+
this.editorRef.nativeElement.innerHTML = this.pendingValue;
|
|
2475
2503
|
}
|
|
2504
|
+
// ---- ControlValueAccessor ----
|
|
2476
2505
|
writeValue(value) {
|
|
2477
|
-
this.
|
|
2478
|
-
this.
|
|
2506
|
+
this.pendingValue = value ?? '';
|
|
2507
|
+
if (this.viewInitialized && this.editorRef) {
|
|
2508
|
+
this.editorRef.nativeElement.innerHTML = this.pendingValue;
|
|
2509
|
+
}
|
|
2479
2510
|
}
|
|
2480
2511
|
registerOnChange(fn) {
|
|
2481
2512
|
this.onChange = fn;
|
|
@@ -2483,256 +2514,126 @@ class TextEditor {
|
|
|
2483
2514
|
registerOnTouched(fn) {
|
|
2484
2515
|
this.onTouch = fn;
|
|
2485
2516
|
}
|
|
2517
|
+
setDisabledState(isDisabled) {
|
|
2518
|
+
this.disabled = isDisabled;
|
|
2519
|
+
}
|
|
2520
|
+
// ---- Editor events (wired from the template) ----
|
|
2486
2521
|
onInput() {
|
|
2487
|
-
|
|
2488
|
-
this.onChange(html);
|
|
2489
|
-
this.saveState();
|
|
2522
|
+
this.onChange(this.editorRef.nativeElement.innerHTML);
|
|
2490
2523
|
}
|
|
2491
|
-
// events
|
|
2492
2524
|
onTouched() {
|
|
2493
2525
|
this.onTouch();
|
|
2494
2526
|
}
|
|
2495
|
-
|
|
2496
|
-
|
|
2497
|
-
}
|
|
2498
|
-
// ---- Selection & Range Utilities ----
|
|
2499
|
-
getSelectionRange() {
|
|
2527
|
+
/** Remember the caret/selection whenever it sits inside the editor. */
|
|
2528
|
+
saveSelection() {
|
|
2500
2529
|
const selection = window.getSelection();
|
|
2501
|
-
if (selection
|
|
2502
|
-
return selection.getRangeAt(0);
|
|
2503
|
-
}
|
|
2504
|
-
return null;
|
|
2505
|
-
}
|
|
2506
|
-
wrapSelection(htmlTag, style) {
|
|
2507
|
-
const range = this.getSelectionRange();
|
|
2508
|
-
if (!range || range.collapsed)
|
|
2509
|
-
return; // no selection
|
|
2510
|
-
const selectedText = range.extractContents();
|
|
2511
|
-
const el = document.createElement(htmlTag);
|
|
2512
|
-
if (style) {
|
|
2513
|
-
el.setAttribute('style', style);
|
|
2514
|
-
}
|
|
2515
|
-
el.appendChild(selectedText);
|
|
2516
|
-
range.insertNode(el);
|
|
2517
|
-
this.mergeAdjacentSimilarElements(el);
|
|
2518
|
-
this.onInput();
|
|
2519
|
-
}
|
|
2520
|
-
// Merges adjacent spans with the same style to avoid clutter
|
|
2521
|
-
mergeAdjacentSimilarElements(el) {
|
|
2522
|
-
const parent = el.parentElement;
|
|
2523
|
-
if (!parent)
|
|
2530
|
+
if (!selection || selection.rangeCount === 0 || !this.editorRef)
|
|
2524
2531
|
return;
|
|
2525
|
-
const
|
|
2526
|
-
|
|
2527
|
-
|
|
2528
|
-
const next = siblings[i + 1];
|
|
2529
|
-
if (current.nodeType === 1 && next?.nodeType === 1) {
|
|
2530
|
-
if (current.tagName === next.tagName && current.getAttribute('style') === next.getAttribute('style')) {
|
|
2531
|
-
// merge them
|
|
2532
|
-
while (next.firstChild) {
|
|
2533
|
-
current.appendChild(next.firstChild);
|
|
2534
|
-
}
|
|
2535
|
-
next.remove();
|
|
2536
|
-
}
|
|
2537
|
-
}
|
|
2532
|
+
const range = selection.getRangeAt(0);
|
|
2533
|
+
if (this.editorRef.nativeElement.contains(range.commonAncestorContainer)) {
|
|
2534
|
+
this.savedRange = range.cloneRange();
|
|
2538
2535
|
}
|
|
2539
2536
|
}
|
|
2540
|
-
// ---- Inline
|
|
2541
|
-
|
|
2542
|
-
|
|
2543
|
-
let style = '';
|
|
2544
|
-
if (styleType === 'bold')
|
|
2545
|
-
style = 'font-weight:bold;';
|
|
2546
|
-
else if (styleType === 'italic')
|
|
2547
|
-
style = 'font-style:italic;';
|
|
2548
|
-
else if (styleType === 'underline')
|
|
2549
|
-
style = 'text-decoration:underline;';
|
|
2550
|
-
this.wrapSelection('span', style);
|
|
2537
|
+
// ---- Inline formatting ----
|
|
2538
|
+
applyInlineStyle(style) {
|
|
2539
|
+
this.exec(style);
|
|
2551
2540
|
}
|
|
2552
|
-
|
|
2553
|
-
|
|
2554
|
-
|
|
2555
|
-
// Now `color` is a string and `input` is never null
|
|
2556
|
-
this.wrapSelection('span', `color:${color};`);
|
|
2541
|
+
// ---- Block formatting (headings / paragraph) ----
|
|
2542
|
+
applyBlockFormat(block) {
|
|
2543
|
+
this.exec('formatBlock', `<${block}>`);
|
|
2557
2544
|
}
|
|
2558
|
-
|
|
2559
|
-
|
|
2560
|
-
|
|
2561
|
-
this.wrapSelection('span', `background-color:${color};`);
|
|
2562
|
-
}
|
|
2563
|
-
// ---- Block Formatting (Headings, Paragraph) ----
|
|
2564
|
-
// Replaces the parent block element containing selection with a new block type
|
|
2565
|
-
applyBlockFormat(blockTag) {
|
|
2566
|
-
const range = this.getSelectionRange();
|
|
2567
|
-
if (!range)
|
|
2568
|
-
return;
|
|
2569
|
-
// Find the nearest block element
|
|
2570
|
-
let block = this.findBlockAncestor(range.commonAncestorContainer);
|
|
2571
|
-
if (!block) {
|
|
2572
|
-
// If no block found, wrap the selection in a block
|
|
2573
|
-
const wrapper = document.createElement(blockTag);
|
|
2574
|
-
wrapper.appendChild(range.extractContents());
|
|
2575
|
-
range.insertNode(wrapper);
|
|
2576
|
-
}
|
|
2577
|
-
else {
|
|
2578
|
-
// Replace the block with a new block type
|
|
2579
|
-
const newBlock = document.createElement(blockTag);
|
|
2580
|
-
// Move children
|
|
2581
|
-
while (block.firstChild) {
|
|
2582
|
-
newBlock.appendChild(block.firstChild);
|
|
2583
|
-
}
|
|
2584
|
-
block.replaceWith(newBlock);
|
|
2585
|
-
}
|
|
2586
|
-
this.onInput();
|
|
2545
|
+
// ---- Lists ----
|
|
2546
|
+
applyList(listType) {
|
|
2547
|
+
this.exec(listType === 'ul' ? 'insertUnorderedList' : 'insertOrderedList');
|
|
2587
2548
|
}
|
|
2588
|
-
|
|
2589
|
-
|
|
2590
|
-
|
|
2591
|
-
return node;
|
|
2592
|
-
}
|
|
2593
|
-
node = node.parentNode;
|
|
2594
|
-
}
|
|
2595
|
-
return null;
|
|
2549
|
+
// ---- Indentation ----
|
|
2550
|
+
indent() {
|
|
2551
|
+
this.exec('indent');
|
|
2596
2552
|
}
|
|
2597
|
-
|
|
2598
|
-
|
|
2599
|
-
return false;
|
|
2600
|
-
const display = window.getComputedStyle(node).display;
|
|
2601
|
-
return display === 'block' || display === 'list-item';
|
|
2553
|
+
outdent() {
|
|
2554
|
+
this.exec('outdent');
|
|
2602
2555
|
}
|
|
2603
|
-
// ----
|
|
2604
|
-
|
|
2605
|
-
|
|
2606
|
-
|
|
2607
|
-
|
|
2608
|
-
|
|
2609
|
-
|
|
2610
|
-
|
|
2611
|
-
|
|
2612
|
-
|
|
2613
|
-
|
|
2614
|
-
|
|
2615
|
-
|
|
2616
|
-
li.appendChild(line);
|
|
2617
|
-
listEl.appendChild(li);
|
|
2618
|
-
}
|
|
2619
|
-
if (commonBlock) {
|
|
2620
|
-
// Insert the list right where the block was or at the selection
|
|
2621
|
-
range.insertNode(listEl);
|
|
2622
|
-
}
|
|
2623
|
-
else {
|
|
2624
|
-
// If no common block, just insert at current position
|
|
2625
|
-
range.insertNode(listEl);
|
|
2626
|
-
}
|
|
2627
|
-
this.onInput();
|
|
2556
|
+
// ---- Alignment ----
|
|
2557
|
+
setAlignment(alignment) {
|
|
2558
|
+
const command = {
|
|
2559
|
+
left: 'justifyLeft',
|
|
2560
|
+
center: 'justifyCenter',
|
|
2561
|
+
right: 'justifyRight',
|
|
2562
|
+
justify: 'justifyFull',
|
|
2563
|
+
}[alignment];
|
|
2564
|
+
this.exec(command);
|
|
2565
|
+
}
|
|
2566
|
+
// ---- Color / highlight ----
|
|
2567
|
+
applyColor(event) {
|
|
2568
|
+
this.exec('foreColor', event.target.value, true);
|
|
2628
2569
|
}
|
|
2629
|
-
|
|
2630
|
-
|
|
2631
|
-
// For advanced logic, detect line breaks more thoroughly.
|
|
2632
|
-
const lines = [];
|
|
2633
|
-
let currentFrag = document.createDocumentFragment();
|
|
2634
|
-
Array.from(fragment.childNodes).forEach((node) => {
|
|
2635
|
-
if (node.nodeName === 'BR' || this.isBlockElement(node)) {
|
|
2636
|
-
// This node signifies a new line
|
|
2637
|
-
lines.push(currentFrag);
|
|
2638
|
-
currentFrag = document.createDocumentFragment();
|
|
2639
|
-
if (this.isBlockElement(node)) {
|
|
2640
|
-
while (node.firstChild) {
|
|
2641
|
-
currentFrag.appendChild(node.firstChild);
|
|
2642
|
-
}
|
|
2643
|
-
lines.push(currentFrag);
|
|
2644
|
-
currentFrag = document.createDocumentFragment();
|
|
2645
|
-
}
|
|
2646
|
-
}
|
|
2647
|
-
else {
|
|
2648
|
-
currentFrag.appendChild(node);
|
|
2649
|
-
}
|
|
2650
|
-
});
|
|
2651
|
-
if (currentFrag.childNodes.length > 0) {
|
|
2652
|
-
lines.push(currentFrag);
|
|
2653
|
-
}
|
|
2654
|
-
return lines;
|
|
2570
|
+
applyHighlight(event) {
|
|
2571
|
+
this.exec('hiliteColor', event.target.value, true);
|
|
2655
2572
|
}
|
|
2656
|
-
// ---- Links ----
|
|
2573
|
+
// ---- Links / images ----
|
|
2657
2574
|
createLink() {
|
|
2658
2575
|
const url = prompt('Enter URL', 'https://');
|
|
2659
2576
|
if (!url)
|
|
2660
2577
|
return;
|
|
2661
|
-
|
|
2662
|
-
if (!range || range.collapsed)
|
|
2663
|
-
return;
|
|
2664
|
-
const selectedContent = range.extractContents();
|
|
2665
|
-
const a = document.createElement('a');
|
|
2666
|
-
a.href = url;
|
|
2667
|
-
a.appendChild(selectedContent);
|
|
2668
|
-
range.insertNode(a);
|
|
2669
|
-
this.onInput();
|
|
2578
|
+
this.exec('createLink', url);
|
|
2670
2579
|
}
|
|
2671
|
-
// ---- Images ----
|
|
2672
2580
|
insertImage() {
|
|
2673
2581
|
const url = prompt('Enter image URL:');
|
|
2674
2582
|
if (!url)
|
|
2675
2583
|
return;
|
|
2676
|
-
|
|
2677
|
-
if (!range)
|
|
2678
|
-
return;
|
|
2679
|
-
const img = document.createElement('img');
|
|
2680
|
-
img.src = url;
|
|
2681
|
-
range.insertNode(img);
|
|
2682
|
-
this.onInput();
|
|
2584
|
+
this.exec('insertImage', url);
|
|
2683
2585
|
}
|
|
2684
|
-
// ----
|
|
2685
|
-
|
|
2686
|
-
|
|
2687
|
-
const range = this.getSelectionRange();
|
|
2688
|
-
if (!range)
|
|
2689
|
-
return;
|
|
2690
|
-
const block = this.findBlockAncestor(range.commonAncestorContainer);
|
|
2691
|
-
if (block) {
|
|
2692
|
-
block.style.textAlign = alignment;
|
|
2693
|
-
this.onInput();
|
|
2694
|
-
}
|
|
2586
|
+
// ---- Undo / redo / clear ----
|
|
2587
|
+
undo() {
|
|
2588
|
+
this.exec('undo');
|
|
2695
2589
|
}
|
|
2696
|
-
|
|
2697
|
-
|
|
2698
|
-
const html = this.editorRef.nativeElement.innerText;
|
|
2699
|
-
// Convert innerText to a plain <p> block for simplicity
|
|
2700
|
-
this.editorRef.nativeElement.innerHTML = `<p>${html}</p>`;
|
|
2701
|
-
this.onInput();
|
|
2590
|
+
redo() {
|
|
2591
|
+
this.exec('redo');
|
|
2702
2592
|
}
|
|
2703
|
-
|
|
2704
|
-
|
|
2705
|
-
const currentHtml = this.editorRef.nativeElement.innerHTML;
|
|
2706
|
-
if (this.undoStack.length === 0 || this.undoStack[this.undoStack.length - 1].html !== currentHtml) {
|
|
2707
|
-
this.undoStack.push({ html: currentHtml });
|
|
2708
|
-
this.redoStack = []; // clear redo on new input
|
|
2709
|
-
}
|
|
2593
|
+
clearFormatting() {
|
|
2594
|
+
this.exec('removeFormat');
|
|
2710
2595
|
}
|
|
2711
|
-
|
|
2712
|
-
|
|
2713
|
-
|
|
2714
|
-
|
|
2715
|
-
|
|
2716
|
-
|
|
2717
|
-
|
|
2718
|
-
|
|
2719
|
-
|
|
2596
|
+
/**
|
|
2597
|
+
* Focus the editor, restore the last selection, run a rich-text command and
|
|
2598
|
+
* propagate the resulting HTML. No-op while readonly/disabled.
|
|
2599
|
+
*/
|
|
2600
|
+
exec(command, value, useCss = false) {
|
|
2601
|
+
if (this.disabled || this.readonly)
|
|
2602
|
+
return;
|
|
2603
|
+
this.editorRef.nativeElement.focus();
|
|
2604
|
+
this.restoreSelection();
|
|
2605
|
+
// `useCss` keeps colors as inline styles; semantic tags (<b>, <i>…) otherwise.
|
|
2606
|
+
document.execCommand('styleWithCSS', false, String(useCss));
|
|
2607
|
+
document.execCommand(command, false, value);
|
|
2608
|
+
this.onInput();
|
|
2720
2609
|
}
|
|
2721
|
-
|
|
2722
|
-
if (this.
|
|
2723
|
-
|
|
2724
|
-
|
|
2725
|
-
|
|
2726
|
-
|
|
2727
|
-
|
|
2610
|
+
restoreSelection() {
|
|
2611
|
+
if (!this.savedRange)
|
|
2612
|
+
return;
|
|
2613
|
+
const selection = window.getSelection();
|
|
2614
|
+
if (!selection)
|
|
2615
|
+
return;
|
|
2616
|
+
selection.removeAllRanges();
|
|
2617
|
+
selection.addRange(this.savedRange);
|
|
2728
2618
|
}
|
|
2729
2619
|
static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextEditor, deps: [], target: i0.ɵɵFactoryTarget.Component });
|
|
2730
|
-
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: TextEditor, isStandalone: true, selector: "app-text-editor", inputs: { header: "header", media: "media", link: "link", placeholder: "placeholder", readonly: "readonly" },
|
|
2620
|
+
static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "20.1.3", type: TextEditor, isStandalone: true, selector: "app-text-editor", inputs: { header: "header", media: "media", link: "link", placeholder: "placeholder", readonly: "readonly" }, host: { listeners: { "document:selectionchange": "saveSelection()" } }, providers: [
|
|
2621
|
+
{
|
|
2622
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2623
|
+
useExisting: forwardRef(() => TextEditor),
|
|
2624
|
+
multi: true,
|
|
2625
|
+
},
|
|
2626
|
+
], viewQueries: [{ propertyName: "editorRef", first: true, predicate: ["editor"], descendants: true }], ngImport: i0, template: "<div class=\"text-editor\" [ngClass]=\"{'readonly': readonly || disabled}\">\n <div class=\"toolbar\" *ngIf=\"!readonly && !disabled\">\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"applyInlineStyle('bold')\" tooltip=\"Bold\">\n <i class=\"he he-bold\"></i>\n </button>\n <button type=\"button\" (click)=\"applyInlineStyle('italic')\" tooltip=\"italic\">\n <i class=\"he he-italics\"></i>\n </button>\n <button type=\"button\" (click)=\"applyInlineStyle('underline')\" tooltip=\"Underline\">\n <i class=\"he he-underline\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"header\">\n <button type=\"button\" (click)=\"applyBlockFormat('h1')\" tooltip=\"Header 1\">\n <i class=\"he he-heading-1\"></i>\n </button>\n <button type=\"button\" (click)=\"applyBlockFormat('h2')\" tooltip=\"Header 2\">\n <i class=\"he he-heading-2\"></i>\n </button>\n </div>\n <div class=\"toolbar-items more-gap\">\n <div class=\"input-wrap\" tooltip=\"Text Color\">\n <i class=\"he he-text-drop\"></i>\n <input type=\"color\" (change)=\"applyColor($event)\" />\n </div>\n <div class=\"input-wrap\" tooltip=\"Background Color\">\n <i class=\"he he-background-drop\"></i>\n <input type=\"color\" (change)=\"applyHighlight($event)\" />\n </div>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"setAlignment('left')\" tooltip=\"Justify Left\">\n <i class=\"he he-left-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('center')\" tooltip=\"Justify Center\">\n <i class=\"he he-center-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('right')\" tooltip=\"Justify Right\">\n <i class=\"he he-right-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('justify')\" tooltip=\"Justify Full\">\n <i class=\"he he-justify\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"indent()\" tooltip=\"Indent\">\n <i class=\"he he-text-indent-left\"></i>\n </button>\n <button type=\"button\" (click)=\"outdent()\" tooltip=\"Outdent\">\n <i class=\"he he-text-indent-right\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"media\">\n <button type=\"button\" (click)=\"applyList('ul')\" tooltip=\"Unordered list\">\n <i class=\"he he-unordered-list\"></i>\n </button>\n <button type=\"button\" (click)=\"applyList('ol')\" tooltip=\"Ordered list\">\n <i class=\"he he-ordered-list\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"link\">\n <button type=\"button\" (click)=\"createLink()\" tooltip=\"Link\">\n <i class=\"he he-link\"></i>\n </button>\n <button type=\"button\" (click)=\"insertImage()\" tooltip=\"Image\">\n <i class=\"he he-image\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"undo()\" tooltip=\"Undo\">\n <i class=\"he he-undo\"></i>\n </button>\n <button type=\"button\" (click)=\"redo()\" tooltip=\"Redo\">\n <i class=\"he he-redo\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"clearFormatting()\" tooltip=\"Clear Formatting\">\n <i class=\"he he-text-clear-format\"></i>\n </button>\n </div>\n </div>\n\n <div #editor class=\"editor-content\" [attr.contenteditable]=\"(readonly || disabled) ? 'false' : 'true'\" (input)=\"onInput()\" (blur)=\"onTouched()\"\n [attr.data-placeholder]=\"placeholder\">\n </div>\n</div>\n", styles: [".text-editor{border:1px solid #ccc}.text-editor .toolbar{gap:.75rem;display:flex;flex-wrap:wrap;background:#f8f8f8;padding:.75rem;border-bottom:1px solid #cccccc}.text-editor .toolbar .toolbar-items{height:18px;display:flex;flex-wrap:wrap;align-items:center;padding-right:.75rem;border-right:1px solid #969090}.text-editor .toolbar .toolbar-items button,.text-editor .toolbar .toolbar-items .input-wrap{cursor:pointer;min-width:25px;min-height:20px}.text-editor .toolbar .toolbar-items button:hover i,.text-editor .toolbar .toolbar-items .input-wrap:hover i{color:#000}.text-editor .toolbar .toolbar-items button{border:0;display:grid;place-items:center;background:transparent}.text-editor .toolbar .toolbar-items button:hover{background:#eee}.text-editor .toolbar .toolbar-items .input-wrap{gap:2px;display:flex;cursor:pointer;align-items:center;flex-direction:column;justify-content:center}.text-editor .toolbar .toolbar-items .input-wrap input[type=color]{width:80%;padding:0;height:2px;border:none;cursor:inherit}.text-editor .toolbar .toolbar-items i{font-size:13px;color:#3c4148}.text-editor .toolbar .toolbar-items i.he-bold{font-weight:600}.text-editor .toolbar .toolbar-items i.he-underline{font-size:14px}.text-editor .toolbar .toolbar-items i.he-text-drop{font-size:12px}.text-editor .toolbar .toolbar-items i.he-background-drop{top:-1px;font-size:12px}.text-editor .toolbar .toolbar-items i.he-heading-1,.text-editor .toolbar .toolbar-items i.he-link,.text-editor .toolbar .toolbar-items i.he-left-align,.text-editor .toolbar .toolbar-items i.he-center-align,.text-editor .toolbar .toolbar-items i.he-right-align,.text-editor .toolbar .toolbar-items i.he-justify{font-size:16px}.text-editor .toolbar .toolbar-items i.he-text-indent-left,.text-editor .toolbar .toolbar-items i.he-text-indent-right{font-size:17px}.text-editor .toolbar .toolbar-items i.he-heading-2{top:1px;font-size:16px}.text-editor .toolbar .toolbar-items i.he-unordered-list,.text-editor .toolbar .toolbar-items i.he-ordered-list{top:-1px;font-size:20px}.text-editor .toolbar .toolbar-items i.he-image{font-size:15px;font-weight:600}.text-editor .toolbar .toolbar-items i.he-redo,.text-editor .toolbar .toolbar-items i.he-undo{top:-1px;font-size:14px}.text-editor .toolbar .toolbar-items.more-gap{gap:5px}.text-editor .toolbar .toolbar-items:last-child{border-right:0}.text-editor .editor-content{outline:none;min-height:200px;position:relative;background:#fff;padding:.75rem}.text-editor .editor-content[data-placeholder]:empty:before{content:attr(data-placeholder);color:#aaa;pointer-events:none;position:absolute;left:.75rem;top:.75rem}\n"], dependencies: [{ kind: "ngmodule", type: CommonModule }, { kind: "directive", type: i1.NgClass, selector: "[ngClass]", inputs: ["class", "ngClass"] }, { kind: "directive", type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }] });
|
|
2731
2627
|
}
|
|
2732
2628
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImport: i0, type: TextEditor, decorators: [{
|
|
2733
2629
|
type: Component,
|
|
2734
|
-
args: [{ selector: 'app-text-editor', imports: [CommonModule,
|
|
2735
|
-
|
|
2630
|
+
args: [{ selector: 'app-text-editor', standalone: true, imports: [CommonModule], providers: [
|
|
2631
|
+
{
|
|
2632
|
+
provide: NG_VALUE_ACCESSOR,
|
|
2633
|
+
useExisting: forwardRef(() => TextEditor),
|
|
2634
|
+
multi: true,
|
|
2635
|
+
},
|
|
2636
|
+
], template: "<div class=\"text-editor\" [ngClass]=\"{'readonly': readonly || disabled}\">\n <div class=\"toolbar\" *ngIf=\"!readonly && !disabled\">\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"applyInlineStyle('bold')\" tooltip=\"Bold\">\n <i class=\"he he-bold\"></i>\n </button>\n <button type=\"button\" (click)=\"applyInlineStyle('italic')\" tooltip=\"italic\">\n <i class=\"he he-italics\"></i>\n </button>\n <button type=\"button\" (click)=\"applyInlineStyle('underline')\" tooltip=\"Underline\">\n <i class=\"he he-underline\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"header\">\n <button type=\"button\" (click)=\"applyBlockFormat('h1')\" tooltip=\"Header 1\">\n <i class=\"he he-heading-1\"></i>\n </button>\n <button type=\"button\" (click)=\"applyBlockFormat('h2')\" tooltip=\"Header 2\">\n <i class=\"he he-heading-2\"></i>\n </button>\n </div>\n <div class=\"toolbar-items more-gap\">\n <div class=\"input-wrap\" tooltip=\"Text Color\">\n <i class=\"he he-text-drop\"></i>\n <input type=\"color\" (change)=\"applyColor($event)\" />\n </div>\n <div class=\"input-wrap\" tooltip=\"Background Color\">\n <i class=\"he he-background-drop\"></i>\n <input type=\"color\" (change)=\"applyHighlight($event)\" />\n </div>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"setAlignment('left')\" tooltip=\"Justify Left\">\n <i class=\"he he-left-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('center')\" tooltip=\"Justify Center\">\n <i class=\"he he-center-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('right')\" tooltip=\"Justify Right\">\n <i class=\"he he-right-align\"></i>\n </button>\n <button type=\"button\" (click)=\"setAlignment('justify')\" tooltip=\"Justify Full\">\n <i class=\"he he-justify\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"indent()\" tooltip=\"Indent\">\n <i class=\"he he-text-indent-left\"></i>\n </button>\n <button type=\"button\" (click)=\"outdent()\" tooltip=\"Outdent\">\n <i class=\"he he-text-indent-right\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"media\">\n <button type=\"button\" (click)=\"applyList('ul')\" tooltip=\"Unordered list\">\n <i class=\"he he-unordered-list\"></i>\n </button>\n <button type=\"button\" (click)=\"applyList('ol')\" tooltip=\"Ordered list\">\n <i class=\"he he-ordered-list\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\" *ngIf=\"link\">\n <button type=\"button\" (click)=\"createLink()\" tooltip=\"Link\">\n <i class=\"he he-link\"></i>\n </button>\n <button type=\"button\" (click)=\"insertImage()\" tooltip=\"Image\">\n <i class=\"he he-image\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"undo()\" tooltip=\"Undo\">\n <i class=\"he he-undo\"></i>\n </button>\n <button type=\"button\" (click)=\"redo()\" tooltip=\"Redo\">\n <i class=\"he he-redo\"></i>\n </button>\n </div>\n <div class=\"toolbar-items\">\n <button type=\"button\" (click)=\"clearFormatting()\" tooltip=\"Clear Formatting\">\n <i class=\"he he-text-clear-format\"></i>\n </button>\n </div>\n </div>\n\n <div #editor class=\"editor-content\" [attr.contenteditable]=\"(readonly || disabled) ? 'false' : 'true'\" (input)=\"onInput()\" (blur)=\"onTouched()\"\n [attr.data-placeholder]=\"placeholder\">\n </div>\n</div>\n", styles: [".text-editor{border:1px solid #ccc}.text-editor .toolbar{gap:.75rem;display:flex;flex-wrap:wrap;background:#f8f8f8;padding:.75rem;border-bottom:1px solid #cccccc}.text-editor .toolbar .toolbar-items{height:18px;display:flex;flex-wrap:wrap;align-items:center;padding-right:.75rem;border-right:1px solid #969090}.text-editor .toolbar .toolbar-items button,.text-editor .toolbar .toolbar-items .input-wrap{cursor:pointer;min-width:25px;min-height:20px}.text-editor .toolbar .toolbar-items button:hover i,.text-editor .toolbar .toolbar-items .input-wrap:hover i{color:#000}.text-editor .toolbar .toolbar-items button{border:0;display:grid;place-items:center;background:transparent}.text-editor .toolbar .toolbar-items button:hover{background:#eee}.text-editor .toolbar .toolbar-items .input-wrap{gap:2px;display:flex;cursor:pointer;align-items:center;flex-direction:column;justify-content:center}.text-editor .toolbar .toolbar-items .input-wrap input[type=color]{width:80%;padding:0;height:2px;border:none;cursor:inherit}.text-editor .toolbar .toolbar-items i{font-size:13px;color:#3c4148}.text-editor .toolbar .toolbar-items i.he-bold{font-weight:600}.text-editor .toolbar .toolbar-items i.he-underline{font-size:14px}.text-editor .toolbar .toolbar-items i.he-text-drop{font-size:12px}.text-editor .toolbar .toolbar-items i.he-background-drop{top:-1px;font-size:12px}.text-editor .toolbar .toolbar-items i.he-heading-1,.text-editor .toolbar .toolbar-items i.he-link,.text-editor .toolbar .toolbar-items i.he-left-align,.text-editor .toolbar .toolbar-items i.he-center-align,.text-editor .toolbar .toolbar-items i.he-right-align,.text-editor .toolbar .toolbar-items i.he-justify{font-size:16px}.text-editor .toolbar .toolbar-items i.he-text-indent-left,.text-editor .toolbar .toolbar-items i.he-text-indent-right{font-size:17px}.text-editor .toolbar .toolbar-items i.he-heading-2{top:1px;font-size:16px}.text-editor .toolbar .toolbar-items i.he-unordered-list,.text-editor .toolbar .toolbar-items i.he-ordered-list{top:-1px;font-size:20px}.text-editor .toolbar .toolbar-items i.he-image{font-size:15px;font-weight:600}.text-editor .toolbar .toolbar-items i.he-redo,.text-editor .toolbar .toolbar-items i.he-undo{top:-1px;font-size:14px}.text-editor .toolbar .toolbar-items.more-gap{gap:5px}.text-editor .toolbar .toolbar-items:last-child{border-right:0}.text-editor .editor-content{outline:none;min-height:200px;position:relative;background:#fff;padding:.75rem}.text-editor .editor-content[data-placeholder]:empty:before{content:attr(data-placeholder);color:#aaa;pointer-events:none;position:absolute;left:.75rem;top:.75rem}\n"] }]
|
|
2736
2637
|
}], propDecorators: { header: [{
|
|
2737
2638
|
type: Input
|
|
2738
2639
|
}], media: [{
|
|
@@ -2746,6 +2647,9 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.1.3", ngImpor
|
|
|
2746
2647
|
}], editorRef: [{
|
|
2747
2648
|
type: ViewChild,
|
|
2748
2649
|
args: ['editor']
|
|
2650
|
+
}], saveSelection: [{
|
|
2651
|
+
type: HostListener,
|
|
2652
|
+
args: ['document:selectionchange']
|
|
2749
2653
|
}] } });
|
|
2750
2654
|
|
|
2751
2655
|
class TextareaControl {
|