@flogeez/angular-tiptap-editor 0.5.4 → 0.6.0

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/index.d.ts CHANGED
@@ -1,6 +1,7 @@
1
- import { Editor, Node, Extension, Mark, EditorOptions } from '@tiptap/core';
1
+ import * as _flogeez_angular_tiptap_editor from '@flogeez/angular-tiptap-editor';
2
2
  import * as _angular_core from '@angular/core';
3
3
  import { AfterViewInit, OnDestroy, ElementRef } from '@angular/core';
4
+ import { Editor, Node, Mark, Extension, EditorOptions } from '@tiptap/core';
4
5
  import { Observable } from 'rxjs';
5
6
  import { ControlValueAccessor } from '@angular/forms';
6
7
 
@@ -390,11 +391,6 @@ declare class TiptapI18nService {
390
391
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<TiptapI18nService>;
391
392
  }
392
393
 
393
- interface CellBubbleMenuConfig$1 {
394
- mergeCells?: boolean;
395
- splitCell?: boolean;
396
- }
397
-
398
394
  interface SlashCommandItem {
399
395
  title: string;
400
396
  description: string;
@@ -486,107 +482,222 @@ interface ImageUploadHandlerResult {
486
482
  */
487
483
  type ImageUploadHandler = (context: ImageUploadContext) => Promise<ImageUploadHandlerResult> | Observable<ImageUploadHandlerResult>;
488
484
  declare class ImageService {
485
+ /** Signals for image state */
489
486
  selectedImage: _angular_core.WritableSignal<ImageData | null>;
490
487
  isImageSelected: _angular_core.Signal<boolean>;
488
+ /** Resizing state */
491
489
  isResizing: _angular_core.WritableSignal<boolean>;
492
490
  private i18n;
493
491
  private readonly t;
492
+ /** Upload state signals */
494
493
  isUploading: _angular_core.WritableSignal<boolean>;
495
494
  uploadProgress: _angular_core.WritableSignal<number>;
496
495
  uploadMessage: _angular_core.WritableSignal<string>;
497
496
  /**
498
- * Custom upload handler for images.
499
- * When set, this handler will be called instead of the default base64 conversion.
500
- * This allows users to implement their own image storage logic.
501
- *
502
- * @example
503
- * ```typescript
504
- * imageService.uploadHandler = async (context) => {
505
- * const formData = new FormData();
506
- * formData.append('image', context.file);
507
- * const response = await fetch('/api/upload', { method: 'POST', body: formData });
508
- * const data = await response.json();
509
- * return { src: data.url };
510
- * };
511
- * ```
497
+ * Custom upload handler.
498
+ * If set, this handler replaces the default base64 conversion.
512
499
  */
513
500
  uploadHandler: ImageUploadHandler | null;
514
501
  private currentEditor;
502
+ /** Select and track an image from the editor */
515
503
  selectImage(editor: Editor): void;
516
504
  clearSelection(): void;
505
+ /** Insert a new image and ensure it's selected */
517
506
  insertImage(editor: Editor, imageData: ImageData): void;
507
+ /** Update attributes of the currently active image */
518
508
  updateImageAttributes(editor: Editor, attributes: Partial<ImageData>): void;
509
+ /** Resize image with optional aspect ratio maintenance */
519
510
  resizeImage(editor: Editor, options: ResizeOptions): void;
520
- resizeImageByPercentage(editor: Editor, percentage: number): void;
511
+ /** Predetermined resize helpers used by UI */
521
512
  resizeImageToSmall(editor: Editor): void;
522
513
  resizeImageToMedium(editor: Editor): void;
523
514
  resizeImageToLarge(editor: Editor): void;
524
515
  resizeImageToOriginal(editor: Editor): void;
525
- resizeImageFreely(editor: Editor, width: number, height: number): void;
516
+ /** Get current image dimensions */
526
517
  getImageDimensions(editor: Editor): {
527
518
  width: number;
528
519
  height: number;
529
520
  } | null;
530
- getNaturalImageDimensions(src: string): Promise<{
531
- width: number;
532
- height: number;
533
- }>;
521
+ /** Remove the selected image */
534
522
  deleteImage(editor: Editor): void;
535
523
  private updateSelectedImage;
524
+ /** Validate file type and size */
536
525
  validateImage(file: File, maxSize?: number): {
537
526
  valid: boolean;
538
527
  error?: string;
539
528
  };
529
+ /** Compress and process image on client side */
540
530
  compressImage(file: File, quality?: number, maxWidth?: number, maxHeight?: number): Promise<ImageUploadResult>;
531
+ /** Core upload logic with progress tracking */
541
532
  private uploadImageWithProgress;
542
- uploadAndInsertImage(editor: Editor, file: File, options?: {
543
- quality?: number;
544
- maxWidth?: number;
545
- maxHeight?: number;
546
- }): Promise<void>;
533
+ private resetUploadState;
534
+ /** Main entry point for file upload and insertion */
535
+ uploadAndInsertImage(editor: Editor, file: File, options?: Record<string, any>): Promise<void>;
536
+ /** Trigger an editor transaction to force decoration update */
547
537
  private forceEditorUpdate;
538
+ /** Generic helper to open file picker and process selection */
548
539
  private selectFileAndProcess;
549
- selectAndUploadImage(editor: Editor, options?: {
550
- quality?: number;
551
- maxWidth?: number;
552
- maxHeight?: number;
553
- accept?: string;
554
- }): Promise<void>;
555
- selectAndReplaceImage(editor: Editor, options?: {
556
- quality?: number;
557
- maxWidth?: number;
558
- maxHeight?: number;
559
- accept?: string;
560
- }): Promise<void>;
561
- uploadAndReplaceImage(editor: Editor, file: File, options?: {
562
- quality?: number;
563
- maxWidth?: number;
564
- maxHeight?: number;
565
- }): Promise<void>;
540
+ /** Select file and upload as new image */
541
+ selectAndUploadImage(editor: Editor, options?: Record<string, any>): Promise<void>;
542
+ /** Select file and replace currently selected image */
543
+ selectAndReplaceImage(editor: Editor, options?: Record<string, any>): Promise<void>;
544
+ /** Internal helper used by replacement logic */
545
+ uploadAndReplaceImage(editor: Editor, file: File, options?: Record<string, any>): Promise<void>;
566
546
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageService, never>;
567
547
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ImageService>;
568
548
  }
569
549
 
550
+ type StateCalculator = (editor: Editor) => Partial<{
551
+ [K in keyof EditorStateSnapshot]: EditorStateSnapshot[K] extends object ? Partial<EditorStateSnapshot[K]> : EditorStateSnapshot[K];
552
+ }>;
553
+ interface EditorStateSnapshot {
554
+ /** Global editor states */
555
+ isFocused: boolean;
556
+ isEditable: boolean;
557
+ /**
558
+ * Detailed selection information to avoid overlap between menus
559
+ */
560
+ selection: {
561
+ type: 'text' | 'node' | 'cell' | 'none';
562
+ from: number;
563
+ to: number;
564
+ empty: boolean;
565
+ /** Specific for CellSelection */
566
+ isSingleCell: boolean;
567
+ };
568
+ /** Text formatting states (Marks) */
569
+ marks: {
570
+ bold: boolean;
571
+ italic: boolean;
572
+ underline: boolean;
573
+ strike: boolean;
574
+ code: boolean;
575
+ superscript: boolean;
576
+ subscript: boolean;
577
+ highlight: boolean;
578
+ link: boolean;
579
+ linkHref: string | null;
580
+ color: string | null;
581
+ computedColor: string | null;
582
+ background: string | null;
583
+ computedBackground: string | null;
584
+ };
585
+ /** Capability states (canExecute) */
586
+ can: {
587
+ toggleBold: boolean;
588
+ toggleItalic: boolean;
589
+ toggleUnderline: boolean;
590
+ toggleStrike: boolean;
591
+ toggleCode: boolean;
592
+ toggleHighlight: boolean;
593
+ toggleLink: boolean;
594
+ toggleSuperscript: boolean;
595
+ toggleSubscript: boolean;
596
+ setColor: boolean;
597
+ setHighlight: boolean;
598
+ undo: boolean;
599
+ redo: boolean;
600
+ /** Table specific capabilities */
601
+ addRowBefore: boolean;
602
+ addRowAfter: boolean;
603
+ deleteRow: boolean;
604
+ addColumnBefore: boolean;
605
+ addColumnAfter: boolean;
606
+ deleteColumn: boolean;
607
+ deleteTable: boolean;
608
+ mergeCells: boolean;
609
+ splitCell: boolean;
610
+ toggleHeaderRow: boolean;
611
+ toggleHeaderColumn: boolean;
612
+ /** Structure/Node capabilities */
613
+ toggleHeading1: boolean;
614
+ toggleHeading2: boolean;
615
+ toggleHeading3: boolean;
616
+ toggleBulletList: boolean;
617
+ toggleOrderedList: boolean;
618
+ toggleBlockquote: boolean;
619
+ setTextAlignLeft: boolean;
620
+ setTextAlignCenter: boolean;
621
+ setTextAlignRight: boolean;
622
+ setTextAlignJustify: boolean;
623
+ insertHorizontalRule: boolean;
624
+ insertTable: boolean;
625
+ insertImage: boolean;
626
+ };
627
+ /** Current node context */
628
+ nodes: {
629
+ isTable: boolean;
630
+ isTableNodeSelected: boolean;
631
+ isTableCell: boolean;
632
+ isImage: boolean;
633
+ isBlockquote: boolean;
634
+ isCodeBlock: boolean;
635
+ isBulletList: boolean;
636
+ isOrderedList: boolean;
637
+ /** Headings */
638
+ h1: boolean;
639
+ h2: boolean;
640
+ h3: boolean;
641
+ /** Alignment */
642
+ alignLeft: boolean;
643
+ alignCenter: boolean;
644
+ alignRight: boolean;
645
+ alignJustify: boolean;
646
+ /** Table specific */
647
+ isTableHeaderRow: boolean;
648
+ isTableHeaderColumn: boolean;
649
+ /** Name of the active node at selection head */
650
+ activeNodeName: string | null;
651
+ };
652
+ /** Placeholder for custom extension states */
653
+ custom: Record<string, any>;
654
+ }
655
+ declare const INITIAL_EDITOR_STATE: EditorStateSnapshot;
656
+
570
657
  declare class EditorCommandsService {
571
- isActive(editor: Editor, name: string, attributes?: Record<string, any>): boolean;
572
- canExecute(editor: Editor, command: string): boolean;
658
+ private imageService;
659
+ private colorPickerSvc;
660
+ private linkSvc;
661
+ private readonly _editorState;
662
+ /** Exposed editor state as a readonly signal */
663
+ readonly editorState: _angular_core.Signal<EditorStateSnapshot>;
664
+ /** Signal to track when the toolbar is being hovered/interacted with */
665
+ private readonly _isToolbarInteracting;
666
+ readonly isToolbarInteracting: _angular_core.Signal<boolean>;
667
+ /** Set toolbar interaction state (called by editor component) */
668
+ setToolbarInteracting(value: boolean): void;
669
+ readonly isUploading: _angular_core.Signal<boolean>;
670
+ readonly uploadProgress: _angular_core.Signal<number>;
671
+ readonly uploadMessage: _angular_core.Signal<string>;
672
+ set uploadHandler(handler: ImageUploadHandler | null);
673
+ /** Update state (called by TiptapStateExtension) */
674
+ updateState(state: EditorStateSnapshot): void;
675
+ /** Signal to toggle link edit mode from UI (Toolbar) - Immediate response */
676
+ readonly linkEditMode: _angular_core.Signal<boolean>;
677
+ /** Reference to the element that triggered the link menu (for anchoring) */
678
+ readonly linkMenuTrigger: _angular_core.Signal<HTMLElement | null>;
679
+ /** Signal to toggle color picker mode from UI (text or highlight) */
680
+ readonly colorEditMode: _angular_core.Signal<_flogeez_angular_tiptap_editor.ColorEditMode | null>;
681
+ /** Reference to the element that triggered the color menu (for anchoring) */
682
+ readonly colorMenuTrigger: _angular_core.Signal<HTMLElement | null>;
683
+ /** Generic method to execute any command by name */
684
+ execute(editor: Editor, command: string, ...args: any[]): void;
573
685
  toggleBold(editor: Editor): void;
574
686
  toggleItalic(editor: Editor): void;
575
687
  toggleStrike(editor: Editor): void;
576
688
  toggleCode(editor: Editor): void;
689
+ toggleUnderline(editor: Editor): void;
690
+ toggleSuperscript(editor: Editor): void;
691
+ toggleSubscript(editor: Editor): void;
577
692
  toggleHeading(editor: Editor, level: 1 | 2 | 3): void;
693
+ toggleHighlight(editor: Editor, color?: string): void;
578
694
  toggleBulletList(editor: Editor): void;
579
695
  toggleOrderedList(editor: Editor): void;
580
696
  toggleBlockquote(editor: Editor): void;
581
- undo(editor: Editor): void;
582
- redo(editor: Editor): void;
583
- toggleUnderline(editor: Editor): void;
584
- toggleSuperscript(editor: Editor): void;
585
- toggleSubscript(editor: Editor): void;
586
697
  setTextAlign(editor: Editor, alignment: "left" | "center" | "right" | "justify"): void;
587
- toggleLink(editor: Editor, url?: string): void;
588
698
  insertHorizontalRule(editor: Editor): void;
589
- toggleHighlight(editor: Editor, color?: string): void;
699
+ undo(editor: Editor): void;
700
+ redo(editor: Editor): void;
590
701
  insertTable(editor: Editor, rows?: number, cols?: number): void;
591
702
  addColumnBefore(editor: Editor): void;
592
703
  addColumnAfter(editor: Editor): void;
@@ -599,13 +710,23 @@ declare class EditorCommandsService {
599
710
  splitCell(editor: Editor): void;
600
711
  toggleHeaderColumn(editor: Editor): void;
601
712
  toggleHeaderRow(editor: Editor): void;
602
- toggleHeaderCell(editor: Editor): void;
603
713
  clearContent(editor: Editor): void;
604
714
  focus(editor: Editor): void;
605
715
  blur(editor: Editor): void;
606
716
  setContent(editor: Editor, content: string, emitUpdate?: boolean): void;
607
717
  setEditable(editor: Editor, editable: boolean): void;
608
718
  insertContent(editor: Editor, content: string): void;
719
+ insertImage(editor: Editor, options?: {
720
+ quality?: number;
721
+ maxWidth?: number;
722
+ maxHeight?: number;
723
+ accept?: string;
724
+ }): Promise<void>;
725
+ uploadImage(editor: Editor, file: File, options?: {
726
+ quality?: number;
727
+ maxWidth?: number;
728
+ maxHeight?: number;
729
+ }): Promise<void>;
609
730
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EditorCommandsService, never>;
610
731
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<EditorCommandsService>;
611
732
  }
@@ -633,7 +754,7 @@ declare const DEFAULT_SLASH_COMMANDS_CONFIG: Record<SlashCommandKey, boolean>;
633
754
  * Factory pour créer les commandes natives avec leurs traductions et leur logique d'exécution.
634
755
  * Utilise les services de l'éditeur pour garantir une cohérence de comportement.
635
756
  */
636
- declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: EditorCommandsService, images: ImageService, imageOptions?: {
757
+ declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: EditorCommandsService, imageOptions?: {
637
758
  quality?: number;
638
759
  maxWidth?: number;
639
760
  maxHeight?: number;
@@ -642,7 +763,7 @@ declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: E
642
763
  /**
643
764
  * Filtre et assemble les commandes selon la configuration fournie.
644
765
  */
645
- declare function filterSlashCommands(config: SlashCommandsConfig, i18n: TiptapI18nService, commands: EditorCommandsService, images: ImageService, imageOptions?: {
766
+ declare function filterSlashCommands(config: SlashCommandsConfig, i18n: TiptapI18nService, commands: EditorCommandsService, imageOptions?: {
646
767
  quality?: number;
647
768
  maxWidth?: number;
648
769
  maxHeight?: number;
@@ -728,11 +849,6 @@ declare class NoopValueAccessorDirective implements ControlValueAccessor {
728
849
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NoopValueAccessorDirective, never, never, {}, {}, never, never, true, never>;
729
850
  }
730
851
 
731
- declare const DEFAULT_TOOLBAR_CONFIG: ToolbarConfig;
732
- declare const DEFAULT_BUBBLE_MENU_CONFIG: BubbleMenuConfig;
733
- declare const DEFAULT_IMAGE_BUBBLE_MENU_CONFIG: ImageBubbleMenuConfig;
734
- declare const DEFAULT_TABLE_MENU_CONFIG: TableBubbleMenuConfig;
735
- declare const DEFAULT_CELL_MENU_CONFIG: CellBubbleMenuConfig$1;
736
852
  declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
737
853
  content: _angular_core.InputSignal<string>;
738
854
  placeholder: _angular_core.InputSignal<string>;
@@ -750,14 +866,18 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
750
866
  slashCommands: _angular_core.InputSignal<SlashCommandsConfig>;
751
867
  customSlashCommands: _angular_core.InputSignal<CustomSlashCommands | undefined>;
752
868
  locale: _angular_core.InputSignal<SupportedLocale | undefined>;
753
- autofocus: _angular_core.InputSignal<number | boolean | "end" | "start" | "all">;
754
- tiptapExtensions: _angular_core.InputSignal<(Node<any, any> | Extension<any, any> | Mark<any, any>)[]>;
869
+ autofocus: _angular_core.InputSignal<number | boolean | "start" | "end" | "all">;
870
+ tiptapExtensions: _angular_core.InputSignal<(Node<any, any> | Mark<any, any> | Extension<any, any>)[]>;
755
871
  tiptapOptions: _angular_core.InputSignal<Partial<EditorOptions>>;
756
872
  showBubbleMenu: _angular_core.InputSignal<boolean>;
757
873
  bubbleMenu: _angular_core.InputSignal<Partial<BubbleMenuConfig>>;
758
874
  showImageBubbleMenu: _angular_core.InputSignal<boolean>;
759
875
  imageBubbleMenu: _angular_core.InputSignal<Partial<ImageBubbleMenuConfig>>;
760
876
  toolbar: _angular_core.InputSignal<Partial<ToolbarConfig>>;
877
+ /**
878
+ * Additionnal state calculators to extend the reactive editor state.
879
+ */
880
+ stateCalculators: _angular_core.InputSignal<StateCalculator[]>;
761
881
  imageUpload: _angular_core.InputSignal<Partial<any>>;
762
882
  /**
763
883
  * Custom handler for image uploads.
@@ -794,10 +914,6 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
794
914
  event: FocusEvent;
795
915
  }>;
796
916
  editorElement: _angular_core.Signal<ElementRef<any>>;
797
- private textMenuComp;
798
- private imageMenuComp;
799
- private tableMenuComp;
800
- private cellMenuComp;
801
917
  hideBubbleMenus(): void;
802
918
  showBubbleMenus(): void;
803
919
  private _editor;
@@ -805,6 +921,7 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
805
921
  private _wordCount;
806
922
  private _isDragOver;
807
923
  private _editorFullyInitialized;
924
+ private lastEmittedHtml;
808
925
  readonly editor: _angular_core.Signal<Editor | null>;
809
926
  readonly characterCount: _angular_core.Signal<number>;
810
927
  readonly wordCount: _angular_core.Signal<number>;
@@ -844,14 +961,13 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
844
961
  private _destroyRef;
845
962
  private ngControl;
846
963
  readonly i18nService: TiptapI18nService;
847
- readonly imageService: ImageService;
848
964
  readonly editorCommandsService: EditorCommandsService;
965
+ readonly editorState: _angular_core.Signal<_flogeez_angular_tiptap_editor.EditorStateSnapshot>;
849
966
  constructor();
850
967
  ngAfterViewInit(): void;
851
968
  ngOnDestroy(): void;
852
969
  private initEditor;
853
970
  private updateCharacterCount;
854
- onSlashCommandImageUpload(file: File): Promise<void>;
855
971
  onDragOver(event: DragEvent): void;
856
972
  onDrop(event: DragEvent): void;
857
973
  private insertImageFromFile;
@@ -867,14 +983,136 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
867
983
  setDisabledState(isDisabled: boolean): void;
868
984
  onEditorClick(event: MouseEvent): void;
869
985
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AngularTiptapEditorComponent, never>;
870
- static ɵcmp: _angular_core.ɵɵComponentDeclaration<AngularTiptapEditorComponent, "angular-tiptap-editor", never, { "content": { "alias": "content"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fillContainer": { "alias": "fillContainer"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; "isSignal": true; }; "showWordCount": { "alias": "showWordCount"; "required": false; "isSignal": true; }; "maxCharacters": { "alias": "maxCharacters"; "required": false; "isSignal": true; }; "enableOfficePaste": { "alias": "enableOfficePaste"; "required": false; "isSignal": true; }; "enableSlashCommands": { "alias": "enableSlashCommands"; "required": false; "isSignal": true; }; "slashCommands": { "alias": "slashCommands"; "required": false; "isSignal": true; }; "customSlashCommands": { "alias": "customSlashCommands"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "tiptapExtensions": { "alias": "tiptapExtensions"; "required": false; "isSignal": true; }; "tiptapOptions": { "alias": "tiptapOptions"; "required": false; "isSignal": true; }; "showBubbleMenu": { "alias": "showBubbleMenu"; "required": false; "isSignal": true; }; "bubbleMenu": { "alias": "bubbleMenu"; "required": false; "isSignal": true; }; "showImageBubbleMenu": { "alias": "showImageBubbleMenu"; "required": false; "isSignal": true; }; "imageBubbleMenu": { "alias": "imageBubbleMenu"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "imageUpload": { "alias": "imageUpload"; "required": false; "isSignal": true; }; "imageUploadHandler": { "alias": "imageUploadHandler"; "required": false; "isSignal": true; }; }, { "contentChange": "contentChange"; "editorCreated": "editorCreated"; "editorUpdate": "editorUpdate"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; }, never, never, true, [{ directive: typeof NoopValueAccessorDirective; inputs: {}; outputs: {}; }]>;
986
+ static ɵcmp: _angular_core.ɵɵComponentDeclaration<AngularTiptapEditorComponent, "angular-tiptap-editor", never, { "content": { "alias": "content"; "required": false; "isSignal": true; }; "placeholder": { "alias": "placeholder"; "required": false; "isSignal": true; }; "editable": { "alias": "editable"; "required": false; "isSignal": true; }; "minHeight": { "alias": "minHeight"; "required": false; "isSignal": true; }; "height": { "alias": "height"; "required": false; "isSignal": true; }; "maxHeight": { "alias": "maxHeight"; "required": false; "isSignal": true; }; "fillContainer": { "alias": "fillContainer"; "required": false; "isSignal": true; }; "showToolbar": { "alias": "showToolbar"; "required": false; "isSignal": true; }; "showCharacterCount": { "alias": "showCharacterCount"; "required": false; "isSignal": true; }; "showWordCount": { "alias": "showWordCount"; "required": false; "isSignal": true; }; "maxCharacters": { "alias": "maxCharacters"; "required": false; "isSignal": true; }; "enableOfficePaste": { "alias": "enableOfficePaste"; "required": false; "isSignal": true; }; "enableSlashCommands": { "alias": "enableSlashCommands"; "required": false; "isSignal": true; }; "slashCommands": { "alias": "slashCommands"; "required": false; "isSignal": true; }; "customSlashCommands": { "alias": "customSlashCommands"; "required": false; "isSignal": true; }; "locale": { "alias": "locale"; "required": false; "isSignal": true; }; "autofocus": { "alias": "autofocus"; "required": false; "isSignal": true; }; "tiptapExtensions": { "alias": "tiptapExtensions"; "required": false; "isSignal": true; }; "tiptapOptions": { "alias": "tiptapOptions"; "required": false; "isSignal": true; }; "showBubbleMenu": { "alias": "showBubbleMenu"; "required": false; "isSignal": true; }; "bubbleMenu": { "alias": "bubbleMenu"; "required": false; "isSignal": true; }; "showImageBubbleMenu": { "alias": "showImageBubbleMenu"; "required": false; "isSignal": true; }; "imageBubbleMenu": { "alias": "imageBubbleMenu"; "required": false; "isSignal": true; }; "toolbar": { "alias": "toolbar"; "required": false; "isSignal": true; }; "stateCalculators": { "alias": "stateCalculators"; "required": false; "isSignal": true; }; "imageUpload": { "alias": "imageUpload"; "required": false; "isSignal": true; }; "imageUploadHandler": { "alias": "imageUploadHandler"; "required": false; "isSignal": true; }; }, { "contentChange": "contentChange"; "editorCreated": "editorCreated"; "editorUpdate": "editorUpdate"; "editorFocus": "editorFocus"; "editorBlur": "editorBlur"; }, never, never, true, [{ directive: typeof NoopValueAccessorDirective; inputs: {}; outputs: {}; }]>;
871
987
  }
872
988
 
873
- type HeightConfig = {
874
- minHeight?: number;
875
- height?: number;
876
- maxHeight?: number;
877
- };
989
+ interface ColorPickerSelection {
990
+ from: number;
991
+ to: number;
992
+ }
993
+ type ColorEditMode = 'text' | 'highlight';
994
+ declare class ColorPickerService {
995
+ private storedSelection;
996
+ /** Current edit mode: null when closed, 'text' or 'highlight' when open */
997
+ readonly editMode: _angular_core.WritableSignal<ColorEditMode | null>;
998
+ /** Reference to the element that triggered the menu (for anchoring) */
999
+ readonly menuTrigger: _angular_core.WritableSignal<HTMLElement | null>;
1000
+ /** Whether the user is currently interacting with the picker UI (e.g. typing) */
1001
+ readonly isInteracting: _angular_core.WritableSignal<boolean>;
1002
+ /**
1003
+ * Open the color picker menu in the specified mode.
1004
+ */
1005
+ open(mode: ColorEditMode, trigger?: HTMLElement): void;
1006
+ /**
1007
+ * Close the color picker menu.
1008
+ */
1009
+ close(): void;
1010
+ /**
1011
+ * Set interaction state (prevents premature closing when blurring editor)
1012
+ */
1013
+ setInteracting(value: boolean): void;
1014
+ /**
1015
+ * Toggle color picker from UI (extracts trigger from event).
1016
+ */
1017
+ toggle(editor: Editor, mode: ColorEditMode, event?: Event): void;
1018
+ /**
1019
+ * Capture current editor selection.
1020
+ */
1021
+ captureSelection(editor: Editor): void;
1022
+ /**
1023
+ * Get last captured selection.
1024
+ */
1025
+ getStoredSelection(): ColorPickerSelection | null;
1026
+ /**
1027
+ * Clear captured selection.
1028
+ */
1029
+ done(): void;
1030
+ /**
1031
+ * Apply text color to selection.
1032
+ */
1033
+ applyColor(editor: Editor, color: string, addToHistory?: boolean, focus?: boolean): void;
1034
+ /**
1035
+ * Remove text color from selection.
1036
+ */
1037
+ unsetColor(editor: Editor, focus?: boolean): void;
1038
+ /**
1039
+ * Apply highlight color to selection.
1040
+ */
1041
+ applyHighlight(editor: Editor, color: string, addToHistory?: boolean, focus?: boolean): void;
1042
+ /**
1043
+ * Remove highlight from selection.
1044
+ */
1045
+ unsetHighlight(editor: Editor, focus?: boolean): void;
1046
+ normalizeColor(color: string | null | undefined): string;
1047
+ getLuminance(color: string): number;
1048
+ getContrastColor(color: string): "black" | "white";
1049
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<ColorPickerService, never>;
1050
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<ColorPickerService>;
1051
+ }
1052
+
1053
+ declare class LinkService {
1054
+ /** Whether link edit mode is active */
1055
+ readonly editMode: _angular_core.WritableSignal<boolean>;
1056
+ /** Reference to the element that triggered the menu (for anchoring) */
1057
+ readonly menuTrigger: _angular_core.WritableSignal<HTMLElement | null>;
1058
+ /** Whether the user is currently interacting with the link UI (input focus) */
1059
+ readonly isInteracting: _angular_core.WritableSignal<boolean>;
1060
+ /**
1061
+ * Open the link edit menu.
1062
+ */
1063
+ open(trigger?: HTMLElement): void;
1064
+ /**
1065
+ * Close the link edit menu.
1066
+ */
1067
+ close(): void;
1068
+ /**
1069
+ * Final cleanup (called after UI is hidden)
1070
+ */
1071
+ done(): void;
1072
+ /**
1073
+ * Set interaction state
1074
+ */
1075
+ setInteracting(value: boolean): void;
1076
+ /**
1077
+ * Toggle link mode from UI.
1078
+ * If a URL string is provided, applies the link and closes.
1079
+ * If an Event is provided, extracts the trigger for anchoring.
1080
+ */
1081
+ toggle(editor: Editor, urlOrEvent?: string | Event): void;
1082
+ /**
1083
+ * Apply a link to the current selection.
1084
+ */
1085
+ setLink(editor: Editor, url: string): void;
1086
+ /**
1087
+ * Remove link from the current selection.
1088
+ */
1089
+ unsetLink(editor: Editor): void;
1090
+ static ɵfac: _angular_core.ɵɵFactoryDeclaration<LinkService, never>;
1091
+ static ɵprov: _angular_core.ɵɵInjectableDeclaration<LinkService>;
1092
+ }
1093
+
1094
+ /**
1095
+ * DiscoveryCalculator automatically detects and tracks the state of any TipTap extension.
1096
+ * It provides a "fallback" reactive state for any mark or node not explicitly handled
1097
+ * by specialized calculators.
1098
+ */
1099
+ declare const DiscoveryCalculator: StateCalculator;
1100
+
1101
+ declare const ImageCalculator: StateCalculator;
1102
+
1103
+ declare const MarksCalculator: StateCalculator;
1104
+
1105
+ declare const SelectionCalculator: StateCalculator;
1106
+
1107
+ declare const StructureCalculator: StateCalculator;
1108
+
1109
+ declare const TableCalculator: StateCalculator;
1110
+
1111
+ declare const DEFAULT_TOOLBAR_CONFIG: ToolbarConfig;
1112
+ declare const DEFAULT_BUBBLE_MENU_CONFIG: BubbleMenuConfig;
1113
+ declare const DEFAULT_IMAGE_BUBBLE_MENU_CONFIG: ImageBubbleMenuConfig;
1114
+ declare const DEFAULT_TABLE_MENU_CONFIG: TableBubbleMenuConfig;
1115
+ declare const DEFAULT_CELL_MENU_CONFIG: CellBubbleMenuConfig;
878
1116
 
879
- export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS_CONFIG, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, EditorCommandsService, ImageService, NoopValueAccessorDirective, SLASH_COMMAND_KEYS, TiptapI18nService, createDefaultSlashCommands, filterSlashCommands };
880
- export type { BubbleMenuConfig, CellBubbleMenuConfig, CustomSlashCommands, HeightConfig, ImageBubbleMenuConfig, ImageData, ImageUploadContext, ImageUploadHandler, ImageUploadHandlerResult, ImageUploadResult, ResizeOptions, SlashCommandItem, SlashCommandKey, SlashCommandsConfig, SupportedLocale, TableBubbleMenuConfig, TiptapTranslations, ToolbarConfig };
1117
+ export { AngularTiptapEditorComponent, ColorPickerService, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS_CONFIG, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, DiscoveryCalculator, EditorCommandsService, INITIAL_EDITOR_STATE, ImageCalculator, ImageService, LinkService, MarksCalculator, NoopValueAccessorDirective, SLASH_COMMAND_KEYS, SelectionCalculator, StructureCalculator, TableCalculator, TiptapI18nService, createDefaultSlashCommands, filterSlashCommands };
1118
+ export type { BubbleMenuConfig, CellBubbleMenuConfig, ColorEditMode, ColorPickerSelection, CustomSlashCommands, EditorStateSnapshot, ImageBubbleMenuConfig, ImageData, ImageUploadContext, ImageUploadHandler, ImageUploadHandlerResult, ImageUploadResult, ResizeOptions, SlashCommandItem, SlashCommandKey, SlashCommandsConfig, StateCalculator, SupportedLocale, TableBubbleMenuConfig, TiptapTranslations, ToolbarConfig };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@flogeez/angular-tiptap-editor",
3
- "version": "0.5.4",
3
+ "version": "0.6.0",
4
4
  "description": "A modern, customizable rich-text editor for Angular (18+), built with Tiptap and featuring complete internationalization support",
5
5
  "keywords": [
6
6
  "angular",
@@ -26,6 +26,13 @@
26
26
  white-space: nowrap;
27
27
  }
28
28
 
29
+ /* Cache le menu intelligemment si la source (le slash) est scrollée hors vue */
30
+ .tippy-box[data-reference-hidden] {
31
+ opacity: 0 !important;
32
+ transition: opacity 0.18s cubic-bezier(0, 0, 0.2, 1);
33
+ pointer-events: none !important;
34
+ }
35
+
29
36
  .bubble-menu .tiptap-separator {
30
37
  width: 1px;
31
38
  height: 28px;