@aquera/ngx-smart-table 0.0.14-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.
@@ -3602,6 +3602,72 @@ class NileInputEditor {
3602
3602
  * Custom editor using NileSelect from @aquera/nile-elements
3603
3603
  * This demonstrates how to create dropdown/select editors for ngx-smart-table
3604
3604
  */
3605
+ /**
3606
+ * Inject global styles for nile-select dropdown height limit
3607
+ */
3608
+ let dropdownStylesInjected = false;
3609
+ function injectDropdownStyles() {
3610
+ if (dropdownStylesInjected)
3611
+ return;
3612
+ dropdownStylesInjected = true;
3613
+ const styleId = 'nile-select-dropdown-styles';
3614
+ if (document.getElementById(styleId))
3615
+ return;
3616
+ const style = document.createElement('style');
3617
+ style.id = styleId;
3618
+ style.textContent = `
3619
+ /* Limit nile-select dropdown height and enable scrolling */
3620
+ .nile-select-portal-append {
3621
+ max-height: 300px !important;
3622
+ }
3623
+ .nile-select-portal-append .select__listbox {
3624
+ min-width: 220px !important;
3625
+ max-height: 260px !important;
3626
+ overflow-y: auto !important;
3627
+ width: auto !important;
3628
+ }
3629
+ .nile-select-portal-append .select__footer {
3630
+ height: 35px !important;
3631
+ }
3632
+ /* Prevent option text truncation - target the options container */
3633
+ .nile-select-portal-append .select__options {
3634
+ width: auto !important;
3635
+ }
3636
+ .nile-select-portal-append nile-option {
3637
+ width: auto !important;
3638
+ max-width: none !important;
3639
+ }
3640
+ .nile-select-portal-append nile-option::part(base) {
3641
+ white-space: nowrap !important;
3642
+ text-overflow: clip !important;
3643
+ overflow: visible !important;
3644
+ max-width: none !important;
3645
+ }
3646
+ /* Fix option text truncation by changing flex direction */
3647
+ .nile-select-portal-append nile-option::part(option_label_container) {
3648
+ flex-direction: row !important;
3649
+ }
3650
+ /* Combobox styling using ::part() selector */
3651
+ nile-select.st-cell-editor::part(combobox) {
3652
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary)) !important;
3653
+ border: solid 1px transparent !important;
3654
+ margin: 1px 4px !important;
3655
+ }
3656
+ nile-select.st-cell-editor::part(combobox):hover {
3657
+ border: solid 1px transparent !important;
3658
+ }
3659
+ .st-cell-editor::part(combobox) {
3660
+ background-color: var(--nile-colors-white-base, var(--ng-colors-bg-primary)) !important;
3661
+ border: solid 1px transparent !important;
3662
+ margin: 1px 4px !important;
3663
+ }
3664
+ /* Search input full width */
3665
+ .nile-select-portal-append .select__search {
3666
+ width: 100% !important;
3667
+ }
3668
+ `;
3669
+ document.head.appendChild(style);
3670
+ }
3605
3671
  /**
3606
3672
  * Custom editor that uses NileSelect component
3607
3673
  * @template T The value type (string for single selection, string[] for multiple)
@@ -3612,7 +3678,6 @@ class NileSelectEditor {
3612
3678
  this.acceptsInitialKeypress = false;
3613
3679
  this.eventListeners = [];
3614
3680
  this.currentOptions = [];
3615
- this.isInitializing = false; // Flag to prevent immediate close during initialization
3616
3681
  // Handle Observable options
3617
3682
  if (isObservable(options.options)) {
3618
3683
  this.optionsSubscription = options.options.subscribe(opts => {
@@ -3631,99 +3696,20 @@ class NileSelectEditor {
3631
3696
  }
3632
3697
  }
3633
3698
  }
3634
- /**
3635
- * Inject global styles to remove border from nile-select combobox
3636
- * Uses ::part selector which works across shadow DOM boundaries
3637
- */
3638
- injectBorderlessStyles() {
3639
- if (NileSelectEditor.stylesInjected)
3640
- return;
3641
- const styleId = 'nile-select-borderless-styles';
3642
- if (document.getElementById(styleId)) {
3643
- NileSelectEditor.stylesInjected = true;
3644
- return;
3645
- }
3646
- const style = document.createElement('style');
3647
- style.id = styleId;
3648
- style.textContent = `
3649
- .st-cell-editor::part(combobox) {
3650
- border: none !important;
3651
- outline: none !important;
3652
- box-shadow: none !important;
3653
- min-height: unset !important;
3654
- height: 100% !important;
3655
- padding: 0 !important;
3656
- }
3657
- .st-cell-editor::part(combobox):hover {
3658
- border: none !important;
3659
- outline: none !important;
3660
- box-shadow: none !important;
3661
- }
3662
- .st-cell-editor::part(combobox):focus {
3663
- border: none !important;
3664
- outline: none !important;
3665
- box-shadow: none !important;
3666
- }
3667
- // .st-cell-editor::part(tag) {
3668
- // margin: 1px 2px !important;
3669
- // border-radius: 3px !important;
3670
- // background-color: #e2e8f0 !important;
3671
- // font-size: 12px !important;
3672
- // height: auto !important;
3673
- // }
3674
- // .st-cell-editor::part(tag__base) {
3675
- // border: none !important;
3676
- // outline: none !important;
3677
- // padding: 0 2px !important;
3678
- // height: auto !important;
3679
- // }
3680
- // .st-cell-editor::part(tag__content) {
3681
- // padding: 0 !important;
3682
- // }
3683
- .st-cell-editor::part(tags) {
3684
- gap: 2px !important;
3685
- align-items: center !important;
3686
- }
3687
- .st-cell-editor::part(tags-count) {
3688
- margin: 0 !important;
3689
- }
3690
- .st-cell-editor::part(footer) {
3691
- height: 35px !important;
3692
- min-height: 35px !important;
3693
- padding: 8px !important;
3694
- padding-bottom: 30px !important;
3695
- box-sizing: border-box !important;
3696
- }
3697
- .st-cell-editor::part(listbox) {
3698
- margin-bottom: 0 !important;
3699
- padding-bottom: 0 !important;
3700
- }
3701
- `;
3702
- document.head.appendChild(style);
3703
- NileSelectEditor.stylesInjected = true;
3704
- }
3705
3699
  edit(context) {
3706
3700
  if (!context.container) {
3707
3701
  console.warn('NileSelectEditor requires a container element');
3708
3702
  return;
3709
3703
  }
3710
- // Set initializing flag to prevent immediate close
3711
- this.isInitializing = true;
3704
+ // Inject dropdown height styles once
3705
+ injectDropdownStyles();
3712
3706
  // Create NileSelect custom element using document.createElement
3713
3707
  this.select = document.createElement('nile-select');
3714
3708
  this.select.className = 'st-cell-editor';
3715
- // Apply inline styles for proper cell fitting - prevent height changes
3716
- this.select.style.width = 'calc(100% - 4px)';
3717
- this.select.style.height = '100%';
3718
- this.select.style.maxHeight = '100%';
3709
+ // Apply inline styles for proper cell fitting
3710
+ this.select.style.width = '100%';
3711
+ this.select.style.height = 'inherit';
3719
3712
  this.select.style.boxSizing = 'border-box';
3720
- this.select.style.margin = '0 2px';
3721
- this.select.style.overflow = 'hidden';
3722
- // Inject styles to remove border using ::part selector
3723
- this.injectBorderlessStyles();
3724
- // Also try removing outline on host element
3725
- this.select.style.outline = 'none';
3726
- this.select.style.border = 'none';
3727
3713
  // Set initial value
3728
3714
  this.setInitialValue(context.value);
3729
3715
  // Apply all configuration options
@@ -3737,19 +3723,10 @@ class NileSelectEditor {
3737
3723
  context.container.appendChild(this.select);
3738
3724
  // Focus and auto-open after render
3739
3725
  setTimeout(() => {
3740
- this.select?.focus();
3741
- // Auto-open the dropdown
3742
3726
  if (this.select) {
3727
+ this.select.focus();
3743
3728
  this.select.open = true;
3744
3729
  }
3745
- // Clear initializing flag after dropdown has opened
3746
- setTimeout(() => {
3747
- this.isInitializing = false;
3748
- // For multi-select, find the portal div for click detection
3749
- if (this.options.multiple) {
3750
- this.portalDiv = document.querySelector('.nile-select-portal-append');
3751
- }
3752
- }, 100);
3753
3730
  }, 0);
3754
3731
  }
3755
3732
  /**
@@ -3833,13 +3810,9 @@ class NileSelectEditor {
3833
3810
  if (this.options.pill) {
3834
3811
  this.select.pill = this.options.pill;
3835
3812
  }
3836
- if (this.options.hoist !== undefined) {
3813
+ if (this.options.hoist) {
3837
3814
  this.select.hoist = this.options.hoist;
3838
3815
  }
3839
- else {
3840
- // Default hoist to true for all selects to prevent clipping issues in table cells
3841
- this.select.hoist = true;
3842
- }
3843
3816
  if (this.options.placement) {
3844
3817
  this.select.placement = this.options.placement;
3845
3818
  }
@@ -3849,12 +3822,8 @@ class NileSelectEditor {
3849
3822
  if (this.options.disableLocalSearch) {
3850
3823
  this.select.disableLocalSearch = this.options.disableLocalSearch;
3851
3824
  }
3852
- if (this.options.portal !== undefined) {
3853
- this.select.portal = this.options.portal;
3854
- }
3855
- else {
3856
- this.select.portal = true; // Default to portal mode for better positioning
3857
- }
3825
+ // Enable portal mode by default to prevent dropdown clipping in table cells
3826
+ this.select.portal = this.options.portal !== false;
3858
3827
  // Prevent width syncing issues in table cells
3859
3828
  this.select.noWidthSync = true;
3860
3829
  }
@@ -3914,13 +3883,6 @@ class NileSelectEditor {
3914
3883
  if (!this.select)
3915
3884
  return;
3916
3885
  const validateOnSave = this.options.validateOnSave !== false;
3917
- // Prevent click events from bubbling up to the cell container
3918
- // This prevents parent handlers from interfering with nile-select's toggle
3919
- const clickHandler = (e) => {
3920
- e.stopPropagation();
3921
- };
3922
- this.select.addEventListener('click', clickHandler);
3923
- this.eventListeners.push({ event: 'click', handler: clickHandler });
3924
3886
  // Handle keyboard events (keydown is not replaced by custom events)
3925
3887
  const keydownHandler = (e) => {
3926
3888
  const keyEvent = e;
@@ -3932,43 +3894,54 @@ class NileSelectEditor {
3932
3894
  };
3933
3895
  this.select.addEventListener('keydown', keydownHandler);
3934
3896
  this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
3935
- // Use nile-change custom event as primary save trigger
3936
- const changeHandler = (e) => {
3937
- // Ignore change events during initialization (can fire when setting initial value)
3938
- if (this.isInitializing) {
3897
+ // Use nile-change custom event as save trigger - ONLY for single-select
3898
+ // Multi-select should stay open until user clicks outside
3899
+ if (!this.options.multiple) {
3900
+ const changeHandler = (e) => {
3901
+ const customEvent = e;
3902
+ if (validateOnSave && !this.select?.checkValidity()) {
3903
+ this.select?.reportValidity();
3904
+ return;
3905
+ }
3906
+ context.onSave(this.parseValue(customEvent.detail.value));
3907
+ };
3908
+ this.select.addEventListener('nile-change', changeHandler);
3909
+ this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
3910
+ }
3911
+ // Track if save was already triggered (to prevent double save)
3912
+ let saveTriggered = false;
3913
+ // Use mousedown on document to detect clicks outside the select and portal
3914
+ const documentMousedownHandler = (e) => {
3915
+ if (saveTriggered)
3939
3916
  return;
3940
- }
3941
- const customEvent = e;
3942
- if (validateOnSave && !this.select?.checkValidity()) {
3943
- this.select?.reportValidity();
3917
+ if (!this.select)
3944
3918
  return;
3945
- }
3946
- // Only save immediately for single-select
3947
- // Multi-select saves on outside click
3948
- if (!this.options.multiple) {
3949
- context.onSave(this.parseValue(customEvent.detail.value));
3950
- }
3951
- };
3952
- this.select.addEventListener('nile-change', changeHandler);
3953
- this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
3954
- // Handle blur as secondary save trigger - only for single-select
3955
- // Multi-select uses hide event instead
3956
- const blurHandler = (e) => {
3957
- // Ignore blur during initialization
3958
- if (this.isInitializing) {
3919
+ const target = e.target;
3920
+ // Check if click is inside the select element
3921
+ if (this.select.contains(target)) {
3959
3922
  return;
3960
3923
  }
3924
+ // Check if click is inside any nile-select portal
3925
+ const portals = document.querySelectorAll('.nile-select-portal-append');
3926
+ for (let i = 0; i < portals.length; i++) {
3927
+ if (portals[i].contains(target)) {
3928
+ return;
3929
+ }
3930
+ }
3931
+ // Click is outside - save and exit
3932
+ saveTriggered = true;
3961
3933
  if (validateOnSave && !this.select?.checkValidity()) {
3962
3934
  this.select?.reportValidity();
3963
3935
  return;
3964
3936
  }
3965
- // For single-select, save on blur (when clicking outside)
3966
- if (!this.options.multiple) {
3967
- context.onSave(this.getCurrentValue());
3968
- }
3937
+ context.onSave(this.getCurrentValue());
3969
3938
  };
3970
- this.select.addEventListener('nile-blur', blurHandler);
3971
- this.eventListeners.push({ event: 'nile-blur', handler: blurHandler });
3939
+ // Add listener after a short delay to avoid capturing the initial click that opened the editor
3940
+ setTimeout(() => {
3941
+ document.addEventListener('mousedown', documentMousedownHandler, true);
3942
+ }, 50);
3943
+ // Store for cleanup
3944
+ this._documentMousedownHandler = documentMousedownHandler;
3972
3945
  // Handle clear button if enabled
3973
3946
  if (this.options.clearable) {
3974
3947
  const clearHandler = (e) => {
@@ -3987,43 +3960,6 @@ class NileSelectEditor {
3987
3960
  this.select.addEventListener('nile-search', searchHandler);
3988
3961
  this.eventListeners.push({ event: 'nile-search', handler: searchHandler });
3989
3962
  }
3990
- // For multi-select: detect clicks outside portal and select
3991
- if (this.options.multiple) {
3992
- const documentClickHandler = (e) => {
3993
- const mouseEvent = e;
3994
- const target = mouseEvent.target;
3995
- // Check if portal div exists (might take a moment to render)
3996
- if (!this.portalDiv) {
3997
- this.portalDiv = document.querySelector('.nile-select-portal-append');
3998
- }
3999
- // Determine if click is inside the select or portal
4000
- const clickedInsideSelect = this.select?.contains(target);
4001
- const clickedInsidePortal = this.portalDiv?.contains(target);
4002
- // If clicked outside both select and portal, save and close
4003
- if (!clickedInsideSelect && !clickedInsidePortal) {
4004
- if (validateOnSave && !this.select?.checkValidity()) {
4005
- this.select?.reportValidity();
4006
- return;
4007
- }
4008
- context.onSave(this.getCurrentValue());
4009
- }
4010
- // Otherwise, click is inside - do nothing (dropdown stays open)
4011
- };
4012
- // Delay adding the document click handler to prevent the initial click
4013
- // (that opened the editor) from immediately triggering it and closing the dropdown
4014
- // Use 200ms to ensure the dropdown has fully opened
4015
- setTimeout(() => {
4016
- // Only add if select still exists (editor not destroyed)
4017
- if (this.select) {
4018
- // Use bubbling phase (false) instead of capture to not interfere with nile-select's toggle
4019
- document.addEventListener('click', documentClickHandler, false);
4020
- this.eventListeners.push({
4021
- event: 'document-click',
4022
- handler: documentClickHandler
4023
- });
4024
- }
4025
- }, 200);
4026
- }
4027
3963
  }
4028
3964
  /**
4029
3965
  * Parse value based on selection mode
@@ -4043,29 +3979,24 @@ class NileSelectEditor {
4043
3979
  return value;
4044
3980
  }
4045
3981
  destroy() {
4046
- // Reset initialization flag
4047
- this.isInitializing = false;
4048
3982
  // Unsubscribe from options Observable
4049
3983
  if (this.optionsSubscription) {
4050
3984
  this.optionsSubscription.unsubscribe();
4051
3985
  this.optionsSubscription = undefined;
4052
3986
  }
3987
+ // Remove document mousedown handler if exists
3988
+ if (this._documentMousedownHandler) {
3989
+ document.removeEventListener('mousedown', this._documentMousedownHandler, true);
3990
+ delete this._documentMousedownHandler;
3991
+ }
4053
3992
  // Remove all event listeners
4054
3993
  if (this.select) {
4055
3994
  this.eventListeners.forEach(({ event, handler }) => {
4056
- if (event === 'document-click') {
4057
- // Remove from document for multi-select click handler (bubbling phase)
4058
- document.removeEventListener('click', handler, false);
4059
- }
4060
- else {
4061
- // Remove from select element
4062
- this.select?.removeEventListener(event, handler);
4063
- }
3995
+ this.select?.removeEventListener(event, handler);
4064
3996
  });
4065
3997
  this.eventListeners = [];
4066
3998
  this.select.remove();
4067
3999
  this.select = undefined;
4068
- this.portalDiv = undefined; // Clean up portal reference
4069
4000
  }
4070
4001
  }
4071
4002
  focus() {
@@ -4084,7 +4015,6 @@ class NileSelectEditor {
4084
4015
  return (Array.isArray(value) ? (value.length > 0 ? value[0] : '') : value);
4085
4016
  }
4086
4017
  }
4087
- NileSelectEditor.stylesInjected = false;
4088
4018
 
4089
4019
  /**
4090
4020
  * Custom editor using NileAutoComplete from @aquera/nile-elements
@@ -8799,6 +8729,15 @@ class StTableComponent {
8799
8729
  focusTdElement(rowIndex, colIndex) {
8800
8730
  // Use setTimeout to ensure DOM has updated
8801
8731
  setTimeout(() => {
8732
+ // Don't steal focus from editors - if a cell is being edited,
8733
+ // the editor should keep focus
8734
+ const editingPosition = this.getActiveTableState().getEditingPosition();
8735
+ if (editingPosition &&
8736
+ editingPosition.rowIndex === rowIndex &&
8737
+ editingPosition.columnIndex === colIndex) {
8738
+ // Cell is being edited, don't steal focus from the editor
8739
+ return;
8740
+ }
8802
8741
  // Use absolute row index for virtual scroll compatibility
8803
8742
  const absoluteRowIndex = this.getAbsoluteRowIndex(rowIndex);
8804
8743
  // Query within this component's host element to avoid selecting rows from other tables
@@ -9059,7 +8998,21 @@ class StTableComponent {
9059
8998
  return;
9060
8999
  const target = event.target;
9061
9000
  const tableElement = target.closest('.st-table');
9062
- if (!tableElement) {
9001
+ // Also check if click is inside a portal (e.g., nile-select dropdown)
9002
+ // These are rendered outside the table but are part of the editing experience
9003
+ const portalElement = target.closest('.nile-select-portal-append');
9004
+ // Check composedPath for shadow DOM elements (e.g., nile-option, nile-input inside shadow root)
9005
+ const composedPath = event.composedPath?.() || [];
9006
+ const isInsideNileElement = composedPath.some((el) => {
9007
+ if (el instanceof HTMLElement) {
9008
+ const tagName = el.tagName?.toLowerCase();
9009
+ // Check for any nile-* custom elements used in editors
9010
+ return tagName?.startsWith('nile-') ||
9011
+ el.classList?.contains('nile-select-portal-append');
9012
+ }
9013
+ return false;
9014
+ });
9015
+ if (!tableElement && !portalElement && !isInsideNileElement) {
9063
9016
  this.getActiveTableState().clearFocus();
9064
9017
  this.getActiveTableState().clearEditingCell();
9065
9018
  }
@@ -10017,9 +9970,13 @@ class StWorkbookComponent {
10017
9970
  this.workbookActionsOpen = false;
10018
9971
  this.workbookActionsPosition = {};
10019
9972
  /**
10020
- * Visible workbook actions (filtered by hidden)
9973
+ * Visible workbook actions for dropdown (filtered by hidden and showInToolbar)
10021
9974
  */
10022
9975
  this.visibleWorkbookActions = [];
9976
+ /**
9977
+ * Workbook actions to show directly in toolbar
9978
+ */
9979
+ this.toolbarWorkbookActions = [];
10023
9980
  /**
10024
9981
  * Destroy subject for cleanup
10025
9982
  */
@@ -10256,14 +10213,18 @@ class StWorkbookComponent {
10256
10213
  updateVisibleWorkbookActions() {
10257
10214
  if (!this.config.workbookActions) {
10258
10215
  this.visibleWorkbookActions = [];
10216
+ this.toolbarWorkbookActions = [];
10259
10217
  return;
10260
10218
  }
10261
- this.visibleWorkbookActions = this.config.workbookActions.filter(action => {
10219
+ const visibleActions = this.config.workbookActions.filter(action => {
10262
10220
  if (typeof action.hidden === 'function') {
10263
10221
  return !action.hidden();
10264
10222
  }
10265
10223
  return !action.hidden;
10266
10224
  });
10225
+ // Separate toolbar actions from dropdown actions
10226
+ this.toolbarWorkbookActions = visibleActions.filter(action => action.showInToolbar === true);
10227
+ this.visibleWorkbookActions = visibleActions.filter(action => action.showInToolbar !== true);
10267
10228
  }
10268
10229
  /**
10269
10230
  * Check if workbook action is disabled
@@ -10460,10 +10421,10 @@ class StWorkbookComponent {
10460
10421
  }
10461
10422
  }
10462
10423
  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 });
10463
- 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 } });
10424
+ 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 } });
10464
10425
  i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, decorators: [{
10465
10426
  type: Component,
10466
- 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"] }]
10427
+ 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"] }]
10467
10428
  }], ctorParameters: function () { return [{ type: AutosaveService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { config: [{
10468
10429
  type: Input
10469
10430
  }], sheetsData: [{