@flogeez/angular-tiptap-editor 0.2.4 → 0.2.6

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/README.md CHANGED
@@ -6,7 +6,7 @@ A modern, customizable rich-text editor for Angular applications, built with Tip
6
6
 
7
7
  ## 🚀 Features
8
8
 
9
- - **Modern Angular**: Built with Angular 19+ and standalone components
9
+ - **Modern Angular**: Built with Angular 18+
10
10
  - **Rich Text Editing**: Powered by Tiptap with extensive formatting options
11
11
  - **Internationalization**: Full i18n support (English & French) with auto-detection
12
12
  - **Customizable**: Highly configurable toolbar, bubble menus, and slash commands
@@ -1,6 +1,6 @@
1
1
  import * as i0 from '@angular/core';
2
- import { input, output, Component, signal, computed, Injectable, inject, effect, ViewChild, viewChild, forwardRef } from '@angular/core';
3
- import { NG_VALUE_ACCESSOR } from '@angular/forms';
2
+ import { input, output, Component, signal, computed, Injectable, inject, effect, ViewChild, Directive, viewChild, DestroyRef } from '@angular/core';
3
+ import { takeUntilDestroyed } from '@angular/core/rxjs-interop';
4
4
  import { Node, nodeInputRule, mergeAttributes, Extension, Editor } from '@tiptap/core';
5
5
  import StarterKit from '@tiptap/starter-kit';
6
6
  import Placeholder from '@tiptap/extension-placeholder';
@@ -16,6 +16,8 @@ import { Plugin, PluginKey } from '@tiptap/pm/state';
16
16
  import { DecorationSet, Decoration } from '@tiptap/pm/view';
17
17
  import tippy from 'tippy.js';
18
18
  import { Plugin as Plugin$1, PluginKey as PluginKey$1 } from 'prosemirror-state';
19
+ import { NG_VALUE_ACCESSOR, NgControl } from '@angular/forms';
20
+ import { tap } from 'rxjs';
19
21
 
20
22
  const ResizableImage = Node.create({
21
23
  name: "resizableImage",
@@ -3495,6 +3497,33 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3495
3497
  args: ["menuRef", { static: false }]
3496
3498
  }] } });
3497
3499
 
3500
+ class NoopValueAccessorDirective {
3501
+ writeValue(obj) { }
3502
+ registerOnChange(fn) { }
3503
+ registerOnTouched(fn) { }
3504
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: NoopValueAccessorDirective, deps: [], target: i0.ɵɵFactoryTarget.Directive }); }
3505
+ static { this.ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "20.0.0", type: NoopValueAccessorDirective, isStandalone: true, providers: [
3506
+ {
3507
+ provide: NG_VALUE_ACCESSOR,
3508
+ multi: true,
3509
+ useExisting: NoopValueAccessorDirective,
3510
+ },
3511
+ ], ngImport: i0 }); }
3512
+ }
3513
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: NoopValueAccessorDirective, decorators: [{
3514
+ type: Directive,
3515
+ args: [{
3516
+ standalone: true,
3517
+ providers: [
3518
+ {
3519
+ provide: NG_VALUE_ACCESSOR,
3520
+ multi: true,
3521
+ useExisting: NoopValueAccessorDirective,
3522
+ },
3523
+ ],
3524
+ }]
3525
+ }] });
3526
+
3498
3527
  // Configuration par défaut de la toolbar
3499
3528
  const DEFAULT_TOOLBAR_CONFIG = {
3500
3529
  bold: true,
@@ -3546,8 +3575,7 @@ const DEFAULT_IMAGE_BUBBLE_MENU_CONFIG = {
3546
3575
  separator: true,
3547
3576
  };
3548
3577
  class AngularTiptapEditorComponent {
3549
- constructor(imageService) {
3550
- this.imageService = imageService;
3578
+ constructor() {
3551
3579
  // Nouveaux inputs avec signal
3552
3580
  this.content = input("");
3553
3581
  this.placeholder = input("");
@@ -3647,10 +3675,11 @@ class AngularTiptapEditorComponent {
3647
3675
  // Configuration par défaut si aucune n'est fournie
3648
3676
  return { commands: undefined }; // Le composant utilisera DEFAULT_SLASH_COMMANDS
3649
3677
  });
3650
- // ControlValueAccessor implementation
3651
- this.onChange = (value) => { };
3652
- this.onTouched = () => { };
3678
+ this._destroyRef = inject(DestroyRef);
3679
+ // NgControl pour gérer les FormControls
3680
+ this.ngControl = inject(NgControl, { self: true, optional: true });
3653
3681
  this.i18nService = inject(TiptapI18nService);
3682
+ this.imageService = inject(ImageService);
3654
3683
  // Effet pour gérer le changement de langue
3655
3684
  effect(() => {
3656
3685
  const locale = this.locale();
@@ -3690,14 +3719,11 @@ class AngularTiptapEditorComponent {
3690
3719
  }
3691
3720
  });
3692
3721
  }
3693
- ngOnInit() {
3694
- // L'initialisation se fait maintenant dans ngAfterViewInit
3695
- }
3696
3722
  ngAfterViewInit() {
3697
- // Attendre que la vue soit complètement initialisée avant de créer l'éditeur
3698
- setTimeout(() => {
3699
- this.initEditor();
3700
- }, 0);
3723
+ // La vue est déjà complètement initialisée dans ngAfterViewInit
3724
+ this.initEditor();
3725
+ // S'abonner aux changements du FormControl
3726
+ this.setupFormControlSubscription();
3701
3727
  }
3702
3728
  ngOnDestroy() {
3703
3729
  const currentEditor = this.editor();
@@ -3764,18 +3790,18 @@ class AngularTiptapEditorComponent {
3764
3790
  onUpdate: ({ editor, transaction }) => {
3765
3791
  const html = editor.getHTML();
3766
3792
  this.contentChange.emit(html);
3767
- // Defer the onChange call to prevent ExpressionChangedAfterItHasBeenCheckedError
3768
- Promise.resolve().then(() => {
3769
- this.onChange(html);
3770
- });
3793
+ // Mettre à jour le FormControl si il existe (dans le prochain cycle)
3794
+ if (this.ngControl?.control) {
3795
+ setTimeout(() => {
3796
+ this.ngControl.control.setValue(html, {
3797
+ emitEvent: false,
3798
+ });
3799
+ }, 0);
3800
+ }
3771
3801
  this.editorUpdate.emit({ editor, transaction });
3772
3802
  this.updateCharacterCount(editor);
3773
3803
  },
3774
- onSelectionUpdate: ({ editor, transaction }) => {
3775
- // Note: La mise à jour des états des boutons est maintenant gérée par TiptapBubbleMenuComponent
3776
- },
3777
3804
  onCreate: ({ editor }) => {
3778
- this.editor.set(editor);
3779
3805
  this.editorCreated.emit(editor);
3780
3806
  this.updateCharacterCount(editor);
3781
3807
  // Marquer l'éditeur comme complètement initialisé après un court délai
@@ -3788,10 +3814,20 @@ class AngularTiptapEditorComponent {
3788
3814
  this.editorFocus.emit({ editor, event });
3789
3815
  },
3790
3816
  onBlur: ({ editor, event }) => {
3791
- this.onTouched();
3817
+ // Marquer le FormControl comme touché si il existe
3818
+ if (this.ngControl?.control) {
3819
+ this.ngControl.control.markAsTouched();
3820
+ }
3792
3821
  this.editorBlur.emit({ editor, event });
3793
3822
  },
3794
3823
  });
3824
+ // Stocker la référence de l'éditeur immédiatement
3825
+ this.editor.set(newEditor);
3826
+ // Vérifier si on a un contenu initial via l'input content()
3827
+ const initialContent = this.content();
3828
+ if (initialContent) {
3829
+ this.setContent(initialContent, false);
3830
+ }
3795
3831
  }
3796
3832
  updateCharacterCount(editor) {
3797
3833
  if (this.showCharacterCount() && editor.storage["characterCount"]) {
@@ -3880,19 +3916,37 @@ class AngularTiptapEditorComponent {
3880
3916
  clearContent() {
3881
3917
  this.editor()?.commands.clearContent();
3882
3918
  }
3883
- // ControlValueAccessor methods
3884
- writeValue(value) {
3885
- const currentEditor = this.editor();
3886
- if (currentEditor && value !== currentEditor.getHTML()) {
3887
- this.setContent(value || "", false);
3919
+ normalizeHTML(html) {
3920
+ if (!html)
3921
+ return "";
3922
+ // Normaliser les espaces et retours à la ligne pour une comparaison fiable
3923
+ return html
3924
+ .replace(/\s+/g, " ") // Remplacer espaces multiples par un seul
3925
+ .replace(/>\s+</g, "><") // Supprimer espaces entre balises
3926
+ .replace(/<br\s*\/?>/gi, "<br>") // Normaliser les <br>
3927
+ .trim();
3928
+ }
3929
+ checkInitialFormValue() {
3930
+ // Vérifier si on a une valeur initiale dans le FormControl
3931
+ if (this.ngControl?.control?.value) {
3932
+ console.log("Found initial FormControl value:", this.ngControl.control.value);
3933
+ this.setContent(this.ngControl.control.value, false);
3888
3934
  }
3889
3935
  }
3890
- registerOnChange(fn) {
3891
- this.onChange = fn;
3892
- }
3893
- registerOnTouched(fn) {
3894
- this.onTouched = fn;
3936
+ setupFormControlSubscription() {
3937
+ const control = this.ngControl?.control;
3938
+ if (control) {
3939
+ control.valueChanges
3940
+ .pipe(tap((value) => {
3941
+ const editor = this.editor();
3942
+ if (editor && value !== editor.getHTML()) {
3943
+ this.setContent(value, false);
3944
+ }
3945
+ }), takeUntilDestroyed(this._destroyRef))
3946
+ .subscribe();
3947
+ }
3895
3948
  }
3949
+ // Méthode pour gérer l'état disabled
3896
3950
  setDisabledState(isDisabled) {
3897
3951
  const currentEditor = this.editor();
3898
3952
  if (currentEditor) {
@@ -3917,14 +3971,8 @@ class AngularTiptapEditorComponent {
3917
3971
  }, 0);
3918
3972
  }
3919
3973
  }
3920
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, deps: [{ token: ImageService }], target: i0.ɵɵFactoryTarget.Component }); }
3921
- static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, slashCommandsConfig: { classPropertyName: "slashCommandsConfig", publicName: "slashCommandsConfig", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur" }, providers: [
3922
- {
3923
- provide: NG_VALUE_ACCESSOR,
3924
- useExisting: forwardRef(() => AngularTiptapEditorComponent),
3925
- multi: true,
3926
- },
3927
- ], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
3974
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3975
+ static { this.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "17.0.0", version: "20.0.0", type: AngularTiptapEditorComponent, isStandalone: true, selector: "angular-tiptap-editor", inputs: { content: { classPropertyName: "content", publicName: "content", isSignal: true, isRequired: false, transformFunction: null }, placeholder: { classPropertyName: "placeholder", publicName: "placeholder", isSignal: true, isRequired: false, transformFunction: null }, editable: { classPropertyName: "editable", publicName: "editable", isSignal: true, isRequired: false, transformFunction: null }, minHeight: { classPropertyName: "minHeight", publicName: "minHeight", isSignal: true, isRequired: false, transformFunction: null }, height: { classPropertyName: "height", publicName: "height", isSignal: true, isRequired: false, transformFunction: null }, maxHeight: { classPropertyName: "maxHeight", publicName: "maxHeight", isSignal: true, isRequired: false, transformFunction: null }, showToolbar: { classPropertyName: "showToolbar", publicName: "showToolbar", isSignal: true, isRequired: false, transformFunction: null }, showCharacterCount: { classPropertyName: "showCharacterCount", publicName: "showCharacterCount", isSignal: true, isRequired: false, transformFunction: null }, maxCharacters: { classPropertyName: "maxCharacters", publicName: "maxCharacters", isSignal: true, isRequired: false, transformFunction: null }, enableOfficePaste: { classPropertyName: "enableOfficePaste", publicName: "enableOfficePaste", isSignal: true, isRequired: false, transformFunction: null }, enableSlashCommands: { classPropertyName: "enableSlashCommands", publicName: "enableSlashCommands", isSignal: true, isRequired: false, transformFunction: null }, slashCommandsConfig: { classPropertyName: "slashCommandsConfig", publicName: "slashCommandsConfig", isSignal: true, isRequired: false, transformFunction: null }, locale: { classPropertyName: "locale", publicName: "locale", isSignal: true, isRequired: false, transformFunction: null }, showBubbleMenu: { classPropertyName: "showBubbleMenu", publicName: "showBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, bubbleMenu: { classPropertyName: "bubbleMenu", publicName: "bubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, showImageBubbleMenu: { classPropertyName: "showImageBubbleMenu", publicName: "showImageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, imageBubbleMenu: { classPropertyName: "imageBubbleMenu", publicName: "imageBubbleMenu", isSignal: true, isRequired: false, transformFunction: null }, toolbar: { classPropertyName: "toolbar", publicName: "toolbar", isSignal: true, isRequired: false, transformFunction: null }, imageUpload: { classPropertyName: "imageUpload", publicName: "imageUpload", isSignal: true, isRequired: false, transformFunction: null } }, outputs: { contentChange: "contentChange", editorCreated: "editorCreated", editorUpdate: "editorUpdate", editorFocus: "editorFocus", editorBlur: "editorBlur" }, viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], hostDirectives: [{ directive: NoopValueAccessorDirective }], ngImport: i0, template: `
3928
3976
  <div class="tiptap-editor">
3929
3977
  <!-- Toolbar -->
3930
3978
  @if (showToolbar() && editor()) {
@@ -3993,7 +4041,7 @@ class AngularTiptapEditorComponent {
3993
4041
  }
3994
4042
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, decorators: [{
3995
4043
  type: Component,
3996
- args: [{ selector: "angular-tiptap-editor", standalone: true, imports: [
4044
+ args: [{ selector: "angular-tiptap-editor", standalone: true, hostDirectives: [NoopValueAccessorDirective], imports: [
3997
4045
  TiptapToolbarComponent,
3998
4046
  TiptapImageUploadComponent,
3999
4047
  TiptapBubbleMenuComponent,
@@ -4064,14 +4112,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4064
4112
  </div>
4065
4113
  }
4066
4114
  </div>
4067
- `, providers: [
4068
- {
4069
- provide: NG_VALUE_ACCESSOR,
4070
- useExisting: forwardRef(() => AngularTiptapEditorComponent),
4071
- multi: true,
4072
- },
4073
- ], styles: [".tiptap-editor{border:2px solid #e2e8f0;border-radius:8px;background:#fff;box-shadow:0 2px 4px #0000001a;overflow:hidden;transition:border-color .2s ease}.tiptap-editor:focus-within{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a}.tiptap-content{padding:16px;min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative}.tiptap-content.drag-over{background:#f0f8ff;border:2px dashed #3182ce}.character-count{padding:8px 16px;font-size:12px;color:#718096;text-align:right;border-top:1px solid #e2e8f0;background:#f8f9fa}.image-upload-container{position:relative;display:inline-block}:host ::ng-deep .ProseMirror{outline:none;line-height:1.6;color:#2d3748;min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid #e2e8f0;margin:1em 0;font-style:italic;background:#f8f9fa;padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:#f1f5f9;padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:#1a202c;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:#a0aec0;pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:8px}:host ::ng-deep .ProseMirror img:hover{border-color:#e2e8f0;box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a;transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid #6366f1;outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px #6366f11a}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:#3b82f6;border:2px solid white;border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:#2563eb;box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:#1d4ed8}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}\n"] }]
4074
- }], ctorParameters: () => [{ type: ImageService }] });
4115
+ `, styles: [".tiptap-editor{border:2px solid #e2e8f0;border-radius:8px;background:#fff;box-shadow:0 2px 4px #0000001a;overflow:hidden;transition:border-color .2s ease}.tiptap-editor:focus-within{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a}.tiptap-content{padding:16px;min-height:var(--editor-min-height, 200px);height:var(--editor-height, auto);max-height:var(--editor-max-height, none);overflow-y:var(--editor-overflow, visible);outline:none;position:relative}.tiptap-content.drag-over{background:#f0f8ff;border:2px dashed #3182ce}.character-count{padding:8px 16px;font-size:12px;color:#718096;text-align:right;border-top:1px solid #e2e8f0;background:#f8f9fa}.image-upload-container{position:relative;display:inline-block}:host ::ng-deep .ProseMirror{outline:none;line-height:1.6;color:#2d3748;min-height:100%;height:100%;word-wrap:break-word;overflow-wrap:break-word}:host ::ng-deep .ProseMirror h1{font-size:2em;font-weight:700;margin-top:0;margin-bottom:.5em}:host ::ng-deep .ProseMirror h2{font-size:1.5em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror h3{font-size:1.25em;font-weight:700;margin-top:1em;margin-bottom:.5em}:host ::ng-deep .ProseMirror p{margin:.5em 0}:host ::ng-deep .ProseMirror ul,:host ::ng-deep .ProseMirror ol{padding-left:2em;margin:.5em 0}:host ::ng-deep .ProseMirror blockquote{border-left:4px solid #e2e8f0;margin:1em 0;font-style:italic;background:#f8f9fa;padding:.5em 1em;border-radius:0 4px 4px 0}:host ::ng-deep .ProseMirror code{background:#f1f5f9;padding:.2em .4em;border-radius:3px;font-family:Monaco,Consolas,monospace;font-size:.9em}:host ::ng-deep .ProseMirror pre{background:#1a202c;color:#e2e8f0;padding:1em;border-radius:6px;overflow-x:auto;margin:1em 0}:host ::ng-deep .ProseMirror pre code{background:none;color:inherit;padding:0}:host ::ng-deep .ProseMirror p.is-editor-empty:first-child:before{content:attr(data-placeholder);color:#a0aec0;pointer-events:none;float:left;height:0}:host ::ng-deep .ProseMirror[contenteditable=false]{pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img{cursor:default;pointer-events:none}:host ::ng-deep .ProseMirror[contenteditable=false] img:hover{transform:none;box-shadow:0 2px 8px #0000001a}:host ::ng-deep .ProseMirror[contenteditable=false] img.ProseMirror-selectednode{outline:none}:host ::ng-deep .ProseMirror img{position:relative;display:inline-block;max-width:100%;height:auto;cursor:pointer;transition:all .2s ease;border:2px solid transparent;border-radius:8px}:host ::ng-deep .ProseMirror img:hover{border-color:#e2e8f0;box-shadow:0 2px 4px #0000001a}:host ::ng-deep .ProseMirror img.ProseMirror-selectednode{border-color:#3182ce;box-shadow:0 0 0 3px #3182ce1a;transition:all .2s ease}:host ::ng-deep .ProseMirror .tiptap-image{max-width:100%;height:auto;border-radius:16px;box-shadow:0 4px 20px #00000014;margin:.5em 0;cursor:pointer;transition:all .3s cubic-bezier(.4,0,.2,1);display:block;filter:brightness(1) contrast(1)}:host ::ng-deep .ProseMirror .tiptap-image:hover{box-shadow:0 8px 30px #0000001f;filter:brightness(1.02) contrast(1.02)}:host ::ng-deep .ProseMirror .tiptap-image.ProseMirror-selectednode{outline:2px solid #6366f1;outline-offset:2px;border-radius:16px;box-shadow:0 0 0 4px #6366f11a}:host ::ng-deep .image-container{margin:.5em 0;text-align:center;border-radius:16px;overflow:hidden;transition:all .3s cubic-bezier(.4,0,.2,1)}:host ::ng-deep .image-container.image-align-left{text-align:left}:host ::ng-deep .image-container.image-align-center{text-align:center}:host ::ng-deep .image-container.image-align-right{text-align:right}:host ::ng-deep .image-container img{display:inline-block;max-width:100%;height:auto;border-radius:16px}:host ::ng-deep .resizable-image-container{position:relative;display:inline-block;margin:.5em 0}:host ::ng-deep .resize-controls{position:absolute;inset:0;pointer-events:none;z-index:1000}:host ::ng-deep .resize-handle{position:absolute;width:12px;height:12px;background:#3b82f6;border:2px solid white;border-radius:50%;pointer-events:all;cursor:pointer;z-index:1001;transition:all .15s ease;box-shadow:0 2px 6px #0003}:host ::ng-deep .resize-handle:hover{background:#2563eb;box-shadow:0 3px 8px #0000004d}:host ::ng-deep .resize-handle:active{background:#1d4ed8}:host ::ng-deep .resize-handle-n:hover,:host ::ng-deep .resize-handle-s:hover{transform:translate(-50%) scale(1.2)}:host ::ng-deep .resize-handle-w:hover,:host ::ng-deep .resize-handle-e:hover{transform:translateY(-50%) scale(1.2)}:host ::ng-deep .resize-handle-n:active,:host ::ng-deep .resize-handle-s:active{transform:translate(-50%) scale(.9)}:host ::ng-deep .resize-handle-w:active,:host ::ng-deep .resize-handle-e:active{transform:translateY(-50%) scale(.9)}:host ::ng-deep .resize-handle-nw:hover,:host ::ng-deep .resize-handle-ne:hover,:host ::ng-deep .resize-handle-sw:hover,:host ::ng-deep .resize-handle-se:hover{transform:scale(1.2)}:host ::ng-deep .resize-handle-nw:active,:host ::ng-deep .resize-handle-ne:active,:host ::ng-deep .resize-handle-sw:active,:host ::ng-deep .resize-handle-se:active{transform:scale(.9)}:host ::ng-deep .resize-handle-nw{top:0;left:-6px;cursor:nw-resize}:host ::ng-deep .resize-handle-n{top:0;left:50%;transform:translate(-50%);cursor:n-resize}:host ::ng-deep .resize-handle-ne{top:0;right:-6px;cursor:ne-resize}:host ::ng-deep .resize-handle-w{top:50%;left:-6px;transform:translateY(-50%);cursor:w-resize}:host ::ng-deep .resize-handle-e{top:50%;right:-6px;transform:translateY(-50%);cursor:e-resize}:host ::ng-deep .resize-handle-sw{bottom:0;left:-6px;cursor:sw-resize}:host ::ng-deep .resize-handle-s{bottom:0;left:50%;transform:translate(-50%);cursor:s-resize}:host ::ng-deep .resize-handle-se{bottom:0;right:-6px;cursor:se-resize}:host ::ng-deep body.resizing{-webkit-user-select:none;user-select:none;cursor:crosshair}:host ::ng-deep body.resizing .ProseMirror{pointer-events:none}:host ::ng-deep body.resizing .ProseMirror .tiptap-image{pointer-events:none}:host ::ng-deep .image-size-info{position:absolute;bottom:-20px;left:50%;transform:translate(-50%);background:#000c;color:#fff;padding:2px 6px;border-radius:3px;font-size:11px;white-space:nowrap;opacity:0;transition:opacity .2s ease}:host ::ng-deep .image-container:hover .image-size-info{opacity:1}\n"] }]
4116
+ }], ctorParameters: () => [] });
4075
4117
 
4076
4118
  /**
4077
4119
  * Factory function pour créer les slash commands traduits
@@ -4236,5 +4278,5 @@ const SLASH_COMMAND_KEYS = {
4236
4278
  * Generated bundle index. Do not edit.
4237
4279
  */
4238
4280
 
4239
- export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS, DEFAULT_TOOLBAR_CONFIG, TiptapI18nService, createI18nSlashCommands };
4281
+ export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS, DEFAULT_TOOLBAR_CONFIG, NoopValueAccessorDirective, TiptapI18nService, createI18nSlashCommands };
4240
4282
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map