@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.
- package/esm2020/lib/editors/nile-select-editor.mjs +126 -225
- package/esm2020/lib/models/workbook-action.interface.mjs +1 -1
- package/esm2020/lib/renderer/components/st-table/st-table.component.mjs +25 -2
- package/esm2020/lib/renderer/components/st-workbook/st-workbook.component.mjs +13 -5
- package/fesm2015/aquera-ngx-smart-table.mjs +165 -233
- package/fesm2015/aquera-ngx-smart-table.mjs.map +1 -1
- package/fesm2020/aquera-ngx-smart-table.mjs +161 -229
- package/fesm2020/aquera-ngx-smart-table.mjs.map +1 -1
- package/lib/editors/nile-select-editor.d.ts +0 -10
- package/lib/models/workbook-action.interface.d.ts +5 -0
- package/lib/renderer/components/st-workbook/st-workbook.component.d.ts +5 -1
- package/package.json +1 -1
|
@@ -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,9 +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
|
-
this.hasSaved = false; // Flag to prevent double-save
|
|
3617
|
-
this.lastSelectedValue = ''; // Track the last selected value
|
|
3618
3681
|
// Handle Observable options
|
|
3619
3682
|
if (isObservable(options.options)) {
|
|
3620
3683
|
this.optionsSubscription = options.options.subscribe(opts => {
|
|
@@ -3633,101 +3696,20 @@ class NileSelectEditor {
|
|
|
3633
3696
|
}
|
|
3634
3697
|
}
|
|
3635
3698
|
}
|
|
3636
|
-
/**
|
|
3637
|
-
* Inject global styles to remove border from nile-select combobox
|
|
3638
|
-
* Uses ::part selector which works across shadow DOM boundaries
|
|
3639
|
-
*/
|
|
3640
|
-
injectBorderlessStyles() {
|
|
3641
|
-
if (NileSelectEditor.stylesInjected)
|
|
3642
|
-
return;
|
|
3643
|
-
const styleId = 'nile-select-borderless-styles';
|
|
3644
|
-
if (document.getElementById(styleId)) {
|
|
3645
|
-
NileSelectEditor.stylesInjected = true;
|
|
3646
|
-
return;
|
|
3647
|
-
}
|
|
3648
|
-
const style = document.createElement('style');
|
|
3649
|
-
style.id = styleId;
|
|
3650
|
-
style.textContent = `
|
|
3651
|
-
.st-cell-editor::part(combobox) {
|
|
3652
|
-
border: none !important;
|
|
3653
|
-
outline: none !important;
|
|
3654
|
-
box-shadow: none !important;
|
|
3655
|
-
min-height: unset !important;
|
|
3656
|
-
height: 100% !important;
|
|
3657
|
-
padding: 0 !important;
|
|
3658
|
-
}
|
|
3659
|
-
.st-cell-editor::part(combobox):hover {
|
|
3660
|
-
border: none !important;
|
|
3661
|
-
outline: none !important;
|
|
3662
|
-
box-shadow: none !important;
|
|
3663
|
-
}
|
|
3664
|
-
.st-cell-editor::part(combobox):focus {
|
|
3665
|
-
border: none !important;
|
|
3666
|
-
outline: none !important;
|
|
3667
|
-
box-shadow: none !important;
|
|
3668
|
-
}
|
|
3669
|
-
// .st-cell-editor::part(tag) {
|
|
3670
|
-
// margin: 1px 2px !important;
|
|
3671
|
-
// border-radius: 3px !important;
|
|
3672
|
-
// background-color: #e2e8f0 !important;
|
|
3673
|
-
// font-size: 12px !important;
|
|
3674
|
-
// height: auto !important;
|
|
3675
|
-
// }
|
|
3676
|
-
// .st-cell-editor::part(tag__base) {
|
|
3677
|
-
// border: none !important;
|
|
3678
|
-
// outline: none !important;
|
|
3679
|
-
// padding: 0 2px !important;
|
|
3680
|
-
// height: auto !important;
|
|
3681
|
-
// }
|
|
3682
|
-
// .st-cell-editor::part(tag__content) {
|
|
3683
|
-
// padding: 0 !important;
|
|
3684
|
-
// }
|
|
3685
|
-
.st-cell-editor::part(tags) {
|
|
3686
|
-
gap: 2px !important;
|
|
3687
|
-
align-items: center !important;
|
|
3688
|
-
}
|
|
3689
|
-
.st-cell-editor::part(tags-count) {
|
|
3690
|
-
margin: 0 !important;
|
|
3691
|
-
}
|
|
3692
|
-
.st-cell-editor::part(footer) {
|
|
3693
|
-
height: 35px !important;
|
|
3694
|
-
min-height: 35px !important;
|
|
3695
|
-
padding: 8px !important;
|
|
3696
|
-
padding-bottom: 30px !important;
|
|
3697
|
-
box-sizing: border-box !important;
|
|
3698
|
-
}
|
|
3699
|
-
.st-cell-editor::part(listbox) {
|
|
3700
|
-
margin-bottom: 0 !important;
|
|
3701
|
-
padding-bottom: 0 !important;
|
|
3702
|
-
}
|
|
3703
|
-
`;
|
|
3704
|
-
document.head.appendChild(style);
|
|
3705
|
-
NileSelectEditor.stylesInjected = true;
|
|
3706
|
-
}
|
|
3707
3699
|
edit(context) {
|
|
3708
3700
|
if (!context.container) {
|
|
3709
3701
|
console.warn('NileSelectEditor requires a container element');
|
|
3710
3702
|
return;
|
|
3711
3703
|
}
|
|
3712
|
-
//
|
|
3713
|
-
|
|
3714
|
-
this.hasSaved = false;
|
|
3715
|
-
this.lastSelectedValue = '';
|
|
3704
|
+
// Inject dropdown height styles once
|
|
3705
|
+
injectDropdownStyles();
|
|
3716
3706
|
// Create NileSelect custom element using document.createElement
|
|
3717
3707
|
this.select = document.createElement('nile-select');
|
|
3718
3708
|
this.select.className = 'st-cell-editor';
|
|
3719
|
-
// Apply inline styles for proper cell fitting
|
|
3720
|
-
this.select.style.width = '
|
|
3721
|
-
this.select.style.height = '
|
|
3722
|
-
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';
|
|
3723
3712
|
this.select.style.boxSizing = 'border-box';
|
|
3724
|
-
this.select.style.margin = '0 2px';
|
|
3725
|
-
this.select.style.overflow = 'hidden';
|
|
3726
|
-
// Inject styles to remove border using ::part selector
|
|
3727
|
-
this.injectBorderlessStyles();
|
|
3728
|
-
// Also try removing outline on host element
|
|
3729
|
-
this.select.style.outline = 'none';
|
|
3730
|
-
this.select.style.border = 'none';
|
|
3731
3713
|
// Set initial value
|
|
3732
3714
|
this.setInitialValue(context.value);
|
|
3733
3715
|
// Apply all configuration options
|
|
@@ -3741,19 +3723,10 @@ class NileSelectEditor {
|
|
|
3741
3723
|
context.container.appendChild(this.select);
|
|
3742
3724
|
// Focus and auto-open after render
|
|
3743
3725
|
setTimeout(() => {
|
|
3744
|
-
this.select?.focus();
|
|
3745
|
-
// Auto-open the dropdown
|
|
3746
3726
|
if (this.select) {
|
|
3727
|
+
this.select.focus();
|
|
3747
3728
|
this.select.open = true;
|
|
3748
3729
|
}
|
|
3749
|
-
// Clear initializing flag after dropdown has opened
|
|
3750
|
-
setTimeout(() => {
|
|
3751
|
-
this.isInitializing = false;
|
|
3752
|
-
// For multi-select, find the portal div for click detection
|
|
3753
|
-
if (this.options.multiple) {
|
|
3754
|
-
this.portalDiv = document.querySelector('.nile-select-portal-append');
|
|
3755
|
-
}
|
|
3756
|
-
}, 100);
|
|
3757
3730
|
}, 0);
|
|
3758
3731
|
}
|
|
3759
3732
|
/**
|
|
@@ -3837,13 +3810,9 @@ class NileSelectEditor {
|
|
|
3837
3810
|
if (this.options.pill) {
|
|
3838
3811
|
this.select.pill = this.options.pill;
|
|
3839
3812
|
}
|
|
3840
|
-
if (this.options.hoist
|
|
3813
|
+
if (this.options.hoist) {
|
|
3841
3814
|
this.select.hoist = this.options.hoist;
|
|
3842
3815
|
}
|
|
3843
|
-
else {
|
|
3844
|
-
// Default hoist to true for all selects to prevent clipping issues in table cells
|
|
3845
|
-
this.select.hoist = true;
|
|
3846
|
-
}
|
|
3847
3816
|
if (this.options.placement) {
|
|
3848
3817
|
this.select.placement = this.options.placement;
|
|
3849
3818
|
}
|
|
@@ -3853,12 +3822,8 @@ class NileSelectEditor {
|
|
|
3853
3822
|
if (this.options.disableLocalSearch) {
|
|
3854
3823
|
this.select.disableLocalSearch = this.options.disableLocalSearch;
|
|
3855
3824
|
}
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
}
|
|
3859
|
-
else {
|
|
3860
|
-
this.select.portal = true; // Default to portal mode for better positioning
|
|
3861
|
-
}
|
|
3825
|
+
// Enable portal mode by default to prevent dropdown clipping in table cells
|
|
3826
|
+
this.select.portal = this.options.portal !== false;
|
|
3862
3827
|
// Prevent width syncing issues in table cells
|
|
3863
3828
|
this.select.noWidthSync = true;
|
|
3864
3829
|
}
|
|
@@ -3918,30 +3883,6 @@ class NileSelectEditor {
|
|
|
3918
3883
|
if (!this.select)
|
|
3919
3884
|
return;
|
|
3920
3885
|
const validateOnSave = this.options.validateOnSave !== false;
|
|
3921
|
-
// Prevent click events from bubbling up to the cell container
|
|
3922
|
-
const selectClickHandler = (e) => {
|
|
3923
|
-
e.stopPropagation();
|
|
3924
|
-
};
|
|
3925
|
-
this.select.addEventListener('click', selectClickHandler);
|
|
3926
|
-
this.eventListeners.push({ event: 'click', handler: selectClickHandler });
|
|
3927
|
-
// For single-select with portal, listen to document mousedown to detect option selection
|
|
3928
|
-
// Use mousedown because it fires before blur
|
|
3929
|
-
if (!this.options.multiple) {
|
|
3930
|
-
const documentMouseDownHandler = (e) => {
|
|
3931
|
-
const composedPath = e.composedPath?.() || [];
|
|
3932
|
-
// Look for nile-option in the event path
|
|
3933
|
-
for (const el of composedPath) {
|
|
3934
|
-
if (el instanceof HTMLElement && el.tagName?.toLowerCase() === 'nile-option') {
|
|
3935
|
-
const optionValue = el.getAttribute('value') || el.value || '';
|
|
3936
|
-
this.lastSelectedValue = optionValue;
|
|
3937
|
-
return;
|
|
3938
|
-
}
|
|
3939
|
-
}
|
|
3940
|
-
};
|
|
3941
|
-
// Add listener immediately (mousedown fires before blur, so no need for delay)
|
|
3942
|
-
document.addEventListener('mousedown', documentMouseDownHandler, true); // capture phase
|
|
3943
|
-
this.eventListeners.push({ event: 'document-mousedown', handler: documentMouseDownHandler });
|
|
3944
|
-
}
|
|
3945
3886
|
// Handle keyboard events (keydown is not replaced by custom events)
|
|
3946
3887
|
const keydownHandler = (e) => {
|
|
3947
3888
|
const keyEvent = e;
|
|
@@ -3953,45 +3894,54 @@ class NileSelectEditor {
|
|
|
3953
3894
|
};
|
|
3954
3895
|
this.select.addEventListener('keydown', keydownHandler);
|
|
3955
3896
|
this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
|
|
3956
|
-
// Use nile-
|
|
3957
|
-
//
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
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)
|
|
3916
|
+
return;
|
|
3917
|
+
if (!this.select)
|
|
3918
|
+
return;
|
|
3919
|
+
const target = e.target;
|
|
3920
|
+
// Check if click is inside the select element
|
|
3921
|
+
if (this.select.contains(target)) {
|
|
3962
3922
|
return;
|
|
3963
3923
|
}
|
|
3964
|
-
//
|
|
3965
|
-
|
|
3966
|
-
|
|
3967
|
-
|
|
3968
|
-
|
|
3969
|
-
|
|
3970
|
-
|
|
3971
|
-
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3975
|
-
|
|
3976
|
-
// Use lastSelectedValue if available, otherwise get from element
|
|
3977
|
-
const target = e.target;
|
|
3978
|
-
const value = this.lastSelectedValue || target?.value || this.select?.value || '';
|
|
3979
|
-
this.hasSaved = true;
|
|
3980
|
-
context.onSave(this.parseValue(value));
|
|
3981
|
-
}, 50); // 50ms delay to allow value to update
|
|
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;
|
|
3933
|
+
if (validateOnSave && !this.select?.checkValidity()) {
|
|
3934
|
+
this.select?.reportValidity();
|
|
3935
|
+
return;
|
|
3982
3936
|
}
|
|
3937
|
+
context.onSave(this.getCurrentValue());
|
|
3983
3938
|
};
|
|
3984
|
-
//
|
|
3985
|
-
|
|
3986
|
-
|
|
3987
|
-
|
|
3988
|
-
|
|
3989
|
-
this.
|
|
3990
|
-
// Also listen to change events as fallback
|
|
3991
|
-
this.select.addEventListener('nile-change', singleSelectSaveHandler);
|
|
3992
|
-
this.eventListeners.push({ event: 'nile-change', handler: singleSelectSaveHandler });
|
|
3993
|
-
this.select.addEventListener('change', singleSelectSaveHandler);
|
|
3994
|
-
this.eventListeners.push({ event: 'change', handler: singleSelectSaveHandler });
|
|
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;
|
|
3995
3945
|
// Handle clear button if enabled
|
|
3996
3946
|
if (this.options.clearable) {
|
|
3997
3947
|
const clearHandler = (e) => {
|
|
@@ -4010,43 +3960,6 @@ class NileSelectEditor {
|
|
|
4010
3960
|
this.select.addEventListener('nile-search', searchHandler);
|
|
4011
3961
|
this.eventListeners.push({ event: 'nile-search', handler: searchHandler });
|
|
4012
3962
|
}
|
|
4013
|
-
// For multi-select: detect clicks outside portal and select
|
|
4014
|
-
if (this.options.multiple) {
|
|
4015
|
-
const documentClickHandler = (e) => {
|
|
4016
|
-
const mouseEvent = e;
|
|
4017
|
-
const target = mouseEvent.target;
|
|
4018
|
-
// Check if portal div exists (might take a moment to render)
|
|
4019
|
-
if (!this.portalDiv) {
|
|
4020
|
-
this.portalDiv = document.querySelector('.nile-select-portal-append');
|
|
4021
|
-
}
|
|
4022
|
-
// Determine if click is inside the select or portal
|
|
4023
|
-
const clickedInsideSelect = this.select?.contains(target);
|
|
4024
|
-
const clickedInsidePortal = this.portalDiv?.contains(target);
|
|
4025
|
-
// If clicked outside both select and portal, save and close
|
|
4026
|
-
if (!clickedInsideSelect && !clickedInsidePortal) {
|
|
4027
|
-
if (validateOnSave && !this.select?.checkValidity()) {
|
|
4028
|
-
this.select?.reportValidity();
|
|
4029
|
-
return;
|
|
4030
|
-
}
|
|
4031
|
-
context.onSave(this.getCurrentValue());
|
|
4032
|
-
}
|
|
4033
|
-
// Otherwise, click is inside - do nothing (dropdown stays open)
|
|
4034
|
-
};
|
|
4035
|
-
// Delay adding the document click handler to prevent the initial click
|
|
4036
|
-
// (that opened the editor) from immediately triggering it and closing the dropdown
|
|
4037
|
-
// Use 200ms to ensure the dropdown has fully opened
|
|
4038
|
-
setTimeout(() => {
|
|
4039
|
-
// Only add if select still exists (editor not destroyed)
|
|
4040
|
-
if (this.select) {
|
|
4041
|
-
// Use bubbling phase (false) instead of capture to not interfere with nile-select's toggle
|
|
4042
|
-
document.addEventListener('click', documentClickHandler, false);
|
|
4043
|
-
this.eventListeners.push({
|
|
4044
|
-
event: 'document-click',
|
|
4045
|
-
handler: documentClickHandler
|
|
4046
|
-
});
|
|
4047
|
-
}
|
|
4048
|
-
}, 200);
|
|
4049
|
-
}
|
|
4050
3963
|
}
|
|
4051
3964
|
/**
|
|
4052
3965
|
* Parse value based on selection mode
|
|
@@ -4066,35 +3979,24 @@ class NileSelectEditor {
|
|
|
4066
3979
|
return value;
|
|
4067
3980
|
}
|
|
4068
3981
|
destroy() {
|
|
4069
|
-
// Reset flags
|
|
4070
|
-
this.isInitializing = false;
|
|
4071
|
-
this.hasSaved = false;
|
|
4072
|
-
this.lastSelectedValue = '';
|
|
4073
3982
|
// Unsubscribe from options Observable
|
|
4074
3983
|
if (this.optionsSubscription) {
|
|
4075
3984
|
this.optionsSubscription.unsubscribe();
|
|
4076
3985
|
this.optionsSubscription = undefined;
|
|
4077
3986
|
}
|
|
3987
|
+
// Remove document mousedown handler if exists
|
|
3988
|
+
if (this._documentMousedownHandler) {
|
|
3989
|
+
document.removeEventListener('mousedown', this._documentMousedownHandler, true);
|
|
3990
|
+
delete this._documentMousedownHandler;
|
|
3991
|
+
}
|
|
4078
3992
|
// Remove all event listeners
|
|
4079
3993
|
if (this.select) {
|
|
4080
3994
|
this.eventListeners.forEach(({ event, handler }) => {
|
|
4081
|
-
|
|
4082
|
-
// Remove from document for multi-select click handler (bubbling phase)
|
|
4083
|
-
document.removeEventListener('click', handler, false);
|
|
4084
|
-
}
|
|
4085
|
-
else if (event === 'document-mousedown') {
|
|
4086
|
-
// Remove from document for single-select option mousedown handler (capture phase)
|
|
4087
|
-
document.removeEventListener('mousedown', handler, true);
|
|
4088
|
-
}
|
|
4089
|
-
else {
|
|
4090
|
-
// Remove from select element
|
|
4091
|
-
this.select?.removeEventListener(event, handler);
|
|
4092
|
-
}
|
|
3995
|
+
this.select?.removeEventListener(event, handler);
|
|
4093
3996
|
});
|
|
4094
3997
|
this.eventListeners = [];
|
|
4095
3998
|
this.select.remove();
|
|
4096
3999
|
this.select = undefined;
|
|
4097
|
-
this.portalDiv = undefined; // Clean up portal reference
|
|
4098
4000
|
}
|
|
4099
4001
|
}
|
|
4100
4002
|
focus() {
|
|
@@ -4113,7 +4015,6 @@ class NileSelectEditor {
|
|
|
4113
4015
|
return (Array.isArray(value) ? (value.length > 0 ? value[0] : '') : value);
|
|
4114
4016
|
}
|
|
4115
4017
|
}
|
|
4116
|
-
NileSelectEditor.stylesInjected = false;
|
|
4117
4018
|
|
|
4118
4019
|
/**
|
|
4119
4020
|
* Custom editor using NileAutoComplete from @aquera/nile-elements
|
|
@@ -8828,6 +8729,15 @@ class StTableComponent {
|
|
|
8828
8729
|
focusTdElement(rowIndex, colIndex) {
|
|
8829
8730
|
// Use setTimeout to ensure DOM has updated
|
|
8830
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
|
+
}
|
|
8831
8741
|
// Use absolute row index for virtual scroll compatibility
|
|
8832
8742
|
const absoluteRowIndex = this.getAbsoluteRowIndex(rowIndex);
|
|
8833
8743
|
// Query within this component's host element to avoid selecting rows from other tables
|
|
@@ -9088,7 +8998,21 @@ class StTableComponent {
|
|
|
9088
8998
|
return;
|
|
9089
8999
|
const target = event.target;
|
|
9090
9000
|
const tableElement = target.closest('.st-table');
|
|
9091
|
-
if (
|
|
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) {
|
|
9092
9016
|
this.getActiveTableState().clearFocus();
|
|
9093
9017
|
this.getActiveTableState().clearEditingCell();
|
|
9094
9018
|
}
|
|
@@ -10046,9 +9970,13 @@ class StWorkbookComponent {
|
|
|
10046
9970
|
this.workbookActionsOpen = false;
|
|
10047
9971
|
this.workbookActionsPosition = {};
|
|
10048
9972
|
/**
|
|
10049
|
-
* Visible workbook actions (filtered by hidden)
|
|
9973
|
+
* Visible workbook actions for dropdown (filtered by hidden and showInToolbar)
|
|
10050
9974
|
*/
|
|
10051
9975
|
this.visibleWorkbookActions = [];
|
|
9976
|
+
/**
|
|
9977
|
+
* Workbook actions to show directly in toolbar
|
|
9978
|
+
*/
|
|
9979
|
+
this.toolbarWorkbookActions = [];
|
|
10052
9980
|
/**
|
|
10053
9981
|
* Destroy subject for cleanup
|
|
10054
9982
|
*/
|
|
@@ -10285,14 +10213,18 @@ class StWorkbookComponent {
|
|
|
10285
10213
|
updateVisibleWorkbookActions() {
|
|
10286
10214
|
if (!this.config.workbookActions) {
|
|
10287
10215
|
this.visibleWorkbookActions = [];
|
|
10216
|
+
this.toolbarWorkbookActions = [];
|
|
10288
10217
|
return;
|
|
10289
10218
|
}
|
|
10290
|
-
|
|
10219
|
+
const visibleActions = this.config.workbookActions.filter(action => {
|
|
10291
10220
|
if (typeof action.hidden === 'function') {
|
|
10292
10221
|
return !action.hidden();
|
|
10293
10222
|
}
|
|
10294
10223
|
return !action.hidden;
|
|
10295
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);
|
|
10296
10228
|
}
|
|
10297
10229
|
/**
|
|
10298
10230
|
* Check if workbook action is disabled
|
|
@@ -10489,10 +10421,10 @@ class StWorkbookComponent {
|
|
|
10489
10421
|
}
|
|
10490
10422
|
}
|
|
10491
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 });
|
|
10492
|
-
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 } });
|
|
10493
10425
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, decorators: [{
|
|
10494
10426
|
type: Component,
|
|
10495
|
-
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
|
|
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"] }]
|
|
10496
10428
|
}], ctorParameters: function () { return [{ type: AutosaveService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { config: [{
|
|
10497
10429
|
type: Input
|
|
10498
10430
|
}], sheetsData: [{
|