@aquera/ngx-smart-table 0.0.15-alpha → 0.0.16-alpha

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.
@@ -3523,6 +3523,72 @@ class NileInputEditor {
3523
3523
  * Custom editor using NileSelect from @aquera/nile-elements
3524
3524
  * This demonstrates how to create dropdown/select editors for ngx-smart-table
3525
3525
  */
3526
+ /**
3527
+ * Inject global styles for nile-select dropdown height limit
3528
+ */
3529
+ let dropdownStylesInjected = false;
3530
+ function injectDropdownStyles() {
3531
+ if (dropdownStylesInjected)
3532
+ return;
3533
+ dropdownStylesInjected = true;
3534
+ const styleId = 'nile-select-dropdown-styles';
3535
+ if (document.getElementById(styleId))
3536
+ return;
3537
+ const style = document.createElement('style');
3538
+ style.id = styleId;
3539
+ style.textContent = `
3540
+ /* Limit nile-select dropdown height and enable scrolling */
3541
+ .nile-select-portal-append {
3542
+ max-height: 300px !important;
3543
+ }
3544
+ .nile-select-portal-append .select__listbox {
3545
+ min-width: 220px !important;
3546
+ max-height: 260px !important;
3547
+ overflow-y: auto !important;
3548
+ width: auto !important;
3549
+ }
3550
+ .nile-select-portal-append .select__footer {
3551
+ height: 35px !important;
3552
+ }
3553
+ /* Prevent option text truncation - target the options container */
3554
+ .nile-select-portal-append .select__options {
3555
+ width: auto !important;
3556
+ }
3557
+ .nile-select-portal-append nile-option {
3558
+ width: auto !important;
3559
+ max-width: none !important;
3560
+ }
3561
+ .nile-select-portal-append nile-option::part(base) {
3562
+ white-space: nowrap !important;
3563
+ text-overflow: clip !important;
3564
+ overflow: visible !important;
3565
+ max-width: none !important;
3566
+ }
3567
+ /* Fix option text truncation by changing flex direction */
3568
+ .nile-select-portal-append nile-option::part(option_label_container) {
3569
+ flex-direction: row !important;
3570
+ }
3571
+ /* Combobox styling using ::part() selector */
3572
+ nile-select.st-cell-editor::part(combobox) {
3573
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary)) !important;
3574
+ border: solid 1px transparent !important;
3575
+ margin: 1px 4px !important;
3576
+ }
3577
+ nile-select.st-cell-editor::part(combobox):hover {
3578
+ border: solid 1px transparent !important;
3579
+ }
3580
+ .st-cell-editor::part(combobox) {
3581
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary)) !important;
3582
+ border: solid 1px transparent !important;
3583
+ margin: 1px 4px !important;
3584
+ }
3585
+ /* Search input full width */
3586
+ .nile-select-portal-append .select__search {
3587
+ width: 100% !important;
3588
+ }
3589
+ `;
3590
+ document.head.appendChild(style);
3591
+ }
3526
3592
  /**
3527
3593
  * Custom editor that uses NileSelect component
3528
3594
  * @template T The value type (string for single selection, string[] for multiple)
@@ -3533,9 +3599,6 @@ class NileSelectEditor {
3533
3599
  this.acceptsInitialKeypress = false;
3534
3600
  this.eventListeners = [];
3535
3601
  this.currentOptions = [];
3536
- this.isInitializing = false; // Flag to prevent immediate close during initialization
3537
- this.hasSaved = false; // Flag to prevent double-save
3538
- this.lastSelectedValue = ''; // Track the last selected value
3539
3602
  // Handle Observable options
3540
3603
  if (isObservable(options.options)) {
3541
3604
  this.optionsSubscription = options.options.subscribe(opts => {
@@ -3554,101 +3617,20 @@ class NileSelectEditor {
3554
3617
  }
3555
3618
  }
3556
3619
  }
3557
- /**
3558
- * Inject global styles to remove border from nile-select combobox
3559
- * Uses ::part selector which works across shadow DOM boundaries
3560
- */
3561
- injectBorderlessStyles() {
3562
- if (NileSelectEditor.stylesInjected)
3563
- return;
3564
- const styleId = 'nile-select-borderless-styles';
3565
- if (document.getElementById(styleId)) {
3566
- NileSelectEditor.stylesInjected = true;
3567
- return;
3568
- }
3569
- const style = document.createElement('style');
3570
- style.id = styleId;
3571
- style.textContent = `
3572
- .st-cell-editor::part(combobox) {
3573
- border: none !important;
3574
- outline: none !important;
3575
- box-shadow: none !important;
3576
- min-height: unset !important;
3577
- height: 100% !important;
3578
- padding: 0 !important;
3579
- }
3580
- .st-cell-editor::part(combobox):hover {
3581
- border: none !important;
3582
- outline: none !important;
3583
- box-shadow: none !important;
3584
- }
3585
- .st-cell-editor::part(combobox):focus {
3586
- border: none !important;
3587
- outline: none !important;
3588
- box-shadow: none !important;
3589
- }
3590
- // .st-cell-editor::part(tag) {
3591
- // margin: 1px 2px !important;
3592
- // border-radius: 3px !important;
3593
- // background-color: #e2e8f0 !important;
3594
- // font-size: 12px !important;
3595
- // height: auto !important;
3596
- // }
3597
- // .st-cell-editor::part(tag__base) {
3598
- // border: none !important;
3599
- // outline: none !important;
3600
- // padding: 0 2px !important;
3601
- // height: auto !important;
3602
- // }
3603
- // .st-cell-editor::part(tag__content) {
3604
- // padding: 0 !important;
3605
- // }
3606
- .st-cell-editor::part(tags) {
3607
- gap: 2px !important;
3608
- align-items: center !important;
3609
- }
3610
- .st-cell-editor::part(tags-count) {
3611
- margin: 0 !important;
3612
- }
3613
- .st-cell-editor::part(footer) {
3614
- height: 35px !important;
3615
- min-height: 35px !important;
3616
- padding: 8px !important;
3617
- padding-bottom: 30px !important;
3618
- box-sizing: border-box !important;
3619
- }
3620
- .st-cell-editor::part(listbox) {
3621
- margin-bottom: 0 !important;
3622
- padding-bottom: 0 !important;
3623
- }
3624
- `;
3625
- document.head.appendChild(style);
3626
- NileSelectEditor.stylesInjected = true;
3627
- }
3628
3620
  edit(context) {
3629
3621
  if (!context.container) {
3630
3622
  console.warn('NileSelectEditor requires a container element');
3631
3623
  return;
3632
3624
  }
3633
- // Set initializing flag to prevent immediate close
3634
- this.isInitializing = true;
3635
- this.hasSaved = false;
3636
- this.lastSelectedValue = '';
3625
+ // Inject dropdown height styles once
3626
+ injectDropdownStyles();
3637
3627
  // Create NileSelect custom element using document.createElement
3638
3628
  this.select = document.createElement('nile-select');
3639
3629
  this.select.className = 'st-cell-editor';
3640
- // Apply inline styles for proper cell fitting - prevent height changes
3641
- this.select.style.width = 'calc(100% - 4px)';
3642
- this.select.style.height = '100%';
3643
- this.select.style.maxHeight = '100%';
3630
+ // Apply inline styles for proper cell fitting
3631
+ this.select.style.width = '100%';
3632
+ this.select.style.height = 'inherit';
3644
3633
  this.select.style.boxSizing = 'border-box';
3645
- this.select.style.margin = '0 2px';
3646
- this.select.style.overflow = 'hidden';
3647
- // Inject styles to remove border using ::part selector
3648
- this.injectBorderlessStyles();
3649
- // Also try removing outline on host element
3650
- this.select.style.outline = 'none';
3651
- this.select.style.border = 'none';
3652
3634
  // Set initial value
3653
3635
  this.setInitialValue(context.value);
3654
3636
  // Apply all configuration options
@@ -3662,20 +3644,10 @@ class NileSelectEditor {
3662
3644
  context.container.appendChild(this.select);
3663
3645
  // Focus and auto-open after render
3664
3646
  setTimeout(() => {
3665
- var _a;
3666
- (_a = this.select) === null || _a === void 0 ? void 0 : _a.focus();
3667
- // Auto-open the dropdown
3668
3647
  if (this.select) {
3648
+ this.select.focus();
3669
3649
  this.select.open = true;
3670
3650
  }
3671
- // Clear initializing flag after dropdown has opened
3672
- setTimeout(() => {
3673
- this.isInitializing = false;
3674
- // For multi-select, find the portal div for click detection
3675
- if (this.options.multiple) {
3676
- this.portalDiv = document.querySelector('.nile-select-portal-append');
3677
- }
3678
- }, 100);
3679
3651
  }, 0);
3680
3652
  }
3681
3653
  /**
@@ -3759,13 +3731,9 @@ class NileSelectEditor {
3759
3731
  if (this.options.pill) {
3760
3732
  this.select.pill = this.options.pill;
3761
3733
  }
3762
- if (this.options.hoist !== undefined) {
3734
+ if (this.options.hoist) {
3763
3735
  this.select.hoist = this.options.hoist;
3764
3736
  }
3765
- else {
3766
- // Default hoist to true for all selects to prevent clipping issues in table cells
3767
- this.select.hoist = true;
3768
- }
3769
3737
  if (this.options.placement) {
3770
3738
  this.select.placement = this.options.placement;
3771
3739
  }
@@ -3775,12 +3743,8 @@ class NileSelectEditor {
3775
3743
  if (this.options.disableLocalSearch) {
3776
3744
  this.select.disableLocalSearch = this.options.disableLocalSearch;
3777
3745
  }
3778
- if (this.options.portal !== undefined) {
3779
- this.select.portal = this.options.portal;
3780
- }
3781
- else {
3782
- this.select.portal = true; // Default to portal mode for better positioning
3783
- }
3746
+ // Enable portal mode by default to prevent dropdown clipping in table cells
3747
+ this.select.portal = this.options.portal !== false;
3784
3748
  // Prevent width syncing issues in table cells
3785
3749
  this.select.noWidthSync = true;
3786
3750
  }
@@ -3840,31 +3804,6 @@ class NileSelectEditor {
3840
3804
  if (!this.select)
3841
3805
  return;
3842
3806
  const validateOnSave = this.options.validateOnSave !== false;
3843
- // Prevent click events from bubbling up to the cell container
3844
- const selectClickHandler = (e) => {
3845
- e.stopPropagation();
3846
- };
3847
- this.select.addEventListener('click', selectClickHandler);
3848
- this.eventListeners.push({ event: 'click', handler: selectClickHandler });
3849
- // For single-select with portal, listen to document mousedown to detect option selection
3850
- // Use mousedown because it fires before blur
3851
- if (!this.options.multiple) {
3852
- const documentMouseDownHandler = (e) => {
3853
- var _a, _b;
3854
- const composedPath = ((_a = e.composedPath) === null || _a === void 0 ? void 0 : _a.call(e)) || [];
3855
- // Look for nile-option in the event path
3856
- for (const el of composedPath) {
3857
- if (el instanceof HTMLElement && ((_b = el.tagName) === null || _b === void 0 ? void 0 : _b.toLowerCase()) === 'nile-option') {
3858
- const optionValue = el.getAttribute('value') || el.value || '';
3859
- this.lastSelectedValue = optionValue;
3860
- return;
3861
- }
3862
- }
3863
- };
3864
- // Add listener immediately (mousedown fires before blur, so no need for delay)
3865
- document.addEventListener('mousedown', documentMouseDownHandler, true); // capture phase
3866
- this.eventListeners.push({ event: 'document-mousedown', handler: documentMouseDownHandler });
3867
- }
3868
3807
  // Handle keyboard events (keydown is not replaced by custom events)
3869
3808
  const keydownHandler = (e) => {
3870
3809
  const keyEvent = e;
@@ -3876,46 +3815,56 @@ class NileSelectEditor {
3876
3815
  };
3877
3816
  this.select.addEventListener('keydown', keydownHandler);
3878
3817
  this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
3879
- // Use nile-blur event as save trigger for single-select
3880
- // Add a small delay to allow the value to update before reading
3881
- const singleSelectSaveHandler = (e) => {
3882
- // Don't save again if already saved
3883
- // Ignore events that fire during initialization (e.g. when setting initial value)
3884
- if (this.hasSaved || this.isInitializing) {
3818
+ // Use nile-change custom event as save trigger - ONLY for single-select
3819
+ // Multi-select should stay open until user clicks outside
3820
+ if (!this.options.multiple) {
3821
+ const changeHandler = (e) => {
3822
+ var _a, _b;
3823
+ const customEvent = e;
3824
+ if (validateOnSave && !((_a = this.select) === null || _a === void 0 ? void 0 : _a.checkValidity())) {
3825
+ (_b = this.select) === null || _b === void 0 ? void 0 : _b.reportValidity();
3826
+ return;
3827
+ }
3828
+ context.onSave(this.parseValue(customEvent.detail.value));
3829
+ };
3830
+ this.select.addEventListener('nile-change', changeHandler);
3831
+ this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
3832
+ }
3833
+ // Track if save was already triggered (to prevent double save)
3834
+ let saveTriggered = false;
3835
+ // Use mousedown on document to detect clicks outside the select and portal
3836
+ const documentMousedownHandler = (e) => {
3837
+ var _a, _b;
3838
+ if (saveTriggered)
3839
+ return;
3840
+ if (!this.select)
3841
+ return;
3842
+ const target = e.target;
3843
+ // Check if click is inside the select element
3844
+ if (this.select.contains(target)) {
3885
3845
  return;
3886
3846
  }
3887
- // Only save for single-select
3888
- if (!this.options.multiple) {
3889
- // Add a small delay to let the value update after selection
3890
- setTimeout(() => {
3891
- var _a, _b, _c;
3892
- // Check again after timeout in case saved by another handler
3893
- if (this.hasSaved) {
3894
- return;
3895
- }
3896
- if (validateOnSave && !((_a = this.select) === null || _a === void 0 ? void 0 : _a.checkValidity())) {
3897
- (_b = this.select) === null || _b === void 0 ? void 0 : _b.reportValidity();
3898
- return;
3899
- }
3900
- // Use lastSelectedValue if available, otherwise get from element
3901
- const target = e.target;
3902
- const value = this.lastSelectedValue || (target === null || target === void 0 ? void 0 : target.value) || ((_c = this.select) === null || _c === void 0 ? void 0 : _c.value) || '';
3903
- this.hasSaved = true;
3904
- context.onSave(this.parseValue(value));
3905
- }, 50); // 50ms delay to allow value to update
3847
+ // Check if click is inside any nile-select portal
3848
+ const portals = document.querySelectorAll('.nile-select-portal-append');
3849
+ for (let i = 0; i < portals.length; i++) {
3850
+ if (portals[i].contains(target)) {
3851
+ return;
3852
+ }
3853
+ }
3854
+ // Click is outside - save and exit
3855
+ saveTriggered = true;
3856
+ if (validateOnSave && !((_a = this.select) === null || _a === void 0 ? void 0 : _a.checkValidity())) {
3857
+ (_b = this.select) === null || _b === void 0 ? void 0 : _b.reportValidity();
3858
+ return;
3906
3859
  }
3860
+ context.onSave(this.getCurrentValue());
3907
3861
  };
3908
- // Listen to blur and nile-blur events for focus loss detection
3909
- // Nile web components emit nile-blur custom event instead of native blur
3910
- this.select.addEventListener('blur', singleSelectSaveHandler);
3911
- this.eventListeners.push({ event: 'blur', handler: singleSelectSaveHandler });
3912
- this.select.addEventListener('nile-blur', singleSelectSaveHandler);
3913
- this.eventListeners.push({ event: 'nile-blur', handler: singleSelectSaveHandler });
3914
- // Also listen to change events as fallback
3915
- this.select.addEventListener('nile-change', singleSelectSaveHandler);
3916
- this.eventListeners.push({ event: 'nile-change', handler: singleSelectSaveHandler });
3917
- this.select.addEventListener('change', singleSelectSaveHandler);
3918
- this.eventListeners.push({ event: 'change', handler: singleSelectSaveHandler });
3862
+ // Add listener after a short delay to avoid capturing the initial click that opened the editor
3863
+ setTimeout(() => {
3864
+ document.addEventListener('mousedown', documentMousedownHandler, true);
3865
+ }, 50);
3866
+ // Store for cleanup
3867
+ this._documentMousedownHandler = documentMousedownHandler;
3919
3868
  // Handle clear button if enabled
3920
3869
  if (this.options.clearable) {
3921
3870
  const clearHandler = (e) => {
@@ -3934,44 +3883,6 @@ class NileSelectEditor {
3934
3883
  this.select.addEventListener('nile-search', searchHandler);
3935
3884
  this.eventListeners.push({ event: 'nile-search', handler: searchHandler });
3936
3885
  }
3937
- // For multi-select: detect clicks outside portal and select
3938
- if (this.options.multiple) {
3939
- const documentClickHandler = (e) => {
3940
- var _a, _b, _c, _d;
3941
- const mouseEvent = e;
3942
- const target = mouseEvent.target;
3943
- // Check if portal div exists (might take a moment to render)
3944
- if (!this.portalDiv) {
3945
- this.portalDiv = document.querySelector('.nile-select-portal-append');
3946
- }
3947
- // Determine if click is inside the select or portal
3948
- const clickedInsideSelect = (_a = this.select) === null || _a === void 0 ? void 0 : _a.contains(target);
3949
- const clickedInsidePortal = (_b = this.portalDiv) === null || _b === void 0 ? void 0 : _b.contains(target);
3950
- // If clicked outside both select and portal, save and close
3951
- if (!clickedInsideSelect && !clickedInsidePortal) {
3952
- if (validateOnSave && !((_c = this.select) === null || _c === void 0 ? void 0 : _c.checkValidity())) {
3953
- (_d = this.select) === null || _d === void 0 ? void 0 : _d.reportValidity();
3954
- return;
3955
- }
3956
- context.onSave(this.getCurrentValue());
3957
- }
3958
- // Otherwise, click is inside - do nothing (dropdown stays open)
3959
- };
3960
- // Delay adding the document click handler to prevent the initial click
3961
- // (that opened the editor) from immediately triggering it and closing the dropdown
3962
- // Use 200ms to ensure the dropdown has fully opened
3963
- setTimeout(() => {
3964
- // Only add if select still exists (editor not destroyed)
3965
- if (this.select) {
3966
- // Use bubbling phase (false) instead of capture to not interfere with nile-select's toggle
3967
- document.addEventListener('click', documentClickHandler, false);
3968
- this.eventListeners.push({
3969
- event: 'document-click',
3970
- handler: documentClickHandler
3971
- });
3972
- }
3973
- }, 200);
3974
- }
3975
3886
  }
3976
3887
  /**
3977
3888
  * Parse value based on selection mode
@@ -3991,36 +3902,25 @@ class NileSelectEditor {
3991
3902
  return value;
3992
3903
  }
3993
3904
  destroy() {
3994
- // Reset flags
3995
- this.isInitializing = false;
3996
- this.hasSaved = false;
3997
- this.lastSelectedValue = '';
3998
3905
  // Unsubscribe from options Observable
3999
3906
  if (this.optionsSubscription) {
4000
3907
  this.optionsSubscription.unsubscribe();
4001
3908
  this.optionsSubscription = undefined;
4002
3909
  }
3910
+ // Remove document mousedown handler if exists
3911
+ if (this._documentMousedownHandler) {
3912
+ document.removeEventListener('mousedown', this._documentMousedownHandler, true);
3913
+ delete this._documentMousedownHandler;
3914
+ }
4003
3915
  // Remove all event listeners
4004
3916
  if (this.select) {
4005
3917
  this.eventListeners.forEach(({ event, handler }) => {
4006
3918
  var _a;
4007
- if (event === 'document-click') {
4008
- // Remove from document for multi-select click handler (bubbling phase)
4009
- document.removeEventListener('click', handler, false);
4010
- }
4011
- else if (event === 'document-mousedown') {
4012
- // Remove from document for single-select option mousedown handler (capture phase)
4013
- document.removeEventListener('mousedown', handler, true);
4014
- }
4015
- else {
4016
- // Remove from select element
4017
- (_a = this.select) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, handler);
4018
- }
3919
+ (_a = this.select) === null || _a === void 0 ? void 0 : _a.removeEventListener(event, handler);
4019
3920
  });
4020
3921
  this.eventListeners = [];
4021
3922
  this.select.remove();
4022
3923
  this.select = undefined;
4023
- this.portalDiv = undefined; // Clean up portal reference
4024
3924
  }
4025
3925
  }
4026
3926
  focus() {
@@ -4040,7 +3940,6 @@ class NileSelectEditor {
4040
3940
  return (Array.isArray(value) ? (value.length > 0 ? value[0] : '') : value);
4041
3941
  }
4042
3942
  }
4043
- NileSelectEditor.stylesInjected = false;
4044
3943
 
4045
3944
  /**
4046
3945
  * Custom editor using NileAutoComplete from @aquera/nile-elements
@@ -8784,6 +8683,15 @@ class StTableComponent {
8784
8683
  // Use setTimeout to ensure DOM has updated
8785
8684
  setTimeout(() => {
8786
8685
  var _a;
8686
+ // Don't steal focus from editors - if a cell is being edited,
8687
+ // the editor should keep focus
8688
+ const editingPosition = this.getActiveTableState().getEditingPosition();
8689
+ if (editingPosition &&
8690
+ editingPosition.rowIndex === rowIndex &&
8691
+ editingPosition.columnIndex === colIndex) {
8692
+ // Cell is being edited, don't steal focus from the editor
8693
+ return;
8694
+ }
8787
8695
  // Use absolute row index for virtual scroll compatibility
8788
8696
  const absoluteRowIndex = this.getAbsoluteRowIndex(rowIndex);
8789
8697
  // Query within this component's host element to avoid selecting rows from other tables
@@ -9044,11 +8952,27 @@ class StTableComponent {
9044
8952
  * Clear focus when clicking outside table
9045
8953
  */
9046
8954
  onDocumentClick(event) {
8955
+ var _a;
9047
8956
  if (!this.isKeyboardNavigationEnabled())
9048
8957
  return;
9049
8958
  const target = event.target;
9050
8959
  const tableElement = target.closest('.st-table');
9051
- if (!tableElement) {
8960
+ // Also check if click is inside a portal (e.g., nile-select dropdown)
8961
+ // These are rendered outside the table but are part of the editing experience
8962
+ const portalElement = target.closest('.nile-select-portal-append');
8963
+ // Check composedPath for shadow DOM elements (e.g., nile-option, nile-input inside shadow root)
8964
+ const composedPath = ((_a = event.composedPath) === null || _a === void 0 ? void 0 : _a.call(event)) || [];
8965
+ const isInsideNileElement = composedPath.some((el) => {
8966
+ var _a, _b;
8967
+ if (el instanceof HTMLElement) {
8968
+ const tagName = (_a = el.tagName) === null || _a === void 0 ? void 0 : _a.toLowerCase();
8969
+ // Check for any nile-* custom elements used in editors
8970
+ return (tagName === null || tagName === void 0 ? void 0 : tagName.startsWith('nile-')) ||
8971
+ ((_b = el.classList) === null || _b === void 0 ? void 0 : _b.contains('nile-select-portal-append'));
8972
+ }
8973
+ return false;
8974
+ });
8975
+ if (!tableElement && !portalElement && !isInsideNileElement) {
9052
8976
  this.getActiveTableState().clearFocus();
9053
8977
  this.getActiveTableState().clearEditingCell();
9054
8978
  }
@@ -10016,9 +9940,13 @@ class StWorkbookComponent {
10016
9940
  this.workbookActionsOpen = false;
10017
9941
  this.workbookActionsPosition = {};
10018
9942
  /**
10019
- * Visible workbook actions (filtered by hidden)
9943
+ * Visible workbook actions for dropdown (filtered by hidden and showInToolbar)
10020
9944
  */
10021
9945
  this.visibleWorkbookActions = [];
9946
+ /**
9947
+ * Workbook actions to show directly in toolbar
9948
+ */
9949
+ this.toolbarWorkbookActions = [];
10022
9950
  /**
10023
9951
  * Destroy subject for cleanup
10024
9952
  */
@@ -10259,14 +10187,18 @@ class StWorkbookComponent {
10259
10187
  updateVisibleWorkbookActions() {
10260
10188
  if (!this.config.workbookActions) {
10261
10189
  this.visibleWorkbookActions = [];
10190
+ this.toolbarWorkbookActions = [];
10262
10191
  return;
10263
10192
  }
10264
- this.visibleWorkbookActions = this.config.workbookActions.filter(action => {
10193
+ const visibleActions = this.config.workbookActions.filter(action => {
10265
10194
  if (typeof action.hidden === 'function') {
10266
10195
  return !action.hidden();
10267
10196
  }
10268
10197
  return !action.hidden;
10269
10198
  });
10199
+ // Separate toolbar actions from dropdown actions
10200
+ this.toolbarWorkbookActions = visibleActions.filter(action => action.showInToolbar === true);
10201
+ this.visibleWorkbookActions = visibleActions.filter(action => action.showInToolbar !== true);
10270
10202
  }
10271
10203
  /**
10272
10204
  * Check if workbook action is disabled
@@ -10463,10 +10395,10 @@ class StWorkbookComponent {
10463
10395
  }
10464
10396
  }
10465
10397
  StWorkbookComponent.ɵfac = i0.ɵɵngDeclareFactory({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, deps: [{ token: AutosaveService }, { token: i0.ChangeDetectorRef }], target: i0.ɵɵFactoryTarget.Component });
10466
- StWorkbookComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StWorkbookComponent, selector: "st-workbook", inputs: { config: "config", sheetsData: "sheetsData", state: "state" }, outputs: { sheetChanged: "sheetChanged", addSheet: "addSheet", sheetTabAction: "sheetTabAction", workbookAction: "workbookAction", cellChange: "cellChange", cellSave: "cellSave", tableStateChange: "tableStateChange", fullscreenToggle: "fullscreenToggle", requestAddRow: "requestAddRow" }, viewQueries: [{ propertyName: "tableComponent", first: true, predicate: StTableComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\"\n (requestAddRow)=\"onRequestAddRow($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab.active .sheet-name{font-weight:600}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"], components: [{ type: StTableComponent, selector: "st-table", inputs: ["tableConfig", "data", "data$", "tableState", "enableSorting", "enableFiltering", "validateConfig"], outputs: ["stateChange", "dataChange", "cellEdit", "cellSave", "cellCancel", "cellChange", "columnResized", "columnMoved", "configValidationErrors", "columnAdded", "rowAction", "validationStateChange", "requestAddRow"] }], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], pipes: { "async": i1.AsyncPipe, "date": i1.DatePipe } });
10398
+ StWorkbookComponent.ɵcmp = i0.ɵɵngDeclareComponent({ minVersion: "12.0.0", version: "13.4.0", type: StWorkbookComponent, selector: "st-workbook", inputs: { config: "config", sheetsData: "sheetsData", state: "state" }, outputs: { sheetChanged: "sheetChanged", addSheet: "addSheet", sheetTabAction: "sheetTabAction", workbookAction: "workbookAction", cellChange: "cellChange", cellSave: "cellSave", tableStateChange: "tableStateChange", fullscreenToggle: "fullscreenToggle", requestAddRow: "requestAddRow" }, viewQueries: [{ propertyName: "tableComponent", first: true, predicate: StTableComponent, descendants: true }], usesOnChanges: true, ngImport: i0, template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Toolbar Workbook Actions (shown as individual buttons) -->\n <button *ngFor=\"let action of toolbarWorkbookActions\"\n class=\"toolbar-action-button\"\n [class.disabled]=\"isActionDisabled(action)\"\n [title]=\"action.label\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" [name]=\"action.icon\"></nile-icon>\n <span *ngIf=\"!action.icon\">{{ action.label }}</span>\n </button>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"visibleWorkbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\"\n (requestAddRow)=\"onRequestAddRow($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.workbook-toolbar-tab button.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab.active .sheet-name{font-weight:600}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"], components: [{ type: StTableComponent, selector: "st-table", inputs: ["tableConfig", "data", "data$", "tableState", "enableSorting", "enableFiltering", "validateConfig"], outputs: ["stateChange", "dataChange", "cellEdit", "cellSave", "cellCancel", "cellChange", "columnResized", "columnMoved", "configValidationErrors", "columnAdded", "rowAction", "validationStateChange", "requestAddRow"] }], directives: [{ type: i1.NgForOf, selector: "[ngFor][ngForOf]", inputs: ["ngForOf", "ngForTrackBy", "ngForTemplate"] }, { type: i1.NgIf, selector: "[ngIf]", inputs: ["ngIf", "ngIfThen", "ngIfElse"] }, { type: i1.NgStyle, selector: "[ngStyle]", inputs: ["ngStyle"] }], pipes: { "async": i1.AsyncPipe, "date": i1.DatePipe } });
10467
10399
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, decorators: [{
10468
10400
  type: Component,
10469
- args: [{ selector: 'st-workbook', template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"config.workbookActions && config.workbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\"\n (requestAddRow)=\"onRequestAddRow($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab.active .sheet-name{font-weight:600}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"] }]
10401
+ args: [{ selector: 'st-workbook', template: "<div class=\"workbook-container\" [class.fullscreen]=\"isFullscreen$ | async\">\n <nile-tab-group [activeIndex]=\"activeSheetIndex\">\n \n <!-- Sheet Tabs (one per sheet) -->\n <nile-tab *ngFor=\"let sheet of sheets; let i = index\"\n slot=\"nav\" \n panel=\"shared-panel\"\n [class.active]=\"i === activeSheetIndex\"\n (click)=\"onTabChange(i)\">\n <div class=\"sheet-tab-content\">\n <span class=\"sheet-name\">{{ sheet.name }}</span>\n \n <!-- Tab actions dropdown button -->\n <button class=\"tab-actions-button\"\n (click)=\"openTabActions($event, sheet, i)\"\n *ngIf=\"hasTabActions(sheet)\">\n <nile-icon name=\"arrowdown\" size=\"14\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Toolbar Tab (for workbook controls) -->\n <nile-tab slot=\"nav\" \n panel=\"shared-panel\"\n class=\"workbook-toolbar-tab\"\n [disabled]=\"true\">\n <div class=\"workbook-toolbar-content\">\n <!-- Autosave Indicator -->\n <div class=\"autosave-indicator\" *ngIf=\"autosaveEnabled\">\n <nile-icon \n *ngIf=\"!isSaving && lastSaveTime\" \n name=\"save\" \n size=\"14\"\n [title]=\"'Saved at ' + (lastSaveTime | date:'HH:mm:ss')\">\n </nile-icon>\n <nile-icon \n *ngIf=\"isSaving\" \n name=\"loader\" \n size=\"14\"\n title=\"Saving...\">\n </nile-icon>\n </div>\n\n <!-- Toolbar Workbook Actions (shown as individual buttons) -->\n <button *ngFor=\"let action of toolbarWorkbookActions\"\n class=\"toolbar-action-button\"\n [class.disabled]=\"isActionDisabled(action)\"\n [title]=\"action.label\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" [name]=\"action.icon\"></nile-icon>\n <span *ngIf=\"!action.icon\">{{ action.label }}</span>\n </button>\n\n <!-- Workbook Actions Dropdown -->\n <button class=\"workbook-actions-button\"\n *ngIf=\"visibleWorkbookActions.length > 0\"\n (click)=\"toggleWorkbookActions($event)\"\n title=\"Workbook Actions\">\n <nile-icon name=\"settings\"></nile-icon>\n </button>\n \n <!-- Add Sheet Button -->\n <button class=\"add-sheet-button\"\n *ngIf=\"canAddSheet\"\n (click)=\"onAddSheet()\"\n title=\"Add Sheet\">\n <nile-icon name=\"plus\"></nile-icon>\n </button>\n \n <!-- Fullscreen Button -->\n <button class=\"fullscreen-button\"\n *ngIf=\"config.display?.allowFullscreen !== false\"\n (click)=\"toggleFullscreen()\"\n [title]=\"(isFullscreen$ | async) ? 'Exit Fullscreen' : 'Fullscreen'\">\n <nile-icon [name]=\"(isFullscreen$ | async) ? 'collapse' : 'expand-06'\"></nile-icon>\n </button>\n </div>\n </nile-tab>\n \n <!-- Single Shared Tab Panel -->\n <nile-tab-panel name=\"shared-panel\">\n <!-- Lazy loading strategy: table is destroyed and recreated with new config/state when sheet changes -->\n <!-- Using ngFor with trackBy to force complete reinitialization when tableComponentKey changes -->\n <ng-container *ngFor=\"let key of [tableComponentKey]; trackBy: trackByKey\">\n <st-table *ngIf=\"currentTableConfig && currentTableState\"\n [attr.data-sheet-key]=\"key\"\n [tableConfig]=\"currentTableConfig\"\n [data$]=\"currentTableData$\"\n [tableState]=\"currentTableState\"\n (cellChange)=\"onCellChange($event)\"\n (cellSave)=\"onCellSave($event)\"\n (stateChange)=\"onTableStateChange($event)\"\n (requestAddRow)=\"onRequestAddRow($event)\">\n </st-table>\n </ng-container>\n </nile-tab-panel>\n \n </nile-tab-group>\n</div>\n\n<!-- Tab Actions Dropdown -->\n<div class=\"tab-actions-dropdown\" \n *ngIf=\"tabActionsOpen\"\n [ngStyle]=\"tabActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeTabActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of selectedSheet?.tabActions\" \n (click)=\"onTabActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Workbook Actions Dropdown -->\n<div class=\"workbook-actions-dropdown\"\n *ngIf=\"workbookActionsOpen\"\n [ngStyle]=\"workbookActionsPosition\">\n <div class=\"dropdown-backdrop\" (click)=\"closeWorkbookActions()\"></div>\n <div class=\"dropdown-menu\">\n <nile-menu>\n <nile-menu-item *ngFor=\"let action of visibleWorkbookActions\"\n [class.disabled]=\"isActionDisabled(action)\"\n (click)=\"onWorkbookActionClick(action, $event)\">\n <nile-icon *ngIf=\"action.icon\" slot=\"prefix\" size=\"14\" [name]=\"action.icon\"></nile-icon>\n {{ action.label }}\n </nile-menu-item>\n </nile-menu>\n </div>\n</div>\n\n<!-- Fullscreen Backdrop -->\n<div class=\"fullscreen-backdrop\" \n *ngIf=\"isFullscreen$ | async\"\n (click)=\"toggleFullscreen()\">\n</div>\n\n", styles: ["@import\"@aquera/nile/lib/styles/variables.css\";:host{display:block;width:100%;height:100%}.workbook-container{display:flex;flex-direction:column;height:100%;background:white;border:1px solid var(--nile-color-neutral-200);border-radius:4px;overflow:hidden}.workbook-container.fullscreen{position:fixed;inset:0;z-index:2000;border:none;border-radius:0}.workbook-container nile-tab-group{height:100%;display:flex;flex-direction:column}.sheet-tab-content{display:flex;align-items:center;gap:8px;padding:0 4px}.sheet-tab-content .sheet-name{font-size:12px;font-weight:500;font-family:var(--nile-font-family-sans-serif);color:#000}.sheet-tab-content .tab-actions-button{width:20px;height:20px;padding:0;background:transparent;border:none;cursor:pointer}.workbook-toolbar-tab{margin-left:auto!important;pointer-events:auto!important;border-left:1px solid var(--nile-color-neutral-200)}.workbook-toolbar-tab .workbook-toolbar-content{display:flex;gap:4px;align-items:center;padding:0 8px}.workbook-toolbar-tab .workbook-toolbar-content .autosave-indicator{display:flex;align-items:center;margin-right:8px;color:var(--nile-color-success-500)}.workbook-toolbar-tab button{display:flex;align-items:center;justify-content:center;width:28px;height:28px;padding:0;background:transparent;border:none;border-radius:4px;cursor:pointer;transition:background-color .2s;color:var(--nile-color-neutral-600)}.workbook-toolbar-tab button:hover{background-color:var(--nile-color-neutral-100);color:var(--nile-color-neutral-900)}.workbook-toolbar-tab button:active{background-color:var(--nile-color-neutral-200)}.workbook-toolbar-tab button nile-icon{font-size:16px}.workbook-toolbar-tab button.disabled{opacity:.5;cursor:not-allowed;pointer-events:none}.tab-actions-dropdown{position:fixed;z-index:1001}.tab-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.tab-actions-dropdown .dropdown-menu{position:relative;min-width:180px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.tab-actions-dropdown .dropdown-menu nile-menu{display:block}.workbook-actions-dropdown{position:fixed;z-index:1001}.workbook-actions-dropdown .dropdown-backdrop{position:fixed;top:0;left:0;width:100%;height:100%;background:transparent;z-index:1000}.workbook-actions-dropdown .dropdown-menu{position:relative;min-width:200px;background:white;border-radius:8px;box-shadow:0 4px 12px #00000026;z-index:1001;overflow:hidden}.workbook-actions-dropdown .dropdown-menu nile-menu{display:block}.fullscreen-backdrop{position:fixed;inset:0;background:rgba(0,0,0,.5);z-index:1999;cursor:pointer}nile-tab-group::part(nav){background-color:#fafafa}nile-tab-group::part(active-tab-indicator-path){background:none}nile-tab-group::part(active-tab-indicator){border-bottom:none}nile-tab-group::part(tabs){gap:0}nile-tab-group nile-tab{border:1px solid #e0e0e0;border-top-left-radius:6px;border-top-right-radius:6px}nile-tab-group nile-tab::part(base){padding-left:.5rem;padding-top:.5rem;padding-bottom:.5rem;border-bottom-left-radius:0;border-bottom-right-radius:0}nile-tab-group nile-tab.active{background-color:#fff;color:#000}nile-tab-group nile-tab.active .sheet-name{font-weight:600}nile-tab-group nile-tab-panel::part(base){padding:0;max-height:30rem}\n"] }]
10470
10402
  }], ctorParameters: function () { return [{ type: AutosaveService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { config: [{
10471
10403
  type: Input
10472
10404
  }], sheetsData: [{