@aquera/ngx-smart-table 0.0.15-alpha → 0.0.17-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 +219 -239
- 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 +258 -246
- package/fesm2015/aquera-ngx-smart-table.mjs.map +1 -1
- package/fesm2020/aquera-ngx-smart-table.mjs +254 -243
- package/fesm2020/aquera-ngx-smart-table.mjs.map +1 -1
- package/lib/editors/nile-select-editor.d.ts +3 -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,7 @@ class NileSelectEditor {
|
|
|
3612
3678
|
this.acceptsInitialKeypress = false;
|
|
3613
3679
|
this.eventListeners = [];
|
|
3614
3680
|
this.currentOptions = [];
|
|
3615
|
-
this.
|
|
3616
|
-
this.hasSaved = false; // Flag to prevent double-save
|
|
3617
|
-
this.lastSelectedValue = ''; // Track the last selected value
|
|
3681
|
+
this.trackedValues = []; // Track selected values for virtual scroll
|
|
3618
3682
|
// Handle Observable options
|
|
3619
3683
|
if (isObservable(options.options)) {
|
|
3620
3684
|
this.optionsSubscription = options.options.subscribe(opts => {
|
|
@@ -3633,101 +3697,20 @@ class NileSelectEditor {
|
|
|
3633
3697
|
}
|
|
3634
3698
|
}
|
|
3635
3699
|
}
|
|
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
3700
|
edit(context) {
|
|
3708
3701
|
if (!context.container) {
|
|
3709
3702
|
console.warn('NileSelectEditor requires a container element');
|
|
3710
3703
|
return;
|
|
3711
3704
|
}
|
|
3712
|
-
//
|
|
3713
|
-
|
|
3714
|
-
this.hasSaved = false;
|
|
3715
|
-
this.lastSelectedValue = '';
|
|
3705
|
+
// Inject dropdown height styles once
|
|
3706
|
+
injectDropdownStyles();
|
|
3716
3707
|
// Create NileSelect custom element using document.createElement
|
|
3717
3708
|
this.select = document.createElement('nile-select');
|
|
3718
3709
|
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%';
|
|
3710
|
+
// Apply inline styles for proper cell fitting
|
|
3711
|
+
this.select.style.width = '100%';
|
|
3712
|
+
this.select.style.height = 'inherit';
|
|
3723
3713
|
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
3714
|
// Set initial value
|
|
3732
3715
|
this.setInitialValue(context.value);
|
|
3733
3716
|
// Apply all configuration options
|
|
@@ -3739,22 +3722,15 @@ class NileSelectEditor {
|
|
|
3739
3722
|
// Clear container and append select
|
|
3740
3723
|
context.container.innerHTML = '';
|
|
3741
3724
|
context.container.appendChild(this.select);
|
|
3742
|
-
// Focus
|
|
3725
|
+
// Focus the select after render
|
|
3743
3726
|
setTimeout(() => {
|
|
3744
|
-
|
|
3745
|
-
|
|
3746
|
-
if (this.select) {
|
|
3747
|
-
this.select.open = true;
|
|
3727
|
+
try {
|
|
3728
|
+
this.select?.focus();
|
|
3748
3729
|
}
|
|
3749
|
-
|
|
3750
|
-
|
|
3751
|
-
|
|
3752
|
-
|
|
3753
|
-
if (this.options.multiple) {
|
|
3754
|
-
this.portalDiv = document.querySelector('.nile-select-portal-append');
|
|
3755
|
-
}
|
|
3756
|
-
}, 100);
|
|
3757
|
-
}, 0);
|
|
3730
|
+
catch (e) {
|
|
3731
|
+
// Ignore errors
|
|
3732
|
+
}
|
|
3733
|
+
}, 50);
|
|
3758
3734
|
}
|
|
3759
3735
|
/**
|
|
3760
3736
|
* Set the initial value for the select
|
|
@@ -3766,17 +3742,21 @@ class NileSelectEditor {
|
|
|
3766
3742
|
// Handle multiple selection - value should be array
|
|
3767
3743
|
if (Array.isArray(value)) {
|
|
3768
3744
|
this.select.value = value;
|
|
3745
|
+
this.trackedValues = [...value]; // Initialize tracked values
|
|
3769
3746
|
}
|
|
3770
3747
|
else if (value) {
|
|
3771
3748
|
this.select.value = [String(value)];
|
|
3749
|
+
this.trackedValues = [String(value)];
|
|
3772
3750
|
}
|
|
3773
3751
|
else {
|
|
3774
3752
|
this.select.value = [];
|
|
3753
|
+
this.trackedValues = [];
|
|
3775
3754
|
}
|
|
3776
3755
|
}
|
|
3777
3756
|
else {
|
|
3778
3757
|
// Handle single selection
|
|
3779
3758
|
this.select.value = value ? String(value) : '';
|
|
3759
|
+
this.trackedValues = value ? [String(value)] : [];
|
|
3780
3760
|
}
|
|
3781
3761
|
}
|
|
3782
3762
|
/**
|
|
@@ -3837,13 +3817,9 @@ class NileSelectEditor {
|
|
|
3837
3817
|
if (this.options.pill) {
|
|
3838
3818
|
this.select.pill = this.options.pill;
|
|
3839
3819
|
}
|
|
3840
|
-
if (this.options.hoist
|
|
3820
|
+
if (this.options.hoist) {
|
|
3841
3821
|
this.select.hoist = this.options.hoist;
|
|
3842
3822
|
}
|
|
3843
|
-
else {
|
|
3844
|
-
// Default hoist to true for all selects to prevent clipping issues in table cells
|
|
3845
|
-
this.select.hoist = true;
|
|
3846
|
-
}
|
|
3847
3823
|
if (this.options.placement) {
|
|
3848
3824
|
this.select.placement = this.options.placement;
|
|
3849
3825
|
}
|
|
@@ -3853,12 +3829,8 @@ class NileSelectEditor {
|
|
|
3853
3829
|
if (this.options.disableLocalSearch) {
|
|
3854
3830
|
this.select.disableLocalSearch = this.options.disableLocalSearch;
|
|
3855
3831
|
}
|
|
3856
|
-
|
|
3857
|
-
|
|
3858
|
-
}
|
|
3859
|
-
else {
|
|
3860
|
-
this.select.portal = true; // Default to portal mode for better positioning
|
|
3861
|
-
}
|
|
3832
|
+
// Enable portal mode by default to prevent dropdown clipping in table cells
|
|
3833
|
+
this.select.portal = this.options.portal !== false;
|
|
3862
3834
|
// Prevent width syncing issues in table cells
|
|
3863
3835
|
this.select.noWidthSync = true;
|
|
3864
3836
|
}
|
|
@@ -3868,12 +3840,17 @@ class NileSelectEditor {
|
|
|
3868
3840
|
updateOptions() {
|
|
3869
3841
|
if (!this.select)
|
|
3870
3842
|
return;
|
|
3871
|
-
//
|
|
3872
|
-
|
|
3873
|
-
this.
|
|
3843
|
+
// For virtual scroll mode, just update the data property (no DOM children to clear)
|
|
3844
|
+
if (this.options.enableVirtualScroll) {
|
|
3845
|
+
this.createAndAppendOptions(this.currentOptions);
|
|
3846
|
+
}
|
|
3847
|
+
else {
|
|
3848
|
+
// Standard mode: clear existing nile-option elements first
|
|
3849
|
+
while (this.select.firstChild) {
|
|
3850
|
+
this.select.removeChild(this.select.firstChild);
|
|
3851
|
+
}
|
|
3852
|
+
this.createAndAppendOptions(this.currentOptions);
|
|
3874
3853
|
}
|
|
3875
|
-
// Re-append with new options
|
|
3876
|
-
this.createAndAppendOptions(this.currentOptions);
|
|
3877
3854
|
// Trigger update on the web component
|
|
3878
3855
|
if (this.select.requestUpdate) {
|
|
3879
3856
|
this.select.requestUpdate();
|
|
@@ -3881,11 +3858,22 @@ class NileSelectEditor {
|
|
|
3881
3858
|
}
|
|
3882
3859
|
/**
|
|
3883
3860
|
* Create NileOption elements and append them to the select
|
|
3861
|
+
* When enableVirtualScroll is true, uses data property instead of DOM elements
|
|
3862
|
+
* @see https://nile.aqueralabs.com/select-virtual?theme=enterprise
|
|
3884
3863
|
* @param options - The options to render
|
|
3885
3864
|
*/
|
|
3886
3865
|
createAndAppendOptions(options) {
|
|
3887
3866
|
if (!this.select)
|
|
3888
3867
|
return;
|
|
3868
|
+
// Virtual scroll mode: use data property instead of DOM elements
|
|
3869
|
+
if (this.options.enableVirtualScroll) {
|
|
3870
|
+
const virtualData = options.map(opt => typeof opt === 'string'
|
|
3871
|
+
? { value: opt, label: opt }
|
|
3872
|
+
: { value: opt.value, label: opt.label, disabled: opt.disabled });
|
|
3873
|
+
this.select.data = virtualData;
|
|
3874
|
+
return;
|
|
3875
|
+
}
|
|
3876
|
+
// Standard mode: create nile-option elements
|
|
3889
3877
|
options.forEach(opt => {
|
|
3890
3878
|
const nileOption = document.createElement('nile-option');
|
|
3891
3879
|
// Auto-detect format: string or SelectOption object
|
|
@@ -3918,30 +3906,6 @@ class NileSelectEditor {
|
|
|
3918
3906
|
if (!this.select)
|
|
3919
3907
|
return;
|
|
3920
3908
|
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
3909
|
// Handle keyboard events (keydown is not replaced by custom events)
|
|
3946
3910
|
const keydownHandler = (e) => {
|
|
3947
3911
|
const keyEvent = e;
|
|
@@ -3953,45 +3917,100 @@ class NileSelectEditor {
|
|
|
3953
3917
|
};
|
|
3954
3918
|
this.select.addEventListener('keydown', keydownHandler);
|
|
3955
3919
|
this.eventListeners.push({ event: 'keydown', handler: keydownHandler });
|
|
3956
|
-
// Use nile-
|
|
3957
|
-
//
|
|
3958
|
-
|
|
3959
|
-
|
|
3960
|
-
|
|
3961
|
-
|
|
3962
|
-
|
|
3963
|
-
|
|
3964
|
-
// Only save for single-select
|
|
3965
|
-
if (!this.options.multiple) {
|
|
3966
|
-
// Add a small delay to let the value update after selection
|
|
3967
|
-
setTimeout(() => {
|
|
3968
|
-
// Check again after timeout in case saved by another handler
|
|
3969
|
-
if (this.hasSaved) {
|
|
3920
|
+
// Use nile-change custom event as save trigger - ONLY for single-select
|
|
3921
|
+
// Multi-select should stay open until user clicks outside
|
|
3922
|
+
if (!this.options.multiple) {
|
|
3923
|
+
const changeHandler = (e) => {
|
|
3924
|
+
const customEvent = e;
|
|
3925
|
+
try {
|
|
3926
|
+
if (validateOnSave && this.select && !this.select.checkValidity()) {
|
|
3927
|
+
this.select.reportValidity();
|
|
3970
3928
|
return;
|
|
3971
3929
|
}
|
|
3972
|
-
|
|
3973
|
-
|
|
3974
|
-
|
|
3930
|
+
}
|
|
3931
|
+
catch (err) {
|
|
3932
|
+
// Ignore validation errors - nile-select internals may not be ready
|
|
3933
|
+
}
|
|
3934
|
+
context.onSave(this.parseValue(customEvent.detail.value));
|
|
3935
|
+
};
|
|
3936
|
+
this.select.addEventListener('nile-change', changeHandler);
|
|
3937
|
+
this.eventListeners.push({ event: 'nile-change', handler: changeHandler });
|
|
3938
|
+
}
|
|
3939
|
+
// For multi-select, track value changes (needed for virtual scroll where value property doesn't update)
|
|
3940
|
+
if (this.options.multiple) {
|
|
3941
|
+
const trackValueHandler = (e) => {
|
|
3942
|
+
const customEvent = e;
|
|
3943
|
+
const newValue = customEvent.detail?.value;
|
|
3944
|
+
if (newValue !== undefined) {
|
|
3945
|
+
if (Array.isArray(newValue)) {
|
|
3946
|
+
this.trackedValues = [...newValue];
|
|
3947
|
+
}
|
|
3948
|
+
else {
|
|
3949
|
+
this.trackedValues = newValue ? [newValue] : [];
|
|
3975
3950
|
}
|
|
3976
|
-
|
|
3977
|
-
|
|
3978
|
-
|
|
3979
|
-
|
|
3980
|
-
|
|
3981
|
-
|
|
3951
|
+
}
|
|
3952
|
+
};
|
|
3953
|
+
this.select.addEventListener('nile-change', trackValueHandler);
|
|
3954
|
+
this.eventListeners.push({ event: 'nile-change', handler: trackValueHandler });
|
|
3955
|
+
}
|
|
3956
|
+
// Track if save was already triggered (to prevent double save)
|
|
3957
|
+
let saveTriggered = false;
|
|
3958
|
+
// Use mousedown on document to detect clicks outside the select and portal
|
|
3959
|
+
const documentMousedownHandler = (e) => {
|
|
3960
|
+
if (saveTriggered)
|
|
3961
|
+
return;
|
|
3962
|
+
if (!this.select)
|
|
3963
|
+
return;
|
|
3964
|
+
// Use composedPath to traverse shadow DOM boundaries
|
|
3965
|
+
const path = e.composedPath();
|
|
3966
|
+
// Check if click is inside the select element or any nile-select related elements
|
|
3967
|
+
for (const element of path) {
|
|
3968
|
+
if (!(element instanceof HTMLElement))
|
|
3969
|
+
continue;
|
|
3970
|
+
// Check if it's our select element
|
|
3971
|
+
if (element === this.select) {
|
|
3972
|
+
return;
|
|
3973
|
+
}
|
|
3974
|
+
// Check for nile-select portals (various class names)
|
|
3975
|
+
if (element.classList?.contains('nile-select-portal-append') ||
|
|
3976
|
+
element.classList?.contains('select__listbox') ||
|
|
3977
|
+
element.classList?.contains('select__options') ||
|
|
3978
|
+
element.tagName?.toLowerCase() === 'nile-option' ||
|
|
3979
|
+
element.tagName?.toLowerCase() === 'nile-select') {
|
|
3980
|
+
return;
|
|
3981
|
+
}
|
|
3982
|
+
// Check for any element with nile-select in its class
|
|
3983
|
+
if (element.className && typeof element.className === 'string' &&
|
|
3984
|
+
element.className.includes('nile-select')) {
|
|
3985
|
+
return;
|
|
3986
|
+
}
|
|
3982
3987
|
}
|
|
3988
|
+
// Also check portal containers directly
|
|
3989
|
+
const portals = document.querySelectorAll('.nile-select-portal-append, [class*="select__listbox"]');
|
|
3990
|
+
for (let i = 0; i < portals.length; i++) {
|
|
3991
|
+
if (portals[i].contains(e.target)) {
|
|
3992
|
+
return;
|
|
3993
|
+
}
|
|
3994
|
+
}
|
|
3995
|
+
// Click is outside - save and exit
|
|
3996
|
+
saveTriggered = true;
|
|
3997
|
+
try {
|
|
3998
|
+
if (validateOnSave && this.select && !this.select.checkValidity()) {
|
|
3999
|
+
this.select.reportValidity();
|
|
4000
|
+
return;
|
|
4001
|
+
}
|
|
4002
|
+
}
|
|
4003
|
+
catch (err) {
|
|
4004
|
+
// Ignore validation errors - nile-select internals may not be ready
|
|
4005
|
+
}
|
|
4006
|
+
context.onSave(this.getCurrentValue());
|
|
3983
4007
|
};
|
|
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 });
|
|
4008
|
+
// Add listener after a short delay to avoid capturing the initial click that opened the editor
|
|
4009
|
+
setTimeout(() => {
|
|
4010
|
+
document.addEventListener('mousedown', documentMousedownHandler, true);
|
|
4011
|
+
}, 50);
|
|
4012
|
+
// Store for cleanup
|
|
4013
|
+
this._documentMousedownHandler = documentMousedownHandler;
|
|
3995
4014
|
// Handle clear button if enabled
|
|
3996
4015
|
if (this.options.clearable) {
|
|
3997
4016
|
const clearHandler = (e) => {
|
|
@@ -4010,43 +4029,6 @@ class NileSelectEditor {
|
|
|
4010
4029
|
this.select.addEventListener('nile-search', searchHandler);
|
|
4011
4030
|
this.eventListeners.push({ event: 'nile-search', handler: searchHandler });
|
|
4012
4031
|
}
|
|
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
4032
|
}
|
|
4051
4033
|
/**
|
|
4052
4034
|
* Parse value based on selection mode
|
|
@@ -4066,54 +4048,52 @@ class NileSelectEditor {
|
|
|
4066
4048
|
return value;
|
|
4067
4049
|
}
|
|
4068
4050
|
destroy() {
|
|
4069
|
-
// Reset flags
|
|
4070
|
-
this.isInitializing = false;
|
|
4071
|
-
this.hasSaved = false;
|
|
4072
|
-
this.lastSelectedValue = '';
|
|
4073
4051
|
// Unsubscribe from options Observable
|
|
4074
4052
|
if (this.optionsSubscription) {
|
|
4075
4053
|
this.optionsSubscription.unsubscribe();
|
|
4076
4054
|
this.optionsSubscription = undefined;
|
|
4077
4055
|
}
|
|
4056
|
+
// Remove document mousedown handler if exists
|
|
4057
|
+
if (this._documentMousedownHandler) {
|
|
4058
|
+
document.removeEventListener('mousedown', this._documentMousedownHandler, true);
|
|
4059
|
+
delete this._documentMousedownHandler;
|
|
4060
|
+
}
|
|
4078
4061
|
// Remove all event listeners
|
|
4079
4062
|
if (this.select) {
|
|
4080
4063
|
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
|
-
}
|
|
4064
|
+
this.select?.removeEventListener(event, handler);
|
|
4093
4065
|
});
|
|
4094
4066
|
this.eventListeners = [];
|
|
4095
4067
|
this.select.remove();
|
|
4096
4068
|
this.select = undefined;
|
|
4097
|
-
this.portalDiv = undefined; // Clean up portal reference
|
|
4098
4069
|
}
|
|
4099
4070
|
}
|
|
4100
4071
|
focus() {
|
|
4101
|
-
|
|
4072
|
+
try {
|
|
4073
|
+
this.select?.focus();
|
|
4074
|
+
}
|
|
4075
|
+
catch (e) {
|
|
4076
|
+
// Ignore focus errors - nile-select internals may not be ready
|
|
4077
|
+
}
|
|
4102
4078
|
}
|
|
4103
4079
|
getCurrentValue() {
|
|
4104
4080
|
if (!this.select) {
|
|
4105
4081
|
return (this.options.multiple ? [] : '');
|
|
4106
4082
|
}
|
|
4107
|
-
|
|
4108
|
-
// Handle multiple selection
|
|
4083
|
+
// For multi-select, prefer tracked values (more reliable for virtual scroll)
|
|
4109
4084
|
if (this.options.multiple) {
|
|
4110
|
-
|
|
4085
|
+
// Use tracked values if available, otherwise try component value
|
|
4086
|
+
let values = this.trackedValues.length > 0 ? this.trackedValues : this.select.value;
|
|
4087
|
+
if (Array.isArray(values)) {
|
|
4088
|
+
return values.filter((v) => v !== undefined && v !== null && v !== '');
|
|
4089
|
+
}
|
|
4090
|
+
return (values ? [values] : []);
|
|
4111
4091
|
}
|
|
4112
|
-
//
|
|
4113
|
-
|
|
4092
|
+
// For single-select, use tracked values or component value
|
|
4093
|
+
let value = this.trackedValues.length > 0 ? this.trackedValues[0] : this.select.value;
|
|
4094
|
+
return (Array.isArray(value) ? (value.length > 0 ? value[0] : '') : (value || ''));
|
|
4114
4095
|
}
|
|
4115
4096
|
}
|
|
4116
|
-
NileSelectEditor.stylesInjected = false;
|
|
4117
4097
|
|
|
4118
4098
|
/**
|
|
4119
4099
|
* Custom editor using NileAutoComplete from @aquera/nile-elements
|
|
@@ -8828,6 +8808,15 @@ class StTableComponent {
|
|
|
8828
8808
|
focusTdElement(rowIndex, colIndex) {
|
|
8829
8809
|
// Use setTimeout to ensure DOM has updated
|
|
8830
8810
|
setTimeout(() => {
|
|
8811
|
+
// Don't steal focus from editors - if a cell is being edited,
|
|
8812
|
+
// the editor should keep focus
|
|
8813
|
+
const editingPosition = this.getActiveTableState().getEditingPosition();
|
|
8814
|
+
if (editingPosition &&
|
|
8815
|
+
editingPosition.rowIndex === rowIndex &&
|
|
8816
|
+
editingPosition.columnIndex === colIndex) {
|
|
8817
|
+
// Cell is being edited, don't steal focus from the editor
|
|
8818
|
+
return;
|
|
8819
|
+
}
|
|
8831
8820
|
// Use absolute row index for virtual scroll compatibility
|
|
8832
8821
|
const absoluteRowIndex = this.getAbsoluteRowIndex(rowIndex);
|
|
8833
8822
|
// Query within this component's host element to avoid selecting rows from other tables
|
|
@@ -9088,7 +9077,21 @@ class StTableComponent {
|
|
|
9088
9077
|
return;
|
|
9089
9078
|
const target = event.target;
|
|
9090
9079
|
const tableElement = target.closest('.st-table');
|
|
9091
|
-
if (
|
|
9080
|
+
// Also check if click is inside a portal (e.g., nile-select dropdown)
|
|
9081
|
+
// These are rendered outside the table but are part of the editing experience
|
|
9082
|
+
const portalElement = target.closest('.nile-select-portal-append');
|
|
9083
|
+
// Check composedPath for shadow DOM elements (e.g., nile-option, nile-input inside shadow root)
|
|
9084
|
+
const composedPath = event.composedPath?.() || [];
|
|
9085
|
+
const isInsideNileElement = composedPath.some((el) => {
|
|
9086
|
+
if (el instanceof HTMLElement) {
|
|
9087
|
+
const tagName = el.tagName?.toLowerCase();
|
|
9088
|
+
// Check for any nile-* custom elements used in editors
|
|
9089
|
+
return tagName?.startsWith('nile-') ||
|
|
9090
|
+
el.classList?.contains('nile-select-portal-append');
|
|
9091
|
+
}
|
|
9092
|
+
return false;
|
|
9093
|
+
});
|
|
9094
|
+
if (!tableElement && !portalElement && !isInsideNileElement) {
|
|
9092
9095
|
this.getActiveTableState().clearFocus();
|
|
9093
9096
|
this.getActiveTableState().clearEditingCell();
|
|
9094
9097
|
}
|
|
@@ -10046,9 +10049,13 @@ class StWorkbookComponent {
|
|
|
10046
10049
|
this.workbookActionsOpen = false;
|
|
10047
10050
|
this.workbookActionsPosition = {};
|
|
10048
10051
|
/**
|
|
10049
|
-
* Visible workbook actions (filtered by hidden)
|
|
10052
|
+
* Visible workbook actions for dropdown (filtered by hidden and showInToolbar)
|
|
10050
10053
|
*/
|
|
10051
10054
|
this.visibleWorkbookActions = [];
|
|
10055
|
+
/**
|
|
10056
|
+
* Workbook actions to show directly in toolbar
|
|
10057
|
+
*/
|
|
10058
|
+
this.toolbarWorkbookActions = [];
|
|
10052
10059
|
/**
|
|
10053
10060
|
* Destroy subject for cleanup
|
|
10054
10061
|
*/
|
|
@@ -10285,14 +10292,18 @@ class StWorkbookComponent {
|
|
|
10285
10292
|
updateVisibleWorkbookActions() {
|
|
10286
10293
|
if (!this.config.workbookActions) {
|
|
10287
10294
|
this.visibleWorkbookActions = [];
|
|
10295
|
+
this.toolbarWorkbookActions = [];
|
|
10288
10296
|
return;
|
|
10289
10297
|
}
|
|
10290
|
-
|
|
10298
|
+
const visibleActions = this.config.workbookActions.filter(action => {
|
|
10291
10299
|
if (typeof action.hidden === 'function') {
|
|
10292
10300
|
return !action.hidden();
|
|
10293
10301
|
}
|
|
10294
10302
|
return !action.hidden;
|
|
10295
10303
|
});
|
|
10304
|
+
// Separate toolbar actions from dropdown actions
|
|
10305
|
+
this.toolbarWorkbookActions = visibleActions.filter(action => action.showInToolbar === true);
|
|
10306
|
+
this.visibleWorkbookActions = visibleActions.filter(action => action.showInToolbar !== true);
|
|
10296
10307
|
}
|
|
10297
10308
|
/**
|
|
10298
10309
|
* Check if workbook action is disabled
|
|
@@ -10489,10 +10500,10 @@ class StWorkbookComponent {
|
|
|
10489
10500
|
}
|
|
10490
10501
|
}
|
|
10491
10502
|
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 } });
|
|
10503
|
+
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
10504
|
i0.ɵɵngDeclareClassMetadata({ minVersion: "12.0.0", version: "13.4.0", ngImport: i0, type: StWorkbookComponent, decorators: [{
|
|
10494
10505
|
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
|
|
10506
|
+
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
10507
|
}], ctorParameters: function () { return [{ type: AutosaveService }, { type: i0.ChangeDetectorRef }]; }, propDecorators: { config: [{
|
|
10497
10508
|
type: Input
|
|
10498
10509
|
}], sheetsData: [{
|