@flogeez/angular-tiptap-editor 0.5.3 → 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
 
@@ -34,6 +35,8 @@ interface TiptapTranslations {
34
35
  redo: string;
35
36
  clear: string;
36
37
  textColor: string;
38
+ customColor: string;
39
+ presets: string;
37
40
  };
38
41
  bubbleMenu: {
39
42
  bold: string;
@@ -53,6 +56,8 @@ interface TiptapTranslations {
53
56
  linkUrl: string;
54
57
  linkText: string;
55
58
  openLink: string;
59
+ customColor: string;
60
+ presets: string;
56
61
  };
57
62
  slashCommands: {
58
63
  heading1: {
@@ -199,6 +204,8 @@ declare class TiptapI18nService {
199
204
  redo: string;
200
205
  clear: string;
201
206
  textColor: string;
207
+ customColor: string;
208
+ presets: string;
202
209
  }>;
203
210
  readonly bubbleMenu: _angular_core.Signal<{
204
211
  bold: string;
@@ -218,6 +225,8 @@ declare class TiptapI18nService {
218
225
  linkUrl: string;
219
226
  linkText: string;
220
227
  openLink: string;
228
+ customColor: string;
229
+ presets: string;
221
230
  }>;
222
231
  readonly slashCommands: _angular_core.Signal<{
223
232
  heading1: {
@@ -382,11 +391,6 @@ declare class TiptapI18nService {
382
391
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<TiptapI18nService>;
383
392
  }
384
393
 
385
- interface CellBubbleMenuConfig$1 {
386
- mergeCells?: boolean;
387
- splitCell?: boolean;
388
- }
389
-
390
394
  interface SlashCommandItem {
391
395
  title: string;
392
396
  description: string;
@@ -478,107 +482,222 @@ interface ImageUploadHandlerResult {
478
482
  */
479
483
  type ImageUploadHandler = (context: ImageUploadContext) => Promise<ImageUploadHandlerResult> | Observable<ImageUploadHandlerResult>;
480
484
  declare class ImageService {
485
+ /** Signals for image state */
481
486
  selectedImage: _angular_core.WritableSignal<ImageData | null>;
482
487
  isImageSelected: _angular_core.Signal<boolean>;
488
+ /** Resizing state */
483
489
  isResizing: _angular_core.WritableSignal<boolean>;
484
490
  private i18n;
485
491
  private readonly t;
492
+ /** Upload state signals */
486
493
  isUploading: _angular_core.WritableSignal<boolean>;
487
494
  uploadProgress: _angular_core.WritableSignal<number>;
488
495
  uploadMessage: _angular_core.WritableSignal<string>;
489
496
  /**
490
- * Custom upload handler for images.
491
- * When set, this handler will be called instead of the default base64 conversion.
492
- * This allows users to implement their own image storage logic.
493
- *
494
- * @example
495
- * ```typescript
496
- * imageService.uploadHandler = async (context) => {
497
- * const formData = new FormData();
498
- * formData.append('image', context.file);
499
- * const response = await fetch('/api/upload', { method: 'POST', body: formData });
500
- * const data = await response.json();
501
- * return { src: data.url };
502
- * };
503
- * ```
497
+ * Custom upload handler.
498
+ * If set, this handler replaces the default base64 conversion.
504
499
  */
505
500
  uploadHandler: ImageUploadHandler | null;
506
501
  private currentEditor;
502
+ /** Select and track an image from the editor */
507
503
  selectImage(editor: Editor): void;
508
504
  clearSelection(): void;
505
+ /** Insert a new image and ensure it's selected */
509
506
  insertImage(editor: Editor, imageData: ImageData): void;
507
+ /** Update attributes of the currently active image */
510
508
  updateImageAttributes(editor: Editor, attributes: Partial<ImageData>): void;
509
+ /** Resize image with optional aspect ratio maintenance */
511
510
  resizeImage(editor: Editor, options: ResizeOptions): void;
512
- resizeImageByPercentage(editor: Editor, percentage: number): void;
511
+ /** Predetermined resize helpers used by UI */
513
512
  resizeImageToSmall(editor: Editor): void;
514
513
  resizeImageToMedium(editor: Editor): void;
515
514
  resizeImageToLarge(editor: Editor): void;
516
515
  resizeImageToOriginal(editor: Editor): void;
517
- resizeImageFreely(editor: Editor, width: number, height: number): void;
516
+ /** Get current image dimensions */
518
517
  getImageDimensions(editor: Editor): {
519
518
  width: number;
520
519
  height: number;
521
520
  } | null;
522
- getNaturalImageDimensions(src: string): Promise<{
523
- width: number;
524
- height: number;
525
- }>;
521
+ /** Remove the selected image */
526
522
  deleteImage(editor: Editor): void;
527
523
  private updateSelectedImage;
524
+ /** Validate file type and size */
528
525
  validateImage(file: File, maxSize?: number): {
529
526
  valid: boolean;
530
527
  error?: string;
531
528
  };
529
+ /** Compress and process image on client side */
532
530
  compressImage(file: File, quality?: number, maxWidth?: number, maxHeight?: number): Promise<ImageUploadResult>;
531
+ /** Core upload logic with progress tracking */
533
532
  private uploadImageWithProgress;
534
- uploadAndInsertImage(editor: Editor, file: File, options?: {
535
- quality?: number;
536
- maxWidth?: number;
537
- maxHeight?: number;
538
- }): 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 */
539
537
  private forceEditorUpdate;
538
+ /** Generic helper to open file picker and process selection */
540
539
  private selectFileAndProcess;
541
- selectAndUploadImage(editor: Editor, options?: {
542
- quality?: number;
543
- maxWidth?: number;
544
- maxHeight?: number;
545
- accept?: string;
546
- }): Promise<void>;
547
- selectAndReplaceImage(editor: Editor, options?: {
548
- quality?: number;
549
- maxWidth?: number;
550
- maxHeight?: number;
551
- accept?: string;
552
- }): Promise<void>;
553
- uploadAndReplaceImage(editor: Editor, file: File, options?: {
554
- quality?: number;
555
- maxWidth?: number;
556
- maxHeight?: number;
557
- }): 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>;
558
546
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<ImageService, never>;
559
547
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<ImageService>;
560
548
  }
561
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
+
562
657
  declare class EditorCommandsService {
563
- isActive(editor: Editor, name: string, attributes?: Record<string, any>): boolean;
564
- 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;
565
685
  toggleBold(editor: Editor): void;
566
686
  toggleItalic(editor: Editor): void;
567
687
  toggleStrike(editor: Editor): void;
568
688
  toggleCode(editor: Editor): void;
689
+ toggleUnderline(editor: Editor): void;
690
+ toggleSuperscript(editor: Editor): void;
691
+ toggleSubscript(editor: Editor): void;
569
692
  toggleHeading(editor: Editor, level: 1 | 2 | 3): void;
693
+ toggleHighlight(editor: Editor, color?: string): void;
570
694
  toggleBulletList(editor: Editor): void;
571
695
  toggleOrderedList(editor: Editor): void;
572
696
  toggleBlockquote(editor: Editor): void;
573
- undo(editor: Editor): void;
574
- redo(editor: Editor): void;
575
- toggleUnderline(editor: Editor): void;
576
- toggleSuperscript(editor: Editor): void;
577
- toggleSubscript(editor: Editor): void;
578
697
  setTextAlign(editor: Editor, alignment: "left" | "center" | "right" | "justify"): void;
579
- toggleLink(editor: Editor, url?: string): void;
580
698
  insertHorizontalRule(editor: Editor): void;
581
- toggleHighlight(editor: Editor, color?: string): void;
699
+ undo(editor: Editor): void;
700
+ redo(editor: Editor): void;
582
701
  insertTable(editor: Editor, rows?: number, cols?: number): void;
583
702
  addColumnBefore(editor: Editor): void;
584
703
  addColumnAfter(editor: Editor): void;
@@ -591,13 +710,23 @@ declare class EditorCommandsService {
591
710
  splitCell(editor: Editor): void;
592
711
  toggleHeaderColumn(editor: Editor): void;
593
712
  toggleHeaderRow(editor: Editor): void;
594
- toggleHeaderCell(editor: Editor): void;
595
713
  clearContent(editor: Editor): void;
596
714
  focus(editor: Editor): void;
597
715
  blur(editor: Editor): void;
598
716
  setContent(editor: Editor, content: string, emitUpdate?: boolean): void;
599
717
  setEditable(editor: Editor, editable: boolean): void;
600
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>;
601
730
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<EditorCommandsService, never>;
602
731
  static ɵprov: _angular_core.ɵɵInjectableDeclaration<EditorCommandsService>;
603
732
  }
@@ -625,7 +754,7 @@ declare const DEFAULT_SLASH_COMMANDS_CONFIG: Record<SlashCommandKey, boolean>;
625
754
  * Factory pour créer les commandes natives avec leurs traductions et leur logique d'exécution.
626
755
  * Utilise les services de l'éditeur pour garantir une cohérence de comportement.
627
756
  */
628
- declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: EditorCommandsService, images: ImageService, imageOptions?: {
757
+ declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: EditorCommandsService, imageOptions?: {
629
758
  quality?: number;
630
759
  maxWidth?: number;
631
760
  maxHeight?: number;
@@ -634,7 +763,7 @@ declare function createDefaultSlashCommands(i18n: TiptapI18nService, commands: E
634
763
  /**
635
764
  * Filtre et assemble les commandes selon la configuration fournie.
636
765
  */
637
- declare function filterSlashCommands(config: SlashCommandsConfig, i18n: TiptapI18nService, commands: EditorCommandsService, images: ImageService, imageOptions?: {
766
+ declare function filterSlashCommands(config: SlashCommandsConfig, i18n: TiptapI18nService, commands: EditorCommandsService, imageOptions?: {
638
767
  quality?: number;
639
768
  maxWidth?: number;
640
769
  maxHeight?: number;
@@ -720,11 +849,6 @@ declare class NoopValueAccessorDirective implements ControlValueAccessor {
720
849
  static ɵdir: _angular_core.ɵɵDirectiveDeclaration<NoopValueAccessorDirective, never, never, {}, {}, never, never, true, never>;
721
850
  }
722
851
 
723
- declare const DEFAULT_TOOLBAR_CONFIG: ToolbarConfig;
724
- declare const DEFAULT_BUBBLE_MENU_CONFIG: BubbleMenuConfig;
725
- declare const DEFAULT_IMAGE_BUBBLE_MENU_CONFIG: ImageBubbleMenuConfig;
726
- declare const DEFAULT_TABLE_MENU_CONFIG: TableBubbleMenuConfig;
727
- declare const DEFAULT_CELL_MENU_CONFIG: CellBubbleMenuConfig$1;
728
852
  declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
729
853
  content: _angular_core.InputSignal<string>;
730
854
  placeholder: _angular_core.InputSignal<string>;
@@ -742,14 +866,18 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
742
866
  slashCommands: _angular_core.InputSignal<SlashCommandsConfig>;
743
867
  customSlashCommands: _angular_core.InputSignal<CustomSlashCommands | undefined>;
744
868
  locale: _angular_core.InputSignal<SupportedLocale | undefined>;
745
- autofocus: _angular_core.InputSignal<number | boolean | "end" | "start" | "all">;
746
- 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>)[]>;
747
871
  tiptapOptions: _angular_core.InputSignal<Partial<EditorOptions>>;
748
872
  showBubbleMenu: _angular_core.InputSignal<boolean>;
749
873
  bubbleMenu: _angular_core.InputSignal<Partial<BubbleMenuConfig>>;
750
874
  showImageBubbleMenu: _angular_core.InputSignal<boolean>;
751
875
  imageBubbleMenu: _angular_core.InputSignal<Partial<ImageBubbleMenuConfig>>;
752
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[]>;
753
881
  imageUpload: _angular_core.InputSignal<Partial<any>>;
754
882
  /**
755
883
  * Custom handler for image uploads.
@@ -786,11 +914,14 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
786
914
  event: FocusEvent;
787
915
  }>;
788
916
  editorElement: _angular_core.Signal<ElementRef<any>>;
917
+ hideBubbleMenus(): void;
918
+ showBubbleMenus(): void;
789
919
  private _editor;
790
920
  private _characterCount;
791
921
  private _wordCount;
792
922
  private _isDragOver;
793
923
  private _editorFullyInitialized;
924
+ private lastEmittedHtml;
794
925
  readonly editor: _angular_core.Signal<Editor | null>;
795
926
  readonly characterCount: _angular_core.Signal<number>;
796
927
  readonly wordCount: _angular_core.Signal<number>;
@@ -830,14 +961,13 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
830
961
  private _destroyRef;
831
962
  private ngControl;
832
963
  readonly i18nService: TiptapI18nService;
833
- readonly imageService: ImageService;
834
964
  readonly editorCommandsService: EditorCommandsService;
965
+ readonly editorState: _angular_core.Signal<_flogeez_angular_tiptap_editor.EditorStateSnapshot>;
835
966
  constructor();
836
967
  ngAfterViewInit(): void;
837
968
  ngOnDestroy(): void;
838
969
  private initEditor;
839
970
  private updateCharacterCount;
840
- onSlashCommandImageUpload(file: File): Promise<void>;
841
971
  onDragOver(event: DragEvent): void;
842
972
  onDrop(event: DragEvent): void;
843
973
  private insertImageFromFile;
@@ -853,14 +983,136 @@ declare class AngularTiptapEditorComponent implements AfterViewInit, OnDestroy {
853
983
  setDisabledState(isDisabled: boolean): void;
854
984
  onEditorClick(event: MouseEvent): void;
855
985
  static ɵfac: _angular_core.ɵɵFactoryDeclaration<AngularTiptapEditorComponent, never>;
856
- 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: {}; }]>;
857
987
  }
858
988
 
859
- type HeightConfig = {
860
- minHeight?: number;
861
- height?: number;
862
- maxHeight?: number;
863
- };
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;
864
1116
 
865
- 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 };
866
- 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.3",
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;
@@ -43,3 +50,4 @@
43
50
  transform: translateY(0) scale(1);
44
51
  }
45
52
  }
53
+