@handsontable/angular-wrapper 0.0.0-next-a4c3bb2-20260211 → 0.0.0-next-48acfa2-20260212

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.
Files changed (28) hide show
  1. package/esm2022/lib/editor/custom-editor-placeholder.component.mjs +8 -5
  2. package/esm2022/lib/editor/editor-factory-adapter.mjs +140 -0
  3. package/esm2022/lib/editor/hot-cell-editor-advanced.component.mjs +92 -0
  4. package/esm2022/lib/editor/hot-cell-editor.component.mjs +2 -1
  5. package/esm2022/lib/editor/models/factory-editor-properties.mjs +2 -0
  6. package/esm2022/lib/editor/models/keyboard-shortcut-config.mjs +2 -0
  7. package/esm2022/lib/hot-table.component.mjs +7 -4
  8. package/esm2022/lib/hot-table.module.mjs +2 -2
  9. package/esm2022/lib/models/column-settings.mjs +1 -1
  10. package/esm2022/lib/renderer/hot-cell-renderer-advanced.component.mjs +55 -0
  11. package/esm2022/lib/renderer/hot-dynamic-renderer-component.service.mjs +62 -10
  12. package/esm2022/lib/services/hot-settings-resolver.service.mjs +54 -17
  13. package/esm2022/public-api.mjs +4 -1
  14. package/fesm2022/handsontable-angular-wrapper.mjs +496 -126
  15. package/fesm2022/handsontable-angular-wrapper.mjs.map +1 -1
  16. package/lib/editor/custom-editor-placeholder.component.d.ts +4 -2
  17. package/lib/editor/editor-factory-adapter.d.ts +13 -0
  18. package/lib/editor/hot-cell-editor-advanced.component.d.ts +74 -0
  19. package/lib/editor/hot-cell-editor.component.d.ts +1 -0
  20. package/lib/editor/models/factory-editor-properties.d.ts +24 -0
  21. package/lib/editor/models/keyboard-shortcut-config.d.ts +13 -0
  22. package/lib/hot-table.component.d.ts +3 -2
  23. package/lib/models/column-settings.d.ts +7 -2
  24. package/lib/renderer/hot-cell-renderer-advanced.component.d.ts +33 -0
  25. package/lib/renderer/hot-dynamic-renderer-component.service.d.ts +19 -0
  26. package/lib/services/hot-settings-resolver.service.d.ts +3 -0
  27. package/package.json +1 -1
  28. package/public-api.d.ts +3 -0
@@ -1,8 +1,9 @@
1
1
  import * as i0 from '@angular/core';
2
- import { ViewContainerRef, Component, ChangeDetectionStrategy, Input, ViewChild, createComponent, Injectable, InjectionToken, Inject, ViewEncapsulation, NgModule, EventEmitter, Directive, HostBinding, Output } from '@angular/core';
2
+ import { ViewContainerRef, Component, ChangeDetectionStrategy, Input, ViewChild, createComponent, EventEmitter, Directive, HostBinding, Output, Injectable, InjectionToken, Inject, ViewEncapsulation, NgModule } from '@angular/core';
3
3
  import Handsontable from 'handsontable/base';
4
4
  import { take } from 'rxjs/operators';
5
- import { baseRenderer } from 'handsontable/renderers';
5
+ import { editorFactory } from 'handsontable/editors/factory';
6
+ import { baseRenderer, rendererFactory, registerRenderer } from 'handsontable/renderers';
6
7
  import { BehaviorSubject } from 'rxjs';
7
8
 
8
9
  /**
@@ -21,6 +22,7 @@ class CustomEditorPlaceholderComponent {
21
22
  set isVisible(value) {
22
23
  this._isVisible = value;
23
24
  }
25
+ placeholderCustomClass = 'handsontableInputHolder ht_clone_master';
24
26
  /** The reference to the component instance of the editor. */
25
27
  set componentRef(hotEditorComponentRef) {
26
28
  if (hotEditorComponentRef) {
@@ -41,8 +43,8 @@ class CustomEditorPlaceholderComponent {
41
43
  this.container.detach();
42
44
  }
43
45
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: CustomEditorPlaceholderComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
44
- static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CustomEditorPlaceholderComponent, selector: "ng-component", inputs: { top: "top", left: "left", height: "height", width: "width", isVisible: "isVisible", componentRef: "componentRef" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["inputPlaceholder"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: ` <div
45
- class="handsontableInputHolder ht_clone_master"
46
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: CustomEditorPlaceholderComponent, selector: "ng-component", inputs: { top: "top", left: "left", height: "height", width: "width", isVisible: "isVisible", placeholderCustomClass: "placeholderCustomClass", componentRef: "componentRef" }, viewQueries: [{ propertyName: "container", first: true, predicate: ["inputPlaceholder"], descendants: true, read: ViewContainerRef, static: true }], ngImport: i0, template: ` <div
47
+ [class]="placeholderCustomClass"
46
48
  [style.display]="display"
47
49
  [style.width.px]="width"
48
50
  [style.height.px]="height"
@@ -58,7 +60,7 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
58
60
  type: Component,
59
61
  args: [{
60
62
  template: ` <div
61
- class="handsontableInputHolder ht_clone_master"
63
+ [class]="placeholderCustomClass"
62
64
  [style.display]="display"
63
65
  [style.width.px]="width"
64
66
  [style.height.px]="height"
@@ -82,6 +84,8 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
82
84
  type: Input
83
85
  }], isVisible: [{
84
86
  type: Input
87
+ }], placeholderCustomClass: [{
88
+ type: Input
85
89
  }], componentRef: [{
86
90
  type: Input
87
91
  }], container: [{
@@ -318,9 +322,384 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
318
322
  type: Input
319
323
  }] } });
320
324
 
325
+ /**
326
+ * Abstract class representing a Handsontable editor in angular.
327
+ */
328
+ class HotCellEditorComponent {
329
+ static EDITOR_MARKER = Symbol('HotCellEditorComponent');
330
+ /** The tabindex attribute for the editor. */
331
+ tabindex = -1;
332
+ /** The data-hot-input attribute for the editor. */
333
+ dataHotInput = '';
334
+ /** The handsontableInput class for the editor. */
335
+ handsontableInputClass = true;
336
+ /** The height of the editor as a percentage of the parent container. */
337
+ heightFitParentContainer = 100;
338
+ /** The width of the editor as a percentage of the parent container. */
339
+ widthFitParentContainer = 100;
340
+ /** The row index of the cell being edited. */
341
+ row;
342
+ /** The column index of the cell being edited. */
343
+ column;
344
+ /** The property name of the cell being edited. */
345
+ prop;
346
+ /** The original value of the cell being edited. */
347
+ originalValue;
348
+ /** The cell properties of the cell being edited. */
349
+ cellProperties;
350
+ /** Event emitted when the edit is finished.
351
+ * The data will be saved to the model.
352
+ */
353
+ finishEdit = new EventEmitter();
354
+ /** Event emitted when the edit is canceled.
355
+ * The entered data will be reverted to the original value.
356
+ */
357
+ cancelEdit = new EventEmitter();
358
+ /** The current value of the editor. */
359
+ _value;
360
+ /** Event triggered by Handsontable on closing the editor.
361
+ * The user can define their own actions for
362
+ * the custom editor to be called after the base logic. */
363
+ onClose() { }
364
+ /** Event triggered by Handsontable on open the editor.
365
+ * The user can define their own actions for
366
+ * the custom editor to be called after the base logic. */
367
+ onOpen(event) { }
368
+ /**
369
+ * Gets the current value of the editor.
370
+ * @returns The current value of the editor.
371
+ */
372
+ getValue() {
373
+ return this._value;
374
+ }
375
+ /**
376
+ * Sets the current value of the editor.
377
+ * @param value The value to set.
378
+ */
379
+ setValue(value) {
380
+ this._value = value;
381
+ }
382
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
383
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HotCellEditorComponent, inputs: { row: "row", column: "column", prop: "prop", originalValue: "originalValue", cellProperties: "cellProperties" }, outputs: { finishEdit: "finishEdit", cancelEdit: "cancelEdit" }, host: { properties: { "attr.tabindex": "this.tabindex", "attr.data-hot-input": "this.dataHotInput", "class.handsontableInput": "this.handsontableInputClass", "style.height.%": "this.heightFitParentContainer", "style.width.%": "this.widthFitParentContainer" } }, ngImport: i0 });
384
+ }
385
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, decorators: [{
386
+ type: Directive
387
+ }], propDecorators: { tabindex: [{
388
+ type: HostBinding,
389
+ args: ['attr.tabindex']
390
+ }], dataHotInput: [{
391
+ type: HostBinding,
392
+ args: ['attr.data-hot-input']
393
+ }], handsontableInputClass: [{
394
+ type: HostBinding,
395
+ args: ['class.handsontableInput']
396
+ }], heightFitParentContainer: [{
397
+ type: HostBinding,
398
+ args: ['style.height.%']
399
+ }], widthFitParentContainer: [{
400
+ type: HostBinding,
401
+ args: ['style.width.%']
402
+ }], row: [{
403
+ type: Input
404
+ }], column: [{
405
+ type: Input
406
+ }], prop: [{
407
+ type: Input
408
+ }], originalValue: [{
409
+ type: Input
410
+ }], cellProperties: [{
411
+ type: Input
412
+ }], finishEdit: [{
413
+ type: Output
414
+ }], cancelEdit: [{
415
+ type: Output
416
+ }] } });
417
+
418
+ /**
419
+ * Factory function to create a custom Handsontable editor adapter for Angular components.
420
+ *
421
+ * This adapter integrates Angular components with Handsontable's editor system using the new
422
+ * editorFactory API, allowing you to use Angular components as custom cell editors while
423
+ * maintaining full Angular lifecycle management and change detection.
424
+ *
425
+ * @returns A custom editor class that can be used in Handsontable column settings.
426
+ *
427
+ */
428
+ const FactoryEditorAdapter = (componentRef) => editorFactory({
429
+ position: componentRef.instance.position,
430
+ shortcuts: componentRef.instance.shortcuts,
431
+ config: componentRef.instance.config,
432
+ init(editor) {
433
+ editor._componentRef = componentRef;
434
+ editor._editorPlaceHolderRef = undefined;
435
+ editor._finishEditSubscription = undefined;
436
+ editor._cancelEditSubscription = undefined;
437
+ createEditorPlaceholder(editor, editor.hot._angularEnvironmentInjector);
438
+ editor.input = editor._editorPlaceHolderRef.location.nativeElement;
439
+ editor._afterRowResizeCallback = () => {
440
+ if (editor.isOpened()) {
441
+ applyPropsToEditor(editor);
442
+ }
443
+ };
444
+ editor._afterColumnResizeCallback = () => {
445
+ if (editor.isOpened()) {
446
+ applyPropsToEditor(editor);
447
+ }
448
+ };
449
+ editor._afterDestroyCallback = () => {
450
+ if (editor._editorPlaceHolderRef) {
451
+ editor._editorPlaceHolderRef.destroy();
452
+ }
453
+ };
454
+ // Hooks are automatically removed by Handsontable on table destroy
455
+ editor.hot.addHook('afterRowResize', editor._afterRowResizeCallback);
456
+ editor.hot.addHook('afterColumnResize', editor._afterColumnResizeCallback);
457
+ editor.hot.addHook('afterDestroy', editor._afterDestroyCallback);
458
+ },
459
+ afterInit: (editor) => editor._componentRef.instance.afterInit?.(editor),
460
+ beforeOpen: (editor, context) => {
461
+ cleanupSubscriptions(editor);
462
+ applyPropsToEditor(editor);
463
+ editor._finishEditSubscription = editor._componentRef.instance.finishEdit.pipe(take(1)).subscribe(() => {
464
+ editor.finishEditing();
465
+ });
466
+ editor._cancelEditSubscription = editor._componentRef.instance.cancelEdit.pipe(take(1)).subscribe(() => {
467
+ editor.cancelChanges();
468
+ });
469
+ editor._componentRef.instance.beforeOpen?.(editor, context);
470
+ },
471
+ afterOpen: (editor, event) => {
472
+ editor._componentRef.instance.afterOpen?.(editor, event);
473
+ },
474
+ onFocus: (editor) => editor._componentRef.instance.onFocus?.(editor),
475
+ afterClose: (editor) => {
476
+ resetEditorState(editor);
477
+ editor._editorPlaceHolderRef.changeDetectorRef.detectChanges();
478
+ editor._editorPlaceHolderRef.instance.detachEditor();
479
+ editor._componentRef.instance.afterClose?.(editor);
480
+ },
481
+ getValue: (editor) => editor._componentRef.instance.getValue(),
482
+ setValue: (editor, value) => {
483
+ editor.value = value;
484
+ editor._componentRef.instance.setValue(value);
485
+ editor._componentRef.changeDetectorRef.detectChanges();
486
+ },
487
+ });
488
+ /**
489
+ * Creates the editor placeholder component.
490
+ * @param editor The editor instance.
491
+ * @param injector The environment injector from Angular.
492
+ */
493
+ function createEditorPlaceholder(editor, injector) {
494
+ if (!injector) {
495
+ return;
496
+ }
497
+ editor._editorPlaceHolderRef = createComponent(CustomEditorPlaceholderComponent, {
498
+ environmentInjector: injector,
499
+ });
500
+ }
501
+ /**
502
+ * Applies properties to the custom Angular editor and editor placeholder.
503
+ * Updates position, size, and cell context information.
504
+ * @param editor The editor instance.
505
+ */
506
+ function applyPropsToEditor(editor) {
507
+ if (!editor._componentRef || !editor._editorPlaceHolderRef) {
508
+ return;
509
+ }
510
+ editor._componentRef.setInput('originalValue', editor.originalValue);
511
+ editor._componentRef.setInput('row', editor.row);
512
+ editor._componentRef.setInput('column', editor.col);
513
+ editor._componentRef.setInput('prop', editor.prop);
514
+ editor._componentRef.setInput('cellProperties', editor.cellProperties);
515
+ const rect = editor.hot.getCell(editor.row, editor.col)?.getBoundingClientRect();
516
+ editor._editorPlaceHolderRef.setInput('placeholderCustomClass', '');
517
+ editor._editorPlaceHolderRef.setInput('height', rect.height);
518
+ editor._editorPlaceHolderRef.setInput('width', rect.width);
519
+ editor._editorPlaceHolderRef.setInput('isVisible', true);
520
+ editor._editorPlaceHolderRef.setInput('componentRef', editor._componentRef);
521
+ editor._editorPlaceHolderRef.changeDetectorRef.detectChanges();
522
+ }
523
+ /**
524
+ * Resets the editor placeholder state.
525
+ * Clears all positioning and visibility settings.
526
+ * @param editor The editor instance.
527
+ */
528
+ function resetEditorState(editor) {
529
+ if (!editor._editorPlaceHolderRef) {
530
+ return;
531
+ }
532
+ editor._editorPlaceHolderRef.setInput('top', undefined);
533
+ editor._editorPlaceHolderRef.setInput('left', undefined);
534
+ editor._editorPlaceHolderRef.setInput('height', undefined);
535
+ editor._editorPlaceHolderRef.setInput('width', undefined);
536
+ editor._editorPlaceHolderRef.setInput('isVisible', false);
537
+ editor._editorPlaceHolderRef.setInput('componentRef', undefined);
538
+ }
539
+ /**
540
+ * Cleans up existing subscriptions.
541
+ * @param editor The editor instance.
542
+ */
543
+ function cleanupSubscriptions(editor) {
544
+ if (editor._finishEditSubscription) {
545
+ editor._finishEditSubscription.unsubscribe();
546
+ editor._finishEditSubscription = undefined;
547
+ }
548
+ if (editor._cancelEditSubscription) {
549
+ editor._cancelEditSubscription.unsubscribe();
550
+ editor._cancelEditSubscription = undefined;
551
+ }
552
+ }
553
+
554
+ /**
555
+ * Abstract base component for creating advanced custom cell renderer components for Handsontable.
556
+ *
557
+ * This class provides a common interface and properties required by any custom cell renderer.
558
+ *
559
+ * @template TValue - The type of the component renderer.
560
+ * @template TProps - The type of additional renderer properties.
561
+ */
562
+ class HotCellRendererAdvancedComponent {
563
+ static RENDERER_MARKER = Symbol('HotCellRendererAdvancedComponent');
564
+ value = '';
565
+ instance;
566
+ td;
567
+ row;
568
+ col;
569
+ prop;
570
+ /**
571
+ * The cell properties provided by Handsontable, extended with optional renderer-specific properties.
572
+ */
573
+ cellProperties;
574
+ /**
575
+ * Retrieves the renderer-specific properties from the cell properties.
576
+ *
577
+ * @returns The additional properties for the renderer.
578
+ */
579
+ getProps() {
580
+ return this.cellProperties?.rendererProps ?? {};
581
+ }
582
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellRendererAdvancedComponent, deps: [], target: i0.ɵɵFactoryTarget.Component });
583
+ static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HotCellRendererAdvancedComponent, selector: "hot-cell-renderer-advanced", inputs: { value: "value", instance: "instance", td: "td", row: "row", col: "col", prop: "prop", cellProperties: "cellProperties" }, ngImport: i0, template: `<!-- This is an abstract component. Extend this component and provide your own template. -->`, isInline: true });
584
+ }
585
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellRendererAdvancedComponent, decorators: [{
586
+ type: Component,
587
+ args: [{
588
+ selector: 'hot-cell-renderer-advanced',
589
+ template: `<!-- This is an abstract component. Extend this component and provide your own template. -->`,
590
+ }]
591
+ }], propDecorators: { value: [{
592
+ type: Input
593
+ }], instance: [{
594
+ type: Input
595
+ }], td: [{
596
+ type: Input
597
+ }], row: [{
598
+ type: Input
599
+ }], col: [{
600
+ type: Input
601
+ }], prop: [{
602
+ type: Input
603
+ }], cellProperties: [{
604
+ type: Input
605
+ }] } });
606
+
607
+ /**
608
+ * Abstract class representing a Handsontable editor in angular.
609
+ */
610
+ class HotCellEditorAdvancedComponent {
611
+ static EDITOR_MARKER = Symbol('HotCellEditorAdvancedComponent');
612
+ /** The height of the editor as a percentage of the parent container. */
613
+ heightFitParentContainer = 100;
614
+ /** The width of the editor as a percentage of the parent container. */
615
+ widthFitParentContainer = 100;
616
+ /** The row index of the cell being edited. */
617
+ row;
618
+ /** The column index of the cell being edited. */
619
+ column;
620
+ /** The property name of the cell being edited. */
621
+ prop;
622
+ /** The original value of the cell being edited. */
623
+ originalValue;
624
+ /** The cell properties of the cell being edited. */
625
+ cellProperties;
626
+ /** Event emitted when the edit is finished.
627
+ * The data will be saved to the model.
628
+ */
629
+ finishEdit = new EventEmitter();
630
+ /** Event emitted when the edit is canceled.
631
+ * The entered data will be reverted to the original value.
632
+ */
633
+ cancelEdit = new EventEmitter();
634
+ /** The current value of the editor. */
635
+ value;
636
+ /** Event triggered by Handsontable on focus the editor.
637
+ * The user have to define focus logic.
638
+ */
639
+ onFocus(editor) { }
640
+ /**
641
+ * Gets the current value of the editor.
642
+ * @returns The current value of the editor.
643
+ */
644
+ getValue() {
645
+ return this.value;
646
+ }
647
+ /**
648
+ * Sets the current value of the editor.
649
+ * @param value The value to set.
650
+ */
651
+ setValue(value) {
652
+ this.value = value;
653
+ }
654
+ /** The position of the editor in the DOM. Used by Handsontable API. Available in advanced mode. */
655
+ position = 'container';
656
+ /** The shortcuts available for the editor. Available in advanced mode. */
657
+ shortcuts;
658
+ /** The group name for the shortcuts. Available in advanced mode.*/
659
+ shortcutsGroup;
660
+ /** Configuration. Available in advanced mode. */
661
+ config;
662
+ /** Lifecycle hook called after the editor is opened. Available in advanced mode.*/
663
+ afterOpen(editor, event) { }
664
+ /** Lifecycle hook called after the editor is closed. Available in advanced mode. */
665
+ afterClose(editor) { }
666
+ /** Lifecycle hook called after the editor is initialized. Available in advanced mode.*/
667
+ afterInit(editor) { }
668
+ /** Lifecycle hook called before the editor is opened. Available in advanced mode. */
669
+ beforeOpen(editor, { row, col, prop, td, originalValue, cellProperties, }) { }
670
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorAdvancedComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
671
+ static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HotCellEditorAdvancedComponent, inputs: { row: "row", column: "column", prop: "prop", originalValue: "originalValue", cellProperties: "cellProperties" }, outputs: { finishEdit: "finishEdit", cancelEdit: "cancelEdit" }, host: { properties: { "style.height.%": "this.heightFitParentContainer", "style.width.%": "this.widthFitParentContainer" } }, ngImport: i0 });
672
+ }
673
+ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorAdvancedComponent, decorators: [{
674
+ type: Directive
675
+ }], propDecorators: { heightFitParentContainer: [{
676
+ type: HostBinding,
677
+ args: ['style.height.%']
678
+ }], widthFitParentContainer: [{
679
+ type: HostBinding,
680
+ args: ['style.width.%']
681
+ }], row: [{
682
+ type: Input
683
+ }], column: [{
684
+ type: Input
685
+ }], prop: [{
686
+ type: Input
687
+ }], originalValue: [{
688
+ type: Input
689
+ }], cellProperties: [{
690
+ type: Input
691
+ }], finishEdit: [{
692
+ type: Output
693
+ }], cancelEdit: [{
694
+ type: Output
695
+ }] } });
696
+
321
697
  const INVALID_RENDERER_WARNING = 'The provided renderer component was not recognized as a valid custom renderer. ' +
322
698
  'It must either extend HotCellRendererComponent or be a valid TemplateRef. ' +
323
699
  'Please ensure that your custom renderer is implemented correctly and imported from the proper source.';
700
+ const INVALID_ADVANCED_RENDERER_WARNING = 'The provided renderer component was not recognized as a valid custom renderer. ' +
701
+ 'It must either extend HotCellRendererAdvancedComponent. ' +
702
+ 'Please ensure that your custom renderer is implemented correctly and imported from the proper source.';
324
703
  /**
325
704
  * Type guard that checks if the given object is a TemplateRef.
326
705
  *
@@ -339,6 +718,15 @@ function isTemplateRef(obj) {
339
718
  function isHotCellRendererComponent(obj) {
340
719
  return obj?.RENDERER_MARKER === HotCellRendererComponent.RENDERER_MARKER;
341
720
  }
721
+ /**
722
+ * Type guard to check if an object is an instance of HotCellRendererAdvancedComponent.
723
+ *
724
+ * @param obj - The object to check.
725
+ * @returns True if the object is a HotCellRendererAdvancedComponent, false otherwise.
726
+ */
727
+ function isAdvancedHotCellRendererComponent(obj) {
728
+ return obj?.RENDERER_MARKER === HotCellRendererAdvancedComponent.RENDERER_MARKER;
729
+ }
342
730
  /**
343
731
  * Service for dynamically creating Angular components or templates as custom renderers for Handsontable.
344
732
  *
@@ -368,14 +756,18 @@ class DynamicComponentService {
368
756
  createRendererFromComponent(component, componentProps = {}, register = false) {
369
757
  return (instance, td, row, col, prop, value, cellProperties) => {
370
758
  const properties = {
371
- value, instance, td, row, col, prop, cellProperties
759
+ value,
760
+ instance,
761
+ td,
762
+ row,
763
+ col,
764
+ prop,
765
+ cellProperties,
372
766
  };
373
767
  if (componentProps) {
374
768
  Object.assign(cellProperties, { rendererProps: componentProps });
375
769
  }
376
- const rendererParameters = [
377
- instance, td, row, col, prop, value, cellProperties
378
- ];
770
+ const rendererParameters = [instance, td, row, col, prop, value, cellProperties];
379
771
  baseRenderer.apply(this, rendererParameters);
380
772
  td.innerHTML = '';
381
773
  if (isTemplateRef(component)) {
@@ -394,6 +786,42 @@ class DynamicComponentService {
394
786
  return td;
395
787
  };
396
788
  }
789
+ /**
790
+ * Creates a custom renderer function using rendererFactory from Handsontable.
791
+ * This is an alternative implementation that uses the factory pattern.
792
+ *
793
+ * @param component - The Angular component type to use as renderer.
794
+ * @param componentProps - An object containing additional properties to use by the renderer.
795
+ * @param register - If true, registers the renderer with Handsontable using the component's name.
796
+ * @returns A renderer function that can be used in Handsontable's configuration.
797
+ */
798
+ createRendererWithFactory(component, componentProps = {}, register = false) {
799
+ return rendererFactory(({ instance, td, row, column, prop, value, cellProperties }) => {
800
+ const properties = {
801
+ value,
802
+ instance,
803
+ td,
804
+ row,
805
+ col: column,
806
+ prop,
807
+ cellProperties,
808
+ };
809
+ if (componentProps) {
810
+ Object.assign(cellProperties, { rendererProps: componentProps });
811
+ }
812
+ td.innerHTML = '';
813
+ if (isAdvancedHotCellRendererComponent(component)) {
814
+ const componentRef = this.createComponent(component, properties);
815
+ this.attachComponentToElement(componentRef, td);
816
+ }
817
+ else {
818
+ console.warn(INVALID_ADVANCED_RENDERER_WARNING);
819
+ }
820
+ if (register && isAdvancedHotCellRendererComponent(component)) {
821
+ registerRenderer(component.constructor.name, component);
822
+ }
823
+ });
824
+ }
397
825
  /**
398
826
  * Attaches an embedded view created from a TemplateRef to a given DOM element.
399
827
  *
@@ -420,9 +848,9 @@ class DynamicComponentService {
420
848
  */
421
849
  createComponent(component, rendererParameters) {
422
850
  const componentRef = createComponent(component, {
423
- environmentInjector: this.environmentInjector
851
+ environmentInjector: this.environmentInjector,
424
852
  });
425
- Object.keys(rendererParameters).forEach(key => {
853
+ Object.keys(rendererParameters).forEach((key) => {
426
854
  if (rendererParameters.hasOwnProperty(key)) {
427
855
  componentRef.setInput(key, rendererParameters[key]);
428
856
  }
@@ -441,8 +869,7 @@ class DynamicComponentService {
441
869
  * @param container - The target DOM element to which the component's root node will be appended.
442
870
  */
443
871
  attachComponentToElement(componentRef, container) {
444
- const domElem = componentRef.hostView
445
- .rootNodes[0];
872
+ const domElem = componentRef.hostView.rootNodes[0];
446
873
  container.appendChild(domElem);
447
874
  }
448
875
  /**
@@ -498,7 +925,7 @@ class HotSettingsResolver {
498
925
  */
499
926
  wrapHooksInNgZone(settings, ngZone) {
500
927
  const options = AVAILABLE_HOOKS.concat(AVAILABLE_OPTIONS);
501
- options.forEach(key => {
928
+ options.forEach((key) => {
502
929
  const isHook = AVAILABLE_HOOKS.indexOf(key) > -1;
503
930
  let option;
504
931
  if (isHook) {
@@ -507,7 +934,7 @@ class HotSettingsResolver {
507
934
  if (option === void 0) {
508
935
  return;
509
936
  }
510
- else if (!!ngZone && (typeof option === 'function' && isHook)) {
937
+ else if (!!ngZone && typeof option === 'function' && isHook) {
511
938
  settings[key] = function (...args) {
512
939
  return ngZone.run(() => option.apply(this, args));
513
940
  };
@@ -526,13 +953,16 @@ class HotSettingsResolver {
526
953
  return;
527
954
  }
528
955
  mergedSettings?.columns
529
- ?.filter((settings) => this.isRendererComponentRefType(settings.renderer) || this.isTemplateRef(settings.renderer))
956
+ ?.filter((settings) => this.isCustomRenderer(settings.renderer))
530
957
  ?.forEach((cellSettings) => {
531
- const renderer = this.isTemplateRef(cellSettings.renderer)
532
- ? cellSettings.renderer
533
- : cellSettings.renderer;
958
+ const renderer = cellSettings.renderer;
534
959
  const props = cellSettings.rendererProps ?? {};
535
- cellSettings.renderer = this.dynamicComponentService.createRendererFromComponent(renderer, props);
960
+ if (this.isAdvancedRendererComponentRefType(renderer)) {
961
+ cellSettings.renderer = this.dynamicComponentService.createRendererWithFactory(renderer, props);
962
+ }
963
+ else if (this.isRendererComponentRefType(renderer) || this.isTemplateRef(renderer)) {
964
+ cellSettings.renderer = this.dynamicComponentService.createRendererFromComponent(renderer, props);
965
+ }
536
966
  });
537
967
  }
538
968
  /**
@@ -544,14 +974,22 @@ class HotSettingsResolver {
544
974
  return;
545
975
  }
546
976
  mergedSettings?.columns
547
- ?.filter((settings) => this.isEditorComponentRefType(settings.editor))
977
+ ?.filter((settings) => this.isEditorComponentRefType(settings.editor) || this.isAdvancedEditorComponentRefType(settings.editor))
548
978
  ?.forEach((cellSettings) => {
549
- const customEditor = cellSettings.editor;
550
- cellSettings['_editorComponentReference'] = createComponent(customEditor, {
551
- environmentInjector: this.environmentInjector,
552
- });
553
- cellSettings['_environmentInjector'] = this.environmentInjector;
554
- cellSettings.editor = BaseEditorAdapter;
979
+ if (this.isAdvancedEditorComponentRefType(cellSettings.editor)) {
980
+ const component = createComponent(cellSettings.editor, {
981
+ environmentInjector: this.environmentInjector,
982
+ });
983
+ cellSettings.editor = FactoryEditorAdapter(component);
984
+ }
985
+ else {
986
+ const component = createComponent(cellSettings.editor, {
987
+ environmentInjector: this.environmentInjector,
988
+ });
989
+ cellSettings['_editorComponentReference'] = component;
990
+ cellSettings['_environmentInjector'] = this.environmentInjector;
991
+ cellSettings.editor = BaseEditorAdapter;
992
+ }
555
993
  });
556
994
  }
557
995
  /**
@@ -576,15 +1014,36 @@ class HotSettingsResolver {
576
1014
  }
577
1015
  isEditorComponentRefType(editor) {
578
1016
  // ecmp - we need it to check if the editor is a component
579
- return typeof editor === 'function' && !!editor?.ɵcmp;
1017
+ return typeof editor === 'function' &&
1018
+ !!editor?.ɵcmp &&
1019
+ editor?.EDITOR_MARKER === HotCellEditorComponent.EDITOR_MARKER;
1020
+ }
1021
+ isAdvancedEditorComponentRefType(editor) {
1022
+ // ecmp - we need it to check if the editor is a component
1023
+ return typeof editor === 'function' &&
1024
+ !!editor?.ɵcmp &&
1025
+ editor?.EDITOR_MARKER === HotCellEditorAdvancedComponent.EDITOR_MARKER;
580
1026
  }
581
1027
  isRendererComponentRefType(renderer) {
582
1028
  // ecmp - we need it to check if the renderer is a component
583
- return typeof renderer === 'function' && !!renderer?.ɵcmp;
1029
+ return typeof renderer === 'function' &&
1030
+ !!renderer?.ɵcmp &&
1031
+ renderer?.RENDERER_MARKER === HotCellRendererComponent.RENDERER_MARKER;
1032
+ }
1033
+ isAdvancedRendererComponentRefType(renderer) {
1034
+ // ecmp - we need it to check if the renderer is a component
1035
+ return typeof renderer === 'function' &&
1036
+ !!renderer?.ɵcmp &&
1037
+ renderer?.RENDERER_MARKER === HotCellRendererAdvancedComponent.RENDERER_MARKER;
584
1038
  }
585
1039
  isTemplateRef(renderer) {
586
1040
  return renderer && typeof renderer.createEmbeddedView === 'function';
587
1041
  }
1042
+ isCustomRenderer(renderer) {
1043
+ return this.isRendererComponentRefType(renderer) ||
1044
+ this.isTemplateRef(renderer) ||
1045
+ this.isAdvancedRendererComponentRefType(renderer);
1046
+ }
588
1047
  static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotSettingsResolver, deps: [{ token: DynamicComponentService }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Injectable });
589
1048
  static ɵprov = i0.ɵɵngDeclareInjectable({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotSettingsResolver });
590
1049
  }
@@ -701,6 +1160,7 @@ class HotTableComponent {
701
1160
  _hotSettingsResolver;
702
1161
  _hotConfig;
703
1162
  ngZone;
1163
+ environmentInjector;
704
1164
  // component inputs
705
1165
  /** The data for the Handsontable instance. */
706
1166
  data = null;
@@ -711,10 +1171,11 @@ class HotTableComponent {
711
1171
  /** The Handsontable instance. */
712
1172
  __hotInstance = null;
713
1173
  configSubscription;
714
- constructor(_hotSettingsResolver, _hotConfig, ngZone) {
1174
+ constructor(_hotSettingsResolver, _hotConfig, ngZone, environmentInjector) {
715
1175
  this._hotSettingsResolver = _hotSettingsResolver;
716
1176
  this._hotConfig = _hotConfig;
717
1177
  this.ngZone = ngZone;
1178
+ this.environmentInjector = environmentInjector;
718
1179
  }
719
1180
  /**
720
1181
  * Gets the Handsontable instance.
@@ -747,6 +1208,7 @@ class HotTableComponent {
747
1208
  options = { ...options, ...negotiatedSettings, data: this.data };
748
1209
  this.ngZone.runOutsideAngular(() => {
749
1210
  this.hotInstance = new Handsontable.Core(this.container.nativeElement, options);
1211
+ this.hotInstance._angularEnvironmentInjector = this.environmentInjector;
750
1212
  this.hotInstance.init();
751
1213
  });
752
1214
  this.configSubscription = this._hotConfig.config$.subscribe((config) => {
@@ -829,13 +1291,13 @@ class HotTableComponent {
829
1291
  }
830
1292
  return negotiatedSettings;
831
1293
  }
832
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotTableComponent, deps: [{ token: HotSettingsResolver }, { token: HotGlobalConfigService }, { token: i0.NgZone }], target: i0.ɵɵFactoryTarget.Component });
1294
+ static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotTableComponent, deps: [{ token: HotSettingsResolver }, { token: HotGlobalConfigService }, { token: i0.NgZone }, { token: i0.EnvironmentInjector }], target: i0.ɵɵFactoryTarget.Component });
833
1295
  static ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "14.0.0", version: "16.2.12", type: HotTableComponent, selector: "hot-table", inputs: { data: "data", settings: "settings" }, providers: [HotSettingsResolver], viewQueries: [{ propertyName: "container", first: true, predicate: ["container"], descendants: true }], usesOnChanges: true, ngImport: i0, template: '<div #container></div>', isInline: true, styles: [":host{display:block}\n"], encapsulation: i0.ViewEncapsulation.None });
834
1296
  }
835
1297
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotTableComponent, decorators: [{
836
1298
  type: Component,
837
1299
  args: [{ selector: 'hot-table', template: '<div #container></div>', encapsulation: ViewEncapsulation.None, providers: [HotSettingsResolver], styles: [":host{display:block}\n"] }]
838
- }], ctorParameters: function () { return [{ type: HotSettingsResolver }, { type: HotGlobalConfigService }, { type: i0.NgZone }]; }, propDecorators: { data: [{
1300
+ }], ctorParameters: function () { return [{ type: HotSettingsResolver }, { type: HotGlobalConfigService }, { type: i0.NgZone }, { type: i0.EnvironmentInjector }]; }, propDecorators: { data: [{
839
1301
  type: Input
840
1302
  }], settings: [{
841
1303
  type: Input
@@ -849,7 +1311,7 @@ class HotTableModule {
849
1311
  * Placeholder for the library version.
850
1312
  * Replaced automatically during the pre-build/post-build process.
851
1313
  */
852
- static version = '0.0.0-next-a4c3bb2-20260211';
1314
+ static version = '0.0.0-next-48acfa2-20260212';
853
1315
  constructor() { }
854
1316
  static forRoot() {
855
1317
  return {
@@ -869,98 +1331,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
869
1331
  }]
870
1332
  }], ctorParameters: function () { return []; } });
871
1333
 
872
- /**
873
- * Abstract class representing a Handsontable editor in angular.
874
- */
875
- class HotCellEditorComponent {
876
- /** The tabindex attribute for the editor. */
877
- tabindex = -1;
878
- /** The data-hot-input attribute for the editor. */
879
- dataHotInput = '';
880
- /** The handsontableInput class for the editor. */
881
- handsontableInputClass = true;
882
- /** The height of the editor as a percentage of the parent container. */
883
- heightFitParentContainer = 100;
884
- /** The width of the editor as a percentage of the parent container. */
885
- widthFitParentContainer = 100;
886
- /** The row index of the cell being edited. */
887
- row;
888
- /** The column index of the cell being edited. */
889
- column;
890
- /** The property name of the cell being edited. */
891
- prop;
892
- /** The original value of the cell being edited. */
893
- originalValue;
894
- /** The cell properties of the cell being edited. */
895
- cellProperties;
896
- /** Event emitted when the edit is finished.
897
- * The data will be saved to the model.
898
- */
899
- finishEdit = new EventEmitter();
900
- /** Event emitted when the edit is canceled.
901
- * The entered data will be reverted to the original value.
902
- */
903
- cancelEdit = new EventEmitter();
904
- /** The current value of the editor. */
905
- _value;
906
- /** Event triggered by Handsontable on closing the editor.
907
- * The user can define their own actions for
908
- * the custom editor to be called after the base logic. */
909
- onClose() { }
910
- /** Event triggered by Handsontable on open the editor.
911
- * The user can define their own actions for
912
- * the custom editor to be called after the base logic. */
913
- onOpen(event) { }
914
- /**
915
- * Gets the current value of the editor.
916
- * @returns The current value of the editor.
917
- */
918
- getValue() {
919
- return this._value;
920
- }
921
- /**
922
- * Sets the current value of the editor.
923
- * @param value The value to set.
924
- */
925
- setValue(value) {
926
- this._value = value;
927
- }
928
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
929
- static ɵdir = i0.ɵɵngDeclareDirective({ minVersion: "14.0.0", version: "16.2.12", type: HotCellEditorComponent, inputs: { row: "row", column: "column", prop: "prop", originalValue: "originalValue", cellProperties: "cellProperties" }, outputs: { finishEdit: "finishEdit", cancelEdit: "cancelEdit" }, host: { properties: { "attr.tabindex": "this.tabindex", "attr.data-hot-input": "this.dataHotInput", "class.handsontableInput": "this.handsontableInputClass", "style.height.%": "this.heightFitParentContainer", "style.width.%": "this.widthFitParentContainer" } }, ngImport: i0 });
930
- }
931
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, decorators: [{
932
- type: Directive
933
- }], propDecorators: { tabindex: [{
934
- type: HostBinding,
935
- args: ['attr.tabindex']
936
- }], dataHotInput: [{
937
- type: HostBinding,
938
- args: ['attr.data-hot-input']
939
- }], handsontableInputClass: [{
940
- type: HostBinding,
941
- args: ['class.handsontableInput']
942
- }], heightFitParentContainer: [{
943
- type: HostBinding,
944
- args: ['style.height.%']
945
- }], widthFitParentContainer: [{
946
- type: HostBinding,
947
- args: ['style.width.%']
948
- }], row: [{
949
- type: Input
950
- }], column: [{
951
- type: Input
952
- }], prop: [{
953
- type: Input
954
- }], originalValue: [{
955
- type: Input
956
- }], cellProperties: [{
957
- type: Input
958
- }], finishEdit: [{
959
- type: Output
960
- }], cancelEdit: [{
961
- type: Output
962
- }] } });
963
-
964
1334
  /*
965
1335
  * Public API Surface of hot-table
966
1336
  */
@@ -969,5 +1339,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
969
1339
  * Generated bundle index. Do not edit.
970
1340
  */
971
1341
 
972
- export { DynamicComponentService, HOT_DESTROYED_WARNING, HOT_GLOBAL_CONFIG, HotCellEditorComponent, HotCellRendererComponent, HotGlobalConfigService, HotSettingsResolver, HotTableComponent, HotTableModule, INVALID_RENDERER_WARNING, NON_COMMERCIAL_LICENSE, PredefinedTheme, isHotCellRendererComponent, isTemplateRef };
1342
+ export { DynamicComponentService, HOT_DESTROYED_WARNING, HOT_GLOBAL_CONFIG, HotCellEditorAdvancedComponent, HotCellEditorComponent, HotCellRendererAdvancedComponent, HotCellRendererComponent, HotGlobalConfigService, HotSettingsResolver, HotTableComponent, HotTableModule, INVALID_ADVANCED_RENDERER_WARNING, INVALID_RENDERER_WARNING, NON_COMMERCIAL_LICENSE, PredefinedTheme, isAdvancedHotCellRendererComponent, isHotCellRendererComponent, isTemplateRef };
973
1343
  //# sourceMappingURL=handsontable-angular-wrapper.mjs.map