@handsontable/angular-wrapper 16.2.0 → 17.0.0-rc1

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 (31) hide show
  1. package/README.md +1 -11
  2. package/esm2022/lib/editor/custom-editor-placeholder.component.mjs +8 -5
  3. package/esm2022/lib/editor/editor-factory-adapter.mjs +140 -0
  4. package/esm2022/lib/editor/hot-cell-editor-advanced.component.mjs +92 -0
  5. package/esm2022/lib/editor/hot-cell-editor.component.mjs +2 -1
  6. package/esm2022/lib/editor/models/factory-editor-properties.mjs +2 -0
  7. package/esm2022/lib/editor/models/keyboard-shortcut-config.mjs +2 -0
  8. package/esm2022/lib/hot-table.component.mjs +15 -5
  9. package/esm2022/lib/hot-table.module.mjs +2 -2
  10. package/esm2022/lib/models/column-settings.mjs +1 -1
  11. package/esm2022/lib/renderer/hot-cell-renderer-advanced.component.mjs +55 -0
  12. package/esm2022/lib/renderer/hot-dynamic-renderer-component.service.mjs +62 -10
  13. package/esm2022/lib/services/hot-global-config.service.mjs +1 -2
  14. package/esm2022/lib/services/hot-settings-resolver.service.mjs +54 -17
  15. package/esm2022/public-api.mjs +4 -1
  16. package/fesm2022/handsontable-angular-wrapper.mjs +504 -128
  17. package/fesm2022/handsontable-angular-wrapper.mjs.map +1 -1
  18. package/lib/editor/custom-editor-placeholder.component.d.ts +4 -2
  19. package/lib/editor/editor-factory-adapter.d.ts +13 -0
  20. package/lib/editor/hot-cell-editor-advanced.component.d.ts +74 -0
  21. package/lib/editor/hot-cell-editor.component.d.ts +1 -0
  22. package/lib/editor/models/factory-editor-properties.d.ts +24 -0
  23. package/lib/editor/models/keyboard-shortcut-config.d.ts +13 -0
  24. package/lib/hot-table.component.d.ts +3 -2
  25. package/lib/models/column-settings.d.ts +7 -2
  26. package/lib/renderer/hot-cell-renderer-advanced.component.d.ts +33 -0
  27. package/lib/renderer/hot-dynamic-renderer-component.service.d.ts +19 -0
  28. package/lib/services/hot-global-config.service.d.ts +5 -0
  29. package/lib/services/hot-settings-resolver.service.d.ts +3 -0
  30. package/package.json +1 -1
  31. 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
  }
@@ -633,7 +1092,6 @@ class HotGlobalConfigService {
633
1092
  */
634
1093
  defaultConfig = {
635
1094
  license: undefined,
636
- themeName: ''
637
1095
  };
638
1096
  /**
639
1097
  * A BehaviorSubject that holds the current Handsontable configuration.
@@ -702,6 +1160,7 @@ class HotTableComponent {
702
1160
  _hotSettingsResolver;
703
1161
  _hotConfig;
704
1162
  ngZone;
1163
+ environmentInjector;
705
1164
  // component inputs
706
1165
  /** The data for the Handsontable instance. */
707
1166
  data = null;
@@ -712,10 +1171,11 @@ class HotTableComponent {
712
1171
  /** The Handsontable instance. */
713
1172
  __hotInstance = null;
714
1173
  configSubscription;
715
- constructor(_hotSettingsResolver, _hotConfig, ngZone) {
1174
+ constructor(_hotSettingsResolver, _hotConfig, ngZone, environmentInjector) {
716
1175
  this._hotSettingsResolver = _hotSettingsResolver;
717
1176
  this._hotConfig = _hotConfig;
718
1177
  this.ngZone = ngZone;
1178
+ this.environmentInjector = environmentInjector;
719
1179
  }
720
1180
  /**
721
1181
  * Gets the Handsontable instance.
@@ -748,6 +1208,7 @@ class HotTableComponent {
748
1208
  options = { ...options, ...negotiatedSettings, data: this.data };
749
1209
  this.ngZone.runOutsideAngular(() => {
750
1210
  this.hotInstance = new Handsontable.Core(this.container.nativeElement, options);
1211
+ this.hotInstance._angularEnvironmentInjector = this.environmentInjector;
751
1212
  this.hotInstance.init();
752
1213
  });
753
1214
  this.configSubscription = this._hotConfig.config$.subscribe((config) => {
@@ -815,21 +1276,28 @@ class HotTableComponent {
815
1276
  const hotConfig = this._hotConfig.getConfig();
816
1277
  const negotiatedSettings = {};
817
1278
  negotiatedSettings.licenseKey = settings.licenseKey ?? hotConfig.license;
818
- negotiatedSettings.themeName = settings.themeName ?? hotConfig.themeName;
819
1279
  negotiatedSettings.language = settings.language ?? hotConfig.language;
1280
+ const theme = settings.theme ?? hotConfig.theme;
1281
+ const themeName = settings.themeName ?? hotConfig.themeName;
1282
+ if (theme !== undefined) {
1283
+ negotiatedSettings.theme = theme;
1284
+ }
1285
+ else if (themeName) {
1286
+ negotiatedSettings.themeName = themeName;
1287
+ }
820
1288
  // settings that can be set only before the Handsontable instance is initialized
821
1289
  if (!this.__hotInstance) {
822
1290
  negotiatedSettings.layoutDirection = settings.layoutDirection ?? hotConfig.layoutDirection;
823
1291
  }
824
1292
  return negotiatedSettings;
825
1293
  }
826
- 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 });
827
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 });
828
1296
  }
829
1297
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotTableComponent, decorators: [{
830
1298
  type: Component,
831
1299
  args: [{ selector: 'hot-table', template: '<div #container></div>', encapsulation: ViewEncapsulation.None, providers: [HotSettingsResolver], styles: [":host{display:block}\n"] }]
832
- }], 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: [{
833
1301
  type: Input
834
1302
  }], settings: [{
835
1303
  type: Input
@@ -843,7 +1311,7 @@ class HotTableModule {
843
1311
  * Placeholder for the library version.
844
1312
  * Replaced automatically during the pre-build/post-build process.
845
1313
  */
846
- static version = '16.2.0';
1314
+ static version = '17.0.0-rc1';
847
1315
  constructor() { }
848
1316
  static forRoot() {
849
1317
  return {
@@ -863,98 +1331,6 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
863
1331
  }]
864
1332
  }], ctorParameters: function () { return []; } });
865
1333
 
866
- /**
867
- * Abstract class representing a Handsontable editor in angular.
868
- */
869
- class HotCellEditorComponent {
870
- /** The tabindex attribute for the editor. */
871
- tabindex = -1;
872
- /** The data-hot-input attribute for the editor. */
873
- dataHotInput = '';
874
- /** The handsontableInput class for the editor. */
875
- handsontableInputClass = true;
876
- /** The height of the editor as a percentage of the parent container. */
877
- heightFitParentContainer = 100;
878
- /** The width of the editor as a percentage of the parent container. */
879
- widthFitParentContainer = 100;
880
- /** The row index of the cell being edited. */
881
- row;
882
- /** The column index of the cell being edited. */
883
- column;
884
- /** The property name of the cell being edited. */
885
- prop;
886
- /** The original value of the cell being edited. */
887
- originalValue;
888
- /** The cell properties of the cell being edited. */
889
- cellProperties;
890
- /** Event emitted when the edit is finished.
891
- * The data will be saved to the model.
892
- */
893
- finishEdit = new EventEmitter();
894
- /** Event emitted when the edit is canceled.
895
- * The entered data will be reverted to the original value.
896
- */
897
- cancelEdit = new EventEmitter();
898
- /** The current value of the editor. */
899
- _value;
900
- /** Event triggered by Handsontable on closing the editor.
901
- * The user can define their own actions for
902
- * the custom editor to be called after the base logic. */
903
- onClose() { }
904
- /** Event triggered by Handsontable on open the editor.
905
- * The user can define their own actions for
906
- * the custom editor to be called after the base logic. */
907
- onOpen(event) { }
908
- /**
909
- * Gets the current value of the editor.
910
- * @returns The current value of the editor.
911
- */
912
- getValue() {
913
- return this._value;
914
- }
915
- /**
916
- * Sets the current value of the editor.
917
- * @param value The value to set.
918
- */
919
- setValue(value) {
920
- this._value = value;
921
- }
922
- static ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, deps: [], target: i0.ɵɵFactoryTarget.Directive });
923
- 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 });
924
- }
925
- i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImport: i0, type: HotCellEditorComponent, decorators: [{
926
- type: Directive
927
- }], propDecorators: { tabindex: [{
928
- type: HostBinding,
929
- args: ['attr.tabindex']
930
- }], dataHotInput: [{
931
- type: HostBinding,
932
- args: ['attr.data-hot-input']
933
- }], handsontableInputClass: [{
934
- type: HostBinding,
935
- args: ['class.handsontableInput']
936
- }], heightFitParentContainer: [{
937
- type: HostBinding,
938
- args: ['style.height.%']
939
- }], widthFitParentContainer: [{
940
- type: HostBinding,
941
- args: ['style.width.%']
942
- }], row: [{
943
- type: Input
944
- }], column: [{
945
- type: Input
946
- }], prop: [{
947
- type: Input
948
- }], originalValue: [{
949
- type: Input
950
- }], cellProperties: [{
951
- type: Input
952
- }], finishEdit: [{
953
- type: Output
954
- }], cancelEdit: [{
955
- type: Output
956
- }] } });
957
-
958
1334
  /*
959
1335
  * Public API Surface of hot-table
960
1336
  */
@@ -963,5 +1339,5 @@ i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "16.2.12", ngImpo
963
1339
  * Generated bundle index. Do not edit.
964
1340
  */
965
1341
 
966
- 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 };
967
1343
  //# sourceMappingURL=handsontable-angular-wrapper.mjs.map