@flogeez/angular-tiptap-editor 0.2.5 → 0.2.7

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.
@@ -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 { concat, defer, of, 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("");
@@ -3587,71 +3615,37 @@ class AngularTiptapEditorComponent {
3587
3615
  // Computed pour les états de l'éditeur
3588
3616
  this.isEditorReady = computed(() => this.editor() !== null);
3589
3617
  // Computed pour la configuration de la toolbar
3590
- this.toolbarConfig = computed(() => {
3591
- const userConfig = this.toolbar();
3592
- // Si aucune configuration n'est fournie, utiliser la configuration par défaut
3593
- if (Object.keys(userConfig).length === 0) {
3594
- return DEFAULT_TOOLBAR_CONFIG;
3595
- }
3596
- // Sinon, utiliser uniquement la configuration fournie par l'utilisateur
3597
- return userConfig;
3598
- });
3618
+ this.toolbarConfig = computed(() => Object.keys(this.toolbar()).length === 0
3619
+ ? DEFAULT_TOOLBAR_CONFIG
3620
+ : this.toolbar());
3599
3621
  // Computed pour la configuration du bubble menu
3600
- this.bubbleMenuConfig = computed(() => {
3601
- const userConfig = this.bubbleMenu();
3602
- // Si aucune configuration n'est fournie, utiliser la configuration par défaut
3603
- if (Object.keys(userConfig).length === 0) {
3604
- return DEFAULT_BUBBLE_MENU_CONFIG;
3605
- }
3606
- // Sinon, fusionner avec la configuration par défaut
3607
- return {
3608
- ...DEFAULT_BUBBLE_MENU_CONFIG,
3609
- ...userConfig,
3610
- };
3611
- });
3622
+ this.bubbleMenuConfig = computed(() => Object.keys(this.bubbleMenu()).length === 0
3623
+ ? DEFAULT_BUBBLE_MENU_CONFIG
3624
+ : { ...DEFAULT_BUBBLE_MENU_CONFIG, ...this.bubbleMenu() });
3612
3625
  // Computed pour la configuration du bubble menu image
3613
- this.imageBubbleMenuConfig = computed(() => {
3614
- const userConfig = this.imageBubbleMenu();
3615
- // Si aucune configuration n'est fournie, utiliser la configuration par défaut
3616
- if (Object.keys(userConfig).length === 0) {
3617
- return DEFAULT_IMAGE_BUBBLE_MENU_CONFIG;
3618
- }
3619
- // Sinon, fusionner avec la configuration par défaut
3620
- return {
3621
- ...DEFAULT_IMAGE_BUBBLE_MENU_CONFIG,
3622
- ...userConfig,
3623
- };
3624
- });
3626
+ this.imageBubbleMenuConfig = computed(() => Object.keys(this.imageBubbleMenu()).length === 0
3627
+ ? DEFAULT_IMAGE_BUBBLE_MENU_CONFIG
3628
+ : { ...DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, ...this.imageBubbleMenu() });
3625
3629
  // Computed pour la configuration de l'upload d'images
3626
- this.imageUploadConfig = computed(() => {
3627
- const userConfig = this.imageUpload();
3628
- return {
3629
- maxSize: 5,
3630
- maxWidth: 1920,
3631
- maxHeight: 1080,
3632
- allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"],
3633
- enableDragDrop: true,
3634
- showPreview: true,
3635
- multiple: false,
3636
- compressImages: true,
3637
- quality: 0.8,
3638
- ...userConfig,
3639
- };
3640
- });
3630
+ this.imageUploadConfig = computed(() => ({
3631
+ maxSize: 5,
3632
+ maxWidth: 1920,
3633
+ maxHeight: 1080,
3634
+ allowedTypes: ["image/jpeg", "image/png", "image/gif", "image/webp"],
3635
+ enableDragDrop: true,
3636
+ showPreview: true,
3637
+ multiple: false,
3638
+ compressImages: true,
3639
+ quality: 0.8,
3640
+ ...this.imageUpload(),
3641
+ }));
3641
3642
  // Computed pour la configuration des slash commands
3642
- this.slashCommandsConfigComputed = computed(() => {
3643
- const userConfig = this.slashCommandsConfig();
3644
- if (userConfig) {
3645
- return userConfig;
3646
- }
3647
- // Configuration par défaut si aucune n'est fournie
3648
- return { commands: undefined }; // Le composant utilisera DEFAULT_SLASH_COMMANDS
3649
- });
3650
- // ControlValueAccessor implementation
3651
- this.onChange = (value) => { };
3652
- this.onTouched = () => { };
3653
- this.pendingFormValue = null;
3643
+ this.slashCommandsConfigComputed = computed(() => this.slashCommandsConfig() ?? { commands: undefined });
3644
+ this._destroyRef = inject(DestroyRef);
3645
+ // NgControl pour gérer les FormControls
3646
+ this.ngControl = inject(NgControl, { self: true, optional: true });
3654
3647
  this.i18nService = inject(TiptapI18nService);
3648
+ this.imageService = inject(ImageService);
3655
3649
  // Effet pour gérer le changement de langue
3656
3650
  effect(() => {
3657
3651
  const locale = this.locale();
@@ -3663,7 +3657,12 @@ class AngularTiptapEditorComponent {
3663
3657
  effect(() => {
3664
3658
  const editor = this.editor();
3665
3659
  const content = this.content();
3660
+ const hasFormControl = !!this.ngControl?.control;
3661
+ // Ne pas écraser le contenu si on a un FormControl et que le content est vide
3666
3662
  if (editor && content !== undefined && content !== editor.getHTML()) {
3663
+ if (hasFormControl && !content) {
3664
+ return;
3665
+ }
3667
3666
  this.setContent(content, false);
3668
3667
  }
3669
3668
  });
@@ -3691,12 +3690,11 @@ class AngularTiptapEditorComponent {
3691
3690
  }
3692
3691
  });
3693
3692
  }
3694
- ngOnInit() {
3695
- // L'initialisation se fait maintenant dans ngAfterViewInit
3696
- }
3697
3693
  ngAfterViewInit() {
3698
3694
  // La vue est déjà complètement initialisée dans ngAfterViewInit
3699
3695
  this.initEditor();
3696
+ // S'abonner aux changements du FormControl
3697
+ this.setupFormControlSubscription();
3700
3698
  }
3701
3699
  ngOnDestroy() {
3702
3700
  const currentEditor = this.editor();
@@ -3763,16 +3761,17 @@ class AngularTiptapEditorComponent {
3763
3761
  onUpdate: ({ editor, transaction }) => {
3764
3762
  const html = editor.getHTML();
3765
3763
  this.contentChange.emit(html);
3766
- // Defer the onChange call to prevent ExpressionChangedAfterItHasBeenCheckedError
3767
- Promise.resolve().then(() => {
3768
- this.onChange(html);
3769
- });
3764
+ // Mettre à jour le FormControl si il existe (dans le prochain cycle)
3765
+ if (this.ngControl?.control) {
3766
+ setTimeout(() => {
3767
+ this.ngControl.control.setValue(html, {
3768
+ emitEvent: false,
3769
+ });
3770
+ }, 0);
3771
+ }
3770
3772
  this.editorUpdate.emit({ editor, transaction });
3771
3773
  this.updateCharacterCount(editor);
3772
3774
  },
3773
- onSelectionUpdate: ({ editor, transaction }) => {
3774
- // Note: La mise à jour des états des boutons est maintenant gérée par TiptapBubbleMenuComponent
3775
- },
3776
3775
  onCreate: ({ editor }) => {
3777
3776
  this.editorCreated.emit(editor);
3778
3777
  this.updateCharacterCount(editor);
@@ -3786,17 +3785,15 @@ class AngularTiptapEditorComponent {
3786
3785
  this.editorFocus.emit({ editor, event });
3787
3786
  },
3788
3787
  onBlur: ({ editor, event }) => {
3789
- this.onTouched();
3788
+ // Marquer le FormControl comme touché si il existe
3789
+ if (this.ngControl?.control) {
3790
+ this.ngControl.control.markAsTouched();
3791
+ }
3790
3792
  this.editorBlur.emit({ editor, event });
3791
3793
  },
3792
3794
  });
3793
3795
  // Stocker la référence de l'éditeur immédiatement
3794
3796
  this.editor.set(newEditor);
3795
- // Appliquer la valeur du FormControl en attente si elle existe
3796
- if (this.pendingFormValue !== null) {
3797
- this.setContent(this.pendingFormValue, false);
3798
- this.pendingFormValue = null;
3799
- }
3800
3797
  }
3801
3798
  updateCharacterCount(editor) {
3802
3799
  if (this.showCharacterCount() && editor.storage["characterCount"]) {
@@ -3885,24 +3882,21 @@ class AngularTiptapEditorComponent {
3885
3882
  clearContent() {
3886
3883
  this.editor()?.commands.clearContent();
3887
3884
  }
3888
- // ControlValueAccessor methods
3889
- writeValue(value) {
3890
- const currentEditor = this.editor();
3891
- if (currentEditor && value !== currentEditor.getHTML()) {
3892
- this.setContent(value || "", false);
3893
- this.pendingFormValue = null; // Valeur appliquée, plus besoin de la stocker
3894
- }
3895
- else {
3896
- // Éditeur pas encore prêt, stocker la valeur du FormControl pour plus tard
3897
- this.pendingFormValue = value || "";
3885
+ setupFormControlSubscription() {
3886
+ const control = this.ngControl?.control;
3887
+ if (control) {
3888
+ const formValue$ = concat(defer(() => of(control.value)), control.valueChanges);
3889
+ formValue$
3890
+ .pipe(tap((value) => {
3891
+ const editor = this.editor();
3892
+ if (editor) {
3893
+ this.setContent(value, false);
3894
+ }
3895
+ }), takeUntilDestroyed(this._destroyRef))
3896
+ .subscribe();
3898
3897
  }
3899
3898
  }
3900
- registerOnChange(fn) {
3901
- this.onChange = fn;
3902
- }
3903
- registerOnTouched(fn) {
3904
- this.onTouched = fn;
3905
- }
3899
+ // Méthode pour gérer l'état disabled
3906
3900
  setDisabledState(isDisabled) {
3907
3901
  const currentEditor = this.editor();
3908
3902
  if (currentEditor) {
@@ -3927,14 +3921,8 @@ class AngularTiptapEditorComponent {
3927
3921
  }, 0);
3928
3922
  }
3929
3923
  }
3930
- static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, deps: [{ token: ImageService }], target: i0.ɵɵFactoryTarget.Component }); }
3931
- 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: [
3932
- {
3933
- provide: NG_VALUE_ACCESSOR,
3934
- useExisting: forwardRef(() => AngularTiptapEditorComponent),
3935
- multi: true,
3936
- },
3937
- ], viewQueries: [{ propertyName: "editorElement", first: true, predicate: ["editorElement"], descendants: true, isSignal: true }], ngImport: i0, template: `
3924
+ static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Component }); }
3925
+ 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: `
3938
3926
  <div class="tiptap-editor">
3939
3927
  <!-- Toolbar -->
3940
3928
  @if (showToolbar() && editor()) {
@@ -4003,7 +3991,7 @@ class AngularTiptapEditorComponent {
4003
3991
  }
4004
3992
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: AngularTiptapEditorComponent, decorators: [{
4005
3993
  type: Component,
4006
- args: [{ selector: "angular-tiptap-editor", standalone: true, imports: [
3994
+ args: [{ selector: "angular-tiptap-editor", standalone: true, hostDirectives: [NoopValueAccessorDirective], imports: [
4007
3995
  TiptapToolbarComponent,
4008
3996
  TiptapImageUploadComponent,
4009
3997
  TiptapBubbleMenuComponent,
@@ -4074,14 +4062,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4074
4062
  </div>
4075
4063
  }
4076
4064
  </div>
4077
- `, providers: [
4078
- {
4079
- provide: NG_VALUE_ACCESSOR,
4080
- useExisting: forwardRef(() => AngularTiptapEditorComponent),
4081
- multi: true,
4082
- },
4083
- ], 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"] }]
4084
- }], ctorParameters: () => [{ type: ImageService }] });
4065
+ `, 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"] }]
4066
+ }], ctorParameters: () => [] });
4085
4067
 
4086
4068
  /**
4087
4069
  * Factory function pour créer les slash commands traduits
@@ -4246,5 +4228,5 @@ const SLASH_COMMAND_KEYS = {
4246
4228
  * Generated bundle index. Do not edit.
4247
4229
  */
4248
4230
 
4249
- export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS, DEFAULT_TOOLBAR_CONFIG, TiptapI18nService, createI18nSlashCommands };
4231
+ export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS, DEFAULT_TOOLBAR_CONFIG, NoopValueAccessorDirective, TiptapI18nService, createI18nSlashCommands };
4250
4232
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map