@flogeez/angular-tiptap-editor 0.3.1 → 0.3.3

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.
@@ -2338,7 +2338,20 @@ class EditorCommandsService {
2338
2338
  }
2339
2339
  // Méthode pour vider le contenu
2340
2340
  clearContent(editor) {
2341
- editor.chain().focus().clearContent().run();
2341
+ editor.chain().focus().setContent("", true).run(); // ✅ Forcer l'émission de l'événement
2342
+ }
2343
+ // Méthodes de base de l'éditeur
2344
+ focus(editor) {
2345
+ editor.chain().focus().run();
2346
+ }
2347
+ blur(editor) {
2348
+ editor.chain().blur().run();
2349
+ }
2350
+ setContent(editor, content, emitUpdate = true) {
2351
+ editor.chain().focus().setContent(content, emitUpdate).run();
2352
+ }
2353
+ setEditable(editor, editable) {
2354
+ editor.setEditable(editable);
2342
2355
  }
2343
2356
  static { this.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: EditorCommandsService, deps: [], target: i0.ɵɵFactoryTarget.Injectable }); }
2344
2357
  static { this.ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "20.0.0", ngImport: i0, type: EditorCommandsService, providedIn: "root" }); }
@@ -3497,7 +3510,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
3497
3510
  `, styles: [".tiptap-toolbar{display:flex;align-items:center;gap:4px;padding:4px 8px;background:#f8f9fa;border-bottom:1px solid #e2e8f0;flex-wrap:wrap;min-height:32px;position:relative}.toolbar-group{display:flex;align-items:center;gap:2px;padding:0 4px}.toolbar-separator{width:1px;height:24px;background:#e2e8f0;margin:0 4px}@media (max-width: 768px){.tiptap-toolbar{padding:6px 8px;gap:2px}.toolbar-group{gap:1px}}@keyframes toolbarSlideIn{0%{opacity:0;transform:translateY(-10px)}to{opacity:1;transform:translateY(0)}}.tiptap-toolbar{animation:toolbarSlideIn .3s cubic-bezier(.4,0,.2,1)}\n"] }]
3498
3511
  }], ctorParameters: () => [{ type: EditorCommandsService }] });
3499
3512
 
3500
- const DEFAULT_SLASH_COMMANDS = [
3513
+ const DEFAULT_SLASH_COMMANDS$1 = [
3501
3514
  {
3502
3515
  title: "Titre 1",
3503
3516
  description: "Grand titre de section",
@@ -3634,7 +3647,7 @@ class TiptapSlashCommandsComponent {
3634
3647
  constructor() {
3635
3648
  this.editor = input.required();
3636
3649
  this.config = input({
3637
- commands: DEFAULT_SLASH_COMMANDS,
3650
+ commands: DEFAULT_SLASH_COMMANDS$1,
3638
3651
  });
3639
3652
  // Output pour l'upload d'image
3640
3653
  this.imageUploadRequested = output();
@@ -3647,7 +3660,7 @@ class TiptapSlashCommandsComponent {
3647
3660
  // Signal pour l'index sélectionné
3648
3661
  this.selectedIndex = signal(0);
3649
3662
  this.commands = computed(() => {
3650
- const configCommands = this.config().commands || DEFAULT_SLASH_COMMANDS;
3663
+ const configCommands = this.config().commands || DEFAULT_SLASH_COMMANDS$1;
3651
3664
  // Remplacer la commande image par une version qui utilise l'output
3652
3665
  return configCommands.map((command) => {
3653
3666
  if (command.icon === "image") {
@@ -4367,6 +4380,178 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
4367
4380
  }]
4368
4381
  }] });
4369
4382
 
4383
+ /**
4384
+ * Clés des commandes dans l'ordre de création
4385
+ */
4386
+ const SLASH_COMMAND_KEYS = [
4387
+ "heading1",
4388
+ "heading2",
4389
+ "heading3",
4390
+ "bulletList",
4391
+ "orderedList",
4392
+ "blockquote",
4393
+ "code",
4394
+ "image",
4395
+ "horizontalRule",
4396
+ "table",
4397
+ ];
4398
+ /**
4399
+ * Factory function pour créer les slash commands traduits
4400
+ */
4401
+ function createI18nSlashCommands(i18nService) {
4402
+ const slashCommands = i18nService.slashCommands();
4403
+ return [
4404
+ {
4405
+ title: slashCommands.heading1.title,
4406
+ description: slashCommands.heading1.description,
4407
+ icon: "format_h1",
4408
+ keywords: slashCommands.heading1.keywords,
4409
+ command: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run(),
4410
+ },
4411
+ {
4412
+ title: slashCommands.heading2.title,
4413
+ description: slashCommands.heading2.description,
4414
+ icon: "format_h2",
4415
+ keywords: slashCommands.heading2.keywords,
4416
+ command: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run(),
4417
+ },
4418
+ {
4419
+ title: slashCommands.heading3.title,
4420
+ description: slashCommands.heading3.description,
4421
+ icon: "format_h3",
4422
+ keywords: slashCommands.heading3.keywords,
4423
+ command: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
4424
+ },
4425
+ {
4426
+ title: slashCommands.bulletList.title,
4427
+ description: slashCommands.bulletList.description,
4428
+ icon: "format_list_bulleted",
4429
+ keywords: slashCommands.bulletList.keywords,
4430
+ command: (editor) => editor.chain().focus().toggleBulletList().run(),
4431
+ },
4432
+ {
4433
+ title: slashCommands.orderedList.title,
4434
+ description: slashCommands.orderedList.description,
4435
+ icon: "format_list_numbered",
4436
+ keywords: slashCommands.orderedList.keywords,
4437
+ command: (editor) => editor.chain().focus().toggleOrderedList().run(),
4438
+ },
4439
+ {
4440
+ title: slashCommands.blockquote.title,
4441
+ description: slashCommands.blockquote.description,
4442
+ icon: "format_quote",
4443
+ keywords: slashCommands.blockquote.keywords,
4444
+ command: (editor) => editor.chain().focus().toggleBlockquote().run(),
4445
+ },
4446
+ {
4447
+ title: slashCommands.code.title,
4448
+ description: slashCommands.code.description,
4449
+ icon: "code",
4450
+ keywords: slashCommands.code.keywords,
4451
+ command: (editor) => editor.chain().focus().toggleCodeBlock().run(),
4452
+ },
4453
+ {
4454
+ title: slashCommands.image.title,
4455
+ description: slashCommands.image.description,
4456
+ icon: "image",
4457
+ keywords: slashCommands.image.keywords,
4458
+ command: (editor) => {
4459
+ // Créer un input file temporaire pour sélectionner une image
4460
+ const input = document.createElement("input");
4461
+ input.type = "file";
4462
+ input.accept = "image/*";
4463
+ input.style.display = "none";
4464
+ input.addEventListener("change", async (e) => {
4465
+ const file = e.target.files?.[0];
4466
+ if (file && file.type.startsWith("image/")) {
4467
+ try {
4468
+ // Utiliser la méthode de compression unifiée
4469
+ const canvas = document.createElement("canvas");
4470
+ const ctx = canvas.getContext("2d");
4471
+ const img = new Image();
4472
+ img.onload = () => {
4473
+ // Vérifier les dimensions (max 1920x1080)
4474
+ const maxWidth = 1920;
4475
+ const maxHeight = 1080;
4476
+ let { width, height } = img;
4477
+ // Redimensionner si nécessaire
4478
+ if (width > maxWidth || height > maxHeight) {
4479
+ const ratio = Math.min(maxWidth / width, maxHeight / height);
4480
+ width *= ratio;
4481
+ height *= ratio;
4482
+ }
4483
+ canvas.width = width;
4484
+ canvas.height = height;
4485
+ // Dessiner l'image redimensionnée
4486
+ ctx?.drawImage(img, 0, 0, width, height);
4487
+ // Convertir en base64 avec compression
4488
+ canvas.toBlob((blob) => {
4489
+ if (blob) {
4490
+ const reader = new FileReader();
4491
+ reader.onload = (e) => {
4492
+ const base64 = e.target?.result;
4493
+ if (base64) {
4494
+ // Utiliser setResizableImage avec toutes les propriétés
4495
+ editor
4496
+ .chain()
4497
+ .focus()
4498
+ .setResizableImage({
4499
+ src: base64,
4500
+ alt: file.name,
4501
+ title: `${file.name} (${Math.round(width)}×${Math.round(height)})`,
4502
+ width: Math.round(width),
4503
+ height: Math.round(height),
4504
+ })
4505
+ .run();
4506
+ }
4507
+ };
4508
+ reader.readAsDataURL(blob);
4509
+ }
4510
+ }, file.type, 0.8 // qualité de compression
4511
+ );
4512
+ };
4513
+ img.onerror = () => {
4514
+ console.error(i18nService.editor().imageLoadError);
4515
+ };
4516
+ img.src = URL.createObjectURL(file);
4517
+ }
4518
+ catch (error) {
4519
+ console.error("Error uploading image:", error);
4520
+ }
4521
+ }
4522
+ document.body.removeChild(input);
4523
+ });
4524
+ document.body.appendChild(input);
4525
+ input.click();
4526
+ },
4527
+ },
4528
+ {
4529
+ title: slashCommands.horizontalRule.title,
4530
+ description: slashCommands.horizontalRule.description,
4531
+ icon: "horizontal_rule",
4532
+ keywords: slashCommands.horizontalRule.keywords,
4533
+ command: (editor) => editor.chain().focus().setHorizontalRule().run(),
4534
+ },
4535
+ {
4536
+ title: slashCommands.table.title,
4537
+ description: slashCommands.table.description,
4538
+ icon: "table_view",
4539
+ keywords: slashCommands.table.keywords,
4540
+ command: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3 }).run(),
4541
+ },
4542
+ ];
4543
+ }
4544
+ /**
4545
+ * Fonction utilitaire pour filtrer les slash commands selon les commandes actives
4546
+ */
4547
+ function filterSlashCommands(activeCommands, i18nService) {
4548
+ const allCommands = createI18nSlashCommands(i18nService);
4549
+ return allCommands.filter((command, index) => {
4550
+ const commandKey = SLASH_COMMAND_KEYS[index];
4551
+ return commandKey && activeCommands.has(commandKey);
4552
+ });
4553
+ }
4554
+
4370
4555
  // Configuration par défaut de la toolbar
4371
4556
  const DEFAULT_TOOLBAR_CONFIG = {
4372
4557
  bold: true,
@@ -4436,6 +4621,9 @@ const DEFAULT_CELL_MENU_CONFIG = {
4436
4621
  mergeCells: true,
4437
4622
  splitCell: true,
4438
4623
  };
4624
+ const DEFAULT_SLASH_COMMANDS = {
4625
+ commands: [], // Sera rempli par filterSlashCommands
4626
+ };
4439
4627
  class AngularTiptapEditorComponent {
4440
4628
  constructor() {
4441
4629
  // Nouveaux inputs avec signal
@@ -4469,12 +4657,18 @@ class AngularTiptapEditorComponent {
4469
4657
  this.editorBlur = output();
4470
4658
  // ViewChild avec signal
4471
4659
  this.editorElement = viewChild.required("editorElement");
4472
- // Signals pour l'état interne
4473
- this.editor = signal(null);
4474
- this.characterCount = signal(0);
4475
- this.wordCount = signal(0);
4476
- this.isDragOver = signal(false);
4477
- this.editorFullyInitialized = signal(false);
4660
+ // Signals privés pour l'état interne
4661
+ this._editor = signal(null);
4662
+ this._characterCount = signal(0);
4663
+ this._wordCount = signal(0);
4664
+ this._isDragOver = signal(false);
4665
+ this._editorFullyInitialized = signal(false);
4666
+ // Accès en lecture seule aux signaux
4667
+ this.editor = this._editor.asReadonly();
4668
+ this.characterCount = this._characterCount.asReadonly();
4669
+ this.wordCount = this._wordCount.asReadonly();
4670
+ this.isDragOver = this._isDragOver.asReadonly();
4671
+ this.editorFullyInitialized = this._editorFullyInitialized.asReadonly();
4478
4672
  // Computed pour les états de l'éditeur
4479
4673
  this.isEditorReady = computed(() => this.editor() !== null);
4480
4674
  // Computed pour la configuration de la toolbar
@@ -4520,12 +4714,21 @@ class AngularTiptapEditorComponent {
4520
4714
  ...this.imageUpload(),
4521
4715
  }));
4522
4716
  // Computed pour la configuration des slash commands
4523
- this.slashCommandsConfigComputed = computed(() => this.slashCommandsConfig() ?? { commands: undefined });
4717
+ this.slashCommandsConfigComputed = computed(() => {
4718
+ const config = this.slashCommandsConfig();
4719
+ if (config) {
4720
+ return config;
4721
+ }
4722
+ // Configuration par défaut avec toutes les commandes
4723
+ const allCommands = filterSlashCommands(new Set(SLASH_COMMAND_KEYS), this.i18nService);
4724
+ return { commands: allCommands };
4725
+ });
4524
4726
  this._destroyRef = inject(DestroyRef);
4525
4727
  // NgControl pour gérer les FormControls
4526
4728
  this.ngControl = inject(NgControl, { self: true, optional: true });
4527
4729
  this.i18nService = inject(TiptapI18nService);
4528
4730
  this.imageService = inject(ImageService);
4731
+ this.editorCommandsService = inject(EditorCommandsService);
4529
4732
  // Effet pour gérer le changement de langue
4530
4733
  effect(() => {
4531
4734
  const locale = this.locale();
@@ -4566,7 +4769,7 @@ class AngularTiptapEditorComponent {
4566
4769
  const currentEditor = this.editor();
4567
4770
  const isEditable = this.editable();
4568
4771
  if (currentEditor) {
4569
- currentEditor.setEditable(isEditable);
4772
+ this.editorCommandsService.setEditable(currentEditor, isEditable);
4570
4773
  }
4571
4774
  });
4572
4775
  // Effect pour la détection du survol des tables
@@ -4588,7 +4791,7 @@ class AngularTiptapEditorComponent {
4588
4791
  if (currentEditor) {
4589
4792
  currentEditor.destroy();
4590
4793
  }
4591
- this.editorFullyInitialized.set(false);
4794
+ this._editorFullyInitialized.set(false);
4592
4795
  }
4593
4796
  initEditor() {
4594
4797
  const extensions = [
@@ -4666,7 +4869,7 @@ class AngularTiptapEditorComponent {
4666
4869
  // Marquer l'éditeur comme complètement initialisé après un court délai
4667
4870
  // pour s'assurer que tous les plugins et extensions sont prêts
4668
4871
  setTimeout(() => {
4669
- this.editorFullyInitialized.set(true);
4872
+ this._editorFullyInitialized.set(true);
4670
4873
  }, 100);
4671
4874
  },
4672
4875
  onFocus: ({ editor, event }) => {
@@ -4681,13 +4884,13 @@ class AngularTiptapEditorComponent {
4681
4884
  },
4682
4885
  });
4683
4886
  // Stocker la référence de l'éditeur immédiatement
4684
- this.editor.set(newEditor);
4887
+ this._editor.set(newEditor);
4685
4888
  }
4686
4889
  updateCharacterCount(editor) {
4687
4890
  if (this.showCharacterCount() && editor.storage["characterCount"]) {
4688
4891
  const storage = editor.storage["characterCount"];
4689
- this.characterCount.set(storage.characters());
4690
- this.wordCount.set(storage.words());
4892
+ this._characterCount.set(storage.characters());
4893
+ this._wordCount.set(storage.words());
4691
4894
  }
4692
4895
  }
4693
4896
  // Méthodes pour l'upload d'images
@@ -4721,12 +4924,12 @@ class AngularTiptapEditorComponent {
4721
4924
  onDragOver(event) {
4722
4925
  event.preventDefault();
4723
4926
  event.stopPropagation();
4724
- this.isDragOver.set(true);
4927
+ this._isDragOver.set(true);
4725
4928
  }
4726
4929
  onDrop(event) {
4727
4930
  event.preventDefault();
4728
4931
  event.stopPropagation();
4729
- this.isDragOver.set(false);
4932
+ this._isDragOver.set(false);
4730
4933
  const files = event.dataTransfer?.files;
4731
4934
  if (files && files.length > 0) {
4732
4935
  const file = files[0];
@@ -4757,22 +4960,33 @@ class AngularTiptapEditorComponent {
4757
4960
  return this.editor()?.getText() || "";
4758
4961
  }
4759
4962
  setContent(content, emitUpdate = true) {
4760
- this.editor()?.commands.setContent(content, emitUpdate);
4963
+ const editor = this.editor();
4964
+ if (editor) {
4965
+ this.editorCommandsService.setContent(editor, content, emitUpdate);
4966
+ }
4761
4967
  }
4762
4968
  focus() {
4763
- this.editor()?.commands.focus();
4969
+ const editor = this.editor();
4970
+ if (editor) {
4971
+ this.editorCommandsService.focus(editor);
4972
+ }
4764
4973
  }
4765
4974
  blur() {
4766
- this.editor()?.commands.blur();
4975
+ const editor = this.editor();
4976
+ if (editor) {
4977
+ this.editorCommandsService.blur(editor);
4978
+ }
4767
4979
  }
4768
4980
  clearContent() {
4769
4981
  const editor = this.editor();
4770
4982
  if (editor) {
4771
- editor.commands.clearContent();
4772
- // Mettre à jour les compteurs après avoir vidé le contenu
4773
- this.updateCharacterCount(editor);
4983
+ this.editorCommandsService.clearContent(editor);
4774
4984
  }
4775
4985
  }
4986
+ // Méthode publique pour obtenir l'éditeur
4987
+ getEditor() {
4988
+ return this.editor();
4989
+ }
4776
4990
  setupFormControlSubscription() {
4777
4991
  const control = this.ngControl?.control;
4778
4992
  if (control) {
@@ -4791,7 +5005,7 @@ class AngularTiptapEditorComponent {
4791
5005
  setDisabledState(isDisabled) {
4792
5006
  const currentEditor = this.editor();
4793
5007
  if (currentEditor) {
4794
- currentEditor.setEditable(!isDisabled);
5008
+ this.editorCommandsService.setEditable(currentEditor, !isDisabled);
4795
5009
  }
4796
5010
  }
4797
5011
  onEditorClick(event) {
@@ -5000,168 +5214,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "20.0.0", ngImpor
5000
5214
  `, styles: [".tiptap-editor{border:2px solid #e2e8f0;border-radius:8px;background:#fff;overflow:hidden;transition:border-color .2s ease}.tiptap-editor:focus-within{border-color:#3182ce}.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}:host ::ng-deep .ProseMirror table{border-collapse:separate;border-spacing:0;margin:0;table-layout:fixed;width:100%;border-radius:8px;overflow:hidden}:host ::ng-deep .ProseMirror table td,:host ::ng-deep .ProseMirror table th{border:none;border-right:1px solid #e2e8f0;border-bottom:1px solid #e2e8f0;box-sizing:border-box;min-width:1em;padding:8px 12px;position:relative;vertical-align:top;background:#fff}:host ::ng-deep .ProseMirror table td:first-child,:host ::ng-deep .ProseMirror table th:first-child{border-left:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child td,:host ::ng-deep .ProseMirror table tr:first-child th{border-top:1px solid #e2e8f0}:host ::ng-deep .ProseMirror table tr:first-child th:first-child{border-top-left-radius:8px}:host ::ng-deep .ProseMirror table tr:first-child th:last-child{border-top-right-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:first-child{border-bottom-left-radius:8px}:host ::ng-deep .ProseMirror table tr:last-child td:last-child{border-bottom-right-radius:8px}:host ::ng-deep .ProseMirror table th{background:#f8f9fa;font-weight:600;color:#374151}:host ::ng-deep .ProseMirror table .selectedCell:after{background:#c8c8ff66;content:\"\";inset:0;pointer-events:none;position:absolute;z-index:2}:host ::ng-deep .ProseMirror table .column-resize-handle{position:absolute;right:-2px;top:0;bottom:0;width:4px;background-color:#6366f1;opacity:0;transition:opacity .2s ease}:host ::ng-deep .ProseMirror table:hover .column-resize-handle{opacity:1}:host ::ng-deep .ProseMirror table .column-resize-handle:hover{background-color:#4f46e5}:host ::ng-deep .ProseMirror .tableWrapper{overflow-x:auto;margin:1em 0;border-radius:8px}:host ::ng-deep .ProseMirror .tableWrapper table{margin:0;border-radius:8px;min-width:600px;overflow:hidden}:host ::ng-deep .ProseMirror table p{margin:0}:host ::ng-deep .ProseMirror table .selectedCell{background-color:#6366f11a}:host ::ng-deep .ProseMirror table th{background-color:#f8f9fa;font-weight:600;color:#374151;text-align:left}:host ::ng-deep .ProseMirror table tbody tr:nth-child(2n){background-color:#fafbfc}:host ::ng-deep .ProseMirror table tbody tr:hover{background-color:#f1f5f9}\n"] }]
5001
5215
  }], ctorParameters: () => [] });
5002
5216
 
5003
- /**
5004
- * Factory function pour créer les slash commands traduits
5005
- */
5006
- function createI18nSlashCommands(i18nService) {
5007
- const slashCommands = i18nService.slashCommands();
5008
- return [
5009
- {
5010
- title: slashCommands.heading1.title,
5011
- description: slashCommands.heading1.description,
5012
- icon: "format_h1",
5013
- keywords: slashCommands.heading1.keywords,
5014
- command: (editor) => editor.chain().focus().toggleHeading({ level: 1 }).run(),
5015
- },
5016
- {
5017
- title: slashCommands.heading2.title,
5018
- description: slashCommands.heading2.description,
5019
- icon: "format_h2",
5020
- keywords: slashCommands.heading2.keywords,
5021
- command: (editor) => editor.chain().focus().toggleHeading({ level: 2 }).run(),
5022
- },
5023
- {
5024
- title: slashCommands.heading3.title,
5025
- description: slashCommands.heading3.description,
5026
- icon: "format_h3",
5027
- keywords: slashCommands.heading3.keywords,
5028
- command: (editor) => editor.chain().focus().toggleHeading({ level: 3 }).run(),
5029
- },
5030
- {
5031
- title: slashCommands.bulletList.title,
5032
- description: slashCommands.bulletList.description,
5033
- icon: "format_list_bulleted",
5034
- keywords: slashCommands.bulletList.keywords,
5035
- command: (editor) => editor.chain().focus().toggleBulletList().run(),
5036
- },
5037
- {
5038
- title: slashCommands.orderedList.title,
5039
- description: slashCommands.orderedList.description,
5040
- icon: "format_list_numbered",
5041
- keywords: slashCommands.orderedList.keywords,
5042
- command: (editor) => editor.chain().focus().toggleOrderedList().run(),
5043
- },
5044
- {
5045
- title: slashCommands.blockquote.title,
5046
- description: slashCommands.blockquote.description,
5047
- icon: "format_quote",
5048
- keywords: slashCommands.blockquote.keywords,
5049
- command: (editor) => editor.chain().focus().toggleBlockquote().run(),
5050
- },
5051
- {
5052
- title: slashCommands.code.title,
5053
- description: slashCommands.code.description,
5054
- icon: "code",
5055
- keywords: slashCommands.code.keywords,
5056
- command: (editor) => editor.chain().focus().toggleCodeBlock().run(),
5057
- },
5058
- {
5059
- title: slashCommands.image.title,
5060
- description: slashCommands.image.description,
5061
- icon: "image",
5062
- keywords: slashCommands.image.keywords,
5063
- command: (editor) => {
5064
- // Créer un input file temporaire pour sélectionner une image
5065
- const input = document.createElement("input");
5066
- input.type = "file";
5067
- input.accept = "image/*";
5068
- input.style.display = "none";
5069
- input.addEventListener("change", async (e) => {
5070
- const file = e.target.files?.[0];
5071
- if (file && file.type.startsWith("image/")) {
5072
- try {
5073
- // Utiliser la méthode de compression unifiée
5074
- const canvas = document.createElement("canvas");
5075
- const ctx = canvas.getContext("2d");
5076
- const img = new Image();
5077
- img.onload = () => {
5078
- // Vérifier les dimensions (max 1920x1080)
5079
- const maxWidth = 1920;
5080
- const maxHeight = 1080;
5081
- let { width, height } = img;
5082
- // Redimensionner si nécessaire
5083
- if (width > maxWidth || height > maxHeight) {
5084
- const ratio = Math.min(maxWidth / width, maxHeight / height);
5085
- width *= ratio;
5086
- height *= ratio;
5087
- }
5088
- canvas.width = width;
5089
- canvas.height = height;
5090
- // Dessiner l'image redimensionnée
5091
- ctx?.drawImage(img, 0, 0, width, height);
5092
- // Convertir en base64 avec compression
5093
- canvas.toBlob((blob) => {
5094
- if (blob) {
5095
- const reader = new FileReader();
5096
- reader.onload = (e) => {
5097
- const base64 = e.target?.result;
5098
- if (base64) {
5099
- // Utiliser setResizableImage avec toutes les propriétés
5100
- editor
5101
- .chain()
5102
- .focus()
5103
- .setResizableImage({
5104
- src: base64,
5105
- alt: file.name,
5106
- title: `${file.name} (${Math.round(width)}×${Math.round(height)})`,
5107
- width: Math.round(width),
5108
- height: Math.round(height),
5109
- })
5110
- .run();
5111
- }
5112
- };
5113
- reader.readAsDataURL(blob);
5114
- }
5115
- }, file.type, 0.8 // qualité de compression
5116
- );
5117
- };
5118
- img.onerror = () => {
5119
- console.error(i18nService.editor().imageLoadError);
5120
- };
5121
- img.src = URL.createObjectURL(file);
5122
- }
5123
- catch (error) {
5124
- console.error("Error uploading image:", error);
5125
- }
5126
- }
5127
- document.body.removeChild(input);
5128
- });
5129
- document.body.appendChild(input);
5130
- input.click();
5131
- },
5132
- },
5133
- {
5134
- title: slashCommands.horizontalRule.title,
5135
- description: slashCommands.horizontalRule.description,
5136
- icon: "horizontal_rule",
5137
- keywords: slashCommands.horizontalRule.keywords,
5138
- command: (editor) => editor.chain().focus().setHorizontalRule().run(),
5139
- },
5140
- {
5141
- title: slashCommands.table.title,
5142
- description: slashCommands.table.description,
5143
- icon: "table_view",
5144
- keywords: slashCommands.table.keywords,
5145
- command: (editor) => editor.chain().focus().insertTable({ rows: 3, cols: 3 }).run(),
5146
- },
5147
- ];
5148
- }
5149
- /**
5150
- * Mapping des clés de commandes pour la compatibilité
5151
- */
5152
- const SLASH_COMMAND_KEYS = {
5153
- heading1: "heading1",
5154
- heading2: "heading2",
5155
- heading3: "heading3",
5156
- bulletList: "bulletList",
5157
- orderedList: "orderedList",
5158
- blockquote: "blockquote",
5159
- code: "code",
5160
- image: "image",
5161
- horizontalRule: "horizontalRule",
5162
- table: "table",
5163
- };
5164
-
5165
5217
  /*
5166
5218
  * Public API Surface of tiptap-editor
5167
5219
  */
@@ -5171,5 +5223,5 @@ const SLASH_COMMAND_KEYS = {
5171
5223
  * Generated bundle index. Do not edit.
5172
5224
  */
5173
5225
 
5174
- export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, NoopValueAccessorDirective, TiptapI18nService, createI18nSlashCommands };
5226
+ export { AngularTiptapEditorComponent, DEFAULT_BUBBLE_MENU_CONFIG, DEFAULT_CELL_MENU_CONFIG, DEFAULT_IMAGE_BUBBLE_MENU_CONFIG, DEFAULT_SLASH_COMMANDS$1 as DEFAULT_SLASH_COMMANDS, DEFAULT_TABLE_MENU_CONFIG, DEFAULT_TOOLBAR_CONFIG, EditorCommandsService, ImageService, NoopValueAccessorDirective, SLASH_COMMAND_KEYS, TiptapI18nService, createI18nSlashCommands, filterSlashCommands };
5175
5227
  //# sourceMappingURL=flogeez-angular-tiptap-editor.mjs.map